Flex3 ブロック崩しもどきの作成 その5

簡単なゲームプログラミング その5


前記事:ボールを跳ね返すためのバーの作成


今回は前回作成したバーに衝突するとボールが跳ね返るようにしていきます
これはブロック崩しもどきの作成 その3で行った方法とほぼ同じようにやってみます


その3とその2では回りくどいやり方をしていて、衝突判定をする箇所と、ブロックのどの位置にあたったか判定している箇所が別になっていました

今回は一つのメソッドで判定するようにしてみます


以下のような方法で跳ね返すようにします


  1. ボールの下端座標this.x , this.x + radius)がBarの領域内にはいっているか判定する

  2. 領域内に入っていたら、ボールのvyを反転させる

領域内に特定の座標がはいっているかどうかの判定はRectangleクラスを利用すると簡単に実装ができます
必要なimport宣言


import flash.geom.Rectangle


上の宣言をBar.asに入力します


使い方


通常通りにRectangleクラスをnewします
newする際にどのサイズの領域を作成するか指定します


var rect:Rectangle = new Rectangle( x , y , width , height )


上記のように宣言すると( x , y )を左上の角として幅をwidth、高さをheightとした領域の四角が作成されます
ある点がその領域にはいっているかの判定は以下のようにします


rect.contains( 判定したいX座標 , 判定したいY座標 )


containsメソッドの結果はBoolean値で返ってきますので、そのままIF文の判定式として使用できます


以上よりBarにぶつかったかどうかの判定及び跳ね返り処理は以下のように記述できます



public function checkCollisionBar(ball:Ball):void
{
var barRect:Rectangle = new Rectangle(this.x, this.y, DEFAULT_BAR_WIDTH, DEFAULT_BAR_HEIGHT);
if (barRect.contains(ball.x, ball.y + ball.radius))
ball.vy *= -1;
}

判定領域をBarのx座標及びy座標を基準としたDEFAULT_BAR_WIDTH,DEFAULT_BAR_HEIGHTの大きさの四角を作成します

containsメソッドを使用し、ボールの下端座標がその領域内に含まれているか判定しています

含まれている場合にはTRUEが返ってくるので、ballのY方向速度を反転して跳ね返しています


このメソッドをBoardクラスのrunBlockBrokenから呼び出すようにしましょう
ただし、Boardクラスの何番目の子要素としてこのBarが登録されているかわからないので、(カウントしようと思えばできるが)getChildByNameメソッドで名前から取り出すようにしたいと思います



// initメソッドの以下の箇所にbar.name = "myBar";として
// Barに名前をつけるようにしておく
var bar:Bar = new Bar(_container);
bar.name = "myBar";
this.addChild(bar);


// runBlockBrokenメソッドの最後に以下を追加
var bar:Bar = this.getChildByName("myBar") as Bar;
bar.isCollision(ball);

このような感じで10行程度の追加でBarに衝突した際にボールを跳ね返すことができるようになりました

ただし、厳密な計算をしていないので、変な動きをすることがあります(特にBallの半径のサイズが大きいときや、速度がはやいときなど)
この辺りの処理はめんどくさいので、Ballの半径のサイズを制限するなどして対処することにします


スポンサーサイト

Flex3 ブロック崩しもどきの作成 その4

簡単なゲームプログラミング その4


前記事:ブロックとボールの衝突判定


今回はボールを跳ね返すためのバーを作成していきます


バーを作るだけならば、drawRectで長方形を作成すればできてしまいますが、マウスの動きと連動させて動くようにしてみましょう


Barクラスの作成


画面部品の基本であるSpriteを継承してBarクラスを作成します


  1. FlashDevelopのProjectManagerの[src]を右クリックします
    表示されていない場合はメニューバーの[View]→[ProjectManager]と選択します

  2. [New Class...]を選択し、ファイル名が[Bar.as]となるようにして[OK]を選択します

  3. public class Barとなっている箇所にSpriteを継承するためにextends Spriteと入力します

これで、Spriteを継承したBarクラスのベースが完成しました


必要なimport宣言


Spriteを継承したので、既に[import flash.display.Sprite]という箇所が作成されていると思います
この下の行に2つのimport宣言を追加します


  1. import flash.events.MouseEvent;
    これはマウスの動きを感知するために必要な宣言となります
    今回はMouseEvent.MOUSE_MOVEなどを利用します

  2. import mx.core.UIComponent;
    このimportは必須ではありません
    ただ、mxml上に設置したUIComponentの幅や高さを取得するためだけに使用しています

定数宣言とプライベイトメンバ変数の宣言



public static const DEFAULT_BAR_WIDTH:Number = 70.0;
public static const DEFAULT_BAR_HEIGHT:Number = 10.0;
public static const DUMMY_AREA:Number = 20;

private var _container:UIComponent;

DEFAULT*はBarのデフォルトの幅と高さを表します
DUMMY_AREAの使用方法は後述します
_containerは自分が設置される領域の幅と高さを取得するために利用します
ここは、UIComponentでなくても、_containerWidth:Number等にしても問題ありません


コンストラクタ


引数としてBarなどが設置される、UIComponentオブジェクトへの参照を与えることにします



public function Bar(parentComponent:UIComponent)
{
_container = parentComponent;
init();
}

private function init():void
{
// バーの初期座標の決定
this.x = _container.width / 2-DEFAULT_WIDTH/2;
this.y = _container.height * 4 / 5;

drawBar();

// イベントのセット
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}

コンストラクタ上では引数を_containerにセットしたあとにinit()が呼ばれます


init()では、Barの初期座標を設定しています。ここでは、BarのX座標(長方形の左上)が表示領域の半分の位置にくるようにしていて、Y座標(長方形の左上)が表示領域の高さにおける4/5の位置にくるようにしています


drawBar()はBarを描画するメソッドです


イベントリスナーの登録は2つしています
1つ目はなんとなくつけたイベントでマウスホイールをころころと回したときに発生するイベントを受け取るリスナーです
2つ目はBarを左右に動かすためにマウスが動いたという情報を受け取るためのリスナーです


drawBar()


この記事において、ドラックドロップでSpriteのサイズを変更する際に利用したダミー領域を作成してその上に実際に表示されるBarを描画します



private function drawBar():void
{
// Moveイベント用に見えない領域を描画
// 領域をみたい場合には第二引数を0以外にする
this.graphics.beginFill(0xFF0000, 0);
this.graphics.drawRect( 0, -DUMMY_AREA, DEFAULT_BAR_WIDTH, DEFAULT_BAR_HEIGHT + DUMMY_AREA*2);
this.graphics.endFill();

// 表示されるbarの描画
this.graphics.beginFill(0x336633);
this.graphics.drawRect(0, 0, DEFAULT_BAR_WIDTH, DEFAULT_BAR_HEIGHT);
this.graphics.endFill();
}

このダミー領域はMOUSE_MOVEイベント用に作成しています
ダミー領域がないと、MOUSE_MOVEイベントの発生する範囲が
( this.x , this.y )~( DEFAULT_BAR_WIDTH , DEFAULT_BAR_HEIGHT )
に限定されてしまい、中々Barが動かないということになることを防ぐためにMOVEイベントの発生する範囲を少し増加させています
詳細は上記の記事を参照してみてください


MOUSE_MOVEイベント発生時の動作


MOUSE_MOVEイベントが発生した際には、マウスの位置に追従するようにBarを動かしたいと思います



private function mouseMoveHandler(event:MouseEvent):void
{
if (event.localX >= this.width / 2 + 15)
this.x +=10;
else if(event.localX <= this.width / 2 - 15 )
this.x -= 10;

checkCollisionWall();
}

private function checkCollisionWall():void
{
if (this.x < 0)
this.x = 0;
else if (this.x + this.width > _container.width)
this.x = _container.width - this.width;
}

MOUSE_MOVEイベントが発生した際には、MOUSE_MOVEが発生した座標を元に動作を決めます
Barの半分より右側で発生したならば、右に10動かし、左側で発生したならば左に10動かすようにしています
この数値は色々といじってみるといいでしょう
個人的に色々いじったところこの数値だとスムーズに動作したため、10にしています


MOUSE_WHEELイベント発生時の動作


これはまったくいらない動作ですが、なんとなくつけてみたかったのでつけました
マウスのホイールを動かすとBarが上下に移動します



private function mouseWheelHandler(event:MouseEvent):void
{
if(event.delta > 0)
this.y -= 1;
else
this.y += 1;

if (this.y >= _container.height * 4 / 5 + 20)
this.y = _container.height * 4 / 5 + 20;
else if (this.y <= _container.height * 4 / 5 -20)
this.y = _container.height * 4 / 5 - 20;
}

マウスホイールを上に転がすとMouseEventのdeltaパラメータは負の値になります
下に転がすと正の値になります(マウスの種類によって違うかも)
この性質を利用して、deltaが正の値(マウスホイールを下に転がした場合)の時にはBarが下に移動し、負の値の時にはBarが上に移動するようにしています


その下のifの判定ではBarの位置の上限と下限を定めています
初期位置から+-20の範囲に収まるように設定しています


ここまでの完成例とソースはこちらから
なお、わかりやすいように、ダミー領域に色がついています
その色がついている箇所にマウスを持っていくとBarが動きます


MOUSE_MOVEの補足


MOUSE_MOVEイベントを利用して何かを動かそうとすると以下の2つのことが問題になります


  1. 動かしたいところがSpriteオブジェクトの領域外のため、MOUSE_OUTイベントが発生する

  2. 動かすことはできたが、横や縦にゆれるような動きになる

1に関してはSpriteの周りに今回のようなダミー領域を作成することで解決します


2に関してはMOUSE_MOVEイベントが発生しても動かない領域を作ることで解決します
理由は以下の通りです
blockbroken_3_1.gif
そのため、MOVEが発生しても、座標が変化しない領域を作ることで連続で動くことを防ぐことができるようになります
このぶれるような現象は座標をlocalX等に移動する際に発生することが多い感じがします


次の記事:Barにボールが衝突した際にBallを跳ね返す

Flex3 ブロック崩しもどきの作成 その3

簡単なゲームプログラミング入門 その3


前記事:ボールとブロックの当たり判定(衝突判定)を作成


今回はBlockとの衝突時にボールを跳ね返す処理を加えてみます
BallがBlockに当たる際にはブロックから見て全部で9方向あります


  1. 左上
    Ballの右下が衝突している状態


  2. ボールの左下と右下が衝突している状態

  3. 右上
    ボールの左下が衝突している状態


  4. ボールの左上と左下が衝突している状態

  5. 右下
    ボールの左上が衝突している状態


  6. ボールの左上と右上が衝突している状態

  7. 左下
    ボールの右上が衝突している状態


  8. ボールの右上と右下が衝突している状態

これらの状態を判別するために、Rectangleクラスを利用します
RectangleクラスにはあるPOINT(x座標,y座標)がRectangleクラスの範囲に入っているかを調べるcontainsメソッドが存在します
このcontainsメソッドを利用して、どの位置が衝突しているか調べていきます


Rectangleのcontainesメソッドを利用したサンプルコードは以下の通りです



var rectangle:Rectangle = new Rectangle(50 , 50 , 100 , 100);
trace( rectangle.containes( 30 , 40 ) );
trace( rectangle.containes( 80 , 80 ) );

Rectangleを(50,50)~(150,150)で作成します
1つ目のtraceでは( 30, 40 )がその領域に含まれているか判定しています
勿論含まれていませんので、falseが返ってきます
2つ目のtraceでは( 80, 80 )がその領域に含まれているか判定しています
これは含まれていますので、trueが返ってきます


このRectangleを利用して、ボールを跳ね返すメソッドを実装すると以下のようになります



public function rebound(ball:Ball):void
{
var blockRect:Rectangle = new Rectangle(this.x , this.y , this.width , this.height);

var left:Number = ball.x - ball.radius;
var right:Number = ball.x + ball.radius;
var top:Number = ball.y - ball.radius;
var bottom:Number = ball.y + ball.radius;

var leftTopFlag:Boolean = blockRect.contains(left,top);
var rightTopFlag:Boolean = blockRect.contains(right,top);
var leftBottomFlag:Boolean = blockRect.contains(left, bottom);
var rightbottomFlag:Boolean = blockRect.contains(right, bottom);

if ( leftBottomFlag && rightbottomFlag ) ball.vy *= -1;
else if ( leftTopFlag && leftBottomFlag ) ball.vx *= -1;
else if ( leftTopFlag && rightTopFlag ) ball.vy *= -1;
else if ( rightTopFlag && rightbottomFlag ) ball.vx *= -1;
else
{
ball.vx *= -1;
ball.vy *= -1;
}

}

まず、Blockと同じ座標をもつRectangleを作成します
次に、(なくてもいいが)ボールの左端、右端、上端、下端の座標を計算した結果をそれぞれleft,right,top,leftという変数に代入しています
Rectangelにおいて、左上、右上、左下、右下の座標が含まれているかを判定し、結果をleftTopFlag等に代入しています
最後にそれぞれにおいての跳ね返る方向を計算しています
例えば、最初のif ( leftBottomFlag && rightbottomFlag )においては
ボールの左下と右下が衝突している状態を示しているので、Y方向速度を反転しています
同様にして、X,Y方向のどちらか一方だけが反転する方向を判別し、それ以外ならば、X方向、Y方向両方とも反転するようにしています


このメソッドをBoardクラスのrunBlockBrokenから呼び出すようにします



public function runBlockBroken(ball:Ball):void
{
for (var i:int = 0; i < _blockArray.length; i++) {
// 壊れていたらcontinue
if (_blockArray[i].isDeleted)
continue;

// BlockクラスのisCollisionを呼び出す
if ( _blockArray[i].isCollision(ball) )
{
// ぶつかっていたら跳ね返す
_blockArray[i].rebound(ball);
_blockArray[i].deleteBlock();
// 一度に壊せるブロックを1つに制限しておく
return;
}
}
}

これらを実装した結果が以下のリンク先になります
Block_0_3.swf


だいぶブロック崩しに近くなってきましたね


次の記事はこちらから


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。