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>
Related
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>
I'm creating a game, and I need to draw) some elements at the top of the <canvas>, but the more elements appear, the more lag the page itself. I found this example where a lot of circles appear, but everything works fine - JSFiddle. Can someone tell me how to optimize my case?
"use strict";
/*Determing canvas*/
window.onload = () => {
const canvas = document.getElementById("canvas"),
ctx = canvas.getContext('2d'),
endTitle = document.getElementById('gameover');
let spawnRate = 300,
lastspawn = -1;
class Wall {
/*Getting values*/
constructor(x, y, width, height, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
}
/*Draw rectangle*/
draw() {
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(this.x, this.y, this.width, this.height)
}
}
/*Making walls*/
let walls = [];
/*Spawn walls endlessly*/
function spawnWalls() {
const wall_x = Math.floor(Math.random() * (canvas.width - 20)) + 10
const wall_y = 0
for (let i = 0; i < 200; i++) {
walls.push(new Wall(wall_x, wall_y, 10, 10, 10))
}
}
/*Update game*/
function refresh() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let time = Date.now()
if (time > (lastspawn + spawnRate)) {
lastspawn = time;
spawnWalls();
spawnRate -= 10;
}
walls.forEach(wall => wall.draw())
for (let j of walls) {
j.y += j.speed;
j.draw();
};
};
let interval = setInterval(refresh, 50);
Walls is too big.
It looks like you're never removing old walls, so they are continuing to be drawn well after they have been removed from the canvas. In your Refresh function, check if the wall has passed the canvas size and if so, remove that from the walls array.
EDIT:
I've added your 'remove' code from the comment.
Another easy win is to stop using new Date() because of who knows what, probably time zones and localization, dates are very expensive to instantiate. However modern browsers offer a performance API, and that can tell you the time since page load, which seems to have a substantial improvement on your existing performance.
"use strict";
/*Determing canvas*/
window.onload = () => {
const canvas = document.getElementById("canvas"),
ctx = canvas.getContext('2d'),
endTitle = document.getElementById('gameover');
let spawnRate = 300,
lastspawn = -1;
endTitle.style.display = "none";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
/*Classes*/
class Player {
/*Get player info*/
constructor(x, y, width, height, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
}
/*Draw player*/
draw() {
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(this.x, this.y, this.width, this.height);
}
/*Move player*/
move() {
}
};
class Wall {
/*Getting values*/
constructor(x, y, width, height, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
}
/*Draw rectangle*/
draw() {
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(this.x, this.y, this.width, this.height)
}
}
/*Defining players*/
let player_01 = new Player(20, 70, 20, 20, 10);
let player_02 = new Player(50, 500, 20, 20, 10);
let players = [];
players.push(player_01);
/*Making walls*/
let walls = [];
/*Spawn Walls for infinity*/
function spawnWalls() {
const wall_x = Math.floor(Math.random() * (canvas.width - 20)) + 10
const wall_y = 0
for (let i = 0; i < 200; i++) {
walls.push(new Wall(wall_x, wall_y, 10, 10, 10))
}
}
/*Update game*/
function refresh() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let time = performance.now()
if (time > (lastspawn + spawnRate)) {
lastspawn = time;
spawnWalls();
spawnRate -= 10;
}
walls.forEach(wall => wall.draw())
outOfWindow()
for (let i of players) {
i.draw();
};
for (let j of walls) {
if (j.y > canvas.height) {
walls.shift(j)
}
j.y += j.speed;
j.draw();
if (player_01.height + player_01.y > j.y && j.height + j.y > player_01.y && player_01.width + player_01.x > j.x && j.width + j.x > player_01.x) {
clearInterval(interval);
endTitle.style.display = "flex";
};
};
};
let interval = setInterval(refresh, 50);
/*Move players on keypress*/
for (let i of players) {
window.addEventListener("keydown", (event) => {
let key = event.key.toLowerCase();
if (key == "w") i.y -= i.speed;
else if (key == "s") i.y += i.speed;
else if (key == "a") i.x -= i.speed;
else if (key == "d") i.x += i.speed;
})
}
/*Check if player out of the window*/
function outOfWindow() {
for (let i of players) {
if (i.x < 0) i.x = 0;
else if (i.x + i.width > canvas.width) i.x = canvas.width - i.width;
else if (i.y < 0) i.y = 0;
else if (i.y + i.height > canvas.height) i.y = canvas.height - i.height;
}
}
}
#gameover {
position: absolute;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: rgba(0, 0, 0, .5);
}
<div id="gameover">
<h2>The Game Is Over</h2>
<button onclick="restart()">Try again!</button>
</div>
<canvas id="canvas"></canvas>
I'm trying to get the character to rotate the character object based on where the mouse is.
So far I got it to rotate incrementally without mouse position. I was checking if it effected my zombie's chasing capabilities.
My script
let player, zombie, mouseX, mouseY;;
let bgCanvas = document.getElementById('backgroundCan');
function startGame() {
document.getElementById("startScreen").style.display = "none";
player = new playerComponent(350, 220);
zombie = new zombieComponent(750, 220);
gameArea.start();
}
let gameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 800;
this.canvas.height = 500;
this.canvas.style = "position: absolute";
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[2]);
this.interval = setInterval(updateArea, 20);
window.addEventListener('keydown', function (e) {
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
gameArea.keys[e.keyCode] = false;
});
this.canvas.addEventListener("mousemove", function(e){
mouseX = e.clientX - ctx.canvas.offsetLeft;
mouseY = e.clientY - ctx.canvas.offsetTop;
});
this.canvas.addEventListener("mousedown", function(e){
let gShot = new Audio('assets/shot.mp3');
gShot.play();
var mX = e.clientX - ctx.canvas.offsetLeft;
var mY = e.clientY - ctx.canvas.offsetTop;
if(mX >= zombie.x && mX < zombie.x+zombie.w && mY >= zombie.y && mY < zombie.y+zombie.h){
if(zombie.health > 0){
zombie.health += -1;
zombie.speedX += 10;
zombie.newPos();
zombie.update();
}
else {
zombie.status = "dead";
}
}
});
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function playerComponent(x, y){
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
this.health = 10;
this.status = "alive";
let rotNum = 1;
this.update = function(){
ctx = gameArea.context;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(rotNum * Math.PI / 180);
playerSprite = new Image();
playerSprite.src = "assets/playerGun.png";
ctx.drawImage(playerSprite, 0, 0);
ctx.restore();
rotNum++;
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function updateArea() {
gameArea.clear();
if(player.status == "alive"){
player.speedX = 0;
player.speedY = 0;
if (gameArea.keys && gameArea.keys[65]) {
if(player.x > 20){
player.speedX = -3;
}
}
if (gameArea.keys && gameArea.keys[68]) {
if(player.x < 740){
player.speedX = 3;
}
}
if (gameArea.keys && gameArea.keys[87]) {
if(player.y > 20){
player.speedY = -3;
}
}
if (gameArea.keys && gameArea.keys[83]) {
if(player.y < 445){
player.speedY = 3;
}
}
player.newPos();
player.update();
}
if(zombie.status == "alive"){
if(zombie.x > player.x){
zombie.speedX = -1;
}
else{
zombie.speedX = 1;
}
if(zombie.y > player.y){
zombie.speedY = -1;
}
else{
zombie.speedY = 1;
}
zombie.newPos();
zombie.update();
}
else{
zombie.update();
}
}
So far, I have the mouse position on the canvas and am able to rotate the character, but I just don't how to connect the two. How should use the mouse position and the character position to rotate towards the mouse? The character is initially facing right (i think?), at least the sprite initially is.
Here's an image illustrating the situation:
You have the mouseY, playerY and mouseX, playerX
Therefore you can calculate the height and base of the triangle,
Thus the angle with
However, since in the second and third quadrants y/x will return an angle in the first and fourth quadrants, you need to use the Math.atan2(y,x) function in Javascript, not Math.atan(y/x). This will give you an angle between -180 and 180 instead of between -90 and 90.
Atan Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
Then all you have to do is rotate based on the angle!
(P.S. Remember that you will have to convert between radians and degrees)
I am getting errors when I used instance mode in my code. My mini game isn't showing up I am not sure where the error is coming from. It works fine without the instance mode but I need to use the instance mode so that I can reference this code in another file. I'm using this link as a reference. http://p5js.org/examples/instance-mode-instantiation.html
let sketch = function(p) {
let blob;
let blobs = [];
let zoom = 1;
let timer = 20;
let hits = false;
let score = 0;
p.setup = function() {
p.createCanvas(600, 600);
blob = new Blob(0, 0, 64);
for (let i = 0; i < 300; i++) {
let x = p.random(-p.width,p.width);
let y = p.random(-p.height,p.height);
blobs[i] = new Blob(x, y, 15);
}
};
p.draw = function() {
p.background(0);
p.translate(width/2, height/2);
let newzoom = 64 / blob.r;
p.zoom = p.lerp(zoom, newzoom, 0.1);
p.scale(zoom);
p.translate(-blob.pos.x, -blob.pos.y);
for (var i = blobs.length-1; i >=0; i--) {
blobs[i].show();
if (blob.eats(blobs[i])) {
blobs.splice(i, 1);
}
}
if (frameCount % 60 == 0 && timer > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
p.timer --;
}
if (timer == 0 && score >=250) {
p.text("You Win", 0, 0);
p.noLoop();
}
if (blob.eats){
p.console.log("hit");
}
if (timer == 0 && score <= 250){
p.text("You Lose", 0, 0);
p.textSize(200);
p.noLoop();
}
blob.show();
blob.update();
};
};
let myp5 = new p5(sketch);
class Blob {
constructor(x, y, r) {
this.pos = createVector(x, y);
this.r = r;
this.vel = createVector(0,0);
}
show(p) {
p.ellipse(this.pos.x, this.pos.y, this.r, this.r);
}
eats(other) {
let d = p5.Vector.dist(this.pos, other.pos);
if (d < this.r + other.r) {
let sum = PI * this.r * this.r + PI * other.r * other.r;
score ++;
this.r = sqrt(sum / PI);
//this.r += other.r;
return true;
} else {
return false;
}
}
While width, height and frameCount are properties of the canvas, console is it not.
p.translate(width/2, height/2);
p.translate(p.width/2, p.height/2);
if (frameCount % 60 == 0 && timer > 0) {
if (p.frameCount % 60 == 0 && timer > 0) {
console.log("hit");
console.log("hit");
I don't know the implementation of Blob. but you have to passe the canvas object (p) to the show method and the let constructor.
The variable score can't be accessed in the class Blob
See the example, where I applied the suggested changes to your original code:
class Blob {
constructor(p, x, y, r) {
this.pos = p.createVector(x, y);
this.r = r;
this.vel = p.createVector(0,0);
}
show(p) {
p.ellipse(this.pos.x, this.pos.y, this.r, this.r);
}
eats(other) {
let d = this.pos.dist(other.pos);
if (d < this.r + other.r) {
let sum = Math.pi * this.r * this.r + Math.pi * other.r * other.r;
this.r = Math.sqrt(sum / Math.pi);
//this.r += other.r;
return true;
} else {
return false;
}
}
update() {
// ...
}
}
let sketch = function(p) {
let blob;
let blobs = [];
let zoom = 1;
let timer = 20;
let hits = false;
let score = 0;
p.setup = function() {
p.createCanvas(600, 600);
blob = new Blob(p, 0, 0, 64);
for (let i = 0; i < 300; i++) {
let x = p.random(-p.width,p.width);
let y = p.random(-p.height,p.height);
blobs[i] = new Blob(p, x, y, 15);
}
};
p.draw = function() {
p.background(0);
p.translate(p.width/2, p.height/2);
let newzoom = 64 / blob.r;
p.zoom = p.lerp(zoom, newzoom, 0.1);
p.scale(zoom);
p.translate(-blob.pos.x, -blob.pos.y);
for (var i = blobs.length-1; i >=0; i--) {
blobs[i].show(p);
if (blob.eats(blobs[i])) {
score ++;
blobs.splice(i, 1);
}
}
if (p.frameCount % 60 == 0 && timer > 0) {
p.timer --;
}
if (timer == 0 && score >=250) {
p.text("You Win", 0, 0);
p.noLoop();
}
if (blob.eats){
console.log("hit");
}
if (timer == 0 && score <= 250){
p.text("You Lose", 0, 0);
p.textSize(200);
p.noLoop();
}
blob.show(p);
blob.update();
};
};
let myp5 = new p5(sketch)
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>
I know you can fillRect right? And you can clearRect. But what happens if there's an animation and you have to remove an object although it would be redrawn from setInterval. How would you remove the fillRect?
Here's an example:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var circle = function (x, y, radius, fillCircle, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fillCircle) {
ctx.fill();
} else {
ctx.stroke();
}
};
var drawRect = function (x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 20, 20)
}
var Object = function (xPos, yPos) {
this.x = xPos;
this.y = yPos;
}
// The Ball constructor
var Ball = function () {
this.x = width / 2;
this.y = height / 2;
this.xSpeed = 0;
this.ySpeed = 0;
this.radius = 10;
};
// Update the ball's position based on its speed
Ball.prototype.move = function () {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x < 11) {
this.x = 11;
} else if (this.x > width - 11) {
this.x = width - 11;
} else if (this.y < 11) {
this.y = 11;
} else if (this.y > height - 11) {
this.y = height - 11;
}
};
// Draw the ball at its current position
Ball.prototype.draw = function () {
circle(this.x, this.y, 10, true, "Black");
};
Object.prototype.draw = function () {
drawRect(this.x, this.y, "Black")
}
Object.prototype.drawKey = function (color) {
drawRect(this.x, this.y, "Yellow")
}
Object.prototype.checkCollision = function (direction) {
return (ball.x-ball.radius < this.x + 20)
&& (ball.x+ball.radius > this.x)
&& (ball.y-ball.radius < this.y + 20)
&& (ball.y+ball.radius > this.y)
;
}
function draw() {
ctx.clearRect(0, 0, width, height);
ball.draw();
object1.draw("Blue");
object2.draw();
object3.draw();
object4.draw();
object5.draw();
key.drawKey();
ctx.strokeRect(0, 0, width, height);
}
function simulate() {
for (z = 0; z < 5; z++) {
var prev_ball_x = ball.x;
var prev_ball_y = ball.y;
ball.move();
// handle collision here
if (object1.checkCollision() || object2.checkCollision() || object3.checkCollision() || object4.checkCollision() || object5.checkCollision()) {
ball.setDirection('stop');
// reset ball's position so they do not overlap
ball.x = prev_ball_x;
ball.y = prev_ball_y;
}
if (key.checkCollision()) {
ball.x = prev_ball_x;
ball.y = prev_ball_y;
}
}
$("body").keyup(function (event) {
ball.setDirection('stop');
})
}
setInterval(function () {
// separate drawing and simulating phases
simulate();
draw();
}, 30);
// Set the ball's direction based on a string
Ball.prototype.setDirection = function (direction) {
if (direction === "up") {
this.xSpeed = 0;
this.ySpeed = -1;
} else if (direction === "down") {
this.xSpeed = 0;
this.ySpeed = 1;
} else if (direction === "left") {
this.xSpeed = -1;
this.ySpeed = 0;
} else if (direction === "right") {
this.xSpeed = 1;
this.ySpeed = 0;
} else if (direction === "stop") {
this.xSpeed = 0;
this.ySpeed = 0;
}
};
// Create the ball object
var ball = new Ball();
var object1 = new Object(50, 0);
var object2 = new Object(50, 20);
var object3 = new Object(50, 40);
var object4 = new Object(50, 60);
var object5 = new Object(50, 80);
var key = new Object(70, 70);
// An object to convert keycodes into action names
var keyActions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
// The keydown handler that will be called for every keypress
$("body").keydown(function (event) {
var direction = keyActions[event.keyCode];
ball.setDirection(direction);
});
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id="canvas" width="800" height="200"></canvas>
You move around a ball with your arrow keys. When I collide with the yellow block, I want it to disappear. Using clearRect would not work simply because it would be redrawn in the setInterval. How would I make it disappear?
Generally when you have several items in a game you place them into a sort of objects array, then when you draw you loop through and call .draw() on each item. Doing it this way allows you to remove items you do not want (such as key), and as such it will no longer be drawn. In your case one thing we could do (assuming there is only a single key) is give your ball a hasKey property. And on collision set it from false to true. Then inside draw, if you wish to also remove the collisions you would do !ball.hasKey && key.checkCollision() inside your collision conditional for the key:
if(!ball.hasKey) key.drawKey();
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var circle = function (x, y, radius, fillCircle, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fillCircle) {
ctx.fill();
} else {
ctx.stroke();
}
};
var drawRect = function (x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 20, 20)
}
var Object = function (xPos, yPos) {
this.x = xPos;
this.y = yPos;
}
// The Ball constructor
var Ball = function () {
this.x = width / 2;
this.y = height / 2;
this.xSpeed = 0;
this.ySpeed = 0;
this.radius = 10;
this.hasKey = false;
};
// Update the ball's position based on its speed
Ball.prototype.move = function () {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x < 11) {
this.x = 11;
} else if (this.x > width - 11) {
this.x = width - 11;
} else if (this.y < 11) {
this.y = 11;
} else if (this.y > height - 11) {
this.y = height - 11;
}
};
// Draw the ball at its current position
Ball.prototype.draw = function () {
circle(this.x, this.y, 10, true, "Black");
};
Object.prototype.draw = function () {
drawRect(this.x, this.y, "Black")
}
Object.prototype.drawKey = function (color) {
drawRect(this.x, this.y, "Yellow")
}
Object.prototype.checkCollision = function (direction) {
return (ball.x-ball.radius < this.x + 20)
&& (ball.x+ball.radius > this.x)
&& (ball.y-ball.radius < this.y + 20)
&& (ball.y+ball.radius > this.y)
;
}
function draw() {
ctx.clearRect(0, 0, width, height);
ball.draw();
object1.draw("Blue");
object2.draw();
object3.draw();
object4.draw();
object5.draw();
if(!ball.hasKey) key.drawKey();
ctx.strokeRect(0, 0, width, height);
}
function simulate() {
for (z = 0; z < 5; z++) {
var prev_ball_x = ball.x;
var prev_ball_y = ball.y;
ball.move();
// handle collision here
if (object1.checkCollision() || object2.checkCollision() || object3.checkCollision() || object4.checkCollision() || object5.checkCollision()) {
ball.setDirection('stop');
// reset ball's position so they do not overlap
ball.x = prev_ball_x;
ball.y = prev_ball_y;
}
if (!ball.hasKey && key.checkCollision()) {
ball.x = prev_ball_x;
ball.y = prev_ball_y;
ball.hasKey = true;
}
}
$("body").keyup(function (event) {
ball.setDirection('stop');
})
}
setInterval(function () {
// separate drawing and simulating phases
simulate();
draw();
}, 30);
// Set the ball's direction based on a string
Ball.prototype.setDirection = function (direction) {
if (direction === "up") {
this.xSpeed = 0;
this.ySpeed = -1;
} else if (direction === "down") {
this.xSpeed = 0;
this.ySpeed = 1;
} else if (direction === "left") {
this.xSpeed = -1;
this.ySpeed = 0;
} else if (direction === "right") {
this.xSpeed = 1;
this.ySpeed = 0;
} else if (direction === "stop") {
this.xSpeed = 0;
this.ySpeed = 0;
}
};
// Create the ball object
var ball = new Ball();
var object1 = new Object(50, 0);
var object2 = new Object(50, 20);
var object3 = new Object(50, 40);
var object4 = new Object(50, 60);
var object5 = new Object(50, 80);
var key = new Object(70, 70);
// An object to convert keycodes into action names
var keyActions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
// The keydown handler that will be called for every keypress
$("body").keydown(function (event) {
var direction = keyActions[event.keyCode];
ball.setDirection(direction);
});
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id="canvas" width="800" height="200"></canvas>