SERVICE 事業内容

ASPサーバーチュートリアル
~100heartsを作ろう~

このページでは、スカイサーバーを利用したネットワークアプリケーション"100hearts" の通信部分をどのように作成したのかを順を追ってご紹介します。

100heartsのサンプルソース(Flashアプリ開発パックの中にあります)をこちらからダウンロードして100heartsのソースを見ながらチュートリアルを読み進めて下さい。


 環境を準備しよう

Adobe Flash CS4上で開発することを前提に説明を進めます。

100heartsはFlash上でスカイサーバーによる通信とPapervision3Dによる3D描画を利用していますので、
 ・nine. for flash ライブラリ
 ・nine.ASP ライブラリ
 ・Papervision3Dライブラリ

が必要です。


ダウンロードしよう

Flashアプリ開発パックをこちらからダウンロードします。
(nine for flashとnine.ASP のライブラリが入っています。)

最新のPapervision3Dライブラリ(.swcファイル)をダウンロードします。

これらのswcファイルをローカルディレクトリに置き、そのディレクトリにパスを設定します。



準備しよう | 作成しよう | 接続後の処理を行おう 

以下の、ASPサーバーチュートリアルをご説明いたします。

 Mainクラスを作る

プログラムをスタートさせるMainクラスを作りましょう
このクラスの中で変数の初期化等を行います。
initGame()の中でタイトルスクリーンを作成してaddChild()を行っています。

Main.asのコード
	                            
    package {
    
        (中略)
        
        public class Main extends Sprite {
                
            (中略)
               
            public static var main_obj : Main;
                
            public function Main() {
                          
                 main_obj = this;
                            
                 initVars();
                 initGame();
             }
                
             (中略)
               
             private function initGame():void {
                
                (中略)
                            
                 _sceneTitle = new Title(this);
                 _sceneTitle.x = 400;
                 _sceneTitle.y = 300;
                 _screen.addChild(_sceneTitle);
                            
                 (中略)
             }
         }
               
     }
	                            

ページトップ

 DistributionServerの派生クラスを作る

スカイサーバーサービスを利用するには、まずDistributionServerを継承したクラスを作成します。
ここではNetworkManagerクラスというクラスを作成し、DistributionServerを継承しています。

NetworkManager.asのコード
	                            
    package {
        
        
    public class NetworkManager extends DistributionServer {
                
        (中略)
                
        public function NetworkManager(game:Main,
             progressBar:Main = null) {
                _parent = game;
                _progressBar = game;
        }
        
        public function connect():void {
                    
            if(!super.initialize(Settings.SERVER_ADDRESS, 
                Settings.SERVER_PORT, Settings.AUTH_CODE,
                Settings.CROSSDOMAIN_PORT)) { return; }
        
            PlayerCharacter.enable();
                    
        (中略)
                    
        }
        
        public function run():void {
            turn();
        }
        
        (中略)
            
        }
    }
	                            

ページトップ

 タイトルを作る

題名 ユーザー名入力欄 ログインボタンが付いたタイトルを作ります。
ログインボタンが押されるとASPへの接続が開始されるようにします。


ready()が呼び出されると、ログインボタンが有効になり、
ログインボタンを押した際に呼び出されるコールバック関数onClickLogin()が有効になります。
ログインボタンを押すとNetworkManagerのconnect()関数が呼び出されます。

Title.asのコード
	                            
    package {
                    
        (中略)
                        
        public class Title extends MovieClip {
                            
            private var _parent:Main;
                                    
            public function Title(parent:Main) {
                                
                stop();
            (中略)
                }
                public function ready():void {
                                
                    nextButton.addEventListener(MouseEvent.MOUSE_DOWN,
                        onClickLogin);
            (中略)
                }
                private function onClickLogin(event:MouseEvent):void {
                                
                    if (nameForm.text != "") {
                        nextButton.removeEventListener(
                            MouseEvent.MOUSE_DOWN, onClickLogin);
                        nextButton.buttonMode = false;
                        _parent.connectServer(nameForm.text);
                    }
                }
        }
    }                 
	                            

Main.asに追加
	                            
    public function connectServer(name:String):void {
                
        myName = name;
                
        _player = new PlayerCharacter();
    
        _networkManager.connect();
        addEventListener(Event.ENTER_FRAME, connectingServer);
     }
	                            

ページトップ

 プレーヤーの各種情報を保持するアバターオブジェクトを作成する

100heartsでは複数のプレーヤーがフィールド上を移動します。
これらのプレーヤ情報をTCP/IPを用いてサーバー・クライアント間で共有しようとすると
大変ですが、ASPサーバーのオブジェクト共有機能を使用すると簡単に共有することができます。

オブジェクト共有を使用するには、情報を共有したいクラスをNetworkObjectクラスを継承して作成します。

TYPE_IDはこのクラスのユニークなIDです。他の共有オブジェクトやメッセージのクラスを作成する際には、必ず別のIDを設定して下さい。このIDはNetworkObjectManager.register()関数を呼び出す時に使用します。
PlayerCharacter上で共有したいデータは、IntProperty(Int型データ)やNumberProperty(Number型データ)、StringProperty(String型データ)等のクラスのオブジェクトにして、PlayerCharacterクラスのメンバーにします。
PlayerCharacterのコンストラクタでaddProperty()を呼び出す事で、プロパティがDistributionServerに登録されます。
今回はユーザーのログインネーム、ハートの数、ステータス番号、アバターの座標と向きをプロパティにします。

PlayerCharacter.asのコード
	                            
    package {
        
        (中略)
        
        public class PlayerCharacter extends NetworkObject {
        
            public static const TYPE_ID:uint = 201;
        
            public var m_userName :StringProperty = new StringProperty();
            public var m_coin     :IntProperty = new IntProperty();
            public var m_status   :IntProperty = new IntProperty();
        
            public var m_tx       :NumberProperty = new NumberProperty();
            public var m_ty       :NumberProperty = new NumberProperty();
            public var m_tz       :NumberProperty = new NumberProperty();
        
            public var m_angle    :NumberProperty = new NumberProperty();

        (中略)
                
            protected var m_is_first_update: Boolean = true;
    
            public static function enable() : void
            {
                NetworkObjectManager.register( TYPE_ID, create );
            }
        
            public static function create() : NetworkObject
            {
                return new PlayerCharacter();
            }
                
            public function PlayerCharacter() 
            {
                super( TYPE_ID );
                addProperty( m_userName );
                addProperty( m_coin );
                addProperty( m_status );
        
        
                addProperty( m_tx );
                addProperty( m_ty );
                addProperty( m_tz );
        
                addProperty( m_angle );
            }
    
         }
     }
	                            


準備しよう | 作成しよう | 接続後の処理を行おう 

以下の、ASPサーバーチュートリアルをご説明いたします。

 ASPサーバーに接続成功した後の処理を行う

ASPサーバーへの接続が完了すると、DistributionServerのonInit()が呼び出されます。
接続が完了したら、DistributionServerのaddObject()関数を呼び出して
アバターオブジェクトの共有を開始します。

共有オブジェクトの登録が完了すると、onAddObject()関数が呼び出されます。
この関数は自分のクライアントのオブジェクトの登録でも、他のクライアントのオブジェクト登録でも
呼び出されますので、NetworkObjectのlocalプロパティで自他の判別を行います。
ここでは、自オブジェクトの登録が完了したと判断した所でMainクラスのstartGame()関数を呼び出しています。

NetworkManager.asのコード
	                            
    package {
        
        
        public class NetworkManager extends DistributionServer {
                
            (中略)
                
            public function NetworkManager(game:Main,
                 progressBar:Main = null) {
                    _parent = game;
                    _progressBar = game;
            }
        
            public function connect():void {
                    
                if(!super.initialize(Settings.SERVER_ADDRESS,
                     Settings.SERVER_PORT, Settings.AUTH_CODE,
                        Settings.CROSSDOMAIN_PORT)) { return; }
        
                            PlayerCharacter.enable();
                    
            (中略)
                    
            }
        
            public function run():void {
                turn();
            }
                
            public override function onInit(id : uint) : void 
            {
                super.onInit(id);
                addObject(_parent._player as PlayerCharacter);
            }
                
            public override function onAddObject(
                object : NetworkObject) : void 
            {
                trace("NetworkManager: function onAddObject()");
                if( object.local )
                {
                    _parent.startGame(object.objectId);
                }
                else
                {
                    _parent.addCharacter(object);
                }
            }
         }
     }

	                            

Main.asに追加
	                            
     public function connectServer(name:String):void {
			
        myName = name;
			
        _player = new PlayerCharacter();

        _networkManager.connect();
        addEventListener(Event.ENTER_FRAME,
            connectingServer);
     }

     private function connectingServer(event:Event):void {
        
        _networkManager.run();
     }
	                            

ページトップ

 メインループ内でDistributionServerのturn()関数を呼び出します。

この関数を繰り返し呼び出す事により、ASPサーバーとの通信が実行されます。

Main.asに追加
                                
    public function startGame(id:int):void {
			
    (中略)
			
        removeEventListener(Event.ENTER_FRAME,
            connectingServer);
        addEventListener(Event.ENTER_FRAME, loop);

    (中略)

    }

    private function loop(event:Event):void {
			
    (中略)
		
        _networkManager.run();
			
    (中略)
		
    }
	                            

ページトップ

 共有オブジェクトの状態変化を検知する

ゲーム中に、共有オブジェクトの状態変化を検知したい場合があります。
100heartsでは、アバターオブジェクトのステータス番号が変化した時に
アバターの外見を変化させていますので、ステータス番号の変化が
自動的に検出されると便利です。
NetworkObjectには、プロパティが変化した時に呼ばれるコールバック関数
onPropertyUpdate()があります。onPropertyUpdate()をオーバーライドして、
その中にコードを追加します。
引数indexは初期化時にPlayerCharacter()でaddProperty()を行った順番を表します。
indexは0始まりなので、index==2 のとき、m_statusが変化したということになります

PlayerCharacter.asに追加
	                            
    public override function onPropertyUpdate(index:uint):void
    {
        super.onPropertyUpdate(index);
			
        if( local ) return;
			
        if( m_is_first_update )
        {
            m_is_first_update = false;
            init( Main.main_obj._scene, Main.main_obj._modelManager );
        }
        else
        {
            if( index == 2/*status*/ )
            {
                Main.main_obj.changeStatusCharacter( objectId,
                     m_status.value );
            }
        }
    }

	                           	

ページトップ

 グローバルチャットを実装する

グローバルチャットを実装するには、チャットデータが入ったメッセージクラスを定義し、
DistributionServerのPostAll()関数を使用してメッセージを全員に送信します。

メッセージの定義
UserMaessageを継承したクラスChatMessageを作成します。
まず、ユニークなメッセージIDを決めます。
MessageTypeRegistry.register()を呼び出し、自クラスとメッセージIDを登録します。
getMarshalSize(), marshal(), unmarshal()を定義します。
getMarshalSize()は、このクラスのデータをシリアル化した時のサイズを返すようにします。
marshal()は、このクラスのデータをバッファに書き込んでシリアル化します。
unmarshal()は、シリアル化されたバッファからクラスのメンバ変数にデータを読み込みます。

ChatMessage.as
	                            
    package
     {
        import nine.*;
        import flash.utils.ByteArray;
        
        public class ChatMessage extends UserMessage
        {
            public static const MESSAGE_ID:UserMessageID
                 = new UserMessageID(205);
            
            public var id : uint;
            public var text : String = null;
                
            public function ChatMessage() {
                super(MESSAGE_ID);
            }
            
            public static function enable() : void {
                MessageTypeRegistry.register(MESSAGE_ID, create);
            }
            
            public static function create() : Message {
                return new ChatMessage();
            }
            
            public override function getMarshalSize() : int {
                return 4 + Buffer.getWriteStringSize(text);
            }
                
            public override function marshal(buf:Buffer) : void {
                buf.writeUInt32(id);
                buf.writeString(text);
            }
                
            public override function unmarshal(buf:Buffer) : void {
                id = buf.readUInt32();
                text = buf.readString();
            }
        }
    }
	                            

ASPサーバーへの接続時にメッセージクラスがDistributionServerに登録されるようにします。

NetworkManager.asに追加
	                            
    public function connect():void {
            
    (中略)
            
        ChatMessage.enable();
            
    (中略)
            
    }
	                            

メッセージオブジェクトを生成してデータを設定し、DistributionServerのpostAll()で送信します。

NetworkManager.asに追加
	                            
    public function sendChatMessage(text:String):void {
         
        if(text.length == 0) return;
                
        var msg : ChatMessage = new ChatMessage();
        msg.text = text;
        postAll(msg);
    }
	                            

ページトップ

 メッセージハンドラを追加しよう

メッセージを受信した時の処理を追加します。
DistributionServerのhandleMessage()関数をオーバーライドします。
受信したメッセージのIDが引数のmsgに入っていますので、IDがChatMessageのものだけを処理します。

NetworkManager.asに追加
	                            
    public override function handleMessage(from : uint,
         msg:Message) : Boolean 
    {
        switch( msg.messageId )
        {
        case ChatMessage.MESSAGE_ID.id:
            return handleChatMessage( from, ChatMessage(msg) );
    (中略)
        default:			
            return false;
        }
    }


    protected function handleChatMessage(from : uint,
         msg:ChatMessage) : Boolean {
        
        _parent.receiveChat(msg.text);
        return true;
    }
	                            



準備しよう | 作成しよう | 接続後の処理を行おう 


ページトップ

お問い合わせ

nineに関するお問い合わせはこちらのアドレスまでご連絡ください。

ページトップ