2008/11/7 金曜日

ゲームを作ってみよう 〜その9〜

第9回『弾が出ます』

duplex第7回『動く物を作ろう』で自機が動くとこまでやりました。
でも動くだけ。やっぱりシューティングゲームだったら弾が出なきゃいけないよね。
そんなわけで弾を撃ちましょう。弾を撃つ、男のロマンだねぇ。

しかし前回の記事は長くて疲れませんでしたか?

なーに心配しなくて結構、絶対疲れてるはずだから今回は短くまとめようと思います。
ね、短いって良いよね。短いって最高ぉぃひゃっほぉい!

さて今回に必要な事を考えてみましょう。

1)弾の絵
2)マウスクリック判定の追加。
3)弾のプログラム

当然弾の絵はムービークリップにしちゃいます。
弾の絵は当然として、マウスクリックの判定の追加、これは弾を撃つにはマウスクリックが必要なのはゲームとして当然です。
ですが、そのプログラムは何処に書けば良いのか?
今あるのはBounce・Play・Shipの3つのクラス。
あなたならどのプログラムで判定させますか?

当然ですが、弾の発射位置は自機の座標からとなります。
ということは最低でも自機の座標が分からないと美味しくありません。
となると、BounceやPlayで処理しようと思えばShipが持つムービークリップの座標を何らかの形で取得しなければいけなくなります。
てーことはだ、当たり前かもしれませんがShip内にマウスの判定処理を追加するのが一番楽って事です。

ですが勘違いをしてはいけません。
今回のShip内にマウス処理を追加するのは楽だからやるのであって、しっかりと作ろうと思えばマウスを管理する所を作り、そこで処理させたほうがDebugは楽になる場合もあります。
まぁあれです、ワシは楽なほうを選ぶ!

最後は弾のプログラムです。
とにかく今回は弾がでりゃええやって事で真上に直線的に飛ぶように作ります。
これもまた一つのタスクとして作成しちゃいます。
せっかく作ったタスクシステムはがんがん活用しましょう。

弾のタスクとして作るクラスは、Myshotと言うクラス名にしました。

 

import nigoro.lib.duplex.Task.*;

class source.task.Myshot implements Task2{

private var mc:MovieClip;

public function Myshot(mmc:MovieClip,x:Number,y:Number)

{

mc = mmc.attachMovie(”p_shot”,”p_shot”+mmc.getNextHighestDepth(),mmc.getNextHighestDepth());

mc._x = x;

mc._y = y;

}

//タスクシステムに登録した際に呼び出される。

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{

 mc._y -= 8;//弾のY座標を上に移動させる

 //もし弾の座標が0未満になれば画面外へ行ったとみなしタスクを削除する

if(mc._y < 0) return false;

 return true;

}

 //タスクを削除する際に必ず呼ばれる。なんか処理したい事を入れておけ

public function finalize(tc:TaskCenter2):Void{

mc.removeMovieClip();

}

 //このタスクとやり取りしたい場合これを使え

public function talkTask(mess:Array, tc:TaskCenter2):Array{

return mess;

}

}

 弾の絵ですが、そのムービークリップのリンゲージに p_shotという名前を付けてください。後は非常に単純なつくりです。毎フレーム呼ばれるrunメソッド内でY座標を8ピクセルづつ上に動かし、0未満になったら消えるというだけのプログラムです。ただ企画書では壁に跳ね返ったり、角度があったりとこんな単純な処理で終わるものではありません。ですが、初めはマウスボタンを押したら弾が出る、この単純な事を出来る様に集中するべきです。初めからあれやこれやと妄想しちゃうと作れずじまいなんて事になっちゃうでしょ?。結局こういう細かい作業レベルまで分解しないとやりづらいんですよ。

 

そんなわけで次に前回作ったShip.asを改造しちゃいましょう。

 

import nigoro.lib.duplex.Task.*;

import source.task.*;

 

import Mouse.*;

 

class source.task.Ship implements Task2{

private var dmc:MovieClip;

 

private var mc:MovieClip;

private var st:Number;

 

private var mouse_f:Boolean; // マウスが押されたらtrueを入れる

 

public function Ship(mmc:MovieClip)

{

dmc = mmc;

 

mc = mmc.attachMovie(”ship”,”ship”+mmc.getNextHighestDepth(),mmc.getNextHighestDepth());

mc._x = 320;//自機の初期座標X

mc._y = 400;//自機の初期座標Y

 

st = 0;

 

Mouse.addListener(this);

mouse_f = false;

}

 

//タスクシステムに登録した際に呼び出される。

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;

}

 

if(mouse_f){

tc.setTask(new Myshot(dmc,mc._x,mc._y));

}

 

mouse_f = false;

 

return true;

}

 

//タスクを削除する際に必ず呼ばれる。なんか処理したい事を入れておけ

public function finalize(tc:TaskCenter2):Void

{

mc.removeMovieClip();

Mouse.removeListener(this); // リスナー削除

}

 

//このタスクとやり取りしたい場合これを使え

public function talkTask(mess:Array, tc:TaskCenter2):Array

{

return mess;

}

 

function onMouseDown(){

mouse_f = true;

}

}

 

色を変えた部分が今回追加されて部分となります。

 

マウスの入力が行われたら呼び出して欲しいと思った場合、Mouse.addListener(Object)を使い自分を登録する必要があります。つまり

 

Mouse.addListener(this);

 

これがその命令の部分です。マウスのイベントを扱うときの一連の流れですが

 

1)addListenerを使い自分にマウスイベントを送って欲しいと要求する

2)自分の中にマウスイベントを受け取るメソッドを書く

3)不要になったらremoveListenerを使い、もうマウスイベントは要らないよと通知する

 

となります。この一連の処理に必要なクラスがMouseクラスなんですが、ここがFlashの不思議なところ。Mathなどはimpoertする必要が無いのに、Mouseimportする必要があります。しないとコンパイル時に、Mouseって知らねーよと怒られます。C言語の様にstadio.hIncleudeしてるから別件でMathとかやんなくて良いよってなら話は分かりやすいんですがね。とにかく、同じAPIでもimportを必要とするものと、そうでないものがあるってわけです。

 

そんなわけでimport Mouse.*;があるわけなんです。

 

次にマウスイベントを受け取るメソッドはどうでも良いのか?というとこれまた違いまして、ちゃんとしたメソッド名が決まっています。幾つかあるんですが、今回使いたいのは、マウスボタンが押されたときに通知して欲しいメソッドです。そのメソッド名は onMouseDownと決まっています。そんなわけで今回追加したメソッドが

 

function onMouseDown(){

mouse_f = true;

}

 

こんなのですね。後はこのタスクが消される=マウスイベントも不要になるってことでfinalizeメソッドに

こんなのですね。後はこのタスクが消される=マウスイベントも不要になるってことでfinalizeメソッドに

Mouse.removeListener(this); // リスナー削除

が追加されてます。これで一連のマウスイベントの流れは終わりです。他にもマウスイベントはあるので調べてみるとよりいっそうの理解を得ることが出来るかもしれません。

 

マウスイベントの処理は分かったって事にして、あとは弾を出す処理に入ります。

 

弾を出すのは簡単です、先に作ったMyshotクラスをタスクシステムに登録するだけで事たります。問題はそのタイミング取りです。

 

mouse_fというBoolean変数がどこで何をやっているか眺めてみましょう。

 

まず、コンストラクタ内でfalseを入れて初期化します。

 

次にrun()メソッド内でif文に使われています、これを見るとtrueであればMyshotクラスをタスクシステムに登録するとなっています。ということはfalseで初期化されてるので、マウスボタンを押さない限り何も行われません。

 

その下、同じくrun()メソッド内でまたfalseを入れています。これでは毎フレーム無駄な処理を行ってるようにも見えますが、当然意味があります。

 

最後に onMouseDownメソッド内でtrueが入れられています。 onMouseDownメソッドは先に書いたとおり、マウスのボタンが押された際に呼ばれるメソッドです。

 

ということは、マウスが押されるとmouse_ftrueになる、その後run()メソッドが呼ばれるとif文の中が処理される、最後にfalseが入れられて次の処理からはまたif文内が処理されない。こういう仕組みになってるわけです。

 

しかしmouse_f = false;なんて毎フレーム処理するよりif(mouse_f){}の中に書いたほうが無駄な処理が省けるんじゃないかと思うかもしれません。そうすりゃ、マウスがクリックされた時だけmouse_f = falseの処理をやれば良いのでパフォーマンス上優位です(ものすごく微々たる話ですが)。ただある条件下だとif(mouse_f)自体に飛びたく無いという処理が必要になるかもしれません。実際そういったプログラムは何度も作ってきました、そんなときif文の中に入れておくと後々忘れていてBugの元になったりもします。そんな経験から、まぁ毎フレーム絶対falseを入れるのが安全だなってわけでこんなやり方でやってます。

今回はこれで終わりです、弾はでました?。出来たものは暫く弄ると、何か問題があると気づくことが多々あります。完成品とソースファイルを置いておくので、とりあえず、何か問題が無いか、弄りながら考えるのも良いかもしれません。

バウンスショット第9回完成品
バウンスショット第9回完成品ソースファイル

・・・しかし、短くまとめるって出来ないもんだなこりゃ。

  1. 第1回『ゲーム製作の流れ、一から見せます』
  2. 第2回『企画内容を吟味しよう』
  3. 第3回『仕様を詰めておこう』
  4. 第4回『では、仕様書をまとめましょうか』
  5. 第5回『ゲーム制作のためのツールを作る。・・・・いやじゃ!』
  6. 第6回『外部ファイルを読み込ませてみよう』
  7. 第7回『動く物を作ろう』
  8. 第8回『グラフィックデザインを考えるのです』

ここまで出てきた資料

 




コメント (0) »

この記事にはまだコメントがついていません。

コメント RSS トラックバック URL

コメントをどうぞ


Copyright (C) Nigoro Allright Reserved. Powered by ASTERIZM