Bitmap/Spriteの扱い方 / ツクールMV小技・Tips集
目次
うーん、結局javascript出来るようにならないといけないってこと?
2、ツクールMVにおけるBitmap/Spriteの扱い方とは
今回はツクールMVで多くの方が困りそうな箇所であるBitmapとSpriteについてを私の知っている範囲内でご紹介したいと思います。
RPGツクールMVより前のVXAce等ではRGSSでBitmap/Spriteを表示したいときは
image_file_name = "Graphics/Pictures/gazou"
とファイルパスを指定し、
$image = Sprite.new
新しいspriteを宣言して、
$image.bitmap = Bitmap.new(image_file_name)
とbitmapを宣言すれば自動的にゲーム画面に表示されていたのですが、ツクールMVで上記のようにやると画像が表示されません。
ツクールMVでは"addChild"という概念があり、これは追加したいシーンやベースとなっているスプライト(画面)に子要素として画像を追加するというイメージの処理です。
(Pixi.jsでは「The DisplayObject to add to the container」と説明されています。詳しくはPixi.jsのページを参考になさってください。)
このため、新しいスプライトを指定しただけでは、どの要素に追加するのかを指定されていないため、ゲーム画面に表示されないのです。
ではどうすれば良いのかというと汎用性があるものとしては以下のようなものとなります。
ex1) 敵バトラー「死神」のサイドビュー画像を表示(イベントスクリプトに入力)
var bitmap = ImageManager.loadSvEnemy('Death');
var sprite = new Sprite(bitmap);
SceneManager._scene.addChild(sprite);
このスクリプトをマップ上で実行すればマップ上に死神のサイドビュー画像が表示され、バトル画面で実行すればバトル画面に表示されます。
Ⅰ、1行目:ファイルの指定
まずは一行目から見ていきます。
ツクールMVにはファイルパスの指定を省略するメソッドが存在します。
それぞれの画像フォルダごとにメソッドが違いますので、以下を参考に。
アニメーション:ImageManager.loadAnimation('ファイル名')
戦闘背景1(床)ImageManager.loadBattleback1('ファイル名')
戦闘背景2(壁)ImageManager.loadBattleback2('ファイル名')
バトラー:ImageManager.loadEnemy('ファイル名')
サイドビューの敵:ImageManager.loadSvEnemy('ファイル名')
キャラチップ:ImageManager.loadCharacter('ファイル名')
顔グラ:ImageManager.loadFace('ファイル名')
遠景:ImageManager.loadParallax('ファイル名')
ピクチャー:ImageManager.loadPicture('ファイル名')
サイドビューのアクター:ImageManager.loadSvActor('ファイル名')
システム:ImageManager.loadSystem('ファイル名')
タイルセット:ImageManager.loadTileset('ファイル名')
タイトル1(背景):ImageManager.loadTitle1('ファイル名')
タイトル2(前景):ImageManager.loadTitle2('ファイル名')
もし独自にフォルダを作っていたリ、独自のシステムを作っていてファイルパスを指定したい場合などには以下のようになります。
ex2) 敵バトラー「死神」のサイドビュー画像を表示(イベントスクリプトに入力)
var bitmap = ImageManager.loadBitmap('img/sv_enemies/','Death' , 0, true)
var sprite = new Sprite(bitmap);
SceneManager._scene.addChild(sprite);
ImageManager.loadBitmapのパラメータにはついては以下の通りです。
ImageManager.loadBitmap('ファイルパス', 'ファイル名', 色相, スムージング)
ファイル、ファイルパスについては上記の例で分かるかと思います。
色相については0~360で設定し、色相を変更させるパラメータです。
スムージングはtrue/falseで設定し、画像を拡大縮小したり、回転させたりしても滑らかに表示されるように画像を滑らかに処理するパラメータです。
デフォルトではドット絵にはスムージングせず、その他の画像にはスムージングするように設定されています。
画像にスムージングをすると処理が重くなってしまうため、ドット絵や回転、拡縮しない画像にはfalseで良いかもしれません。
Ⅱ、2行目:Spriteの宣言
2行目ですが、これはRGSSのときにもあった新しいSpriteの宣言です。
Ⅲ、3行目:addChild
3行目のaddChildこそツクールMVにおける厄介な存在です。
最初の例
SceneManager._scene.addChild(sprite);
では_sceneで「現在のシーン」を取得し、そのシーンに追加しています。
例えばマップ上にいればScene_Mapですし、バトル中ならScene_Battleとなります。
そのシーンに子要素としてspriteを追加しているのです。
3、addChildとは要素に追加するということ
この辺りは画像を表示する上で重要という訳ではなく、少し専門的な話になるので興味のない方は読み飛ばしていただいて構いません。
ただ、今後の話が分かり易くなります。
このaddChildというのがRGSSを弄っていた方やプログラミングに慣れていない方にとって分かりにくいポイントになるかと思います。
そもそもJavascirptというは主にウェブページで使われる言語です。
そのため、HTMLの操作が主な役割となっているのです。
HTML、つまり私達がよく見るwebページというは大体以下のようにできています。
つまり、文章や画像などのコンテンツは幾重にも重なる箱の中に入っているように存在しているのです。
その箱をここでは単純に「要素」と呼びましょう。
javascriptではこの箱の中に新しく文章や画像を入れるときに"append child"、「要素を追加する」というイメージで入れていきます。
javascriptを基盤にしているツクールMVでもその仕様が採用され、"addChild"となっているのだと思います。
実際、その働きはjavascriptにおける".append Child"によく似ています。
こういったことがあるため、spriteを新たに宣言をしただけでは、どの箱(=要素)に追加すればよいかが分からないため、表示されない、ということです。(たぶん)
技術的に正確かどうかは分からないので、より知りたい方は専門書など読むことをお勧めします。
4、特殊な場面でのaddChild
多くの場合は上記のスクリプトで事足りますが、マップ上でキャラクターの下に画像を表示させたい(=タイルマップの上に表示させたい)時など、特殊な場面ではこれでは対応できません。
これらの場合にはイベントスクリプトではなく、スクリプトを弄る、つまり自作のプラグインを製作する必要があります。(イベントスクリプトで済む方法があったら教えてほしいです(汗
bitmapを表示させるプラグインを製作方法については弓猫さんのwebページ「ゲーム研究の弓猫ちゃんねる」で紹介されています。
とても参考になります。
http://lucky-duet.com/archives/1555
弓猫さんのページで自作プラグインとして以下のようなものが紹介されています。
ex3) ゲーム開始時からマップにサキュバスの画像を表示
( function () {
var createUpper = Spriteset_Map.prototype.createUpperLayer;
Spriteset_Map.prototype.createUpperLayer = function () {
createUpper.call( this );
var sprite = new Sprite();
sprite.bitmap = ImageManager.loadEnemy( 'Succubus');
this.addChild(sprite);
}
})();
(上記のコードの意味としては元からある" Spriteset_Map.prototype.createUpperLayer"というものを呼んで、そこで更にbitmapを追加するというものです。詳しくは猫弓さんのページ参照 )
このプラグインを実行するとゲーム初期からマップに指定した画像が表示されます。
これを少し変えてタイルマップの上、キャラクターの下に画像を表示したいときは以下のようになります。
ex4) ゲーム開始時からタイルマップにサキュバスの画像を表示
( function () {
var createUpper = Spriteset_Map.prototype.createUpperLayer;
Spriteset_Map.prototype.createUpperLayer = function () {
createUpper.call( this );
var sprite = new Sprite();
sprite.bitmap = ImageManager.loadEnemy('Succubus'); //Succubusに画像名を入力
sprite.z = 3;
this._tilemap.addChild(sprite);
}
})();
赤地で表示した部分が新たに追加した箇所です。
「this._tilemap」というのはタイルマップに子要素を追加するというイメージとなります。
しかしこれだけではゲームを始めた時に画像とキャラクターがチカチカしてしまいます。
これはキャラクターのいるレイヤーと画像を表示しているレイヤーが被ってしまっているため、フレーム数分だけ入れ替わり表示されているという状況です。
これを解消するため、z軸の設定をする必要があるのです。
しかし、マップにはキャラクターの他にも色々な物が表示されます。
地面のタイルセット、木などの上に表示されるタイルセット、飛行船、フキダシアイコン、天候…これらはrpg_core.jsにてそのz座標が設定されています。
それによると
0 : 地面のタイルセット
1 : プライオリティが通常キャラの下に設定されているキャラクター
3 : プライオリティが通常キャラと同じに設定されているキャラクター
4 : 木などのキャラクターの上に表示されるタイルセット
5 : プライオリティが通常キャラの上に設定されているキャラクター
6 : 飛行船の影
7 : フキダシアイコン
8 : アニメーション
9 : 移動時に拡大&フェードアウトするの白い四角
となっています。
ちなみにこのz座標は整数である必要はなく、3.5などにすることも出来ます。
sprite.z = 3.5
にするとプライオリティが通常キャラと同じキャラクターより上、上に表示されるタイルセットより下、ということになります。
他にも戦闘画面で背景より上、バトラーより下に画像を表示させたい時には以下のようになります。(rpg_sprites.jsの2465行目"Spriteset_Battle.prototype.createBattleback")
ex5) 戦闘開始時に背景よりも上に、バトラーよりも下にサキュバスの画像を表示
( function () {
var _createBattleback = Spriteset_Battle.prototype.createBattleback ;
Spriteset_Map.prototype.createBattleback = function () {
_createBattleback.call( this );
var sprite = new Sprite();
sprite.bitmap = ImageManager.loadEnemy('Succubus'); //Succubusに画像名を入力
this._battleField.addChild(sprite);
}
})();
さらに前景の後ろ、後景の前に画像を表示させたいときは以下のようになります。
(rpg_sprites.jsの2465行目"Spriteset_Battle.prototype.createBattleback")
ex6) 戦闘開始時に後景よりも上に、前景よりも下にサキュバスの画像を表示
( function () {
var _createBattleback = Spriteset_Battle.prototype.createBattleback;
Spriteset_Battle.prototype.createBattleback = function() {
var margin = 32;
var x = -this._battleField.x - margin;
var y = -this._battleField.y - margin;
var width = Graphics.width + margin * 2;
var height = Graphics.height + margin * 2;
this._back1Sprite = new TilingSprite();
this._back2Sprite = new TilingSprite();
this._back1Sprite.bitmap = this.battleback1Bitmap();
this._back2Sprite.bitmap = this.battleback2Bitmap();
this._back1Sprite.move(x, y, width, height);
this._back2Sprite.move(x, y, width, height);
this._battleField.addChild(this._back1Sprite); //後景の画像
_createBattleback.call( this );
var sprite = new Sprite();
sprite.bitmap = ImageManager.loadEnemy('Succubus'); //Succubusに画像名を入力
this._battleField.addChild(sprite);
this._battleField.addChild(this._back2Sprite); //前景の画像
};
})();
5、addChildの順番と表示される画像の順番は同じ
この例から分かるようにaddChildを記述する順番によって画像の表示順が自動で決まります。
このため、どう画像を表示させたいのか、どのような順番にしたいのかを予め決めておき、プラグインを作る際にその順番通りにaddChildを指定した方がよりスムーズに作れます。
ちなみにspriteを宣言したり、位置をずらしたりということは関係しなく、あくまでaddChildの順番で決まるようです。
では、addChildで追加した画像がすべてがそうなるのかというとそうではなく、「どの要素に追加したか」によって順番が前後することがあるのです。
分かりやすい例としてはタイルマップに追加したbitmapはマップそのものに追加したbitmapの上に来ることはないことが挙げられます。
これは 「sprite.z = 100」のようにz軸を指定しても変わりません。
これは「addChildとは」の項目で説明した"要素に追加する"ということが関わっています。
例えば先ほどの画像を例にとりましょう。
「文章1」要素から見てそれを内包している「メインコンテンツ」要素は「親」と呼ばれますが、この親に追加した画像というのは「文章1」に追加した画像の下に来ることはありません。
タイルマップの例では文章1がタイルマップ、メインコンテンツがマップそのものに当たります。
言い換えれば子要素に追加した画像というのはz軸弄っても親要素の上には来れないのです。
6、bitmapを表示するタイミングはupdateで調整する
上記のプラグインの例では最初からbitmapが表示されますが、多くの場合は特定のタイミングで表示されて欲しいものだと思います。
プラグインを使用して特定のタイミングで表示するには、その状況に合ったupdateに条件を入れる方法があります。
ex7) スイッチ1がonになった時にマップ上にサキュバスの画像を表示
( function () {
var _Spriteset_Map_update = Spriteset_Map.prototype.update;
Spriteset_Map.prototype.update = function() {
_Spriteset_Map_update.call(this);
if($gameSwitches.value(1)){
var sprite = new Sprite();
sprite.bitmap = ImageManager.loadEnemy('Succubus');
this.addChild(sprite);
}
};
})();
この場合にはex2)のように" createUpperLayer"に何かを入れる必要はありません。
updateの部分のみでokです。
注目してほしいのは赤字の部分です。
まずはSpriteset_Mapの部分から。
今回はマップ上に表示する場合ですので、Spriteset_Mapのupdateに条件分岐であるif文を入れました。
もし戦闘の場面だったらSpriteset_Battle.prototype.updateに代入する必要があります。
次にif($gameSwitches.value(1)){ の部分。
これが画像を表示する条件になります。
if文の()の中に条件をスクリプトで入力します。
(各条件の記述法については当サイトの条件分岐のページでも紹介しています。)
このようにupdateに条件分岐を作っておけば1フレームごとに判断してくれるので条件が合ったときに即、表示されます。
欠点としては、条件分岐が数個程度なら良いのですが、より数が多くなり、数十個、数百個になった時にフレームごとに判別されるので、ゲームの処理の速度に影響してくることが考えられることです。
この辺りは工夫次第ですね。(そしてそれがプログラミングの面白さでもあるのかなと思います。)
自分は色々試してみて落ち着いたのはこのupdateを使う方法でした。
7、うーん、結局javascript出来るようにならないといけないってこと?
ここまで駆け足で解説をしてきましたが、専門的な話が多かったかもしれません。
プログラミングに馴染みのない方は読むのが辛かったかもしれませんね。
また、この記事ではbitmap表示の掴みだけで、実際の場面ではどうすればいいのか分からない、となることが多いでしょう。
ではどうすればいいのか?
結局、自分の思う通りのゲームを作ろうとするならjavascriptをある程度知っていて、かつ、ツクールMVのjsフォルダにあるjsファイルの内容をある程度知っている必要があるのです。
ツクールMVのスクリプトはjavascirptを知っているだけでは思うように操れません。
ツクール内の関数を知っていないといけない、もはや独自の言語と自分は感じています。
(もっと言えばQueryのようなライブラリですね)
そこまでツクールのスクリプトと触れ合って初めて思うようになるのかな、と思います。
8、"ツクールでプログラミングする"ということ
しかし、ツクールというのは「プログラミングできなくてもゲームが作れる!」という趣旨のツールですので、そこまでやるのは本末転倒な感じもします。
イベントコマンドのみで何とかなるならそれが一番ですし、楽しんで作るのが前提だと思うので…
ツクール界隈には絵が上手い方、プログラミング能力が高い方、ストーリーを作るのが上手い方、無料での素材提供に専念する方が多くいらっしゃり、そのいずれの方にも尊敬の念を感じずにはいられません。
しかし、デフォルトの素材のみで、スクリプトに頼ることもなく、ただ自分の思いのたけを形にしようという情熱を持ってゲームを作り、公開している方を拝見するとあるべき姿というのはこうなんだろうな、と個人的には思います。
9、スクリプトで表示した画像の変形方法(プロパティ)
spriteのプロパティについてはヘルプにも載っていますが、最後に改めて説明しておきます。
一番初めの例(ex1)
var bitmap = ImageManager.loadSvEnemy('Death');
var sprite = new Sprite(bitmap);
と定義したら
sprite.x : 画像のx座標を設定
ex)画像の位置を左から300pxの位置に
sprite.x = 300;
sprite.y :画像のy座標を設定
sprite.scale.x : 画像のx方向の拡大率を設定。1で100%。
ex)画像をx方向3倍の大きさにする
sprite.scale.x = 3
sprite.scale.y : 画像のy方向の拡大率を設定。1で100%。
sprite.anchor.x :画像のx座標の中心を設定。0で左端、1で右端となる。0~1で設定
sprite.anchor.y :画像のy座標の中心を設定。0で左端、1で右端となる。0~1で設定
sprite.width : 画像の横方向の表示領域を設定
sprite.height : 画像の縦方向の表示領域を設定
sprite.rotation : 画像の角度を設定。値はラジアン「角度*(Math.PI / 180)」で設定
ex)画像の角度を45度にする。
sprite.rotation = 45*(Math.PI / 180)
sprite.opacity : 画像の透明度を設定。値は0(透明)~255(くっきり)
sprite.blendMode : 画像の合成方法を設定。0:通常 1:加算 2:乗算 3:スクリーン
sprite.visible : true/falseで画像を表示/非表示
この指定はaddChildをする前にしても後にしても見た目には変わりません。
オリジナルシステムなどの製作依頼・相談があればプラグイン・マップ等個別製作依頼のページまでお願いします。