今回の記事ではライフゲームを紹介したいと思います。
担当は大橋です。よろしくおねがいします。
ライフゲームって?
ライフゲームは、生命の誕生と死をモチーフにしたシミュレーションゲームです。
1970年にJohn Horton Conwayという人が考案しました。
「生命の誕生と死」というと、なんだか凄そうですが、
ルール自体はとても単純です。
上の画像のように、格子状に区切った盤面がライフゲームの全てです。
盤面上のマス(セル)が1つの生命を表します。
緑色のセルは「生きている」状態、黒色のセルは「死んでいる」状態です。
各セルの周囲8マスに生きているセルが何個あるかで、次のステップにそのセルがどちらの状態になるかが決まります。
セルの状態が「死」のとき
- 周囲8マスに生きているセルが3つ → 次のステップで「生」(誕生)
セルの状態が「生」のとき
- 周囲8マスに生きているセルが2つか3つ → 次のステップでも「生」(生存)
- 周囲8マスに生きているセルが4つ以上 → 次のステップで「死」(過密で死滅)
- 周囲8マスに生きているセルが1つ以下 → 次のステップで「死」(過疎で死滅)
全てのセルに対して上記ルールで、次のステップの状態を判定します。
そして次のステップにその状態に変化させます。
これを延々と繰り返すのです。
Unityで作ってみる
ライフゲームをUnity(4.6)で作ってみます。
まずはセルを作ります。
「死」状態のCube1、「生」状態のCube2を作ります。
2つの立方体をひとつのGameObjectにまとめてプレハブ化して、これをセルにします。
セルには次のスクリプトを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
これを実行すると、セルがグリッド状に敷き詰められた状態になります。
好きなセルをクリックすると、そのセルの状態(生、死)が切り替わります。
いくつかのセルの状態を「生」にしたあと、「S」キーを押してシミュレーション開始です。
始めの状態によって、いろいろな動きが見れると思います。
いろんなパターン
おもしろい動きをするパターンがいくつも発見されています。
その中で、代表的なものを紹介します。