Unityではじめてのテトリス風落ちゲー制作記 ~その5 当たり判定編~

Unity

Unityでテトリス風落ちゲーを作ってみたいけど、何から手を付けていいか分からない。私もそんな一人です。ここでは、自分の勉強も含めて、簡単なテトリス風落ちゲーを制作していく過程を掲載していきます。

この連載を最後まで読めば、このようなテトリス風落ちゲーが1人で作れるようになります。

別ウィンドウで開く場合はこちら

今回は当たり判定編です。

本記事は4つの構成で解説していきます。

  1. 落下ブロックが壁または配置ブロックに着地したことを判定するプログラム解説
  2. 落下ブロックが壁または配置ブロックに側面当たりしたことを判定するプログラム解説
  3. 落下ブロック着地したら、一定時間経過後に次の落下ブロックを落とすプログラム解説
  4. 落下ブロック側面当たりしたら、落下ブロックの横移動を拘束するプログラム解説

まだ落下ブロック操作編を読んでない方は、こちらを先に読むことをお勧めします。

この記事はこんな人におすすめ!

  • 用意されたアセットを極力使わず、自分の力でゲームを作りたい
  • プログラミング初心者で1つ1つ丁寧に解説した記事が読みたい
  • Unity初心者がスキルを上げながらゲームを制作していく過程を見たい

本ゲーム制作方法は自己流です。もっと簡単に作る方法もあると思いますので、ご了承下さい。

落下ブロックの着地判定

落下ブロックの着地判定は、落下ブロックの1マス下が壁ブロックまたは配置ブロックの場合に判定するようにします。

各マスの状態は、落下ブロック「2」、壁ブロック「1」、配置ブロック「3」と定義しました。そのため、落下ブロック「2」の1マス下が壁ブロック「1」または配置ブロック「3」であれば、着地と判定させます。

下のjudgeGroundメソッドが落下ブロックの着地判定になります。

public bool judgeGround(int blockNum, int rot, int[,] blockStat, int x, int y){
    bool groundFlg = false;      
    int[,] block = new int[4,4];
    block = fallBlockSet.set(blockNum, rot);
    for(int i=0; i<4; i++){
        for(int j=3; j>=0; j--){
            if(block[j,i] == 2){
                if(blockStat[y + j + 1, x + i] == 1 || blockStat[y + j + 1, x + i] == 3){
                    groundFlg = true;
                    break;
                }
            }
        }
    }
    return groundFlg;
}

落下ブロックの側面当たり判定

落下ブロックの側面当たり判定は、着地判定と同様、 落下ブロックの1マス右または左が壁ブロックまたは配置ブロックの場合に判定するようにします。

下のjudgeContactRightメソッドが落下ブロックの右側面当たり判定、judgeContactLeftメソッドが左側面当たり判定になります。

public bool judgeContactRight(int blockNum, int rot, int[,] blockStat, int x, int y){
    bool contactFlg = false;      
    int[,] block = new int[4,4];
    block = fallBlockSet.set(blockNum, rot);
    for(int j=0; j<4; j++){
        for(int i=3; i>=0; i--){
            if(block[j,i] == 2){
                if(blockStat[y + j, x + i + 1] == 1 || blockStat[y + j, x + i + 1] == 3){
                    contactFlg = true;
                    break;
                }
            }
        }
    }
    return contactFlg;
}
public bool judgeContactLeft(int blockNum, int rot, int[,] blockStat, int x, int y){
    bool contactFlg = false;      
    int[,] block = new int[4,4];
    block = fallBlockSet.set(blockNum, rot);
    for(int j=0; j<4; j++){
        for(int i=0; i<4; i++){
            if(block[j,i] == 2){
                if(blockStat[y + j, x + i - 1] == 1 || blockStat[y + j, x + i - 1] == 3){
                    contactFlg = true;
                    break;
                }
            }
        }
    }
    return contactFlg;
}

落下ブロックが着地したら、一定時間経過後に次の落下ブロックを落とす

落下ブロックの着地判定プログラムが完成したので、着地判定したら一定時間経過後に次の落下ブロックを落とすプログラムを作成していきます。

企画編でゲームの状態遷移を設計しました。

これに則ってプログラムを書いていきましょう。

下のプログラムが 落下ブロックが着地判定したら一定時間毎に落下ブロックを落とすプログラムです。GameControllerスクリプトのStartメソッド、Updateメソッドにプログラムを追加します。既に説明した内容については省略していますので、 ご了承を。

private int gameStat; // ゲームステータス
private const int GAMEOVER = 0;
private const int START = 1;
private const int GROUND = 2;
private const int ERASE = 3;
private bool downIhbFlg; // 0:下移動許可、1:下移動禁止
private float fallCountTime; // 落下ブロックが1マス落下してからの経過時間
private float groundCountTime; // 落下ブロックが着地してからの経過時間
/*
... (省略)
*/
void Start()
{
    gameStat = START;
    fallCountTime = 0;
    groundCountTime = 0;
    downIhbFlg = false;
  /*
    ... (省略)
    */
}
void Update()
{        
    switch(gameStat){
        case START:
            downIhbFlg = false;
            // 落下ブロックを1秒毎に落下
            fallCountTime += Time.deltaTime;
            if(fallCountTime >= 1){
                fallBlockPosY++;
                fallCountTime = 0;
            }
            // 落下ブロックの着地判定
            if(judgeGround(blockNum, rot, blockStat, fallBlockPosX, fallBlockPosY) == true){
                gameStat = GROUND;
            }
            break;
        case GROUND:
            fallCountTime = 0;
            groundCountTime += Time.deltaTime;
            downIhbFlg = true;
            // 落下ブロックの非着地判定
            if(judgeGround(blockNum, rot, blockStat, fallBlockPosX, fallBlockPosY) == false){  
                groundCountTime = 0;
                gameStat = START;
            }
            if(groundCountTime >= 1){
                fallBlockPosX = fallBlockInitPosX;
                fallBlockPosY = fallBlockInitPosY;
                blockNum = Random.Range(0, 8);
                rot = 0;
                fallBlockStat = fallBlockSet.set(blockNum, rot);
                gameStat = START;
                groundCountTime = 0;
            }
            break;
        case ERASE:
            break;
            
        case GAMEOVER:
            break;
    }
    // 下キー or Sキーを押したとき落下ブロックを1マス下に移動
    if(Input.GetButtonDown("Vertical") == true && downIhbFlg == false){
        if(Input.GetAxis("Vertical") < -0.01f)
        {
            fallBlockPosY++; 
            fallCountTime = 0;
        }
    }
}

ゲームが開始したら、ゲームステータスを”START”にします。”START”では、落下ブロックの着地判定を行い、判定したら一定時間後にゲームステータスを”GROUND”に遷移させます。”GROUND”では、着地時間を計測し、一定時間経過したら次の落下ブロックを表示させ、”START”状態に戻します。もしプレイヤーが落下ブロックを左右に移動させ、着地判定から抜けたら何もせず”START”状態に戻します。

落下ブロック側面当たりしたら、落下ブロックの横移動を拘束する

落下ブロックの側面当たり判定を使って、落下ブロックの横移動を拘束するプログラムを作っていきます。

if(Input.GetButtonDown("Horizontal") == true)
{
    // 右キー or Dキーを押したとき落下ブロックを1マス右に移動
    if(Input.GetAxis("Horizontal") > 0.01f && judgeContactRight(blockNum, rot, blockStat, fallBlockPosX, fallBlockPosY) == false)
    {
        fallBlockPosX++; 
    }
    // 左キー or Aキーを押したとき落下ブロックを1マス左に移動
    if(Input.GetAxis("Horizontal") < -0.01f && judgeContactLeft(blockNum, rot, blockStat, fallBlockPosX, fallBlockPosY) == false)
    {
        fallBlockPosX--;
    }
}

キー入力で左右移動するプログラムは既に作成済ですので、キー操作判定内に側面当たり判定を追加することで落下ブロックの横移動を拘束させました。

出来上がったプログラムを動かしてみよう!

落下ブロックを左右に移動し、側面が壁ブロックに接触した際、左右移動が拘束出来ていることが確認出来ましたね。

更に壁ブロックに着地すると、次の落下ブロックが落ちてくるようになりました。

まとめ

今回は当たり判定編として以下内容について解説しました。

  1. 落下ブロックが壁または配置ブロックに着地したことを判定するプログラム解説
  2. 落下ブロックが壁または配置ブロックに側面当たりしたことを判定するプログラム解説
  3. 落下ブロック着地したら、一定時間経過後に次の落下ブロックを落とすプログラム解説
  4. 落下ブロック側面当たりしたら、落下ブロックの横移動を拘束するプログラム解説

現状では、落下ブロックが下壁まで落下してもブロックが配置されず、次の落下ブロックが表示されるだけです。次回は、落下ブロックを配置ブロックへ変換させる方法について解説する予定です。

最後までお読み頂きありがとうございました。