ステージが進むにつれゲームが難しくなる仕組み作りの続きです。
前回 [ ステージ構成にする その1 ] からご覧ください。
次のようにプログラムを変更します。
const LINE_MAX = 6; // 宝と敵の段数 var enemyArr:Array = []; var gemArr:Array = []; var turn:Vector.<int> = new Vector.<int>(LINE_MAX,true); var state:Vector.<int> = new Vector.<int>(LINE_MAX,true); var delcnt:Vector.<int> = new Vector.<int>(LINE_MAX,true); var beamFlg:int = 0; var xSpeed:Number = 0; var retCnt:int = 0; var retryWait:int = 0; var score:int = 0; var hisc:int = 0; var nokori:int = 0; var stageNo:int = 0; var clearCnt:int = 0; var stageWait:int = 0; initFunc(); //---------------------------------------------------- // システム関連の初期化 function initFunc() { trace("initFunc ---"); for( var i=0; i < LINE_MAX; i++ ){ enemyArr[i] = new enemy(); // 敵の作成 enemyArr[i].stop(); gemArr[i] = new gem(); // 宝石の作成 disp.addChild(enemyArr[i]); // ステージに表示 disp.addChild(gemArr[i]); } hisc = 0; hi_txt.text = hisc.toString(); initStart(); initGame(); // 毎フレームイベントの登録 stage.addEventListener(Event.ENTER_FRAME, mainloop); // マウスイベント 左ボタン押下のイベント登録 stage.addEventListener(MouseEvent.MOUSE_DOWN, m_down); } //---------------------------------------------------- // 毎フレーム処理 function mainloop(event:Event):void { // マウスの座標を取得して自機を動かす player_mc.x += (player_mc.mouseX / 5); if( beamFlg == 0 ){ // 0:未発射 1:発射中 // 未発射なら自機と動きを同期させる beam_mc.x = player_mc.x; } else { // 発射中のとき弾を移動する beam_mc.y -= 10; if( beam_mc.y < -30 ){ // 画面外へ弾が出たとき beamFlg = 0; beam_mc.x = player_mc.x; beam_mc.y = player_mc.y - 25; } } for( var i=0; i < LINE_MAX; i++ ){ // 爆発中 if( state[i] == 2 ){ delcnt[i]--; if( delcnt[i] == 0 ){ state[i] = 0; // 0:待機中 enemyArr[i].x = 530; enemyArr[i].gotoAndStop(1); } } } if( stageWait > 0 ){ stageWait--; if( stageWait == 0 ){ // メッセージを非表示 stage_txt.visible = false; stageno_txt.visible = false; order_txt.visible = false; ordernum_txt.visible = false; } return; } if( nokori == 0 ){ // ステージクリア clearCnt--; if( clearCnt == 0 ){ initGame(); // 次のステージへ } return; } moveEnemy(); // 敵の移動 if( retCnt == LINE_MAX ){ // 全ての宝石を奪われた gameover_txt.visible = true; // ゲームオーバーを表示 if( !retry_txt.visible ){ retryWait++; if( retryWait > 120 ){ retry_txt.visible = true; // リトライ表示 } } } } //---------------------------------------------------- // 敵の移動 function moveEnemy() { var ex:int; var ey:int; for( var i=0; i < LINE_MAX; i++ ){ // 出現中のみ以下の処理を行う if( !(state[i] == 1) ) continue; // 敵の移動処理 if( turn[i] == 0 ){ enemyArr[i].x -= xSpeed; // 宝石と接触したら引き返す if( (gemArr[i].x + 36) > enemyArr[i].x ){ turn[i] = 2; } // 左際まできたら右移動へ変更 if( enemyArr[i].x < 20 ){ turn[i] = 1; } } else { enemyArr[i].x += xSpeed; if( turn[i] == 2 ){ // 宝を連れて行く gemArr[i].x = enemyArr[i].x - 36; } // 画面外へ出たら待機中にする if( enemyArr[i].x > 580 ){ state[i] = 0; // 0:待機中 retCnt++; // 宝石を盗った回数 } } ex = enemyArr[i].x; ey = enemyArr[i].y; // 敵と弾との当たり判定の範囲を、見た目で調整する if( (beam_mc.x > ex-20) && (beam_mc.x < ex+20) && (beam_mc.y > ey-20) && (beam_mc.y < ey+20) ){ state[i] = 2; // 2:爆発中 delcnt[i] = 18; // 消滅までのカウント beamFlg = 0; // 0:未発射 1:発射中 enemyArr[i].play(); beam_mc.x = player_mc.x; beam_mc.y = player_mc.y - 25; score += 10; sc_txt.text = score.toString(); nokori--; // 敵残数 nokori_txt.text = nokori.toString(); if( nokori == 0 ){ // ステージクリア stageclear_txt.visible = true; clearCnt = 180; } } } if( Math.random() < 0.015 ){ // 敵の出現率 // 出現位置 var rnd:int = Math.floor(Math.random()*LINE_MAX); for( i=0; i < LINE_MAX; i++ ){ var lno = (rnd + i) % LINE_MAX; if( !(state[lno] == 0) ) continue; if( gemArr[lno].x > 530 ) continue; state[lno] = 1; // 1:出現中 turn[lno] = 0; // 0:左移動 enemyArr[lno].x = 530; break; } xSpeed += 0.1; // 敵移動速度アップ } } //---------------------------------------------------- // ゲーム関連の初期化 function initGame() { trace("initGame ---"); gameover_txt.visible = false; // ゲームオーバーを非表示 retry_txt.visible = false; // リトライを非表示 stageclear_txt.visible = false; // ステージクリアを非表示 stageNo++; // ステージ番号 nokori = stageNo * 5; // 敵残数 stage_txt.visible = true; // ステージ文字を表示 stageno_txt.visible = true; // ステージ番号を表示 stageno_txt.text = stageNo.toString(); order_txt.visible = true; // 指令を表示 ordernum_txt.visible = true; // 指令(数)を表示 ordernum_txt.text = nokori.toString(); stageWait = 180; beamFlg = 0; // 0:未発射 1:発射中 xSpeed = 1.5 + (stageNo * 0.5); // 敵の初期移動速度 retCnt = 0; // 宝石を盗った回数 retryWait = 0; // リトライ表示までの時間 sc_txt.text = score.toString(); nokori_txt.text = nokori.toString(); player_mc.y = 360; // 自機のy座標(固定) beam_mc.x = player_mc.x; beam_mc.y = player_mc.y - 25; // 弾の表示(半分 自機と重ねる) for( var i=0; i < LINE_MAX; i++ ){ // 敵の初期設定 enemyArr[i].x = 530; enemyArr[i].y = (i*42) + 60; // 宝石の初期設定 gemArr[i].x = 40; gemArr[i].y = (i*42) + 60; turn[i] = 0; // 0:左移動 1:右移動 2:右(宝付き) state[i] = 0; // 0:待機中 1:出現中 2:爆発中 } } //---------------------------------------------------- // ゲーム開始前の初期化 function initStart() { score = 0; stageNo = 0; } //---------------------------------------------------- // マウス左ボタン押されたとき function m_down(event:MouseEvent):void { if( retry_txt.visible ){ // リトライが表示中の時 if( hisc < score ){ hisc = score; } hi_txt.text = hisc.toString(); initStart(); // ゲーム開始前の初期化 initGame(); // ゲーム関連の初期化 return; } if( beamFlg == 1 ) return; // 0:未発射 1:発射中 beamFlg = 1; // 自機の位置に弾を表示する beam_mc.x = player_mc.x; beam_mc.y = player_mc.y - 25; }
変更点を見ていきます。
新しく3つの変数を追加します。
15行目、ステージ番号のカウント用
16行目、「STAGE CLEAR」メッセージを表示している時間
17行目、ステージ番号とクリア条件を表示している時間
変数名についてですが、自分なりに規則性を考えて名前付けした方が、見やすいプログラムに
なります。
たとえば時間をカウントするための変数なら、名前に Wait を付けるようにします。
16行目の clearCnt などは悪い例です。
stageWait と同様に clearWait の方が良いですね。
198-207行目、ステージ開始前に必要なメッセージを表示しています。
200行目では、クリア条件を作ってます。1ステージにつき5機ずつ条件が厳しくなります。
210行目、敵の移動速度を設定しています。ステージが進むにつれ徐々に早くなります。
214行目、敵残数を表示しています。
236行目、ステージ番号を0クリアしています。ステージが始まる前に initGame() を
呼び出して stageNo を1ずつ加算していくので0から始めます。
68-78行目、前回まで敵の移動処理にあった爆発中の処理を、ここに移動しました。
理由は、ステージクリアのメッセージを表示している最中に moveEnemy() の処理を
動かしたくなかったからです。
つまり、ステージクリアした瞬間に敵の動きを止めたかったのです。
79-89行目、ステージ番号とクリア条件の表示を stageWait が0になるまで表示します。
90-96行目、ステージクリアのメッセージを表示後、clearCnt が0になったら
次のステージを開始します。
initGame() を上手に作成することで、ステージの切り替えがこれだけで行えます。
これは前回のプログラムです。
移動処理の中にあった爆発中の処理を削除して mainloop() へ移しました。
162-167行目、敵が撃破されたとき敵残数を減らして、0になったらステージクリアの
メッセージを表示します。
185行目、敵を1機出現させるたびに少しずつ移動速度を上げていきます。
これをコメントにして遊んでみて下さい。
ステージが進むにつれ、ゲームの単調さを感じると思います。
このようなやり方でもゲームに緩急を付けることができます
今回の課題は少し長かったですね。お疲れ様でした。
これでゲームは完成しましたが、プログラムをより理解する為には
やはり改造するのが一番です。
次のような課題に挑戦してみましょう。
・ビームを2発当てないと撃破出来ない赤いUFOをときどき出現させる
・宝石を持っていかないフェイントするUFOを出現させる
・UFOもビームを撃ってきて、自機に当たるとしばらく動けなくなる
・タイトル画面を表示する
・ゲームオーバー後、過去10回分のスコアを表示する
上記のような新しい仕様を考えて改造するのもいいですが、わざとバグ(不具合)を
出すような改造も勉強になります。
88行目や 95行目をコメントにして実行してみて下さい。
当然、バグが発生します。
発生している現象を説明できれば、プログラム内容を理解していると言えるでしょう。
このような動作とプログラムを一致させるトレーニングも有効です。