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アプリケーションプロジェクトとして始めようかな。
でもそれだと、ローカルでクロスドメインとか面倒なことでてきそうだな。

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

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

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



抽象インターフェース
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();
}
}
}


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

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

というイメージかな。



シングルトンパターン

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

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

今回はこの本に沿って、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 {}

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形式で書き出す」という流れです。

こちらのサイトにphpFlashの連携方法が書かれていたので、そちらを参考にしつつ行いました。
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形式のファイルが出来上がります。



// 保存する中身
$name = $_POST["name"];
$userData = $_POST["userData"];

// jsonデータ
$dataFile = "$name.json";

// 書き込むデータ
$contents = "$userData";

// データを開く(データ名が存在しない場合は、作成を試みる)
$fp = fopen($dataFile, "w");
// データを書き込む
if(fwrite($fp, $contents) === false) { // 型も値も含めて厳密に比較してくれる
echo "書き込めなかった";
exit;
}

fclose($fp);

?>




【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     
#include

#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( &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]
*/