暇人の寝室
技術系の記事や読書・アニメの感想などを投稿します。
プログラミング
Unityを使って3Dゲームを作りたいと思い、チュートリアル的にブロック崩しを作った。
実は作ったのはだいぶ前なのでどんな作ったのかソースを見て思い出しながら記事を書いている。チュートリアルとしては有名な題材で他のサイトにも詳しい解説があるようなのでここではざっくりと解説する。
まずはゲームを構成するシーンを作成する。
今回作ったブロック崩しはタイトル画面、ゲーム画面、リザルト画面の3つなので、シーンを3つ作成する。
下記のような感じでキャンバスとテキストを配置する。
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");
}
}
}
中身はスペースを押したらゲーム画面のシーンに移動する記述のみ。
オブジェクトを下記のように配置する。球には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()
{
}
}
次にプレイヤーバーのコントローラを作成する。方向キーの左または右を入力するとバーの位置を進めるように記述されている。
// 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()
{
}
}
球がバーより下に来た場合の処理を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);
}
}
ゲーム状態を管理する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()
{
}
}
タイトル画面と同じく、オブジェクトを配置する。
テキストはゲームオーバー時とゲームクリア時で違うものにしたいので、下記のスクリプトを記述し、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");
}
}
}
一昔前だと3Dのゲームを作成するにはモデルの表示から長々とコードを書かなければならなかったのが、現代ではこんなに簡単に書けるようになったのだなぁと嘆息した。
昔作りたいけど作れなかったゲームとか今なら簡単に作れるのかしら。機会があれば作ってみよう…。