Unityと物理・数学の話

UnityやPython、天文関連、物理学、生物学を中心に更新していく予定です。塾講師、家庭教師の仕事をやっているのでお仕事の依頼はコメントにて承っております。当面はUnityをPythonで動かすのを試行錯誤しています。

Unityで簡易的なドミノ倒しゲームの作成

今回は,以前作成したUnityの自動生成ドミノについて紹介したいと思います。Unityに組み込まれている物理エンジンを用いて,現実で行うのと同じようなドミノを配置して,実際にUnity内でそれらを連続的に倒していくことが可能です。

実際的な方法としては、Cubeを配置してそれらをドミノの平板のような形に調整して,あとはこの作った平板を作りたいドミノの配列に並べて行って,初期位置にあるドミノを倒して,並べたドミノが倒れていくのを見届けることになります。

 

 

平板dominocubeの作成

平板を作成していくのですが,その前にPlaneを設定します。適当な大きさのPlaneを設定して,配置します。ComponentからPhysics>Mesh Colliderを設定しました。

f:id:unitypy:20171231155208p:plain

UnityのGameObjectからCubeを作成します。つくったCubeを平板にしていきます。平板のscaleは以下のInspectorのように設定しました。Positionは今は気にしないでください。

f:id:unitypy:20171231153713p:plain

これによってできるdominocubeを一方向から見たものが以下です。ちょっと縦に細長い感じもしますが,ドミノ倒しをするにはちょうどよさそうな平板って感じがしますね。

f:id:unitypy:20171231153851p:plain

dominocubeを列ごとに自動生成するスクリプト

しかし,どうにもドミノをいちいち配置していくのは面倒ですね。そこで平板を自動配置するスクリプトを書いてしまおうと思います。

スクリプトの名前はaddcubescriptになってます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class addcubescript : MonoBehaviour {
    public GameObject obj;

    public int createNumbers = 200;
    public int space = 1;
    public Vector3 createPos;
	// Use this for initialization
	void Start () {
        CreateObject();
	}
    void CreateObject()
    {
        for(int i = 0; i < createNumbers; i++)
        {
            Instantiate(obj, createPos + new Vector3(0, 2, -i - space * i), Quaternion.identity);
            transform.GetComponent();
        }
      
    }  
	// Update is called once per frame
	void Update () {
       
	}
}

dominocubeのPrefabの作成 

つくったdominocubeをAssetsの方へドラッグしてdominocubeのPrefabを作成しました。

これにより,スクリプトによるdominocubeの自動生成が可能となります。

上のスクリプトで注意してほしいのが,GameObject objを初期の基準としてcubeが自動生成されるということです。

addcubescriptの組み込みなど

dominocubeへaddcubescriptをドラッグして,objのところをdominocubeに設定する。

dominocubeへRigidbodyを組み込み,dominocubeの設定のPositionなどを以下のように設定します。これのうち,Yの位置が微妙に下だとdominocubeがPlaneより下へ沈んで行ってしまったり,上側だと生成した後に不安定になって勝手に倒れてしまうなどが起きるので注意してください。

f:id:unitypy:20171231160309p:plain

 

ドミノを倒す方法(無理やり)

次にドミノの最初の倒し方ですが,これは以前ビリヤードの球を動かすのに用いたスクリプトを弾の形にしたcube(名前はbullet)に組み込んで,平板の狙ったところに力を加えられるように初期位置を選ぶことができます。Positionをいじることによって可能です。

f:id:unitypy:20171231160851p:plain

スクリプトは以下です。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class study3_11 : MonoBehaviour
{
    float dgr = 0;
    int power = 0;
    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        Vector3 pos = transform.position;
        if (Input.GetKeyDown(KeyCode.Space))
        {
            power = 0;
        }
        if (Input.GetKey(KeyCode.Space))
        {
            power++;
        }
        if (Input.GetKeyUp(KeyCode.Space))
        {
            Vector3 v = Camera.main.transform.position;
            v -= pos;
            v *= power * -1;
            v.y = 0;
            transform.GetComponent().AddForce(v);
        }
        if (Input.GetKey(KeyCode.Q))
        {
            power++;
        }
        if (Input.GetKeyUp(KeyCode.Q))
        {
            Vector3 V = Camera.main.transform.position;
            V -= pos;
            V *= power * 1;
            V.y = 0;
            transform.GetComponent().AddForce(V);
        }
        
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            Camera.main.transform.RotateAround(pos, new Vector3(0, 10, 0), -0.1f);
            dgr += 0.1f;
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            Camera.main.transform.RotateAround(pos, new Vector3(0, 10, 0), 0.1f);
            dgr -= 0.1f;
        }
        float d = (2 * Mathf.PI) * (dgr / 360);
        float x = Mathf.Sin(d);
        float y = Mathf.Cos(d);
        x *= 10f;
        y *= 10f;
        pos.x += x;
        pos.y += 10f;
        pos.z -= y;
        Camera.main.transform.position = pos;                
    }
}

 このスクリプトは以下の本で載っていたものを参考にしています。 

見てわかるUnity5 C#超入門 (Game Developer Books)
 

 このスクリプトにより,スペースキーを押した時間だけ強くbulletを発射することができます。

ここでbulletに対する注意としては,組み込んだRigidbodyのうちUse Gravityをオフにしていることです。これがオンになっているとbulletが重力で落ちてしまうので決まった高さに当てることができません。

これらからドミノ倒しをやってみたときの画像たちが以下です。

f:id:unitypy:20171231162437p:plain

ドミノの初期位置に当たらんとするbullet。待ち構えるは無数のドミノ達。f:id:unitypy:20171231162120p:plain

 …無限に続くドミノ。

f:id:unitypy:20171231162623p:plain

(1分20秒後)終わりが見えてきた。

200個のドミノを自動生成したので,終わりまでいくのに1分半近くかかりました。ドミノの倒れるスピードは形によって変えられると思うので,もっと倒れるのを早くしてやってみるのもいいかもですね。

 途中からドミノを曲げる場合のスクリプト

直線では面白味に欠けると思い,途中から斜めにできないかと思って作成したのが以下のスクリプトです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class addcubescript : MonoBehaviour {
    public GameObject obj;

    public int createNumbers = 200;
    public int space = 1;
    public Vector3 createPos;
	// Use this for initialization
	void Start () {
        CreateObject();
	}
    void CreateObject()
    {
        for(int i = 0; i < createNumbers; i++)
        {
            Instantiate(obj, createPos + new Vector3(0, 2, -i - space * i), Quaternion.identity);
            transform.GetComponent();
        }
        for (int i = 0; i < createNumbers; i++)
        {
            Instantiate(obj, createPos + new Vector3(-i*0.3f, 2, i + space * i), Quaternion.identity);
            transform.GetComponent();
            transform.Rotate(new Vector3(0, 10, 0));
        }
    }  
	// Update is called once per frame
	void Update () {
       
	}
}

 こんな感じで途中から曲がったドミノの列になります。もうちょっと微調整するべきポイントはいくつかありますが...。

f:id:unitypy:20171231163627p:plain

 これをうまく組み合わせれば,もっと色々な形のドミノ倒しを自動生成できるかもしれないですね。

もっとちゃんとやるってなったときには改善しなきゃいけないポイントが山ほどあるので,それを改善できたらな,と思います。(やってみればわかるのですが,実行時に注意しなきゃいけない点が結構山積みです...。)