Tag : actionscript3

12 月 29 2008

Papervision3D 各バージョンのオンラインドキュメント

Posted by tanjo at 2:34 AM

拡張と改変が著しいPV3D。頼りのドキュメントが自分の使ってるバージョンと違ったりすると、思わぬ混乱を招いたりします(…しました)。
そんなわけで、各バージョンのオンラインドキュメントURLをまとめておきます。なお最近のリビジョンだと、ダウンロードしたライブラリの docs ディレクトリ以下にドキュメントが同梱されているようです。

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;
}

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

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] キーを押すとカメラモードが切り替わります。
(今までこんな直方体の車に乗っていたと!?)

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 月 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取得値を元に前回の周回カウントアルゴリズムを実行するだけ。ちょっと遠回りしましたが、ようやく周回のカウントが実現できました。

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

9 月 06 2008

PV3Dでレースゲーム制作 13 - 周回カウント作成

Posted by tanjo at 2:58 AM

ちょっとでも遊べる要素をまず作ろう、ということで、タイムアタック機能を実装してみたいと思います。

タイムを計測するには正しく周回をカウントしないといけなく、カウントするにはそのためのロジックが必要、と。結構長くなりそうなので、3回くらいに分けて書きます。(またしてもデモはしばらくお預けの予感…!)

周回の判定

まず最初は周回をカウントする方法について。

プレイヤーが逆走したり、途中で折り返してきても正しく判定できるようにするには、やっぱりチェックポイントを順走 or 逆走のどちらで通過したのかを、調べてやるのが手っ取り早そうです。
今作っているコースのように輪っか状に閉じたコースなら、チェックポイントはスタート(ゴール)地点に一カ所、コースを横切るように設置しておけばコトは足ります。そこを順走で通過したら1周カウント、というわけです。

でもこれだとチェックポイント上を往復しているだけでどんどんカウントされてしまいます。そこで、たとえば初期状態が「0」のカウンタを用意して、
 ◆ 「0」のまま順走通過したら1周したことにする。
 ◆ 逆走通過したらカウンタを「-1」減らす。
 ◆ 「0」以外の状態で順走通過したら「+1」増やす。
…という処理をしてあげれば、ちゃんと1周してこないとカウントされないし、ひねくれたプレイヤーが何周も逆走した場合には、その分何周も順走しないと次の周回に進めないようになります。

結局私はコースの中間地点にもチェックポイントを作って、2カ所で判定することにしましたが、基本的な考え方は同じです。チェックポイントを増やしたのは、セクションとかあった方がサーキットっぽくね?と思ったからです(笑)。

順走/逆走通過の判定

やりかたはいろいろありそうですが、私の場合は路面判定のときと同様、コースデータを記録した画像を使ってやっていこうと思います。
画像をもとに巨大な配列を作って、車の座標から今どういうエリアにいるのか割り出す方法です。(→割り出し方の具体例はこちらのエントリ

2枚の接するエリアを作れば、その境界を順走/逆走通過したかを調べることができます。
具体的には、たとえば2つのエリアをそれぞれA、Bとしたとすると、
 ◆ A上にいるときは、変数に「1」を代入する。
 ◆ B上にいるときは、変数に「2」を代入する。
 ◆ それ以外の場所では、変数に「0」を代入する。
とした上で、
 ◆ A上にいるとき、もし代入前に変数が「2」だったら、逆走している。
 ◆ B上にいるとき、もし代入前に変数が「1」だったら、順走している。
というふうに判定できるわけです。

pass

つづく。

8 月 23 2008

PV3Dでレースゲーム制作 12 - 3Dモデル読み込み編

Posted by tanjo at 8:41 PM

以前、SWFに埋め込んだ3DモデルをPapervision3Dで使用する方法を紹介しましたが、実は、ローダで読み込んでから描画するのも同じようなやり方でできます。
ゲーム的にも外部からロードした方が勝手が良さそうだし、今後はこっちを採用する方向でいきます。

*PV3DでCOLLADAを表示できた状態を基準に書いてます。COLLADAを表示方法はこちら
*以下、共通部分は以前の記事のコピペです。

それでは、手順を追っていきましょう。(ところどころ用語が間違ってたらすいません…。)
Papervision3D のマテリアルリストを使うので、まずはクラスをインポート。

import org.papervision3d.materials.utils.MaterialsList;

3DモデルのDAEファイルとテクスチャ画像を読み込み、それぞれ XML と Bitmap にインスタンス化しておきます。
参照:PV3Dでレースゲーム制作 11 - AS3で複数のファイルをロードする
↓↓インスタンス作成部分の抜粋。(宣言は割愛してます。)

courseXML = XML(loaderArray[0].data);
txMainBitmap = Bitmap(loaderArray[1].content);
txTreeBitmap = Bitmap(loaderArray[2].content);
txItemBitmap = Bitmap(loaderArray[3].content);

次にマテリアルの作成。

var mdCourse01Materials:Object = new Object();
mdCourse01Materials = {
	t_all_jpg:new BitmapMaterial(txMainBitmap.bitmapData),
	t_all_jpg_2:new BitmapMaterial(txMainBitmap.bitmapData),
	t_tree_png:new BitmapMaterial(txTreeBitmap.bitmapData),
	t_item_jpg:new BitmapMaterial(txItemBitmap.bitmapData)
};

テクスチャの Bitmap を元に、マテリアル用の Object へ BitmapMaterial を登録していきます。
ここで注意点!Object のキーは、COLLADAのマテリアル名を使います。(赤文字部分。記述は順不同でOK。)
ですので、もしマテリアル名に妙な記号とか2バイト文字とかが入ってる場合は、あらかじめCOLLADAを修正しておかなければなりません。
マテリアル名は、Blender(COLLADAインポート)ではアニメーションビュー、球体アイコンで確認できます。

COLLADAのXML内では material タグに定義されています。

<library_materials>
	<material id="t_all_jpg" name="t_all_jpg">
...

ただしここ以外の場所からもid名を参照していたりするので、手作業で名前を変更するときは、検索してそれらを一括で変えてあげないとダメかもしれません。

さてASに戻り、最後に3Dオブジェクト作成。

courseObjects = new Collada( courseXML, new MaterialsList(mdCourse01Materials) );

Collada() メソッドの第1引数に読み込んだ XML インスタンス、第2引数に先ほどの Object から作った MaterialsList を指定すればOK。

あとはいつもどおり、Collada を Scene3D に addChild して、renderScene で描画するだけ。(→参照
今回もゲーム的には進展なしなので、デモとかはありません…!

Next »