The "spaceShip" in the following code isn't moving smoothly at the beginning of holding any arrow key down. It moves one step, freezes for a split second, and then moves "smoothly". How can I make it move smoothly right from the beginning, with not "freezing"?
My code:
<!doctype html>
<html>
<head>
<meta http-content="Content-type" charset="utf-8">
<title>Make body move smoothly</title>
<style type="text/css">
body {
}
canvas {
margin: auto;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="600"></canvas>
<script type="text/javascript">
// Set up requestAnimationFrame and cancelAnimationFrame
(function() {
var lastTime =0;
var vendors=['ms', 'moz', 'webkit', 'o'];
for(var x=0; x<vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame=window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+ 'CancelAnimationFrame'] ||
window[vendors[x] +'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame=function(callback, element) {
var currTime =new Date().getTime();
var timeToCall =Math.max(0, 16 - (currTime - lastTime));
var id =window.setTimeout(function() { callback(currTime+timeToCall); },
timeToCall);
lastTime =currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame=function(id) {
clearTimeout(id);
};
}());
var canvas;
var ctx;
// ship data
var shipPositionX = document.getElementById('canvas').width/2;
var shipPositionY = document.getElementById('canvas').height - 30;
var deltaShipPositionX = 10;
var deltaShipPositionY = 10;
function init() {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
draw();
}
function draw(){
clear();
createRectangleToCoverCanvas();
createSpaceShip(shipPositionX, shipPositionY, 10);
requestAnimationFrame(draw);
}
function clear(){
ctx.clearRect(0, 0, document.getElementById('canvas').width, document.getElementById('canvas').height);
}
function createRectangleToCoverCanvas(){
ctx.fillStyle = 'black';
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function createSpaceShip(x, y, radius) {
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'
ctx.beginPath();
ctx.rect(x, y, 20, 20);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function moveSpaceShip(event){
switch(event.keyCode){
// left
case 37:
if(shipPositionX - deltaShipPositionX + 15 > 0){
shipPositionX -= deltaShipPositionX;
}
break;
// up
case 38:
if(shipPositionY - deltaShipPositionY + 15 > 0){
shipPositionY -= deltaShipPositionY;
}
break;
// right
case 39:
if(shipPositionX + deltaShipPositionX < document.getElementById('canvas').width){
shipPositionX += deltaShipPositionX;
}
break;
//down
case 40:
if(shipPositionY + deltaShipPositionY < document.getElementById('canvas').height){
shipPositionY += deltaShipPositionY;
}
break;
}
}
window.addEventListener('load', init);
window.addEventListener('keydown', moveSpaceShip, true);
</script>
</body>
</html>
Notice the difference between my code and this example: http://atomicrobotdesign.com/blog_media/sprite_sheet/spritesheet.html
See how the example's is smooth, but my "spaceShip" isn't?
Why is it happening and how can I fix it? Is it because the example uses a sprite (but this doesn't seem to make much sense)?
The problem is that you wait for each keydown event to update the ship position.
The keydown event has a delay before it triggers again : the delay you are experiencing at beginning and the jump you face at each redraw.
The solution here is to trigger the movement on keydown and release it on keyup. This way, your ship will move smoothly as soon as you push the button.
// Im' assuming most of visitors here have recent browsers, so I removed the rAF polyfill for readibility
// If you wrap it after the canvas element decalaration, you can already populate this variable, it will avoid that you make a lot of calls to document.getElementById()
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// ship data
var shipPositionX = canvas.width / 2;
// Just for the snippet height
var shipPositionY = 0;
var deltaShipPositionX = 10;
var deltaShipPositionY = 10;
//Removed the init() function, since our elements are loaded.
function draw() {
clear();
createRectangleToCoverCanvas();
createSpaceShip(shipPositionX, shipPositionY, 10);
}
function clear() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function createRectangleToCoverCanvas() {
ctx.fillStyle = 'black';
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
ctx.stroke();
}
function createSpaceShip(x, y, radius) {
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'
ctx.beginPath();
ctx.rect(x, y, 20, 20);
ctx.fill();
ctx.stroke();
}
// instantiate a variable that will store our animationFrame id, so we can cancel it further
var raf,
// the direction object, with an x and y values
direction = {
x: 0,
y: 0
};
// we can set a speed variable
var speed = 2.5;
function triggerMoveSpaceShip(event) {
switch (event.keyCode) {
// left
case 37:
// update the direction object
direction.x = -speed;
// avoid the scroll in the snippet
event.preventDefault();
break;
// up
case 38:
direction.y = -speed;
event.preventDefault();
break;
// right
case 39:
direction.x = speed;
event.preventDefault();
break;
//down
case 40:
direction.y = speed;
event.preventDefault();
break;
}
// if we haven't initiated the animation yet, and that our direction is not 0, then do it now
if (!raf && (direction.x || direction.y)) moveSpaceShip();
}
function releaseMoveSpaceShip(event) {;
switch (event.keyCode) {
// left
case 37:
//reset this direction
direction.x = 0;
break;
// up
case 38:
direction.y = 0;
break;
// right
case 39:
direction.x = 0;
break;
//down
case 40:
direction.y = 0;
break;
}
if (!direction.x && !direction.y) {
// if none of the directions is set, stop the animation
cancelAnimationFrame(raf);
raf = undefined;
}
}
function moveSpaceShip() {
// declare our animation function
var move = function() {
// update the positions without going out of the screen
// Sorry, this is dirty...
if(direction.x){
if(
(shipPositionX > 0 && shipPositionX < canvas.width-20) ||
(shipPositionX <= 0 && direction.x > 0) ||
(shipPositionX >= canvas.width-20 && direction.x < 0))
shipPositionX += direction.x;
}
if(direction.y){
if(
(shipPositionY > 0 && shipPositionY < canvas.height-20) ||
(shipPositionY <= 0 && direction.y > 0) ||
(shipPositionY >= canvas.width-20 && direction.y < 0))
shipPositionY += direction.y;
}
// finally draw ou ship
draw();
// update our raf id
raf = requestAnimationFrame(move);
};
// let's go !
raf = requestAnimationFrame(move);
}
draw();
window.addEventListener('keydown', triggerMoveSpaceShip, true);
window.addEventListener('keyup', releaseMoveSpaceShip, true);
canvas {
margin: auto;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border: 1px solid black;
}
body{
overflow: none;
}
<canvas id="canvas" width="400" height="200"></canvas>
uncommented fiddle
Related
I have trying to achieve motion effect for the standing object.
I assume that my object will be able to go to the right or the left for example.
And I wanna make illusion like that object is still moving. Even if currently is not moving. (Meanwhile background can moving still...)
'use strict';
const pressedKeys = [];
const canvas = document.querySelector('#game');
const ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 150;
class Player
{
xPosition = 150;
yPosition = 50;
speed = 5;
isMoving = false;
update(pressedKeys)
{
if (pressedKeys['ArrowLeft']) {
this.xPosition -= this.speed;
} else if (pressedKeys['ArrowRight']) {
this.xPosition += this.speed;
}
}
draw(ctx)
{
ctx.fillStyle = '#2976f2';
ctx.fillRect(this.xPosition, this.yPosition, 30, 30);
}
}
const player = new Player();
function animate()
{
window.requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height)
if (player.isMoving) {
player.update(pressedKeys);
}
player.draw(ctx);
}
animate();
window.addEventListener('keyup', function (event) {
delete pressedKeys[event.key];
player.isMoving = false;
})
window.addEventListener('keydown', function (event) {
switch (event.key) {
case 'ArrowLeft':
case 'ArrowRight':
pressedKeys[event.key] = true;
player.isMoving = true;
break;
}
})
canvas {
border: 1px solid blue;
}
<canvas id="game"></canvas>
Usually this kind of effect is done by constantly duplicating the desired object, move it to the exact same screen position and ultimately fade it out over time e.g. within one second.
In your case though we can simplify things a bit since you want to keep that "motion blurred" look even if it ain't moving.
So first we need to another property to your Player class oldX. It holds the position of the object before a movement occured. By subtracting oldX from x we can determine if the object is moving to the left or to the right - so we know where to put the trailing duplicates.
If we know the direction, we can start creating duplicates using a simple for loop like:
for (var a = 0; a < 7; a++) {
ctx.fillRect(this.x - (this.x - this.oldX) / this.speed * a * 2, this.y, 30, 30);
}
this will create seven equal looking squares - so it won't look good yet. The duplicate next to the original should have almost the same color while the last one should almost blend with the background. To do this we can use the globalAlpha property of the canvases context. A value of 1 is opaque while 0 is completely transparent.
Putting it all together:
const keys = [];
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 150;
class Player {
x = 150;
y = 50;
oldX = 150;
speed = 5;
moving = false;
update(keys) {
this.oldX = this.x;
if (keys['ArrowLeft']) {
this.x -= this.speed;
} else if (keys['ArrowRight']) {
this.x += this.speed;
}
}
draw(ctx) {
ctx.fillStyle = '#2976f2';
ctx.fillRect(this.x, this.y, 30, 30);
ctx.save();
for (var a = 0; a < 7; a++) {
ctx.globalAlpha = 0.5 - (a / 7) * 0.5;
ctx.fillRect(this.x - (this.x - this.oldX) / this.speed * a * 2, this.y, 30, 30);
}
ctx.restore();
}
}
const player = new Player();
function animate() {
window.requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height)
if (player.moving) {
player.update(keys);
}
player.draw(ctx);
}
animate();
window.addEventListener('keyup', function(event) {
delete keys[event.key]
player.moving = false;
})
window.addEventListener('keydown', function(event) {
switch (event.key) {
case 'ArrowLeft':
case 'ArrowRight':
keys[event.key] = true;
player.moving = true;
break;
}
})
canvas {
border: 1px solid blue;
}
<canvas id="game"></canvas>
The issue is, the HTML canvas is not outputting a character object inside the canvas. The canvas is defined as the gameArea and the character is defined as a character. Both have been written in JavaScript, and the character is made into an object.
This is for a 2D platformer game, so far I have tried to change the character form an object to a variable to see whether the encapsulation of the object was causing issues, but this has not worked. I have also tried to add a new function within the character object called update, in order to make sure the character (rectangle) is filled when moving.
Here is my code:
The first section declares the character variable, and also has a function, which is started first in the HTML using onload:
var Character;
function startGame(){
gameArea.start();
Character = new character(40, 80, "#4286f4", 30, 500, true, 0, 0);
}
The parameters for the character object are width, height, colour, x, y, jumping state, speedX and speedY (also shown later on in the code).
The second section shows the gameArea variable (the canvas), I have also called my EventListeners of Keydown and Keyup here:
var gameArea = {
canvas: document.getElementById("canvas"), //Defining the canvas'
//dimensions
start: function() {
this.canvas.width = 700;
this.canvas.height = 500;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas,document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {//EventListener
functions
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = (e.type == "keydown");
})
window.addEventListener('keyup', function (e) {
gameArea.keys[e.keyCode] = (e.type == "keydown");
})
},
clear : function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
The next section is the character object:
function character(width, height, color, x, y, jumping, speedX, speedY)
{//Character object, blue rectangle
this.jumping=jumping;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.speedX = speedX;
this.speedY= speedY;
ctx = gameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.width, this.height, this.x, this.y);
this.update = function() {//Updating the gameArea function
ctx = gameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
To my knowledge, the code above is the only relevant code to the issue. If needed the rest of the code (the controller logic and movement mechanics) can be found below:
function updateGameArea() {
gameArea.clear();
character.speedX = 0;
character.speedY = 0;
character.update();
var controller = {
up: false,
down: false,
left: false,
right: false,
keyListener: function (event) {
var keyPosition = (event.type == "keydown") ? true : false;
switch (event.keyCode) {
case 87:
controller.up = keyPosition;
break;
case 83:
controller.down = keyPosition;
break;
case 65:
controller.left = keyPosition;
break;
case 68:
controller.right = keyPosition;
break;
}
}
};
if (controller.up && character.jumping == false) {
character.speedY -= 2.5
character.jumping = true;
}
if (controller.down) {
character.speedY += 2.5;
}
if (controller.left) {
character.speedX -= 2.5;
}
if (controller.right) {
character.speedX += 2.5;
}
character.speedY += 1.5;
character.x = character.speedX;
character.y = character.speedY;
character.speedX *= 0.9;
character.speedY *= 0.9
var ground = gameArea.canvas.height - this.height;
if (this.y > ground) {
character.jumping = false;
character.y = ground;
character.speedY = 0;
}
}
Expected results: character displays inside the canvas and has the correct movement
Actual results: character does not display.
I understand that there is a lot of code here, but I would really appreciate it if someone could point me in the correct direction, as I don't really understand why the character does not display in the canvas.
Thanks a lot!
You are setting the character's position to 500 so he is drawn outside of the visible area (the canvas is only 500 tall).
x= 0, y= 0 is in the top right in a canvas.
Change Character = new character(40, 80, "#4286f4", 30, 500, true, 0, 0);
To Character = new character(40, 80, "#4286f4", 30, 400, true, 0, 0);
EDIT: Rewrote the whole thing.
var character, ctx;
function startGame() {
gameArea.start();
character = new Character(40, 80, "#4286f4", 30, 400, true, 0, 0);
gameArea.clear();
updateGameArea();
}
let gameArea = {
start() {
this.canvas = document.getElementById("canvas"); //Defining the canvas'
this.canvas.width = 700;
this.canvas.height = 500;
this.context = this.canvas.getContext("2d");
},
clear() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
}
function Character(width, height, color, x, y, jumping, speedX, speedY) { //Character object, blue rectangle
this.jumping = jumping;
this.width = width;
this.height = height;
this.position = {
x,
y
};
this.velocity = {
x: speedX,
y: speedY
};
ctx = gameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.width, this.height, this.x, this.y);
this.color = color;
return this;
}
Character.prototype = {
update() { //Updating the gameArea function
character.position.x += character.velocity.x;
character.position.y += character.velocity.y;
let ground = gameArea.canvas.height - this.height;
if (this.position.y > ground) {
character.jumping = false;
character.position.y = ground;
character.velocity.y = 0;
}
ctx = gameArea.context;
ctx.fillStyle = this.color;
ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
}
}
var controller = {
x: 0,
y: 0
};
document.addEventListener('keydown', e => {
switch (e.keyCode) {
case 87:
controller.y = -1;
break;
case 83:
controller.y = 1;
break;
case 65:
controller.x = -1;
break;
case 68:
controller.x = 1;
break;
}
});
document.addEventListener('keyup', e => {
switch (e.keyCode) {
case 87:
controller.y = 0;
break;
case 83:
controller.y = 0;
break;
case 65:
controller.x = 0;
break;
case 68:
controller.x = 0;
break;
}
});
function updateGameArea() {
gameArea.clear();
character.velocity.x = controller.x;
character.velocity.y = controller.y;
character.update();
requestAnimationFrame(updateGameArea);
}
window.onload = startGame;
canvas {
width:100%;
}
<canvas id="canvas"></canvas>
I am trying to create a simple snake game.
(function() {
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
x = 0,
y = 0,
speed = 2;
x_move = speed,
y_move = 0,
food_position_x = Math.floor(Math.random() * canvas.width / 10) * 10,
food_position_y = Math.floor(Math.random() * canvas.height / 10) * 10,
size_x = 10;
function eat() {
console.log('food_x:' + food_position_x + ' x:' + x + ' / food_y:' + food_position_y + ' y:' + y);
if (Math.floor(y / 10) * 10 == food_position_y && Math.floor(x / 10) *10 == food_position_x) {
size_x += 2;
//throw new Error("MATCH!"); // This is not an error. Just trying to stop the script
}
}
// Drawing
function draw() {
eat();
requestAnimationFrame(function() {
draw();
});
// Draw the snake
ctx.beginPath();
ctx.rect(Math.floor(x/10)*10, Math.floor(y/10)*10, size_x, 10);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.closePath();
// Draw the food
ctx.beginPath();
ctx.rect(Math.floor(food_position_x/10)*10, Math.floor(food_position_y/10)*10, 10, 10);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
// Increase the value of x and y in order to animate
x = x + x_move;
y = y + y_move;
}
draw();
// Key Pressing
document.addEventListener('keydown', function(event) {
switch(event.keyCode) {
case 40: // Moving down
if (x_move != 0 && y_move != -1) {
x_move = 0;
y_move = speed;
}
break;
case 39: // Moving right
if (x_move != -1 && y_move != 0) {
x_move = speed;
y_move = 0;
}
break;
case 38: // Moving top
if (x_move != 0 && y_move != 1) {
x_move = 0;
y_move = -speed;
}
break;
case 37: // Moving left
if (x_move != 1 && y_move != 0) {
x_move = -speed;
y_move = 0;
}
break;
}
});
})();
canvas { background-color: #000022 }
<canvas id="canvas" width="400" height="400"></canvas>
jsfiddle
The problem
Every time when I catch the food, the snake becomes longer but when you press the down or up key, it moves horizontally.
Maybe a solution
This is what I believe the solution could be:
The snake should be an array! Every time when the key is pressed, define the position of HEAD of snake and move the snake step by step, because it is an array. So the body follows the head. But in this case, I have no idea how to make an array from it.
Maybe there are other solutions. Any helps would be appreciated!
You need to maintain an array of the points currently occupied by the snake body, and add new point (unshift) to the array as the snake approaches and remove point from the back of the array (pop). The following code is a starter, you need to make it your own :).
(function () {
const COLORS={ SNAKE:'#ff7bf5', FOOD:'blue' };
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
var snake=[], score=0;
var x, y, food_position_x, food_position_y, x_move, y_move;
var frameCount=0, framesRequiredToMove=10;
//the less the framesToMove the faster the sname moves
function draw(){
if(++frameCount==framesRequiredToMove){
frameCount=0;
move();
}
requestAnimationFrame(draw);
}
function init(){
snake = [{x:3,y:0},{x:2,y:0},{x:1,y:0},{x:0,y:0}];
snake.forEach((p)=>{plot(p.x,p.y,COLORS.SNAKE)})
x=snake[0].x;y=snake[0].y;
score=0;x_move=1;y_move=0;
scoreboard.innerText=score;
newfood();
setTimeout(draw,1000);
}
function plot(x,y,color){
ctx.beginPath();
ctx.rect(x * 10, y * 10, 10, 10);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
function move(){
snakepx.innerText = x;
snakepy.innerText = y;
x = x + x_move;
y = y + y_move;
// Advance The Snake
plot(x,y,COLORS.SNAKE);
snake.unshift({x:x,y:y});
// Check food encounter
if(x==food_position_x && y==food_position_y){
scoreboard.innerText=++score;
newfood();
}
else{
var last=snake.pop();
ctx.clearRect(last.x * 10, last.y * 10, 10, 10);
}
}
function newfood(){
food_position_x=Math.floor(Math.random() * canvas.width / 10);
food_position_y=Math.floor(Math.random() * canvas.height / 10);
plot(food_position_x,food_position_y,COLORS.FOOD);
foodpx.innerText = food_position_x;
foodpy.innerText = food_position_y;
}
init();
// Key Pressing
document.addEventListener('keydown', function (event) {
event.preventDefault();
switch (event.keyCode) {
case 40: // Moving down
if (x_move != 0 && y_move != -1) {
x_move = 0;
y_move = 1;
}
break;
case 39: // Moving right
if (x_move != -1 && y_move != 0) {
x_move = 1;
y_move = 0;
}
break;
case 38: // Moving top
if (x_move != 0 && y_move != 1) {
x_move = 0;
y_move = -1;
}
break;
case 37: // Moving left
if (x_move != 1 && y_move != 0) {
x_move = -1;
y_move = 0;
}
break;
}
});
})();
canvas {
background-color: #000022;
float: left;
}
<canvas id="canvas" width="400" height="180"></canvas>
<div style="margin-left: 410px">
Snake: (<span id="snakepx"></span>, <span id="snakepy"></span>)<br>
Food: (<span id="foodpx"></span>, <span id="foodpy"></span>)<br>
Score: <span id="scoreboard"></span>
</div>
Well as I had some free time to spare I created my own JS snake to demonstrate you how it can be done. Most important parts are in this.snakeBody where array of body is stored and this.moveForward() where you can see how body is updated.
https://jsfiddle.net/nooorz24/p8xtdv3h/13/
moveForward: function() {
var next = this.getNextfieldValue();
if (next == "frame" || next == "body") {
console.log("You lose!")
this.isAlive = false;
} else {
var newHead = this.getNextfieldCoords();
this.draw.snake(newHead.x, newHead.y);
this.body.unshift(newHead);
if (next == "food") {
this.generateFood();
this.snakeSize++;
} else {
var last = this.body.pop();
this.draw.empty(last.x, last.y);
}
}
},
I tried to make it as readable as I could, but note that this is unfinished example and would need need loads of improvements to be a playable game
I think that you have hit on the answer with an array. In the past, I have found that Snake works best with an array, tracking each block of the snake and checking for any two blocks being in the same position. For movement, however, you must directly control the head, and have the body follow based on the position of the array space one ahead of it. The snake head would be the first item in the array. I am not sure what you mean about not understanding the implementation of the array, but the coordinates for each block would be an item in the array.
Have a length variable and make that the x or y size based on direction, like this:
(function() {
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
x = 0,
y = 0,
speed = 2,
x_move = speed,
y_move = 0,
food_position_x = Math.floor(Math.random() * canvas.width / 10) * 10,
food_position_y = Math.floor(Math.random() * canvas.height / 10) * 10,
size_x = 10,
size_y = 10,
snake_length = 10;
function eat() {
console.log('food_x:' + food_position_x + ' x:' + x + ' / food_y:' + food_position_y + ' y:' + y);
if (Math.floor(y / 10) * 10 == food_position_y && Math.floor(x / 10) *10 == food_position_x) {
snake_length += 2;
//throw new Error("MATCH!"); // This is not an error. Just trying to stop the script
}
}
// Drawing
function draw() {
eat();
requestAnimationFrame(function() {
draw();
});
// Draw the snake
ctx.beginPath();
ctx.rect(Math.floor(x/10)*10, Math.floor(y/10)*10, size_x, size_y);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.closePath();
// Draw the food
ctx.beginPath();
ctx.rect(Math.floor(food_position_x/10)*10, Math.floor(food_position_y/10)*10, 10, 10);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
// Increase the value of x and y in order to animate
x = x + x_move;
y = y + y_move;
}
draw();
// Key Pressing
document.addEventListener('keydown', function(event) {
switch(event.keyCode) {
case 40: // Moving down
if (x_move != 0 && y_move != -1) {
x_move = 0;
y_move = speed;
size_x = 10;
size_y = snake_length;
}
break;
case 39: // Moving right
if (x_move != -1 && y_move != 0) {
x_move = speed;
y_move = 0;
size_x = snake_length;
size_y = 10;
}
break;
case 38: // Moving top
if (x_move != 0 && y_move != 1) {
x_move = 0;
y_move = -speed;
size_x = 10;
size_y = snake_length;
}
break;
case 37: // Moving left
if (x_move != 1 && y_move != 0) {
x_move = -speed;
y_move = 0;
size_x = snake_length;
size_y = 10;
}
break;
}
});
})();
canvas { background-color: #000022 }
<canvas id="canvas" width="400" height="400"></canvas>
I make the game using C++ soi can advice make a new object for every snake tile and on move set it to parent position
function Tile(x, y)
{
this.x = x;
this.y = y;
}
To debug your down or up problem, I would add some console.log('message') calls in your code for debugging.
Specifically, add some console messages in the switch statement so that you know the correct event and branch is firing. For Example:
switch(event.keyCode) {
case 40:
console.log('down');
...
case 39:
console.log('right');
...
}
Then add more and more debugging log messages until you find your problem.
You can view the console messages in the F12 developer tools in your browser of choice while running your game.
Also make sure you are not calling ctx.translate(...) on the canvas context to flip the canvas, this would change the direction of the x and/or y axis.
hi i am trying to gat an arc to resize as it moves across the screen.
I cant seem to asign the value of increment to the radius of the arc for it to get bigger and then smaller.
please see below for the code block in question and then the entire code.
resize(){
this.up = true;
this.r = 0;
this.increment = 10;
this.ceiling = 100;
function PerformCalc() {
if (this.up == true && this.r <= this.ceiling) {
this.r += increment
if (this.r == ceiling) {
this.up = false;
}
} else {
this.up = false
this.r -= increment;
if (this.r == 0) {
this.up = true;
}
}
console.log(this.r);
}
setInterval(PerformCalc, 1000);
}
When i log out the radius to the console it gives nan for some reason.
Any help would be greatly appreciated.
<!DOCTYPE html>
<html>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<head>
<meta charset="UTF-8">
<title>Canvas</title>
<style type="text/css">
canvas {
border: 1px solid grey;
}
</style>
</head>
<body>
<canvas id="canvas-for-ball"></canvas>
<script type="text/javascript">
// Gets a handle to the element with id canvasOne.
var canvas = document.getElementById("canvas-for-ball");
// Get a 2D context for the canvas.
var ctx = canvas.getContext("2d");
function init(){
canvas.width = 500;
canvas.height = 500;
}
init();
//angle defining spin and sections of ball
var theta = 0;
//for the sections of the ball
var theta2 = 0;
//fort he amount of sections needed
var seventh = (Math.PI*2)/7
//to control the amount of spin the ball has
var thetaInc = 0.0029;
//ball object
class Ball {
constructor(x,y,r,xvel,yvel,mass){
this.x =x;
this.y = y;
this.r =r;
this.xvel = xvel;
this.yvel = yvel;
this.mass = mass;
}
draw(){
// Update the y location.
this.x = this.x + this.xvel;
this.y = this.y + this.yvel;
//draw circle
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
ctx.stroke();
//fill the circle
ctx.fillStyle = "orange";
ctx.fill();
//draw inner circle of ball
ctx.beginPath();
ctx.arc(this.x,this.y,this.r*.9,0,Math.PI*2,false);
ctx.stroke();
//spin control
theta += thetaInc;
//loop for adding sections to pie
for( var n = 0; n < 7; ++n) { // add loop to draw radii
theta2 = theta + n * seventh;
ctx.moveTo( this.x, this.y);
ctx.lineTo( this.x + this.r*Math.cos(theta2), this.y + this.r*Math.sin(theta2));
}
ctx.lineWidth = "2";
ctx.lineCap = "round";
ctx.strokeStyle = "black";
ctx.stroke();
}
move(){
//condition take into account the this.r of the ball so
//it bounces at the edge of the canvas instead
//of going off of the screen to its center point.
if(this.y > canvas.height - this.r || this.y - this.r <0){
this.yvel = -1*this.yvel;
//to reverse the direction of the ball when hitting walls
if((this.xvel<0 && this.yvel >0) && thetaInc <0){
thetaInc = -1*thetaInc;
}
else if((this.xvel <0 && this.yvel>0) && thetaInc >0){
thetaInc = -1*thetaInc
}
else if((this.xvel >0 && this.yvel >0) && thetaInc >0){
thetaInc = -1 * thetaInc;
}
else if((this.xvel > 0 && this.yvel < 0)&& thetaInc <0){
thetaInc = -1 * thetaInc;
}
}
if(this.x > canvas.width - this.r || this.x - this.r < 0){
this.xvel = -1*this.xvel;
}
}
resize(){
this.up = true;
this.r = 0;
this.increment = 10;
this.ceiling = 100;
function PerformCalc() {
if (this.up == true && this.r <= this.ceiling) {
this.r += increment
if (this.r == ceiling) {
this.up = false;
}
} else {
this.up = false
this.r -= increment;
if (this.r == 0) {
this.up = true;
}
}
console.log(this.r);
}
setInterval(PerformCalc, 1000);
}
colour(){
}
}
//Intersect function takes a ball as a perameter
//ball will be the the object used to test if the two are touching.
function intersect(ball,ball1) {
//the x and y cordinates of the first ball are subtracted from the test ball and stored
//in productX and productY
var productX = ball1.x - ball.x;
var productY = ball1.y - ball.y;
//pythagoras theorem is used to get the distance between both center points of each circle.
var distance = Math.sqrt(productX * productX + productY * productY);
//A condition is used to check if the distance between both bencer point of each circle
//is less than or equal to the sum of both radii the circles are touching.
//the result is p[rinted out to the console
if (distance <= (ball1.r + ball.r)) {
dx = ball.x-ball1.x;
dy = ball.y-ball1.y;
collision_angle = Math.atan2(dy,dx);
magnitude_1 = Math.sqrt(ball.xvel*ball.xvel+ball.yvel*ball.yvel);
magnitude_2 = Math.sqrt(ball1.xvel*ball1.xvel+ball1.yvel*ball1.yvel);
direction_1 = Math.atan2(ball.yvel, ball.xvel);
direction_2 = Math.atan2(ball1.yvel, ball1.xvel);
new_xvel_1 = magnitude_1 * Math.cos(direction_1-collision_angle);
new_yvel_1 = magnitude_1 * Math.sin(direction_1-collision_angle);
new_xvel_2 = magnitude_2 * Math.cos(direction_2-collision_angle);
new_yvel_2 = magnitude_1 * Math.sin(direction_2-collision_angle);
final_xvel_1 = ((ball.mass-ball1.mass)*new_xvel_1+(ball1.mass+ball1.mass)*new_xvel_2)/(ball.mass+ball1.mass);
final_xvel_2 = ((ball.mass+ball.mass)*new_xvel_1+(ball1.mass-ball.mass)*new_xvel_2)/(ball.mass+ball1.mass);
final_yvel_1 = new_yvel_1;
final_yvel_2 = new_yvel_2;
ball.xvel = Math.cos(collision_angle)*final_xvel_1+Math.cos(collision_angle+Math.PI/2)*final_yvel_1;
ball.yvel = Math.sin(collision_angle)*final_xvel_1+Math.sin(collision_angle+Math.PI/2)*final_yvel_1;
ball1.xvel = Math.cos(collision_angle)*final_xvel_2+Math.cos(collision_angle+Math.PI/2)*final_yvel_2;
ball1.yvel = Math.sin(collision_angle)*final_xvel_2+Math.sin(collision_angle+Math.PI/2)*final_yvel_2;
}
}
canvas.addEventListener("click", function(event) {
var clickX = event.clientX - canvas.offsetLeft;
var clickY = event.clientY- canvas.offsetTop;
b1.x = clickX;
b1.y = clickY;
});
// Add a Javascript event listener to the keypress event.
window.addEventListener("keypress", function(event) {
// Just log the event to the console.
console.log(event);
});
//keypresses with jQuery
$(document.body).on('keydown', function(e) {
console.log(e.which);
switch (e.which) {
// key code for left arrow
case 37:
console.log('left arrow key pressed!');
b1.xvel --;
break;
//keycode for up
case 38:
console.log('up key pressed');
b1.yvel++;
break;
//key code for right
case 39:
console.log('right arrow key pressed!');
b1.xvel++;
break;
//key code for down
case 40:
console.log('down arrow key pressed!');
b1.yvel--;
break;
//key code for + key to increase spin
case 107:
console.log('down arrow key pressed!');
thetaInc +=.001;
break;
//key code for - key to decrease spin
case 109:
console.log('down arrow key pressed!');
thetaInc -=.001;
break;
}
});
b1 = new Ball(200,200,40,1,1,50);
b2 = new Ball(100,100,40,2,2,5);
b1.resize();
// A function to repeat every time the animation loops.
function repeatme() {
//clear canvas for each frame of the animation.
ctx.clearRect(0,0,500,500);
// Draw the ball (stroked, not filled).
b1.draw();
b2.draw();
b1.move();
b2.move();
intersect(b1,b2);
//put repeatme function into the animation frame and store it in animate
animate = window.requestAnimationFrame(repeatme);
}
// Get the animation going.
repeatme();
</script>
</body>
</html>
There is a reference error for increment, e.g. you didn't use this.increment, same for ceiling, should be this.ceiling.
this is used by the setInterval so you save this as that so you can use them. this removes the NaN.
<!DOCTYPE html>
<html>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<head>
<meta charset="UTF-8">
<title>Canvas</title>
<style type="text/css">
canvas {
border: 1px solid grey;
}
</style>
</head>
<body>
<canvas id="canvas-for-ball"></canvas>
<script type="text/javascript">
// Gets a handle to the element with id canvasOne.
var canvas = document.getElementById("canvas-for-ball");
// Get a 2D context for the canvas.
var ctx = canvas.getContext("2d");
function init(){
canvas.width = 500;
canvas.height = 500;
}
init();
//angle defining spin and sections of ball
var theta = 0;
//for the sections of the ball
var theta2 = 0;
//fort he amount of sections needed
var seventh = (Math.PI*2)/7
//to control the amount of spin the ball has
var thetaInc = 0.0029;
//ball object
class Ball {
constructor(x,y,r,xvel,yvel,mass){
this.x =x;
this.y = y;
this.r =r;
this.xvel = xvel;
this.yvel = yvel;
this.mass = mass;
}
draw(){
// Update the y location.
this.x = this.x + this.xvel;
this.y = this.y + this.yvel;
//draw circle
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
ctx.stroke();
//fill the circle
ctx.fillStyle = "orange";
ctx.fill();
//draw inner circle of ball
ctx.beginPath();
ctx.arc(this.x,this.y,this.r*.9,0,Math.PI*2,false);
ctx.stroke();
//spin control
theta += thetaInc;
//loop for adding sections to pie
for( var n = 0; n < 7; ++n) { // add loop to draw radii
theta2 = theta + n * seventh;
ctx.moveTo( this.x, this.y);
ctx.lineTo( this.x + this.r*Math.cos(theta2), this.y + this.r*Math.sin(theta2));
}
ctx.lineWidth = "2";
ctx.lineCap = "round";
ctx.strokeStyle = "black";
ctx.stroke();
}
move(){
//condition take into account the this.r of the ball so
//it bounces at the edge of the canvas instead
//of going off of the screen to its center point.
if(this.y > canvas.height - this.r || this.y - this.r <0){
this.yvel = -1*this.yvel;
//to reverse the direction of the ball when hitting walls
if((this.xvel<0 && this.yvel >0) && thetaInc <0){
thetaInc = -1*thetaInc;
}
else if((this.xvel <0 && this.yvel>0) && thetaInc >0){
thetaInc = -1*thetaInc
}
else if((this.xvel >0 && this.yvel >0) && thetaInc >0){
thetaInc = -1 * thetaInc;
}
else if((this.xvel > 0 && this.yvel < 0)&& thetaInc <0){
thetaInc = -1 * thetaInc;
}
}
if(this.x > canvas.width - this.r || this.x - this.r < 0){
this.xvel = -1*this.xvel;
}
}
resize(){
var that = this;
that.up = true;
that.r = 0;
that.increment = 10;
that.ceiling = 100;
function PerformCalc() {
if (that.up == true && that.r <= that.ceiling) {
that.r += that.increment
if (that.r == that.ceiling) {
that.up = false;
}
} else {
that.up = false
that.r -= that.increment;
if (that.r == 0) {
that.up = true;
}
}
console.log(that.r);
}
setInterval(PerformCalc, 1000);
}
colour(){
}
}
//Intersect function takes a ball as a perameter
//ball will be the the object used to test if the two are touching.
function intersect(ball,ball1) {
//the x and y cordinates of the first ball are subtracted from the test ball and stored
//in productX and productY
var productX = ball1.x - ball.x;
var productY = ball1.y - ball.y;
//pythagoras theorem is used to get the distance between both center points of each circle.
var distance = Math.sqrt(productX * productX + productY * productY);
//A condition is used to check if the distance between both bencer point of each circle
//is less than or equal to the sum of both radii the circles are touching.
//the result is p[rinted out to the console
if (distance <= (ball1.r + ball.r)) {
dx = ball.x-ball1.x;
dy = ball.y-ball1.y;
collision_angle = Math.atan2(dy,dx);
magnitude_1 = Math.sqrt(ball.xvel*ball.xvel+ball.yvel*ball.yvel);
magnitude_2 = Math.sqrt(ball1.xvel*ball1.xvel+ball1.yvel*ball1.yvel);
direction_1 = Math.atan2(ball.yvel, ball.xvel);
direction_2 = Math.atan2(ball1.yvel, ball1.xvel);
new_xvel_1 = magnitude_1 * Math.cos(direction_1-collision_angle);
new_yvel_1 = magnitude_1 * Math.sin(direction_1-collision_angle);
new_xvel_2 = magnitude_2 * Math.cos(direction_2-collision_angle);
new_yvel_2 = magnitude_1 * Math.sin(direction_2-collision_angle);
final_xvel_1 = ((ball.mass-ball1.mass)*new_xvel_1+(ball1.mass+ball1.mass)*new_xvel_2)/(ball.mass+ball1.mass);
final_xvel_2 = ((ball.mass+ball.mass)*new_xvel_1+(ball1.mass-ball.mass)*new_xvel_2)/(ball.mass+ball1.mass);
final_yvel_1 = new_yvel_1;
final_yvel_2 = new_yvel_2;
ball.xvel = Math.cos(collision_angle)*final_xvel_1+Math.cos(collision_angle+Math.PI/2)*final_yvel_1;
ball.yvel = Math.sin(collision_angle)*final_xvel_1+Math.sin(collision_angle+Math.PI/2)*final_yvel_1;
ball1.xvel = Math.cos(collision_angle)*final_xvel_2+Math.cos(collision_angle+Math.PI/2)*final_yvel_2;
ball1.yvel = Math.sin(collision_angle)*final_xvel_2+Math.sin(collision_angle+Math.PI/2)*final_yvel_2;
}
}
canvas.addEventListener("click", function(event) {
var clickX = event.clientX - canvas.offsetLeft;
var clickY = event.clientY- canvas.offsetTop;
b1.x = clickX;
b1.y = clickY;
});
// Add a Javascript event listener to the keypress event.
window.addEventListener("keypress", function(event) {
// Just log the event to the console.
console.log(event);
});
//keypresses with jQuery
$(document.body).on('keydown', function(e) {
console.log(e.which);
switch (e.which) {
// key code for left arrow
case 37:
console.log('left arrow key pressed!');
b1.xvel --;
break;
//keycode for up
case 38:
console.log('up key pressed');
b1.yvel++;
break;
//key code for right
case 39:
console.log('right arrow key pressed!');
b1.xvel++;
break;
//key code for down
case 40:
console.log('down arrow key pressed!');
b1.yvel--;
break;
//key code for + key to increase spin
case 107:
console.log('down arrow key pressed!');
thetaInc +=.001;
break;
//key code for - key to decrease spin
case 109:
console.log('down arrow key pressed!');
thetaInc -=.001;
break;
}
});
b1 = new Ball(200,200,40,1,1,50);
b2 = new Ball(100,100,40,2,2,5);
b1.resize();
// A function to repeat every time the animation loops.
function repeatme() {
//clear canvas for each frame of the animation.
ctx.clearRect(0,0,500,500);
// Draw the ball (stroked, not filled).
b1.draw();
b2.draw();
b1.move();
b2.move();
intersect(b1,b2);
//put repeatme function into the animation frame and store it in animate
animate = window.requestAnimationFrame(repeatme);
}
// Get the animation going.
repeatme();
</script>
</body>
</html>
I have to make a game for school :)
I'am a beginner in Javascript (and English) and I have a question about a canvas.
I made a spaceship in my canvas, and I can fly around with it with my arrowkeys. The problem is that my spaceship can fly trough the border of my canvas.. and thats not way it ment to be.
I hope one of you guys can help me with it! :)
Here is my code..
window.onload = init;
var ctx;
var x = 750;
var y = 400;
var up = false;
var right = false;
var left = false;
var gravity = -1.1;
var horizontalSpeed = 0.1;
function init() {
document.onkeydown = handleKeyDown;
document.onkeyup = handleKeyUp;
var canvas = document.querySelector("canvas");
ctx = canvas.getContext("2d");
animate();
}
function animate() {
moveShip();
drawScene();
requestAnimationFrame(animate);
}
function drawScene(){
ctx.clearRect(0,0,1600,800);
drawSpaceship();
}
function moveShip() {
if (left) {
horizontalSpeed -= 0.1;
}else if (right){
horizontalSpeed += 0.1;
}
if (up) {
gravity -=0.4;
}
gravity += 0.12;
y += gravity;
x += horizontalSpeed;
}
function drawSpaceship () {
ctx.save();
ctx.translate(x, y);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, -40);
ctx.lineTo(30,-80);
ctx.lineTo(60,-40);
ctx.lineTo(60,0);
ctx.lineTo(80,30);
ctx.lineTo(80,90);
ctx.lineTo(60,50);
ctx.lineTo(0,50);
ctx.lineTo(-20,90);
ctx.lineTo(-20,30);
ctx.lineTo(0,0);
ctx.strokeStyle = 'white';
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function handleKeyDown (evt) {
evt = evt || window.event;
switch (evt.keyCode) {
case 37:
left = true;
break;
case 38:
up = true;
break;
case 39:
right = true;
break;
}
}
function handleKeyUp(evt) {
evt = evt || window.event;
switch (evt.keyCode) {
case 37:
left = false;
break;
case 38:
up = false;
break;
case 39:
right = false;
break;
}
}
You need a concept called "clamping".
Clamping is limiting a value within a specified range.
You want to clamp your ship's horizontal position to be no less than 0 and no greater than the canvas width.
You want to clamp your ship's vertical position to be no less than 0 and no greater than the canvas height.
This is code to clamp a value between a min and max value.
clampedValue = Math.min(Math.max(currentValue, min), max);
To keep any [x,y] within the canvas, you can clamp the x,y like this:
clampedX = Math.min(Math.max(x, 0), canvasWidth);
clampedY = Math.min(Math.max(y, 0), canvasHeight);
Now your ship is larger than just a single [x,y] point. So to keep your entire ship inside your canvas you would clamp the [x,y] like this:
y += gravity;
x += horizontalSpeed;
x=Math.min(Math.max(x,0+20),width-80);
y=Math.min(Math.max(y,0+80),height-90);
Also, you're letting gravity increase even if you ship is on the ground. At that point the gravity effect becomes zero because the ship is being entirely supported by the ground against gravity. Therefore, you'll want to set gravity to zero when the ship is on the ground:
if(y==height-90){gravity=0;}
Here's your code refactored to keep your ship inside the canvas:
var x = 200;
var y = 100;
var up = false;
var right = false;
var left = false;
var gravity = -1.1;
var horizontalSpeed = 0.1;
var canvas = document.querySelector("canvas");
var width=canvas.width;
var height=canvas.height;
var ctx = canvas.getContext("2d");
init();
function init() {
document.onkeydown = handleKeyDown;
document.onkeyup = handleKeyUp;
animate();
}
function animate() {
moveShip();
drawScene();
requestAnimationFrame(animate);
}
function drawScene(){
ctx.clearRect(0,0,1600,800);
drawSpaceship();
}
function moveShip() {
if (left) {
horizontalSpeed -= 0.1;
}else if (right){
horizontalSpeed += 0.1;
}
if (up) {
gravity -=0.4;
}
gravity += 0.12;
y += gravity;
x += horizontalSpeed;
x=Math.min(Math.max(x,0+20),width-80);
y=Math.min(Math.max(y,0+80),height-90);
if(y==height-90){gravity=0;}
}
function drawSpaceship () {
ctx.save();
ctx.translate(x, y);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, -40);
ctx.lineTo(30,-80);
ctx.lineTo(60,-40);
ctx.lineTo(60,0);
ctx.lineTo(80,30);
ctx.lineTo(80,90);
ctx.lineTo(60,50);
ctx.lineTo(0,50);
ctx.lineTo(-20,90);
ctx.lineTo(-20,30);
ctx.lineTo(0,0);
ctx.strokeStyle = 'blue';
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function handleKeyDown (evt) {
evt = evt || window.event;
switch (evt.keyCode) {
case 37:
left = true;
break;
case 38:
up = true;
break;
case 39:
right = true;
break;
}
}
function handleKeyUp(evt) {
evt = evt || window.event;
switch (evt.keyCode) {
case 37:
left = false;
break;
case 38:
up = false;
break;
case 39:
right = false;
break;
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Hold down the arrow keys to move the ship.</h4>
<canvas id="canvas" width=500 height=400></canvas>