第7回『動く物を作ろう』
前回外部ファイルの読み込みを少々説明しましたが、今回はまるっきり飛ばして自機を作成しようと思います。
ほら、なんであれ自分で作ったものが動く時が一番楽しいじゃないですか。
実は中の人から、読んでる人が楽しくなるように書けと言われたのです。
ちぃ、ワシと一緒に地獄へダイブ計画が台無しです。
さて、自機を作るのであれば、まず何が必要か考えて見ましょう。
1)絵
以上です。
え、簡単すぎる?
だったら他に何が必要か言ってみろ、こら。
とまぁ読者に喧嘩を売るなんて、普通はやらないですよね。
でも良いんです、ここは普通じゃないから。
絵は外部で作ってもFlashのツールで作ってもかまいませんが、とにかくその絵をMC化する事が必要です。
Flashの基本的な使い方はここでは書かないので、MCの作り方が分からない人はマニュアル等を良く見て基本的な事は出来るようになっておいて下さい。
ところでグラフィックにはFlashなどでおなじみのシェイプや、ペイントツール等でおなじみのビットマップ等があります。
基本的にはシェイプで描かれた絵よりビットマップで描かれた絵の方が高速に描画されて、ゲームの処理を軽くする事が出来ます。
ただ、シェイプで描かれた画像は回転、拡大等を行っても画像崩れが起こりにくくデザイナさん達は、基本的に此方を重要視されてるみたいです。
だがあえて書かなければなりません。
グネグネdot萌えと。
ねじれるドット、画面を覆いつくさんばかりのドット、A-JAXやらメタルホークやら、はては、OUTRUNにAfterBunner、巨大化するドットは綺麗な線では補えない、
何も言えぬ迫力が在るのです。それは目の錯覚、ごまかしで生まれた視覚効果として決してして無くしてはならないと、そうワシは思います。
ちなみに、最近はドットではなくPixel(ピクセル)と言う方が一般的なんだとか。
話を戻しますが、基本的にシェイプで描かれた絵の方が処理が重いと描きましたが、必ずそうともいえない様子です。
これは多分個々の環境にもよると思いますが、NIGORO作のFLASHゲームにある、ひっこしぽろぽろでは、ワシのPCに限って逆転現象が起きました。
つまりビットマップよりシェイプの方が軽かったってわけです。
ところが、ワシ以外のPCではやはりビットマップが軽かったらしく公開板はビットマップでMCを作成しました。
皆さんもこの方法が最速だと過信せずに、テストは出来る限り多くのPCでやった方が良いかもしれません。
さて、自機のMCが出来たら基準点を中心に持ってきましょう、というより基準点に自機の中心を持っていきましょう。
整列を使えば間単に出来るはずです。
ここからプログラムの時間です、以前のワシの経堂の記事「開発の中心にタスクあり」で公開したFlashのライブラリを使います。
duplexが使ってるライブラリのソース
さてさて。早速作り始めるのですが、まずは自機を動かすにはどんなプログラムが必要になるか考えてみましょう。
考えた人だけ下へ進んでください、考えてない人は熱が出て倒れるまで考えてください。
こういう何が必要なのかを考えるとき、ワシはよく逆算方式で考えます。
要はこれを作りたいからこれが必要だな、というやつ。それで考えると。
1)自機を動かすプログラム
2)1)を動かすプログラム
3)2)を動かすプログラム
自機を動かすプログラムが必要なのはすぐに分かると思います。
でも2)3)とは何でしょうか?。
もう少し考えを進めると、他に動くものとして敵がいるはずです。
それと共に、自機と敵が好き勝手動いたとしても当然ゲームになりません。ゲームには一連の流れを制御する所と、各々の処理の調停を行う場所が必要になります。
3)とはゲームの一連の流れを制御する所です。タイトル - ゲーム - ゲームオーバーと言う流れを制御するものが必要です。
2)とはゲーム中のタスクを処理する所です。自機、敵、弾などの動きを監視し、衝突判定、スコア計算等を行います。
タスクがわからない?そういう人は「開発の中心にタスクあり」から出直してこい。
プログラムとは、見えない部分に多くの処理があります。
こういった所は普通の人には分かってもらえないんですが、仕方ないのです。だって見えないんだから。
だからIT土方なんて産まれるんですが、芸術家も大抵は死んでから評価されるんで、人間みな平等です。
これでプログラムの大まかな流れは決まったと思います。
Flash上で外部スクリプトを呼び出す方法は既に書いてます。
あくまでワシ流のやり方ですが、それに沿った作りにするので分からない人はこの、「ゲームを作ってみよう」シリーズを読んでみてください。
次にプログラムソースをどこに置くか?という問題が発生します。
これはあくまでワシのやり方ですが参考になればどうぞ。
Duplex流ディレクトリ構成

これらがActionScriptと言うフォルダに入っています。
Flaファイルゲーム別のフォルダで一番上になります。
そこにソース用フォルダを作り、必要に応じて階層を作って行くやり方です。
なんでもそうですが、クラスパスの設定は重要でそれが正しくないと動きません。
以前上げたライブラリにキチンとクラスパスを通し、今後作っていくクラスファイル(.asファイル)もきちんとパスを通さなければ動かないので注意です。
では3)から作って行きましょう。
import nigoro.lib.duplex.Task.*;
import nigoro.lib.duplex.DB.*;
import source.task.*;
class source.Bounce{
private var ftc:TaskCenter2;//Nigoroライブラリのタスクシステム
private var btc:TaskCenter2;
private var db:MiniDB2;//NigoroライブラリのミニDB
private var st:Number;
private var dmc:MovieClip;
private var dbmc:MovieClip;
function Bounce(mc:MovieClip)
{
st = 0;
dmc = mc;
dbmc = dmc.createEmptyMovieClip(”db_g”, dmc.getNextHighestDepth());
db = new MiniDB2();
ftc = new TaskCenter2(500,db);
btc = new TaskCenter2(10,db);
}
public function mainLoop()
{
btc.playTask();
switch(st)
{
case 0://タイトル初期化
st = 10;
break;
case 10://ゲームの初期化
ftc.setTask(new Play(dbmc,this));
st = 10000;
break;
case 10000://wait
break;
}
ftc.playTask();
}
}
これは以前説明した方法でmainLoop()を呼び出すという条件下で動いています。
分からない人はバックナンバーを読め、読んで分からなきゃ、多分ワシの下手な文章のせいなので諦めずに頑張れ。
ここで作った Bounceと言うスクリプトは基本的にメインのクラスとなります。
ここでタイトル表示やゲームシステムへの移行等を管理するわけです。
今回は最初なので、この短さですが、後でどんどんと増えて長くなって行きます。
では説明ですが、import文、これも以前説明をしているので詳細は省きます。
import nigoro.lib.duplex.Task.*;
import nigoro.lib.duplex.DB.*;
この2つはワシのライブラリを使えるようにするためのおまじないです。
ただし、importをしたとしても、そもそもクラスパスが通ってないと意味がありません。
確実に設定をしましょう。これは上で書いたワシのソースの置き方に沿った置き方をしている場合、クラスパスをActionscriptに通していれば動いてくれます。
import source.task.*;
こいつは、同じようにクラスパスがちゃんと設定されている事が条件です。
これはワシのやり方であれば、個別のゲームフォルダにクラスパスが通っていれば動きます。
ではコンストラクタの中身を説明します。
1 : st = 0;
2 : dmc = mc;
3 : dbmc = dmc.createEmptyMovieClip(”db_g”, dmc.getNextHighestDepth());
4 :
5 : db = new MiniDB2();
6 : ftc = new TaskCenter2(500,db);
7 : btc = new TaskCenter2(10,db);
1行目はmainLoopでの処理場所を決めるSwitch命令用の変数の初期化。
2、3行目は送られてきたMCを保存し、そのMCを使い新しい空のムービークリップを作成しています。
新しく作ったムービークリップは今後背景や自機などの表示物に使われます。
5行目はNIGOROライブラリ中にあるミニDBクラスを作成しているもので、これは6、7行目で生成されているタスクシステムで使用されます。
以前も説明しましたがこのミニDBとは名前だけデータベースっぽい、単なるArrayだと思ってもらって結構です。
6、7行目もNIGOROライブラリ中にあるタスクシステムの生成です。
TaskCenter2の引数は(処理可能最大タスク数、MiniDB)となっています。
見れば分かるように5行で生成したMiniDBを両方のタスクシステムの引数として渡しています。
同じ物を渡すことで、MiniDBに登録したデータを両方のタスクシステムで参照出来る様にします。
処理可能最大タスク数はその名の通りこのタスクシステムで処理できるタスクの最大数の設定です。
ただFlashのActionscriptはその言語特性から必要ならどんどんメモリを確保して行けるもので、ワザワザ最大数の設定等は滑稽に映るかもしれません。
しかし、最大数を設ける様にし、最大数を超えるとメッセージを返すようにすればそれだけでDEBUGが捗ります。
大した事はなさげなのですが、今まで意外とこのお陰でタスクの開放忘れBUGに気づかされた事もあります。
まぁ転ばぬ先の杖とでも思ってください。
そしてmainLoopメソッド。
これは毎フレーム呼ばれる部分です。なぜここが毎フレーム呼ばれるのかが分からない人は、以前の記事を読み返してください。
1 : btc.playTask();
2 : switch(st)
3 : {
4 : case 0://タイトル初期化
5 : st = 10;
6 : break;
7 : case 10://ゲームの初期化
8 : ftc.setTask(new Play(dbmc,this));
9 : st = 10000;
10 : break;
11 : case 10000://wait
12 : break;
13 : }
14 :
15 : ftc.playTask();
1と15はタスクシステムに登録されたタスクを処理する命令です。
基本的に描画より先にやりたいタスクはftcに登録し、描画より後に処理したいタスクはbtcに登録します。
この二つはコンストラクタで生成したタスクシステムですね。
さて、ここのメインとなる処理の場所を決めるswitch。2~13行を見てください。
stはコンストラクタで0と設定されていたので、初めて来た時は当然case 0:が処理されます。
しかし単純にst=10なんて処理をして1フレーム目は終了してしまいます。
コメントではでかでかとタイトルの初期化なんて書いているのにです。
でも今回ではタイトル画面を作る等はやりません、あくまで自機が動く様になるのが主題です。
ってなわけですが、将来絶対にタイトル画面を作ることにはなるわけで、それなら今のうちからその部分を作るのは有りだと思いませんか?
こんな簡単な部分でも作っておくかどうかで少しは気分が違います。
8行目では、ftcにタスクを登録しています。
一度登録すると登録抹消するまではplayTask()により延々と呼び出される事となります。
当然登録されるものはある規則に乗ったものでなければいけません。
その規則とはPlayメクラスの生成で説明します。
2)の生成、このスクリプトは、ワシ的なフォルダの説明で言うtaskフォルダに保存します。
import nigoro.lib.duplex.Task.*;
import soruce.task.*;
class soruce.task.Play implements Task2{
private var fmc:MovieClip;
private var mmc:MovieClip;
private var bmc:MovieClip;
private var st:Number;
private var oya:Object;
public function Play(dmc:MovieClip,o:Object)
{
bmc = dmc.createEmptyMovieClip(”b_g”, dmc.getNextHighestDepth());
mmc = dmc.createEmptyMovieClip(”m_g”, dmc.getNextHighestDepth());
fmc = dmc.createEmptyMovieClip(”f_g”, dmc.getNextHighestDepth());
st = 0;
oya = o;
}
//タスクシステムに登録した際に呼び出される。
public function init(no:Number, tc:TaskCenter2):Void{
tc.setTask(new Ship(mmc));
}
public function frun(tc:TaskCenter2):Boolean{
return true;
}
//タスクシステムからの常時の呼び出しはここ。返り値がtrueだとそのままだが、falseを返すとタスクの削除となる
public function run(tc:TaskCenter2):Boolean{
return true;
}
//タスクを削除する際に必ず呼ばれる。なんか処理したい事を入れておけ
public function finalize(tc:TaskCenter2):Void{
fmc.removeMovieClip();
mmc.removeMovieClip();
bmc.removeMovieClip();
}
//このタスクとやり取りしたい場合これを使え
public function talkTask(mess:Array, tc:TaskCenter2):Array{
return mess;
}
}
これはちょっと長いですが、タスクシステムというものを使うと必然的に長くなってしまいます。
さて、このタスクシステムに登録するためには規則があると書きました。
それは
class sorce.task.Play implements Task2
この部分。クラスにTask2というものをimplementsしています。
Task2というのはNIGOROライブラリの中にあり、タスクに必要なメソッド名だけが指定されたインターフェースと言う物です。
このインターフェースはメソッドの中身は記載されてなく、実体はimplementsしたクラスで記述しなくてはなりません。
この辺は以前に説明したと思いますが、以前説明したときに公開したライブラリと現在は若干異なっているので注意してください。
1 : bmc = dmc.createEmptyMovieClip(”b_g”, dmc.getNextHighestDepth());
2 : mmc = dmc.createEmptyMovieClip(”m_g”, dmc.getNextHighestDepth());
3 : fmc = dmc.createEmptyMovieClip(”f_g”, dmc.getNextHighestDepth());
コンストラクタの中のこれは、新たに3つのムービークリップを生成しています。
このムービークリップは昔のゲーム機で言うところのBGに当たる部分で、生成した順に下の層となります。
後は描画したい物にその物に合った層のムービークリップを渡す事で、重ね合わせの順番をあまり気にせず作ろうって魂胆です。
今回はmmcしかつかいませんけどね。
さーて、問題の記述
oya = o;
の説明をやりたいと思います。oは引数の設定でObjectと設定されてます。
oyaも変数の設定でObjectと指定されてます。
問題はなさそうですが、そもそもこのObjectって何が入っているのでしょうか?
それは3)を見れば分かります。
tc.setTask(new Play(dbmc,this));
二つ目の引数にthisとされてます。
これは自分自身を表しており、Bounceと言うクラスという事になります。
となると当然 o:Objectではなく o:Bounceと書くのが当然で、oyaの方もそうです。
実はBounceと書くことに問題はありません。
むしろそう書くべきでありObjectなんて書いてると、普通だったらドラム缶に生コンと一緒にいれられ東京湾に沈められてもおかしく無い所です。
それにプログラムの見通しも悪くなりますし、まともに動くとも思えません。
ところがFlashは「なぜだか」これでBounceとして処理できるんです。
不思議な気もしますが、そういう言語仕様だからしかたありません。
気持ち悪い人はObjectの部分をBounceとするべきでしょう。
ですがBounceと変更したところで、今度はコンパイルエラーになるはずです。
それはなぜでしょうか?
答えは書かないので考えてください、今までの流れからピンとこないとマズイ所です。
実のところ今回、このoyaを使用して何かを行う事はありませんが、なんでも練習です。
以前も説明はしたのですが、今回もさらっとタスクシステムの説明をしたいと思います。
タスクシステムは playTask()が呼び出されると、そのタスクシステムに登録されているタスククラスの run(TaskCenter2)メソッドを呼び出します。
またタスクシステムに登録した際に一度だけ init(Number, TaskCenter2):Voidが呼び出され、またタスクが殺される時にfinalize(TaskCenter2)が呼び出されます。
また条件によってはfrun(TaskCenter2)やTalktask(Array ,TaskCenter2)が呼び出されます。
この5つがTASK2により設定されているメソッドで必ず作らなくてはいけない物となっています。
finalizeを見てもらえれば分かりますが、生成したムービークリップはキチンと削除を行っています。
タスクは死ぬときに必ずここを呼び出すので、なにも考えずに削除したいものはここに書けば良い訳です。
そして最後、自機のプログラムに入ります。
こいつもタスクなのでtaskフォルダに保存します。
import nigoro.lib.duplex.Task.*;
import source.task.*;
class source.task.Ship implements Task2{
private var mc:MovieClip;
private var st:Number;
public function Ship(mmc:MovieClip)
{
mc = mmc.attachMovie(“ship”,”ship”+mmc.getNextHighestDepth(),mmc.getNextHighestDepth());
dmc = mmc;
mc._x = 320;//自機の初期座標X
mc._y = 400;//自機の初期座標Y
st = 0;
}
//タスクシステムに登録した際に呼び出される。
public function init(no:Number, tc:TaskCenter2):Void{
}
//タスクシステムからの常時の呼び出しはここ。返り値がtrueだとそのままだが、falseを返すとタスクの削除となる
public function frun(tc:TaskCenter2):Boolean{
return true;
}
//タスクシステムからの常時の呼び出しはここ。返り値がtrueだとそのままだが、falseを返すとタスクの削除となる
public function run(tc:TaskCenter2):Boolean{
var tmp:Number;
switch(st)
{
case 0:
tmp = (_root._xmouse - mc._x) * 0.15;
mc._x += tmp;
mc._rotation = tmp*2;
if(mc._rotation > 75) mc._rotation = 75;
if(mc._rotation < -75) mc._rotation = -75;
if(mc._x < 40) mc._x = 40;
if(mc._x > 600) mc._x = 600;
break;
}
return true;
}
//タスクを削除する際に必ず呼ばれる。なんか処理したい事を入れておけ
public function finalize(tc:TaskCenter2):Void
{
mc.removeMovieClip();
}
//このタスクとやり取りしたい場合これを使え
public function talkTask(mess:Array, tc:TaskCenter2):Array
{
return mess;
}
}
先ずはじめに確認しなくては成らないのが、先に作った自機のMCのリンゲージです。
ここではリンゲージ名をshipとしてインスタンスを試みていますので、当然自機のリンゲージはshipでないと表示されません。
mc = mmc.attachMovie(“ship”,”ship”+mmc.getNextHighestDepth(),mmc.getNextHighestDepth());
ここでムービークリップを生成しているので、もし違うリンゲージ名をつけたい場合はここを変更してください。
今回の自機はマウスを追っかけて左右に動き、それにあわせて若干傾きを変えるだけの物です。
1 : tmp = (_root._xmouse - mc._x) * 0.15;
2 : mc._x += tmp;
3 : mc._rotation = tmp*2;
4 : if(mc._rotation > 75) mc._rotation = 75;
5 : if(mc._rotation < -75) mc._rotation = -75;
6 : if(mc._x < 40) mc._x = 40;
7 : if(mc._x > 600) mc._x = 600;
1行で現在のマウス座標と自分の座標を比べ移動方向と量を割り出し、2行で移動させています。
1行目最後の*0.15の値を変えることで自機の移動速度が変わってきます。
ワシはいろいろな数値を割り当ててみて、0.15が一番しっくり来たので0.15にしています。
この辺は作り手の癖が出るところで面白い部分なので、自分にあったスピードを見つけてみるのも楽しいと思います。
さて3行で自機の傾きを設定しています。
左に移動中は左に傾き、右はその逆なのですが、_rotationに入れるデータも、左なら-右なら+でやることが出来ます。
それを考えればちょうど1で作った数値がよさそうです。
でもそのままでは数値が低くてあまり傾きません。そこで数値を2倍にするわけです。
この2倍もワシ的にこんな感じかなと出した数値なので、各々好きな数値を見つけてみると良いかもしれません。
4,5行は見た目の通り回転角の限度設定としています。
これは企画書を読めばわかりますが、そう書かれているので、その設定です。
6,7行で指定しているものは左右の限界移動座標ですね。
今は壁も何もないので、適当に40~600の範囲を移動出来る様にしています。
このへんはまだテキトーで良い部分ですね。
さて、上で説明した3つのファイルと以前公開したライブラリ、それに自機のMCがあればコンパイルをして動かす事ができる筈です。
できない場合の殆どの場合はクラスパスが間違っているだけだと思います。
クラスパスは目に見えづらいですが重要で、初心者にとっては頭痛の種になるかもしれません。
とにかく今回はクラスパスの訓練だと思ってしっかりやってください。
正直これが動けばもうFlashでスクリプトを扱う知識が付いたとも言えるとワシは思います。
というわけで、出来上がるとこんな風にうごくわけですな。
自機移動のみテスト
第7回分、完成品。(ZIP 約7.8MByte)
関連ページ
- 第1回『ゲーム製作の流れ、一から見せます』
- 第2回『企画内容を吟味しよう』
- 第3回『仕様を詰めておこう』
- 第4回『では、仕様書をまとめましょうか』
- 第5回『ゲーム制作のためのツールを作る。・・・・いやじゃ!』
- 第6回『外部ファイルを読み込ませてみよう』
ここまで出てきた資料