2013年12月19日木曜日

Starlingを始める

Starlingとは、stage3Dで2D描画で扱いやすく作られたフレームワークです。
 stage3Dではハードウェアアクセラレーションで高速なGPU描画が可能となります。 

airアプリプロジェクトを作ってみたらはまったからメモ。
webアプリケーションの設定でプロジェクトを作ると、インストールされているFlashPlayerで実行できるんですが、airだとそうじゃないらしい。。。
怒られた。。

そこでair3.9っていう最新っぽいのを試してみる。
http://www.adobe.com/devnet/air/air-sdk-download.html
上記はコンパイラなので、以下のアーカイブを参照したほうがいいかも。
http://helpx.adobe.com/air/kb/archived-air-sdk-version.html

ランタイムバージョンを落としてみた。
けど、通らない…!

普通にwebアプリケーションプロジェクトとして始めようかな。
でもそれだと、ローカルでクロスドメインとか面倒なことでてきそうだな。

2013年12月10日火曜日

テンプレートメソッド、ファクトリーパターン

テンプレートメソッドパターン

大本の汎用的なアルゴリズムを記述したテンプレートクラス



抽象インターフェース
package factory
{ 
 public interface IField
 {
  // 「フィールドを描く」というメソッドを定義
  function drawField():void;
 }
}

実装クラス
package factory
{
 public class FootballField implements IField
 {
  public function FootballField()
  {
  }
  
  public function drawField():void
  {
   trace('Drawing the Football Fieald!');
  }
 }
}

抽象テンプレートクラス
package factory
{
 /**
  *  テンプレートメソッド抽象クラス
  * 
  */ 
 public class BaseGame
 {
  public function BaseGame()
  {
  }
  
  public function initialize():void
  {
   // 無処理
  }
  
  public function createField():IField
  {
   throw new Error('createField:Abstract Methd!');
  }
 }
}

継承クラス
package factory
{
 /**
  *  テンプレートメソッドを継承するクラス
  * 
  */ 
 public class FootballGame extends BaseGame
 {
  public function FootballGame()
  {
   super();
  }
  
  override public function initialize():void
  {
   var field:IField = createField();
   field.drawField();
  }
  
  override public function createField():IField
  {
   return new FootballField();
  }
 }
}


コードだけでは理解するのが難しいので、クラス図を書いたほうがいい。

「テンプレートとなる抽象クラスで処理のアルゴリズムを書き、
抽象インターフェースで各処理の詳細を記述していく」

というイメージかな。



2013年12月9日月曜日

シングルトンパターン

デザインパターンの勉強を始めたので、その勉強録です。

まともに今までデザインパターンを勉強したことがなく、なんとなくソース読んで、似たような仕様で書くっていうのを今まで繰り返してました。
インターンの時にシングルトンを全く意味不明なまま使っていて、結局その時理解せずにインターンを終えてしまった苦い思い出が思い出されます。

今回はこの本に沿って、ActionScriptでデザインパターンを勉強していきたいと思います。(本当はC++とかでやりたい)
ActionScriptでデザインパターンを解説している唯一の本で、丁寧に解説してくれていて理解しやすいと思います。




シングルトンパターン

「クラスのインスタンスを一つしか作らない」ようにするパターンです。
静的なデータとかこういった形で持つ事が多いですかね。

SingletonEnforcerクラス
シングルトンのクラスのみが使用できるSingletonEnforcerクラスをコンストラクタの引数にして、強制的にコンストラクタを外部で呼び出せなくする手法。


package
{
 /**
  *  シングルトンパターンクラス
  * 
  */ 
 public class SingletonClass
 {
  // privateでstaticな変数
  private static var _instance:SingletonClass;
  
  /**
   * constructor 
   * 
   */  
  public function SingletonClass(enforcer:SingletonEnforcer)
  {
  }
  
  /**
   * インスタンス取得メソッド 
   * @return SingletonClass
   * 
   */  
  public static function getInstance():SingletonClass
  {
   if (SingletonClass._instance == null)
   {
    SingletonClass._instance = new SingletonClass(new SingletonEnforcer());
   }
   return SingletonClass._instance;
  }
  
  public function sayHello():void
  {
   trace('Hello World');
  }
 }
}
/**
 * コンストラクタを強制的に呼び出せなくするため
 * 外部からアクセス不可能な空クラスを定義
 * 
 */
class SingletonEnforcer {}

2013年12月7日土曜日

AS3でカスタムイベント

Flash開発する上ではイベントの処理は必要ですよね。 僕はFlashを勉強し始めて、二週間ちょっとですが、もう何回使ったかわかりません。 そのくらい頻繁に用いるイベント処理。 ゲームを制作する時とかは自分でイベントを作りたくなります。 「着地した瞬間」とか「的に当たった瞬間」とか「ゴール地点に到達した瞬間」とかね。 今日、カスタムイベントの定義の方法を勉強したので、復習を込めた備忘録です。

カヤックのブログの説明がよかったです。

今回作成したサンプルです。

壁にボールが当たるとイベントが発生します。

【Main.as】

メインのクラス

package  {
     import flash.display.MovieClip;
     import flash.events.Event;
     import flash.events.MouseEvent;
     /**
      * 円をクリックすれば、ステージを円が跳ね返っていく
      * 跳ね返った瞬間にイベントディスパッチする
      */
     public class Main extends MovieClip {
          // キャラインスタンス
          private var chara:Chara;
          // 壁にあたった回数
          private var cnt:uint;
          public function Main() {
               // キャラのインスタンスを生成
               chara = new Chara();
               // クリックイベントを設定
               chara.addEventListener(MouseEvent.CLICK, onCharaClick);
               // 壁に当たるカスタムイベントを設定
               chara.addEventListener(CharaEvent.HIT_WALL, onCharaHitWall);
               // 中央に配置
               chara.x = stage.stageWidth / 2;
               chara.y = stage.stageHeight / 2;
               addChild(chara);
               // 壁にあたった回数
               cnt = 0;
               // テキスト表示
               outputText.text = cnt.toString() + "回 壁にあたった";
          }
          /**
           * キャラのクリックイベント
           * @param {MouseEvent.CLICK} e : マウスイベントオブジェクト
           * @return {void} :
           */
          private function onCharaClick(e:MouseEvent):void {
               // キャラの毎フレームイベントを設定
               chara.addEventListener(Event.ENTER_FRAME, onCharaEnterFrame);
          }
          /**
           * キャラの毎フレーム関数
           * @param {Event} e : イベントオブジェクト
           * @return {void} :
           */
          private function onCharaEnterFrame(e:Event):void {
               chara.moveChara();
          }
          /**
           * キャラが壁にあたったイベントハンドラ
           * @param {CharaEvent} e : キャライベントオブジェクト
           * @return {void} :
           */
          private function onCharaHitWall(e:CharaEvent):void {
               // 壁にあたった回数をインクリメント
               cnt++;
               trace("壁にあたった" + cnt.toString() + "回");
               // テキスト表示
               outputText.text = cnt.toString() + "回 壁にあたった";
          }
     }
}

【Chara.as】

キャラの動きとイベントの設定

package  {
     import flash.display.MovieClip;
     public class Chara extends MovieClip {
          // キャラのスピード
          private var speedX:int;
          private var speedY:int;
          public function Chara() {
               // ランダムなスピードの値設定
               speedX = Math.random() * 10;
               speedY = Math.random() * 10;
          }
          /**
           * キャラを動かす関数
           * @param {}  :
           * @return {void} :
           */
          public function moveChara():void {
               // 動かす
               x += speedX;
               y += speedY;
               // 壁にあたったかどうかのフラグ
               var isHitWall:Boolean = false;
               // 壁にあたった時のはねかえり処理
               if(x < width / 2) {
                    x = width / 2;
                    speedX *= -1;
                    isHitWall = true;
               } else if(x > stage.stageWidth - width / 2) {
                    x = stage.stageWidth - width / 2;
                    speedX *= -1;
                    isHitWall = true;
               }
               if(y < height / 2) {
                    y = height / 2;
                    speedY *= -1;
                    isHitWall = true;
               } else if(y > stage.stageHeight - height / 2) {
                    y = stage.stageHeight - height / 2;
                    speedY *= -1;
                    isHitWall = true;
               }
               // ヒットイベントをブロードキャスト( = 登録されたリスナーのメソッドを実行する)する
               if(isHitWall == true) {
                    var evt:CharaEvent = new CharaEvent(CharaEvent.HIT_WALL);
                    dispatchEvent(evt);
               }
          }
     }
}

【CharaEvent.as】

このクラスでイベントを定義している

package  {
     import flash.events.Event;
     public class CharaEvent extends Event {
          // イベントの種類を定義(静的なプロパティ)
          public static const HIT_WALL:String = "hitWall";
          public function CharaEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false) {
               // 継承元のEventクラスのコンストラクタを呼び出す
               super(type, bubbles, cancelable);
          }
          // EventDispacherを呼び出すためのオーバーライド
          override public function clone():Event {
               return new CharaEvent(type, bubbles, cancelable);
          }
          // toString()出力用
          override public function toString():String {
               return formatToString("CharaEvent", "type", "bubbles", "cancelable");
          }
     }
}

FlashからJSON書き出し

タイトルの通り、FlashからJSONファイルの書き出しをやってみました。

今回は、「Flashからphpファイルに値を渡して、それをJSON形式で書き出す」という流れです。

こちらのサイトにphpとFlashの連携方法が書かれていたので、そちらを参考にしつつ行いました。
AS3でPHPと通信する



【ConnectPHP.as】

phpファイルにデータを送信 + phpファイルを実行

package connectphp
{
     import flash.events.Event;
     import flash.events.EventDispatcher;
     import flash.net.URLLoader;
     import flash.net.URLLoaderDataFormat;
     import flash.net.URLRequest;
     import flash.net.URLRequestMethod;
     import flash.net.URLVariables;
     public class ConnectPHP extends EventDispatcher
     {
          public static const COMPLETE:String = "connectPHP_complete";
          /**
           * コンストラクタ
           */
          public function ConnectPHP(url:String = null, variables:Object = null)
          {
               if (url && variables)
               {
                    sendAndLoad(url, variables);
               }
          }
          /**
           * phpファイルにデータの内容を送信する
           * @param {String} : phpデータのパス
           * @param {String} : データの中身
           */
          public function sendAndLoad(url:String, variables:Object):void
          {
               var urlRequest:URLRequest = new URLRequest(url);
               // php に送信するデータをオブジェクト形式で保存する
               var urlVariables:URLVariables = new URLVariables();   
               // 引数のデータを全て渡す
               for (var i:String in variables)
               {
                    urlVariables[i] = variables[i];
               }
               // urlRequest.dataに渡す
               urlRequest.data = urlVariables;
               // phpへの送信方法
               urlRequest.method = URLRequestMethod.POST;
               // phpファイルをリクエスト
               var urlLoader:URLLoader = new URLLoader();
               urlLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
               urlLoader.addEventListener(Event.COMPLETE, completeHandler);
               urlLoader.load(urlRequest);
          }
          /**
           * phpファイルの呼び出しが終了したときのイベントハンドラ
           */
          private function completeHandler(e:Event):void
          {   
               // イベントディスパッチ
               dispatchEvent(new Event(COMPLETE));
          }
     }
}




【data.php】

phpファイル

このファイルを通せば、"name".jsonというJSON形式のファイルが出来上がります。






【Main.as】

メインのドキュメントクラス


package connectphp
{
     import flash.display.MovieClip;
     import flash.events.Event;
     import connectphp.*;
     public class Main extends MovieClip
     {
          /**
           * コンストラクタ
           */
          public function Main()
          {
               // 保存したいデータのオブジェクトを作る
               var userData:Object = {
                    // 名前
                    name: "tanaka",
                    // 買い物リスト
                    item:[
                         "ファブリーズ",
                         "ジョイ",
                         "キャベツ",
                         "カレールー",
                         "じゃがいも"
                    ]
               };
               // オブジェクトをJSON形式の文字列に変換
               var userDataForJson:String = JSON.stringify(userData);
               // 送るデータ
               var sendData:Object = new Object();
               sendData.name = userData.name.toString();
               sendData.userData = userDataForJson;
               // データをphpに送る
               var connectPHP:ConnectPHP = new ConnectPHP();
               connectPHP.addEventListener(ConnectPHP.COMPLETE, completeHandler);
               connectPHP.sendAndLoad("data.php", sendData);
          }
          private function completeHandler(e:Event):void
          {
               trace("送信完了");
          }
     }
}

earth.idlコンパイル方法

(1) Microsoft Visual C++ 2008 Express EditionからVisual Studio 2008 コマンド プロンプトを起動
(2) コマンドラインは以下の通り
  midl /cpp_cmd cl.exe /cpp_opt "/E" /out "出力先ディレクトリ" "earth.idlのあるディレクトリ"
*スペースに注意


参考:IDLコンパイル成功!
http://futr.cocolog-nifty.com/blog/2008/04/idl_f4f5.html

Google Earth COM API を C++ で使う
http://oshiro.bpe.es.osaka-u.ac.jp/people/staff/imura/computer/misc/google_earth_com_api/disp_content


------以下サンプルプログラムを空のプロジェクトから記述(追加のインクルードファイルにearth.hを追加)------
// main.cpp
#include     <windows.h>
#include     <iostream>

#include     "earth.h"

using namespace std;



int
main( int argc, char **argv )
{
     IApplicationGE     *app;
     ICameraInfoGE     *camera;
     HRESULT     hr;

     // COMの初期化

     CoInitialize( 0 );

     // IApplicationGE を得る
    
     hr = CoCreateInstance(
          CLSID_ApplicationGE,
          0,
          CLSCTX_LOCAL_SERVER,
          IID_IApplicationGE,
          reinterpret_cast<LPVOID *>( &app ));
     if ( FAILED( hr )) {
          cerr << "cannot create IApplicationGE" << endl;
          return -1;
     }

     // Google Earth の情報を得る

     AppTypeGE     app_type;
     int     major, minor, build;
     app->get_VersionAppType( &app_type );
     app->get_VersionMajor( &major );
     app->get_VersionMinor( &minor );
     app->get_VersionBuild( &build );
     cout << "You are using ";
     switch ( app_type ) {
     case EnterpriseClientGE:
          cout << "Google Earth Enterprise Client" << endl;
          break;
     case ProGE:
          cout << "Google Earth Pro" << endl;
          break;
     case PlusGE:
          cout << "Google Earth Plus" << endl;
          break;
     case FreeGE:
          cout << "Google Earth Free" << endl;
          break;
     default:
          cout << "Unknown" << endl;
          break;
     }
     cout << "version: " << major << "." << minor << "." << build << endl;

     // 初期化が終了するまで待つ

     BOOL     is_initialized;
     do {
          hr = app->IsInitialized( &is_initialized );
     } while ( is_initialized == false );

     // 現在のカメラ位置に関する情報を得る

     // まずカメラのインスタンスを取得する

     hr = app->GetCamera( true, &camera );
     if ( FAILED( hr )) {
          cerr << "IApplicationGE::GetCamera() failed" << endl;
          return -1;
     }

     // 各種パラメータの値を得る

     double     lat, lon, alt;
     AltitudeModeGE     alt_mode;
     double     range, tilt, azimuth;
     camera->get_FocusPointLatitude( &lat );          // 緯度
     camera->get_FocusPointLongitude( &lon );     // 経度
     camera->get_FocusPointAltitude( &alt );          // 高度
     camera->get_FocusPointAltitudeMode( &alt_mode );     // 高度の計測方法
     camera->get_Range( &range );
     camera->get_Tilt( &tilt );
     camera->get_Azimuth( &azimuth );
     cout << "現在の視点情報" << endl;
     cout << "注視点の緯度: " << lat << endl;
     cout << "注視点の経度: " << lon << endl;
     cout << "注視点の高度: " << alt << endl;
     cout << "高度の原点は地表: " << alt_mode << endl;
     cout << "注視点からの距離: " << range << endl;
     cout << "天頂角: " << tilt << endl;     // 水平だと90度
     cout << "方位角: " << azimuth << endl; // 注視点に対して視点が真南にあると
     // 0度。そこから時計まわりに角度が増える。

     // カメラを設定する

     app->SetCameraParams( 34.7316,     // 緯度
                                135.734,     // 経度
                                0.0,          // 高度
                                RelativeToGroundAltitudeGE,     // 高度の計測方法
                                300.0,     // 距離
                                0.0,          // 天頂角
                                0.0,          // 方位角
                                0.1 );     // 移動速度 (5.0より大きいと瞬時に移動)

     // 終了

     CoUninitialize();

     return 0;
}

for each

var mc1:MovieClip = new MovieClip();
mc1.name = 'material';
var mc2:MovieClip = new MovieClip();
mc2.name = 'furniture';
var mc3:MovieClip = new MovieClip();
mc3.name = 'instant';
var obj:Object = {
     material: mc1,
     furniture:mc2,
     instant:mc3
};
trace('--------------------------------------');
for (var key:String in obj)
{
     trace(obj[key]);
     trace(key);
}
/*
[object MovieClip]
furniture
[object MovieClip]
instant
[object MovieClip]
material
*/
trace('obj[item]--------------------------------------');
for each(var item:String in obj)
{
     trace(item);
}
/*
[object MovieClip]
[object MovieClip]
[object MovieClip]
*/
trace('elementがobjになる--------------------------------------');
for each(var element:Object in obj)
{
     trace(element);
}
/*
[object MovieClip]
[object MovieClip]
[object MovieClip]
*/

as3でlzma圧縮

FlashPlayer11.3から搭載された新機能。

今まではzip圧縮しかできなかったのが高効率のlzma圧縮も対応できるようになっているらしい。

Adobe純正の flex sdk4.6 では compress('lzma') に対応していなくて、コードから直接読み込むのは不可能でした。

flex sdkはAdobeではもう作っていなくて、apache flex sdkになっているらしい。
apache flex sdk 4.11 をダウンロードして使ってみたら、lzma呼び出せました。

ByteArray形式のデータを保存するだけなので、すごい簡単です。

// 保存したいファイルのデータ(本当はこのデータを外部ファイルを読み込む形になる)
var file:FileReference = new FileReference();
// ByteArrayのデータを抽出
var data:ByteArray = new ByteArray();
data.writeBytes(file.data);
// 圧縮するとき
data.compress(CompressionAlgorithm.LZMA);
file.save(data);
// 解凍するとき
data.uncompress(CompressionAlgorithm.LZMA);
file.save(data);