この記事は「ツクールアドベントカレンダー2022 Advent Calendar 2022」の15日目の記事になります。
はじめに
今回作った鏡はこんな感じです!ウワガキアイより
これを作ったお話をしていきます。
変数や条件分岐の組み合わせで作るのもできますが、大変そうだったので、使用時、お手軽に設定できるのを目指しました。
使い方
プラグインリストからプラグインをONにし、鏡用の地形タグをつけるだけでできるようにしています。
どうやってつくったの?
やなさんの作成されたMV用プラグイン鏡面反射スプライトというプラグインを参考に作成しました。
こちらのプラグイン、水面みたいな反射をお手軽な設定だけで実現できる素晴らしいプラグインとなっています。
このプラグインで水面の映り込みはできますが、鏡の場合
- キャラの上に鏡像が出てほしい
- 上下反転したい
- 画面に見えてない反対側が映ってほしい
ウワガキアイがMZ作品だったということもあって作成することにしました。
鏡面反射スプライトの生成
遠景と下層レイヤーの間に鏡像のスプライトを表示する地形タグを設定するプラグインとなっています。
下層レイヤーに透明、または半透明の画像を使用し、ツクールの地形タグ機能を使って鏡面反射する部分を指定します。
マップがロードされたとき
鏡像の本体であるSprite_SpecularをcreateParallaxの処理に追加して生成するようになっています。
var _sref_SMap_createParallax = Spriteset_Map.prototype.createParallax;
Spriteset_Map.prototype.createParallax = function() {
_sref_SMap_createParallax.call(this);
this.createSpecular();
};
Spriteset_Map.prototype.createSpecular = function() {
this._specularSprites = [];
$gameMap.events().forEach(function(event) {
this._specularSprites.push(new Sprite_Specular(event));
}, this);
$gameMap.vehicles().forEach(function(vehicle) {
this._specularSprites.push(new Sprite_Specular(vehicle));
}, this);
$gamePlayer.followers().reverseEach(function(follower) {
this._specularSprites.push(new Sprite_Specular(follower));
}, this);
this._specularSprites.push(new Sprite_Specular($gamePlayer));
for (var i = 0; i < this._specularSprites.length; i++) {
this._baseSprite.addChild(this._specularSprites[i]);
}
};
createParallaxに追記することで、マップのロードが終わり、遠景画像生成後かつタイルマップの画像生成処理Spriteset_Map.createTilemap()の直前に画像を追加することができます。
MZではfollowes()をreverseEachで回そうとするとエラーになります。
ウワガキアイでは、追従キャラを鏡を映す必要がないため、そのままこの部分削除しています。
$gamePlayer.followers().reverseEach(function(follower) {
this._specularSprites.push(new Sprite_Specular(follower));
}, this);
反射スプライトの仕組み
- 上下の反転
- 地形タグを見て鏡像を表示するかどうかの判定
Sprite_Specular.updateVisibility() - どの画像を鏡像として表示するか
Sprite_Specular.updateCharacterFrame() - 鏡像を表示する位置
Sprite_Specular.updatePosition()
これらの処理が主になっています。
上下の反転は
Sprite_Specular.initMembers()にて
this.scale = new Point(1.0,-1.0);
scaleのyを-1.0にするというものです。
scaleは拡大率になっていて、-1にすると画像が反転します。下のようなイメージです。
地形タグの判定はSprite_Specular.updateVisibility()
var x = this._character.x;
var y = this._character.y + Math.ceil(this.offsetY() / $gameMap.tileWidth())+1;
var dir = this._character._direction;
var terrainTag = $gameMap.terrainTag(x, y);
var reflectTerrain = terrainIds.contains(terrainTag);
switch(dir) {
case 2: y = reflectTerrain ? y : y - 1; break;
case 4: x = reflectTerrain ? x : x + 1; break;
case 6: x = reflectTerrain ? x : x - 1; break;
case 8: y = reflectTerrain ? y : y + 1; break;
}
terrainTag = $gameMap.terrainTag(x, y);
this.visible = terrainIds.contains(terrainTag);
プレイヤーの位置と向きを_characterから取り、上を向いているなら1マス上、右を向いているなら1マス右のタイルの地形タグを$gameMap.terrainTag(x, y)から取得
terreinIdsには、プラグインのパラメータで設定した反射させたい地形タグのIdが入っているので、該当するIdが含まれていれば鏡像の表示/非表示を制御するvisibleに結果が反映されます。
どの画像を鏡像として表示するかSprite_Specular.updateCharacterFrame()
var pw = this.patternWidth();//キャラの横幅
var ph = this.patternHeight();//キャラの縦幅
var sx = (this.characterBlockX() + this.characterPatternX()) * pw;
var sy = (this.characterBlockY() + this.characterPatternY()) * ph;
this.updateHalfBodySprites();
if (this._bushDepth > 0) {
var d = this._bushDepth;
this._upperBody.setFrame(sx, sy, pw, ph - d);
this._lowerBody.setFrame(sx, sy + ph - d, pw, d);
this.setFrame(sx, sy, 0, ph);
} else {
this.setFrame(sx, sy, pw, ph);//これが使用画像設定の本体
}
characterBlockはプレイヤーが操作しているキャラの画像idが入ります。下の画像でいうと赤、緑、紫で区切った単位です。
characterPatternXは横向きのアニメーション時どの画像を使っているか列番号が入ります。下の画像赤枠の部分です。
characterPatternYには向いてる方向に応じた画像の行番号が入ります。下の画像の赤枠部分です。
this.setFrameにこれらの方法で取得した数値を渡し、プレイヤーのアニメーションや向きを反映した、今の見た目と同じ画像を指定することができます。
鏡像を表示する位置は
Sprite_Specular.prototype.updatePosition = function() {
this.x = this._character.screenX();
this.y = this._character.screenY() + this.offsetY();
this.z = this._character.screenZ();
};
Sprite_Specular.prototype.offsetY = function() {
var offsetY = offsetYVariableId ? $gameVariables.value(offsetYVariableId) : 0;
offsetY = this._character.isEvent() ? this._character.specularOffsetY() : offsetY;
return offsetY;
};
_character.screenXYZ()にプレイヤーの現在の座標が入っています。
鏡像はscaleのyをすでに-1にして上下の反転を行っているため、プレイヤーと同じ位置に出すことで反射を表現できるようになっています。
また、プラグインのパラメータで指定したoffsetY分上下にずらすことができます。
鏡にするための改変
やりたいことは
- キャラの上に鏡像が出てほしい
- 上下反転したい
- 画面に見えてない反対側が映ってほしい
キャラの上に鏡像を出す
これはプラグインのoffsetYの設定で可能でした。
上下反転したい
scaleのyを-1にすることで上下反転していると言うことでしたが、鏡の場合これが必要なくなります。
そのため、
this.scale = new Point(1.0,-1.0);
としていた部分を
this.scale = new Point(1.0,1.0);
マイナスを取ることで上下の反転をなくすことができました。
画面に見えてない反対側が映ってほしい
これはどの画像を鏡像として表示するかの項で書いたSprite_Specular.updateCharacterFrame()の中で、使用する画像を決めているという部分がありましたが、ここで、プレイヤーの画像に対して鏡に映っていてほしい画像を取得して来るように改変しています。
下の画像、同じ色のものが反対側と扱いました。
ツクールの規格で作られているキャラグラフィックはこの向きと組み合わせで12枚の絵でできているので、どのキャラであっても規格にそっていれば映すことができます。
コードは以下のように改変
Sprite_Specular.prototype.updateCharacterFrame = function() {
var pw = this.patternWidth();
var ph = this.patternHeight();
//向きと反対の画像をつかう
var pattern_x = this.characterPatternX();
var pattern_y = this.characterPatternY();
var block_x = this.characterBlockX();
var block_y = this.characterBlockY();
//左右反転
switch (pattern_x) {
case 0:
pattern_x = 2;
break;
case 1:
break;
case 2:
pattern_x = 0;
break;
default:
break;
}
//上下反転
switch (pattern_y) {
case 0:
pattern_y = 3;
break;
case 1:
pattern_y = 1;
break;
case 2:
pattern_y = 2;
break;
case 3:
pattern_y = 0;
break;
default:
break;
}
var sx = (block_x + pattern_x) * pw;
var sy = (block_y+ pattern_y) * ph;
this.updateHalfBodySprites();
if (this._bushDepth > 0) {
var d = this._bushDepth;
this._upperBody.setFrame(sx, sy, pw, ph - d);
this._lowerBody.setFrame(sx, sy + ph - d, pw, d);
this.setFrame(sx, sy, 0, ph);
} else {
this.setFrame(sx, sy, pw, ph);
}
};
向きの反転部分は力技になっていますが、事前にどの向きのときどの画像を使うのか整理していたのでその通り対応させています。
以上で大筋は完成です。
こだわりを足す
地形タグの設定簡略化
元のプラグインでは、反射したい地形にすべて地形タグを入れていく必要がありました。
大きい鏡であればその鏡のサイズ分のマスだけ地形タグを設定することになります。
使うときにお手軽に設定できるのを目指していたためちょっと工夫します。
2Dゲームの性質上、カメラ位置は手前から奥を見る向きで固定のため、鏡にキャラが映るとき、鏡は自キャラより上に必ずあります。
ならば、キャラの上方向に設定した地形タグがあれば、鏡像を表示するというようにしてやれば、鏡のサイズがどうあっても1行分の地形タグを設定してやればいいだけになります。
上方向にタグがあるかの判定は単純にキャラの上方向にfor文で回して確認しにいっています。
Sprite_Specular.prototype.findReflectBorder = function () {
var terrainTag;
var reflectTerrain;
var x = this._character.x;
for (var _y = this._character.y; _y > 0; _y--) {
terrainTag = $gameMap.terrainTag(x, _y);
reflectTerrain = terrainIds.contains(terrainTag);
if (reflectTerrain == true) {
return _y;
}
}
return -1;
}
鏡の角度みたいなのの表現
鏡から近づいたとき、離れたとき、カメラに対して水平な鏡か前傾しているか、後傾しているかで結構表示位置が変わります。
感覚的なものになるのですが、以下の画像のようなパターンです。
カメラと鏡が平行だと腰辺りまで映りますが、傾いてると下の方が映ったり上の方が映ったりのような感じです。
コードはもともとあった上下の位置をずらすoffsetYに対し、新規で用意した係数と鏡との距離をかけることで実現しています。
var length = (this.convertToScreenY(BorderY) - this._character.screenY());
this.offsetY = (length + jyouge) * toosa;
語彙がないためjyougeとかtoosaという表現になってしまっていますね・・・
応用例
ツノウサギの家のメインの方やかろさんが自分の想定してない使い方で演出を追加していました。
鏡の中に映る方の画像と背中側の画像の対応でキャラチップを作ることにより、アニメーションさせています。素材は以下のような素材を用意し、イベントの移動ルートの設定からウェイトと画像の変更の組み合わせでアニメーションさせていました。
そういう機能があるなら組み合わせてこういう事もできるんじゃないか、と発想して工夫するのはゲーム制作でかなり重宝するスキルだと思います。
まとめ
何をしたいのかを整理し、ツクールの機能や先人のプラグインなどでそれが表現できるのであればそれを使いましょう。
なければ作ることになるのですが、できるだけ挙動の近いプラグインなどを参考にすると実装しやすいと思います。
プラグイン改変は、
- プラグインがどう動いているのかを理解する
→肝となる処理を見つける - 自作ゲームで必要なものだけ作る
→追従する仲間や草むらの表現もサポートするプラグインでしたが、このゲームには不要だったので無視または削除しました
配布しないのであればここは最小限ですまし他に注力できます - こだわりを足す(任意)
→自分で作りなおすのならちょっと足したくなります
鏡を例にプラグイン改変の手引きになっているようなそんな記事でした。
明日は一緒にゲームを作っているメインの方、やかろさんによる記事になります!
よろしければご覧ください!
コメント
すごいプラグインですね!
公開の予定とかあったりしますか……?
もし公開されたら自分も使ってみたいなと思って
コメントありがとうございます!
こちらは自作品用に製作しているため、配布する予定はないのですが、作り方はほぼ載っているので自己責任でアレンジしてお使いいただく分にはご自由にお使いください〜
ご返信遅くなって申し訳ありません
参考にさせていただきます
自作ゲームに使用したいと思います