「Starling」は、Flash Player 11の新しい描画機能「Stage3D」[*1]にもとづいて作られた2次元の描画用フレームワークです。GPU[*2]を用いることにより、速くて滑らかな描画ができます。オープンソースのフレームワークをダウンロードしてインストールします。インストールの方法については、「Starlingフレームワークをインストールする」をお読みください。

[*1]「Stage3D」は、2次元・3次元に限らず、スクリーンの描画を速め、きめ細かで滑らかなアニメーションを実現する技術です。Flash Player 11から備わりました。ハードウェアの「GPU」を使うことによって、描画の処理を飛躍的に高めます。ハードウェアにそもそもGPUが備わっていない場合や、チップセットやドライバが古いなどの理由でGPUを使えないときには、CUPによる描画に切り替わります。Stage3Dについて詳しくは、「Stage3Dコンテンツ制作入門」をご参照ください。

[*2]「GPU」(Graphics Processing Unit)は、コンピュータの処理の中でも負荷が高くパフォーマンスに影響する画像処理の演算を扱うプロセッサです。単に画面を描くことだけでなく、その前の座標空間における変換やカラーの演算など描画に伴う大量の処理をCPUに代わって行います。そのためGPUを用いると、きめ細かで滑らかなアニメーションが表現できます。

01 Stage3DとStarlingフレームワーク

Stage3DはGPUに処理を命じるため、プログラミングはアセンブラ(機械語)に近く、お世辞にも親しみやすいとはいえません(図001)。けれど、フレームワークを使えば、おなじみのActionScript 3.0に近い感覚でスクリプトが書けます。特に2次元の描画やアニメーションを、これまでのActionScript 3.0と同じスタイルで扱えるのがStarlingフレームワークです。つまり、Stage3DとActionScript 3.0標準のスクリプティングとを橋渡しする通訳が、Starlingフレームワークだといえるでしょう。

Starlingに限らずStage3Dのコンテンツの描かれる場所は、Flashのステージとは階層が違います。Stage3Dは全てのオブジェクトを、Flashのステージの背面に置きます(図002)。

02 ActionScript 3.0を使った簡単なアニメーション

まずは、これまでのActionScript 3.0で簡単なアニメーションのムービーを作りましょう。[ライブラリ]のビットマップをステージに置いて、水平にスクロールさせます(図003)。アニメーションはEvent.ENTER_FRAMEイベントを使ったスクリプトで行います。

また、ビットマップはあらかじめステージには置くのでなく、スクリプトで[ライブラリ]からインスタンスをダイナミックに作ります。その準備として、[ビットマッププロパティ]ダイアログボックスでビットマップに[クラス]を設定しておきます(図004)。

[ライブラリ]のビットマップからインスタンスを作るには、インスタンスに設定したクラス(Pen)をnew演算子とともにコンストラクタとして呼び出します。また、インスタンスを代入する変数のデータ型は、BitmapDataで定めます。ただし、ビットマップの(BitmapData)インスタンスは、そのままではステージに置けません。

Bitmapクラスという容れ物になるオブジェクトにビットマップのイメージ(BitmapDataインスタンス)を納め、BitmapインスタンスをDisplayObjectContainer.addChild()メソッドでタイムラインに加えます。ビットマップ(BitmapDataインスタンス)は、Bitmapオブジェクトを作るとき、コンストラクタメソッドの引数に渡します。

var ビットマップ用変数:BitmapData = new クラス();
var Bitmap用変数:Bitmap = new Bitmap(ビットマップ用変数);
addChild(Bitmap用変数);

その上で、BitmapインスタンスにEventDispatcher.addEventListener()メソッドで、DisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナー関数を加えます。でき上がったフレームアクションが、次のスクリプト001です。これまでご説明した処理は、初期設定用の関数(initialize())に定めて呼出しています。

スクリプト001 [ライブラリ]のビットマップから作ったインスタンスを水平にスクロールする(001_frame_action)

// フレームアクション // 変数の宣言 var nWidth:Number; var nScrollWidth:Number; // 初期化の関数を呼出す initialize(); // 初めに1度呼出す初期化の関数 function initialize():void { var myBitmapData:BitmapData = new Pen(); var instance:Bitmap = new Bitmap(myBitmapData); addChild(instance); instance.addEventListener(Event.ENTER_FRAME, scroll); instance.y = (stage.stageHeight - instance.height) / 2; nWidth = instance.width; nScrollWidth = stage.stageWidth + nWidth; } // 毎フレーム呼出す関数(イベントリスナー) function scroll(eventObject:Event):void { var instance:DisplayObject = eventObject.currentTarget as DisplayObject; var nX:Number = instance.x - 5; if (nX < -nWidth) { nX += nScrollWidth; } instance.x = nX; }

アニメーションのリスナー関数(scroll())は、Bitmapインスタンスの位置を毎フレーム水平に動かし、ステージの端から消えたら反対側の端に戻しています。[ムービープレビュー]を確かめると、ステージに置かれたビットマップのイメージ(実際にはイメージを含んだBitmapインスタンス)が水平にスクロールを続けます(図003)。

03 スクリプトをStarlingフレームワークに移行する

前述01のとおりStarlingフレームワークは、定義済みActionScript 3.0とは別のステージにコンテンツを描画します(図002)。つまり、FlashのタイムラインにStarlingのオブジェクトは置けません。まず、StarlingのステージにFlashのメインタイムラインに当たるオブジェクトを、クラスで定義しなければなりません(これをStarlingフレームワークのルートクラスと呼ぶことにします)。

ActionScript 3.0では、クラスは次のような形でActionScript(AS)ファイルに定めます。入れ子の作りになっていて、パッケージ(package)の中にクラス(class)を定義し、そのクラスの中にプロパティ(変数)やメソッド(関数)を定めます。ややこしく見えるかもしれません。でも、この後の作業でわかるとおり、フレームアクションをクラスに入れて、パッケージで包むイメージです。クラス名とコンストラクタメソッドの名前、およびASファイルの名前は全て同じにします。

// ActionScript 3.0クラス定義ファィル: クラス名.as package { // クラスをimport宣言 import starling.display.Sprite; // その他使うクラスをすべて完全パス(完全修飾名)で宣言 public class クラス名 extends Sprite { // プロパティ(変数)の宣言 var プロパティ名:データ型; // コンストラクタメソッドの定義 public function クラス名() { // おもに初期化の処理 } // メソッド(関数)の定義 function メソッド名(引数:データ型):戻り値データ型 { // メソッドの処理 } } }

本稿では、クラス定義の細かい意味については解説を省きます[*3]。ActionScript 3.0のフレームアクションを、どうやってStarlingフレームワークに移すかの手順に絞ってご説明するためです。前掲フレームアクション(スクリプト001)をStarlingフレームワークに移す場合、クラス定義(MySprite)の雛形は次のようになります。

// Starlingルートクラスの定義 package { import flash.display.BitmapData; import starling.display.Sprite; import starling.display.DisplayObject; import starling.display.Image; import starling.textures.Texture; import starling.events.Event; public class MySprite extends Sprite { // [1]プロパティの宣言 public function MySprite() { // コンストラクタメソッド // [2]初期化の処理 } // [3]メソッドの定義 } }

前掲スクリプト001をStarlingフレームワークに移すときに必要なクラスのimport宣言が具体的に加わりました。この宣言には、バッケージから始まるクラスの完全パス(完全修飾名)を書かなければなりません。バッケージはActionScript 3.0リファレンスガイドで確かめます。例えば、BitmapDataクラスはflash.display.BitmapDataです(図005)。Starlingフレームワークのパッケージは、全てstarlingで始まります。

それでは、前掲フレームアクション(スクリプト001)のステートメントを、クラス定義(MySprite)の雛形に移行します。移す場所を[1]プロパティの宣言と、コンストラクタメソッド内の[2]初期化の処理、および[3]メソッドの定義の3つに分けて考えましょう。

[1]スクリプト001の変数宣言は、そのままStarlingルートクラスのプロパティの宣言にします。

// [1]プロパティの宣言 var nWidth:Number; var nScrollWidth:Number;

[2]スクリプト001の初期化の関数(initialize())は、Starlingルートクラスのコンストラクタメソッドの中から呼出します。

public function MySprite() { // コンストラクタメソッド // [2]初期化の処理 initialize(); }

[3]スクリプト001の2つの関数(initialize()とscroll())は、Starlingルートクラスのメソッドとして、ひとまずそのまま移します。

// [3]メソッドの定義 function initialize():void { var myBitmapData:BitmapData = new Pen(); var instance:Bitmap = new Bitmap(myBitmapData); addChild(instance); instance.addEventListener(Event.ENTER_FRAME, scroll); instance.y = (stage.stageHeight - instance.height) / 2; nWidth = instance.width; nScrollWidth = stage.stageWidth + nWidth; } function scroll(eventObject:Event):void { var instance:DisplayObject = eventObject.currentTarget as DisplayObject; var nX:Number = instance.x - 5; if (nX < -nWidth) { nX += nScrollWidth; } instance.x = nX; }

そして、初期化のメソッド(initialize())に、大きく2つの修正を加えます。まず、ビットマップのイメージとその容れ物にする2つのオブジェクトは、Starlingフレームワーク専用のクラスに置換えなければなりません(表001)。Starlingフレームワークでは、ビットマップイメージはTextureオブジェクトとして作り、Imageインスタンスの容れ物に納めてStarlingステージに置きます。

表001 ActionScript 3.0とStarlingフレームワークでビットマップを扱うためのクラス
オブジェクト クラス
ActionScript 3.0定義済み Starlingフレームワーク
ビットマップイメージ BitmapData Texture
ステージに表示する容れ物 Bitmap Image

もっとも、[ライブラリ]のビットマップから作れるのは、BitmapDataインスタンスです。そこで、静的メソッドTexture.fromBitmapData()を使うと、BitmapDataオブジェクトから同じイメージのTextureインスタンスができます。そのTextureインスタンスをImageクラスのコンストラクタに渡せば、Starlingのステージに置くImageオブジェクトが作れます。インスタンスをタイムラインに加えるのは、前掲スクリプト001と同じくDisplayObjectContainer.addChild()メソッドです。

var Texture用変数:Texture = Texture.fromBitmapData(BitmapDataオブジェクト); var Image用変数:Image = new Image(Texture用変数); addChild(Image用変数);

そしてつぎの修正として、DisplayObject.stageプロパティの参照はいったん取り止め、数値を直に打込みます。このスクリプトのままでは、StarlingのStageオブジェクトが参照できないからです。できあがったStarlingルートクラス(MySprite)の定義は次のスクリプト002のとおりです。前掲スクリプト001から移行した後修正した部分は、コメントとして残しました。

スクリプト002 Starlingルートクラスの定義(002_Starling)

// ActionScript 3.0クラス定義ファイル: MySprite.as package { import flash.display.BitmapData; import starling.display.Sprite; import starling.display.DisplayObject; import starling.display.Image; import starling.textures.Texture; import starling.events.Event; public class MySprite extends Sprite { var nWidth:Number; var nScrollWidth:Number; public function MySprite() { initialize(); } function initialize():void { var myBitmapData:BitmapData = new Pen(); var myTexture:Texture = Texture.fromBitmapData(myBitmapData); // var instance:Bitmap = new Bitmap(myBitmapData); var instance:Image = new Image(myTexture); addChild(instance); instance.addEventListener(Event.ENTER_FRAME, scroll); instance.y = 50; // (stage.stageHeight - instance.height) / 2; nWidth = instance.width; nScrollWidth = 320; // stage.stageWidth + nWidth; } function scroll(eventObject:Event):void { var instance:DisplayObject = eventObject.currentTarget as DisplayObject; var nX:Number = instance.x - 5; if (nX < -nWidth) { nX += nScrollWidth; } instance.x = nX; } } }

Flash Professional CS6では、ASファイルに定めたクラスはFLAファイルから呼出してSWFコンテンツをパブリッシュします。Starlingフレームワークを使う場合は、FLAファイルからStarlingクラスのコンストラクタメソッドを呼出します。コンストラクタの第1引数はStarlingルートクラスの参照、第2引数にはFlashのStageオブジェクト(DisplayObject.stageプロパティ)を渡します。

var 変数:Starling = new Starling(Starlingルートクラス, stage);

そして、Starlingインスタンスに対するStarling.start()メソッドの呼出しにより、Starlingフレームワークの描画を始めます。前掲スクリプト002のStarlingルートクラス(MySprite)を使ったフレームワークの初期化は、次のスクリプト003のフレームアクションで行います。

スクリプト003 Starlingインスタンスをつくるフレームアクション

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

[ムービープレビュー]を試すと、前掲スクリプト001と同じアニメーションが、StarlingフレームワークによりStage3Dで再生されます。

ここでひとつ確認しておきましょう。インスタンスの座標を決めるDisplayObject.xやDisplayObject.yプロパティや、タイムラインに加えるDisplayObjectContainer.addChild()メソッド、あるいはイベントリスナーを登録するEventDispatcher.addChild()メソッドは、Starlingルートクラスのスクリプト002でもフレームアクションのスクリプト001とまったく同じように用いています。けれど実は、使われているクラスがStarlingルートクラスとフレームアクションとで別なのです(表002)。

表002 ActionScript 3.0とStarlingフレームワークで名前は同じでも異なるプロパティやメソッドなど
プロパティやメソッドなど クラス
ActionScript 3.0定義済み Starlingフレームワーク
x/y/width/height/stageプロパティ flash.display.DisplayObject starling.display.DisplayObject
addChild()メソッド flash.display.DisplayObjectContainer starling.display.DisplayObjectContainer
addEventListener()メソッド flash.events.EventDispatcher starling.events.EventDispatcher
enterFrameイベント flash.display.DisplayObject starling.display.DisplayObject
ENTER_FRAME定数 flash.events.Event starling.events.Event

前掲スクリプト002から、import宣言を一部以下に抜出しました。これらが実際に使われるクラスです。ActionScript 3.0定義済みのクラスと名前は同じでも、パッケージがstarlingで始まっていますので異なることを示します。これらのクラスがいわば通訳になって、フレームアクションと同じように書いたスクリプトを、Stage3Dの処理に直して命じているのです。

import starling.display.Sprite; import starling.display.DisplayObject; import starling.events.Event;

[*3] クラスの定義について詳しくは、gihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第18回「カスタムクラスを定義する」をお読みください。

04 StarlingフレームワークのルートクラスからStageオブジェクトを参照する

前掲スクリプト001のフレームアクションから002のクラス定義に書替えるとき、DisplayObject.stageプロパティへの参照を除きました。それは、Starlingルートクラスのコンストラクタメソッドが呼出されたときには、そのインスタンスがまだStageオブジェクトの(表示リストの)子として加えられておらず、プロパティの値がnullになってしまうからです。

Stageオブジェクトの子インスタンスに加えられてから行いたい処理は、ActionScript 3.0定義済みのイベントと同じEvent.ADDED_TO_STAGEにリスナーメソッドとして加えます。前掲スクリプト002の場合は、初期化のメソッド(initialize())をイベントリスナーにすればよいでしょう。修正を終えたクラス定義は、後にスクリプト004として掲げます。手を加えるステートメントは次のとおりです。

public function MySprite() { // initialize(); this.addEventListener(Event.ADDED_TO_STAGE, initialize); } // function initialize():void { function initialize(eventObject:Event):void { // ...[中略]... instance.y = (stage.stageHeight - instance.height) / 2; // ...[中略]... nScrollWidth = stage.stageWidth + nWidth; }

初期化のメソッド(initialize())をイベントリスナーとして呼出すときは、イベントオブジェクトを引数(eventObject)にとらなければなりません。これで晴れてDisplayObject.stageプロパティでステージ(Stageオブジェクト)が参照できますので、前掲フレームアクション(スクリプト001)と同じように、ステージの幅や高さを位置合わせに使えます。

以上の修正を前掲スクリプト002に加えてでき上がったStarlingルートクラス(MySprite)が、次のスクリプト004です。FLAファイルのフレームアクションは前掲スクリプト003のままです。

スクリプト004 ステージの表示リストに加わってから初期化を始めるStarlingルートクラスの定義(003_Starling_standard)

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

実はこのスクリプトには、もう1つだけ手が加わっています。クラスに定義したプロパティやメソッドに、アクセス制御の属性キーワードprivateを添えたことです。アクセス制御というのは、各プロパティやメソッドにクラスの外からアクセスを許すかどうか、許すとしたらどの範囲かを定めることです。SNSで情報の公開範囲を決めようなものと捉えればよいでしょう。private属性はもっとも厳しく、クラス外からのアクセスを禁じます[*4]。

すでに前掲スクリプト002でもわかるように、このprivate属性を添えなくても、スクリプトは問題なく動きます。ただ、クラス定義ではアクセス制御の属性は狭く定めた方が、一般には意図しない問題が生じにくく、また発生しても調べやすいといえます。

[*4] コンストラクタメソッドに添えられたpublicは範囲がもっとも広く、どこからでもアクセスできます。ActionScript 3.0では、コンストラクタの属性はpublicでなければなりません。詳しくは、前出注[*3]「カスタムクラスを定義する」をお読みください。