ブラウザで遊べるライフゲーム(Conway’s Game of Life)を作成してみました。
緑の自セルを配置して赤い敵セルを消してみましょう。
基本的なパターンの動きを確認するも良し。
パターンについては以下をご参照ください。
ライフゲーム – Wikipedia
さらにオンライン対戦版もあります!
ライフゲームバトル
友達を誘って対戦してみましょう!
Android版(無料)はこちら
https://play.google.com/store/apps/details?id=jp.co.g_llc.lifegamebattlemobile
1P版(vs CPU)
[操作]
・自セル配置:左クリック(タップ)
・配置セルの投下:青ボタン or スペースキー
・1時停止:緑ボタン or 「p」キー
・オールクリア:白ボタン
・グライダー配置:「g」キー
・移動物体パターン向きの変更:十字キー
・隠しコマンド:あるキーを押すと宇宙船がでます。
自セルは緑。敵セルは赤。
敵セルを全て消すとレベルが上がり敵セルが増加します。
(ゲームが最新版になっていない時は、「Ctrl」+「F5」でリロードしてみてください。)
{ver0.4}
・マウスムーブでセル配置を可能に
{ver0.3}
・レベルの追加。
{ver0.2}
・フィールド拡大。
・敵セルの追加。
{ver0.1}
・グライダー配置の追加
・セル配置の音を変更しました。
{ver0.0}
・公開
オンライン対戦版
ライフゲームバトル (別タブ開きます)
グライダー銃が実装されました。
マックスが実装されました。
ソースコード(1P版)
雑なコードですが載せておきます。
window.focus();
enchant();
window.onload = function ()
{
console.log( mHelloLifeGameFunc() );
var gWidth = 540; //432;
var gHeight = gWidth + 100;
var game = new Game(gWidth, gHeight);
game.fps = 15;
var ClickSound = "mBeam.wav";//"click.wav";
game.preload([ClickSound]);
var CellSound = "shotgun-pumpaction1.mp3";
game.preload([CellSound]);
var mImgUp = "./image/mUp.png";
game.preload([mImgUp]);
var mImgDown = "./image/mDown.png";
game.preload([mImgDown]);
var mImgRight = "./image/mRight.png";
game.preload([mImgRight]);
var mImgLeft = "./image/mLeft.png";
game.preload([mImgLeft]);
var mImgG = "./image/mG.png";
game.preload([mImgG]);
/////////////////////////////////////////////////
game.onload = function ()
{ //
//game.assets[mBGM1].play();
//var se = Sound.load("GB-Shooter-A05-1(Stage4).mp3");
//se.play();
/////////////////////////////////////////////////
var Point = 0; //ポイント
var State = 0; //現在のゲーム状態
/////////////////////////////////////////////////
var mGrey ='rgb(80,80,80)';
var S_MAIN = new Scene();
game.pushScene(S_MAIN);
S_MAIN.backgroundColor = 'rgba(50,50,50,1)';
var S_Text = new Label();
S_Text.font = "14px Meiryo";
S_Text.color = 'rgba(0,255,0,1)';
S_Text.width = 150;
S_Text.moveTo(gWidth-150, gWidth+10);
S_MAIN.addChild(S_Text);
S_Text.text = "Cell:0";
var E_Text = new Label();
E_Text.font = "14px Meiryo";
E_Text.color = 'rgba(255,0,0,1)';
E_Text.width = 150;
E_Text.moveTo(gWidth-150, gWidth+30);
S_MAIN.addChild(E_Text);
E_Text.text = "Cell:0";
var C_Text = new Label();
C_Text.font = "12px Meiryo";
C_Text.color = 'rgba(255,255,255,1)';
C_Text.width = 100;
C_Text.moveTo(gWidth-100, gHeight-20);
S_MAIN.addChild(C_Text);
C_Text.text = "(c) 2020 G LLC";
var mPause = 0;
var mGsize = 10;
//--- cell ---//
var mCells = Array();
var mState = Array();// 0:dead, 1:alive(me), -1:alive(enemy)
var mPreState = Array();
var mReady = Array();
var mLastPos = Array();// 0:x, 1:y
mLastPos[0] = 0;
mLastPos[1] = 0;
var mCellSprite = new Sprite(gWidth, gHeight);
var mCellSurface = new Surface(gWidth, gHeight);
mCellSprite.image = mCellSurface;
S_MAIN.addChild(mCellSprite);
var nW = gWidth / mGsize;
var nH = gWidth / mGsize;//Square
for(i=0;i<nH;i++){
for(j=0;j<nW;j++){
var id_ = j+i*nW;
mState[id_] = 0;
mPreState[id_] = 0;
mReady[id_] = 0;
mCellSurface.context.beginPath();
mCellSurface.context.fillStyle = mGrey;//'rgb(50, 50, 50)';
mCellSurface.context.fillRect(mGsize*j, mGsize*i, mGsize, mGsize);//X、Y、W、H
mCellSurface.context.strokeStyle = 'rgb(0, 0, 0)';
mCellSurface.context.strokeRect(mGsize*j, mGsize*i, mGsize, mGsize);
}//j
}//i
function mSetCellColor(id_)
{
var color_ = mGrey;
if(mState[id_]==0){
}
else if(mState[id_]==1){
color_ = 'rgb(0,255,0)';
}
else if(mState[id_]==-1){
color_ = 'rgb(255,0,0)';
}
if(mReady[id_]==1){
color_ = 'rgb(0,0,255)';
}
// mCells[id_].image.context.beginPath();
// mCells[id_].image.context.fillStyle = color_;
// mCells[id_].image.context.fillRect(0, 0, mGsize, mGsize);//X、Y、W、H
// mCells[id_].image.context.strokeStyle = 'rgb(0, 0, 0)';
// mCells[id_].image.context.strokeRect(0, 0, mGsize, mGsize);
var y = Math.floor(id_/nW);
var x = id_%nW;
mCellSprite.image.context.beginPath();
mCellSprite.image.context.fillStyle = color_;
mCellSprite.image.context.fillRect(x*mGsize, y*mGsize, mGsize, mGsize);//X、Y、W、H
mCellSprite.image.context.strokeStyle = 'rgb(0, 0, 0)';
mCellSprite.image.context.strokeRect(x*mGsize, y*mGsize, mGsize, mGsize);
}//
//--- Enemy cell ---//
// var array_enemy = [0,1,2,4,5,6,8,9,10,12,13,14,16,17,18];
// for(j=0;j<array_enemy.length;j++){
// id_ = array_enemy[j] + nW*2;
// mState[id_]=-1;
// mSetCellColor(id_);
// }//
// for(j=1;j<nW;j++){
// if(j%4!=0){
// id_ = j + nW*2;
// mState[id_]=-1;
// mSetCellColor(id_);
// }
// }//
var mEnemyLevel = 1;
function mSetEnemyCell()
{
var n_ = 10 + (mEnemyLevel-1)*5
for(i=0;i<n_;i++){
var x = Math.floor(Math.random()*1000000000) % nW;
var y = Math.floor(Math.random()*1000000000) % nH;
x = Math.max(x,1);
x = Math.min(x,nW-2);
y = Math.max(y,1);
y = Math.min(y,nH-2);
var id_ = x + y*nW;
mState[id_-1]=-1;
mState[id_]=-1;
mState[id_+1]=-1;
mSetCellColor(id_-1);
mSetCellColor(id_);
mSetCellColor(id_+1);
}
}
mSetEnemyCell();
function mGetAroundCellCount(i, j)
{
//console.log('mGetCellCount[' + i +',' + j + ']');
var is_ = Math.max(0,i-1);
var ie_ = Math.min(nH-1,i+1);
var js_ = Math.max(0,j-1);
var je_ = Math.min(nW-1,j+1);
//console.log(is_ +',' + ie_ +',' + js_ + ','+je_);
var c = 0;
var d = 0;
for(i2=is_;i2<=ie_;i2++){
for(j2=js_;j2<=je_;j2++)
{
//console.log(i2+',' + j2 );
var idx_ = j2+i2*nW;
d += mPreState[idx_];
if( (i2==i) && (j2==j) ){
continue;
}
if(mPreState[idx_]!=0){
c += 1;
}
}
}
//console.log('c:' + c);
//return c;
return Array(c,d);
}// func
function mUpdateCell()
{
console.log('mUpdateCell:');
for(i=0;i<nH;i++){
for(j=0;j<nW;j++){
var id_ = j+i*nW;
mPreState[id_]=mState[id_];
}
}
for(i=0;i<nH;i++){
for(j=0;j<nW;j++){
var id_ = j+i*nW;
var a_ = mGetAroundCellCount(i, j);
//console.log('id='+id_+', a:'+a_);
var c_ = a_[0];
var d_ = a_[1];
//console.log('id='+id_+':'+c_+','+d_);
if( (mPreState[id_]==0) && (c_!=3) ){ //その他
mState[id_] = 0;
}
else if( (mPreState[id_]==0) && (c_==3) ){ //誕生
mState[id_] = 1;
if(d_<0){
mState[id_] = -1;
}
}
else if( (mPreState[id_]!=0) && ((c_==2) || (c_==3)) ){ //生存
mState[id_] = 1;
if(d_<0){
mState[id_] = -1;
}
}
else if( (mPreState[id_]!=0) && (c_<=1) ){ //過疎
mState[id_] = 0;
}
else if( (mPreState[id_]!=0) && (c_>=4) ){ //過密
mState[id_] = 0;
}
}
}
//--- Updated Color ---//
for(i=0;i<nH*nW;i++){
if(mState[i]!=mPreState[i]){
mSetCellColor(i);
}
}
}//
function mAddReadyCell()
{
console.log('mAddReadyCell:');
for(i=0;i<nH;i++){
for(j=0;j<nW;j++){
var id_ = j+i*nW;
if(mReady[id_]==1){
mState[id_]=1;
mReady[id_]=0;
mSetCellColor(id_);
}
}
}
}
function mClearCell()
{
console.log('mClearCell:');
for(i=0;i<nH;i++){
for(j=0;j<nW;j++){
var id_ = j+i*nW;
mState[id_]=0;
mReady[id_]=0;
mSetCellColor(id_);
}
}
}
function mGetCellNumber()
{
var c_ = 0;
for(i=0;i<nH*nW;i++){
if(mState[i]==1){
c_ += 1;
}
}
return c_;
}//
function mGetEnemyCellNumber()
{
var c_ = 0;
for(i=0;i<nH*nW;i++){
if(mState[i]==-1){
c_ += 1;
}
}
return c_;
}//
var mDrop = new Sprite(30,30);
mDrop.moveTo(10, 10+gWidth);
var surface_ = new Surface(30, 30);
surface_.context.beginPath();
surface_.context.fillStyle = 'rgb(0,0,255)';
surface_.context.fillRect(0, 0, 30, 30);//X、Y、W、H
mDrop.image = surface_;
S_MAIN.addChild(mDrop);
mDrop.ontouchend = function ()
{
game.assets[ClickSound].clone().play();
//mUpdateCell();
mAddReadyCell();
};
//--- Touch event ---//
var mTouchDown = 0;
S_MAIN.addEventListener('touchstart', function(e)
{
mTouchDown = 1;
//console.log('touchstart:'+ 'X:' + e.localX + ',' + 'Y:' + e.localY);
var cx = Math.floor(e.localX/mGsize);
var cy = Math.floor(e.localY/mGsize);
console.log('touchstart:'+ 'X:' + cx +','+'Y:'+cy);
if( (cx>=0) && (cx<nW) && (cy>=0) && (cy<nH) ){
mLastPos[0] = cx;
mLastPos[1] = cy;
game.assets[CellSound].clone().play();
var idx_ = cy*nW+cx;
mReady[idx_] = (mReady[idx_]+1)%2;
console.log('mReady:' + mReady[idx_]);
mSetCellColor(idx_);
}
});
S_MAIN.addEventListener('touchmove', function(e)
{
if( mTouchDown == 0 ){
return;
}
var cx = Math.floor(e.localX/mGsize);
var cy = Math.floor(e.localY/mGsize);
console.log('touchmove:'+ 'X:' + cx +','+'Y:'+cy);
if( (cx>=0) && (cx<nW) && (cy>=0) && (cy<nH) )
{
var idx_ = cy*nW+cx;
var last_idx_ = mLastPos[1]*nW+mLastPos[0];
if( idx_ != last_idx_ )
{
game.assets[CellSound].clone().play();
mReady[idx_] = (mReady[idx_]+1)%2;
console.log('mReady:' + mReady[idx_]);
mSetCellColor(idx_);
mLastPos[0] = cx;
mLastPos[1] = cy;
}
}
});
S_MAIN.addEventListener('touchend', function(e)
{
mTouchDown = 0;
});
//--- Key command ---//
var mDirection = 0; //0:up, 1;right, 2:down, 3:left
function mSetReadyCell(array_)
{
console.log('mSetReadyCell:'+ array_.length);
var n_ = array_.length / 2;
for(p=0;p<n_;p++){
var i = array_[p*2+1];
var j = array_[p*2+0];
var id_ = j+i*nW;
mReady[id_]=1;
mSetCellColor(id_);
}
}
mDrop.addEventListener('enterframe', function(e)
{
if (game.input.left){
console.log('left pressed:'); //-> not shown
//game.assets[CellSound].clone().play(); //-> executed
mDirection = 3;
game.assets[CellSound].clone().play();
mDirIndicator.moveTo(mBtnLeft.x-3,mBtnLeft.y-3);
}
if (game.input.right){
mDirection = 1;
game.assets[CellSound].clone().play();
mDirIndicator.moveTo(mBtnRight.x-3,mBtnRight.y-3);
}
if (game.input.up){
mDirection = 0;
game.assets[CellSound].clone().play();
mDirIndicator.moveTo(mBtnUp.x-3,mBtnUp.y-3);
}
if (game.input.down){
mDirection = 2;
game.assets[CellSound].clone().play();
mDirIndicator.moveTo(mBtnDown.x-3,mBtnDown.y-3);
}
//console.log('mDirection:'+mDirection);//->OK
if (game.input.g){
game.assets[CellSound].clone().play();
//mCreateGlider();
mSetReadyCell( mCreateGlider(mLastPos[0],mLastPos[1],nW,nH,mDirection) );
}
if (game.input.s){
game.assets[CellSound].clone().play();
mSetReadyCell( mCreateSpaceShipS(mLastPos[0],mLastPos[1],nW,nH,mDirection) );
}
if (game.input.p){
mPause = (mPause+1)%2;
game.assets[CellSound].clone().play();
}
if (game.input.space){
game.assets[ClickSound].clone().play();
mAddReadyCell();
}
});
game.keybind(71,'g');
game.keybind(80,'p');
game.keybind(83,'s');
game.keybind(32,'space');
//------------
var mDirIndicator = new Sprite(36,36);
mDirIndicator.moveTo(-200, 0);
var surface_d = new Surface(36, 36);
surface_d.context.beginPath();
surface_d.context.fillStyle = 'rgb(0,255,255)';
surface_d.context.fillRect(0, 0, 36, 36);//X、Y、W、H
mDirIndicator.image = surface_d;
S_MAIN.addChild(mDirIndicator);
var px_mBtnUp = 90;
var mBtnUp = new Sprite(30,30);
mBtnUp.moveTo(px_mBtnUp, gWidth+10);
mBtnUp.image = game.assets[mImgUp];
S_MAIN.addChild(mBtnUp);
mBtnUp.ontouchend = function ()
{
game.assets[CellSound].clone().play();
mDirection = 0;
mDirIndicator.moveTo(mBtnUp.x-3,mBtnUp.y-3);
};
var mBtnRight = new Sprite(30,30);
mBtnRight.moveTo(px_mBtnUp+35, gWidth+10+25);
mBtnRight.image = game.assets[mImgRight];
S_MAIN.addChild(mBtnRight);
mBtnRight.ontouchend = function ()
{
game.assets[CellSound].clone().play();
mDirection = 1;
mDirIndicator.moveTo(mBtnRight.x-3,mBtnRight.y-3);
};
var mBtnDown = new Sprite(30,30);
mBtnDown.moveTo(px_mBtnUp, gWidth+10+50);
mBtnDown.image = game.assets[mImgDown];
S_MAIN.addChild(mBtnDown);
mBtnDown.ontouchend = function ()
{
game.assets[CellSound].clone().play();
mDirection = 2;
mDirIndicator.moveTo(mBtnDown.x-3,mBtnDown.y-3);
};
var mBtnLeft = new Sprite(30,30);
mBtnLeft.moveTo(px_mBtnUp-35, gWidth+10+25);
mBtnLeft.image = game.assets[mImgLeft];
S_MAIN.addChild(mBtnLeft);
mBtnLeft.ontouchend = function ()
{
game.assets[CellSound].clone().play();
mDirection = 3;
mDirIndicator.moveTo(mBtnLeft.x-3,mBtnLeft.y-3);
};
var mBtnGlider = new Sprite(30,30);
mBtnGlider.moveTo(200, gWidth+10);
mBtnGlider.image = game.assets[mImgG];
S_MAIN.addChild(mBtnGlider);
mBtnGlider.ontouchend = function ()
{
game.assets[CellSound].clone().play();
//mCreateGlider();
mSetReadyCell( mCreateGlider(mLastPos[0],mLastPos[1],nW,nH,mDirection) );
};
var mClear = new Sprite(30,30);
mClear.moveTo(350, gWidth+10+50);
var surface_c = new Surface(30, 30);
surface_c.context.beginPath();
surface_c.context.fillStyle = 'rgb(255,255,255)';
surface_c.context.fillRect(0, 0, 30, 30);//X、Y、W、H
mClear.image = surface_c;
S_MAIN.addChild(mClear);
mClear.ontouchend = function ()
{
game.assets[ClickSound].clone().play();
mClearCell();
};
var mBtnPause = new Sprite(30,30);
mBtnPause.moveTo(350, gWidth+10);
var surface_p = new Surface(30, 30);
surface_p.context.beginPath();
surface_p.context.fillStyle = 'rgb(0,200,0)';
surface_p.context.fillRect(0, 0, 30, 30);//X、Y、W、H
mBtnPause.image = surface_p;
S_MAIN.addChild(mBtnPause);
mBtnPause.ontouchend = function ()
{
game.assets[ClickSound].clone().play();
mPause = (mPause+1)%2;
};
///////////////////////////////////////////////////
//Main Loop
game.onenterframe = function ()
{
if(mPause==0)
{
mUpdateCell();
S_Text.text = "My Cell:" + mGetCellNumber();
E_Text.text = "Enemy Lv."+mEnemyLevel+ ":" + mGetEnemyCellNumber();
//mPause = 1;
if(mGetEnemyCellNumber()==0){
mSetEnemyCell();
mEnemyLevel += 1;
//game.assets[mBGM1].play();
}
}
};
};// onload
game.start();
};
///----------
ソースコード(オンライン対戦版)
ご自由に改変してお楽しみください。