I am creating a mini space ship game with a feel similar to escape velocity. I cannot seem to get inertia to work correctly.
I am also having problems with the turning functions. If I tap the left or right arrow keys then it does not follow anything similar to the circular type movement that should be happening.
$(function() {
var canvas = Raphael('game', 1000, 800);
var background = canvas.rect(0, 0, 1000, 800);
background.attr("fill", "black");
var ship = canvas.rect(200, 200, 10, 35);
ship.attr("fill", "red");
ship.angle = 0;
ship.turnrate = 4;
ship.maxSpeed = 2;
ship.acc = 0;
ship.accSpeed = 0.25;
ship.vel = [0,0];
var up = 0;
var left = 0;
var right = 0;
var speedx = 0;
var speedy = 0;
function moveShip() {
if (left == 1) {
ship.angle = (ship.angle - ship.turnrate) % 360;
}
if (right == 1) {
ship.angle = (ship.angle + ship.turnrate) % 360;
}
if (up == 1) {
if (ship.acc < ship.maxSpeed) {
ship.acc += ship.accSpeed;
}
if (ship.acc > ship.maxSpeed) {
ship.acc = ship.speed;
}
}
if (up == 0) {
if (ship.acc > 0) {
ship.acc -= ship.accSpeed;
}
if (ship.acc < 0) {
ship.acc = 0;
}
}
speedx = ship.vel[0] + ship.acc * Math.sin(ship.angle);
speedy = ship.vel[1] + ship.acc * Math.cos(ship.angle);
ship.vel = [speedx, speedy];
ship.transform("");
ship.rotate(ship.angle);
ship.attr({x: ship.vel[0], y: ship.vel[1]});
$("#stats").text("ship.angle: " + ship.angle
+ " vel[0]: " + ship.vel[0] + " vel[1]: " + ship.vel[1]
+ " ship.speed: " + ship.maxSpeed + " speedx: " + speedx + " speedy: " + speedy);
}
function keyPressed(evt) {
if (evt.keyCode == 38) {
up = 1;
}
if (evt.keyCode == 37) {
left = 1;
}
if (evt.keyCode == 39) {
right = 1;
}
}
function keyReleased(evt) {
if (evt.keyCode == 38) {
up = 0;
}
if (evt.keyCode == 37) {
left = 0;
}
if (evt.keyCode == 39) {
right = 0;
}
}
function gameloop() {
moveShip();
}
$(document).keydown(keyPressed);
$(document).keyup(keyReleased);
setInterval(gameloop, 30);
});
I have been searching stack overflow and the internet, but most questions out there are to do with the classic Space Invaders type game which does not involve turning or inertia.
Any help would be appreciated, I really just want to have a good understanding of what I am missing.
Are you sure you want to change the acceleration of a ship with the arrow keys? Remember, acceleration is the rate of change of the velocity. This means, if you have a constant acceleration then you velocity will start to grow and grow and grow. Pretty soon you will have a ship that is going way too fast.
The results from the chatroom, here for posterity:
$(function() {
var canvas = Raphael('game', 1000, 800);
var background = canvas.rect(0, 0, 1000, 800);
background.attr("fill", "black");
var ship = canvas.rect(200, 200, 10, 35);
ship.attr("fill", "red");
ship.angle = 0;
ship.turnrate = 4;
ship.maxSpeed = 0.25;
ship.acc = 0.25;
ship.vel = [0,0];
ship.pos = [500,400];
var up = 0;
var left = 0;
var right = 0;
var speedx = 0;
var speedy = 0;
function moveShip() {
if (left == 1) {
ship.angle = (ship.angle - ship.turnrate) % 360;
}
if (right == 1) {
ship.angle = (ship.angle + ship.turnrate) % 360;
}
if (up == 1) {
speedx = ship.vel[0] + ship.acc * Math.sin(ship.angle * Math.PI / 180);
speedy = ship.vel[1] - ship.acc * Math.cos(ship.angle * Math.PI / 180);
ship.vel = [speedx, speedy];
}
ship.pos = [ship.pos[0] + speedx, ship.pos[1] + speedy];
ship.transform("");
ship.rotate(ship.angle);
ship.attr({x: ship.pos[0], y: ship.pos[1]});
$("#stats").text("ship.angle: " + ship.angle
+ " vel[0]: " + ship.vel[0] + " vel[1]: " + ship.vel[1]
+ " ship.speed: " + ship.maxSpeed + " speedx: " + speedx + " speedy: " + speedy);
}
function keyPressed(evt) {
if (evt.keyCode == 38) {
up = 1;
}
if (evt.keyCode == 37) {
left = 1;
}
if (evt.keyCode == 39) {
right = 1;
}
}
function keyReleased(evt) {
if (evt.keyCode == 38) {
up = 0;
}
if (evt.keyCode == 37) {
left = 0;
}
if (evt.keyCode == 39) {
right = 0;
}
}
function gameloop() {
moveShip();
}
$(document).keydown(keyPressed);
$(document).keyup(keyReleased);
setInterval(gameloop, 30);
});
There were two bugs fixed. A pos attribute was added, so that velocity and position can be updated separately. Also, Math.sin and Math.cos take angles in radians, so we converted the angle for those functions.
You can play with the results: http://jsfiddle.net/mJcN7/8/
Related
I'm trying to make a program where the user moves with a circle using the keyboard to collect coins. What I tried so far is using an if statement to see if they collide and if they do, the user gets 1 coin and the image disappears. When I try this method, the code runs normally, but the collision doesn't happen.
/* Credit: https://openprocessing.org/sketch/525131
*/
var left, right, up, down;
var coin = [];
var player, posX, posY, radius, speed, colour, gameScreen, coins;
function setup() {
createCanvas(400, 400);
left = right = up = down = false;
gameScreen = "game";
coins = 0;
for (var i = 0; i < 10; i++) {
coin[i] = new Coin(random(width), random(height), 20, 20);
}
player = new Player();
}
function game() {
background(0);
if (left == true) {
player.moveLeft();
}
if (right == true) {
player.moveRight();
}
if (up == true) {
player.moveUp();
}
if (down == true) {
player.moveDown();
}
for (var i = 0; i < 10; i++) {
coin[i].display();
}
player.display();
//player.collision();
fill(255);
textSize(20);
text("Coins: " + coins, 10, 30);
}
function draw() {
game();
}
function keyPressed() {
// if (keyPressed) { For Processing add this line
if (keyCode == LEFT_ARROW || key == 'a') {
left = true;
}
if (keyCode == RIGHT_ARROW || key == 'd') {
right = true;
}
if (keyCode == UP_ARROW || key == 'w') {
up = true;
}
if (keyCode == DOWN_ARROW || key == 's') {
down = true;
}
// }
}
function keyReleased() {
if (keyCode == LEFT_ARROW || key == 'a') {
left = false;
}
if (keyCode == RIGHT_ARROW || key == 'd') {
right = false;
}
if (keyCode == UP_ARROW || key == 'w') {
up = false;
}
if (keyCode == DOWN_ARROW || key == 's') {
down = false;
}
}
function Coin(coinX, coinY, coinR) {
this.img = loadImage("https://www.paulwheeler.us/files/Coin120.png");
this.coinX = coinX;
this.coinY = coinY;
this.coinR = coinR;
this.display = function() {
image(this.img, this.coinX, this.coinY, this.coinR, this.coinR);
}
}
function Player() {
this.posX = width / 2;
this.posY = height / 2;
this.radius = 10;
this.speed = 4;
this.colour = "blue";
this.display = function() {
fill(this.colour);
ellipse(this.posX, this.posY, this.radius * 2, this.radius * 2);
}
this.moveLeft = function() {
// posX = posX - speed;
this.posX = constrain(this.posX - this.speed, this.radius, width - this.radius);
}
this.moveRight = function() {
// posX = posX + speed;
this.posX = constrain(this.posX + this.speed, this.radius, width - this.radius);
}
this.moveUp = function() {
// posY = posY-speed;
this.posY = constrain(this.posY - this.speed, this.radius, height - this.radius);
}
this.moveDown = function() {
// posY = posY+speed;
this.posY = constrain(this.posY + this.speed, this.radius, height - this.radius);
}
/*this.collision = function(){
if (this.posX > this.coinX && this.posX < this.coinX + this.coinR && this.posY > this.coinY && this.posY < this.coinY + this.coinR){
coins+=1;
//Coin disappears
}
}
*/
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
You can use dist() in p5 to determine the distance between the center of the two circles and then see if it smaller than the two radi combined. Once you have that you can execute whatever code is needed.
for (let i = 0; i < coin.length; i++) {
if (
dist(player.posX, player.posY, coin[i].coinX, coin[i].coinY) <
player.radius + coin[i].coinR
) {
coin.splice(i, 1);
coins++;
i--;
}
}
Snippet
/* Credit: https://openprocessing.org/sketch/525131
*/
var left, right, up, down;
var coin = [];
var player, posX, posY, radius, speed, colour, gameScreen, coins;
function setup() {
createCanvas(400, 400);
left = right = up = down = false;
gameScreen = "game";
coins = 0;
for (var i = 0; i < 10; i++) {
coin[i] = new Coin(random(400), random(400), 20);
}
player = new Player();
}
function game() {
background(0);
if (left == true) {
player.moveLeft();
}
if (right == true) {
player.moveRight();
}
if (up == true) {
player.moveUp();
}
if (down == true) {
player.moveDown();
}
//change to coin.length to prevent error
for (var i = 0; i < coin.length; i++) {
coin[i].display();
}
player.display();
//player.collision();
fill(255);
textSize(20);
text("Coins: " + coins, 10, 30);
for (let i = 0; i < coin.length; i++) {
if (
dist(player.posX, player.posY, coin[i].coinX, coin[i].coinY) <
player.radius + coin[i].coinR
) {
coin.splice(i, 1);
coins++;
i--;
}
}
}
function draw() {
game();
}
function keyPressed() {
// if (keyPressed) { For Processing add this line
if (keyCode == LEFT_ARROW || key == "a") {
left = true;
}
if (keyCode == RIGHT_ARROW || key == "d") {
right = true;
}
if (keyCode == UP_ARROW || key == "w") {
up = true;
}
if (keyCode == DOWN_ARROW || key == "s") {
down = true;
}
// }
}
function keyReleased() {
if (keyCode == LEFT_ARROW || key == "a") {
left = false;
}
if (keyCode == RIGHT_ARROW || key == "d") {
right = false;
}
if (keyCode == UP_ARROW || key == "w") {
up = false;
}
if (keyCode == DOWN_ARROW || key == "s") {
down = false;
}
}
function Coin(coinX, coinY, coinR) {
/*this.img = loadImage(
"https://www.searchpng.com/wp-content/uploads/2019/04/Game-Coins-PNG-jpeg-715x715.png"
);*/
this.coinX = coinX;
this.coinY = coinY;
this.coinR = coinR;
this.colour = "yellow";
this.display = function () {
fill(this.colour);
ellipse(this.coinX, this.coinY, this.coinR * 2, this.coinR * 2);
};
}
function Player() {
this.posX = width / 2;
this.posY = height / 2;
this.radius = 10;
this.speed = 4;
this.colour = "blue";
this.display = function () {
fill(this.colour);
ellipse(this.posX, this.posY, this.radius * 2, this.radius * 2);
};
this.moveLeft = function () {
// posX = posX - speed;
this.posX = constrain(
this.posX - this.speed,
this.radius,
width - this.radius
);
};
this.moveRight = function () {
// posX = posX + speed;
this.posX = constrain(
this.posX + this.speed,
this.radius,
width - this.radius
);
};
this.moveUp = function () {
// posY = posY-speed;
this.posY = constrain(
this.posY - this.speed,
this.radius,
height - this.radius
);
};
this.moveDown = function () {
// posY = posY+speed;
this.posY = constrain(
this.posY + this.speed,
this.radius,
height - this.radius
);
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js" integrity="sha512-N4kV7GkNv7QR7RX9YF/olywyIgIwNvfEe2nZtfyj73HdjCUkAfOBDbcuJ/cTaN04JKRnw1YG1wnUyNKMsNgg3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Your collision function was trying to access this.coinX, this.coinY, and this.coinR but it was defined on Player which doesn't have any of those properties. You need to use a loop to check each coin for collision:
this.collision = function() {
for (let ix = 0; ix < coin.length; ix++) {
const currentCoin = coin[ix];
if (this.posX > currentCoin.coinX &&
this.posX < currentCoin.coinX + currentCoin.coinR &&
this.posY > currentCoin.coinY &&
this.posY < currentCoin.coinY + currentCoin.coinR) {
coins += 1;
// Coin disappears:
// delete the current coin from the array
coin.splice(ix, 1);
// Since the coin that was at ix + 1 is now at position ix in
// the coin array, we need to set ix back by one.
ix--;
}
}
}
/* Credit: https://openprocessing.org/sketch/525131
*/
var left, right, up, down;
var coin = [];
var player, posX, posY, radius, speed, colour, gameScreen, coins;
function setup() {
createCanvas(400, 400);
left = right = up = down = false;
gameScreen = "game";
coins = 0;
for (var i = 0; i < 10; i++) {
coin[i] = new Coin(random(width), random(height), 20, 20);
}
player = new Player();
}
function game() {
background(0);
if (left == true) {
player.moveLeft();
}
if (right == true) {
player.moveRight();
}
if (up == true) {
player.moveUp();
}
if (down == true) {
player.moveDown();
}
for (var i = 0; i < coin.length; i++) {
coin[i].display();
}
player.display();
player.collision();
fill(255);
textSize(20);
text("Coins: " + coins, 10, 30);
}
function draw() {
game();
}
function keyPressed() {
// if (keyPressed) { For Processing add this line
if (keyCode == LEFT_ARROW || key == 'a') {
left = true;
}
if (keyCode == RIGHT_ARROW || key == 'd') {
right = true;
}
if (keyCode == UP_ARROW || key == 'w') {
up = true;
}
if (keyCode == DOWN_ARROW || key == 's') {
down = true;
}
// }
}
function keyReleased() {
if (keyCode == LEFT_ARROW || key == 'a') {
left = false;
}
if (keyCode == RIGHT_ARROW || key == 'd') {
right = false;
}
if (keyCode == UP_ARROW || key == 'w') {
up = false;
}
if (keyCode == DOWN_ARROW || key == 's') {
down = false;
}
}
function Coin(coinX, coinY, coinR) {
this.img = loadImage("https://www.paulwheeler.us/files/Coin120.png");
this.coinX = coinX;
this.coinY = coinY;
this.coinR = coinR;
this.display = function() {
image(this.img, this.coinX, this.coinY, this.coinR, this.coinR);
}
}
function Player() {
this.posX = width / 2;
this.posY = height / 2;
this.radius = 10;
this.speed = 4;
this.colour = "blue";
this.display = function() {
fill(this.colour);
ellipse(this.posX, this.posY, this.radius * 2, this.radius * 2);
}
this.moveLeft = function() {
// posX = posX - speed;
this.posX = constrain(this.posX - this.speed, this.radius, width - this.radius);
}
this.moveRight = function() {
// posX = posX + speed;
this.posX = constrain(this.posX + this.speed, this.radius, width - this.radius);
}
this.moveUp = function() {
// posY = posY-speed;
this.posY = constrain(this.posY - this.speed, this.radius, height - this.radius);
}
this.moveDown = function() {
// posY = posY+speed;
this.posY = constrain(this.posY + this.speed, this.radius, height - this.radius);
}
this.collision = function() {
for (let ix = 0; ix < coin.length; ix++) {
const currentCoin = coin[ix];
if (this.posX > currentCoin.coinX &&
this.posX < currentCoin.coinX + currentCoin.coinR &&
this.posY > currentCoin.coinY &&
this.posY < currentCoin.coinY + currentCoin.coinR) {
coins += 1;
// Coin disappears:
// delete the current coin from the array
coin.splice(ix, 1);
// Since the coin that was at ix + 1 is now at position ix in
// the coin array, we need to set ix back by one.
ix--;
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I created a RaceTrack game, where you simply drive a car, collect yellow bonuses and avoid black obstacles.
The problem is that the animation of my car is not smooth ( When u try to go left or right etc. )
Can someone help me understand how can I do a smooth animation when steering a car ?
How could I add a spontanious curves instead of straight road?
Ps. I know my ColissionChecker() functions aren't perfect, but that is a problem for another day.
I don't know why in snippet's Full Page the canvas is very small and you can't see anything.
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var LineWidth = 10;
var LineHeight = 80;
var boundaryTopOffset = 5;
var boundaryLeftOffset = 2;
var boundaryPadding = 50;
var boundaryMiddleOffset = 2;
var speed = 50;
let executedTimer = false;
let dateDiff;
let currentScore = 0;
var leftBoundary = [];
var rightBoundary = [];
var middleBoundary = [];
var bonuses = [];
var obstacles = [];
var car = {
x: 1200,
y: 800
}
document.addEventListener('keydown', function(event) {
let key = event.which
if(key === 37) {
car.x -= speed;
} else if(key === 39) {
car.x += speed;
} else if(key === 38) {
car.y -= speed;
} else if(key === 40) {
car.y += speed;
}
})
for (x = 0; x < 8; x++) {
leftBoundary[x] =
{
offset: boundaryLeftOffset + 400,
topOffset: 0,
width: LineWidth,
height: LineHeight,
color: "red"
};
}
for (x = 0; x < 8; x++) {
middleBoundary[x] =
{
offset: boundaryMiddleOffset + 890,
topOffset: 0,
width: LineWidth,
height: LineHeight,
color: "white"
};
}
for (x = 0; x < 8; x++) {
rightBoundary[x] =
{
offset: boundaryLeftOffset + 1400,
topOffset: 0,
width: LineWidth,
height: LineHeight,
color: "red"
};
}
var cycle = 0,
totalCycle = LineHeight + boundaryPadding;
window.requestAnimationFrame(draw);
function draw() {
if(executedTimer == false) {
obstacles.push({x: Math.floor((Math.random() * 1000) + 450), y: 10});
timerStart();
}
drawCanvas(boundaryLeftOffset-2, 0, canvas.width, canvas.height, 'grey');
cycle = (cycle + 4) % totalCycle;
for (boundary of [leftBoundary, rightBoundary, middleBoundary]) {
for (i = 0; i < boundary.length; i++) {
boundary[i].topOffset = cycle + (i-1) * totalCycle;
drawBoundary(boundary[i], boundary[i].color);
}
}
if(dateDiff >= 1000) {
obstacles.push({x: Math.floor((Math.random() * 900) + 490), y: 10});
bonuses.push({x: Math.floor((Math.random() * 900) + 490), y: 10})
}
drawScore();
drawObstacle();
drawBonus();
drawCar();
obstacleColissionChecker();
bonusColissionChecker();
timerCheck();
window.requestAnimationFrame(draw);
}
function drawBoundary(x, elementColor) {
c.fillStyle = elementColor;
c.fillRect(x.offset+100, x.topOffset, x.width, x.height);
}
function drawCanvas(posX, posY, width, height, elementColor) {
c.fillStyle = elementColor;
c.fillRect(posX, posY, width, height);
}
function drawCar() {
c.fillStyle = "blue";
c.fillRect(car.x, car.y, 100, 150);
c.fillStyle = "black";
for(var i = 0; i < 101; i+=100){
c.beginPath();
c.ellipse(car.x + i, car.y + 10, 10, 15, Math.PI, 0, 2 * Math.PI);
c.ellipse(car.x + i, car.y + 140, 10, 15, Math.PI, 0, 2 * Math.PI);
c.fill();
c.closePath();
}
}
function timerStart() {
date1 = new Date();
executedTimer = true;
}
function timerCheck() {
var date2 = new Date();
dateDiff = Math.abs(date1 - date2);
if(dateDiff >= 1000)date1 = date2;
}
function drawScore() {
c.font='25px Verdana';
c.fillStyle = 'hsl('+ 0 +', 100%, 50%)';
c.fillText('Score : ' + currentScore, 100, 80);
}
function drawObstacle() {
c.fillStyle = "#080D23";
for(obstacle of [obstacles]) {
for (i = 0; i < obstacles.length; i++) {
c.fillRect(obstacle[i].x, obstacle[i].y+= 5, 80, 50);
}
}
}
function drawBonus() {
c.fillStyle = "#F2C14A";
for(bonus of [bonuses]) {
for (i = 0; i < bonuses.length; i++) {
c.beginPath();
c.arc(bonuses[i].x, bonuses[i].y+= 5, 20, 0, Math.PI * 2, false);
c.fill();
c.closePath();
}
}
}
function obstacleColissionChecker() {
for (i = 0; i < obstacles.length; i++) {
if(car.y + 20 - obstacles[i]?.y + 20 > 0 && car.y - 20 - obstacles[i]?.y + 20 < 100
&& car.x + 100 - obstacles[i]?.x + 20 > 0 && car.x - 100 - obstacles[i]?.x - 20 < 200) {
currentScore--;
}
}
}
function bonusColissionChecker() {
for (i = 0; i < bonuses.length; i++) {
if(car.y + 20 - bonuses[i]?.y + 20 > 0 && car.y - 20 - bonuses[i]?.y + 20 < 100
&& car.x + 100 - bonuses[i]?.x + 20 > 0 && car.x - 100 - bonuses[i]?.x - 20 < 200) {
currentScore++;
}
}
}
canvas {
border: 1px solid black;
margin: 0 !important;
padding: 0 !important;
}
body {
margin: 0;
}
<canvas></canvas>
In your code the speed is constant.
The car is either moving at that speed or is not moving.
This is the problem : you need to introduce acceleration.
You should car.x += speed on every frame and alter the speed in the key press handler. It would be a good start for you.
I'm trying to write a variation of Snake where the snake "bounces" off the walls.
It works most of the time, but occasionally the snake "escapes" and I can't figure out why. Initially I had the inequalities in the collison detection function set to strictly < or > which I thought was the cause of the problem, but I've changed them to <= and >= and the problem persists.
Can anyone explain why this is happening please? (You usually have to play for a minute or so before the snake escapes...)
<canvas id="canvas" width=500 height=500 style="display: block; border: 1px solid green; margin: auto;"></canvas>
<script>
var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = '30px Arial';
var HEIGHT = 500;
var WIDTH = 500;
var SEGMENT_WIDTH = 30;
var snakeVelocity = {
i: 1,
j: 0
};
var snakeArray = createSnake();
function createSnake() {
var snakeArray = [];
var length = 5; // Initial length of snake
for (var i = 0; i < length; i++) {
snakeArray.push({
x: i + 1,
y: 1
});
}
return snakeArray;
}
function moveSnake(arr) {
var head = arr.slice(-1)[0];
var tail = arr[0];
var newHead = arr.shift();
// check for wall collision, which also updates velocity if needed
snakeWallCollision(head);
newHead.x = head.x + snakeVelocity.i;
newHead.y = head.y + + snakeVelocity.j;
arr.push(newHead);
return arr;
}
function snakeWallCollision(obj) {
var collision = false;
if (obj.x >= WIDTH / SEGMENT_WIDTH || obj.x <= 0) {
snakeVelocity.i *= -1;
collision = true;
}
if (obj.y >= HEIGHT / SEGMENT_WIDTH || obj.y <= 0) {
snakeVelocity.j *= -1;
collision = true;
}
return collision;
}
function drawSnake() {
console.log(snakeArray[0]);
for (var i = 0; i < snakeArray.length; i++) {
var segment = snakeArray[i];
ctx.fillText('S', segment.x * SEGMENT_WIDTH, segment.y * SEGMENT_WIDTH + 30);
}
}
function update() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
moveSnake(snakeArray);
drawSnake();
}
function checkKey(e) {
e = e || window.event;
if ([38, 40, 37, 39].includes(e.keyCode)) {
e.preventDefault();
}
if (e.keyCode == '38') {
snakeVelocity = {
i: 0,
j: -1
};
} else if (e.keyCode == '40') {
snakeVelocity = {
i: 0,
j: 1
};
} else if (e.keyCode == '37') {
snakeVelocity = {
i: -1,
j: 0
};
} else if (e.keyCode == '39') {
snakeVelocity = {
i: 1,
j: 0
};
}
}
document.onkeydown = checkKey;
setInterval(update, 1000 / 20);
drawSnake();
</script>
The problem occurs if you change direction away from the wall just before you hit the wall.
Say for example the snake's head has just moved to x = 0 and moving left and the user keys right arrow just before the next update frame. Now the snakeVelocity.i is set to 1 away from the wall.
You then test the wall
if (obj.x >= WIDTH / SEGMENT_WIDTH || obj.x <= 0) {
snakeVelocity.i *= -1; // negate the direction
// but the direction is already away from the
// wall due to user input. This will turn it back
// onto the wall
}
Same happens for up and down.
You need to have the collision test know what direction the snake is heading and then based on that test if that move will result in a collision.
Change the test function to find the next position the snake's head will be if allowed to move as its current state dictates. Only if that move results in the head being outside the bounds of the game do you change the direction.
function snakeWallCollision(head) {
var x = head.x + snakeVelocity.i; // find out where the head will be
var y = head.y + snakeVelocity.j; // next frame
if (x > WIDTH / SEGMENT_WIDTH || x < 0) {
snakeVelocity.i *= -1;
return true;
}
if (y > HEIGHT / SEGMENT_WIDTH || y < 0) {
snakeVelocity.j *= -1;
return true;
}
return false;
}
What happens if you swap these lines around:
newHead.x = head.x + snakeVelocity.i;
newHead.y = head.y + + snakeVelocity.j;
// check for wall collision, which also updates velocity if needed
snakeWallCollision(head);
The problem seems to be that if the user presses a key synchronized with your collision check the directions changes twice and the snake goes beyond the wall.
Maybe this one helps you out (i added a test variable USER_ACTION to check if the user has pressed the key and if so, don't do the collision check):
var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = '30px Arial';
var HEIGHT = 500;
var WIDTH = 500;
var SEGMENT_WIDTH = 30;
var USER_ACTION = false;
var snakeVelocity = {
i: 1,
j: 0
};
var snakeArray = createSnake();
function createSnake() {
var snakeArray = [];
var length = 5; // Initial length of snake
for (var i = 0; i < length; i++) {
snakeArray.push({
x: i + 1,
y: 1
});
}
return snakeArray;
}
function moveSnake(arr) {
var head = arr.slice(-1)[0];
var tail = arr[0];
var newHead = arr.shift();
// check for wall collision, which also updates velocity if needed
snakeWallCollision(head);
newHead.x = head.x + snakeVelocity.i;
newHead.y = head.y + +snakeVelocity.j;
arr.push(newHead);
return arr;
}
function snakeWallCollision(obj) {
if (!USER_ACTION) {
var collision = false;
if (obj.x >= WIDTH / SEGMENT_WIDTH || obj.x <= 0) {
snakeVelocity.i *= -1;
collision = true;
}
if (obj.y >= HEIGHT / SEGMENT_WIDTH || obj.y <= 0) {
snakeVelocity.j *= -1;
collision = true;
}
} else {
USER_ACTION = false;
}
return collision;
}
function drawSnake() {
console.log(snakeArray[0]);
for (var i = 0; i < snakeArray.length; i++) {
var segment = snakeArray[i];
ctx.fillText('S', segment.x * SEGMENT_WIDTH, segment.y * SEGMENT_WIDTH + 30);
}
}
function update() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
moveSnake(snakeArray);
drawSnake();
}
function checkKey(e) {
USER_ACTION = true;
e = e || window.event;
if ([38, 40, 37, 39].includes(e.keyCode)) {
e.preventDefault();
}
if (e.keyCode == '38') {
snakeVelocity = {
i: 0,
j: -1
};
} else if (e.keyCode == '40') {
snakeVelocity = {
i: 0,
j: 1
};
} else if (e.keyCode == '37') {
snakeVelocity = {
i: -1,
j: 0
};
} else if (e.keyCode == '39') {
snakeVelocity = {
i: 1,
j: 0
};
}
}
document.onkeydown = checkKey;
setInterval(update, 1000 / 20);
drawSnake();
I am coding a Pacman clone. The steps that I have accomplished are the floor, the maze, as also the pacman and the movement of pacman through wall paths.
The maze is in array format where the walls are represented as "x". Also the movement should go with wasd keys from the keyboard. Currently the movement of the object is smooth and can walk in the path and change directions correctly only if I have the good timing with the hit.
I have also read this post Relevant Post and I followed some of these techniques so that I can store the key presses. After I did that I don't think that something changed from the previous design. Also when I change to a direction that is not allowed it rotates to that direction and stops moving. I want to implement this like classic Pac-man game. Below is the code of my issue.
That's the handleKeyDown function where i check for key press.
function handleKeyDown(event) {
currentlyPressedKeys[event.keyCode] = true;
if (String.fromCharCode(event.keyCode) == "A") {
curr_key=temp;
curr_key="A";
memory_key = temp;
pacRot = 0;
}
if (String.fromCharCode(event.keyCode) == "D") {
curr_key=temp;
curr_key="D";
memory_key = temp;
pacRot = 180;
}
if (String.fromCharCode(event.keyCode) == "W") {
curr_key=temp;
curr_key="W";
memory_key = temp;
pacRot = 270;
}
if (String.fromCharCode(event.keyCode) == "S") {
curr_key=temp;
curr_key="S";
memory_key = temp;
pacRot = 90;
}
}
Now the function can_move(key) that checks if the object can move to a direction
function can_move(key){
if (key == "W") {
if (stage[Math.round(y) - 1][x] == "x") {
y = Math.round(y);
return false;
} else {
return true;
}
}
if (key == "A") {
if (stage[y][Math.round(x) - 1] == "x") {
x = Math.round(x);
return false;
} else {
return true;
}
}
if (key == "S") {
if (stage[Math.round(y) + 1][x] == "x") {
y = Math.round(y);
return false;
} else {
return true;
}
}
if (key == "D") {
if (stage[y][Math.round(x) + 1] == "x") {
x = Math.round(x);
return false;
} else {
return true;
}
}
}
And finally the main moving function move_conditions that is called inside tick() after drawScene()
function move_conditions(elapsed) {
if(can_move(curr_key)==true){
if(curr_key=="W"){
ySpeed = 5;
xSpeed = 0;
y -= (ySpeed * 25) / 1000.0;
x = Math.round(x);
}
if(curr_key=="A"){
ySpeed = 0;
xSpeed = 5;
x -= (xSpeed * 25) / 1000.0;
y = Math.round(y);
}
if(curr_key=="S"){
ySpeed = 5;
xSpeed = 0;
y += (ySpeed * 25) / 1000.0;
x = Math.round(x);
}
if(curr_key=="D"){
ySpeed = 0;
xSpeed = 5;
x += (xSpeed * 25) / 1000.0;
y = Math.round(y);
}
}else if(can_move(memory_key)==true){
if(memory_key=="W"){
ySpeed = 5;
xSpeed = 0;
y -= (ySpeed * 25) / 1000.0;
x = Math.round(x);
}
if(memory_key=="A"){
ySpeed = 0;
xSpeed = 5;
x -= (xSpeed * 25) / 1000.0;
y = Math.round(y);
}
if(memory_key=="S"){
ySpeed = 5;
xSpeed = 0;
y += (ySpeed * 25) / 1000.0;
x = Math.round(x);
}
if(memory_key=="D"){
ySpeed = 0;
xSpeed = 5;
x += (xSpeed * 25) / 1000.0;
y = Math.round(y);
}
}
i have a simpel game but when the red ball hits the blue ball while in motion the SPLICING desen't work i get the error form the movement script. I think the best solution to this problem is to registrer a collition when only the red ball hits the blu ball. can somone help me whith that?
To se a demo clik the linkDEMO press R to restart
Code:
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var tileldig = Math.floor((Math.random() * 300) + 1);
var tekst = document.getElementById("tekst")
var log = document.getElementById("log")
var highlog = document.getElementById("highlog")
var kuler = [{
r: 10,
x: canvas.width / 2,
y: canvas.height - 100,
f: "red",
dy: 0
}, ];
var fiender = [{
r: 20,
x: tileldig,
y: -20,
vx: 0,
vy: 1,
}, ];
//var snd = new Audio("Skudd.m4a");
var poeng = 0;
var highscore = 0;
var høyre = 0;
var venstre = 0;
var opp = 0;
var ned = 0;
var restart = 0;
var død = 0;
document.onkeydown = function tast(e) {
if (e.keyCode == 39) { // høyre
høyre = 1;
}
if (e.keyCode == 37) { // venstre
venstre = 1;
}
if (e.keyCode == 38) { // opp
opp = 1;
}
if (e.keyCode == 40) { // ned
ned = 1;
}
if (e.keyCode == 32) {
newskudd();
// snd.play();
console.log("hit space")
}
if (e.keyCode == 82) {
init();
log.innerHTML += ("Poeng: " + poeng)
poeng = 0;
}
}
document.onkeyup = function tast2(e) {
if (e.keyCode == 39) { // høyre
høyre = 0;
}
if (e.keyCode == 37) { // venstre
venstre = 0;
}
if (e.keyCode == 38) { // opp
opp = 0;
}
if (e.keyCode == 40) { // ned
ned = 0;
}
}
function spill() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < kuler.length; i++) {
kuler[i].x += 0;
kuler[i].y += kuler[i].dy;
ctx.fillStyle = kuler[i].f;
ctx.beginPath();
ctx.arc(kuler[i].x, kuler[i].y, kuler[i].r, 2 * Math.PI, 0);
ctx.closePath();
ctx.fill();
if (kuler[0].x >= canvas.width - kuler[0].r) {
kuler[0].x = canvas.width - kuler[0].r
};
if (kuler[0].x <= 0 + kuler[0].r) {
kuler[0].x = 0 + kuler[0].r
};
if (kuler[0].y >= canvas.height - kuler[0].r) {
kuler[0].y = canvas.height - kuler[0].r
};
if (kuler[0].y <= 0 + kuler[0].r) {
kuler[0].y = 0 + kuler[0].r
};
for (var j = 0; j < fiender.length; j++) {
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(fiender[j].x, fiender[j].y, fiender[j].r, 2 * Math.PI, 0);
ctx.closePath();
ctx.fill();
if (fiender[j].x >= canvas.width - fiender[j].r) {
fiender[j].x = canvas.width - fiender[j].r;
};
if (fiender[j].x <= 0 + fiender[j].r) {
fiender[j].x = 0 + fiender[j].r;
};
if (fiender[j].vy >= 2) {
fiender[j].vy = 2;
};
var distanceFromCenters = Math.sqrt(Math.pow(Math.abs(fiender[j].x - kuler[i].x), 2) + Math.pow(Math.abs(fiender[j].y - kuler[i].y), 2)); // you have a collision
if (distanceFromCenters <= (fiender[j].r + kuler[i].r)) {
fiender.splice(j, 1);
kuler.splice(i, 1);
poeng += 1;
restart = 1;
} else if (fiender[j].y > canvas.height) {
fiender.splice(j, 1)
}
if (j > 1) {
fiender.splice(j, 1)
}
log.innerHTML = ("<br>" + "Score denne runden: " + poeng + "</br>" + "<br>" + "Highscore: " + highscore + "</br>")
if (poeng >= highscore) {
log.innerHTML += ("<br>" + "Ny Highscore: " + highscore + "</br>")
highscore = poeng;
}
}
}
for (var j = 0; j < fiender.length; j++) {
fiender[j].y += fiender[j].vy;
}
if (venstre == 1) {
kuler[0].x -= 4;
}
if (høyre == 1) {
kuler[0].x += 4;;
}
if (opp == 1) {
kuler[0].y -= 4;
}
if (ned == 1) {
kuler[0].y += 4;
}
requestAnimationFrame(spill);
return;
}
document.onload = spill();
function newskudd() {
var nyttskudd = {
x: kuler[0].x,
y: kuler[0].y,
r: 5,
dy: -5,
f: "white"
};
kuler.push(nyttskudd);
};
setInterval(function() {
fiender.push({
r: 20,
x: Math.floor((Math.random() * 300) + 1),
y: -20,
vx: 0,
vy: 1,
f: "green"
});
}, 1000);
function init() {
kuler = [{
r: 10,
x: canvas.width / 2,
y: canvas.height - 100,
f: "red",
dy: 0
}, ];
fiender = [{
r: 20,
x: tileldig,
y: -20,
vx: 0,
vy: 1,
}, ];
}