【連載】Starlingフレームワークを用いたStage3Dによる2Dアニメーション 第2回 Starlingフレームワークでスプライトシートを使う [Edit]

Starlingフレームワークで用いるアニメーションは、スクリプトだけでつくらなければならない訳ではありません。ムービークリップシンボルにつくり込んだフレームアニメーションも、コンテンツにもってこられます。そこで使うのが、スプライトシートです。スプライトシートの書出しは、Flash Professional CS6から備わりました。

  • サンプルファイル: スクリプト004 (Starling_02_004.zip/Flash CS6形式/約168KB)
    ダウンロードファイルには、Starlingフレームワークのライブラリは含まれていません。予めインストールしてください。

01 ムービークリップシンボルから[スプライトシートを生成]する

スプライトシートは、ムービークリップシンボルのフレームごとのアニメーション(図001)を、ビットマップイメージとしてまとめて書出したファイルです。Starlingフレームワークは、スプライトシートからフレームごとのイメージを切り出して、アニメーションとして再生できます。

図001■ムービークリップシンボルにつくったフレームアニメーション
図001上

図001下

スプライトシートを書出すには、[ライブラリ]でシンボルを右クリックしてショートカット(コンテクスト)メニューを開きます。そして、[スプライトシートを生成]のメニューを選びます(図002)。

図002■[ライブラリ]のシンボルを右クリックして[スプライトシートを生成]
図002

[スプライトシートを生成]のダイアログボックスが開きますので、書出しの設定を行います(図003)。スプライトシートはStarlingフレームワークで使うため、[データ形式]から[Starling]を選びます。[画像形式]は、ここでは背景を透過させる[PNG 32 bit]にします[*1]

図003■[スプライトシートを生成]のダイアログボックス
図003

[アルゴリズム]の選択や、[カット]と[フレームをスタック]の設定を変えると、[スプライトシート]タブで各フレームの画像の並び方や数が変わります(図004)。また、上部に[出力ファイルサイズ]が示されますので、書出すシンボルに応じて設定をいくつか変えて試してみるとよいでしょう。

図004■設定によって[スプライトシート]タブの表示や[出力ファイルサイズ]が変わる
図004

ファイルの保存場所は、[参照]ボタンで定めます。[書き出し]ボタンをクリックすると、スプライトシートが書出されます。でき上がるファイルはPNG画像とXMLデータで、デフォルトではFLAファイルと同じ名前です(図005)。これらのファイルを用いて、Starlingフレームワークでシンボルのアニメーションを再生します。

図005■書出されたPNG画像とXMLファイル
図005

[*1] 透過できない[PNG 24 bit]や[JPEG]などの[画像形式]を選ぶと、[背景色]が定まります。その後[PNG 32 bit]に戻しても、その前の[背景色]がそのまま残り透過されません(図006)。その場合には、改めて[背景色]をなし(アルファ0)にします。

図006■[PNG 32 bit]を選択しても前の[背景色]が残る
図006

02 スプライトシートのファイルを読込む

前回の「Starlingフレームワークで書く初めてのアニメーション」では、インスタンスを水平にスクロールさせました(図007)。今回はそのコンテンツに手を加え、インスタンスをスプライトシートに書出したムービークリップシンボルのフレームアニメーションに差替えます。Flashムービー(FLA)ファイルのフレームアクションとStarlingルートクラス(MySprite)は、それぞれつぎのスクリプト001と002のとおりでした(前回の解説には、それぞれスクリプト001と005として掲載)。

図007■ステージに置かれたビットマップが水平にスクロール
図007

スクリプト001■Starlingフレームワークを初期化するフレームアクション

// フレームアクション: メインタイムライン
import starling.core.Starling;
var myStarling:Starling = new Starling(MySprite, stage);
myStarling.start();

スクリプト002■インスタンスをステージ左端から右端に水平スクロールさせる

// ActionScript 3.0クラスファイル: MySprite.as
package  {
	import starling.display.Sprite;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.events.Event;
	import flash.display.BitmapData;
	public class MySprite extends Sprite {
		private var instance:Image;
		private var scrollWidth:Number;
		private var scrollLeft:Number;
		public function MySprite() {
			addEventListener(Event.ADDED_TO_STAGE, initialize);
		}
		private function initialize(eventObject:Event):void {
			var myBitmapData:BitmapData = new Pen();
			var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
			var stageWidth:int = stage.stageWidth;
			instance = new Image(myTexture);
			var instanceWidth:Number = instance.width;
			scrollWidth = stageWidth + instanceWidth;
			scrollLeft = -instanceWidth / 2;
			instance.pivotX = instanceWidth / 2;
			instance.pivotY = instance.height / 2;
			instance.x = scrollWidth;
			instance.y = stage.stageHeight / 2;
			addChild(instance);
			addEventListener(Event.ENTER_FRAME, scroll);
		}
		private function scroll(eventObject:Event):void {
			var nX:Number = instance.x;
			nX -= 5;
			if (nX < scrollLeft) {
				nX += scrollWidth;
			}
			instance.x = nX;
		}
	}	
}

PNG画像やXMLデータをコンテンツに組込むやり方はいくつか考えられます。ただ、その処理はStarlingフレームワークのお話しではなく、通常のActionScript 3.0によるデータの扱いです。ですから、簡単なご説明にとどめます。

まず、PNG画像はもとのFLAファイルで[ライブラリ]のビットマップと差替えます。[ライブラリ]でビットマップを選び、ショートカット(コンテクスト)メニューから[プロパティ]で[ビットマッププロパティ]ダイアログボックスを開きます(図008)。[読み込み]ボタンでスプライトシートのPNGファイルを指定して、[OK]ボタンをクリックすれば[ライブラリ]のビットマップが差替わります。

図008■[ライブラリ]のビットマップを[ビットマッププロパティ]ダイアログボックスで差替える
図008

この手順はビットマップを差替えただけですので、[クラス]の設定もそのまま残ります。したがって、前掲スクリプト001のフレームアクションおよび002のStarlingルートクラス(MySprite)は、何も手をくわえずにとりあえずエラーなく動きます。もちろん、複数のフレームの画像がひとつのファイルになっていますので、それらすべてが群れをなして動くアニメーションになります(図009)。

図009■スプライトシートにあるすべての画像がまとめてスクロールする
図009

このひとつのPNG画像からフレームごとにイメージを切り出し、シンボルのアニメーションに添って並べ替える情報がXMLデータに納められています(図010)。まず、TextureAtlas要素にはPNG画像ファイルのパスが定められています(imagePath属性)。つぎに、その子のSubTexture要素がひとつひとつのフレームを表します。name属性にはシンボル名 + 連番が定められています。連番は0から始まる4桁のフレーム番号です。そして、切り出す矩形領域の情報が続きます。

<TextureAtlas imagePath=画像ファイルのパス>
	<SubTexture name=シンボル名+連番 x=水平座標 y=垂直座標 width=幅 height=高さ/>
	<!-- ...[中略]... -->
</TextureAtlas>

図010■スプライトシートの画像をフレームに切り分けてアニメーションに並べ替えるためのXMLデータ
図010上

図010下

XMLデータは、URLLoaderクラスで読込みます。前掲スクリプト002のStarlingルートクラスに加える処理は、つぎのとおりです。まず、URLLoaderとURLRequestクラスのインスタンス(loaderとurl)をつくります。URLRequest()コンストラクタには、引数として読込むXMLファイルのパスを文字列("animation.xml")で渡します。つぎに、データの読込みが済んだとき(URLLoader.completeイベント)のリスナーメソッド(createInstance())を、EventDispatcher.addEventListener()メソッドで定めます。そして、URLLoader.load()メソッドにより、読込みを始めます。

import flash.display.BitmapData;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class MySprite extends Sprite {
	// ...[中略]...
	private function initialize(eventObject:Event):void {
		var loader:URLLoader = new URLLoader();
		var url:URLRequest = new URLRequest("animation.xml");
		loader.addEventListener(Event.COMPLETE, createInstance);
		loader.load(url);
	}
	private function createInstance(eventObject:Object):void {
		var _xml:XML = XML(eventObject.target.data);
		trace(_xml);  // 確認用
		// インスタンスを初期化する処理
	}
}

URLLoader.completeイベントのリスナーメソッド(createInstance())は、引数(eventObject)のEvent.targetプロパティで参照したURLLoaderオブジェクトのURLLoader.dataプロパティでデータをテキストとして取出します[*2]。そのテキストをXML()関数に引数として渡せば、XMLデータ(_xml)が得られます。なお、前掲スクリプト002でコンストラクタから呼出すメソッド(initialize())に定めたインスタンスを初期化する処理は、後でこのイベントリスナーのメソッドに移します。URLLoaderクラスを使ったデータの読込みについては、「ActionScript 3.0で外部テキストファイルを読込む」をご参照ください。

[*2] リスナーメソッド(createInstance())は、引数のイベントオブジェクト(eventObject)をEventではなくObjectで型指定しました。これは、このイベントオブジェクトがStarling界(starlingパッケージ)でなく、Flash界(flashパッケージ)のEventクラスに属するためです。

Flash界のEventクラスで型指定しようとすると、そのimport宣言(flash.events.Event)を加えなければならず、Starling界との重複が生じてしまいます。すると、Eventクラスをすべてフルネーム(完全修飾名)で書かなければなりません。引数をObject型で指定すれば、import宣言を加えずに済むのです。

03 スプライトシートから切出したテクスチャでインスタンスをつくる

スプライトシートとして書出された1枚絵の画像にXMLデータを組合わせると、任意のフレームのイメージが切出せます。この画像とXMLデータを組合わせて扱うのが、TextureAtlasクラスです。TextureAtlasオブジェクトは、TextureAtlas()コンストラクタメソッドに、画像からつくったTextureオブジェクトとXMLオブジェクトを引数に渡してつくります。

new TextureAtlas(Textureオブジェクト, XMLオブジェクト)

フレームを定めてスプライトシートからテクスチャを切出すのが、TextureAtlas.getTexture()メソッドです。引数には、XMLデータのSubTexture要素に定められたname属性の値を文字列で渡します。すると、そのname属性のフレームが切出されてTextureオブジェクトで返ります。

XMLデータを読込んだ後、画像とそのXMLオブジェクトからTextureAtlasインスタンスをつくって、最初のフレームのテクスチャをインスタンスにするには、つぎのようなスクリプトを加えます。

private function createInstance(eventObject:Object):void {
	var _xml:XML = XML(eventObject.target.data);
	var stageWidth:int = stage.stageWidth;
	var myBitmapData:BitmapData = new Pen();
	var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
	var myTextureAtlas:TextureAtlas = new TextureAtlas(myTexture, _xml);
	var frame:Texture = myTextureAtlas.getTexture("pen_walk0000");
	// instance = new Image(myTexture);
	instance = new Image(frame);
	// ...[中略]...
}

これで、スプライトシートに書出された最初のフレームがテクスチャに切出され、Imageインスタンスに納められたうえで、水平にスクロールします(図011)。最初のフレームだけですから、もとのムービークリップのフレームアニメーションそのものはまだ再生はされません。書替えたStarlingルートクラスは、つぎのスクリプト003のとおりです。

図011■スプライトシートの最初のフレームがテクスチャとしてインスタンスに加えられて水平スクロールする
図011

スクリプト003■スプライトシートの初めのフレームを水平スクロールさせる

// ActionScript 3.0クラスファイル: MySprite.as
package  {
	import flash.display.BitmapData;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import starling.display.Sprite;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;
	import starling.events.Event;
	public class MySprite extends Sprite {
		private var instance:Image;
		private var scrollWidth:Number;
		private var scrollLeft:Number;
		public function MySprite() {
			addEventListener(Event.ADDED_TO_STAGE, initialize);
		}
		private function initialize(eventObject:Event):void {
			var loader:URLLoader = new URLLoader();
			var url:URLRequest = new URLRequest("animation.xml");
			loader.addEventListener(Event.COMPLETE, createInstance);
			loader.load(url);
		}
		private function createInstance(eventObject:Object):void {
			var _xml:XML = XML(eventObject.target.data);
			var stageWidth:int = stage.stageWidth;
			var myBitmapData:BitmapData = new Pen();
			var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
			var myTextureAtlas:TextureAtlas = new TextureAtlas(myTexture, _xml);
			var frame:Texture = myTextureAtlas.getTexture("pen_walk0000");
			instance = new Image(frame);
			var instanceWidth:Number = instance.width;
			scrollWidth = stageWidth + instanceWidth;
			scrollLeft = -instanceWidth / 2;
			instance.pivotX = instanceWidth / 2;
			instance.pivotY = instance.height / 2;
			instance.x = scrollWidth;
			instance.y = stage.stageHeight / 2;
			addChild(instance);
			addEventListener(Event.ENTER_FRAME, scroll);
		}
		private function scroll(eventObject:starling.events.Event):void {
			var nX:Number = instance.x;
			nX -= 5;
			if (nX < scrollLeft) {
				nX += scrollWidth;
			}
			instance.x = nX;
		}
	}	
}

04 スプライトシートに書出したフレームアニメーションをインスタンスで再生する

TextureAtlasクラスにはテクスチャを取出す複数形のメソッドTextureAtlas.getTextures()メソッドがあります。引数にシンボル名の文字列を渡して呼出せば、シンボルの中のフレームごとの画像をTextureエレメントとして納めたVectorオブジェクトが得られます。これらの画像をフレームごとに切替えれば、アニメーションが再生されるという訳です。

var Vectorオブジェクト用変数:Vector.<Texture> = TextureAtlasオブジェクト.getTextures(シンボル名の文字列)

けれど、その仕組みを新たにスクリプトでつくるには及びません。そのためにStarling界のMovieClipクラスが備えられています。MovieClip()コンストラクタメソッドの引数には、Textureエレメントが納められたVectorオブジェクトを渡します。

new MovieClip(Textureベース型Vectorオブジェクト)

さて、これらを前掲Starlingルートクラス(スクリプト003)に加えて、インスタンス初期化の関数(createInstance())を書替えたのが、以下に抜書きしたスクリプトです。アニメーションさせるインスタンス(instance)の型指定はImageからMovieClipに変わります(import宣言にはStarlingフレームワークのクラスMovieClipを加え、逆にImageクラスは要らなくなります)。

そして、TextureAtlas.getTextures()メソッドから返されたTextureベース型のVectorオブジェクト(frames)を引数にして、MovieClipインスタンスがつくられ、プロパティ(instance)に納められました。でも、これだけではMovieClipインスタンスにもたせたフレームのテクスチャが切替わりません。つまり、前掲スクリプト003のアニメーションと同じく、初めのフレームのまま水平スクロールしてしまいます(前掲図011)。

では、MovieClipインスタンスのテクスチャを切替える処理を、DisplayObject.enterFrameイベントのリスナーメソッドに加えなければならないのでしょうか。内部的には、そのような結果にはなります。けれど、リスナーメソッドには手を触れません。ステートメントをもう1行だけ書けばよいのです。

Starling.juggler.add(MovieClipオブジェクト)

このメソッドにより、StarlingオブジェクトがもつJugglerというオブジェクトに、MovieClipインスタンスを加えます[*3]。すると、StarlingオブジェクトがDisplayObject.enterFrameイベントを受取るたびに、Jugglerの中のMovieClipインスタンスにTextureオブジェクトを切替えるよう命じるのです(なお、Starlingオブジェクトを参照するので、Starlingクラスのimport宣言も加えます)。

// private var instance:Image;
private var instance:MovieClip;
// ...[中略]...
private function createInstance(eventObject:Object):void {
	var _xml:XML = XML(eventObject.target.data);
	// ...[中略]...
	var myBitmapData:BitmapData = new Pen();
	var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
	var myTextureAtlas:TextureAtlas = new TextureAtlas(myTexture, _xml);
	// var frame:Texture = myTextureAtlas.getTexture("pen_walk0000");
	var frames:Vector.<Texture> = myTextureAtlas.getTextures("pen_walk");
	// instance = new Image(frame);
	instance = new MovieClip(frames);
	// ...[中略]...
	Starling.juggler.add(instance);
	// ...[中略]...
}

これで、フレームごとにMovieClipインスタンスのテクスチャが切替わり、フレームアニメーションが再生されながら水平に移動します(図012)。でき上がったStarlingルートクラス(MySprite)全体は、以下のスクリプト004のとおりです。

図012■スプライトシートのフレームアニメーションがMovieClipインスタンスで再生される
図012

スクリプト004■TextureAtlasオブジェクトのフレームアニメーションをMovieClipインスタンスで再生する

// ActionScript 3.0クラスファイル: MySprite.as
package  {
	import flash.display.BitmapData;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.display.MovieClip;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;
	import starling.events.Event;
	public class MySprite extends Sprite {
		private var instance:MovieClip;
		private var scrollWidth:Number;
		private var scrollLeft:Number;
		public function MySprite() {
			addEventListener(Event.ADDED_TO_STAGE, initialize);
		}
		private function initialize(eventObject:Event):void {
			var loader:URLLoader = new URLLoader();
			var url:URLRequest = new URLRequest("animation.xml");
			loader.addEventListener(Event.COMPLETE, createInstance);
			loader.load(url);
		}
		private function createInstance(eventObject:Object):void {
			var _xml:XML = XML(eventObject.target.data);
			var stageWidth:int = stage.stageWidth;
			var myBitmapData:BitmapData = new Pen();
			var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
			var myTextureAtlas:TextureAtlas = new TextureAtlas(myTexture, _xml);
			var frames:Vector.<Texture> = myTextureAtlas.getTextures("pen_walk");
			instance = new MovieClip(frames);
			var instanceWidth:Number = instance.width;
			scrollWidth = stageWidth + instanceWidth;
			scrollLeft = -instanceWidth / 2;
			instance.pivotX = instanceWidth / 2;
			instance.pivotY = instance.height / 2;
			instance.x = scrollWidth;
			instance.y = stage.stageHeight / 2;
			addChild(instance);
			Starling.juggler.add(instance);
			addEventListener(Event.ENTER_FRAME, scroll);
		}
		private function scroll(eventObject:starling.events.Event):void {
			var nX:Number = instance.x;
			nX -= 5;
			if (nX < scrollLeft) {
				nX += scrollWidth;
			}
			instance.x = nX;
		}
	}	
}

05 スプライトシートを活用しよう

ひとつのスプライトシートを、複数のシンボルからつくることもできます。[ライブラリ]でそれらのシンボルを選んだうえで、右クリックのショートカット(コンテクスト)メニューから[スプライトシートを生成]ダイアログボックスを開けばよいのです(図013)。スプライトシートに加えるシンボルは、アニメーションを含むものにかぎりません。背景のような静止画も、まとめてスプライトシートにしてよいのです。

図013■複数のシンボルをまとめて[スプライトシートを生成]する
Starling_02_001.gif

書出された画像はひとつにまとまっても、XMLデータのSubTexture要素に定められたname属性で、シンボル名をもとにして必要なイメージが切出せます。ここでは、ふたつの背景画像のシンボル(ice_01とice_02)をスプライトシートに加えました。

<SubTexture name="ice_010000" x="0" y="0" width="242" height="82"/>
<SubTexture name="ice_020000" x="0" y="82" width="252" height="72"/>
<SubTexture name="pen_walk0000" x="0" y="154" width="79" height="108" frameX="0" frameY="0" frameWidth="82" frameHeight="109"/>
<!-- ...[後略]... -->

静止画であれば、すでにご説明したとおり、TextureAtlas.getTexture()メソッドで取出したTextureオブジェクトを、Image()コンストラクタの引数に渡して、表示リストに加えればよいでしょう(図014)。ここで知っていただきたいのは、シンボルのテクスチャの取出し方ではなく、複数のシンボルをひとつのスプライトシートにまとめる意義です。

var _background:Texture = myTextureAtlas.getTexture(name属性値);
var myImage:Image = new Image(_background);
addChild(myImage);

図014■スプライトシートからシンボルのアニメーションに加えて背景も取出した
図014

原則として、異なる画像イメージは、表示リストの重ね順にしたがって、それぞれ分けて描かれます。ところが、テクスチャアトラス(TextureAtlasオブジェクト)は、Starlingフレームワークがひとつのイメージとして扱うため、まとめて一気に描き出せるのです。描画速度が強みのStarlingにとって、このポイントは見逃せません。つまり、アニメーションのあるなしにかかわらず、画像イメージはTextureAtlasオブジェクトにまとめるのがお得です。

ただし、Starlingフレームワークが表示リストをもっている、ということに注意しなければなりません。一気に描きたい表示オブジェクトをひとつのテクスチャアトラスにまとめても、重ね順のどこか間に別の画像イメージが挟まると、そこで描画はふたつに分けられてしまいます。また、すべての画像イメージをひとつのテクスチャアトラスにまとめることは、サイズの制限[*4]や扱いの都合から現実的ではないでしょう。そこでいくつかのテクスチャアトラスに分ける場合も、重ね順を必ず考え合わせてください。

たとえば、下図015の表示リストで、表示オブジェクトは左から右に重なりが上になるとします。これらの表示オブジェクトのテクスチャを、ふたつのテクスチャアトラスにどう分けたらよいかです。上段と下段のふたつの分け方で、オブジェクトの色はテクスチャアトラスの違いを表します。上段の図は、表示オブジェクトの重ね順、つまり表示リストの順序に対して、テクスチャアトラスが交互に違っています。すると、オフジェクトひとつ処理するごとにテクスチャの状態が変わるため、描画もその都度分けて行われることになります。これを下段のようにまとめ直せば、描画は2回の処理で済むのです[*5]

図015■テクスチャアトラスは表示オブジェクトの重ね順でまとめる
図015上
図015中
図015下

[*3] 正確には、静的プロパティStarling.jugglerでStarlingオブジェクトがもつJugglerオブジェクトを参照し、Juggler.add()メソッドによりMovieClipインスタンスを加えています。

[*4] テクスチャアトラスの大きさは、2048×2048ピクセル以内とされます(一部のモバイルデバイスにおけるテクスチャの最大値)。それより大きなテクスチャは、分けなければなりません。

[*5] Starlingの描画をいかに最適化するかについては、「Starlingフレームワークで表示オブジェクトの描画を最適化する」をお読みください。

 

コメント

この記事にコメントを書く

記事に対するテクニカルな質問はご遠慮ください(利用規約)。

その他の記事