upgraded engine with input buffering and added arcade & survival
This commit is contained in:
parent
03e0c97280
commit
de390dff8a
5 changed files with 74 additions and 18 deletions
|
@ -20,6 +20,7 @@
|
||||||
"fruitRegrow": true,
|
"fruitRegrow": true,
|
||||||
"speedIncrease": true,
|
"speedIncrease": true,
|
||||||
"speedMultiplier": 0.9,
|
"speedMultiplier": 0.9,
|
||||||
|
"speedCap": 50,
|
||||||
"worldWrap": true
|
"worldWrap": true
|
||||||
},
|
},
|
||||||
"levelFilename": "arcade-<l>.json",
|
"levelFilename": "arcade-<l>.json",
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
"join": "round",
|
"join": "round",
|
||||||
"cap": "round",
|
"cap": "round",
|
||||||
"headSize": 0.8,
|
"headSize": 0.8,
|
||||||
"tailSize": 0.4
|
"tailSize": 0.4,
|
||||||
|
"tailWrapSize": 0.1
|
||||||
}
|
}
|
||||||
|
|
12
levels/arcade-arcade.json
Normal file
12
levels/arcade-arcade.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"dimensions": [32, 32],
|
||||||
|
"delay": 200,
|
||||||
|
"food": [
|
||||||
|
[16, 16]
|
||||||
|
],
|
||||||
|
"snake": [
|
||||||
|
[16, 12],
|
||||||
|
[16, 11],
|
||||||
|
[16, 10]
|
||||||
|
]
|
||||||
|
}
|
16
levels/arcade-survival.json
Normal file
16
levels/arcade-survival.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"dimensions": [32, 32],
|
||||||
|
"delay": 200,
|
||||||
|
"food": [],
|
||||||
|
"snake": [
|
||||||
|
[16, 12],
|
||||||
|
[16, 11],
|
||||||
|
[16, 10]
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"autoSpeedIncrease": true,
|
||||||
|
"autoSpeadIncreaseTicks": 10,
|
||||||
|
"autoSizeGrow": true,
|
||||||
|
"autoSizeGrowTicks": 100
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
const assets=require('assets');
|
|
||||||
|
|
||||||
const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys();
|
const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys();
|
||||||
|
|
||||||
const ifNaN=(v, r) => isNaN(v)?r:v;
|
const ifNaN=(v, r) => isNaN(v)?r:v;
|
||||||
|
@ -51,7 +49,7 @@ class SnekGame {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the walls
|
// add the walls
|
||||||
settings.walls.forEach(([x, y]) => this.world[x][y]=WALL);
|
if(settings.walls) settings.walls.forEach(([x, y]) => this.world[x][y]=WALL);
|
||||||
|
|
||||||
// add the food
|
// add the food
|
||||||
settings.food.forEach(([x, y]) => this.world[x][y]=FOOD);
|
settings.food.forEach(([x, y]) => this.world[x][y]=FOOD);
|
||||||
|
@ -68,6 +66,7 @@ class SnekGame {
|
||||||
ifNaN(settings.snake[0][0]-settings.snake[1][0], 1),
|
ifNaN(settings.snake[0][0]-settings.snake[1][0], 1),
|
||||||
ifNaN(settings.snake[0][1]-settings.snake[1][1], 0)
|
ifNaN(settings.snake[0][1]-settings.snake[1][1], 0)
|
||||||
];
|
];
|
||||||
|
this.lastDirection=this.direction
|
||||||
|
|
||||||
// store the snake
|
// store the snake
|
||||||
this.snake=[...settings.snake];
|
this.snake=[...settings.snake];
|
||||||
|
@ -75,7 +74,6 @@ class SnekGame {
|
||||||
// get our canvas, like, if we want to actually draw
|
// get our canvas, like, if we want to actually draw
|
||||||
this.canvas=canvas;
|
this.canvas=canvas;
|
||||||
this.ctx=canvas.getContext('2d');
|
this.ctx=canvas.getContext('2d');
|
||||||
//TODO this.gl=canvas.getContext('webgl');
|
|
||||||
|
|
||||||
// load the custom rules
|
// load the custom rules
|
||||||
this.rules=Object.assign({
|
this.rules=Object.assign({
|
||||||
|
@ -93,6 +91,8 @@ class SnekGame {
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
|
const assets=require('assets');
|
||||||
|
|
||||||
// clear the canvas, because it's easier than having to deal with everything
|
// clear the canvas, because it's easier than having to deal with everything
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
|
||||||
|
@ -150,11 +150,22 @@ class SnekGame {
|
||||||
this.ctx.fill();
|
this.ctx.fill();
|
||||||
|
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.snake.forEach(([x, y], i) => {
|
this.snake.forEach(([x, y], i, a) => {
|
||||||
this.ctx.lineTo(
|
this.ctx.lineTo(
|
||||||
offsetX+cellSize*(x+1/2),
|
offsetX+cellSize*(x+1/2),
|
||||||
offsetY+cellSize*(y+1/2)
|
offsetY+cellSize*(y+1/2)
|
||||||
);
|
);
|
||||||
|
if(i!=0 && Math.hypot(x-a[i-1][0], y-a[i-1][1])>1) {
|
||||||
|
this.ctx.lineWidth=cellSize*snake.tailWrapSize;
|
||||||
|
} else {
|
||||||
|
this.ctx.lineWidth=cellSize*snake.tailSize;
|
||||||
|
}
|
||||||
|
this.ctx.stroke();
|
||||||
|
this.ctx.beginPath()
|
||||||
|
this.ctx.moveTo(
|
||||||
|
offsetX+cellSize*(x+1/2),
|
||||||
|
offsetY+cellSize*(y+1/2)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
|
|
||||||
|
@ -174,6 +185,9 @@ class SnekGame {
|
||||||
}
|
}
|
||||||
|
|
||||||
step() {
|
step() {
|
||||||
|
this.tickId++;
|
||||||
|
this.lastDirection=this.direction;
|
||||||
|
|
||||||
// compute our new head
|
// compute our new head
|
||||||
const head=[
|
const head=[
|
||||||
this.snake[0][0]+this.direction[0],
|
this.snake[0][0]+this.direction[0],
|
||||||
|
@ -227,12 +241,27 @@ class SnekGame {
|
||||||
this.fruits.push(cell);
|
this.fruits.push(cell);
|
||||||
this.world[cell[0]][cell[1]]=FOOD;
|
this.world[cell[0]][cell[1]]=FOOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.rules.speedIncrease) {
|
||||||
|
this.delay*=this.rules.speedMultiplier;
|
||||||
|
if(this.delay<this.rules.speedCap) this.delay=this.rules.speedCap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move our head forward
|
// move our head forward
|
||||||
this.world[head[0]][head[1]]=SNAKE;
|
this.world[head[0]][head[1]]=SNAKE;
|
||||||
this.snake.unshift(head);
|
this.snake.unshift(head);
|
||||||
|
|
||||||
|
// automatic speed increase
|
||||||
|
if(this.rules.autoSpeedIncrease) {
|
||||||
|
if(this.delay>50 && this.tickId%this.rules.autoSpeadIncreaseTicks==0) this.delay--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatic size grow
|
||||||
|
if(this.rules.autoSizeGrow) {
|
||||||
|
if(this.tickId%this.rules.autoSizeGrowTicks==0) this.snake.push(tail);
|
||||||
|
}
|
||||||
|
|
||||||
// victory condition
|
// victory condition
|
||||||
if(this.rules.winCondition=='fruit') {
|
if(this.rules.winCondition=='fruit') {
|
||||||
if(!this.fruits.length) return this.win();
|
if(!this.fruits.length) return this.win();
|
||||||
|
@ -271,23 +300,20 @@ class SnekGame {
|
||||||
|
|
||||||
handleInputs(inputs) {
|
handleInputs(inputs) {
|
||||||
const trySet=(dir) => {
|
const trySet=(dir) => {
|
||||||
if(!(this.direction[0]==-dir[0] && this.direction[1]==-dir[1])) this.direction=dir;
|
if(!dir.every((e, i) => e==this.lastDirection[i] || e==-this.lastDirection[i])) {
|
||||||
|
this.direction=dir;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if(inputs.left) {
|
|
||||||
trySet([-1, 0]);
|
|
||||||
} else if(inputs.right) {
|
|
||||||
trySet([ 1, 0]);
|
|
||||||
} else if(inputs.up) {
|
|
||||||
trySet([ 0,-1]);
|
|
||||||
} else if(inputs.down) {
|
|
||||||
trySet([ 0, 1]);
|
|
||||||
}
|
}
|
||||||
|
if(inputs.left && trySet([-1, 0])) return delete inputs.left;
|
||||||
Object.keys(inputs).forEach(k => delete inputs[k]);
|
else if(inputs.right && trySet([ 1, 0])) return delete inputs.right;
|
||||||
|
else if(inputs.up && trySet([ 0,-1])) return delete inputs.up;
|
||||||
|
else if(inputs.down && trySet([ 0, 1])) return delete inputs.down;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.firstStep=Date.now();
|
this.firstStep=Date.now();
|
||||||
|
this.tickId=0;
|
||||||
this.playing=true;
|
this.playing=true;
|
||||||
requestAnimationFrame(() => this.tick());
|
requestAnimationFrame(() => this.tick());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue