Category : レースゲーム制作記

12 月 21 2008

PV3Dでレースゲーム制作 23 - UI作成

Posted by tanjo at 4:03 AM

レースゲームデモ画面

» ゲームをプレイする (要:Flash Player 9.0.124.0以降)

ナビゲーションを追加してみました。レースゲームらしくなってきたでしょうか?

Flash CS3でUIを作成

実は結構どうしようか迷ってたのがUIの作り方。
生産性を上げるために、3Dとか画像のファイルはすべて外部化、Flash CS3 は使わず FlashDevelop と Flex で開発…ってしてきたんだけど、ナビゲーション系はどう考えても Flash で作った方がやりやすい。。

調べてもどうしたらいいのかピンとこなかったので適当にやってみました。
それで結局、「UI専用のSWFをメインSWFに読み込んで、そのインスタンスからムービークリップを取り出して配置」という流れに。

外部SWFのライブラリにあるムービークリップへ、アクセス

UI用Flashファイルのドキュメントクラスで、ライブラリにあるムービークリップを一度インスタンス化して、それをゲッターで返すようにしておきます。その状態でSWFファイルにパブリッシュ!
一方メイン側では、UI用SWFを Object 型インスタンスとして読み込むと、「(インスタンス名).(ゲッター名)」でライブラリにあるムービークリップにアクセスできるわけです。分かりにくッ!

***

ええと、具体的には。
たとえばFlash CS3上にこんな感じの「map」っていうクラス名のムービークリップがあったとします。

mapmcmapmc

ドキュメントクラスでは「インスタンス化 → ゲッター」で外部からこのムービークリップを参照できるようにしておきます。

package
{
	import flash.display.*;
	public class UI extends Sprite
	{
		private var _mapMC:MovieClip
		public function UI()
		{
			_mapMC = new map();
		}
		public function get mapMC():MovieClip
		{
			return _mapMC;
		}
	}
}

そして今まで作ってたメインの方からは、_loader にSWFを読み込んだとして

var _ui:Object = Object(_loader.content);
var _mapMC:MovieClip = _ui.mapMC;
addChild(_mapMC);

という風にアクセスできました。
Loader.content で子SWFのルートにアクセスできるけど、ここではステージに配置せずにゲッターを使っているわけです。

さらに、先ほどのムービークリップに配置されていたマルポチが実は「myPoint」という名前のインスタンスだったとすると、

_mapMC.myPoint.x = 100;

のように階層化されたムービークリップも操作できます。

こうしておけば、UIを増やしたりデザインを修正するときはFlashのライブラリをいじってパブリッシュし直すだけ、制御系は今までどおりFlashDevelopからFlexコンパイルするだけ、とまあ、作業が別々になってわたし的には扱いやすくなってくれました、と。

(もっといい方法もあるのかなぁ。)

11 月 30 2008

PV3Dでレースゲーム制作 22 - デモ走行追加

Posted by tanjo at 5:18 PM

レースゲームデモ画面

» デモ走行を見る (要:Flash Player 9.0.124.0以降)

デモンストレーションが繰り返し再生されるサンプルです。

デモ走行追加

三人称カメラリプレイ再生車モデルの挙動 と作ってきました。
ということは、走行データを固定の外部ファイルから読み込むようにすれば、市販ゲームなんかによくある「デモ走行映像」が作れるぞ、と。

リプレイの時は操作入力の履歴を順に配列へ保存して、1フレームずつ shift で取り出して再生していました。ですのでデモ走行を作るには、「操作履歴を表す数字ばかりのファイルを用意」→「それを読み込んで配列に変換」→「リプレイ時と同様に再生」という流れでできそうです。

操作履歴ファイルの作成

まあこの辺はてきとうです。区切り文字「,」で数字が羅列したファイルを作ります。
ドライブすると drivingHistory という変数に履歴が記録されるようにしてあるので、実際に走ったあとそれをトレース&コピペしてテキストファイルを作成しました。

//ドライビングヒストリ出力
for (var i:int = 0; i < drivingHistory.length; i++)
{
	trace(drivingHistory[i] + ",");
}

ファイルを読み込み配列に変換

URLLoader.data でテキストファイルの内容をString型で取得できます。String.split() メソッドで文字列を配列に変換し、そして各要素を Number に型変換してやれば、必要な再生用配列の完成です。…もうちょっとスマートなやり方があるのかな。。

var drivingDemoString:String = String(myURLLoader.data);
var drivingDemo:Array = drivingDemoString.split(",");
drivingDemo = strToNumArray(drivingDemo);
function strToNumArray(array:Array):Array
{
	for (var i:int; i < array.length; i++)
	{
		array[i] = parseInt(array[i]);
	}
	return array;
}

配列を元に車の運転を再生するのは、リプレイモード搭載の回と同じやり方です。

11 月 23 2008

PV3Dでレースゲーム制作 21 - 車のモーション

Posted by tanjo at 5:04 AM

車のモデルをちゃんと作ります。直方体のままじゃあまりにも…なので。
今回はキャラクターとか乗り物に必要な、「パーツごとの動き」を追加してみました。

モデリング

モデリングは Lightwave 3D Modeler を使いました。

fdmodel

3Dオブジェクトを階層化

車モデルをそのまま1つの COLLADA で読み込むと、タイヤの回転とかが再現できません。
そこで、動かしたいパーツごとにモデルを分割して、ActionScript 側でインスタンスを再構成することで個別のモーションに対応させます。ちょうど Sprite を addChild で階層化して個別2Dアニメーションさせるのと同じようなやり方で、3Dオブジェクトも制御できちゃいます。

具体的にやりたいことは次のとおり。

  • 荷重移動でカウルが動く
  • 車の速度に応じてタイヤが回転する
  • 操作に合わせて前輪が左右に動く
  • 影は固定
  • 車全体がセットで移動する

最低限これくらいやっとけば車っぽく見えるでしょう。テールランプがあってもよかったけど。

というわけでパーツ分割は、カウル・タイヤ・影の3つなりました。
これらを包括した DisplayObject3D を作り、車全体が一緒に移動できるようにします。さらに前輪は、回転しながら左右に動かないといけないので、ここも階層化する必要がありそうです。
*DisplayObject3D は Collada のスーパークラスです。

シーン : Scene3D
+-コース : Collada
+-車 : DisplayObject3D
  +-カウル : Collada
  +-前輪 : DisplayObject3D
    +-タイヤ : Collada
  +-前輪 : DisplayObject3D
    +-タイヤ : Collada
  +-後輪 : DisplayObject3D
    +-タイヤ : Collada
    +-タイヤ : Collada
  +-影 : Collada

ソース。

//宣言---------------------------------------------------------------//
private var _scene:Scene3D;

private var _carObject:DisplayObject3D;
private var _cowl:Collada;
private var _tireFL:DisplayObject3D;
private var _tireFR:DisplayObject3D;
private var _tireR:DisplayObject3D;
private var _tire:Collada;
private var _shadow:Collada;

(略)

//カウル-------------------------------------------------------------//
//マテリアル
var cowlMaterials:Object = new Object();
cowlMaterials = {
	tx_front_jpg:new BitmapMaterial(txCarBitmap1.bitmapData),
	tx_side_jpg:new BitmapMaterial(txCarBitmap2.bitmapData),
	tx_back_jpg:new BitmapMaterial(txCarBitmap3.bitmapData),
	tx_under_png:new BitmapMaterial(txCarBitmap4.bitmapData)
};
//オブジェクト
_cowl = new Collada( cowlXML, new MaterialsList(cowlMaterials) );

//タイヤ-------------------------------------------------------------//
//マテリアル
var tireMaterials:Object = new Object();
tireMaterials = {
	tx_tire_jpg:new BitmapMaterial(txTireBitmap.bitmapData)
};
//オブジェクト
_tireFL = new DisplayObject3D();
_tire = new Collada( tireXML, new MaterialsList(tireMaterials) );
_tireFL.addChild(_tire, "tire");
_tireFL.x = -730;
_tireFL.y = 310;
_tireFL.z = 1280;

_tireFR = new DisplayObject3D();
_tire = new Collada( tireXML, new MaterialsList(tireMaterials) );
_tireFR.addChild(_tire, "tire");
_tireFR.x = 730;
_tireFR.y = 310;
_tireFR.z = 1280;

_tireR = new DisplayObject3D();
_tire = new Collada( tireXML, new MaterialsList(tireMaterials) );
_tire.x = -730;
_tireR.addChild(_tire);
_tire = new Collada( tireXML, new MaterialsList(tireMaterials) );
_tire.x = 730;
_tireR.addChild(_tire);
_tireR.y = 310;
_tireR.z = -1100;

//影-----------------------------------------------------------------//
//マテリアル
var shadowMaterials:Object = new Object();
shadowMaterials = {
	tx_shadow_png:new BitmapMaterial(txShadowBitmap.bitmapData)
};
//オブジェクト
_shadow = new Collada( shadowXML, new MaterialsList(shadowMaterials) );

//車-----------------------------------------------------------------//
_carObject = new DisplayObject3D();
_carObject.addChild(_cowl);
_carObject.addChild(_tireFL);
_carObject.addChild(_tireFR);
_carObject.addChild(_tireR);
_carObject.addChild(_shadow);

//3D登録-------------------------------------------------------------//
_scene.addChild(_carObject);

あとは車の動きに合わせて、各パーツの x, y, z, rotationX, rotationY, rotationZ あたりを操作してやればOK。この辺は理屈よりも「らしく見えるか」というセンスの問題にもなってきますね。微調整には試行錯誤しました。

デモ

レースゲームデモ画面

» ゲームをプレイする (要:Flash Player 9.0.124.0以降)

このゲームのソースファイル Main.as はこちらから参照できます。

1周走るか、ドライブ中に[C]キーを押すと、三人称カメラになって車の挙動を確認できます。

10 月 08 2008

PV3Dでレースゲーム制作 20 - リプレイモード搭載

Posted by tanjo at 3:24 AM

ドライビングに関わるキー入力を記録→それを再生して車を走らせる、という流れでリプレイを作ります。キー入力だけなら1つのint値に収めることができ、座標やら回転やら荷重やらを記録するよりもだいぶコンパクトに処理できます。

キーの組み合わせを整数値に変換

たとえば、記録用の変数を用意したとして [左]が押されたら +1、[右]が押されたら +2、[アクセル]なら +4、[ブレーキ]は +8、のように設定しておけば、13 = [左] + [アクセル] + [ブレーキ] のように、0~15までの整数でどの組み合わせだったかを判断できます。

…と言うと一見すごそう?に思えるけど、2進数で書くと、[左]は +1、[右]は +10、[アクセル]は +100、[ブレーキ]は +1000 だから、1101 = [左] + [アクセル] + [ブレーキ] と、0~1111までで表現できるのは当たり前でした。(そんなことに3年も気づかなかった自分orz)

これをコードにするとこんな風になります。
まずは記録のしかた。keyPress○○ は Boolean です。

var drivingHistory:Array = new Array();
//--------------------------
//以下、メインループから呼び出し
drivingHistory.push(
	int(keyPressLeft)
	+ (int(keyPressRight) << 1)
	+ (int(keyPressSpace) << 2)
	+ (int(keyPressB) << 3)
	+ (int(keyPressDown) << 4)
);

一方再生は…

//メインループから
var input:int = drivingHistory.shift()
keyPressLeft = Boolean(input & 1);
keyPressRight = Boolean((input >> 1) & 1);
keyPressSpace = Boolean((input >> 2) & 1);
keyPressB = Boolean((input >> 3) & 1);
keyPressDown = Boolean((input >> 4) & 1);

RGB抽出のときと似ていますね。ビットシフトと論理積。
記録するときは2進数の各桁に false=0 か true=1 を代入し、再生時は逆に各桁が 1=true かどうかをチェックしてフラグを立てています。

リプレイモードへ

あとは、1周したとか、適当なタイミングで記録モードと再生モードを切り替えてあげればOK。切り替えるときに車の座標などいろいろな変数をリセットし、前回作ったリプレイ用カメラへ移行します。これでついにリプレイモードの完成です!

デモ

レースゲームデモ画面

» ゲームをプレイする (要:Flash Player 9.0.115.0以降)

このゲームのソースファイル Main.as はこちらから参照できます。

1周走るとリプレイが再生されます。
でもごめんなさい、1周するには1分もかかります。

10 月 06 2008

PV3Dでレースゲーム制作 19 - リプレイ用カメラの設置

Posted by tanjo at 4:31 PM

今度の目標はリプレイモードの搭載。
まずは、TV中継のように車を追ってくれる固定カメラを設置していきます。

この辺は、私と同じく Papervision3D でレースゲームを作っているかたの記事がとても参考になりました。ほとんど同じやり方で実装してみます。
» CgInstitute Flash コミュニティー belcro - blog | d5 さんのブログ

処理の流れはこんな感じです。しかも2Dです。

  1. 配列に各カメラの座標を登録。
  2. 車の位置から最も近い座標を割り出す。
  3. その座標にカメラを移動し、車の方を向かせる。

これで車がどんな走り方をしようが、それなりに上手く追っかけてくれます。もちろんカメラの設置場所は結構吟味しましたけど。

カメラ座標の登録

配列の中に座標を示した連想配列を入れていきます。

cameraList = new Array();
cameraList[0] = new Object();
cameraList[0] = {
	x:1200,
	y:300
};

ま、手作業だと面倒くさいので、実際は例のごとく配列作成用画像を使って自動化させます。赤がFFの点を調べて push() で配列を作っていきました。

dt_course01
*赤い点が見えない。。。クリックすると拡大表示します。

最も近い点を割り出す

車の位置から1番近いカメラ座標を求めます。
候補が20個程度なら総当たりで調べてもそんなに問題なかろう…というわけで、1つずつ距離を調べて最短を割り出しています。2点間の距離(というかその2乗)を求めるために簡単な関数を作りました。

private function targetDistance2(x1:Number = 0, y1:Number = 0, x2:Number = 0, y2:Number = 0):Number
{
	var distance:Number;
	var dx:Number = x2 - x1;
	var dy:Number = y2 - y1;
	distance = dx * dx + dy * dy;
	return distance;
}

カメラをセット

リプレイ用カメラと言っても、普段使っているドライバーズ・ビューのカメラを移動させて使い回しているだけです。上で求めた座標にカメラを移動させ、そこから車が中心になるようにカメラを回転させます。今度は2点から向きを計算する関数が必要ですね。

private function targetDirection(x1:Number = 0, y1:Number = 0, x2:Number = 0, y2:Number = 0):Number
{
	var direction:Number;		//RADIAN
	var dx:Number = x2 - x1;
	var dy:Number = y2 - y1;
	direction = Math.atan2(dy, dx);			// -π ~ π
	if (direction < 0) direction += 2 * Math.PI;	// 0 ~ 2π
	return direction;
}

角度を求めるには、タンジェントの逆関数アークタンジェントを使います。
アークタンジェント?Δy=0だったときとか、対角象限の場合分けが面倒くさ~と思いきや、ActionScript 3 には Math.atan(val) のほかに、Math.atan2(y,x) ってのがあるんですね…!atan2 なら座標がどんな値でも一発で360度しっかり求めてくれます。これは便利。

ちなみに、このゲームでは疑似3Dや2Dを多用しているせいで、カメラは水平にしか振ることができません。でも意外とそれっぽい見え方にはなってくれている、はず!

デモ

レースゲームデモ画面

» ゲームをプレイする (要:Flash Player 9.0.115.0以降)

このゲームのソースファイル Main.as はこちらから参照できます。

プレイ中に [C] キーを押すとカメラモードが切り替わります。
(今までこんな直方体の車に乗っていたと!?)

10 月 04 2008

PV3Dでレースゲーム制作 18 - PV3D 2.0 Beta 1へ移行

Posted by tanjo at 7:36 PM

先日(…といっても1ヶ月以上前ですが)、Google Codeのダウンロードページにて「Papervision3D 2.0 Beta 1」のソースコードが配布開始されました。マイナーリビジョンよりは正式公開版使った方が情報共有しやすいかな、と思い rev.585 から移行します。

ちなみに最新リビジョンが公開されているリポジトリはこちら。
http://papervision3d.googlecode.com/svn/trunk/

カメラの仕様変更

シェーダはじめPV3Dの大半の機能は「使ってません」ので、移植でつまずいたのはカメラまわりくらいでした。2.0α GreatWhite(前期)から 2.0β1 のカメラ仕様変更点とかを私なりにまとめておきます。
» 以前のカメラについてはこちらのエントリ

まず大きな変更は、Camera3D, FreeCamera3D, FrustumCamera3D の3つが統合されて(新)Camera3D になったこと。
基本的には以前の FrustumCamera3D をベースにしたような感じでしょうか。コンストラクタは以下。

Camera3D(
	fov:Number = 60,
	near:Number = 10,
	far:Number = 5000,
	useCulling:Boolean = false,
	useProjection:Boolean = false
);
  • fov(Field of View)は視野角。デフォルトは60度。
  • near, far はクリッピング平面までの距離。ただし useCulling か useProjection を設定しないと描画には反映はされません。
  • near は focus と同期を取るような仕様になっています。near を変更すると投影が変わってしまうので要注意です。

useCulling, useProjection はどちらも投影空間(視錐台)の外を間引く効果があるっぽいけど、まだ具体的な処理は確認していません。どう使い分けるのやら。

ソースを見ると useCulling は FrustumCuller クラス、useProjection は Matrix3D クラスを使っているようですが。。ベンチマークしてみても負荷の違いはほとんどありませんでした。
しかも、私の環境だと useCulling が機能しないことがあったので、とりあえず useCulling = false, useProjection = true で作業しています。

そんでもって、移植にあたって気づいた点とか。

  • 以前の FrustumCamera3D にあった Rotation系プロパティが代入しても反映されないバグは解消されています。
  • 一方、コンストラクタで fov が受け渡せないバグがあるようなので、Camera3D.as(org.papervision3d.cameras.Camera3D)のソース、コンストラクタ部分に以下のような行を追加しておきました。(参考:note.x | [PV3D2.0] Updated to beta 1
    this.fov = fov;
    
  • near が focus のエイリアスになってしまった…ということは、レンダリング分けの回で使った「遠くの方だけ描画する」という妙なテクが使えなくなってしまいました。
    しかたなく near と focus を切り分けたクラスを作って対処しています。
    即席ソース » FrustumCamera3D.as

9 月 27 2008

PV3Dでレースゲーム制作 17 - 画質切り替え機能

Posted by tanjo at 11:23 PM

モデル切り替え描画で遠くまで表示できるようになったのはいいけど、それでもやっぱり処理は重くなってきていますね。一気にパフォーマンスを上げるには、画質を犠牲にするしかないっ!
そんなわけで、今までどおりの通常描画モードに加え、パフォーマンス重視のモードを追加してみました。

パフォーマンスモードでは、

  • Flash Playerのレンダリング品質は「低」
  • 3D描画部分の解像度を 1/2 にする
  • 3Dのクリッピング距離を少しだけ狭くする

という感じに調整。

ポリゴン数をなるべく削らずにパフォーマンスを上げるには、Viewport3D の解像度を落としてやるのが効果的です。まあ画質「低」とあいまって、ガッビガビになりましたけど。

quality

気をつけなければいけなかったのは、低解像度の Viewport3D を通常サイズまで拡大するために1回ビットマップに落とし込んでやる必要があるってこと。BitmapData.draw を使って3D描画をキャプチャし、キャプチャしたビットマップを拡大させて等寸・低解像度の状態を作っています。

デモ

レースゲームデモ画面

» ゲームをプレイする (要:Flash Player 9.0.115.0以降)

このゲームのソースファイル Main.as はこちらから参照できます。

描画モードを切り替えるには、プレイ中に [Q] キーを押してください。
劇的、とまではいかないとしても、私の環境だと通常の60%くらいに負荷が下がりました。

9 月 23 2008

PV3Dでレースゲーム制作 16 - 距離に応じたレンダリング切り替え

Posted by tanjo at 3:45 PM

カメラからの距離に応じて、近くは精密なモデルを、遠くの方はザックリとしたものを表示分けできるようにします。もちろん Papervision3D にそんな都合の良いピンポイント機能はありません! FrustumCamera3D のニア・クリップを活かしてなんとか再現します。

(訂正)
サーセン。大嘘でした。
PV3D 2.0 Beta 1あたりで追加された SimpleLevelOfDetail を使えばカメラからの距離に応じて3Dオブジェクトを切り替えたりできます。
しかし!残念なことにオブジェクト単位の判定しかできない模様。私のように環境全体を1つの3Dモデルで作っちゃってる場合には使えませんね。環境全部が一気に入れ替わってしまいますから。

SimpleLevelOfDetail については、ぽりGさんのブログにデモ付きの解説があります。
» flashゲーム作成記 | PV3D:Beta版の機能をいろいろ調べてみた

建物を増やすには

今のコースだとそれほど意味はないけど、市街地みたいなコースを作るにはかなり重要になってきそうです。(超広大3Dを実現しているゲーム「Grand Theft Auto」からヒントを得ました。なんて言ったら大げさかな。)

どういうことかちょっと詳しく書きますと…
現行( » 前回デモ)では以前のエントリで書いたように FrustumCamera3D (PV3D最新版では Camera3D に統合)を使ってファー・クリッピングすることで、巨大な3D空間を限られた処理内で実現しています。
でもこれだけだと、遠くからも見えて欲しい建物もパイロンのような小さなオブジェクトも、同じ距離で判定されてしまいます。市街地を作るにはある程度遠くの建物も表示する必要がありますが、そうすると細かいオブジェクトのポリゴン数がやたらと増えて、処理のネックになっちゃうわけです。

3次元クリップ&2次元合成

そこで、3Dオブジェクトのレンダリングを、「すべての要素を描画する近距離」、「アバウトでいいから大きな建物だけ遠くまで表示する遠距離」の2つに分けることにしました。
具体的には、Camera3D や Scene3D を複数使い分けることで実現します。たとえば…

距離 Scene3Dにあるオブジェクト Camera3Dのクリップ Viewport3D描画順
すべての要素を含む精細モデル near=10, far=22000 最後に addChild
大きなものだけを含む簡易モデル near=13000, far=40000 先に addChild

viewport同士の合成は2次元の処理になります。つまり、「中距離~遠距離をレンダリングしたレイヤー」の上に、「カメラすぐ~中距離までのレイヤー」を重ねたような状態です。2D合成でも距離の順序は保たれているから無問題!なお床面はこれらとは別に、1番最初に描画しています。

近カメラの far と遠カメラの near が同じ値じゃないのは、近・遠距離の間に描画抜けが出ないように調整したため。こんなに重複させないといけないとは。。。

文字の説明だとわかりにくかったですね。
こういう描画になっています。この3つを合成。

synthe

本来なら、(同じカメラを使って)3Dオブジェクトごとにどのくらいの距離まで表示させるか設定できれば良かったんですけどね、最近のFPSみたいな感じに。残念ながらPV3Dでそういう描画は無理そうだったので2D合成でカバーしました。
近距離/遠距離用で2種類のモデルを読み込まなきゃいけなくなるのは難点だけど、まず当面はパフォーマンスの確保が優先、と。

デモ

レースゲーム・デモ画面

» ゲームをプレイする (要:Flash Player 9.0.115.0以降)

このゲームのソースファイル Main.as はこちらから参照できます。

相当注意深く見ないと何が変わったのか全然分かりませんね…。第1コーナーを曲がったあととか最終コーナーの緑色の建物とかはまだ分かりやすいでしょうか。遠くの色が薄い状態の建物が、今回追加した遠距離レンダリングです。

9 月 10 2008

PV3Dでレースゲーム制作 15 - タイムアタック機能

Posted by tanjo at 10:05 PM

ようやくタイムアタック機能の実装です!ゲームだけしたいかたは下の方にスクロースしてね。

前回までで周回を正しく判定することができるようになりました。なので、あとは「周回が始まってから~次の周回になるまでの累計フレーム数」をカウントしていけばいいだけです。新しい周に入ったら、またゼロからカウントし直す、と。
内部処理ではラップタイムはフレーム数で扱っています。処理しやいからです。分・秒・小数に変換するのは表示の時だけでいいでしょう。
1つ前の周のカウント数とか、累計カウントの最小値とかを変数に保存しておけば、それぞれラスト・ラップタイム、ファステスト・ラップタイムが作れます。おお、レースゲームっぽい!

この辺の処理はあまりにフツーですので割愛させてもらうとして、今回は取得したフレーム数を「XX分YY秒ZZ」と2桁区切りの時間に直す方法を紹介したいと思います。

まずは、フレームを分、秒、小数に変換します。

function frameToTime(frames:int):Array
{
	var time:Array = new Array();
	var fr:Number = stage.frameRate;
	time[0] = Math.floor(frames / (60 * fr));          //分
	time[1] = Math.floor((frames % (60 * fr)) / fr);   //秒
	time[2] = Math.floor((frames % fr) * 100 / fr);    //小数
	time[0] = zeroPadding(time[0], 2);
	time[1] = zeroPadding(time[1], 2);
	time[2] = zeroPadding(time[2], 2);
	return time;
}

フレーム数の int 値を引数に、分、秒、小数の整数値を配列に格納しています。
でもこれだけだと数値が2桁だったり1桁だったりバラバラです。そこで、必ず2桁になるように、数値を文字列値に変換してから配列を返しています。

数値を任意の桁数に直す関数。

function zeroPadding(number:Number, size:uint):String
{
	var str:String = number.toString(10);
	while (str.length < size)
	{
		str = "0" + str;
	}
	return str;
}

「ゼロパディング」と言うそうです。第1引数の桁数が第2引数の設定に満たないときは、頭に「0」を付け足して揃えてくれます。これくらい ActionScript の標準クラスにあるんじゃ…と思ってたけど、どうやら無いみたいでした。
ActionScriptでゼロパディング - 続・ken39arg を参考に、じゃなくて、そのまんま使っています。

実際にこれらを使用するには、たとえば累計フレームのカウント数が lapTime = 2345 だったとすると、

var lapTimeArr:Array = frameToTime(lapTime);
trace("TIME : " + lapTimeArr[0] + "'" + lapTimeArr[1] + "." + lapTimeArr[2]);

これを実行すると

TIME : 01'18.16

と返ってきます。

久々のデモ!

が、しかし、たいして進展したように見えません。。。

ゲームをプレイする

ゲームをプレイする (要:Flash Player 9.0.115.0以降)

*このゲームのソースファイル Main.as はこちらから参照できます。

今回からFlexコンパイルに移行しました。ソースも以前よりはスマートにしたつもりです。主な追加機能は、データファイルのプリローディングとタイムアタック。動作が重いときは「右クリック」 > 「画質」 > 「低」を選んでください。

ちなみに私の最速タイムは 00′50.96 でした。

9 月 07 2008

PV3Dでレースゲーム制作 14 - 色情報を操る

Posted by tanjo at 11:22 PM

前回の続きです。
周回カウントの判定自体は前回のエントリの内容をそのままコードにすれば良かったので問題なくできました。

実は難しかったのは、コースデータ画像にグラベルとか壁という「地形情報」と、周回判定用の「チェックポイントエリア」、この2つを両方記録して、しかもプログラムは別々に処理できるようにしないといけない点。(前回はサラッと流してたけど。)
データ画像と変換した巨大配列を2つず作ってもできるけど、何とか工夫して1セットで、しかも効率よく実装できないものか…?色情報の扱い方がカギとなりました。

*データ画像→巨大配列で判定する話はこちらのエントリ

いろいろ考えた末、色情報を R/G/B 別々に扱えばできそう、という結論に。
どういうことかというと、地形情報は Blue の値で表し、チェックポイントは Green の値で表すというわけです。(Red は未使用。そのうち別のデータに使うかも。)
データ画像はこんな風になりました。

dt_course01

色情報 地形情報 チェックポイント
0x****ff ターマック -
0x****99 グリーン -
0x****66 サンド -
0x****00 コース外 -
0x**ff** - 判定用エリア1
0x**cc** - 判定用エリア2
0x**99** - 判定用エリア3
0x**66** - 判定用エリア4
0x**00** - エリア外

色はこの2つの組み合わせで決まります。たとえば、「判定用エリア1の砂地部分」は「0×00ff66」で塗られます。
Photoshop のレイヤー合成で「スクリーン」を使えば、「000000」+「000066」+「00ff00」→「00ff66」となり、別々のレイヤーに青・緑それぞれ作っておいても重なれば自動的に期待どおりの色になってくれます。
これなら画像作るのが楽チン!

synthe

RGB16進数の色分解

今度はプログラムのお話。
RGBのデータから、必要な部分の単色の値だけを抜き出す方法です。

複数の情報(地形/チェックポイント)を格納しないといけないので、エリア判定用の巨大配列には、getPixel で取得した16進数の uint 値をそのまま代入していきました。データを取り出すときに、地形情報が欲しいときは Blue の値を取得、チェックポイント判定の時は Green を取得するようにすればOK。

//コースデータの作成
groundData = new Array();
var groundBitmapData:BitmapData = groundBitmap.bitmapData;
for (var i:int = 0; i < 560; i++)
{
	groundData[i] = new Array();
	for (var j:int = 0; j < 420; j++)
	{
		groundData[i][j] = groundBitmapData.getPixel(i, j);
	}
}

たとえば、(100, 200)マス目の情報を参照するには、以下のようになります。

var color:uint = groundData[100][200];
var colB:uint = color & 0xff;		//Blueの値(地形情報)を取得
var colG:uint = (color >> 8) & 0xff;	//Greenの値(チェックエリア情報)を取得

参考サイト:flair4 blog - AS3.0 16進数の色分解ではまった事

「&」はAND演算子、「>>」はビットシフトです。何をしているかは、参考サイトの記事下の方にとても分かりやすい解説があるので、気になるかたは参照してください。
今までビット演算ってどんなとき使うんだろ、、とか思っていたけど、こういう使い方もあるんですね。

あとはGreen取得値を元に前回の周回カウントアルゴリズムを実行するだけ。ちょっと遠回りしましたが、ようやく周回のカウントが実現できました。

次回、タイムアタック付きのデモ公開、…できるといいな。

Next »