Animations work! (albeit laggy)

This commit is contained in:
theKidOfArcrania 2017-02-23 00:51:41 +00:00
parent 30023a7f3c
commit 3ce7ba97cf
5 changed files with 332 additions and 115 deletions

49
Grid.js Normal file
View File

@ -0,0 +1,49 @@
this.Grid = (function()
{
function Grid(size, changeCallback)
{
var grid = new Array(size);
var data = {
grid: grid,
size: size
};
this.get = function(row, col)
{
if (isOutOfBounds(data, row, col))
throw new RangeError("Row or Column value out of bounds");
return grid[row] && grid[row][col];
}
this.set = function(row, col, value)
{
if (isOutOfBounds(data, row, col))
throw new RangeError("Row or Column value out of bounds");
if (!grid[row])
grid[row] = new Array(size);
var before = grid[row][col];
grid[row][col] = value;
if (typeof changeCallback === "function")
changeCallback(row, col, before, value);
return before;
}
this.isOutOfBounds = isOutOfBounds.bind(this, data);
Object.defineProperty(this, "size", {
get: function() {return size; },
enumerable: true
});
}
function isOutOfBounds(data, row, col)
{
return row < 0 || row >= data.size || col < 0 || col >= data.size;
}
return Grid;
})()

87
color.js Normal file
View File

@ -0,0 +1,87 @@
this.Color = function()
{
function Color(h, s, l, a)
{
verifyRange(h, s, l);
if (a === undefined) a = 1;
else verifyRange(a);
Object.defineProperties(this, {
"hue": {value: h, enumerable: true},
"sat": {value: s, enumerable: true},
"lum": {value: l, enumerable: true},
"alpha": {value: a, enumerable: true},
});
}
function verifyRange()
{
for (var i = 0; i < arguments.length; i++)
{
if (arguments[i] < 0 || arguments[i] > 1)
throw new RangeError("H, S, L, and A parameters must be between the range [0, 1]");
}
}
Color.prototype.deriveLumination = function(amount)
{
var lum = this.lum + amount;
lum = Math.min(Math.max(lum, 0), 1);
return new Color(this.hue, this.sat, lum, this.alpha);
};
Color.prototype.deriveHue = function(amount)
{
var hue = this.hue - amount;
return new Color(hue - Math.floor(hue), this.sat, this.lum, this.alpha);
};
Color.prototype.deriveSaturation = function(amount)
{
var sat = this.sat + amount;
sat = Math.min(Math.max(sat, 0), 1);
return new Color(this.hue, sat, this.lum, this.alpha);
};
Color.prototype.deriveAlpha = function(newAlpha)
{
verifyRange(newAlpha);
return new Color(this.hue, this.sat, this.lum, newAlpha);
};
Color.prototype.rgbString = function() {
var rgb = hslToRgb(this.hue, this.sat, this.lum);
rgb[3] = this.a;
return 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', ' + this.alpha + ')';
};
//http://stackoverflow.com/a/9493060/7344257
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
return Color;
}()

212
game.js
View File

@ -1,14 +1,12 @@
var Player; var Player;
if (!Player) if (!Player)
throw new Error("Requires player.js"); throw new Error("Requires player.js");
//Thanks to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random var Grid;
function getRandomInt(min, max) { if (!Grid)
min = Math.ceil(min); throw new Error("Requires grid.js");
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
/** /**
* Provides requestAnimationFrame in a cross browser way. * Provides requestAnimationFrame in a cross browser way.
@ -35,7 +33,10 @@ $(function() {
var CELL_WIDTH = 30; var CELL_WIDTH = 30;
var SPEED = 5; var SPEED = 5;
var SHADOW_OFFSET = 5; var SHADOW_OFFSET = 5;
var MAX_FRAMES = 16;
var DROP_HEIGHT = 16;
var BOUNCE_FRAMES = 8;
var DROP_SPEED = 2;
var canvas = $("#main-ui")[0]; var canvas = $("#main-ui")[0];
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
@ -43,28 +44,36 @@ $(function() {
var height = canvas.height = window.innerHeight - 20; var height = canvas.height = window.innerHeight - 20;
var players = []; var players = [];
var grid = []; var animateOff = true;
var animateGrid = new Grid(GRID_SIZE);
var grid = new Grid(GRID_SIZE, function(row, col, before, after) {
if (before === after || animateOff)
return;
animateGrid.set(row, col, {
before: before,
after: after,
frame: 0
});
});
//Load players. //Load players.
for (var p = 0; p < 1; p++) for (var p = 0; p < 9; p++)
{ {
//TODO: socket loading. //TODO: socket loading.
players[p] = new Player(null, grid, p); players[p] = new Player(null, grid, p, getRandomInt(0, GRID_SIZE), getRandomInt(0, GRID_SIZE));
} }
//Load grid. //Load grid.
for (var r = 0; r < GRID_SIZE; r++) for (var r = 0; r < grid.size; r++)
{ {
grid[r] = []; for (var c = 0; c < grid.size; c++)
for (var c = 0; c < GRID_SIZE; c++)
{ {
//TODO: load data. //TODO: load data.
if (Math.random() < .9) if (Math.random() > .9)
grid[r][c] = -1; grid.set(r, c, players[getRandomInt(0, players.length)]);
else
grid[r][c] = players[getRandomInt(0, players.length)];
} }
} }
animateOff = false;
var frameCount = 0; var frameCount = 0;
var animateTo = [0, 0]; var animateTo = [0, 0];
@ -72,6 +81,7 @@ $(function() {
//TODO: current player index //TODO: current player index
var user = players[0]; var user = players[0];
centerOnPlayer(user, offset);
function update() function update()
{ {
@ -115,9 +125,9 @@ $(function() {
squaresIntersect(players[i].startY, players[j].startY)) squaresIntersect(players[i].startY, players[j].startY))
{ {
//...if one player is own his own territory, the other is out. //...if one player is own his own territory, the other is out.
if (grid[players[i].row][players[i].col] === players[i]) if (grid.get(players[i].row, players[i].col) === players[i])
removing[j] = true; removing[j] = true;
else if (grid[players[j].row][players[j].col] === players[j]) else if (grid.get(players[j].row, players[j].col) === players[j])
removing[i] = true; removing[i] = true;
else else
{ {
@ -148,13 +158,25 @@ $(function() {
dead.forEach(function(val) { dead.forEach(function(val) {
console.log(val.name + " is dead"); console.log(val.name + " is dead");
}); });
for (var r = 0; r < grid.size; r++)
{
for (var c = 0; c < grid.size; c++)
{
if (dead.indexOf(grid.get(r, c)) !== -1)
grid.set(r, c, null);
}
}
//TODO: animate dead, and if this player is dead. //TODO: animate dead, and if this player is dead.
var xOff = Math.floor(user.posX - (width - CELL_WIDTH) / 2); centerOnPlayer(user, animateTo);
var yOff = Math.floor(user.posY - (height - CELL_WIDTH) / 2); }
animateTo[0] = Math.min(Math.max(xOff, 0), GRID_SIZE * CELL_WIDTH); function centerOnPlayer(player, pos)
animateTo[1] = Math.min(Math.max(yOff, 0), GRID_SIZE * CELL_WIDTH); {
var xOff = Math.floor(player.posX - (width - CELL_WIDTH) / 2);
var yOff = Math.floor(player.posY - (height - CELL_WIDTH) / 2);
pos[0] = Math.min(Math.max(xOff, 0), grid.size * CELL_WIDTH - width / 2);
pos[1] = Math.min(Math.max(yOff, 0), grid.size * CELL_WIDTH - width / 2);
} }
function area(player) function area(player)
@ -168,6 +190,13 @@ $(function() {
return Math.abs(player.startX - xDest); return Math.abs(player.startX - xDest);
} }
//Thanks to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function squaresIntersect(a, b) function squaresIntersect(a, b)
{ {
if (a < b) if (a < b)
@ -176,44 +205,122 @@ $(function() {
return a < b + CELL_WIDTH; return a < b + CELL_WIDTH;
} }
function paintGridLines()
{
ctx.fillStyle = 'lightgray';
ctx.beginPath();
for (var x = modRotate(-offset[0], CELL_WIDTH); x < width; x += CELL_WIDTH)
{
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
}
for (var y = modRotate(-offset[1], CELL_WIDTH); y < height; y+= CELL_WIDTH)
{
ctx.moveTo(0, y);
ctx.lineTo(width, y);
}
ctx.stroke();
}
function modRotate(val, mod)
{
var res = val % mod;
if (res >= 0)
return res;
else
return mod + res;
}
function paintGrid() function paintGrid()
{ {
//Paint bottom grid lines. //Paint bottom grid lines.
//ctx.fillStyle = 'lightgray'; //paintGridLines();
//ctx.beginPath();
//for (var x = modRotate(-offset[0], CELL_WIDTH); x < width; x += CELL_WIDTH)
//{
// ctx.moveTo(x, 0);
// ctx.lineTo(x, height);
//}
//for (var y = modRotate(-offset[1], CELL_WIDTH); y < height; y+= CELL_WIDTH)
//{
// ctx.moveTo(0, y);
// ctx.lineTo(width, y);
//}
//ctx.stroke();
//Paint occupied areas. //Paint occupied areas. (and fading ones).
for (var r = Math.floor(offset[1] / CELL_WIDTH); r * CELL_WIDTH - offset[1] < height; r++) for (var r = Math.floor(offset[1] / CELL_WIDTH); r < grid.size && r * CELL_WIDTH - offset[1] < height; r++)
{ {
for (var c = Math.floor(offset[0] / CELL_WIDTH); c * CELL_WIDTH - offset[0] < width; c++) for (var c = Math.floor(offset[0] / CELL_WIDTH); c < grid.size && c * CELL_WIDTH - offset[0] < width; c++)
{ {
if (grid[r][c] !== -1) var p = grid.get(r, c);
var x = c * CELL_WIDTH, y = r * CELL_WIDTH, baseColor, shadowColor;
var animateSpec = animateGrid.get(r, c);
if (!animateOff && animateSpec)
{ {
var x = c * CELL_WIDTH, if (animateSpec.before) //fading animation
y = r * CELL_WIDTH, {
p = grid[r][c]; var alpha = 1 - (animateSpec.frame / MAX_FRAMES);
ctx.fillStyle = p.shadowColor; baseColor = animateSpec.before.baseColor.deriveAlpha(alpha);
ctx.fillRect(x, y + SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH); shadowColor = animateSpec.before.shadowColor.deriveAlpha(alpha);
ctx.fillStyle = p.baseColor; }
ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH); else
continue;
}
else if (p)
{
baseColor = p.baseColor;
shadowColor = p.shadowColor;
}
else //No animation nor is this player owned.
continue;
ctx.fillStyle = shadowColor.rgbString();
ctx.fillRect(x, y + CELL_WIDTH, CELL_WIDTH, SHADOW_OFFSET);
ctx.fillStyle = baseColor.rgbString();
ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
}
}
if (animateOff)
return;
//Paint squares with drop in animation.
for (var r = 0; r < grid.size; r++)
{
for (var c = 0; c < grid.size; c++)
{
animateSpec = animateGrid.get(r, c);
x = c * CELL_WIDTH, y = r * CELL_WIDTH;
if (animateSpec && !animateOff)
{
if (animateSpec.after)
{
var offsetBounce = 0;
if (animateSpec.frame >= MAX_FRAMES - BOUNCE_FRAMES)
{
var bounce = animateSpec.frame - MAX_FRAMES + BOUNCE_FRAMES;
var bounceHeight = BOUNCE_FRAMES / 2 * DROP_SPEED;
if (bounce >= BOUNCE_FRAMES / 2)
offsetBounce = bounceHeight - (bounce - BOUNCE_FRAMES / 2) * DROP_SPEED;
else
offsetBounce = bounce * DROP_SPEED;
}
else
offsetBounce = DROP_HEIGHT - DROP_SPEED * animateSpec.frame;
y -= offsetBounce;
shadowColor = animateSpec.after.shadowColor;
baseColor = animateSpec.after.baseColor.deriveLumination(-(offsetBounce / DROP_HEIGHT) * .1);
ctx.fillStyle = shadowColor.rgbString();
ctx.fillRect(x, y + SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH);
ctx.fillStyle = baseColor.rgbString();
ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
}
animateSpec.frame++;
if (animateSpec.frame >= MAX_FRAMES)
animateGrid.set(r, c, null);
} }
} }
} }
} }
var showedDead = false;
function paintLoop() function paintLoop()
{ {
ctx.fillStyle = 'whitesmoke'; ctx.fillStyle = 'whitesmoke';
@ -226,10 +333,11 @@ $(function() {
}); });
ctx.setTransform(1, 0, 0, 1, 0, 0); //Reset transform. ctx.setTransform(1, 0, 0, 1, 0, 0); //Reset transform.
if (user.dead) if (user.dead && !showedDead)
{ {
showedDead = true;
console.log("You died!"); console.log("You died!");
return; //return;
} }
//TODO: sync each loop with server. (server will give frame count.) //TODO: sync each loop with server. (server will give frame count.)
@ -242,6 +350,8 @@ $(function() {
//Event listeners //Event listeners
$(document).keydown(function(e) { $(document).keydown(function(e) {
if (user.dead)
return;
var newHeading = -1; var newHeading = -1;
switch (e.which) switch (e.which)
{ {

View File

@ -2,6 +2,10 @@ var Stack;
if (!Stack) if (!Stack)
throw new Error("Require stack.js"); throw new Error("Require stack.js");
var Color;
if (!Color)
throw new Error("Requre color.js");
var Tail = (function() { var Tail = (function() {
var CELL_WIDTH = 30; var CELL_WIDTH = 30;
var GRID_SIZE = 200; var GRID_SIZE = 200;
@ -44,7 +48,6 @@ var Tail = (function() {
if (!tailGrid[r]) if (!tailGrid[r])
tailGrid[r] = []; tailGrid[r] = [];
tailGrid[r][c] = true; tailGrid[r][c] = true;
} }
function addTail(data, orientation) function addTail(data, orientation)
@ -87,7 +90,7 @@ var Tail = (function() {
function render2(data, ctx) function render2(data, ctx)
{ {
ctx.fillStyle = data.player.tailColor; ctx.fillStyle = data.player.tailColor.rgbString();
for (var r = 0; r < data.tailGrid.length; r++) for (var r = 0; r < data.tailGrid.length; r++)
{ {
if (!data.tailGrid[r]) if (!data.tailGrid[r])
@ -100,7 +103,10 @@ var Tail = (function() {
function render(data, ctx) function render(data, ctx)
{ {
ctx.fillStyle = data.player.tailColor; if (data.tail.length === 0)
return;
ctx.fillStyle = data.player.tailColor.rgbString();
var prevOrient = -1; var prevOrient = -1;
var start = [data.startRow, data.startCol]; var start = [data.startRow, data.startCol];
@ -203,7 +209,7 @@ var Tail = (function() {
function onTail(c) { return data.tailGrid[c[0]] && data.tailGrid[c[0]][c[1]]; } function onTail(c) { return data.tailGrid[c[0]] && data.tailGrid[c[0]][c[1]]; }
var start = [data.startRow, data.startCol]; var start = [data.startRow, data.startCol];
var been = new Array(grid.length); var been = new Grid(grid.size);
var coords = []; var coords = [];
coords.push(start); coords.push(start);
@ -213,18 +219,16 @@ var Tail = (function() {
var r = coord[0]; var r = coord[0];
var c = coord[1]; var c = coord[1];
if (r < 0 || c < 0 || r >= grid.length || c >= grid[r].length) if (grid.isOutOfBounds(r, c))
continue; //Out of bounds! continue;
if (been[r] && been[r][c]) if (been.get(r, c))
continue; continue;
if (onTail(coord)) //on the tail. if (onTail(coord)) //on the tail.
{ {
if (!been[r]) been.set(r, c, true);
been[r] = new Array(grid[r].length); grid.set(r, c, data.player);
been[r][c] = true;
grid[r][c] = data.player;
//Find all spots that this tail encloses. //Find all spots that this tail encloses.
floodFill(data, grid, r + 1, c, been); floodFill(data, grid, r + 1, c, been);
@ -255,19 +259,17 @@ var Tail = (function() {
var r = coord[0]; var r = coord[0];
var c = coord[1]; var c = coord[1];
if (r < 0 || c < 0 || r >= grid.length || c >= grid[r].length) if (grid.isOutOfBounds(r, c))
{ {
surrounded = false; surrounded = false;
continue; //Out of bounds! continue;
} }
//End this traverse on boundaries (where we been, on the tail, and when we enter our territory) //End this traverse on boundaries (where we been, on the tail, and when we enter our territory)
if ((been[r] && been[r][c]) || onTail(coord) || grid[r][c] === data.player) if (been.get(r, c) || onTail(coord) || grid.get(r, c) === data.player)
continue; continue;
if (!been[r]) been.set(r, c, true);
been[r] = new Array(grid[r].length);
been[r][c] = true;
if (surrounded) if (surrounded)
filled.push(coord); filled.push(coord);
@ -282,7 +284,7 @@ var Tail = (function() {
while (!filled.isEmpty()) while (!filled.isEmpty())
{ {
coord = filled.pop(); coord = filled.pop();
grid[coord[0]][coord[1]] = data.player; grid.set(coord[0], coord[1], data.player);
} }
} }
@ -302,24 +304,23 @@ this.Player = (function() {
var SPEED = 5; var SPEED = 5;
var SHADOW_OFFSET = 10; var SHADOW_OFFSET = 10;
function Player(socket, grid, num) { function Player(socket, grid, num, row, col) {
var data = {}; var data = {};
//TODO: load player data and color. //TODO: load player data and color.
var hue = Math.random(); var hue = Math.random();
var base = hslToRgb(hue, .8, .5); var base = new Color(hue, .8, .5);
this.baseColor = rgbString(base); this.baseColor = base;
this.shadowColor = rgbString(hslToRgb(hue, .8, .2)); this.shadowColor = base.deriveLumination(-.3);
base[3] = .5; this.tailColor = base.deriveLumination(.2).deriveAlpha(.5);
this.tailColor = rgbaString(base);
this.name = 'Player ' + (num + 1); this.name = 'Player ' + (num + 1);
data.grid = grid; data.grid = grid;
data.curHeading = 2; data.curHeading = 2;
data.row = Math.floor(Math.random() * 10) + 10; data.row = row || 0;
data.col = 10;//num; data.col = col || 0;
data.dead = false; data.dead = false;
data.tail = new Tail(this); data.tail = new Tail(this);
@ -350,13 +351,13 @@ this.Player = (function() {
this.tail.render(ctx); this.tail.render(ctx);
//Render player. //Render player.
ctx.fillStyle = this.shadowColor; ctx.fillStyle = this.shadowColor.rgbString();
ctx.fillRect(this.posX, this.posY, CELL_WIDTH, CELL_WIDTH); ctx.fillRect(this.posX, this.posY, CELL_WIDTH, CELL_WIDTH);
var mid = CELL_WIDTH / 2; var mid = CELL_WIDTH / 2;
var grd = ctx.createRadialGradient(this.posX + mid, this.posY + mid - SHADOW_OFFSET, 1, var grd = ctx.createRadialGradient(this.posX + mid, this.posY + mid - SHADOW_OFFSET, 1,
this.posX + mid, this.posY + mid - SHADOW_OFFSET, CELL_WIDTH); this.posX + mid, this.posY + mid - SHADOW_OFFSET, CELL_WIDTH);
grd.addColorStop(0, this.baseColor); grd.addColorStop(0, this.baseColor.rgbString());
grd.addColorStop(1, "white"); grd.addColorStop(1, "white");
ctx.fillStyle = grd; ctx.fillStyle = grd;
ctx.fillRect(this.posX, this.posY - SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH); ctx.fillRect(this.posX, this.posY - SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH);
@ -394,13 +395,13 @@ this.Player = (function() {
data.row = row; data.row = row;
data.col = col; data.col = col;
if (row < 0 || row > data.grid.length || col < 0 || col > data.grid[row].length) if (data.grid.isOutOfBounds(row, col))
{ {
data.dead = true; data.dead = true;
return; return;
} }
if (data.grid[row][col] === this) if (data.grid.get(row, col) === this)
{ {
//Safe zone! //Safe zone!
this.tail.fillTail(data.grid); this.tail.fillTail(data.grid);
@ -423,39 +424,7 @@ this.Player = (function() {
}; };
} }
//http://stackoverflow.com/a/9493060/7344257
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function rgbString(rgb) {
return 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
}
function rgbaString(rgb) {
return 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', ' + rgb[3] + ')';
}
return Player; return Player;
})(); })();

View File

@ -1,4 +1,6 @@
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="color.js"></script>
<script src="Grid.js"></script>
<script src="stack.js"></script> <script src="stack.js"></script>
<script src="player.js"></script> <script src="player.js"></script>
<script src="game.js"></script> <script src="game.js"></script>