コミュ障だから明日が僕らをよんだって返事もろくにしなかった

何かを創る人に憧れたからブログをはじめたんだと思うよ

Unityで玉転がしゲームをつくったよの巻

Unityでボール転がしゲームをつくろう

ここで言うボールとは、男性器のメタファーでありそれを転がすというのは自己の肥大化したリビドーをどこまでも抑えられずに日々葛藤していく現実の比喩のことを示し、それをゲームと言う閉鎖的な空間に落とし込むことによって精神的な安堵感を得……。


えぇ……、ちょっと何を言っているのかわかりませんね。


はい、ちょうど僕が応援したりしなかったりしている方がUnityをやっているので僕も手を出すことにしたのでした。



今までUnityってプログラム書かなくてもアセットで動いてしまう何かをつくれる3Dキットってことで敬遠してたんですけど、やっぱりサクッとなにかを作るにはUnityしかないなって思ったんです。手軽にマルチプラットフォーム対応できるし、手軽に物理演算シミュレーションできるし、手軽に電卓的なアプリやアニメーションの制作に応用できるし、流行りのVRにも対応してしまっているしで、” アセットでなんでもできるしいいか ” と避けて通っちゃダメな気がしたんです。そうしたわけでUnityをいじっていくことにしました。


そうしたわけでまずは玉転がしゲームをつくっていきます。

それじゃあサクサクと作っていきましょう。

起動したら3Dのプロジェクトを作成します。まずはこの無限大な夢のあとの何もないこの空間にボールと床を配置します。
f:id:andron:20180523061457p:plain


ボールと床のオブジェクトはメニューの赤い枠部分から召喚することができるらしいぞ。
f:id:andron:20180523062153p:plain

さくっと召喚したよ。
f:id:andron:20180523062539p:plain
!?

f:id:andron:20180523062645p:plain
誰だお前は……!?


はい、オブジェクトを設置したらプレイヤーを動かす準備をしていきます。ゴリゴリスクリプト書いていってもいいのですが、Unityは標準でオブジェクトを物理制御(Rigidbody、Collider)できる機能がついているらしいのでそれをいじっていきます*1。ただ、プレイヤー動かすにはスクリプトを書く必要があるのでそれは書いていきます。

using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {
    public float speed;
    private Rigidbody rb;

    void Start ()
    {
        rb = GetComponent();
    }

    void FixedUpdate ()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");
        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.AddForce (movement * speed);
    }
}

これを、ボールに紐づけます。すると、このように動かせます。
f:id:andron:20180523203941g:plain


次にカメラもプレイヤーに追従する形でいじっていきます。作成したら初期にあった、 Main Camera にスクリプトを紐付けていきます。

using UnityEngine;
using System.Collections;

public class CameraController : MonoBehaviour {
    public GameObject player;
    private Vector3 offset;

    void Start ()
    {
        offset = transform.position - player.transform.position;
    }
    
    void LateUpdate ()
    {
        transform.position = player.transform.position + offset;
    }
}

f:id:andron:20180523210554g:plain

あ、ボールを転がして落ちないように壁もついでにつけました。(落ちない壁はできましたか……?)



はい、これだけですとゲームとして成り立たないので収集アイテムを作ります。収集アイテムは例のごとくゲームオブジェクトからちゃちゃっと作っていきましょう。そして、収集アイテムが触れたらの処理するスクリプトを描きます。

収集アイテムのスクリプト*2

using UnityEngine;
using System.Collections;

public class Rotator : MonoBehaviour {

    void Update () 
    {
        transform.Rotate (new Vector3 (15, 30, 45) * Time.deltaTime);
    }
}

f:id:andron:20180523211449g:plain
回るだけです。

収集オブジェクトに「Pick Up」のタグ付けして、プレイヤーが接触したらオブジェクトが消えるようにしていきます。
プレイヤースクリプト追記_01

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Pick Up"))
        {
            other.gameObject.SetActive(false);
        }
    }


ついでに収集アイテムのテキスト処理を書いていきましょう。ゲームオブジェクトからUIのテキストを選択します。それでテキストが表示されます。そしたら、収集オブジェクトの処理状況とかのスクリプトを追記していきます。

プレイヤースクリプト追記_02

using UnityEngine;
using UnityEngine.UI;  // UI機能 追記
using System.Collections;

public class PlayerController : MonoBehaviour {
    public float speed;
    public Text countText;
    public Text winText;
    private Rigidbody rb;
    private int count;

    // 開始時の処理
    void Start ()
    {
        rb = GetComponent<Rigidbody>();
        count = 0;
        SetCountText ();
        winText.text = "";
    }

    // ボールの移動関連
    void FixedUpdate ()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");
        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.AddForce (movement * speed);
    }

    // 接触時の処理
    void OnTriggerEnter(Collider other) 
    {
        if (other.gameObject.CompareTag ( "Pick Up"))
        {
            other.gameObject.SetActive (false);
            count = count + 1;
            SetCountText ();
        }
    }

    // 収集カウンタ
    void SetCountText ()
    {
        countText.text = "Count: " + count.ToString ();
        if (count >= 12)
        {
            winText.text = "You Win!";
        }
    }
}

追記箇所だけ挿入したかったのですが、いろんなところに追記箇所がごちゃごちゃ挟まれていたのでコメント入れました。これをテキスト側に紐づけるとこんな感じになるらしいですぞ。

f:id:andron:20180523221922g:plain

この辺で文字入れるの飽きてきててテキスト設定しなくても動くっしょっとか適当なことやって動作確認したら、エラーでおちた……。ちゃんと設定したらテキストの紐づけ(countText、winText)をしよう。




というわけで茶番ましましで提供しましたが、この記事いままで僕があげてきたUnity記事の中で一番Unityっぽい使い方してるんですよ。えぇ……。ちなみに今までやってきたことはここに全部書いてあります。
Roll-a-ball tutorial - Unity

まあ、僕が茶番のために作成したモデルについての話は書いてませんけどね……。そして、スクリプト部分も若干改変していたりします。まあ、パラメータいじっている程度ですけどね。



といった感じでUnityを少しまじめに触っていこうと決意を固めたのでした。

おまけ

あ、そうだ。今回まじめにコード書いたので再描画処理とかによく使われるループ処理やっているupdate系のメソッドに関しての知見もメモしておく。

メソッド 説明
FixedUpdate MonoBehaviour が有効の場合、この関数は毎回、固定フレームレートで呼び出されます。
LateUpdate Behaviour が有効の場合、LateUpdate は毎フレーム呼びだされます
Update Update は MonoBehaviour が有効の場合に、毎フレーム呼び出されます

似たようなのがいくつもあったから違いあるのかと調べたら、こんな感じらしいです。UpdateとLateUpdateの違いよくわかんないけどLateUpdateはメソッドを呼ぶ順番を担保してくれるらしい。だいたいそんな感じの機能でした。

参考
MonoBehaviour - Unity スクリプトリファレンス

*1:追加することでオブジェクトに重力や接触の判定が追加されます

*2:回転させるだけ