javascript minesweeper issue with revealing tiles surrounding a clicked 0 tile - javascript

Clicking on one of the tiles triggers clickerbox().
The issue is that if the user clicks on a tile with b
no surrounding mines it's supposed to reveal them, and if any of those also have no mines it should do the same.
It seems to do some of this and then stop before revealing all the tiles it should. I was wondering if anyone knew why or how to fix this?
screenshot of the game board after a zero is clicked on
The first 5 clicks were made by the user then the rest where triggered by the function.
var width = 0;
var height = 0;
var bombs = 0;
var score = 0;
var boxs = 0;
let feild = new Array(2);
userfeild = new Array;
function selectdificulty() {
feild = new Array(2);
userfeild = new Array;
document.getElementById('board').innerHTML = '';
boxs = 0;
score = 0;
if (document.getElementById('dificulty').value == '4') {
var widthinput = document.createElement('input');
var heightinput = document.createElement('input');
var bombinput = document.createElement('input');
widthinput.placeholder = 'width';
heightinput.placeholder = 'height';
bombinput.placeholder = 'mines';
widthinput.id = 'width';
heightinput.id = 'height';
bombinput.id = 'bombs';
widthinput.setAttribute('type', 'text');
heightinput.setAttribute('type', 'text');
bombinput.setAttribute('type', 'text');
document.getElementById('board').appendChild(widthinput);
document.getElementById('board').appendChild(heightinput);
document.getElementById('board').appendChild(bombinput);
var entercustom = document.createElement('button')
entercustom.id = 'entercustom';
entercustom.innerHTML = 'enter custom values'
document.getElementById('board').appendChild(entercustom);
document.getElementById('entercustom').addEventListener('click', customsize);
} else {
if (document.getElementById('dificulty').value == '1') {
width = 9;
height = 9;
bombs = 10;
} else if (document.getElementById('dificulty').value == '2') {
width = 16;
height = 16;
bombs = 38;
} else if (document.getElementById('dificulty').value == '3') {
width = 32;
height = 16;
bombs = 99;
}
createuserboard(width, height, bombs);
}
}
function customsize() {
var width = document.getElementById('width').value;
var height = document.getElementById('height').value;
var bombs = document.getElementById('bombs').value;
if (!isNaN(width) && !isNaN(height) && !isNaN(bombs)) {
if (width * height <= 601) {
createuserboard(width, height, bombs);
}
}
}
function createuserboard(width, height, bombs) {
document.getElementById('board').innerHTML = '';
for (y = 0; y < height; y++) {
userfeild[y] = new Array;
var row = document.createElement('div');
row.classList = 'row';
row.id = 'row' + y;
document.getElementById('board').appendChild(row);
for (x = 0; x < width; x++) {
var box = document.createElement('button');
box.classList = 'box';
box.id = +y + ',' + x;
document.getElementById('row' + y).appendChild(box);
document.getElementById(y + ',' + x).onclick = clickedbox;
userfeild[y][x] = '/';
}
}
}
function clickedbox() {
var y = this.id.split(',')[0];
var x = this.id.split(',')[1];
if (boxs == 0) {
createmineboard(x, y)
}
autoclick(x, y);
}
function autoclick(x, y) {
userfeild[y][x] = feild[y][x];
boxs = boxs + 1;
document.getElementById(y + ',' + x).innerHTML = feild[y][x];
document.getElementById(y + ',' + x).classList = 'clickedbox';
console.log(userfeild[y][x] + ' | x:' + x + ' | y:' + y);
document.getElementById(y + ',' + x).onclick = '';
if (feild[y][x] == 9) {
//gameover();
} else if (feild[y][x] == 0) {
var check = [
[-1, 0, 1, -1, 1, -1, 0, 1],
[-1, -1, -1, 0, 0, 1, 1, 1]
];
for (i = 0; i < 8; i++) {
var newx = parseInt(x) + parseInt(check[0][i]);
var newy = parseInt(y) + parseInt(check[1][i]);
if (newx >= 0 && newx < width && newy >= 0 && newy < height) {
if (userfeild[newy][newx] == '/') {
autoclick(newx, newy);
}
}
}
}
}
function createmineboard(clickedx, clickedy) {
for (x = 0; x < height; x++) {
feild[x] = new Array;
for (y = 0; y < width; y++) {
feild[x][y] = 0;
}
}
for (bomb = 0; bomb < bombs;) {
var valid = false;
let bombx = 0;
let bomby = 0;
bombx = Math.floor(Math.random() * width);
bomby = Math.floor(Math.random() * height);
if (feild[bomby][bombx] !== 9 && !(bombx == clickedx && bomby == clickedy)) {
valid = true;
feild[bomby][bombx] = 9;
bomb++
}
}
function nearbombs(x, y, w, h, feild) {
var count = 0;
if (y !== 0) {
if (x !== 0) {
if (feild[y - 1][x - 1] == 9) {
count = count + 1
}
} //top left
if (feild[y - 1][x] == 9) {
count = count + 1
} //top
if (x !== w - 1) {
if (feild[y - 1][x + 1] == 9) {
count = count + 1
}
} //top right
}
if (x !== 0) {
if (feild[y][x - 1] == 9) {
count = count + 1
}
} //left
if (x !== w - 1) {
if (feild[y][x + 1] == 9) {
count = count + 1
}
} // right
if (y !== h - 1) {
if (x !== 0) {
if (feild[y + 1][x - 1] == 9) {
count = count + 1
}
} //bottom left
if (feild[y + 1][x] == 9) {
count = count + 1
} //bottom
if (x !== w - 1) {
if (feild[y + 1][x + 1] == 9) {
count = count + 1
}
} //bottom right
}
return count;
}
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (feild[y][x] == 0) {
feild[y][x] = nearbombs(x, y, width, height, feild);
}
}
}
console.log(feild);
}
.board {
display: flex;
flex-direction: column;
height: fit-content;
width: fit-content;
}
.row {
display: flex;
flex-direction: row;
height: fit-content;
width: fit-content;
background-color: aquamarine;
}
.box {
width: 30px;
height: 30px;
background-color: #C0C0C0;
border-radius: 0;
border: 0 solid;
box-shadow: inset 2px 2px #FFFFFF, inset -2px -2px #808080, inset 4px 4px #FFFFFF, inset -4px -4px #808080;
}
.clickedbox {
width: 30px;
height: 30px;
background-color: #C0C0C0;
border-radius: 0;
border: 0 solid;
box-shadow: inset 2px 2px #808080;
}
<select name="dificulty" id="dificulty" onchange="selectdificulty()">
<option value="1">begginer</option>
<option value="2">intermediate</option>
<option value="3">expert</option>
<option value="4">custom</option>
</select>
<script src="windows.js"></script>
<script src="minesweeper.js"></script>
<div class="board" id="board">
</div>

The mistake is hidden in for loop of autoclick function:
for (i = 0; i < 8; i++) { ... }
You have to use var keyword to create local iteration variable. Otherwise the global variable is created and reused by recursive calls.
for (var i = 0; i < 8; i++) { ... }
My favorite game btw.

Related

Would it be faster to plot or to copy reflections in a canvas? [duplicate]

I am making a script that auto generates planets see codepen for example.
But the problem I have is that i want to make it less pixelated and I am having some problems doing that if i make the tiles 70 * 70 and tile size to 10 * 10 pixels it works fine. But i want to have it set to something like tiles 360 * 360 and size to 1 or 2 pixels. But when I try to do that I get maximum call stack error. So I tried to use the requestAnimationFrame but then it take ages to load is there a way to speed up the process?
var tileNum = 0;
var tiles;
var colorsLand;
var colorsWater;
var size = 360;
var tileSize = 2;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
window.onload = function () {
generatePlanet();
}
function generatePlanet() {
tileNum = 0;
tiles = [{ x: 0, y: 0, land: false }];
//Retrive colors
colorsLand = interpolateColors("rgb(" + getColor(true) + ")", "rgb(" + getColor(true) + ")", 6000);
colorsWater = interpolateColors("rgb(" + getColor(false) + ")", "rgb(" + getColor(false) + ")", 6000);
//Creates a array of my tiles and sets either water or land to them and calculates the % of being water/land
for (var i = 0; i < (size * size); i++) {
var currentTile = tiles[tiles.length - 1];
if (currentTile.x <= (size - 1)) {
var isLand = false;
if (currentTile.land == true || tiles.length > size && tiles[tiles.length - size].land == true) {
isLand = (Math.floor(Math.random() * 100) + 1) > 35;
}
else if (currentTile.land == true || tiles.length > size &&
(tiles[tiles.length - 1].land == true ||
tiles[tiles.length - size].land == true)) {
isLand = (Math.floor(Math.random() * 100) + 1) > size;
}
else {
isLand = (Math.floor(Math.random() * 100) + 1) > 99;
}
tiles.push({ x: currentTile.x + 1, y: currentTile.y, land: isLand });
}
else {
tiles.push({ x: 0, y: currentTile.y + 1, land: isLand });
}
}
drawPlanet()
}
//retrive a random color if it's a land tile i want it dark water i want light
function getColor(land) {
while (true) {
var r = Math.floor(Math.random() * 256) + 1
var g = Math.floor(Math.random() * 256) + 1
var b = Math.floor(Math.random() * 256) + 1
var hsp = Math.sqrt(
0.299 * (r * r) +
0.587 * (g * g) +
0.114 * (b * b)
);
//light color
if (hsp > 127.5 && land == false) {
return r + "," + g + "," + b;
}
//dark color
else if (hsp < 127.5 && land == true) {
return r + "," + g + "," + b;
}
}
}
//these 2 functions interpolateColor(s) takes 2 colors and gives me 'steps' colors between
function interpolateColors(color1, color2, steps) {
var stepFactor = 1 / (steps - 1),
interpolatedColorArray = [];
color1 = color1.match(/\d+/g).map(Number);
color2 = color2.match(/\d+/g).map(Number);
for (var i = 0; i < steps; i++) {
interpolatedColorArray.push(interpolateColor(color1, color2, stepFactor * i));
}
return interpolatedColorArray;
}
function interpolateColor(color1, color2, factor) {
if (arguments.length < 3) {
factor = 0.5;
}
var result = color1.slice();
for (var i = 0; i < 3; i++) {
result[i] = Math.round(result[i] + factor * (color2[i] - color1[i]));
}
return result;
};
//retrives a random color for land
function rndLandColor() {
return 'rgb(' + colorsLand[Math.floor(Math.random() * 5999) + 1] + ')';
}
//retrives a random color for water
function rndWaterColor() {
return 'rgb(' + colorsWater[Math.floor(Math.random() * 5999) + 1] + ')';
}
function drawPlanet() {
var RAF;
var i = 0, j = 0;
function animate() {
ctx.beginPath();
//fill in holes in the land that is bigger then 1
var score = 0;
if (tiles[tileNum - (size + 1)] !== undefined && tiles[tileNum + (size + 1)] !== undefined) {
if (tiles[tileNum].land == false) {
score++;
}
if (tiles[tileNum - 1].land == true) {
score++;
}
if (tiles[tileNum + 1].land == true) {
score++;
}
if (tiles[tileNum + (size + 1)].land == true) {
score++;
}
if (tiles[tileNum - (size + 1)].land == true) {
score++;
}
}
if (score >= 3) {
ctx.fillStyle = rndLandColor();
}
//cover single land tiles with water (if water tile is up,down,left and right of this tile)
else if (
tiles[tileNum - (size + 1)] !== undefined &&
tiles[tileNum + (size + 1)] !== undefined &&
tiles[tileNum - 1].land == false &&
tiles[tileNum + 1].land == false &&
tiles[tileNum - (size + 1)].land == false &&
tiles[tileNum + (size + 1)].land == false) {
ctx.fillStyle = rndWaterColor();
}
//cover single water tiles with land (if land tile is up,down,left and right of this tile)
else if (
tiles[tileNum - (size + 1)] !== undefined &&
tiles[tileNum + (size + 1)] !== undefined &&
tiles[tileNum - 1].land == true &&
tiles[tileNum + 1].land == true &&
tiles[tileNum - (size + 1)].land == true &&
tiles[tileNum + (size + 1)].land == true) {
ctx.fillStyle = rndLandColor();
}
//cover tile with land
else if (tiles[tileNum] !== undefined && tiles[tileNum].land == true) {
ctx.fillStyle = rndLandColor();
}
//cover tile with water
else if (tiles[tileNum] !== undefined && tiles[tileNum].land == false) {
ctx.fillStyle = rndWaterColor();
}
tileNum++;
ctx.fill();
ctx.closePath();
ctx.fillRect(tileSize * j, tileSize * i, tileSize, tileSize);
j++;
if (j >= (size + 1)) {
i += 1;
j = 0;
if (i >= (size + 1)) {
cancelAnimationFrame(RAF);
}
}
RAF = requestAnimationFrame(function () {
animate();
});
}
animate();
}
#canvas {
border: 10px solid #000000;
border-radius: 50%;
background-color: aquamarine;
}
.container {
width: 720px;
height: 720px;
position: relative;
}
.gradient {
position: absolute;
height: 730px;
width: 730px;
top: 0;
left: 0;
border-radius: 50%;
opacity: 0.8;
}
<div class="container">
<img class="gradient" src="https://www.mediafire.com/convkey/1f5a/cgu50lw1ehcp4fq6g.jpg" />
<canvas id="canvas" width="710" height="710"></canvas>
</div>
Do not use canvas drawing methods to perform pixel art.
Filling a path is a relatively slow operation, to draw pixels through fillRect(), is almost never the correct way.
Instead one should prefer manipulating an ImageData object directly, and paint it on the canvas only once.
If you need to set up a scale, then use an unscaled ImageBitmap, put it on your context and then upscale it using drawImage.
Here is an updated version of your script, where I did apply some not-so minor improvements like not generating colors for out-of-screen pixels, along with this ImageData manipulation technique.
It now runs fast enough to be launched synchronously. But if you need to improve it even more, note that your getColor seems rather inneficient, but I didn't touch it.
var tileNum = 0;
var tiles;
var colorsLand;
var colorsWater;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var tileSize = 2;
canvas.width = canvas.height = 710;
// 'size' should be your grid size, not the actual pixel size painted on screen
var size = Math.ceil(canvas.width / tileSize);
function generatePlanet() {
tileNum = 0;
tiles = [{
x: 0,
y: 0,
land: false
}];
//Retrive colors
colorsLand = interpolateColors(getColor(true), getColor(true), 6000);
colorsWater = interpolateColors(getColor(false), getColor(false), 6000);
//Creates a array of my tiles and sets either water or land to them and calculates the % of being water/land
for (var i = 0; i < (size * size); i++) {
var currentTile = tiles[tiles.length - 1];
if (currentTile.x <= (size - 1)) {
var isLand = false;
if (currentTile.land == true || tiles.length > size && tiles[tiles.length - size].land == true) {
isLand = (Math.floor(Math.random() * 100) + 1) > 35;
} else if (currentTile.land == true || tiles.length > size &&
(tiles[tiles.length - 1].land == true ||
tiles[tiles.length - size].land == true)) {
isLand = (Math.floor(Math.random() * 100) + 1) > size;
} else {
isLand = (Math.floor(Math.random() * 100) + 1) > 99;
}
tiles.push({
x: currentTile.x + 1,
y: currentTile.y,
land: isLand
});
} else {
tiles.push({
x: 0,
y: currentTile.y + 1,
land: isLand
});
}
}
drawPlanet()
}
//retrive a random color if it's a land tile i want it dark water i want light
function getColor(land) {
while (true) {
var r = Math.floor(Math.random() * 256) + 1
var g = Math.floor(Math.random() * 256) + 1
var b = Math.floor(Math.random() * 256) + 1
var hsp = Math.sqrt(
0.299 * (r * r) +
0.587 * (g * g) +
0.114 * (b * b)
);
//light color
if (hsp > 127.5 && land == false) {
return [r,g,b];
}
//dark color
else if (hsp < 127.5 && land == true) {
return [r,g,b];
}
}
}
//these 2 functions interpolateColor(s) takes 2 colors and gives me 'steps' colors between
function interpolateColors(color1, color2, steps) {
var stepFactor = 1 / (steps - 1),
interpolatedColorArray = [];
for (var i = 0; i < steps; i++) {
interpolatedColorArray.push(toUint32AARRGGBB(interpolateColor(color1, color2, stepFactor * i)));
}
return interpolatedColorArray;
}
function toUint32AARRGGBB(arr) {
return Number('0xFF' + arr.map(toHexString2).join(''))
}
function toHexString2(val) {
return val.toString(16)
.padStart(2, '0'); // padStart may need a polyfill
}
function interpolateColor(color1, color2, factor) {
if (arguments.length < 3) {
factor = 0.5;
}
var result = color1.slice();
for (var i = 0; i < 3; i++) {
result[i] = Math.round(result[i] + factor * (color2[i] - color1[i]));
}
return result;
};
//retrives a random color for land
function rndLandColor() {
return colorsLand[Math.floor(Math.random() * 5999) + 1];
}
//retrives a random color for water
function rndWaterColor() {
return colorsWater[Math.floor(Math.random() * 5999) + 1];
}
// now drawing synchronously:
function drawPlanet() {
var gridsize = size;
var rad = gridsize / 2;
// generate an ImageData, the size of our pixel grid
var imgData = new ImageData(gridsize, gridsize);
// work directly on Uint32 values (0xAARRGGBB on LittleEndian)
var data = new Uint32Array(imgData.data.buffer);
var score, y, x;
for (y = 0; y < gridsize; y++) {
for (x = 0; x < gridsize; x++) {
score = 0;
// if we are outside of the inner area
if (Math.hypot(rad - x, rad - y) > rad + 2) {
tileNum++;
continue;
}
//fill in holes in the land that is bigger then 1
if (tiles[tileNum - (gridsize + 1)] !== undefined && tiles[tileNum + (size + 1)] !== undefined) {
if (tiles[tileNum].land == false) {
score++;
}
if (tiles[tileNum - 1].land == true) {
score++;
}
if (tiles[tileNum + 1].land == true) {
score++;
}
if (tiles[tileNum + (gridsize + 1)].land == true) {
score++;
}
if (tiles[tileNum - (gridsize + 1)].land == true) {
score++;
}
}
if (score >= 3) {
color = rndLandColor();
}
//cover single land tiles with water (if water tile is up,down,left and right of this tile)
else if (
tiles[tileNum - (gridsize + 1)] !== undefined &&
tiles[tileNum + (gridsize + 1)] !== undefined &&
tiles[tileNum - 1].land == false &&
tiles[tileNum + 1].land == false &&
tiles[tileNum - (gridsize + 1)].land == false &&
tiles[tileNum + (gridsize + 1)].land == false) {
color = rndWaterColor();
}
//cover single water tiles with land (if land tile is up,down,left and right of this tile)
else if (
tiles[tileNum - (gridsize + 1)] !== undefined &&
tiles[tileNum + (gridsize + 1)] !== undefined &&
tiles[tileNum - 1].land == true &&
tiles[tileNum + 1].land == true &&
tiles[tileNum - (gridsize + 1)].land == true &&
tiles[tileNum + (gridsize + 1)].land == true) {
color = rndLandColor();
}
//cover tile with land
else if (tiles[tileNum] !== undefined && tiles[tileNum].land == true) {
color = rndLandColor();
}
//cover tile with water
else if (tiles[tileNum] !== undefined && tiles[tileNum].land == false) {
color = rndWaterColor();
}
tileNum++;
data[(y * gridsize) + x] = color;
}
}
// all done populating the ImageData
// put it on the context at scale(1,1)
ctx.putImageData(imgData, 0, 0);
// remove antialiasing
ctx.imageSmoothingEnabled = false;
// up-scale
ctx.scale(tileSize, tileSize);
// draw the canvas over itself
ctx.drawImage(ctx.canvas, 0, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
generatePlanet();
#canvas {
border: 10px solid #000000;
border-radius: 50%;
background-color: aquamarine;
}
.container {
width: 720px;
height: 720px;
position: relative;
}
.gradient {
position: absolute;
height: 730px;
width: 730px;
top: 0;
left: 0;
border-radius: 50%;
opacity: 0.8;
}
<div class="container">
<img class="gradient" src="https://www.mediafire.com/convkey/1f5a/cgu50lw1ehcp4fq6g.jpg" />
<canvas id="canvas" width="710" height="710"></canvas>
</div>
Now, if I were in your position, I think I would even start looking somewhere else completely. For what you want to do, it seems that some noise generator could be more efficient with a more realistic output.
There is one such noise generator available in SVG filters, and hence accessible to Canvas2D API, however I have to admit that controlling it is not that easy.
But if you wish to take a look at it, here is a rough playground:
const controls = new Set();
function randColor() {
return '#' + (Math.floor((Math.random()*0xFFFFFF)))
.toString(16)
.padStart(6, 0);
}
function makeInput(type, options) {
return Object.assign(document.createElement('input'), {type}, options);
}
class Control {
constructor() {
this.color = makeInput('color', {value: randColor()});
this.freq = makeInput('range', {min: 0.0001, max:1, step: 0.0001, value: Math.random() / 20});
this.numOctaves = makeInput('range', {min: 1, max:10, step: 1, value: 7});
this.opacity = makeInput('range', {min:0.01, max:1, step: 0.001, value:1});
this.seed = Math.random() * 1000;
const remover = document.createElement('span');
remover.textContent = 'x';
remover.classList.add('remover');
const container = document.createElement('div');
container.classList.add('control');
container.append(
"color: ", this.color,
"baseFrequency: ", this.freq,
"numOctaves: ", this.numOctaves,
"opacity", this.opacity,
remover
);
document.querySelector('.controls').append(container);
remover.onclick = e => {
container.remove();
controls.delete(this);
draw();
};
this.color.oninput = this.freq.oninput = this.numOctaves.oninput = this.opacity.oninput = draw;
}
}
for(let i=0; i<3; i++) {
controls.add(new Control());
}
const main = c.getContext('2d');
const ctx = c.cloneNode().getContext('2d');
main.arc(c.width/2, c.height/2, Math.min(c.width, c.height)/2,0,Math.PI*2);
draw();
add_control.onclick = e => {
controls.add(new Control());
draw();
}
function draw() {
main.globalCompositeOperation = 'source-over';
main.clearRect(0,0,c.width,c.height);
controls.forEach(control => {
ctx.globalCompositeOperation = 'source-over';
ctx.filter = "none";
ctx.clearRect(0,0,c.width,c.height);
// update <filter>
turb.setAttribute('seed', control.seed);
turb.setAttribute('baseFrequency', control.freq.value);
turb.setAttribute('numOctaves', control.numOctaves.value);
// draw black and transp
ctx.filter = "url(#myFilter)"
ctx.fillRect(0,0,c.width, c.width);
// do the composition with solid color
ctx.filter = "none"
ctx.fillStyle = control.color.value;
ctx.globalCompositeOperation = 'source-in'
ctx.fillRect(0,0,c.width, c.width);
main.globalAlpha = control.opacity.value;
// draw on visible context
main.drawImage(ctx.canvas, 0,0)
main.globalAlpha = 1;
});
// cut-out as a circle
main.globalCompositeOperation = 'destination-in';
main.fill()
}
.control {
display: inline-block;
border: 1px solid;
padding: 6px;
position: relative
}
.control input {
display: block;
}
.control span {
position: absolute;
top: 6px;
right: 6px;
cursor: pointer;
}
#canvas {
border: 10px solid #000000;
border-radius: 50%;
background-color: aquamarine;
}
.container {
width: 360px;
height: 360px;
position: relative;
}
.gradient {
position: absolute;
height: 360px;
width: 360px;
top: 0;
left: 0;
border-radius: 50%;
opacity: 0.8;
}
<div class="controls">
<button id="add_control">add new layer</button><br>
</div>
<div class="container">
<canvas id="c" width="360" height="360"></canvas>
<svg>
<filter id="myFilter">
<feTurbulence type="fractalNoise" baseFrequency="0.045"
id="turb" result="turb"/>
<feComponentTransfer in="turb" result="contrast">
<feFuncR type="linear" slope="1.6" intercept="-0.15"/>
<feFuncG type="linear" slope="1.6" intercept="-0.15"/>
<feFuncB type="linear" slope="1.6" intercept="-0.15"/>
</feComponentTransfer>
<feColorMatrix in="contrast"
type="luminanceToAlpha" result="alpha"/>
</filter>
</svg>
<img class="gradient" src="https://www.mediafire.com/convkey/1f5a/cgu50lw1ehcp4fq6g.jpg" />
</div>

How to make points in Javascript

I need help to make points every time the snake eats a piece of food. I am coding in Brackets.
I've watched The Coding Train and I was thinking the problem was that he was using p5.js when I was using Brackets. Could somebody please answer my question. Thank you! The link for the video that I have watched is: https://www.youtube.com/watch?v=AaGK-fj-BAM.
function Snake() { <- Javascript
this.x = 0;
this.y = 0;
this.xspeed = 1;
this.yspeed = 0;
this.total = 0;
this.tail = [];
this.eat = function(pos) {
var d = dist(this.x, this.y, pos.x, pos.y);
if (d < 1) {
this.total++;
return true;
} else {
return false;
}
}
this.dir = function(x, y) {
this.xspeed = x;
this.yspeed = y;
}
this.death = function() {
for (var i = 0; i < this.tail.length; i++) {
var pos = this.tail[i];
var d = dist(this.x, this.y, pos.x, pos.y);
if (d < 1) {
alert(' BAD LUCK ');
this.total = 0;
this.tail = [];
point.counterReset;
}
}
}
this.update = function() {
for (var i = 0; i < this.tail.length - 1; i++) {
this.tail[i] = this.tail[i + 1];
}
if (this.total >= 1) {
this.tail[this.total - 1] = createVector(this.x, this.y);
}
this.x = this.x + this.xspeed * scl;
this.y = this.y + this.yspeed * scl;
this.x = constrain(this.x, 0, width - scl);
this.y = constrain(this.y, 0, height - scl);
}
this.show = function() {
fill(89, 152, 47);
for (var i = 0; i < this.tail.length; i++) {
rect(this.tail[i].x, this.tail[i].y, scl, scl);
}
rect(this.x, this.y, scl, scl);
}
}
var s;
var point = 1;
var scl = 20;
var food;
var fool;
function setup() {
createCanvas(600,600);
s = new Snake();
frameRate(10);
pickLocation();
}
function pickLocation() {
var cols = floor(width / scl);
var rows = floor(height / scl);
food = createVector(floor(random(cols)), floor(random(rows)));
food.mult(scl);
fool = createVector(floor(random(cols)), floor(random(rows)));
fool.mult(scl);
}
function draw() {
background(120,6,6);
if (s.eat(food)) {
pickLocation();
point.total++;
if (Math.random() * 100 < 45) {
let name = prompt("What is 2 ÷ 4?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 45) {
let name = prompt("What is the intergration of X²?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50) {
let name = prompt("What is Mass x Acceleration?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50) {
let name = prompt("How many elements are in the Periodic Table?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50 ) {
let name = prompt("What is 336 ÷ 84?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50 ) {
let name = prompt("(VERY RARE)Who was the captain of The First Fleet?");
alert ("Yes it is, " + name);
}
}
if (s.eat(fool)) {
pickLocation();
point.total++;
if (Math.random() * 100 < 45) {
let name = prompt("What is 2 ÷ 4?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 45) {
let name = prompt("What is the intergration of X²?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50) {
let name = prompt("What is Mass x Acceleration?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50 ) {
let name = prompt("What is 336 ÷ 84?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50) {
let name = prompt("How many elements are in the Periodic Table?");
alert ("Yes it is, " + name);
}
else if (Math.random() * 100 < 50 ) {
let name = prompt("(VERY RARE)Who was the captain of The First Fleet?");
alert ("Yes it is, " + name);
}
}
s.death();
s.update();
s.show();
fill(255, 0, 100);
rect(food.x, food.y, scl, scl);
fill(255, 0, 100);
rect(fool.x, fool.y, scl, scl);
}
document.write("Score: " + point);
point.update();
point.show();
function keyPressed() {
if (keyCode === UP_ARROW) {
s.dir(0, -1);
} else if (keyCode === DOWN_ARROW) {
s.dir(0, 1);
} else if (keyCode === RIGHT_ARROW) {
s.dir(1, 0);
} else if (keyCode === LEFT_ARROW) {
s.dir(-1, 0)
}
}
html, body { <- css
margin: 0;
padding: 0;
background-color: #40E0D0;
}
canvas {
display: block;
}
<!DOCTYPE html> <-html
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
<title>Snake Game</title>
</head>
<body>
<script src="sketch.js"></script>
<script src="snake.js"></script>
<script src="skin.js"></script>
</body>
</html>
I am counting points when snake eats apple with that code
if (cell.x === apple.x && cell.y === apple.y) snake.maxCells++;
And my snake-game:
var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
var grid = 16;
var count = 0;
var snake = {
x: 160,
y: 160,
dx: grid,
dy: 0,
cells: [], //tail
maxCells: 4
};
var apple = {
x: 320,
y: 320
};
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function loop() {
requestAnimationFrame(loop);
if (++count < 1) {
return;
}
count = -15; // speed
context.clearRect(0,0,canvas.width,canvas.height);
snake.x += snake.dx;
snake.y += snake.dy;
if (snake.x < 0) {
snake.x = canvas.width - grid;
}
else if (snake.x >= canvas.width) {
snake.x = 0;
}
if (snake.y < 0) {
snake.y = canvas.height - grid;
}
else if (snake.y >= canvas.height) {
snake.y = 0;
}
snake.cells.unshift({x: snake.x, y: snake.y});
if (snake.cells.length > snake.maxCells) {
snake.cells.pop();
}
context.fillStyle = 'red';
context.fillRect(apple.x, apple.y, grid-1, grid-1);
context.fillStyle = 'green';
snake.cells.forEach(function(cell, index) {
context.fillRect(cell.x, cell.y, grid-1, grid-1);
if (cell.x === apple.x && cell.y === apple.y) { // if our snake eat red apple, so you count points
// and adding one green cell to snake
snake.maxCells++;
// Drawing new apple
apple.x = getRandomInt(0, 25) * grid;
apple.y = getRandomInt(0, 25) * grid;
}
for (var i = index + 1; i < snake.cells.length; i++) {
if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
snake.x = 160;
snake.y = 160;
snake.cells = [];
snake.maxCells = 4;
snake.dx = grid;
snake.dy = 0;
apple.x = getRandomInt(0, 25) * grid;
apple.y = getRandomInt(0, 25) * grid;
}
}
});
}
document.addEventListener('keydown', function(e) {
if (e.which === 37 && snake.dx === 0) {
snake.dx = -grid;
snake.dy = 0;
}
else if (e.which === 38 && snake.dy === 0) {
snake.dy = -grid;
snake.dx = 0;
}
else if (e.which === 39 && snake.dx === 0) {
snake.dx = grid;
snake.dy = 0;
}
else if (e.which === 40 && snake.dy === 0) {
snake.dy = grid;
snake.dx = 0;
}
});
requestAnimationFrame(loop);
html, body {
height: 100%;
margin: 0;
}
body {
background: black;
display: flex;
align-items: center;
justify-content: center;
}
canvas {
border: 1px solid white;
}
<!DOCTYPE html>
<html>
<body>
<canvas width="400" height="400" id="game"></canvas>
</body>
</html>

How could I achieve the effect of "lava" leaving traces

After some time of "development" of the JavaScript game, I've came to a great idea, or so it seemed / sounded.
I was thinking of creating an entity which would represent lava. That lava would move in a specific direction, using:
function Lava(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
if(ch == '-') {
this.speed = new Vector(3, 0)
}
}
where is var acrotchar = {"-": Lava};.
The whole code can be seen here or below:
var LEVELS = [
[" x x",
" xx x",
" xxx x x",
" xx!xx x ox",
" x!!!x x xx",
" xx!xx x x",
" x xvx x x",
" x xx x",
" x x x",
" xx x x",
" x x xx",
" x x",
" x # xxxxx o x",
" xxxxxx xxxxxxxxx xxxxxxxxxxxxx",
" x x ",
" x!!!!!x ",
" x!!!!!x ",
" xxxxxxx ",
"--------------------------------------"]
];
//set variables (HP and EXP)
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);
//set the playzone
function Vector(x, y) {
this.x = x; this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
Vector.prototype.times = function(scale) {
return new Vector(this.x * scale, this.y * scale);
};
// Note: uppercase words are used that means constructor are values
var actorchars = {
"#": Player,
"o": Coin,
"=": Lava,
"|": Lava,
"v": Lava,
"#": Lava,
"-": Flood
};
function Player(pos) {
this.pos = pos.plus(new Vector(0, -.5));
this.size = new Vector(.5, 1);
this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";
function Lava(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
if (ch === "=")
this.speed = new Vector(2, 0);
else if (ch === '|')
this.speed = new Vector(0, 2);
else if (ch === 'v'){
this.speed = new Vector(0, 5);
this.repeatPos = pos;
} else if (ch === '#')
this.speed = new Vector(0, 10);
}
Lava.prototype.type = "lava"
function Flood(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
if (ch === '-') {
this.speed = new Vector(0, 1);
this.repeatPos = pos; //will be removed in the future
}
}
Flood.prototype.type = "flood"
//Lava.prototype.type = "Lava";
// function Wall(pos, ch) {
// this.pos = pos;
// this.size = new Vector(1, 1);
// if (ch === "z")
// this.speed = new Vector(0, 1);
// }
// Wall.prototype.type = "wall"
function Coin(pos) {
this.basePos = this.pos = pos;
this.size = new Vector(.6, .6);
// take a look back
this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";
Level.prototype.isFinished = function() {
return this.status !== null && this.finishDelay < 0;
};
function Level(plan) {
this.width = plan[0].length;
this.height = plan.length;
this.grid = [];
this.actors = [];
for (var y = 0; y < this.height; y++) {
var line = plan[y], gridLine = [];
for (var x = 0; x < this.width; x++) {
var ch = line[x], fieldType = null;
var Actor = actorchars[ch];
if (Actor)
this.actors.push(new Actor(new Vector(x, y), ch));
else if (ch === "x")
fieldType = "wall";
else if (ch === "z")
fieldType = "wall";
else if (ch === "!")
fieldType = "lava";
else if (ch === "|")
fieldType = "lava";
else if (ch === "=")
fieldType = "lava";
else if (ch === "#")
fieldType = "lava";
else if (ch === "-")
fieldType = "flood";
else if (ch === "v"){
fieldType = "lava";
console.log(fieldType);
}
gridLine.push(fieldType);
}
this.grid.push(gridLine);
}
this.player = this.actors.filter(function(actor) {
return actor.type === "player";
})[0];
this.status = this.finishDelay = null;
}
function element(name, className) {
var elem = document.createElement(name);
if(className) elem.className = className;
return elem;
}
function DOMDisplay(parent, level) {
this.wrap = parent.appendChild(element("div", "game"));
this.level = level;
this.wrap.appendChild(this.drawBackground());
this.actorLayer = null;
this.drawFrame();
}
var scale = 15;
DOMDisplay.prototype.drawBackground = function() {
var table = element("table", "background");
table.style.width = this.level.width * scale + "px";
table.style.height = this.level.height * scale + "px";
this.level.grid.forEach(function(row) {
var rowElement = table.appendChild(element("tr"));
rowElement.style.height = scale + "px";
row.forEach(function(type) {
rowElement.appendChild(element("td", type));
});
});
return table;
};
DOMDisplay.prototype.drawActors = function() {
var wrap = element("div");
this.level.actors.forEach(function(actor) {
var rect = wrap.appendChild(element("div", "actor " + actor.type));
rect.style.width = actor.size.x * scale + "px";
rect.style.height = actor.size.y * scale + "px";
rect.style.left = actor.pos.x * scale + "px";
rect.style.top = actor.pos.y * scale + "px";
});
return wrap;
};
DOMDisplay.prototype.drawFrame = function() {
if (this.actorLayer)
this.wrap.removeChild(this.actorLayer);
this.actorLayer = this.wrap.appendChild(this.drawActors());
this.wrap.className = "game " + (this.level.status || "");
this.scrollPlayerIntoView();
};
// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
var width = this.wrap.clientWidth;
var height = this.wrap.clientHeight;
var margin = width / 3;
// The viewport
var left = this.wrap.scrollLeft, right = left + width;
var top = this.wrap.scrollTop, bottom = top + height;
var player = this.level.player;
var center = player.pos.plus(player.size.times(0.5))
.times(scale);
if (center.x < left + margin)
this.wrap.scrollLeft = center.x - margin;
else if (center.x > right - margin)
this.wrap.scrollLeft = center.x + margin - width;
if (center.y < top + margin)
this.wrap.scrollTop = center.y - margin;
else if (center.y > bottom - margin)
this.wrap.scrollTop = center.y + margin - height;
};
DOMDisplay.prototype.clear = function() {
this.wrap.parentNode.removeChild(this.wrap);
};
Level.prototype.obstacleAt = function(pos, size) {
var xStart = Math.floor(pos.x);
var xEnd = Math.ceil(pos.x + size.x);
var yStart = Math.floor(pos.y);
var yEnd = Math.ceil(pos.y + size.y);
if (xStart < 0 || xEnd > this.width || yStart < 0)
return "wall";
if (yEnd > this.height)
return "lava", "flood";
for (var y = yStart; y < yEnd; y++) {
for (var x = xStart; x < xEnd; x++) {
var fieldType = this.grid[y][x];
if (fieldType) return fieldType;
}
}
};
Level.prototype.actorAt = function(actor) {
for (var i = 0; i < this.actors.length; i++) {
var other = this.actors[i];
if (other != actor &&
actor.pos.x + actor.size.x > other.pos.x &&
actor.pos.x < other.pos.x + other.size.x &&
actor.pos.y + actor.size.y > other.pos.y &&
actor.pos.y < other.pos.y + other.size.y)
return other;
}
};
var maxStep = 0.05;
Level.prototype.animate = function(step, keys) {
if (this.status !== null)
this.finishDelay -= step;
while (step > 0) {
var thisStep = Math.min(step, maxStep);
this.actors.forEach(function(actor) {
actor.act(thisStep, this, keys);
}, this);
step -= thisStep;
}
};
Lava.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.size))
this.pos = newPos;
else if (this.repeatPos)
this.pos = this.repeatPos;
else
this.speed = this.speed.times(-1);
};
Flood.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.size))
this.pos = newPos;
else
this.speed = this.speed.times(-1);
};
var wobbleSpeed = 8, wobbleDist = 0.07;
Coin.prototype.act = function(step) {
this.wobble += step * wobbleSpeed;
var wobblePos = Math.sin(this.wobble) * wobbleDist;
this.pos = this.basePos.plus(new Vector(0, wobblePos));
};
var playerXSpeed = 10;
Player.prototype.moveX = function(step, level, keys) {
this.speed.x = 0;
if (keys.left) this.speed.x -= playerXSpeed;
if (keys.right) this.speed.x += playerXSpeed;
var motion = new Vector(this.speed.x * step, 0);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle)
level.playerTouched(obstacle);
else
this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
Player.prototype.moveY = function(step, level, keys) {
this.speed.y += step * gravity;
var motion = new Vector(0, this.speed.y * step);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle) {
level.playerTouched(obstacle);
if (keys.up && this.speed.y > 0)
this.speed.y = -jumpSpeed;
else
this.speed.y = 0;
} else {
this.pos = newPos;
}
};
Player.prototype.act = function(step, level, keys) {
this.moveX(step, level, keys);
this.moveY(step, level, keys);
var otherActor = level.actorAt(this);
if (otherActor)
level.playerTouched(otherActor.type, otherActor);
// Losing animation
if (level.status == "lost") {
this.pos.y += step;
this.size.y -= step;
}
};
Level.prototype.playerTouched = function(type, actor) {
//if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
if (type == "lava" && this.status === null || type == "flood" && this.status === null) {
this.status = "lost";
life -= 1;
console.log(life);
expo = 0;
document.getElementById("expo").innerHTML = ("Points: " + expo);
document.getElementById("life").innerHTML = ("Lives left: " + life);
if(life < 0) {
sessionStorage.setItem("reloading", "true");
document.location.reload();
}
this.finishDelay = 1;
} else if (type == "coin") {
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
this.actors = this.actors.filter(function(other) {
return other != actor;
});
if (!this.actors.some(function(actor) {
return actor.type == "coin";
})) {
life += 1;
document.getElementById("life").innerHTML = ("Lives left: " + life);
this.status = "won";
this.finishDelay = 1;
}
}
};
var arrowCodes = {37: "left", 38: "up", 39: "right"};
function trackKeys(codes) {
var pressed = Object.create(null);
function handler(event) {
if (codes.hasOwnProperty(event.keyCode)) {
var down = event.type == "keydown";
pressed[codes[event.keyCode]] = down;
event.preventDefault();
}
}
addEventListener("keydown", handler);
addEventListener("keyup", handler);
return pressed;
}
function runAnimation(frameFunc) {
var lastTime = null;
function frame(time) {
var stop = false;
if (lastTime !== null) {
var timeStep = Math.min(time - lastTime, 100) / 1000;
stop = frameFunc(timeStep) === false;
}
lastTime = time;
if (!stop)
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
var arrows = trackKeys(arrowCodes);
function runLevel(level, Display, andThen) {
var display = new Display(document.body, level);
runAnimation(function(step) {
level.animate(step, arrows);
display.drawFrame(step);
if (level.isFinished()) {
display.clear();
if (andThen)
andThen(level.status);
return false;
}
});
}
var lives = function() {
ctx.font = "20px Courier";
ctx.fontFamily = "monospace";
ctx.fillStyle = "#666";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Lives left: " + life, 10, 10);
};
function runGame(plans, Display) {
function startLevel(n) {
runLevel(new Level(plans[n]), Display, function(status) {
if (status == "lost") {
startLevel(n);
} else if (n < plans.length - 1)
startLevel(n + 1);
else
alert("You win!");
});
}
startLevel(0);
}
runGame(LEVELS, DOMDisplay);
body {
background: #222;
}
h2 {
color: #666;
font-family: monospace;
text-align: center;
}
.background {
table-layout: fixed;
border-spacing: 0;
}
.background td {
padding: 0;
}
.lava, .actor {
background: #e55;
}
.wall {
background: #444;
border: solid 3px #333;
box-sizing: content-box;
}
.actor {
position: absolute;
}
.coin {
background: #e2e838;
border-radius: 50%;
}
.player {
background: #335699;
box-shadow: none;
}
.lost .player {
background: #a04040;
}
.won .player {
background: green;
}
.game {
position: relative;
overflow: hidden;
}
#life, #expo {
font-size: 16px;
font-family: monospace;
color: #666;
text-align: left;
baseline: top;
margin-left: 30px;
font-weight: bold;
}
input {
margin-left: 30px;
}
<h2>Simple JavaScript Game</h2>
<div id="life"></div>
<div id="expo"></div>
What I would like to achieve is the following:
Lava (red colored blocks, in code presented as "-", tagged as "flood") that would:
move in a specific direction (is already done, more or less),
leaves "trace" behind it's movement, (technically speaking, the lava level in this case would seem as it is rising up),
"flood" is able to go through the objects (optional, as I have to check the whole code carefully on my own to see where did I give the entity the "collisions")
I would really appreciate any help, especially with the lava rising effect.
Everything did not fit in one post, so here is the rest:
To make the lava rise through the objects, just reverse the speed and remove the obstacleAt if statement. To make the lava appear like it is behind objects, just add a css class lava-flood and set the z-index to something like -1. If you want the lava to rise in front of the objects, just remove the z-index property from the lava-flood class in CSS.
Full code - lava rising behind objects
var LEVELS = [
[" x x",
" xx x",
" xxx x x",
" xx!xx x ox",
" x!!!x x xx",
" xx!xx x x",
" x xvx x x",
" x xx x",
" x x x",
" xx x x",
" x x xx",
" x x",
" x # xxxxx o x",
" xxxxxx xxxxxxxxx xxxxxxxxxxxxx",
" x x ",
" x!!!!!x ",
" x!!!!!x ",
" xxxxxxx ",
"--------------------------------------"]
];
//set variables (HP and EXP)
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);
//set the playzone
function Vector(x, y) {
this.x = x; this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
Vector.prototype.times = function(scale) {
return new Vector(this.x * scale, this.y * scale);
};
// Note: uppercase words are used that means constructor are values
var actorchars = {
"#": Player,
"o": Coin,
"=": Lava,
"|": Lava,
"v": Lava,
"#": Lava,
"-": LavaFlood
};
function Player(pos) {
this.pos = pos.plus(new Vector(0, -.5));
this.size = new Vector(.5, 1);
this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";
function Lava(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
if (ch === "=")
this.speed = new Vector(2, 0);
else if (ch === '|')
this.speed = new Vector(0, 2);
else if (ch === 'v'){
this.speed = new Vector(0, 5);
this.repeatPos = pos;
} else if (ch === '#')
this.speed = new Vector(0, 10);
}
Lava.prototype.type = "lava"
function LavaFlood(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
this.trueSize = new Vector(1, 1);
if (ch === '-') {
this.speed = new Vector(0, -1);
this.repeatPos = pos; //will be removed in the future
}
}
LavaFlood.prototype.type = "lava-flood"
//Lava.prototype.type = "Lava";
// function Wall(pos, ch) {
// this.pos = pos;
// this.size = new Vector(1, 1);
// if (ch === "z")
// this.speed = new Vector(0, 1);
// }
// Wall.prototype.type = "wall"
function Coin(pos) {
this.basePos = this.pos = pos;
this.size = new Vector(.6, .6);
// take a look back
this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";
Level.prototype.isFinished = function() {
return this.status !== null && this.finishDelay < 0;
};
function Level(plan) {
this.width = plan[0].length;
this.height = plan.length;
this.grid = [];
this.actors = [];
for (var y = 0; y < this.height; y++) {
var line = plan[y], gridLine = [];
for (var x = 0; x < this.width; x++) {
var ch = line[x], fieldType = null;
var Actor = actorchars[ch];
if (Actor)
this.actors.push(new Actor(new Vector(x, y), ch));
else if (ch === "x")
fieldType = "wall";
else if (ch === "z")
fieldType = "wall";
else if (ch === "!")
fieldType = "lava";
else if (ch === "|")
fieldType = "lava";
else if (ch === "=")
fieldType = "lava";
else if (ch === "#")
fieldType = "lava";
else if (ch === "-")
fieldType = "lava-flood";
else if (ch === "v"){
fieldType = "lava";
console.log(fieldType);
}
gridLine.push(fieldType);
}
this.grid.push(gridLine);
}
this.player = this.actors.filter(function(actor) {
return actor.type === "player";
})[0];
this.status = this.finishDelay = null;
}
function element(name, className) {
var elem = document.createElement(name);
if(className) elem.className = className;
return elem;
}
function DOMDisplay(parent, level) {
this.wrap = parent.appendChild(element("div", "game"));
this.level = level;
this.wrap.appendChild(this.drawBackground());
this.actorLayer = null;
this.drawFrame();
}
var scale = 15;
DOMDisplay.prototype.drawBackground = function() {
var table = element("table", "background");
table.style.width = this.level.width * scale + "px";
table.style.height = this.level.height * scale + "px";
this.level.grid.forEach(function(row) {
var rowElement = table.appendChild(element("tr"));
rowElement.style.height = scale + "px";
row.forEach(function(type) {
rowElement.appendChild(element("td", type));
});
});
return table;
};
DOMDisplay.prototype.drawActors = function() {
var wrap = element("div");
this.level.actors.forEach(function(actor) {
var rect = wrap.appendChild(element("div", "actor " + actor.type));
rect.style.width = actor.size.x * scale + "px";
rect.style.height = actor.size.y * scale + "px";
rect.style.left = actor.pos.x * scale + "px";
rect.style.top = actor.pos.y * scale + "px";
});
return wrap;
};
DOMDisplay.prototype.drawFrame = function() {
if (this.actorLayer)
this.wrap.removeChild(this.actorLayer);
this.actorLayer = this.wrap.appendChild(this.drawActors());
this.wrap.className = "game " + (this.level.status || "");
this.scrollPlayerIntoView();
};
// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
var width = this.wrap.clientWidth;
var height = this.wrap.clientHeight;
var margin = width / 3;
// The viewport
var left = this.wrap.scrollLeft, right = left + width;
var top = this.wrap.scrollTop, bottom = top + height;
var player = this.level.player;
var center = player.pos.plus(player.size.times(0.5))
.times(scale);
if (center.x < left + margin)
this.wrap.scrollLeft = center.x - margin;
else if (center.x > right - margin)
this.wrap.scrollLeft = center.x + margin - width;
if (center.y < top + margin)
this.wrap.scrollTop = center.y - margin;
else if (center.y > bottom - margin)
this.wrap.scrollTop = center.y + margin - height;
};
DOMDisplay.prototype.clear = function() {
this.wrap.parentNode.removeChild(this.wrap);
};
Level.prototype.obstacleAt = function(pos, size) {
var xStart = Math.floor(pos.x);
var xEnd = Math.ceil(pos.x + size.x);
var yStart = Math.floor(pos.y);
var yEnd = Math.ceil(pos.y + size.y);
if (xStart < 0 || xEnd > this.width || yStart < 0)
return "wall";
if (yEnd > this.height)
return "lava", "lava-flood";
for (var y = yStart; y < yEnd; y++) {
for (var x = xStart; x < xEnd; x++) {
var fieldType = this.grid[y][x];
if (fieldType) return fieldType;
}
}
};
Level.prototype.actorAt = function(actor) {
for (var i = 0; i < this.actors.length; i++) {
var other = this.actors[i];
if (other != actor &&
actor.pos.x + actor.size.x > other.pos.x &&
actor.pos.x < other.pos.x + other.size.x &&
actor.pos.y + actor.size.y > other.pos.y &&
actor.pos.y < other.pos.y + other.size.y)
return other;
}
};
var maxStep = 0.05;
Level.prototype.animate = function(step, keys) {
if (this.status !== null)
this.finishDelay -= step;
while (step > 0) {
var thisStep = Math.min(step, maxStep);
this.actors.forEach(function(actor) {
actor.act(thisStep, this, keys);
}, this);
step -= thisStep;
}
};
Lava.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.size))
this.pos = newPos;
else if (this.repeatPos)
this.pos = this.repeatPos;
else
this.speed = this.speed.times(-1);
};
LavaFlood.prototype.act = function(step, level) {
this.pos = this.pos.plus(this.speed.times(step));
this.size = this.size.plus(this.speed.times(-step));
};
var wobbleSpeed = 8, wobbleDist = 0.07;
Coin.prototype.act = function(step) {
this.wobble += step * wobbleSpeed;
var wobblePos = Math.sin(this.wobble) * wobbleDist;
this.pos = this.basePos.plus(new Vector(0, wobblePos));
};
var playerXSpeed = 10;
Player.prototype.moveX = function(step, level, keys) {
this.speed.x = 0;
if (keys.left) this.speed.x -= playerXSpeed;
if (keys.right) this.speed.x += playerXSpeed;
var motion = new Vector(this.speed.x * step, 0);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle)
level.playerTouched(obstacle);
else
this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
Player.prototype.moveY = function(step, level, keys) {
this.speed.y += step * gravity;
var motion = new Vector(0, this.speed.y * step);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle) {
level.playerTouched(obstacle);
if (keys.up && this.speed.y > 0)
this.speed.y = -jumpSpeed;
else
this.speed.y = 0;
} else {
this.pos = newPos;
}
};
Player.prototype.act = function(step, level, keys) {
this.moveX(step, level, keys);
this.moveY(step, level, keys);
var otherActor = level.actorAt(this);
if (otherActor)
level.playerTouched(otherActor.type, otherActor);
// Losing animation
if (level.status == "lost") {
this.pos.y += step;
this.size.y -= step;
}
};
Level.prototype.playerTouched = function(type, actor) {
//if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
if (type == "lava" && this.status === null || type == "lava-flood" && this.status === null) {
this.status = "lost";
life -= 1;
console.log(life);
expo = 0;
document.getElementById("expo").innerHTML = ("Points: " + expo);
document.getElementById("life").innerHTML = ("Lives left: " + life);
if(life < 0) {
sessionStorage.setItem("reloading", "true");
document.location.reload();
}
this.finishDelay = 1;
} else if (type == "coin") {
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
this.actors = this.actors.filter(function(other) {
return other != actor;
});
if (!this.actors.some(function(actor) {
return actor.type == "coin";
})) {
life += 1;
document.getElementById("life").innerHTML = ("Lives left: " + life);
this.status = "won";
this.finishDelay = 1;
}
}
};
var arrowCodes = {37: "left", 38: "up", 39: "right"};
function trackKeys(codes) {
var pressed = Object.create(null);
function handler(event) {
if (codes.hasOwnProperty(event.keyCode)) {
var down = event.type == "keydown";
pressed[codes[event.keyCode]] = down;
event.preventDefault();
}
}
addEventListener("keydown", handler);
addEventListener("keyup", handler);
return pressed;
}
function runAnimation(frameFunc) {
var lastTime = null;
function frame(time) {
var stop = false;
if (lastTime !== null) {
var timeStep = Math.min(time - lastTime, 100) / 1000;
stop = frameFunc(timeStep) === false;
}
lastTime = time;
if (!stop)
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
var arrows = trackKeys(arrowCodes);
function runLevel(level, Display, andThen) {
var display = new Display(document.body, level);
runAnimation(function(step) {
level.animate(step, arrows);
display.drawFrame(step);
if (level.isFinished()) {
display.clear();
if (andThen)
andThen(level.status);
return false;
}
});
}
var lives = function() {
ctx.font = "20px Courier";
ctx.fontFamily = "monospace";
ctx.fillStyle = "#666";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Lives left: " + life, 10, 10);
};
function runGame(plans, Display) {
function startLevel(n) {
runLevel(new Level(plans[n]), Display, function(status) {
if (status == "lost") {
startLevel(n);
} else if (n < plans.length - 1)
startLevel(n + 1);
else
alert("You win!");
});
}
startLevel(0);
}
runGame(LEVELS, DOMDisplay);
body {
background: #222;
}
h2 {
color: #666;
font-family: monospace;
text-align: center;
}
.background {
table-layout: fixed;
border-spacing: 0;
}
.background td {
padding: 0;
}
.lava, .actor {
background: #e55;
}
.lava-flood {
z-index: -1;
}
.wall {
background: #444;
border: solid 3px #333;
box-sizing: content-box;
}
.actor {
position: absolute;
}
.coin {
background: #e2e838;
border-radius: 50%;
}
.player {
background: #335699;
box-shadow: none;
}
.lost .player {
background: #a04040;
}
.won .player {
background: green;
}
.game {
position: relative;
overflow: hidden;
}
#life, #expo {
font-size: 16px;
font-family: monospace;
color: #666;
text-align: left;
baseline: top;
margin-left: 30px;
font-weight: bold;
}
input {
margin-left: 30px;
}
<h2>Simple JavaScript Game</h2>
<div id="life"></div>
<div id="expo"></div>
If you need clarification or help for anything, please feel free to ask.
Your game is quite well made, but I have a few suggestions:
Please, please don't use prototypes to add functions to Objects. It makes is very difficult for someone not familiar with the code to follow. A better way is to put all the member functions in the same object, so they are grouped together. It makes the code (in my opinion) way easier to read and understand.
For example:
function Flood(pos, ch){
this.pos = pos;
//other assignments
this.act = function(){
//code
}
//other functions
}
Keeping all your functions in one class also makes it easier to find a function rather than hunting through the whole file to find it.
Even though most of the game is already written, it might be worthwhile looking into the new ES6 class syntax, because this is the perfect example for inheritance and object oriented programming. You can have a generic GridElement class that implements the all standard behaviours, and extend and modify only the parts that differ from type to type. This approach is extremely powerful, and makes it very easy to add new types. I would highly recommend reading more on the theory of object oriented programming, because it is a very useful tool, especially in large projects. Daniel Shiffman is an awesome YouTuber, and makes amazing videos about programming. He has an entire series on Object Oriented programming on youtube, and I would suggest watching the video here.
Don't display the elements in the main display function. Instead, let each class have it's own display function that returns a div element. You can call each element's display function in the main display loop. That way, it becomes much easier to add additional behaviour to particular classes. If the Flood object had its own display function, then it could return a noncontinuous trail instead of modifying its size, which is not the ideal way to implement this behaviour.
I am unaware of any place on earth where there are floods of Lava, and so I would suggest that you use more accurate names in your code. When I see Flood, I immediately think of water, and it led to a bit of head scratching for me to figure out that Flood was in fact, rising lava. A better class name could have been LavaFlood, so I have taken the liberty of changing the name of the class Flood to LavaFlood in the code below.
As for the lava rising effect, it was quite simple. All I did was add a trueSize variable to the (Lava)Flood class and change the act function:
(I could not post this as normal code, as the site kept saying that the code was not formatted properly)
LavaFlood.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.trueSize)) {
this.size = this.size.plus(this.speed.times(-step));
this.pos = newPos;
} else
this.speed = this.speed.times(-1);
};
Full code - lava rising:
var LEVELS = [
[" x x",
" xx x",
" xxx x x",
" xx!xx x ox",
" x!!!x x xx",
" xx!xx x x",
" x xvx x x",
" x xx x",
" x x x",
" xx x x",
" x x xx",
" x x",
" x # xxxxx o x",
" xxxxxx xxxxxxxxx xxxxxxxxxxxxx",
" x x ",
" x!!!!!x ",
" x!!!!!x ",
" xxxxxxx ",
"--------------------------------------"]
];
//set variables (HP and EXP)
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);
//set the playzone
function Vector(x, y) {
this.x = x; this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
Vector.prototype.times = function(scale) {
return new Vector(this.x * scale, this.y * scale);
};
// Note: uppercase words are used that means constructor are values
var actorchars = {
"#": Player,
"o": Coin,
"=": Lava,
"|": Lava,
"v": Lava,
"#": Lava,
"-": LavaFlood
};
function Player(pos) {
this.pos = pos.plus(new Vector(0, -.5));
this.size = new Vector(.5, 1);
this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";
function Lava(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
if (ch === "=")
this.speed = new Vector(2, 0);
else if (ch === '|')
this.speed = new Vector(0, 2);
else if (ch === 'v'){
this.speed = new Vector(0, 5);
this.repeatPos = pos;
} else if (ch === '#')
this.speed = new Vector(0, 10);
}
Lava.prototype.type = "lava"
function LavaFlood(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
this.trueSize = new Vector(1, 1);
if (ch === '-') {
this.speed = new Vector(0, -1);
this.repeatPos = pos; //will be removed in the future
}
}
LavaFlood.prototype.type = "lava-flood"
//Lava.prototype.type = "Lava";
// function Wall(pos, ch) {
// this.pos = pos;
// this.size = new Vector(1, 1);
// if (ch === "z")
// this.speed = new Vector(0, 1);
// }
// Wall.prototype.type = "wall"
function Coin(pos) {
this.basePos = this.pos = pos;
this.size = new Vector(.6, .6);
// take a look back
this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";
Level.prototype.isFinished = function() {
return this.status !== null && this.finishDelay < 0;
};
function Level(plan) {
this.width = plan[0].length;
this.height = plan.length;
this.grid = [];
this.actors = [];
for (var y = 0; y < this.height; y++) {
var line = plan[y], gridLine = [];
for (var x = 0; x < this.width; x++) {
var ch = line[x], fieldType = null;
var Actor = actorchars[ch];
if (Actor)
this.actors.push(new Actor(new Vector(x, y), ch));
else if (ch === "x")
fieldType = "wall";
else if (ch === "z")
fieldType = "wall";
else if (ch === "!")
fieldType = "lava";
else if (ch === "|")
fieldType = "lava";
else if (ch === "=")
fieldType = "lava";
else if (ch === "#")
fieldType = "lava";
else if (ch === "-")
fieldType = "lava-flood";
else if (ch === "v"){
fieldType = "lava";
console.log(fieldType);
}
gridLine.push(fieldType);
}
this.grid.push(gridLine);
}
this.player = this.actors.filter(function(actor) {
return actor.type === "player";
})[0];
this.status = this.finishDelay = null;
}
function element(name, className) {
var elem = document.createElement(name);
if(className) elem.className = className;
return elem;
}
function DOMDisplay(parent, level) {
this.wrap = parent.appendChild(element("div", "game"));
this.level = level;
this.wrap.appendChild(this.drawBackground());
this.actorLayer = null;
this.drawFrame();
}
var scale = 15;
DOMDisplay.prototype.drawBackground = function() {
var table = element("table", "background");
table.style.width = this.level.width * scale + "px";
table.style.height = this.level.height * scale + "px";
this.level.grid.forEach(function(row) {
var rowElement = table.appendChild(element("tr"));
rowElement.style.height = scale + "px";
row.forEach(function(type) {
rowElement.appendChild(element("td", type));
});
});
return table;
};
DOMDisplay.prototype.drawActors = function() {
var wrap = element("div");
this.level.actors.forEach(function(actor) {
var rect = wrap.appendChild(element("div", "actor " + actor.type));
rect.style.width = actor.size.x * scale + "px";
rect.style.height = actor.size.y * scale + "px";
rect.style.left = actor.pos.x * scale + "px";
rect.style.top = actor.pos.y * scale + "px";
});
return wrap;
};
DOMDisplay.prototype.drawFrame = function() {
if (this.actorLayer)
this.wrap.removeChild(this.actorLayer);
this.actorLayer = this.wrap.appendChild(this.drawActors());
this.wrap.className = "game " + (this.level.status || "");
this.scrollPlayerIntoView();
};
// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
var width = this.wrap.clientWidth;
var height = this.wrap.clientHeight;
var margin = width / 3;
// The viewport
var left = this.wrap.scrollLeft, right = left + width;
var top = this.wrap.scrollTop, bottom = top + height;
var player = this.level.player;
var center = player.pos.plus(player.size.times(0.5))
.times(scale);
if (center.x < left + margin)
this.wrap.scrollLeft = center.x - margin;
else if (center.x > right - margin)
this.wrap.scrollLeft = center.x + margin - width;
if (center.y < top + margin)
this.wrap.scrollTop = center.y - margin;
else if (center.y > bottom - margin)
this.wrap.scrollTop = center.y + margin - height;
};
DOMDisplay.prototype.clear = function() {
this.wrap.parentNode.removeChild(this.wrap);
};
Level.prototype.obstacleAt = function(pos, size) {
var xStart = Math.floor(pos.x);
var xEnd = Math.ceil(pos.x + size.x);
var yStart = Math.floor(pos.y);
var yEnd = Math.ceil(pos.y + size.y);
if (xStart < 0 || xEnd > this.width || yStart < 0)
return "wall";
if (yEnd > this.height)
return "lava", "lava-flood";
for (var y = yStart; y < yEnd; y++) {
for (var x = xStart; x < xEnd; x++) {
var fieldType = this.grid[y][x];
if (fieldType) return fieldType;
}
}
};
Level.prototype.actorAt = function(actor) {
for (var i = 0; i < this.actors.length; i++) {
var other = this.actors[i];
if (other != actor &&
actor.pos.x + actor.size.x > other.pos.x &&
actor.pos.x < other.pos.x + other.size.x &&
actor.pos.y + actor.size.y > other.pos.y &&
actor.pos.y < other.pos.y + other.size.y)
return other;
}
};
var maxStep = 0.05;
Level.prototype.animate = function(step, keys) {
if (this.status !== null)
this.finishDelay -= step;
while (step > 0) {
var thisStep = Math.min(step, maxStep);
this.actors.forEach(function(actor) {
actor.act(thisStep, this, keys);
}, this);
step -= thisStep;
}
};
Lava.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.size))
this.pos = newPos;
else if (this.repeatPos)
this.pos = this.repeatPos;
else
this.speed = this.speed.times(-1);
};
LavaFlood.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.trueSize)){
this.size = this.size.plus(this.speed.times(-step));
this.pos = newPos;
}
else
this.speed = this.speed.times(-1);
};
var wobbleSpeed = 8, wobbleDist = 0.07;
Coin.prototype.act = function(step) {
this.wobble += step * wobbleSpeed;
var wobblePos = Math.sin(this.wobble) * wobbleDist;
this.pos = this.basePos.plus(new Vector(0, wobblePos));
};
var playerXSpeed = 10;
Player.prototype.moveX = function(step, level, keys) {
this.speed.x = 0;
if (keys.left) this.speed.x -= playerXSpeed;
if (keys.right) this.speed.x += playerXSpeed;
var motion = new Vector(this.speed.x * step, 0);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle)
level.playerTouched(obstacle);
else
this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
Player.prototype.moveY = function(step, level, keys) {
this.speed.y += step * gravity;
var motion = new Vector(0, this.speed.y * step);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle) {
level.playerTouched(obstacle);
if (keys.up && this.speed.y > 0)
this.speed.y = -jumpSpeed;
else
this.speed.y = 0;
} else {
this.pos = newPos;
}
};
Player.prototype.act = function(step, level, keys) {
this.moveX(step, level, keys);
this.moveY(step, level, keys);
var otherActor = level.actorAt(this);
if (otherActor)
level.playerTouched(otherActor.type, otherActor);
// Losing animation
if (level.status == "lost") {
this.pos.y += step;
this.size.y -= step;
}
};
Level.prototype.playerTouched = function(type, actor) {
//if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
if (type == "lava" && this.status === null || type == "lava-flood" && this.status === null) {
this.status = "lost";
life -= 1;
console.log(life);
expo = 0;
document.getElementById("expo").innerHTML = ("Points: " + expo);
document.getElementById("life").innerHTML = ("Lives left: " + life);
if(life < 0) {
sessionStorage.setItem("reloading", "true");
document.location.reload();
}
this.finishDelay = 1;
} else if (type == "coin") {
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
this.actors = this.actors.filter(function(other) {
return other != actor;
});
if (!this.actors.some(function(actor) {
return actor.type == "coin";
})) {
life += 1;
document.getElementById("life").innerHTML = ("Lives left: " + life);
this.status = "won";
this.finishDelay = 1;
}
}
};
var arrowCodes = {37: "left", 38: "up", 39: "right"};
function trackKeys(codes) {
var pressed = Object.create(null);
function handler(event) {
if (codes.hasOwnProperty(event.keyCode)) {
var down = event.type == "keydown";
pressed[codes[event.keyCode]] = down;
event.preventDefault();
}
}
addEventListener("keydown", handler);
addEventListener("keyup", handler);
return pressed;
}
function runAnimation(frameFunc) {
var lastTime = null;
function frame(time) {
var stop = false;
if (lastTime !== null) {
var timeStep = Math.min(time - lastTime, 100) / 1000;
stop = frameFunc(timeStep) === false;
}
lastTime = time;
if (!stop)
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
var arrows = trackKeys(arrowCodes);
function runLevel(level, Display, andThen) {
var display = new Display(document.body, level);
runAnimation(function(step) {
level.animate(step, arrows);
display.drawFrame(step);
if (level.isFinished()) {
display.clear();
if (andThen)
andThen(level.status);
return false;
}
});
}
var lives = function() {
ctx.font = "20px Courier";
ctx.fontFamily = "monospace";
ctx.fillStyle = "#666";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Lives left: " + life, 10, 10);
};
function runGame(plans, Display) {
function startLevel(n) {
runLevel(new Level(plans[n]), Display, function(status) {
if (status == "lost") {
startLevel(n);
} else if (n < plans.length - 1)
startLevel(n + 1);
else
alert("You win!");
});
}
startLevel(0);
}
runGame(LEVELS, DOMDisplay);
body {
background: #222;
}
h2 {
color: #666;
font-family: monospace;
text-align: center;
}
.background {
table-layout: fixed;
border-spacing: 0;
}
.background td {
padding: 0;
}
.lava, .actor {
background: #e55;
}
.wall {
background: #444;
border: solid 3px #333;
box-sizing: content-box;
}
.actor {
position: absolute;
}
.coin {
background: #e2e838;
border-radius: 50%;
}
.player {
background: #335699;
box-shadow: none;
}
.lost .player {
background: #a04040;
}
.won .player {
background: green;
}
.game {
position: relative;
overflow: hidden;
}
#life, #expo {
font-size: 16px;
font-family: monospace;
color: #666;
text-align: left;
baseline: top;
margin-left: 30px;
font-weight: bold;
}
input {
margin-left: 30px;
}
<h2>Simple JavaScript Game</h2>
<div id="life"></div>
<div id="expo"></div>
If you need clarification or help for anything, please feel free to ask.

I have a issue in my canvas. i want to change the color of particles. currently they are in black color. How i can change..?

I have a issue in my canvas. i want to change the color of particles. currently they are in black color. How i can change..?
here is my code please let me know if u have any solution.
I have a issue in my canvas. i want to change the color of particles. currently they are in black color. How i can change..?
here is my code please let me know if u have any solution. http://jsfiddle.net/gbcL0uks/
<head>
<title>
Text Particles
</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
<link rel="stylesheet" href="https://cdn.bootcss.com/hover.css/2.3.1/css/hover-min.css">
<meta property="og:title" content="Text Particles" />
<meta property="og:description" content="Some cool canvas pixel manipulation" />
<meta property="og:image" content="http://william.hoza.us/images/text-small.png" />
<style>
#import url('https://fonts.googleapis.com/css?family=Bree+Serif');
body {
font-family: 'Bree Serif', serif;
}
.float {
position: absolute;
left: 20px;
}
.menu {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
}
.fa-bars {
font-size: 24px;
border: 2px solid #333333;
padding: 12px;
transition: .4s;
}
.fa-bars:hover {
background: #333333;
color: #f7d600;
}
.overlay {
height: 100%;
width: 100%;
position: fixed;
z-index: 1;
top: 0;
left: -100%;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.9);
overflow-x: hidden;
transition: 0.5s;
}
.overlay-content {
position: relative;
top: 50%;
width: 100%;
text-align: center;
margin-top: 30px;
transform: translate(0, -100%);
}
.overlay a {
padding: 8px;
text-decoration: none;
font-size: 6rem;
color: #818181;
display: inline-block;
transition: 0.3s;
margin: 0 3rem;
}
.overlay a:hover,
.overlay a:focus {
color: #f1f1f1;
}
.overlay .closebtn {
position: absolute;
top: 20px;
right: 0;
font-size: 60px;
}
#media screen and (max-height: 450px) {
.overlay a {
font-size: 20px
}
.overlay .closebtn {
font-size: 40px;
top: 15px;
right: 0;
}
}
</style>
</head>
<body style="margin:0px; background:#f7d600;">
<div class="float">
<h1>Herbialis</h1>
</div>
<!-- <div class="menu">
<i class="fa fa-bars"></i>
</div> -->
<div id="myNav" class="overlay">
×
<div class="overlay-content">
About
Products
Contact
</div>
</div>
<div class="menu">
<i class="fa fa-bars" onclick="openNav()"></i>
</div>
<canvas id="canv" onmousemove="canv_mousemove(event);" onmouseout="mx=-1;my=-1;">
you need a canvas-enabled browser, such as Google Chrome
</canvas>
<canvas id="wordCanv" width="500px" height="500px" style="border:1px solid black;display:none;">
</canvas>
<textarea id="wordsTxt" style="position:absolute;left:-100;top:-100;" onblur="init();" onkeyup="init();" onclick="init();"></textarea>
<script type="text/javascript">
var l = document.location + "";
l = l.replace(/%20/g, " ");
var index = l.indexOf('?t=');
if (index == -1) document.location = l + "?t=Hello world";
var pixels = new Array();
var canv = $('canv');
var ctx = canv.getContext('2d');
var wordCanv = $('wordCanv');
var wordCtx = wordCanv.getContext('2d');
var mx = -1;
var my = -1;
var words = "";
var txt = new Array();
var cw = 0;
var ch = 0;
var resolution = 1;
var n = 0;
var timerRunning = false;
var resHalfFloor = 0;
var resHalfCeil = 0;
function canv_mousemove(evt) {
mx = evt.clientX - canv.offsetLeft;
my = evt.clientY - canv.offsetTop;
}
function Pixel(homeX, homeY) {
this.homeX = homeX;
this.homeY = homeY;
this.x = Math.random() * cw;
this.y = Math.random() * ch;
//tmp
this.xVelocity = Math.random() * 10 - 5;
this.yVelocity = Math.random() * 10 - 5;
}
Pixel.prototype.move = function () {
var homeDX = this.homeX - this.x;
var homeDY = this.homeY - this.y;
var homeDistance = Math.sqrt(Math.pow(homeDX, 2) + Math.pow(homeDY, 2));
var homeForce = homeDistance * 0.01;
var homeAngle = Math.atan2(homeDY, homeDX);
var cursorForce = 0;
var cursorAngle = 0;
if (mx >= 0) {
var cursorDX = this.x - mx;
var cursorDY = this.y - my;
var cursorDistanceSquared = Math.pow(cursorDX, 2) + Math.pow(cursorDY, 2);
cursorForce = Math.min(10000 / cursorDistanceSquared, 10000);
cursorAngle = Math.atan2(cursorDY, cursorDX);
} else {
cursorForce = 0;
cursorAngle = 0;
}
this.xVelocity += homeForce * Math.cos(homeAngle) + cursorForce * Math.cos(cursorAngle);
this.yVelocity += homeForce * Math.sin(homeAngle) + cursorForce * Math.sin(cursorAngle);
this.xVelocity *= 0.92;
this.yVelocity *= 0.92;
this.x += this.xVelocity;
this.y += this.yVelocity;
}
function $(id) {
return document.getElementById(id);
}
function timer() {
if (!timerRunning) {
timerRunning = true;
setTimeout(timer, 33);
for (var i = 0; i < pixels.length; i++) {
pixels[i].move();
}
drawPixels();
wordsTxt.focus();
n++;
if (n % 10 == 0 && (cw != document.body.clientWidth || ch != document.body.clientHeight)) body_resize();
timerRunning = false;
} else {
setTimeout(timer, 10);
}
}
function drawPixels() {
var imageData = ctx.createImageData(cw, ch);
var actualData = imageData.data;
var index;
var goodX;
var goodY;
var realX;
var realY;
for (var i = 0; i < pixels.length; i++) {
goodX = Math.floor(pixels[i].x);
goodY = Math.floor(pixels[i].y);
for (realX = goodX - resHalfFloor; realX <= goodX + resHalfCeil && realX >= 0 && realX < cw; realX++) {
for (realY = goodY - resHalfFloor; realY <= goodY + resHalfCeil && realY >= 0 && realY < ch; realY++) {
index = (realY * imageData.width + realX) * 4;
actualData[index + 3] = 255;
}
}
}
imageData.data = actualData;
ctx.putImageData(imageData, 0, 0);
}
function readWords() {
words = $('wordsTxt').value;
txt = words.split('\n');
}
function init() {
readWords();
var fontSize = 200;
var wordWidth = 0;
do {
wordWidth = 0;
fontSize -= 5;
wordCtx.font = fontSize + "px sans-serif";
for (var i = 0; i < txt.length; i++) {
var w = wordCtx.measureText(txt[i]).width;
if (w > wordWidth) wordWidth = w;
}
} while (wordWidth > cw - 50 || fontSize * txt.length > ch - 50)
wordCtx.clearRect(0, 0, cw, ch);
wordCtx.textAlign = "center";
wordCtx.textBaseline = "middle";
for (var i = 0; i < txt.length; i++) {
wordCtx.fillText(txt[i], cw / 2, ch / 2 - fontSize * (txt.length / 2 - (i + 0.5)));
}
var index = 0;
var imageData = wordCtx.getImageData(0, 0, cw, ch);
for (var x = 0; x < imageData.width; x += resolution) //var i=0;i<imageData.data.length;i+=4)
{
for (var y = 0; y < imageData.height; y += resolution) {
i = (y * imageData.width + x) * 4;
if (imageData.data[i + 3] > 128) {
if (index >= pixels.length) {
pixels[index] = new Pixel(x, y);
} else {
pixels[index].homeX = x;
pixels[index].homeY = y;
}
index++;
}
}
}
pixels.splice(index, pixels.length - index);
}
function body_resize() {
cw = document.body.clientWidth;
ch = document.body.clientHeight;
canv.width = cw;
canv.height = ch;
wordCanv.width = cw;
wordCanv.height = ch;
init();
}
wordsTxt.focus();
wordsTxt.value = l.substring(index + 3);
resHalfFloor = Math.floor(resolution / 2);
resHalfCeil = Math.ceil(resolution / 2);
body_resize();
timer();
</script>
<script>
function openNav() {
document.getElementById("myNav").style.left = "0";
}
function closeNav() {
document.getElementById("myNav").style.left = "-100%";
}
</script>
</body>
</html>
Add 3 color components to image data. For example, for orange color:
for (realX = goodX - resHalfFloor; realX <= goodX + resHalfCeil && realX >= 0 && realX < cw; realX++) {
for (realY = goodY - resHalfFloor; realY <= goodY + resHalfCeil && realY >= 0 && realY < ch; realY++) {
index = (realY * imageData.width + realX) * 4;
actualData[index + 0] = 253; // Red component
actualData[index + 1] = 106; // Green component
actualData[index + 2] = 2; // Blue component
actualData[index + 3] = 255;
}
}

Strange issue with arithmetics (?)

Background: I'm working on a library for creating "low resolution" display.
Now I wanted to test it with a simple demo - it should draw a radial gradient around cursor.
I think I got the math right and it is working, except when you move your mouse into the bottom left corner.
I tried printing the numbers, changing order and all, but can't find the cause.
Here is also a Fiddle: https://jsfiddle.net/to5qfk7o/
var size = 16; // number of pixels
var lo = new Lores('#board', size, size);
// DRAWING FUNCTION
setInterval(function () {
// Mouse coords
var m_x = lo.mouse.x;
var m_y = lo.mouse.y;
// print where is mouse - for debug
console.log(m_x + "," + m_y);
// for all pixels on screen
for (var x = 0; x < size; x++) {
for (var y = 0; y < size; y++) {
// mouse distance from this pixel
var distance = (Math.sqrt((m_x - x) * (m_x - x) + (m_y - y) * (m_y - y)));
// convert: 0..255, "size"..0
var color = 255 - Math.floor((255 / size) * distance);
// set color
if (color < 0) {
lo.set(y, x, 'black');
} else {
lo.set(x, y, 'rgb(' + color + ', 0, 0)');
}
}
}
}, 100);
// ---- LIBRARY CODE -----
function Lores(selector, width, height) {
this.boardElem = document.querySelector(selector);
this.boardElem.className += ' lores-screen';
this.grid = [];
this.mouse = {
inside: false,
x: 0,
y: 0 // position rounded to nearest board "pixel"
};
this.width = width;
this.height = height;
if (this.boardElem === null) {
console.error('No such element!');
return;
}
if (width <= 0 || height <= 0) {
console.error('Dimensions must be positive!');
return;
}
// Inject a style block for the sizes
var css = selector + ' > div {height:' + (100 / height) + '%}';
css += selector + ' > div > div {width:' + (100 / width) + '%}';
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
document.head.appendChild(style);
var frag = document.createDocumentFragment();
// Create the grid
for (var i = height; i > 0; i--) {
var rowElem = document.createElement('div');
rowElem.dataset.y = i;
var row = [];
for (var j = width; j > 0; j--) {
var cellElem = document.createElement('div');
cellElem.dataset.x = j;
rowElem.appendChild(cellElem);
row.push(cellElem);
}
frag.appendChild(rowElem);
this.grid.push(row);
}
this.boardElem.appendChild(frag);
console.log('yo');
var self = this;
// add mouse listener
document.addEventListener('mousemove', function (e) {
var rect = self.boardElem.getBoundingClientRect();
var x = (self.width * (e.clientX - rect.left)) / rect.width;
var y = (self.height * (e.clientY - rect.top)) / rect.height;
self.mouse.x = Math.floor(x);
self.mouse.y = Math.floor(y);
self.mouse.inside = (x >= 0 && x < self.width && y >= 0 && y < self.height);
}, false);
}
Lores.prototype.set = function (x, y, color) {
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
this.grid[y][x].style.backgroundColor = color;
};
#board {
margin: 0 auto;
width: 128px;
height: 128px;
outline: 1px solid black;
}
#board > div:nth-child(odd) > div:nth-child(odd) {
background: #eee
}
#board > div:nth-child(even) > div:nth-child(even) {
background: #eee
}
.lores-screen {
display: block;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.lores-screen, .lores-screen * {
white-space: nowrap;
padding: 0;
}
.lores-screen * {
margin: 0
}
.lores-screen > div {
display: block;
}
.lores-screen > div > div {
display: inline-block;
vertical-align: top;
height: 100%;
}
<div id="board"></div>
It could be something trivial, I really don't know. Thanks!
The problem is a simple typo in your if statement. You have x and y mixed up in the set of your first branch. Having said that, we can eliminate the branch entirely. A simple Math.max(0, ...) will default it to black.
Change the following:
var color = 255 - Math.floor((255 / size) * distance);
// set color
if (color < 0) {
lo.set(y, x, 'black');
} else {
lo.set(x, y, 'rgb(' + color + ', 0, 0)');
}
to
var color = Math.max(0, 255-Math.floor((255 / size) * distance));
lo.set(x, y, 'rgb(' + color + ', 0, 0)');
jsfiddle
change
// set color
if (color < 0) {
lo.set(y, x, 'black');
} else {
lo.set(x, y, 'rgb(' + color + ', 0, 0)');
}
to
// set color
lo.set(x, y, 'rgb(' + Math.max(color, 0) + ', 0, 0)');
https://jsfiddle.net/hrvqa457/

Categories