Unityでブロック崩しを作った

1. 経緯

Unityを使って3Dゲームを作りたいと思い、チュートリアル的にブロック崩しを作った。

2. ざっくりと作り方

実は作ったのはだいぶ前なのでどんな作ったのかソースを見て思い出しながら記事を書いている。チュートリアルとしては有名な題材で他のサイトにも詳しい解説があるようなのでここではざっくりと解説する。

2-1. シーンを作成する

まずはゲームを構成するシーンを作成する。

今回作ったブロック崩しはタイトル画面、ゲーム画面、リザルト画面の3つなので、シーンを3つ作成する。

2-2. タイトル画面

下記のような感じでキャンバスとテキストを配置する。

タイトル画面

MasterにSpaceScriptというスクリプトをアタッチする。

// SpaceStart.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SpaceStart : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKey(KeyCode.Space)){
            SceneManager.LoadScene("MainScene");
        }
    }
}

中身はスペースを押したらゲーム画面のシーンに移動する記述のみ。

2-3. ゲーム画面

2-3-1. オブジェクトの配置

オブジェクトを下記のように配置する。球にはSphereCollider、プレイヤーが操作するバーにはBoxColliderを適用して当たり判定を行えるようにする。マテリアルを作成しアタッチすると下記のように色付けすることもできる。

ブロックのみはPrefabを作成し、スクリプトで量産する。

// BoxInit.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BoxInit : MonoBehaviour
{
    public GameObject boxObjPrefab;
    public GameObject boxesObj;

    // Awake is called before calling Start
    void Awake(){
        GameObject masterObj = GameObject.Find("Master");
        for(int x=0; x<8; x++){
            for(int y=0; y<5; y++){
                GameObject g = Instantiate(boxObjPrefab, boxesObj.transform);
                g.transform.position = new Vector3((2f + (1f * y)), 0.4f, (-4.2f + (1.2f * x)));
                g.GetComponent<Destroyer>().masterObj = masterObj;
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

2-3-2. プレイヤーバーと球の管理

次にプレイヤーバーのコントローラを作成する。方向キーの左または右を入力するとバーの位置を進めるように記述されている。

// Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Controller : MonoBehaviour
{
    private float playerSpeed = 0.2f;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKey(KeyCode.LeftArrow)){
            transform.position += transform.forward * playerSpeed;
        }
        if(Input.GetKey(KeyCode.RightArrow)){
            transform.position -= transform.forward * playerSpeed;
        }
    }
}

球にはスタート時に動き出すようなスクリプトを記述しアタッチする。

// StartShot.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StartShot : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        transform.eulerAngles = new Vector3(0,Random.Range(30,120),0);
        gameObject.GetComponent<Rigidbody>().AddForce(transform.forward * 500);
    }

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

2-3-3. 当たり判定の実装

球がバーより下に来た場合の処理をKabeOut.csに記述し、一番下の壁にアタッチする。OnCollisionEnter()で衝突時の挙動を記述する。ここではGameMasterのGameOver関数を呼んで、リザルト画面に移るようになっている。

// KabeOut.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KabeOut : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

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

    private void OnCollisionEnter(Collision collision){
        GameObject.Find("Master").GetComponent<GameMaster>().GameOver("ゲーム失敗、また挑戦しよう");
    }
}

Destroyer.csに球がブロックに当たった際の処理を記述し、BoxesのPrefabにアタッチする。ここでは、GameMasterのboxNumを減らした後、オブジェクトを消去している。

// Destroyer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Destroyer : MonoBehaviour
{
    public GameObject masterObj;

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

    private void OnCollisionEnter(Collision collision){
        masterObj.GetComponent<GameMaster>().boxNum--;
        Destroy(gameObject);
    }
}

2-3-4. ゲーム状態の管理

ゲーム状態を管理するGameMasterスクリプトを作成し、Masterにアタッチする。ブロックの数をboxNumに、経過時間をnowTimeに保持しておく。boxNumが0になったらクリアとみなし、リザルト画面に移る。

// GameMaster.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameMaster : MonoBehaviour
{
    public int boxNum;
    public float nowTime;

    // Start is called before the first frame update
    void Start()
    {
        nowTime = 0;    
    }

    // Update is called once per frame
    void Update()
    {
        nowTime += Time.deltaTime;
        if(boxNum <= 0){
            GameOver(nowTime.ToString("F0")+"秒でクリアしました!");
        }
    }

    public void GameOver(string resultMessage){
        DataSender.resultMessage = resultMessage;
        SceneManager.LoadScene("ResultScene");
    }
}

また、リザルト画面に表示するテキストをクリア時とゲームオーバー時で変えるため、DataSender.csを作成し、テキストを管理しておく。

// DataSender.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DataSender : MonoBehaviour
{
    public static string resultMessage;
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

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

2-4. リザルト画面

タイトル画面と同じく、オブジェクトを配置する。

リザルト画面

テキストはゲームオーバー時とゲームクリア時で違うものにしたいので、下記のスクリプトを記述し、Masterにアタッチする。アタッチ後、EditorのInspectorのResultTextMessageには配置したTextをセットしておく。こうすることで、DataSenderに登録されたテキストを画面に表示できる。

// TextDataFetcher.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TextDataFetcher : MonoBehaviour
{
    public Text resultMessageText;

    // Start is called before the first frame update
    void Start()
    {
        resultMessageText.text = DataSender.resultMessage;
    }

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

スペースを押したらタイトルに戻りたいので、下記スクリプトを書いてMasterにアタッチする。

// ReturnTitle.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ReturnTitle : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKey(KeyCode.Space)){
            SceneManager.LoadScene("MainScene");   
        }
    }
}

3. 完成

ゲーム画面

4. まとめ

一昔前だと3Dのゲームを作成するにはモデルの表示から長々とコードを書かなければならなかったのが、現代ではこんなに簡単に書けるようになったのだなぁと嘆息した。

昔作りたいけど作れなかったゲームとか今なら簡単に作れるのかしら。機会があれば作ってみよう…。

参考サイト

今日から始めるUnity

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA