めっきり更新ができていないふきのとうです。
ふと「Canvasを使ってみたい」と思い立ち何かいいネタはないかと考えたのですが、これといって思いつかないためライフゲームを作ってみることにします。
今回は単純にルールに従って動作させてみます。次回以降、START / STOPボタンなどの追加、速度調整機能などを追加していきたいと思います。
記事の目次
デモ
今回の完成形はこんな感じ。
デモページ
コーディング
HTML
シンプルにcanvas
タグだけあればOKです。
<canvas id="canvas" width="400" height="400" style="border: solid 1px #999;"></canvas>
Javascript
jQuery(function($) { var WIDTH = 80; var HEIGHT = WIDTH; var SIZE = 5; var field; var canvas; var Field = function(width, height){ this.width = width; this.height = height; this.field; this.init(); }; Field.prototype = { init: function(){ this.field = []; for (var y = 0; y < this.height; y++) { this.field[y] = []; for (var x = 0; x < this.width; x++) { this.set(x, y, null); } } }, randomize: function(){ for (var y = 0; y < this.height; y++) { for (var x = 0; x < this.width; x++) { var val = random(1, 10) == 1 ? 1 : 0; this.set(x, y, val); } } }, next: function(){ this.active = 0; var tempField = $.extend(true, {}, this.field); for (var y = 0; y < this.height; y++) { for (var x = 0; x < this.width; x++) { var n = this.neighbor(x, y, tempField); var val = this.fate(tempField[y][x], n); this.set(x, y, val); } } }, neighbor: function(x, y, field) { var n = 0; for (var s = -1; s < 2; s++) { if (y + s < 0 || y + s > this.height - 1) { continue; } for (var t = -1; t < 2; t++) { if (s == 0 && t == 0) { continue; } if (x + t < 0 || x + t > this.width - 1) { continue; } if (field[y + s][x + t] == 1) { n++; } } } return n; }, fate: function(mine, neighbor){ // 生存 if (mine == 1 && (neighbor == 2 || neighbor == 3)) { return 1; } // 誕生 if (mine == 0 && neighbor == 3) { return 1; } // 過疎, 過密 return 0; }, val: function(x, y){ return this.field[y][x]; }, set: function(x, y, v){ this.field[y][x] = v; } }; var Canvas = function(ctx, field, size) { this.ctx = ctx; this.field = field; this.size = size; }; Canvas.prototype = { clear: function(){ this.ctx.clearRect(0, 0, this.field.width * this.size, this.field.height * this.size); }, draw: function() { this.clear(); this.ctx.fillStyle = 'rgb(0, 0, 0)'; for (var y = 0; y < this.field.height; y++) { for (var x = 0; x < this.field.width; x++) { if (this.field.val(x, y) == 0) { continue; } this.ctx.fillRect(x * this.size, y * this.size, this.size, this.size); } } } }; var init = function() { var $canvas = $("#canvas"); var ctx = $canvas.get(0).getContext('2d'); field = new Field(WIDTH, HEIGHT); field.randomize(); canvas = new Canvas(ctx, field, SIZE); }; var draw = function() { canvas.draw(); }; var lifegame = function() { field.next(); draw(); } var random = function(min, max) { return min + Math.floor(Math.random() * (max + 1)); }; init(); draw(); setInterval(lifegame, 100); }); [/code] <h2>簡単な説明</h2> Javascriptの簡単な説明をつけておきます。 <h3>Javascriptの実行</h3> これはDOMへのアクセス準備ができた段階で実行するための方法で、<code>$</code>がコンフリクトを起こさないようにもするためですね。 jQuery(function($) { いろいろな処理 });
変数について
5つの変数を宣言します。
var WIDTH = 80; // 横のセル数 var HEIGHT = WIDTH; // 縦のセル数 var SIZE = 5; // セルサイズ var field; // 各セルの状態管理用 var canvas; // canvas制御用
メソッドについて
メソッドは3つ。
init
:初期化処理(各オブジェクトの初期化など)draw
:描画処理の呼び出しrandom
:指定された範囲内で乱数を返す
Fieldオブジェクト
メンバ変数
オブジェクトのメンバ変数は3つ。
this.width = width; // 横のセル数 this.height = height; // 縦のセル数 this.field; // 各ドットの状態保持配列
prototype(内部メソッド)
prototype(内部メソッド)として7つ。
init
:Filedの初期化処理randomize
:fieldをランダムに設定するnext
:次の状態に遷移させるneighbor
:8近傍のうちactiveなセルの数を返すfate
:生死判定val
:fieldから状態を返すset
:fieldに状態を設定する
Canvas
メンバ変数
オブジェクトのメンバ変数は3つ。
this.ctx = ctx; // canvasのコンテキスト this.field = field; // Fieldオブジェクト this.size = size; // セルサイズ
prototype(内部メソッド)
prototype(内部メソッド)として2つ。
clear
:canvasエリアのクリアdraw
:fieldをもとにcanvasエリアに描画
ライフゲームの実行
setInterval(lifegame, 100);
setInterval
を利用してlifegame
メソッドを100ms間隔で繰り返し実行します。
lifegame
を1回だけ実行することでステップ実行が実現できます。
100
を変更することで、実行スピードの調整ができます。
次回はこのあたりの作りこみをしてみます。