Flashゲーム作成講座

初心者からクリエイター志望まで Flashゲームの作り方 入門

ゲームの作り方を初心者にも分かりやすく説明します。ゲームクリエイターを目指す方にも役立つ情報を。

ステージ構成にする その2

ステージが進むにつれゲームが難しくなる仕組み作りの続きです。

前回 [ ステージ構成にする その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;
}

 

変更点を見ていきます。

flash ゲームプログラムのサンプル

新しく3つの変数を追加します。
15行目、ステージ番号のカウント用
16行目、「STAGE CLEAR」メッセージを表示している時間
17行目、ステージ番号とクリア条件を表示している時間

変数名についてですが、自分なりに規則性を考えて名前付けした方が、見やすいプログラムに
なります。
たとえば時間をカウントするための変数なら、名前に Wait を付けるようにします。
16行目の clearCnt などは悪い例です。
stageWait と同様に clearWait の方が良いですね。

 

 

flash ゲームの作り方

198-207行目、ステージ開始前に必要なメッセージを表示しています。
200行目では、クリア条件を作ってます。1ステージにつき5機ずつ条件が厳しくなります。

210行目、敵の移動速度を設定しています。ステージが進むにつれ徐々に早くなります。
214行目、敵残数を表示しています。

236行目、ステージ番号をクリアしています。ステージが始まる前に initGame() を
呼び出して stageNoずつ加算していくのでから始めます。

 

 

flash 処理の流れを見極めよう

68-78行目、前回まで敵の移動処理にあった爆発中の処理を、ここに移動しました。
理由は、ステージクリアのメッセージを表示している最中に moveEnemy() の処理を
動かしたくなかったからです。
つまり、ステージクリアした瞬間に敵の動きを止めたかったのです

79-89行目、ステージ番号とクリア条件の表示を stageWait が0になるまで表示します。

90-96行目、ステージクリアのメッセージを表示後、clearCnt が0になったら
次のステージを開始します。
initGame() を上手に作成することで、ステージの切り替えがこれだけで行えます。

 

 

flash 爆発処理を別の関数へ移す

これは前回のプログラムです。
移動処理の中にあった爆発中の処理を削除して mainloop() へ移しました。

 

 

flash ステージクリアの処理

162-167行目、敵が撃破されたとき敵残数を減らして、0になったらステージクリアの
メッセージを表示します。

185行目、敵を1機出現させるたびに少しずつ移動速度を上げていきます。
これをコメントにして遊んでみて下さい。
ステージが進むにつれ、ゲームの単調さを感じると思います。
このようなやり方でもゲームに緩急を付けることができます

 

 

プログラムを改造しよう

今回の課題は少し長かったですね。お疲れ様でした。

これでゲームは完成しましたが、プログラムをより理解する為には
やはり改造するのが一番です
次のような課題に挑戦してみましょう。

・ビームを2発当てないと撃破出来ない赤いUFOをときどき出現させる
・宝石を持っていかないフェイントするUFOを出現させる
・UFOもビームを撃ってきて、自機に当たるとしばらく動けなくなる
・タイトル画面を表示する
・ゲームオーバー後、過去10回分のスコアを表示する

 

上記のような新しい仕様を考えて改造するのもいいですが、わざとバグ(不具合)を
出すような改造も勉強になります。

88行目や 95行目をコメントにして実行してみて下さい。
当然、バグが発生します。
発生している現象を説明できれば、プログラム内容を理解していると言えるでしょう。

このような動作とプログラムを一致させるトレーニングも有効です。