Xelf

Xelfは、ゲームをはじめとするインタラクティブコンテンツの作成を手助けするフレームワークです。イベント駆動やタスクを用いたアーキテクチャと、Dependency InjectionやConvention over Configurationの導入により、効率よく開発を行えます。

最新情報

ダウンロード

導入方法

Flex Builder 2 の場合

  1. Flex Builder上で、Xelfを使用したいプロジェクトを開くか、作成します。
  2. ASReflectを、記載されている導入方法に従って導入します。
  3. AOContainerを、記載されている導入方法に従って導入します。
  4. プロジェクトのプロパティを表示し、「ActionScript ビルドパス」内の「ライブラリパス」タブを開きます。
  5. 「SWCの追加」をクリックし、表示されたダイアログ内で、ダウンロードした「xelf-xxx.swc」(xxxはバージョン)を指定して追加します。
  6. 「OK」を押して設定を保存すれば完了です。これでXelf(org.libspark.xelfパッケージ)が使えるようになります。

必要な環境

ファーストステップ

ファーストステップでは、シューティングを想定して、キー入力で上下左右に動き、弾を発射できるプレイヤーを、Xelfを用いて作ってみます。

スタートアップコードの記述

DocumentRootにあたるクラスに以下のようなコードを記述すると、Xelfが起動して実行されるようになります。

package
{
    import flash.display.Sprite;
    import org.libspark.xelf.Xelf;

    public class Sample extends Sprite
    {
        public function Sample()
        {
            var config:XML =

            <game>
            <scene name='main'>
            <!-- ここに設定を記述 -->
            </scene>
            </game>;

            new Xelf(config, this).run();
        }
    }
}

基本的にこのコードを弄る必要はありませんが、XMLによる設定は「ここに設定を記述」と書いてある場所に追加していきます。

移動タスクの作成

早速タスクを作りますが、最初にインターフェイスを作っておくと、他のタスクがこのタスクを参照する場合に実装クラスに依存しなくて済むので便利です。ここではActorというインターフェイスを作っておきます。

public interface Actor
{
    function get x():Number;
    function get y():Number;
}

このActorインターフェイスを実装し、xy座標を斜めに動くタスクを作ってみます。「update」という名前のメッセージが毎フレーム送られてくるので、そのメッセージハンドラを作って処理をします。「onUpdate」という名前のメソッドを作れば自動的にメッセージハンドラとみなされます。

public class PlayerTask implements Actor
{
    private var _x:Number = 10;
    private var _y:Number = 10;
    private const speed:Number = 5;
    
    public function get x():Number
    {
        return _x;
    }
    
    public function get y():Number
    {
        return _y;
    }
    
    public function onUpdate():void
    {
        // 移動
        _x += speed;
        _y += speed;
    }
}

描画タスクの作成

このままでは画面に表示されないので、描画をするタスクを作ってみます。先程のActorインターフェイスを使って、Shapeクラスで描いた白い丸に位置を反映します。「Actor」型のプロパティを宣言しておけば、自動でActorを実装したタスクがDIされます。

白い丸を描くには、レイヤーを使用することが出来ます。「layer」という名前で「DisplayObjectContainer」型のプロパティを宣言すれば、自動的にレイヤーがDIされ、そこに対して描画可能になります。

キャラクタが作られると「created」、放棄されると「destroy」メッセージが送られてくるので、それを利用して初期化と解体処理を行います。

import flash.display.DisplayObjectContainer;
import flash.display.Shape;

public class ActorViewTask
{
    // Actorが自動でDIされる
    public var actor:Actor;
    // レイヤーが自動でDIされる
    public var layer:DisplayObjectContainer;
    
    // 弾グラフィック
    private var view:Shape;
    
    public function onCreated():void
    {
    	// 白い丸を描く
        view = new Shape();
        view.graphics.beginFill(0xffffff);
        view.graphics.drawCircle(0, 0, 6);
        view.graphics.endFill();
        // レイヤーに追加
        layer.addChild(view);
    }
    
    public function onDestroyed():void
    {
        // レイヤーから取り除く
        layer.removeChild(view);
    }
    
    public function onUpdate():void
    {
        // 位置を反映
        view.x = actor.x;
        view.y = actor.y;
    }
}

設定の記述と実行

次のような設定を記述して、キャラクタにタスクを追加します。DIは自動的に行われるので、設定を記述する必要はありません。

<character name='player'>
    <task class='PlayerTask'/>
    <task class='ActorViewTask'/>
</character>

これで実行をすると、白い丸が斜めに移動し、期待通りの結果を得られます。

ただし、予めコードの何処かで

PlayerTask; ActorViewTask;

というようにクラスを宣言しておかないと、SWFファイルにリンクされず、クラスが見つからないというエラーが出るので注意してください。以後全てそうです。

キーボードからの入力を受け付ける

今度は白い丸が勝手に動くのではなく、キーボードの入力を受けて動くようにしてみます。入力に応じて「inputLeft」「inputRight」といったメッセージが送られてくるので、先程のupdateは消して書き直します。

public class PlayerTask implements Actor
{
    private var _x:Number = 100;
    private var _y:Number = 100;
    private const speed:Number = 5;
    
    public function get x():Number
    {
        return _x;
    }
    
    public function get y():Number
    {
        return _y;
    }
    
    public function onInputLeft():void
    {
        _x -= speed;
    }
    
    public function onInputRight():void
    {
        _x += speed;
    }
    
    public function onInputUp():void
    {
        _y -= speed;
    }
    
    public function onInputDown():void
    {
        _y += speed;
    }
}

このままではまだキーボードからの入力がメッセージとしてやってこないので、KeyboardInputTaskを利用してメッセージを送出します。KeyboardInputTaskはorg.libspark.xelf.core.tasks.KeyboardInputTaskに用意されています。

<character name='player'>
    <task class='org.libspark.xelf.core.tasks.KeyboardInputTask'>
        <method name='registerKeyDown'><arg>'Left'</arg></method>
        <method name='registerKeyDown'><arg>'Right'</arg></method>
        <method name='registerKeyDown'><arg>'Up'</arg></method>
        <method name='registerKeyDown'><arg>'Down'</arg></method>
    </task>
    <task class='PlayerTask'/>
    <task class='ActorViewTask'/>
</character>

registerKeyDownに対するメソッドインジェクションで、どのキーを押したらメッセージを送出するかを指定します。

これで実行すると、白い丸がキーボードで操作できるようになります。

弾タスクの作成

今度は白い丸が弾らしきものを発射できるようにしてみます。そこでまず、弾タスクを作ります。このとき、弾は画面外に出たら消える必要があるので、画面外に出た際に「destroyInstance」メッセージを自分のメッセンジャーに対して送ります。自分のメッセンジャーは「self」という名前で「Messenger」型のプロパティを宣言するとDIされます。

import org.libspark.xelf.core.Messenger;
import org.libspark.xelf.core.messages.CharacterMessage;

public class ShotTask implements Actor
{
    private var _x:Number = 200;
    private var _y:Number = 100;
    private const speed:Number = 5;
    
    // 自分のメッセンジャーがDIされる
    public var self:Messenger;
    
    public function get x():Number
    {
        return _x;
    }
    
    public function get y():Number
    {
        return _y;
    }
    
    public function onUpdate():void
    {
        _y -= speed;
        if (_y < 0) {
        	// 画面外に出たらdestroyInstanceメッセージを送る
            self.sendMessage(new CharacterMessage(CharacterMessage.DESTROY_INSTANCE));
        }
    }
}

弾は画面上に複数出現するので、instanceを使用して設定を書きます。

<character name='shot'>
    <instance>
        <task class='ShotTask'/>
        <task class='ActorViewTask'/>
    </instance>
</character>

弾発射処理の作成

PlayerTaskに、スペースキーを押したら弾を発射するための処理を追加します。弾を発射するということは、弾キャラクタの新しいインスタンスを作るということなので、弾キャラクタのメッセンジャーに対して「createInstance」メッセージを送ります。弾キャラクタのメッセンジャーは、キャラクタ名と同じ「shot」という名前で「Messenger」型のプロパティを宣言するとDIされます。

import org.libspark.xelf.core.Messenger;
import org.libspark.xelf.core.messages.CharacterMessage;

public class PlayerTask implements Actor
{
    private var _x:Number = 100;
    private var _y:Number = 100;
    private const speed:Number = 5;
    
    // 弾キャラクタのメッセンジャーがDIされる
    public var shot:Messenger;
    
    public function get x():Number
    {
        return _x;
    }
    
    public function get y():Number
    {
        return _y;
    }
    
    public function onInputLeft():void
    {
        _x -= speed;
    }
    
    public function onInputRight():void
    {
        _x += speed;
    }
    
    public function onInputUp():void
    {
        _y -= speed;
    }
    
    public function onInputDown():void
    {
        _y += speed;
    }
    
    public function onInputSpace():void
    {
    	// createInstanceメッセージを送る
        shot.sendMessage(new CharacterMessage(CharacterMessage.CREATE_INSTANCE));
    }
}

スペースキーを押したらメッセージを送るようにKeyboardInputTaskを設定します。ここでregisterKeyDownではなくregisterKeyPushを使うと、キーが押された瞬間のみメッセージが配信されるようになるので、弾の連射を防ぐ事が出来ます。

<task class='org.libspark.xelf.core.tasks.KeyboardInputTask'>
    <method name='registerKeyDown'><arg>'Left'</arg></method>
    <method name='registerKeyDown'><arg>'Right'</arg></method>
    <method name='registerKeyDown'><arg>'Up'</arg></method>
    <method name='registerKeyDown'><arg>'Down'</arg></method>
    <method name='registerKeyPush'><arg>'Space'</arg></method>
</task>

これで実行すると、スペースキーを押すと弾が発射されるようになります。

初期化メッセージのやり取り

弾が発射されるのはいいのですが、おかしな場所から弾が発射されていますね。これはPlayerTaskの座標をShotTaskに対して送っていないからです。

「createInstance」メッセージは、こういった場合に備えてもう1つメッセージを送る事が出来るようになっているので、それを利用して座標を送ってみます。

まず、実際にやり取りするメッセージを新たに作ります。

import org.libspark.xelf.core.messages.Message;

public class ShotMessage extends Message
{
    public function ShotMessage(type:String, x:Number, y:Number)
    {
        super(type);
        this.x = x;
        this.y = y;
    }
    
    public var x:Number;
    public var y:Number;
}

PlayerTaskから「shotCreated」メッセージを送るようにします。CharacterMessageコンストラクタの第2引数に指定します。

public function onInputSpace():void
{
	// createInstanceメッセージを送る
    shot.sendMessage(new CharacterMessage(CharacterMessage.CREATE_INSTANCE, 
                     new ShotMessage('shotCreated', x, y)));
}

ShotTaskは「shotCreated」メッセージを受け取って座標を初期化します。

import org.libspark.xelf.core.Messenger;
import org.libspark.xelf.core.messages.CharacterMessage;

public class ShotTask implements Actor
{
    private var _x:Number = 200;
    private var _y:Number = 100;
    private const speed:Number = 5;
    
    public var self:Messenger;
    
    public function get radius():Number
    {
        return 4;
    }
    
    public function get x():Number
    {
        return _x;
    }
    
    public function get y():Number
    {
        return _y;
    }
    
    public function onShotCreated(message:ShotMessage):void
    {
        // shotCreatedメッセージを受け取って座標を初期化する
        _x = message.x;
        _y = message.y;
    }
    
    public function onUpdate():void
    {
        _y -= speed;
        if (_y < 0) {
            // 画面外に出たらdestroyInstanceメッセージを送る
            self.sendMessage(new CharacterMessage(CharacterMessage.DESTROY_INSTANCE));
        }
    }
}

これで実行すると、めでたく白い丸から弾が発射されるようになります。

更に詳しい使い方はドキュメントをご覧下さい。

このページの目次