-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Conway's Game of Life example, an animated texture applied to a torus rotating in space.
- Loading branch information
Showing
4 changed files
with
398 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Conway's Game of Life</title> | ||
</head> | ||
<body> | ||
|
||
<center> | ||
<h1>Conway's Game of Life</h1> | ||
<canvas id="canvas" width=512 height=512></canvas> | ||
</center> | ||
|
||
<script> | ||
|
||
// "fixed" mod function | ||
Number.prototype.mod = function(n) | ||
{ | ||
return ((this%n)+n)%n; | ||
} | ||
|
||
class Cell | ||
{ | ||
constructor() | ||
{ | ||
this.prevState = null; // used to detect change (need to draw) when rendering? | ||
this.currentState = 0; // 0:dead, 1:alive | ||
this.nextState = null; // used to avoid creating a second board | ||
this.count = 0; // # of live neighbors | ||
} | ||
} | ||
|
||
let canvas = document.getElementById("canvas"); | ||
let context = canvas.getContext("2d"); | ||
|
||
let canvasSize = 512; | ||
let boardSize = 128; | ||
let cellSize = canvasSize / boardSize; | ||
|
||
let DEAD = 0; | ||
let ALIVE = 1; | ||
let cellColors = { 0: "#DDDDDD", 1: "#0088FF" }; | ||
|
||
// used to identify cell neighbors | ||
let offsets = [ {dx:1, dy:0}, {dx:1, dy:1}, {dx:0, dy:1}, {dx:-1, dy:1}, | ||
{dx:-1, dy:0}, {dx:-1, dy:-1}, {dx:0, dy:-1}, {dx:1, dy:-1} ]; | ||
|
||
|
||
// board: 2D array of cells | ||
let board = null; | ||
|
||
function initialize() | ||
{ | ||
// initialize board | ||
board = []; | ||
|
||
for (let rowNumber = 0; rowNumber < boardSize; rowNumber++) | ||
{ | ||
let row = []; | ||
for (let columnNumber = 0; columnNumber < boardSize; columnNumber++) | ||
{ | ||
let cell = new Cell(); | ||
|
||
if ( Math.random() < 0.30 ) | ||
cell.currentState = 1; | ||
else | ||
cell.currentState = 0; | ||
|
||
row.push( cell ); | ||
} | ||
board.push(row); | ||
} | ||
|
||
// initialize canvas | ||
context.fillStyle = "#DDDDDD"; | ||
context.fillRect(0,0, 512,512); | ||
|
||
context.strokeStyle = "#BBBBBB"; | ||
context.lineWidth = 1; | ||
|
||
// draw lines across rows | ||
for (let rowNumber = 0; rowNumber < boardSize; rowNumber++) | ||
{ | ||
context.beginPath(); | ||
context.moveTo(0, rowNumber * cellSize + 0.5); | ||
context.lineTo(canvasSize, rowNumber * cellSize + 0.5); | ||
context.closePath(); | ||
context.stroke(); | ||
} | ||
|
||
// draw lines down columns | ||
for (let columnNumber = 0; columnNumber < boardSize; columnNumber++) | ||
{ | ||
context.beginPath(); | ||
context.moveTo(columnNumber * cellSize + 0.5, 0); | ||
context.lineTo(columnNumber * cellSize + 0.5, canvasSize); | ||
context.closePath(); | ||
context.stroke(); | ||
} | ||
|
||
} | ||
|
||
function update() | ||
{ | ||
// count adjacent neighbors currentState | ||
for (let rowNumber = 0; rowNumber < boardSize; rowNumber++) | ||
{ | ||
for (let columnNumber = 0; columnNumber < boardSize; columnNumber++) | ||
{ | ||
let cell = board[rowNumber][columnNumber]; | ||
|
||
// count neighbors whose current state is ALIVE | ||
cell.count = 0; | ||
|
||
for (let i=0; i<8; i++) | ||
{ | ||
if (board[ (rowNumber + offsets[i].dy).mod(boardSize) ][ (columnNumber + offsets[i].dx).mod(boardSize) ].currentState == ALIVE) | ||
cell.count += 1; | ||
} | ||
|
||
if (cell.count < 2) // death by underpopulation | ||
cell.nextState = 0; | ||
else if (cell.count == 2) // stable | ||
cell.nextState = cell.currentState; | ||
else if (cell.count == 3) // birth | ||
cell.nextState = 1; | ||
else // (cell.count > 3) // death by overpopulation | ||
cell.nextState = 0 | ||
} | ||
} | ||
|
||
// update current states to next states | ||
for (let rowNumber = 0; rowNumber < boardSize; rowNumber++) | ||
{ | ||
for (let columnNumber = 0; columnNumber < boardSize; columnNumber++) | ||
{ | ||
let cell = board[rowNumber][columnNumber]; | ||
cell.prevState = cell.currentState; | ||
cell.currentState = cell.nextState; | ||
cell.nextState = null; | ||
} | ||
} | ||
} | ||
|
||
function render() | ||
{ | ||
// console.log("Rendering..."); | ||
|
||
for (let rowNumber = 0; rowNumber < boardSize; rowNumber++) | ||
{ | ||
for (let columnNumber = 0; columnNumber < boardSize; columnNumber++) | ||
{ | ||
let cell = board[rowNumber][columnNumber]; | ||
let canvasY = rowNumber * cellSize; | ||
let canvasX = columnNumber * cellSize; | ||
|
||
// only redraw if cell has changed state | ||
if (cell.currentState != cell.prevState) | ||
{ | ||
context.fillStyle = cellColors[ cell.currentState ]; | ||
context.fillRect(canvasX+1, canvasY+1, cellSize-1, cellSize-1); | ||
} | ||
} | ||
} | ||
} | ||
|
||
function gameloop() | ||
{ | ||
update(); | ||
render(); | ||
setTimeout( gameloop, 50 ); | ||
} | ||
|
||
// run the code | ||
initialize(); | ||
gameloop(); | ||
|
||
</script> | ||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
|
||
<head> | ||
<title>A-Frame: Conway's Game of Life</title> | ||
<meta name="description" content="A-Frame Examples"> | ||
<script src="js/aframe-master.1.0.4.min.js"></script> | ||
<script src="js/aframe-environment-component.min.js"></script> | ||
|
||
</head> | ||
|
||
<body> | ||
|
||
<script> | ||
|
||
// "fixed" mod function | ||
Number.prototype.mod = function(n) | ||
{ | ||
return ((this%n)+n)%n; | ||
} | ||
|
||
class Cell | ||
{ | ||
constructor() | ||
{ | ||
this.prevState = null; // used to detect change (need to draw) when rendering? | ||
this.currentState = 0; // 0:dead, 1:alive | ||
this.nextState = null; // used to avoid creating a second board | ||
this.count = 0; // # of live neighbors | ||
} | ||
} | ||
|
||
|
||
AFRAME.registerComponent('draw-canvas', { | ||
|
||
init: function() | ||
{ | ||
this.canvas = document.querySelector("#mycanvas"); | ||
this.context = this.canvas.getContext("2d"); | ||
this.canvasSize = 512; | ||
this.boardSize = 128; | ||
this.cellSize = this.canvasSize / this.boardSize; | ||
this.DEAD = 0; | ||
this.ALIVE = 1; | ||
this.cellColors = { 0: "#FFFF88", 1: "#0088FF" }; | ||
|
||
// used to identify cell neighbors | ||
this.offsets = [ {dx:1, dy:0}, {dx:1, dy:1}, {dx:0, dy:1}, {dx:-1, dy:1}, | ||
{dx:-1, dy:0}, {dx:-1, dy:-1}, {dx:0, dy:-1}, {dx:1, dy:-1} ]; | ||
// board: 2D array of cells | ||
this.board = []; | ||
|
||
for (let rowNumber = 0; rowNumber < this.boardSize; rowNumber++) | ||
{ | ||
let row = []; | ||
for (let columnNumber = 0; columnNumber < this.boardSize; columnNumber++) | ||
{ | ||
let cell = new Cell(); | ||
|
||
if ( Math.random() < 0.30 ) | ||
cell.currentState = 1; | ||
else | ||
cell.currentState = 0; | ||
|
||
row.push( cell ); | ||
} | ||
this.board.push(row); | ||
} | ||
|
||
// initialize canvas | ||
this.context.fillStyle = this.cellColors[this.DEAD]; | ||
this.context.fillRect(0,0, this.canvasSize,this.canvasSize); | ||
this.context.strokeStyle = "#BBBBBB"; | ||
this.context.lineWidth = 1; | ||
|
||
// draw lines across rows | ||
for (let rowNumber = 0; rowNumber < this.boardSize; rowNumber++) | ||
{ | ||
this.context.beginPath(); | ||
this.context.moveTo(0, rowNumber * this.cellSize + 0.5); | ||
this.context.lineTo(this.canvasSize, rowNumber * this.cellSize + 0.5); | ||
this.context.closePath(); | ||
this.context.stroke(); | ||
} | ||
|
||
// draw lines down columns | ||
for (let columnNumber = 0; columnNumber < this.boardSize; columnNumber++) | ||
{ | ||
this.context.beginPath(); | ||
this.context.moveTo(columnNumber * this.cellSize + 0.5, 0); | ||
this.context.lineTo(columnNumber * this.cellSize + 0.5, this.canvasSize); | ||
this.context.closePath(); | ||
this.context.stroke(); | ||
} | ||
|
||
this.tickNumber = 0; | ||
}, | ||
|
||
tick: function(t) | ||
{ | ||
// slow down animation by updating every n-th tick... | ||
this.tickNumber += 1; | ||
if (this.tickNumber % 4 != 0) | ||
return; | ||
|
||
// update board state | ||
|
||
// count adjacent neighbors currentState | ||
for (let rowNumber = 0; rowNumber < this.boardSize; rowNumber++) | ||
{ | ||
for (let columnNumber = 0; columnNumber < this.boardSize; columnNumber++) | ||
{ | ||
let cell = this.board[rowNumber][columnNumber]; | ||
|
||
// count neighbors whose current state is ALIVE | ||
cell.count = 0; | ||
|
||
for (let i=0; i<8; i++) | ||
{ | ||
if (this.board[ (rowNumber + this.offsets[i].dy).mod(this.boardSize) ][ (columnNumber + this.offsets[i].dx).mod(this.boardSize) ].currentState == this.ALIVE) | ||
cell.count += 1; | ||
} | ||
|
||
if (cell.count < 2) // death by underpopulation | ||
cell.nextState = 0; | ||
else if (cell.count == 2) // stable | ||
cell.nextState = cell.currentState; | ||
else if (cell.count == 3) // birth | ||
cell.nextState = 1; | ||
else // (cell.count > 3) // death by overpopulation | ||
cell.nextState = 0 | ||
} | ||
} | ||
|
||
// update current states to next states | ||
for (let rowNumber = 0; rowNumber < this.boardSize; rowNumber++) | ||
{ | ||
for (let columnNumber = 0; columnNumber < this.boardSize; columnNumber++) | ||
{ | ||
let cell = this.board[rowNumber][columnNumber]; | ||
cell.prevState = cell.currentState; | ||
cell.currentState = cell.nextState; | ||
cell.nextState = null; | ||
} | ||
} | ||
|
||
// update canvas | ||
for (let rowNumber = 0; rowNumber < this.boardSize; rowNumber++) | ||
{ | ||
for (let columnNumber = 0; columnNumber < this.boardSize; columnNumber++) | ||
{ | ||
let cell = this.board[rowNumber][columnNumber]; | ||
let canvasY = rowNumber * this.cellSize; | ||
let canvasX = columnNumber * this.cellSize; | ||
|
||
// only redraw if cell has changed state | ||
if (cell.currentState != cell.prevState) | ||
{ | ||
this.context.fillStyle = this.cellColors[ cell.currentState ]; | ||
this.context.fillRect(canvasX+1, canvasY+1, this.cellSize-1, this.cellSize-1); | ||
} | ||
} | ||
} | ||
|
||
// update material map from canvas | ||
// thanks to https://github.com/aframevr/aframe/issues/3936 for the update fix | ||
let material = this.el.getObject3D('mesh').material; | ||
if (!material.map) | ||
return; | ||
else | ||
material.map.needsUpdate = true; | ||
} | ||
|
||
}); | ||
|
||
AFRAME.registerComponent('rotato', { | ||
|
||
tick: function() | ||
{ | ||
this.el.object3D.rotation.x += 0.005337; | ||
this.el.object3D.rotation.y += 0.008049; | ||
} | ||
}); | ||
|
||
</script> | ||
|
||
<!-- | ||
In a-scene, normally disable default lights with light="defaultLightsEnabled: false;" | ||
Alternative approach needed when using environment component. | ||
--> | ||
<a-scene environment="preset: default; lighting: none;"> | ||
|
||
<a-assets> | ||
<!-- texture sizes should be powers of 2 --> | ||
<canvas id="mycanvas" width=512 height=512></canvas> | ||
</a-assets> | ||
|
||
|
||
<a-entity light="type: ambient; color: #AAA"></a-entity> | ||
<a-entity light="type: directional; color: #BBB; intensity: 0.5" position="-0.5 1 1"></a-entity> | ||
|
||
<a-torus position="0 2 -3" material = "src: #mycanvas;" draw-canvas rotato> | ||
|
||
</a-torus> | ||
|
||
</a-scene> | ||
|
||
</body> | ||
</html> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.