I am trying to make a Snake game by canvas in JavaScript. I have completed almost all the settings but the collision check that the game will crash when the snake hit itself or the wall as it cannot insert the checkCollision method which I have defined.
<script>
//Create canvas
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width = canvas.width;
let height = canvas.height;
let blockSize = 10;
let widthInBlocks = width / blockSize;
let heightInBlocks = height / blockSize;
let drawBorder = function () {
ctx.fillStyle = 'Black';
ctx.fillRect(0, 0, width, blockSize);
ctx.fillRect(0, height - blockSize, width, blockSize);
ctx.fillRect(0, 0, blockSize, height);
ctx.fillRect(width - blockSize, 0, blockSize, height);
};
drawBorder();
//Create score
var score = 0;
let drawScore = function () {
ctx.clearRect(10, 10, width - 20, 40);
ctx.fillStyle = 'Black';
ctx.textBaseLine = 'top';
ctx.textAlign = 'left';
ctx.font = '24px Arial';
ctx.fillText('Score : ' + score, 15, 45);
};
//Block constrcutor
const Block = function (col, row) {
this.col = col;
this.row = row;
};
Block.prototype.drawSquare = function (color) {
let x = this.col * blockSize;
let y = this.row * blockSize;
ctx.fillStyle = color;
ctx.fillRect(x, y, blockSize, blockSize);
};
//Create food
const circle = function (x, y, radius, color, fill) {
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fill) {
ctx.fill();
} else {
ctx.stroke();
}
};
Block.prototype.drawCircle = function (color) {
let x = this.col * blockSize + blockSize / 2;
let y = this.row * blockSize + blockSize / 2;
ctx.fillStyle = color;
circle(x, y, blockSize / 2, color, true);
};
let Apple = function () {
this.position = new Block(10, 10);
};
Apple.prototype.draw = function () {
this.position.drawCircle(colorList[Math.floor(Math.random() * 7)]);
};
Apple.prototype.move = function () {
let randomCol = Math.floor(Math.random() * (widthInBlocks - 2)) + 1;
let randomRow = Math.floor(Math.random() * (heightInBlocks - 2)) + 1;
if (randomCol !== this.segments && randomRow !== this.segments) {
this.position = new Block(randomCol, randomRow);
}
};
//Setting keycode
const directions = {
37:'left',
38:'up',
39:'right',
40:'down'
};
$('body').keydown(function (event) {
let newDirection = directions[event.keyCode];
if (newDirection !== undefined) {
snake.setDirection(newDirection);
}
});
//Create snake
var colorList = ['Blue','Green','Red','Gold','Silver','Purple','Cyan']
var Snake = function () {
this.segments = [
new Block(7,5),
new Block(6,5),
new Block(5,5)
];
this.direction = 'right';
this.nextDirection = 'right';
};
Snake.prototype.draw = function () {
for (let i = 0; i < this.segments.length; i ++) {
this.segments[i].drawSquare(colorList[Math.floor(Math.random() * 7)]);
};
};
//Setting moving directions
Snake.prototype.move = function () {
let head = this.segments[0];
let newHead;
this.direction = this.nextDirection;
if (this.direction === 'right'){
newHead = new Block(head.col + 1, head.row);
} else if (this.direction === 'left') {
newHead = new Block(head.col - 1, head.row);
} else if (this.direction === 'up') {
newHead = new Block(head.col, head.row - 1);
} else if (this.direction === 'down') {
newHead = new Block(head.col, head.row + 1)
}
if (this.checkCollision(newHead)) {
gameOver();
return;
}
this.segments.unshift(newHead);
if (newHead.equal(apple.position)) {
score ++;
apple.move();
aniTime -= 1;
} else {
this.segments.pop();
}
};
//Define collision
Block.prototype.equal = function (otherBlock) {
return this.col === otherBlock.col && this.row === otherBlock.row;
};
Snake.prototype.checkCollision = function (head) {
var leftCollision = (head.col === 0);
var topCollision = (head.row === 0);
var rightCollision = (head.col === widthInBlocks - 1);
var bottomCollision = (head.row === heightInBlocks - 1);
var wallCollision = leftCollision || topCollision ||
rightCollision || bottomCollision;
var selfCollision = false;
for (let i = 0; i < this.segments.length; i ++) {
if (head.equal(this.segments[i])) {
selfCollision = true;
}
}
return wallCollision || selfCollision
};
Snake.prototype.setDirection = function (newDirection) {
if (this.direction === 'up' && newDirection === 'down') {
return;
} else if (this.direction === 'right' && newDirection ==='left') {
return;
} else if (this.direction === 'down' && newDirection ==='up') {
return;
} else if (this.direction === 'left' && newDirection ==='right') {
return;
}
this.nextDirection = newDirection;
};
//run the game
let snake = new Snake();
let apple = new Apple();
var aniTime = 100;
function core () {
ctx.clearRect(0, 0, width, height);
drawScore();
snake.move();
snake.draw();
apple.draw();
drawBorder();
timeOutId = setTimeout(core, aniTime);
if (snake.checkCollision() === true) { //**the PROBLEM
clearTimeout(timeOutId);
gameOver();
};
};
core();
//Game over condition
var gameOver = function () {
clearTimeout(timeOutId);
ctx.font = '60px Arial';
ctx.fillStyle = 'Black';
ctx.textAlign = 'center';
ctx.textBaseLine = 'middle';
ctx.fillText('Game Over', width / 2, height / 2);
};
</script>
When the snake hit itself or the wall, the error message are as below:
Uncaught TypeError: Cannot read properties of undefined (reading 'col')
at Snake.checkCollision (snake.html:148:43)
at core (snake.html:191:27)
at snake.html:196:13
Uncaught TypeError: Cannot read properties of undefined (reading 'col')
at Snake.checkCollision (snake.html:148:43)
at core (snake.html:191:27)
snake.html:129
Uncaught TypeError: gameOver is not a function
at Snake.move (snake.html:129:21)
at core (snake.html:186:23)
There seem to be several minor mistakes that are luckily easy to fix:
gameOver is declared as a var after calling core(). Declare it as a function to make sure its definition gets hoisted to the top. (or call core() below var gameOver = ...)
timeOutId is used by both the core and gameOver functions. Declare it outside of those functions to make sure both have access.
Snake.prototype.checkCollision expects head as an argument, but core calls it without one.
The self collision loop starts at i = 0, which means it will check wether the head collides with itself, which is always the case.
Here's an example that has all the issues fixed:
//Create canvas
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width = canvas.width;
let height = canvas.height;
let blockSize = 15;
let widthInBlocks = width / blockSize;
let heightInBlocks = height / blockSize;
let drawBorder = function () {
ctx.fillStyle = 'Black';
ctx.fillRect(0, 0, width, blockSize);
ctx.fillRect(0, height - blockSize, width, blockSize);
ctx.fillRect(0, 0, blockSize, height);
ctx.fillRect(width - blockSize, 0, blockSize, height);
};
drawBorder();
//Create score
var score = 0;
let drawScore = function () {
ctx.clearRect(10, 10, width - 20, 40);
ctx.fillStyle = 'Black';
ctx.textBaseLine = 'top';
ctx.textAlign = 'left';
ctx.font = '24px Arial';
ctx.fillText('Score : ' + score, 15, 45);
};
//Block constrcutor
const Block = function (col, row) {
this.col = col;
this.row = row;
};
Block.prototype.drawSquare = function (color) {
let x = this.col * blockSize;
let y = this.row * blockSize;
ctx.fillStyle = color;
ctx.fillRect(x, y, blockSize, blockSize);
};
//Create food
const circle = function (x, y, radius, color, fill) {
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fill) {
ctx.fill();
} else {
ctx.stroke();
}
};
Block.prototype.drawCircle = function (color) {
let x = this.col * blockSize + blockSize / 2;
let y = this.row * blockSize + blockSize / 2;
ctx.fillStyle = color;
circle(x, y, blockSize / 2, color, true);
};
let Apple = function () {
this.position = new Block(10, 10);
};
Apple.prototype.draw = function () {
this.position.drawCircle(colorList[Math.floor(Math.random() * 7)]);
};
Apple.prototype.move = function () {
let availableCells = [];
for (let r = 1; r < heightInBlocks - 2; r += 1) {
for (let c = 1; c < widthInBlocks - 2; c += 1) {
if (snake.segments.some(s => s.row === r && s.col === c)) continue;
availableCells.push([r, c]);
};
};
if (availableCells.length === 0) {
console.log("You won!");
return;
}
const [ randomRow, randomCol ] = availableCells[Math.floor(Math.random() * availableCells.length)];
this.position = new Block(randomCol, randomRow);
};
//Setting keycode
const directions = {
37:'left',
38:'up',
39:'right',
40:'down'
};
$('body').keydown(function (event) {
let newDirection = directions[event.keyCode];
if (newDirection !== undefined) {
snake.setDirection(newDirection);
}
});
//Create snake
var colorList = ['Blue','Green','Red','Gold','Silver','Purple','Cyan']
var Snake = function () {
this.segments = [
new Block(7,5),
new Block(6,5),
new Block(5,5)
];
this.direction = 'right';
this.nextDirection = 'right';
};
Snake.prototype.draw = function () {
for (let i = 0; i < this.segments.length; i ++) {
this.segments[i].drawSquare(colorList[Math.floor(Math.random() * 7)]);
};
};
//Setting moving directions
Snake.prototype.move = function () {
let head = this.segments[0];
let newHead;
this.direction = this.nextDirection;
if (this.direction === 'right'){
newHead = new Block(head.col + 1, head.row);
} else if (this.direction === 'left') {
newHead = new Block(head.col - 1, head.row);
} else if (this.direction === 'up') {
newHead = new Block(head.col, head.row - 1);
} else if (this.direction === 'down') {
newHead = new Block(head.col, head.row + 1)
}
if (this.checkCollision(newHead)) {
gameOver();
return;
}
this.segments.unshift(newHead);
if (newHead.equal(apple.position)) {
score ++;
apple.move();
aniTime -= 1;
} else {
this.segments.pop();
}
};
//Define collision
Block.prototype.equal = function (otherBlock) {
return this.col === otherBlock.col && this.row === otherBlock.row;
};
Snake.prototype.checkCollision = function () {
// FIX 3: make sure head is defined
var head = this.segments[0];
var leftCollision = (head.col === 0);
var topCollision = (head.row === 0);
var rightCollision = (head.col === widthInBlocks - 1);
var bottomCollision = (head.row === heightInBlocks - 1);
var wallCollision = leftCollision || topCollision ||
rightCollision || bottomCollision;
var selfCollision = false;
// Fix 4: start at 1 so head does not self collide
for (let i = 1; i < this.segments.length; i ++) {
if (head.equal(this.segments[i])) {
selfCollision = true;
}
}
return wallCollision || selfCollision
};
Snake.prototype.setDirection = function (newDirection) {
if (this.direction === 'up' && newDirection === 'down') {
return;
} else if (this.direction === 'right' && newDirection ==='left') {
return;
} else if (this.direction === 'down' && newDirection ==='up') {
return;
} else if (this.direction === 'left' && newDirection ==='right') {
return;
}
this.nextDirection = newDirection;
};
//run the game
let snake = new Snake();
let apple = new Apple();
var aniTime = 100;
// FIX 2: declare at top level
var timeOutId;
function core () {
ctx.clearRect(0, 0, width, height);
drawScore();
snake.move();
snake.draw();
apple.draw();
drawBorder();
timeOutId = setTimeout(core, aniTime);
if (snake.checkCollision() === true) {
clearTimeout(timeOutId);
gameOver();
};
};
core();
//Game over condition
// FIX 1: declare as function
function gameOver() {
clearTimeout(timeOutId);
ctx.font = '60px Arial';
ctx.fillStyle = 'Black';
ctx.textAlign = 'center';
ctx.textBaseLine = 'middle';
ctx.fillText('Game Over', width / 2, height / 2);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" width="300" height="300"></canvas>
Related
I just made this animation using canvas. Shows a number of bubbles scrolling from top to bottom. Clicking on any bubble starts it moving top to bottom again.
I plan to add a counter of the number of bubbles clicked/picked with a localStorage but I have difficulties implementing it.
Here is how I implemented it.
Each bubble now has id property.
Every time you run the script, it puts empty array to localStorage.clickedBubbles.
When a bubble is clicked, the array is checked if it contains the id. If not, the id is pushed to local storage.
Also, each click the count is logged to console.
const INITIALIZATION = 100;
const STEP1 = 200;
const BUBBLES = 10;
const SPEED = 10;
let counter = 0;
localStorage.setItem("clickedBubbles", "[]");
function Bubble(x, y, radio) {
this.id = counter++;
this.x = x;
this.y = y;
this.radio = radio;
this.color = "blue";
this.speed = 5;
this.getId = function () {
return this.id;
};
this.getX = function () {
return this.x;
};
this.getY = function () {
return this.y;
};
this.getRadio = function () {
return this.radio;
};
this.getColor = function () {
return this.color;
};
this.getspeed = function () {
return this.speed;
};
this.setX = function (x) {
this.x = x;
};
this.setY = function (y) {
this.y = y;
};
this.setRadio = function (radio) {
this.radio = radio;
};
this.setColor = function (color) {
this.color = color;
};
this.setSpeed = function (speed) {
this.speed = speed;
};
this.draw = function (ctx) {
ctx.save();
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radio, 0, Math.PI * 2, true);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(
this.x + this.radio / 3,
this.y - this.radio / 3,
this.radio / 4,
0,
Math.PI * 2,
true
);
ctx.fill();
ctx.restore();
};
this.coordinates = function (x, y) {
var ax = this.getX() - this.getRadio();
var ay = this.getY() - this.getRadio();
return (
x >= ax &&
x <= ax + 2 * this.getRadio() &&
y >= ay &&
y <= ay + 2 * this.getRadio()
);
};
}
function animationBubbles() {
var bubbles = [];
var arrayColors;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var status = INITIALIZATION;
var app = this;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
this.click = function (e) {
var x = e.x || e.pageX || e.clientX;
var y = e.y || e.clientY || e.pageY;
const clickedArr = JSON.parse(localStorage.getItem("clickedBubbles"));
for (var i = 0; i < bubbles.length; i++) {
var aux = bubbles[i];
if (aux.coordinates(x, y)) {
if (!clickedArr.includes(aux.getId())) {
clickedArr.push(aux.getId());
localStorage.setItem("clickedBubbles", JSON.stringify(clickedArr));
}
aux.setY(0);
break;
}
}
console.log("Clicked count: " + clickedArr.length);
};
canvas.addEventListener("mousedown", this.click, false);
this.realizeAnimation = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < bubbles.length; i++) {
var aux = bubbles[i];
if (aux.getY() < canvas.height) {
aux.setY(aux.getY() + aux.getspeed());
} else {
aux.setY(0);
aux.setColor(arrayColors[Math.floor(Math.random() * 4)]);
}
aux.draw(ctx);
}
setTimeout(app.realizeAnimation, 100);
};
this.createBubbles = function () {
for (var i = 1; i <= BUBBLES; i++) {
var burbuja = new Bubble(
canvas.width * (i / BUBBLES),
0,
this.generateRandom(Math.floor(canvas.width / 20))
);
burbuja.setSpeed(SPEED);
burbuja.setColor(arrayColors[this.generateRandom(4)]);
bubbles.push(burbuja);
}
};
this.generateRandom = function (num) {
return Math.floor(Math.random() * num);
};
this.initColor = function () {
arrayColors[0] = "#C923C9";
arrayColors[1] = "#FAEF20";
arrayColors[2] = "#20ECFA";
arrayColors[3] = "#FA209C";
};
this.machineStates = function () {
if (status === INITIALIZATION) {
arrayColors = [];
this.initColor();
this.createBubbles();
status = STEP1;
setTimeout(app.machineStates, 100);
} else {
app.realizeAnimation();
}
};
this.machineStates();
}
new animationBubbles();
I am making a snake game with plain javascript. But now I have come to the food part of the game. but I just cant get it to work properly. I have an food function that generates random cords within the gamefield and then draws the food. This is already quite wonky at the first place. but then I want to detect of the snake cords and foodcords match up with a margin of 20px. but I just cant get it to word smoothly together.
Can anyone maybe help me figure out what is going wrong and how I can fix this? Thanks!
let canvas = document.getElementById("canvas");
let movespeedX = 2;
let movespeedY = 0;
var canvasContext = canvas.getContext('2d');
//newest cord
let locationX = 20;
let locationY = 20;
//cords up to snakelength
let cords = [
{X: 5, Y: 5}
];
let snakeLength = 5;
//food location
let foodX;
let foodY;
let isfood = false;
//onload draw, move and set food
window.onload = function() {
setInterval(callField => {draw(); move(); food()}, 1000/60);
//keyboard controls
document.addEventListener('keydown', event => {
const key = event.key.toLowerCase();
if(key == "w" || key == "arrowup")
{
movespeedX = 0;
movespeedY = -2;
}
if(key == "s" || key == "arrowdown")
{
movespeedX = 0;
movespeedY = 2;
}
if(key == "a" || key == "arrowleft")
{
movespeedY = 0;
movespeedX = -2;
}
if(key == "d" || key == "arrowright")
{
movespeedY = 0;
movespeedX = 2;
}
});
}
function move()
{
//add movespeed to location to move all directions
locationX += movespeedX;
locationY += movespeedY;
//if a wall is hit restart
if(cords[0].X >= canvas.width || cords[0].X <= 0 || cords[0].Y >= canvas.height || cords[0].Y < 0)
{
restart();
}
//if food is hit with 20px margin (this is currently verry trippy and does not work)
if(foodY+20 > cords[0].X && foodY+20 > cords[0].Y)
{
isfood = false;
snakeLength += 5;
}
//update cords array with newest location
cords.unshift({X: locationX, Y: locationY});
if(cords.length > snakeLength)
{
delete cords[snakeLength];
}
}
function draw()
{
//draw canvas
drawRect(0,0,canvas.width,canvas.height,"black");
//draw food
drawCircle(foodX, foodY, 20, "red");
//draw snake
cords.forEach(element => {
drawCircle(element.X+20,element.Y,20,"white");
});
}
//reset to standard values
function restart()
{
locationX = 20;
locationY = 20;
movespeedX = 0;
movespeedY = 0;
snakeLength = 1;
cords = [
{X: 0, Y: 0}
];
}
//if the is no food, generate new cords and set food to true
function food()
{
if(isfood === false)
{
foodX = Math.floor(Math.random() * canvas.width) + 50;
foodY = Math.floor(Math.random() * canvas.height) + 50;
isfood = true;
}
}
function drawRect(leftX, topY, width, height, color)
{
canvasContext.fillStyle = color;
canvasContext.fillRect(leftX, topY, width, height);
}
function drawCircle(leftX,topY,radius,color)
{
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(leftX,topY,radius,0,Math.PI*2,true);
canvasContext.fill()
}```
Use Pythagorean theorem to find the distance of two objects then for circles account the radius plus any additional buffer you may want.
So something like
if (distance < objects.radius + other.objects.radius) {
return true
}
Here's your code
let canvas = document.getElementById("canvas");
let movespeedX = 2;
let movespeedY = 0;
var canvasContext = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;
//newest cord
let locationX = 20;
let locationY = 20;
//cords up to snakelength
let cords = [{ X: 5, Y: 5 }];
let snakeLength = 5;
//food location
let foodX;
let foodY;
let isfood = false;
//onload draw, move and set food
window.onload = function () {
setInterval((callField) => {
draw();
move();
food();
}, 1000 / 60);
//keyboard controls
document.addEventListener("keydown", (event) => {
const key = event.key.toLowerCase();
if (key == "w" || key == "arrowup") {
movespeedX = 0;
movespeedY = -2;
}
if (key == "s" || key == "arrowdown") {
movespeedX = 0;
movespeedY = 2;
}
if (key == "a" || key == "arrowleft") {
movespeedY = 0;
movespeedX = -2;
}
if (key == "d" || key == "arrowright") {
movespeedY = 0;
movespeedX = 2;
}
});
};
function move() {
//add movespeed to location to move all directions
locationX += movespeedX;
locationY += movespeedY;
//if a wall is hit restart
if (
cords[0].X >= canvas.width ||
cords[0].X <= 0 ||
cords[0].Y >= canvas.height ||
cords[0].Y < 0
) {
restart();
}
//if food is hit with 20px margin (this is currently verry trippy and does not work)
let dx = foodX - cords[0].X;
let dy = foodY - cords[0].Y;
let dist = Math.hypot(dx, dy);
if (dist < 60) {
isfood = false;
snakeLength += 5;
}
//update cords array with newest location
cords.unshift({ X: locationX, Y: locationY });
if (cords.length > snakeLength) {
delete cords[snakeLength];
}
}
function draw() {
//draw canvas
drawRect(0, 0, canvas.width, canvas.height, "black");
//draw food
drawCircle(foodX, foodY, 20, "red");
//draw snake
cords.forEach((element) => {
drawCircle(element.X + 20, element.Y, 20, "white");
});
}
//reset to standard values
function restart() {
locationX = 20;
locationY = 20;
movespeedX = 0;
movespeedY = 0;
snakeLength = 1;
cords = [{ X: 0, Y: 0 }];
}
//if the is no food, generate new cords and set food to true
function food() {
if (isfood === false) {
foodX = Math.floor(Math.random() * canvas.width) + 50;
foodY = Math.floor(Math.random() * canvas.height) + 50;
isfood = true;
}
}
function drawRect(leftX, topY, width, height, color) {
canvasContext.fillStyle = color;
canvasContext.fillRect(leftX, topY, width, height);
}
function drawCircle(leftX, topY, radius, color) {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(leftX, topY, radius, 0, Math.PI * 2, true);
canvasContext.fill();
}
<canvas id="canvas"></canvas>
I am trying to make a javascript game, got some codes online and I am trying to refine it to what I want, so I want the interval to be faster after score gets to 1000. I have tried all my possible best to dig into this, please kindly help me to refine my code. The start function takes an interval of 20 miliseconds in the gamearea function. I do a count of scores, then when the scores get to 1000, i want to increase the interval by settiing it in the updateGame function
function startGame() {
myGameArea = new gamearea();
myGamePiece = new component(30, 30, "red", 10, 75);
myscore = new component("15px", "Consolas", "black", 220, 25, "text");
myGameArea.start();
}
function gamearea() {
this.canvas = document.createElement("canvas");
this.canvas.width = 320;
this.canvas.height = 180;
document.getElementById("canvascontainer").appendChild(this.canvas);
this.context = this.canvas.getContext("2d");
this.pause = false;
this.frameNo = 0;
this.start = function() {
this.interval = setInterval(updateGameArea, 20);
}
this.stop = function() {
clearInterval(this.interval);
this.pause = true;
}
this.clear = function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
if (type == "text") {
this.text = color;
}
this.score = 0; this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function updateGameArea() {
var x, y, min, max, height, gap;
for (i = 0; i < myObstacles.length; i += 1) {
if (myGamePiece.crashWith(myObstacles[i])) {
myGameArea.stop();
document.getElementById("myfilter").style.display = "block";
document.getElementById("myrestartbutton").style.display = "block";
return;
}
}
if (myGameArea.pause == false) {
myGameArea.clear();
myGameArea.frameNo += 1;
myscore.score +=1;
if (myGameArea.frameNo == 1 || everyinterval(150)) {
x = myGameArea.canvas.width;
y = myGameArea.canvas.height - 100;
min = 20;
max = 100;
height = Math.floor(Math.random()*(max-min+1)+min);
min = 50;
max = 100;
gap = Math.floor(Math.random()*(max-min+1)+min);
myObstacles.push(new component(10, height, "green", x, 0));
myObstacles.push(new component(10, x - height - gap, "green", x, height + gap));
}
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].x += -1;
myObstacles[i].update();
}
myscore.text="SCORE: " + myscore.score;
if (myscore.score == 1000){
this.start = function() {
this.interval = setInterval(updateGameArea, 10);
}
}
myscore.update();
myGamePiece.x += myGamePiece.speedX;
myGamePiece.y += myGamePiece.speedY;
myGamePiece.update();
}
}
startGame();
You need to explicitly stop the old interval running and start a new one.
if (myscore.score == 1000){
this.stop(); // Stop the old interval
// Start a new interval with the new timing
this.interval = setInterval(updateGameArea, 10);
}
I'm writing simple "snake" game and I'm facing this issue:
every tame my snake hits the red circle (apple) , apple should be moved to a new location on the canvas. Right now new apple appears, but the old one does not disappear ( it should) , and also when there are more than 2 apples on the canvas they create a filled figure... it looks like this: ibb.co/nrYdLQ (also shouldn't happen).
The code responsible for moving an apple is this:
if (!this.objectCollide(myApple)) {
this.segments.pop();
} else {
myApple = new block(Math.floor(Math.random() * gameField.width),Math.floor(Math.random() * gameField.height))
};
and I have no idea why It's working like I described above, instead just moving an apple to a new location and removing old one.
Please help.
JSFiddle: https://jsfiddle.net/e1ga0fpm/
full JavaScript code:
var gameField = document.getElementById('gameField');
var ctx = gameField.getContext("2d");
var blockSize = 10;
columnCt = gameField.width / blockSize;
rowsCt = gameField.height / blockSize;
var block = function(x, y) {
this.x = x;
this.y = y;
}
block.prototype.drawBlock = function() {
ctx.fillStyle = "blue";
ctx.fillRect(this.x * blockSize, this.y * blockSize, blockSize,
blockSize);
};
block.prototype.drawApple = function() {
ctx.fillStyle = "red";
ctx.textBaseline = "bottom";
ctx.arc(this.x, this.y, 6, 2 * Math.PI, false);
ctx.fill();
}
var Snake = function() {
this.segments = [new block(20, 20), new block(19, 20), new block(18, 20), new block(17, 20),
new block(16, 20), new block(15, 20), new block(14, 20), new block(13, 20), new block(12, 20),
new block(11, 20), new block(10, 20)
];
this.direction = "right";
}
Snake.prototype.drawSnake = function() {
for (i = 0; i < this.segments.length; i++) {
this.segments[i].drawBlock();
}
}
Snake.prototype.setDirection = function(dir) {
if (this.direction == "left" && dir == "right" || this.direction == "right" && dir == "left" || this.direction == "up" && dir == "down" ||
this.direction == "down" && dir == "up") {
return
} else {
this.direction = dir;
};
};
Snake.prototype.objectCollide = function(obj) {
if (this.segments[0].x == Math.round(obj.x / blockSize) && this.segments[0].y == Math.round(obj.y / blockSize)) {
return true
} else {
return false
}
};
Snake.prototype.move = function() {
var head = this.segments[0];
var newHead;
switch (this.direction) {
case "right":
newHead = new block(head.x + 1, head.y);
break;
case "left":
newHead = new block(head.x - 1, head.y)
break;
case "down":
newHead = new block(head.x, head.y + 1)
break;
case "up":
newHead = new block(head.x, head.y - 1)
break;
}
this.segments.unshift(newHead);
if (!this.objectCollide(myApple)) {
this.segments.pop();
} else {
myApple = new block(Math.floor(Math.random() * gameField.width),Math.floor(Math.random() * gameField.height))
};
var collision = newHead.x >= columnCt || newHead.x <= -1 ||
newHead.y >= rowsCt || newHead.y <= -1;
for (i = 1; i < this.segments.length; i++) {
if (this.segments[i].x == newHead.x && this.segments[i].y == newHead.y) {
collision = true;
break;
};
};
if (collision) {
clearInterval(myFun);
};
};
var mySnake = new Snake()
mySnake.drawSnake();
var myApple = new block(Math.floor(Math.random() * gameField.width),
Math.floor(Math.random() * gameField.height));
var myFun = setInterval(function() {
ctx.clearRect(0, 0, gameField.width, gameField.height);
mySnake.move();
mySnake.drawSnake();
myApple.drawApple();
}, 100)
var directions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
document.onkeydown = function(event) {
var newDirection = directions[event.keyCode]
if (newDirection != undefined) {
mySnake.setDirection(newDirection);
};
Im quite unshure why the apple is not "eaten" however, i might know why it looks so weird:
If you draw to a canvas it looks like a pen. So whenever you draw a new apple, the pen moves to that position, and draws a line. After a few apples, if you call .fill(), this (yet invisible) line, gets filled. So you need to move the pen before you draw:
block.prototype.drawApple = function() {
ctx.fillStyle = "red";
ctx.textBaseline = "bottom";
ctx.moveTo(this.x,this.y);
ctx.arc(this.x, this.y, 6, 2 * Math.PI, false);
ctx.fill();
}
You forgot to beginpath while you draw apple. Also when apple eaten, you have to add new block to snake. Check edited code below.
Here is updated fiddle
block.prototype.drawApple = function() {
ctx.fillStyle = "red";
ctx.textBaseline = "bottom";
ctx.beginPath();
ctx.arc(this.x, this.y, 6, 2 * Math.PI, false);
ctx.fill();
}
var gameField = document.getElementById('gameField');
var ctx = gameField.getContext("2d");
var blockSize = 10;
columnCt = gameField.width / blockSize;
rowsCt = gameField.height / blockSize;
var block = function(x, y) {
this.x = x;
this.y = y;
}
block.prototype.drawBlock = function() {
ctx.fillStyle = "blue";
ctx.fillRect(this.x * blockSize, this.y * blockSize, blockSize,
blockSize);
};
block.prototype.drawApple = function() {
ctx.fillStyle = "red";
ctx.textBaseline = "bottom";
ctx.beginPath();
ctx.arc(this.x, this.y, 6, 2 * Math.PI, false);
ctx.fill();
}
var Snake = function() {
this.segments = [new block(20, 20), new block(19, 20), new block(18, 20), new block(17, 20),
new block(16, 20), new block(15, 20)
];
this.direction = "right";
}
Snake.prototype.drawSnake = function() {
for (i = 0; i < this.segments.length; i++) {
this.segments[i].drawBlock();
}
}
Snake.prototype.setDirection = function(dir) {
if (this.direction == "left" && dir == "right" || this.direction == "right" && dir == "left" || this.direction == "up" && dir == "down" ||
this.direction == "down" && dir == "up") {
return
} else {
this.direction = dir;
};
};
Snake.prototype.objectCollide = function(obj) {
if (this.segments[0].x == Math.round(obj.x / blockSize) && this.segments[0].y == Math.round(obj.y / blockSize)) {
return true
} else {
return false
}
};
Snake.prototype.move = function() {
var head = this.segments[0];
var newHead;
switch (this.direction) {
case "right":
newHead = new block(head.x + 1, head.y);
break;
case "left":
newHead = new block(head.x - 1, head.y)
break;
case "down":
newHead = new block(head.x, head.y + 1)
break;
case "up":
newHead = new block(head.x, head.y - 1)
break;
}
this.segments.unshift(newHead);
if (!this.objectCollide(myApple)) {
this.segments.pop();
} else {
myApple = new block(Math.floor(Math.random() * gameField.width), Math.floor(Math.random() * gameField.height));
this.segments.push(new block(this.segments[0][0], 20))
};
var collision = newHead.x >= columnCt || newHead.x <= -1 ||
newHead.y >= rowsCt || newHead.y <= -1;
for (i = 1; i < this.segments.length; i++) {
if (this.segments[i].x == newHead.x && this.segments[i].y == newHead.y) {
collision = true;
break;
};
};
if (collision) {
clearInterval(myFun);
};
};
var mySnake = new Snake()
mySnake.drawSnake();
var myApple = new block(Math.floor(Math.random() * gameField.width),
Math.floor(Math.random() * gameField.height));
var myFun = setInterval(function() {
ctx.clearRect(0, 0, gameField.width, gameField.height);
mySnake.move();
mySnake.drawSnake();
myApple.drawApple();
}, 100)
var directions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
document.onkeydown = function(event) {
var newDirection = directions[event.keyCode]
if (newDirection != undefined) {
mySnake.setDirection(newDirection);
};
};
canvas {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
border: 5px solid grey;
}
<canvas id="gameField" height="500" width="500">
</canvas>
Here is the JavaScript for a catcher game I’m making. Some of the code was given, which is why I quite fully don’t understand how to do certain things. Right now, I’m have different faller objects that are basically red rectangles that vary in height and width. What I’m trying to do is make it so that the faller objects randomize between red and blue (blue showing up less) but I’m extremely confused as how to do so. I tried making it so that the colors added to game.fillstyle were randomized prior, but that doesn’t seem to be working. Any help or advice is greatly appreciated–doesn’t have to be an answer. I’m just looking to figure this out.
Also, if I should put all of the code in please let me know.
Here is the JSfiddle : https://jsfiddle.net/ianlizzo/4dLr48v0/7/#&togetherjs=irGLk3uxOE
(() => {
let canvas = document.getElementById("game");
let game = canvas.getContext("2d");
let lastTimestamp = 0;
const FRAME_RATE = 60;
const FRAME_DURATION = 1000 / FRAME_RATE;
let fallers = [];
let score = 0;
let colourValues = ["red", "blue"]
colourValues = {
red: "#ff0000",
blue: "#0000ff"
};
let colour = colourValues[Math.floor(Math.random()*colourValues.length)];
//ignore
//let scoreCount = document.getElementById("scoreCount”);
let showScore = function(){
scoreCount.innerHTML = "Your score is " + score;
};
let addScore = function(pointValue){
score += pointValue;
showScore();
};
let fallerIn = function(inside){
inside.captured = true;
addScore(inside.pointValue);
};
const DEFAULT_DESCENT = 0.0001; // This is per millisecond.
let Faller = function (x, y, width, height, dx = 0, dy = 0, ax = 0, ay = DEFAULT_DESCENT) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.captured = false;
this.pointValue = 5;
this.colour;
// Velocity.
this.dx = dx;
this.dy = dy;
// Acceleration.
this.ax = ax;
this.ay = ay;
};
Faller.prototype.draw = function () {
game.fillStyle = colour;
game.fillRect(this.x, this.y, this.width, this.height);
};
Faller.prototype.move = function (millisecondsElapsed) {
this.x += this.dx * millisecondsElapsed;
this.y += this.dy * millisecondsElapsed;
this.dx += this.ax * millisecondsElapsed;
this.dy += this.ay * millisecondsElapsed;
};
const DEFAULT_PLAYER_WIDTH = 65;
const DEFAULT_PLAYER_HEIGHT = 45;
const DEFAULT_PLAYER_Y = canvas.height - DEFAULT_PLAYER_HEIGHT;
let Player = function (x, y = DEFAULT_PLAYER_Y, width = DEFAULT_PLAYER_WIDTH, height = DEFAULT_PLAYER_HEIGHT) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
};
Player.prototype.draw = function () {
let grd = game.createLinearGradient(0, 200, 200, 0);
grd.addColorStop(0, "black");
grd.addColorStop(0.5, "red");
grd.addColorStop(1, "white");
game.fillStyle = grd;
game.fillRect(this.x, this.y, this.width, this.height);
game.fill();
};
let player = new Player(canvas.width / 2);
let draw = (millisecondsElapsed) => {
game.clearRect(0, 0, canvas.width, canvas.height);
fallers.forEach((faller) => {
faller.draw();
faller.move(millisecondsElapsed);
if (!(faller.captured)&&
faller.y + faller.height > canvas.height &&
faller.x + faller.width < player.x + player.width &&
faller.x > player.x){
fallerIn(faller);
}
});
player.draw();
fallers = fallers.filter((faller) => {
return faller.y < canvas.height;
});
};
const MIN_WIDTH = 10;
const WIDTH_RANGE = 20;
const MIN_HEIGHT = 10;
const HEIGHT_RANGE = 20;
const MILLISECONDS_BETWEEN_FALLERS = 750;
let fallerGenerator;
let startFallerGenerator = () => {
fallerGenerator = setInterval(() => {
let fallerWidth = Math.floor(Math.random() * WIDTH_RANGE) + MIN_WIDTH;
fallers.push(new Faller(
Math.floor(Math.random() * (canvas.width - fallerWidth)), 0,
fallerWidth, Math.floor(Math.random() * HEIGHT_RANGE) + MIN_HEIGHT
));
}, MILLISECONDS_BETWEEN_FALLERS);
};
let stopFallerGenerator = () => clearInterval(fallerGenerator);
let setPlayerPositionBasedOnMouse = (event) => {
player.x = event.clientX / document.body.clientWidth * canvas.width;
};
document.body.addEventListener("mouseenter", setPlayerPositionBasedOnMouse);
document.body.addEventListener("mousemove", setPlayerPositionBasedOnMouse);
let running = false;
let nextFrame = (timestamp) => {
if (!lastTimestamp) {
lastTimestamp = timestamp;
}
if (timestamp - lastTimestamp < FRAME_DURATION) {
if (running) {
window.requestAnimationFrame(nextFrame);
}
return;
}
draw(timestamp - lastTimestamp);
lastTimestamp = timestamp;
if (running) {
window.requestAnimationFrame(nextFrame);
}
};
document.getElementById("start-button").addEventListener("click", () => {
running = true;
lastTimestamp = 0;
startFallerGenerator();
window.requestAnimationFrame(nextFrame);
});
document.getElementById("stop-button").addEventListener("click", () => {
stopFallerGenerator();
running = false;
});
})();
let colourValues = ["red", "blue"]
/* colourValues.length will be undefined for object.
colourValues = {
red: "#ff0000",
blue: "#0000ff"
};*/
let colour = colourValues[Math.floor(Math.random()*colourValues.length)];
See this fiddle
Random color generator should generate red for 75% times.
Faller.prototype.randomColour = function() {
return colourValues[Math.floor(Math.random() * colourValues.length * 0.75)];
};
Faller should use its own color to fill
Faller.prototype.draw = function() {
game.fillStyle = this.colour;
game.fillRect(this.x, this.y, this.width, this.height);
};
which was assigned in Faller constructor.
this.colour = this.randomColour();
I couldn't figure out how to set ES6 in jsFiddle. So it is done in ES5
let colourValues = ["red", "blue", "red", "red"];
game.fillStyle = colourValues[Math.floor(Math.random()*colourValues.length)];
plnkr http://plnkr.co/edit/uY5hm8Pkaoklfr6Tikrd?p=preview