XNA でライフゲームを作った。

シェアする

lifegame

当初の目的である、グライダーガンの動作が上手くいった様なのでうp

ライフゲームに関してはwikipediaを参照すべし。

画面端の処理を入れてないのでグライダーが壁にぶつかったときにブロックに変化するが、グライダーはブロックにぶつかると消滅する性質を持ってるので気にしないことにする。

うpとは言うものの、ソースコード全部をエントリに書く事はできないので、XNA の中でゲームの処理を書く Update() の中から重要そうなところと Draw() をここにメモしておく。実際のソースコードは Visual Studio で自動整形してるので、インデントやブロックの書き方は微妙に違う。

public void Update(GameTime gameTime) {
    MouseState mouseState = Mouse.GetState();
    KeyboardState keyboardState = Keyboard.GetState();

    this.previousMouseButtonPushed = this.nowMouseButtonPushed;
    this.nowMouseButtonPushed = mouseState.LeftButton == ButtonState.Pressed;

    if (this.nowMouseButtonPushed && !this.previousMouseButtonPushed) {
        Vector2 mousePos = new Vector2(mouseState.X, mouseState.Y);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                pos = makePos(x, y);
                if (pos.X < mousePos.X && pos.X + cellWidth > mousePos.X
                     && pos.Y < mousePos.Y && pos.Y + cellHeight > mousePos.Y) {
                    board[x, y] = (board[x, y] == 0) ? 1 : 0;
                }
            }
        }
    }

    this.previousRunFlg = this.nowRunFlg;
    this.nowRunFlg = keyboardState.IsKeyDown(Keys.S);
    if (this.nowRunFlg && !this.previousRunFlg)
        this.runFlg = !this.runFlg;

    double nowMillSeconds = gameTime.TotalGameTime.TotalMilliseconds;

    if (settim + interval < nowMillSeconds && this.runFlg) {
        settim = nowMillSeconds;
        updateBoard();
    }
}

まずはUpdate 本体。
上半分はマウスをクリックした箇所のセルの生死を逆転させてます。
「マウスを一回クリックする」という処理が容易されてない為フラグを用意してやる必要がある。
真ん中にある、ポーズの処理も同様。
interval の数値によって更新時間が変わる。単位は見てのとおり millseconds。

下にある updateBoard() を Update() の中からおいやったので上のやつもメソッドを一個作るべきかもしれない。

public void updateBoard() {
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            int count = 0;
            if (x - 1 > 0     && y - 1 > 0 && board[x - 1, y - 1] == 1) count++;
            if (                 y - 1 > 0 && board[x, y - 1] == 1)     count++;
            if (x + 1 < width && y - 1 > 0 && board[x + 1, y - 1] == 1) count++;

            if (x - 1 > 0     && board[x - 1, y] == 1) count++;
            if (x + 1 < width && board[x + 1, y] == 1) count++;

            if (x - 1 > 0     && y + 1 < height && board[x - 1, y + 1] == 1) count++;
            if (                 y + 1 < height && board[x, y + 1] == 1)     count++;
            if (x + 1 < width && y + 1 < height && board[x + 1, y + 1] == 1) count++;

            if (board[x, y] == 0 && count == 3)
                _board[x, y] = 1;
            else if (board[x, y] == 1 && (count == 2 || count == 3))
                _board[x, y] = 1;
            else
                _board[x, y] = 0;
        }
    }

    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            board[x, y] = _board[x, y];
        }
    }
}

盤面の更新処理。ライフゲームのルール通りに処理しているだけです。作業用の配列と実際に表示を行う配列を分けて順番に計算していきます。
最初2次元配列の board を bool[,] で処理してたんだけど、あれ、なんで int にしたんだっけ? これは bool でいいと思います。

ちなみに C# の配列は参照型というものらしく、int型のように board = _board とやると値ではなく参照が渡るので上手く処理できませんでした。なので for 文をまわして一個ずつ処理してます。

なんか良い方法無いんですかね。と思って今ぐぐったら Array.CopyTo(Array array, int index) というメソッドを知った。これ使うと良さそう。

public void Draw(SpriteBatch spriteBatch) {
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            pos = makePos(x, y);
            if (board[x, y] == 0)
                spriteBatch.Draw(TextureCell, pos, Color.Black);
            else
                spriteBatch.Draw(TextureCell, pos, Color.White);
        }
    }
}

Draw は board を実際に表示しているだけです。面白くないですね。

さて次は何しようかな・・・

Sponsored Link

シェアする

フォローする

コメント

  1. […] ▼XNA でライフゲームを作った。 (jkl.lomo.jp) […]