読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

ライフゲームを作ってみた

今回の記事ではライフゲームを紹介したいと思います。
担当は大橋です。よろしくおねがいします。

LifeGame_01

ライフゲームって?

ライフゲームは、生命の誕生と死をモチーフにしたシミュレーションゲームです。
1970年にJohn Horton Conwayという人が考案しました。

「生命の誕生と死」というと、なんだか凄そうですが、
ルール自体はとても単純です。
上の画像のように、格子状に区切った盤面がライフゲームの全てです。

盤面上のマス(セル)が1つの生命を表します。
緑色のセルは「生きている」状態、黒色のセルは「死んでいる」状態です。
各セルの周囲8マスに生きているセルが何個あるかで、次のステップにそのセルがどちらの状態になるかが決まります。

セルの状態が「死」のとき

  • 周囲8マスに生きているセルが3つ → 次のステップで「生」(誕生)

セルの状態が「生」のとき

  • 周囲8マスに生きているセルが2つか3つ → 次のステップでも「生」(生存)
  • 周囲8マスに生きているセルが4つ以上 → 次のステップで「死」(過密で死滅)
  • 周囲8マスに生きているセルが1つ以下 → 次のステップで「死」(過疎で死滅)

全てのセルに対して上記ルールで、次のステップの状態を判定します。
そして次のステップにその状態に変化させます。
これを延々と繰り返すのです。

Unityで作ってみる

ライフゲームをUnity(4.6)で作ってみます。

まずはセルを作ります。
「死」状態のCube1、「生」状態のCube2を作ります。

LifeGame_02

2つの立方体をひとつのGameObjectにまとめてプレハブ化して、これをセルにします。

LifeGAme_03

セルには次のスクリプトをComponentとしてセットしておきます。

public class Cell : MonoBehaviour {  
    public bool Living { get; private set; }    // このセルが生存状態か  
  
    private GameObject cube1;    // 死滅時  
    private GameObject cube2;    // 生存時  
  
    void Awake () {  
        cube1 = transform.FindChild ("Cube1").gameObject;  
        cube2 = transform.FindChild ("Cube2").gameObject;  
  
        cube1.SetActive (true);  
        cube2.SetActive (false);  
        Living = false;  
    }  
      
    // Update is called once per frame  
    void Update () {  
        Living = cube2.activeSelf;  
    }  
  
    /// <summary>  
    /// 誕生  
    /// </summary>  
    public void Birth() {  
        cube1.SetActive (false);  
        cube2.SetActive (true);  
    }  
  
    /// <summary>  
    /// 死滅  
    /// </summary>  
    public void Die() {  
        cube1.SetActive (true);  
        cube2.SetActive (false);  
    }  
  
}  

次に、セルの生存と死滅の判定をして、ライフゲームのステップを進めるスクリプトを作ります。

public class LifeGame : MonoBehaviour {  
    private const float CELL_SIZE = 1f;   // セルのサイズ  
  
    public int gridSize = 100;            // グリッドの1辺のセルの数  
    public GameObject cellPrefab;         // セルのプレハブ  
  
    private Cell[,] cells;                // グリッド状のセル  
  
  
    // Use this for initialization  
    void Start () {  
        // グリッド状にセルを作成  
        cells = new Cell[gridSize, gridSize];  
  
        for (int x = 0; x < gridSize; x++) {  
            for (int z = 0; z < gridSize; z++) {  
                // セルを作成  
                GameObject obj = Instantiate (cellPrefab) as GameObject;  
                obj.transform.SetParent (transform);  
  
                // 位置を設定  
                float xPos = (x - gridSize * 0.5f) * CELL_SIZE;  
                float zPos = (z - gridSize * 0.5f) * CELL_SIZE;  
                obj.transform.localPosition = new Vector3 (xPos, 0f, zPos);   
  
                // Cellをセット  
                cells [x, z] = obj.GetComponent<Cell> ();  
            }  
        }  
    }  
      
    // Update is called once per frame  
    void Update () {  
        // クリックしたセルの生存/死滅を切り替える  
        if (Input.GetMouseButtonDown(0)) {  
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
            RaycastHit hit = new RaycastHit();  
  
            if (Physics.Raycast(ray, out hit)){  
                Cell cell = hit.collider.gameObject.transform.parent.GetComponent<Cell>();  
  
                if (cell.Living) {  
                    cell.Die ();  
                } else {  
                    cell.Birth ();  
                }  
            }  
        }  
  
        // 「S」キーで開始  
        if (Input.GetKeyDown(KeyCode.S)) {  
            StartCoroutine (LifeGameCoroutine ());  
        }  
  
        // 「E」キーで停止  
        if (Input.GetKeyDown (KeyCode.E)) {  
            StopAllCoroutines ();  
        }  
    }  
  
    /// <summary>  
    /// ライフゲームの更新コルーチン  
    /// </summary>  
    /// <returns>The game coroutine.</returns>  
    private IEnumerator LifeGameCoroutine() {  
        while (true) {  
            // 全セルを更新  
            for (int x = 0; x < gridSize; x++) {  
                for (int z = 0; z < gridSize; z++) {  
                    UpdateCell (x, z);  
                }  
            }  
  
            // ちょっと待つ  
            yield return new WaitForSeconds (0.03f);  
        }  
    }  
  
    /// <summary>  
    /// セルの状態を更新  
    /// </summary>  
    /// <param name="x">The x coordinate.</param>  
    /// <param name="z">The z coordinate.</param>  
    private void UpdateCell(int cellX, int cellZ) {  
        // 周囲の生存セル数  
        int count = 0;  
        for (int x = cellX - 1; x <= cellX + 1; x++) {  
            for (int z = cellZ - 1; z <= cellZ + 1; z++) {  
                if (x == cellX && z == cellZ) {  
                    continue;  
                }  
  
                if (cells [(x + gridSize) % gridSize, (z + gridSize) % gridSize].Living) {  
                    count++;  
                }  
            }  
        }  
  
        // 誕生/死滅  
        Cell cell = cells [cellX, cellZ];  
        if (cell.Living) {  
            // 周囲の生存セルが1以下、または4以上のとき死滅  
            if (count <= 1 || count >= 4) {  
                cell.Die ();  
            }  
        } else {  
            // 周囲の生存セルが3つのとき誕生  
            if (count == 3) {  
                cell.Birth ();  
            }  
        }  
    }  
}  
  

これを、シーンに配置した空のGameObjectにセットしておきます。

実行してみる

出来上がったものがこちらです。
LifeGame

これを実行すると、セルがグリッド状に敷き詰められた状態になります。
好きなセルをクリックすると、そのセルの状態(生、死)が切り替わります。

LifeGame_04

いくつかのセルの状態を「生」にしたあと、「S」キーを押してシミュレーション開始です。
始めの状態によって、いろいろな動きが見れると思います。

いろんなパターン

おもしろい動きをするパターンがいくつも発見されています。
その中で、代表的なものを紹介します。

グライダー

LifeGame_05
移動します!

宇宙船

LifeGame_06
これも移動します。

パルサー

LifeGame_07
パルサーって感じです。