game
Unity3D
Unity
初心者
チュートリアル

【Unity】ブロックをスクリプトで配置:「はじめてのUnity」のブロック崩しを改造しながら学ぶ

はじめに

JunShimuraさんの「[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で ::(1)ステージ配置~(6)ブロックを手作業で並べる)」をよりゲームらしく改造する。

作成する記事は下記の通りです。

前回の状態

前回の記事「ゲームマネージャークラスを作る」では、ゲームクリアー後にブロックが全て消えた状態でゲームスタートとなってしまいました。これはブロックの初期化(並べる)処理が入っていないためです。
何故かというとブロックが全て消えてしまうため、ブロックを複製することが出来なく無くなってしまうからです。

ブロックを複製するためには、ブロックのプレハブ(Prefab)化を行います。

プレハブ化とは

プレハブ(Prefab)とはオブジェクトの作り置きが出来る仕組みのことです。テンプレートとして使用できます。

BlockをPrefab化する

現在、HierarchyにBlockを複数ある方は、1つのみ残して削除してください。
不要なBlockを複数選択するにはShiftキーを押しながらBlockを選択します。その後に右クリックメニューからDeleteを選択します。

Hierarchyから、BlockをAssetsにドラッグすると、Blockプレハブが出来上がります。
Unity_PrefabAdd.gif

Blockプレハブができたら、HierarchyのBlockを選択して右クリックメニューからDeleteで削除してください。

Prefabのメリットなどは下記記事を参照してください。
PrefabとInstantiateの基本(1)ブロックをScriptで配置:「はじめてのUnity」のブロック崩しを改造しながら学ぶ

Blockの色を変える準備

白一色だったブロックですが、複数の色のブロックにして色ごとに得点を分けるようにしたいと思っています。

[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(2)色を変える」で壁の色を変える際に、Wall Materialを作成しました。
今回はブロックの色を変えるBlock Materialを作成します。

下に在るAssetsの左にある[Create]>[Material]をクリックして下さい。
名前をBlock Materialに変えます。
名前を間違えた場合、名前のところをクリックすれば編集できるようになります。
Unity_MaterialAdd.gif

AssetsにあるBlockを選択して、Inspectorを表示します。ちなみにプレハブ化された場合、水色の箱アイコンになります。
次にMaterialsElement 0のところにある○をクリックし、開いたリスト画面からBlock Materialを選択します。
Unity_BlockMaterial.png

色はスクリプトによって複数の色に変更するので、ここでは何もしません。

スクリプトの作成

先に空のゲームオブジェクトを作成します。
Hierarchyに下にある[Create]>[Create Empty]を選択して下さい。
Unity_EmptyAdd.png

名前をBlockMapとします。PositionはXYZを0にしておきます。
Unity_BlockMap.png

BlockMapの[Inspector]タブの一番下にある[Add Compornent]ボタンをクリックして、Scriptアセットを作成、追加します。ファイル形式はcsを選びます。
Unity_BlockMapScriptAdd.png

ファイル名をBlockMapとします。そうすると、Assetsの中に「BlockMap.cs」が出来ています。
※BとMは英大文字です。間違えた場合、ファイル名と中身のクラス名の両方を修正してください。
Unity_BlockMapScript.png

BlockMap.csを編集する

Assets内のBlockMapスクリプト(C#のアイコン)をダブルクリックすると、エディタが開きます。
優先的に開かれるエディタは、多くの場合はMonoDevelopというソフトが起動されます。

少し長いのでコピー&ペーストで入力してしまいましょう。
※MonoDevelopでペーストが効かないに時はMonoDevelopを閉じ直してください。

BlockMap.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BlockMap : MonoBehaviour {

    public GameObject blockPrefab;
    public Material material;

    Color[] itemColor = { Color.clear, Color.red, Color.blue, Color.green, Color.yellow, Color.magenta };

    // 0:None 1:Red 2:Blue 3:Green 4:Yellow 5:Magenta
    int[,,] stageMap = {
        {
            { 3, 3, 3, 3, 3},
            { 2, 0, 2, 0, 2},
            { 1, 1, 0, 1, 1},
            { 0, 0, 0, 0, 0}
        },
        {
            { 4, 4, 0, 4, 4},
            { 3, 0, 3, 0, 3},
            { 2, 2, 2, 2, 2},
            { 0, 1, 1, 1, 0},
        },
        {
            { 4, 5, 4, 5, 4},
            { 5, 3, 3, 3, 5},
            { 2, 5, 2, 5, 2},
            { 1, 1, 5, 1, 1},
        }
    };

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }

    // ステージ最大数
    public int GetStageMax(){
        return stageMap.GetLength(0);
    }

    // ステージ作成
    public void CreateStage(int stage){
        var parent = this.transform;

        // 配置する座標を設定
        Vector3 placePosition = new Vector3(-4,0,9);
        // 初期化する座標を設定
        Vector3 initPosition = placePosition;

        // 配置する回転角を設定
        Quaternion q = new Quaternion();
        q = Quaternion.identity;

        // ブロック全削除
        var clones = GameObject.FindGameObjectsWithTag ("Block");
        foreach (var clone in clones){
            Destroy(clone);
        }

        // 配置
        for (int i = 0; i < stageMap.GetLength(1); i++) {
            placePosition.x = initPosition.x;
            for (int j = 0; j < stageMap.GetLength(2); j++) {
                int item = stageMap[stage-1, i, j];
                if(item != 0){
                    // ブロックの複製
                    GameObject block = Instantiate(blockPrefab, placePosition, q, parent);
                    // ステージのブロック番号により色変更
                    Renderer r = block.GetComponent<Renderer>();
                    r.material.color = itemColor[item];
                    // ステージのブロック番号で名前を生成
                    block.name = "Block_" + item.ToString();
                }
                placePosition.x += blockPrefab.transform.localScale.x;
            }
            placePosition.z -= blockPrefab.transform.localScale.z * 2;
        }
    }
}

少し説明します。

  • 3次元配列を用意し、ステージごとに2次元配列(5x4)をブロック番号をセットしています。
  • ステージマップのブロック番号が1以上ならブロックのプレハブを複製し、各番号の色を変更しています。
  • 名前にブロック番号を付与して、スコア処理で番号の判定をします。

Block PrefabとMaterialを設定する

スクリプト内でpublicにした型はInspectorに表示されます。
ここにBlockとBlock Materialをセットします。
unity_BlockMapScript.png

Block Prefabの右横にある○をクリックし、開いたリスト画面からBlockを選択します。
unity_BlockPrefabSelect.png
Materialの右横にある○をクリックし、開いたリスト画面からBlock Materialを選択します。
unity_BlockMaterialSelect.png

Manager.csを編集する

GameOpening処理には、ステージ作成の処理を追加しています。
GameClear処理には、3ステージまではステージクリアとして処理を追加しています。

Manager.cs
    // オープニング処理
    void GameOpening ()
    {
        currentState = GameState.Opening;

        // タイトル名のセット
        SetTitle ("Game Start", Color.green);

        // ボールの動作停止
        Time.timeScale = 0;

        // ラケットの初期位置をセット
        racket.transform.position = new Vector3 (0.0f, 0.0f, -8.5f);
        // ボールの初期位置をセット
        ball.transform.position = new Vector3 (0.0f, 0.0f, -7.5f);

        // ステージ作成
        FindObjectOfType<BlockMap>().CreateStage(stage);
    }

    // ゲームクリアー処理
    void GameClear ()
    {
        // ステージ最大数の取得
        int stageMax = FindObjectOfType<BlockMap> ().GetStageMax();

        // 次のステージへ
        stage++;
        if (stage > stageMax) {
            // タイトル名のセット
            SetTitle ("Game Clear", Color.yellow);
            // ステージ初期値
            stage = 1;
        } 
        else {
            // タイトル名のセット
            SetTitle (string.Format("Stage {0} Clear", stage -1) , Color.yellow);
        }
        //3秒後にオープニング処理を呼び出す
        Invoke ("GameOpening", 3f);
    }

Block.csを編集する

これまで一律10点だった得点をブロックの色ごとに得点を分けるようにします。
ブロックの名前の末尾を取得して、10倍したのを得点とします。

Block.cs
    void OnCollisionEnter(Collision collision) {
        //衝突判定
        if (collision.gameObject.tag == "Ball") {
            if (FindObjectOfType<Manager> ().currentState == Manager.GameState.Playing) {
                //スコア処理を追加
                string name = this.name;
                int score = int.Parse (name.Substring (name.Length - 1));
                FindObjectOfType<Score> ().AddPoint (score * 10);
            }

            //相手のタグがBallならば、自分を消す
            Destroy(this.gameObject);
        }
    }

改善

テスト用に一時的に、Ball.csのゲームオーバー判定をコメントアウト。

ボールが壁を突き抜ける

たまにボールが壁を突き抜けてしまうことが判明、下記の記事を参考にボールにも適用してみます。
衝突判定の再考、壁抜けを止める:「はじめてのUnity」のブロック崩しを改造しながら学ぶ

Ballを選択し、[Inspector]タブでRigidbodyCollision DetectionContinuosに変更します。
Unity_BallCollision.png

ラケットの形状を変更

ボールが垂直方向のままで方向変更ができないことがあるます。
これはラケットがCubeになっているので、末端を丸みがあるようにCapsuleに変更します。
Meshのところにある○をクリックし、開いたリスト画面からCapsuleを選択します。
Unity_RacketShape.png

このままだとBox Colliderのままなので、Capsule Colliderに変更します。
一旦、Box Colliderコンポーネントを削除します。
Box Colliderコンポーネントの右横にある歯車をRemove Compornentを選択します。
次に[Inspector]タブの一番下にある[Add Compornent]ボタンをクリックして、[Physics]の[Capsule Collider]を追加します。
Unity_RacketCollider.gif

Height1DirectionX-Axisにします。
Unity_RacketCollider2.png

ラケットのサイズを変更

ついでにラケットのサイズを大きくします。ScaleX5にします。
Unity_RacketScaleX.png

実装を確認する

エディタ画面中央の実行ボタンを押してみましょう。
オープニング画面でスペースキーで押すとゲームスタートとなります。
ブロックが全て無くなるとステージクリアーになり、次のステージとなります。
現在は、3ステージまでしか用意していません。
Unity_GameStart3.png

学んだこと

  • プレハブ化することでテンプレートとして使用できること
  • スクリプトによりブロックを自由に配置できること
  • スクリプトによりブロックの色を変更できること

参照