今回は(残念ながら)バッドプラクティス的な話です。
セルオートマトンで水面を表現してみよう!と思い立ったものの、途中で色々見失ってました。w
セルオートマトンとは?
座標をブロック(セル)で区切ってブロックの状態を把握しながら動いていくというものです。
また後半にくわしく書きましたがこれはセル同士が影響を与えあっていないという意味でセルオートマトンではないので、「風」としました。
水面の波紋をセルオートマトン風に表現すると・・・
今回作ったもの
jsdoit : http://jsdo.it/hp0me_/b1Wl
イメージ
それぞれのセル(ブロック)は一つずつがインスタンスです。
セルには状態があって「発火状態 = state」と「現在のステップ = step」があります。全てのステップが終わったら発火状態がリセットされます。あとはこっちで随時発火させていきます。
本当は、発火したら隣のセルを発火させるように書けばよかった・・・反省。。
コード
実装の詳細はコメントに
//セルのオブジェクト
var cellObj;
//セルを格納する配列(2次元)
var cells = new Array();
//セルの大きさ
var cellSize = 10;
//一行のセルの数
var cellLineNum = 40;
var band;
var canvasSize = cellSize * cellLineNum + 1;
var hamonStep = 1;
var hamonStepMax = Math.floor(cellLineNum/2);
var fixedUpdateFlg = 0;
function setup() {
createCanvas(canvasSize,canvasSize);
smooth();
//2次元配列でセルを格納する
for(var x = 0; x < cellLineNum; x++){
cells[x] = new Array();
for(var y = 0; y < cellLineNum; y++){
cellObj = new CellClass(cellSize * x,cellSize * y);
cells[x].push(cellObj);
}
}
}
function draw() {
//アップデートを3フレに一回に絞る
if(fixedUpdateFlg == 3){
background(255);
//波紋のステップ
band = hamonStep * 2 - 1;
for(var x = 0; x<band;x++){
for(var y = 0; y<band;y++){
if(x == (band-1) || y == (band-1)){
cells[x][y].state = 1;
cells[x+1][y+1].state = 1;
cells[x][y+1].state = 1;
cells[x+1][y].state = 1;
}
}
}
//波紋ステップが最後までいったらやり直す
if(hamonStep == hamonStepMax)hamonStep = 0;
hamonStep++;
//描画
for(var x = 0; x<cells.length;x++){
for(var y = 0; y<cells.length;y++){
cells[x][y].draw();
}
}
fixedUpdateFlg = 0;
}
fixedUpdateFlg ++;
}
function mousePressed() {
}
//セルのクラス
var CellClass = function(x,y){
this.pos = createVector(x,y);
this.step = 0;
//this.colors = [0,50,100,255,100,50,0];
//this.colors = [255,200,150,100,50,0,50,100,150,100,50,100,150,200,255];
//波紋の色調整
this.colors = [255,225,200,175,150,125,100,75,50,25,0,25,50,75,100,125,150,125,100,25,50,75,100,125,150,175,200,225,255];
//ステート駆動
this.state = 0; //1で動く
//ステップが終了したらステートをゼロに戻す
this.draw = function(){
if(this.state == 0)return;
this.step++;
fill(this.colors[this.step]);
stroke(1);
rect(this.pos.x,this.pos.y,cellSize,cellSize);
if(this.step == (this.colors.length-1)){
this.step = 0;
this.state = 0;
}
};
};
これは正確にはセルオートマトンじゃない(?)・・・
本来なら水面を描く時には隣の水面の状態を把握して影響されていくというのが綺麗なセルオートマトンだと思いますが、**今回はセルの色はステップに応じて最初から決まっているのでセルオートマトンじゃないと言えるでしょう。**なので「風」とつけてます。
本当は隣接する水面セルにベクトルを付けて随時チェックする&減衰するようにしたら綺麗なセルオートマトンの水面ができます。
セルオートマトンは設計が鍵
今回のように、最初の段階で隣同士のセルが影響を与えられないように作っていると人口知能っぽいものを作ることができなくなってしまいます。
セル同士が影響を与えて動いていくように設計する。これが鉄則ですね。。
逆に言うと**「(プログラミング的に)人工知能とは何か?」という線引きがここで厳然とある**ように感じました。これがあるかないかの違いが大きいです。