object movement has unwanted increase in speed using setTimeout inside setInterval - javascript

I'm testing movement for a practice game I'm working on, what I'm trying to get going with is a loop that will run twice (because the object is a rabbit) inside of an interval so that the object won't move in a continuous circle and it'll have uniqueness to it. How I'm executing it is the interval will run every 1500 miliseconds and the timeout (inside the interval) will run in half of that time to create the rabbit moving in one direction twice instead of once. The problem is after some time the bunny will take larger steps forword and do so faster. I'm not completely sure what the problem is, thank you for looking at this. Heres my code
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var wabbits = {
wabbit1: {
x: 200,
y: 200,
w: 10,
h: 10,
speed: 2,
moving: "",
dead: false,
updateInterval: 2000
}
};
//make it easier to type out the object
var bunny = wabbits.wabbit1;
var movement = ["up", "down", "left", "right"];
var left = "left";
var up = "up";
var down = "down";
var right = "right";
var update = setInterval(function(){
draw();
}, 1);
canvas.style.backgroundColor = "green";
function draw(){
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "grey";
context.fillRect(bunny.x, bunny.y, bunny.w, bunny.h);
context.fill();
border();
}
function border(){
if(bunny.x <= 0){
bunny.x += bunny.speed * 2;
}
if(bunny.x >= 490){
bunny.x -= bunny.speed * 2;
}
if(bunny.y <= 0){
bunny.y += bunny.speed * 2;
}
if(bunny.y >= 490){
bunny.y -= bunny.speed * 2;
}
}
function bunny1move(){
if(!wabbits.wabbit1.dead){
var randM = Math.floor(Math.random() * 3) + 0;
wabbits.wabbit1.moving = movement[randM];
function mv(){
switch(wabbits.wabbit1.moving){
case "up":
wabbits.wabbit1.y -= wabbits.wabbit1.speed;
break;
case "down":
wabbits.wabbit1.y += wabbits.wabbit1.speed;
break;
case "left":
wabbits.wabbit1.x -= wabbits.wabbit1.speed;
break;
case "right":
wabbits.wabbit1.x += wabbits.wabbit1.speed;
break;
default:
console.log("something in bunny1.mv() is not working properly, err: " + wabbits.wabbits1.moving);
break;
};
if(wabbits.wabbit1.y <= 0){
wabbits.wabbit1.y += wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = down;
}
if(wabbits.wabbit1.y >= 758){
wabbits.wabbit1.y -= wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = up;
}
if(wabbits.wabbit1.x <= 0){
wabbits.wabbit1.x += wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = right;
}
if(wabbits.wabbit1.x >= 1356){
wabbits.wabbit1.x -= wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = left;
}
//make mv repeat twice
this.setTimeout(mv, wabbits.wabbit1.updateInterval / 2);
}
mv();
}
}
//update the movement function
setInterval(bunny1move, wabbits.wabbit1.updateInterval);
<canvas id="canvas" height="500px" width="500px"></canvas>

You immediately invoke mv after creating it in bunny1move, but after each call to mv it sets a timeout to call itself again one second later. After it gets called again it sets a new timeout to call itself after the next second, and so on. This chain of calls extends ad infinitum.
This by itself wouldn't be too bad, actually it seems like it would be what you want, to have mv called every second. The problem happens when you call bunny1move every two seconds, each of these repeating calls to bunny1move creates a new chain of mv's and they all stack on top of the previous chains.
So it's not so much that the bunny is moving farther on each iteration, it's that more and more mv chains are being created, and they are all being called in sync, making it look like a longer jump when really it's just many more little jumps.
If you put a console.log statement at the end of mv you can see this increase in mv calls over time.
You should just be able to get rid of mv altogether, then call setInterval on bunny1move for every second:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var wabbits = {
wabbit1: {
x: 200,
y: 200,
w: 10,
h: 10,
speed: 2,
moving: "",
dead: false,
updateInterval: 2000
}
};
//make it easier to type out the object
var bunny = wabbits.wabbit1;
var movement = ["up", "down", "left", "right"];
var left = "left";
var up = "up";
var down = "down";
var right = "right";
var update = setInterval(function(){
draw();
}, 1);
canvas.style.backgroundColor = "green";
function draw(){
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "grey";
context.fillRect(bunny.x, bunny.y, bunny.w, bunny.h);
context.fill();
border();
}
function border(){
if(bunny.x <= 0){
bunny.x += bunny.speed * 2;
}
if(bunny.x >= 490){
bunny.x -= bunny.speed * 2;
}
if(bunny.y <= 0){
bunny.y += bunny.speed * 2;
}
if(bunny.y >= 490){
bunny.y -= bunny.speed * 2;
}
}
function bunny1move(){
if(!wabbits.wabbit1.dead){
var randM = Math.floor(Math.random() * 3) + 0;
wabbits.wabbit1.moving = movement[randM];
switch(wabbits.wabbit1.moving){
case "up":
wabbits.wabbit1.y -= wabbits.wabbit1.speed;
break;
case "down":
wabbits.wabbit1.y += wabbits.wabbit1.speed;
break;
case "left":
wabbits.wabbit1.x -= wabbits.wabbit1.speed;
break;
case "right":
wabbits.wabbit1.x += wabbits.wabbit1.speed;
break;
default:
console.log("something in bunny1.mv() is not working properly, err: " + wabbits.wabbits1.moving);
break;
};
if(wabbits.wabbit1.y <= 0){
wabbits.wabbit1.y += wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = down;
}
if(wabbits.wabbit1.y >= 758){
wabbits.wabbit1.y -= wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = up;
}
if(wabbits.wabbit1.x <= 0){
wabbits.wabbit1.x += wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = right;
}
if(wabbits.wabbit1.x >= 1356){
wabbits.wabbit1.x -= wabbits.wabbit1.speed * 2;
wabbits.wabbit1.moving = left;
}
}
}
//update the movement function
setInterval(bunny1move, wabbits.wabbit1.updateInterval / 2);
<canvas id="canvas" height="500px" width="500px"></canvas>
As an aside, you should consider posting this on https://codereview.stackexchange.com/ . They should have a few constructive comments on better ways to design your program for more flexibility, extendability, readability etc.

Related

How to fix the direction and body problems?

i'm a rookie and i try to learn by myself from different sources html css and now i'm on the block of JS
I'm actually doing a snake game but i have differents issues , the body of my snake supposed to be 3 blocks width , and he is also supposed to move with the arrow key !!
I'm kind stuck and request your knowledge to let me find the light again :)
//* I'm french and hope my english is correct *//
window.onload = function()
{
var canvasWidth = 900;
var canvasHeight = 600;
var blockSize = 30;
var ctx;
var delay = 100;
var snakee;
init();
function init()
{
var canvas = document.createElement('canvas')
canvas.width = canvasWidth;
canvas.height = canvasHeight;
canvas.style.border = "1px solid"
document.body.appendChild(canvas);
ctx = canvas.getContext('2d');
snakee = new Snake([[6,4],[5,4],[4,4],[3,4],[2,4]], "right");
refreshCanvas();
}
function refreshCanvas()
{
ctx.clearRect (0,0,canvasWidth, canvasHeight);
snakee.draw();
snakee.advance();
setTimeout(refreshCanvas, delay);
}
function drawBlock(ctx, position)
{
var x = position[0] * blockSize;
var y = position[1] * blockSize;
ctx.fillRect(x ,y , blockSize , blockSize );
}
function Snake(body, _direction)
{
this.body = body;
this.direction = "right";
this.draw = function ()
{
ctx.save();
ctx.fillStyle = "#ff0000";
for(var i = 0; i < this.body.lenght; i++);
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
};
this.advance = function()
{
var nextPosition = this.body[0].slice();
switch(this.direction)
{
case "left":
nexPosition[0] -= 1;
break;
case "right":
nextPosition[0] += 1;
break;
case "down":
nextPosition[1] -= 1;
break;
case "up":
nextPosition[1] += 1;
break;
default:
throw("Invalid Direction");
}
this.body.unshift(nextPosition);
this.body.pop();
};
this.setDirection = function(newDirection)
{
var allowedDirections;
switch(this.direction)
{
case "left":
case "right":
allowedDirections = ["up", "donw"];
break;
case "down":
case "up":
allowedDirections = ["left","right"]
break;
default:
throw("Invalid Direction");
}
if(allowedDirections.indexOf(newDirection) > -1 )
{
this.direction = newDirection;
}
};
}
}
document.addEventListener = function handleKeyDown(e)
{
const key = e.keyCode;
var newDirection;
switch(key)
{
case 37:
newDirection = "left";
break;
case 38:
newDirection = "right";
break;
case 39:
newDirection = "up";
break;
case 40:
newDirection = "down";
break;
default:
return;
}
snakee.setDirection(newDirection);
}
addEventListener is a function and takes at least two arguments
it should be:
document.addEventListener('keydown', (e) => {
let code = e.keyCode;
})
Where the first arg is the type of event to handle and the second is the callback - handling the event.
I've noticed a few bugs in your code:
for(var i = 0; i < this.body.*lenght*; i++); << remove semicolon, lenght is typo
case "down":
nextPosition[1] -= 1; << should incrementing
break;
case "up":
nextPosition[1] += 1; << should decrementing
break;
Also the addEventListener code should be inside the window.onload statement
or else it will not see the snakee object.
This code simply won't work for a full blown snake program, because
each block of the snake can have a separate direction, but it's a start.

If statement inside requestanimationframe continues even if parameters aren't met

I'm making a little video game, where the background (platform) moves down with my character (square) jumps. On line 113 I have tried different strategies to implement this thought, and that is the closest it has gotten to work. The issue is that even when the square velocity is 0, the platform velocity isn't. I have been stuck for a week now, and feels like javascript is the problem (even though I know it isn't).
canvasmain.width = window.innerWidth;
canvasmain.height = window.innerHeight;
//CHARACTER:
const square = {
height: 75,
jumping: true,
width: 75,
x: canvasmain.width / 2,
xVelocity: 0,
y: canvasmain.height / 2,
yVelocity: 0
};
//floor
const platform = {
height: 30,
width: 100,
x: square.x,
xVelocity: 0,
y: square.y + square.height,
yVelocity:0
}
//MOVEMENT:
const controller = {
left: false,
right: false,
up: false,
keyListener: function (event) {
let key_state = (event.type == "keydown") ? true : false;
switch (event.keyCode) {
case 37: // left arrow
controller.left = key_state;
break;
case 38: // up arrow
controller.up = key_state;
break;
case 39: // right arrow
controller.right = key_state;
break;
}
}
};
const loop = function () {
if (controller.up && square.jumping == false) {
square.yVelocity -= 30;
square.jumping = true;
}
if (controller.left) {
square.xVelocity -= 0.5;
}
if (controller.right) {
square.xVelocity += 0.5;
}
square.yVelocity += 1.5;// gravity
square.x += square.xVelocity;
square.y += square.yVelocity;
square.xVelocity *= 0.9;// friction
square.yVelocity *= 0.9;// friction
// if square is falling below floor line
if (square.y > canvasmain.height - 75) {
square.jumping = false;
square.y = canvasmain.height - 75;
square.yVelocity = 0;
}
// if square is going off the left of the screen
if (square.x < 0) {
square.x = 0;
} else if (square.x > canvasmain.width - 75) {// if square goes past right boundary
square.x = canvasmain.width - 75;
}
// Creates the backdrop for each frame
context.fillStyle = "#394129";
context.fillRect(0, 0, canvasmain.width, canvasmain.height); // x, y, width, height
// Creates and fills the cube for each frame
context.fillStyle = "#8DAA9D"; // hex for cube color
context.beginPath();
context.rect(square.x, square.y, square.width, square.height);
context.fill();
// Creates the "ground" for each frame
context.fillStyle = "#202020";
context.beginPath();
context.rect(platform.x, platform.y, platform.width, platform.height)
context.fill();
// call update when the browser is ready to draw again
window.requestAnimationFrame(loop);
//platform
platform.y += platform.yVelocity;
console.log(square.yVelocity)
if (square.yVelocity > 0.1 ) {
platform.yVelocity = 1.5
};
if (square.yVelocity < 0 ) {
platform.yVelocity = -1.5
};
}
window.addEventListener("keydown", controller.keyListener)
window.addEventListener("keyup", controller.keyListener);
window.requestAnimationFrame(loop);
You're not reseting the value of yVelocity.
Got it guys, thx #Teemu, I just needed to set it to 0 if it wasn't the if's. Dang, really took a while. if (square.yVelocity > 0 ) {platform.yVelocity = 1.5} else{ platform.yVelocity = 0}; if (square.yVelocity < 0 ) {platform.yVelocity = -1.5} else { platform.yVelocity = 0} }
edit: frick, it only moves the platform when the cube is going up, not down ;-;

How do I add a counter in my javascript game?

I got this game coded in Javascript:
window.onload = function() {
canv = document.getElementById("gc");
ctx = canv.getContext("2d");
document.addEventListener("keydown", keyPush);
setInterval(game, 1000 / 7);
}
px = py = 10;
gs = tc = 27;
ax = ay = 15;
xv = yv = 0;
trail = [];
tail = 2;
function game () {
px += xv;
py += yv;
if (px < 0) {
px = tc - 1;
}
if (px > tc - 1) {
px = 0;
}
if (py < 0) {
py = tc-1;
}
if (py > tc - 1) {
py = 0;
}
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canv.width, canv.height);
ctx.fillStyle = "lime";
for(var i = 0; i < trail.length; i++) {
ctx.fillRect(trail[i].x * gs, trail[i].y * gs, gs - 2, gs - 2);
if (trail[i].x == px && trail[i].y == py) {
tail = 2;
}
}
trail.push({ x: px, y: py });
while(trail.length > tail) {
trail.shift();
}
if (ax == px && ay == py) {
tail++;
ax = Math.floor(Math.random() * tc);
ay = Math.floor(Math.random() * tc);
}
ctx.fillStyle = "red";
ctx.fillRect(ax * gs, ay * gs, gs - 2, gs - 2);
}
function keyPush(evt) {
switch(evt.keyCode) {
case 37:
xv = -1; yv = 0;
break;
case 38:
xv = 0; yv = -1;
break;
case 39:
xv = 1; yv = 0;
break;
case 40:
xv = 0; yv = 1;
break;
}
}
<canvas id="gc" width="729" height="729"></canvas>
And I want to add a counter anywhere on the page, so it counts how long the "tail" is.
I have tried a little myself but it doesn't seem to work, any ideas how I should do?
Also another question... how do I change the code with a button or text field on a webpage? Like for example changing:
setInterval(game,1000/7);
to
setInterval(game,1000/9);
with a button or a text field, where you can type the numbers and it gets pasted into the code?
And i want to add a counter anywhere on the page, so it counts how
long the "tail" is. I have tried a little myself but it dosen't seem
to work, any ideas how i do?
For this question, you can add an element to the page and then update it's text in javascript each time the tail changes length (using the innerText property of the element).
Sample html element:
<!-- inside the body element -->
<div>
<span>Tail Count: </span>
<span id="tail-counter">2</span>
</div>
And the javascript:
while (trail.length > tail) {
trail.shift();
document.getElementById("tail-counter").innerText = tail;
}
if (ax == px && ay == py) {
tail++;
document.getElementById("tail-counter").innerText = tail;
ax = Math.floor(Math.random() * tc);
ay = Math.floor(Math.random() * tc);
}
Also another queston... how do i change the code with a button or text
field on a webpage? Like for example changing
setInterval(game,1000/7); to setInterval(game,1000/9); with a button
or a text field, where you can type the numbers and it gets pasted
into the code?
For this question, to change the code based on a text field, you need to first add the text field to the page:
<div>
<span>Set Game Speed: </span>
<input id="game-speed" type="number" value="7" />
</div>
Then you can use javascript to get the value of the text field and use it in your code
game_speed = Number.parseInt(document.getElementById("game-speed").value);
interval = setInterval(game, 1000 / game_speed);
Now all together (note that this code allows you to change the game speed while playing. This is done by clearing the interval you already made with clearInterval and then setting a new interval with the new game speed)
<canvas id="gc" width="729" height="729"></canvas>
<body style="overflow-x: hidden; overflow-y: hidden;">
<div>
<span>Tail Count: </span>
<span id="tail-counter">2</span>
</div>
<div>
<span>Set Game Speed: </span>
<input id="game-speed" type="number" value="7" />
</div>
</body>
<script>
px = py = 10;
gs = tc = 27;
ax = ay = 15;
xv = yv = 0;
trail = [];
tail = 2;
game_speed = 7;
interval = {};
window.onload = function () {
canv = document.getElementById("gc");
ctx = canv.getContext("2d");
document.addEventListener("keydown", keyPush);
game_speed = Number.parseInt(document.getElementById("game-speed").value);
interval = setInterval(game, 1000 / game_speed);
}
function game() {
let new_speed = Number.parseInt(document.getElementById("game-speed").value);
if (new_speed !== game_speed) {
clearInterval(interval);
game_speed = new_speed;
interval = setInterval(game, 1000 / game_speed);
}
px += xv;
py += yv;
if (px < 0) {
px = tc - 1;
}
if (px > tc - 1) {
px = 0;
}
if (py < 0) {
py = tc - 1;
}
if (py > tc - 1) {
py = 0;
}
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canv.width, canv.height);
ctx.fillStyle = "lime";
for (var i = 0; i < trail.length; i++) {
ctx.fillRect(trail[i].x * gs, trail[i].y * gs, gs - 2, gs - 2);
if (trail[i].x == px && trail[i].y == py) {
tail = 2;
}
}
trail.push({
x: px,
y: py
});
while (trail.length > tail) {
trail.shift();
document.getElementById("tail-counter").innerText = tail;
}
if (ax == px && ay == py) {
tail++;
document.getElementById("tail-counter").innerText = tail;
ax = Math.floor(Math.random() * tc);
ay = Math.floor(Math.random() * tc);
}
ctx.fillStyle = "red";
ctx.fillRect(ax * gs, ay * gs, gs - 2, gs - 2);
}
function keyPush(evt) {
switch (evt.keyCode) {
case 37:
xv = -1;
yv = 0;
break;
case 38:
xv = 0;
yv = -1;
break;
case 39:
xv = 1;
yv = 0;
break;
case 40:
xv = 0;
yv = 1;
break;
}
}
</script>
<html>
To use predefined value as you setInterval timer, all you need to do is to find your best method of getting user inputs, e.g using an input box. Then you can grab the value with javascript
E.g
[HTML]
<input type="number" value="500" id="num">
[JS]
let num = document.getElementById('num').value;
num = +num; //cast the value to number since its returned as a string.
setInterval(game, num/nth); //nth is any value of your choice, or you can also grab from user input
```
For your first question, getting how long the tail is depends on how you want it.
you can use a variable and always increment it, each time your code condition is met.
In order to write the trail length in the canvas, I added to your code
ctx.fillStyle = "blue";
ctx.fillText(trail.length, 100, 100);
ctx.font = "bold 20px sans-serif";
ctx.textBaseline = "bottom";
that way it constantly prints the trail length at the top left corner.
and as for your second question, I added the intervalTime variable at the global scope so you could change it and then the next interval will be in the time you entered to that variable.
var intervalTime = 1000/7;
window.onload=function() {
canv=document.getElementById("gc");
ctx=canv.getContext("2d");
document.addEventListener("keydown",keyPush);
setInterval(game, intervalTime);
}
px=py=10;
gs=tc=27;
ax=ay=15;
xv=yv=0;
trail=[];
tail = 2;
function game() {
px+=xv;
py+=yv;
if(px<0) {
px= tc-1;
}
if(px>tc-1) {
px= 0;
}
if(py<0) {
py= tc-1;
}
if(py>tc-1) {
py= 0;
}
ctx.fillStyle="black";
ctx.fillRect(0,0,canv.width,canv.height);
ctx.fillStyle = "blue";
ctx.fillText(trail.length, 100, 100);
ctx.font = "bold 20px sans-serif";
ctx.textBaseline = "bottom";
ctx.fillStyle="lime";
for(var i=0;i<trail.length;i++) {
ctx.fillRect(trail[i].x*gs,trail[i].y*gs,gs-2,gs-2);
if(trail[i].x==px && trail[i].y==py) {
tail = 2;
}
}
trail.push({x:px,y:py});
while(trail.length>tail) {
trail.shift();
}
if(ax==px && ay==py) {
tail++;
ax=Math.floor(Math.random()*tc);
ay=Math.floor(Math.random()*tc);
}
ctx.fillStyle="red";
ctx.fillRect(ax*gs,ay*gs,gs-2,gs-2);
}
function keyPush(evt) {
switch(evt.keyCode) {
case 37:
xv=-1;yv=0;
break;
case 38:
xv=0;yv=-1;
break;
case 39:
xv=1;yv=0;
break;
case 40:
xv=0;yv=1;
break;
}
}
<canvas id="gc" width="729" height="729"></canvas>

Detecting collision on canvas border

I am making a little game with my friends but i am trying to detect the collision when bumping in the the walls of the canvas, and I made it work but when i hit the wall my caracter gets stuck.. and i dont know how to find out a way to make him move again. Do you think any of you guys could help me out with this one. Thanks for all help!
Javascript / jQuery:
function Player() {
this.w = 50;
this.h = 60;
this.x = (cW / 2) - (this.w / 2);
this.y = (cH / 2) - (this.h / 2);
this.name = username.value;
this.nw = ctx.measureText(this.name).width; // username.clientWidth + 1;
this.src = playerImage;
this.dx = 0;
this.dy = 0;
this.render = function() {
ctx.drawImage(this.src, this.x, this.y, this.w, this.h);
ctx.fillStyle = 'orange';
ctx.font = '15px Arial, sans-serif';
// fixa x-värdet
ctx.fillText(this.name, (this.x + this.w / 2) - (this.nw / 2), (this.y - 6));
}
}
var player = new Player();
function animate() {
if(player.x > 0 && player.y > 0 && player.x + player.w < cW && player.y + player.h < cH) {
player.x += spd * player.dx;
player.y += spd * player.dy;
}
else {
// Need code here to release player from wall
}
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(bg, 0, 0);
player.render();
ctx.rotate(-.4);
raining();
ctx.restore();
}
var animateInterval = setInterval(animate, 1000/60);
document.addEventListener('keydown', function(e) {
var key_press = String.fromCharCode(e.keyCode);
switch(key_press) {
case 'W':
player.dy = -1;
break;
case 'A':
player.dx = -1;
break;
case 'S':
player.dy = 1;
break;
case 'D':
player.dx = 1;
break;
default:
break;
}
});
document.addEventListener('keyup', function(e) {
var key_press = String.fromCharCode(e.keyCode);
switch(key_press) {
case 'W':
player.dy = 0;
break;
case 'A':
player.dx = 0;
break;
case 'S':
player.dy = 0;
break;
case 'D':
player.dx = 0;
break;
default:
break;
}
});
The problem you are having is the following:
Suppose your character is moving at the speed of 10 pixels per frame, the character position is currently 595px (the right side of the character) and the canvas width is 600.
On the current frame you are checking if there is a collision: there's none so you add the speed to the current position and you get 605px. Now on the next frame the character is out the bounds and you cannot move him anymore because the player.x + player.width > canvas.width
What you can do:
1: You check for the collision and move the character back inside the viewport:
if (player.x + player.width > canvas.width) {
player.x = canvas.width - player.width;
}
Because the collision check fails now, you can move him wherever you want.
You should do this check for all the sides, but the logic is different for each side, this is sample code for collision with the left wall:
if (player.x < 0) {
player.x = 0;
}
2: In your if statement you should add the speed in your calculations and not move the character if the player.x + player.vx exceeds canvas.width, but I prefer the first method as you can actually be at the side of the viewport
Tip with working with canvas positions: always round off your positions at the render level:
this.render = function() {
ctx.drawImage(this.src, Math.round(this.x), Math.round(this.y), this.w, this.h);
...
}

How do I stop the screen flickering when moving my character by pressing the arrow keys in JavaScript?

Can anybody help me with my problem please? I've managed to clear the canvas before redrawing my game character, but was wondering how to stop the screen from flickering as the character moves in JavaScript. Thank you for your help. Help much appreciated. Here's my code.
var avatarX = 0;
var avatarY = 267;
var avatarImage;
var counter = 1;
var XWIDTH = 0;
var WIDTH = 400;
var dx = 5;
var tt;
window.addEventListener('keydown', KeyDown);
function setUpGame() { //This is the function that is called from the html document.
var gameCanvas = document.getElementById("gameCanvas"); //Declare a new variable & assigns it the id of the CANVAS from the html document.
avatarImage = new Image(); //Declaring a new variable. This is so that we can store the image at a later date.
avatarImage.src = "img/avatar.png"; //Ditto from above.
gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, avatarY);
var tt = setInterval(function(){counTer()},1000);
setInterval(handleTick, 25);
}
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
gameCanvas.width = gameCanvas.width;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
gameCanvas.width = gameCanvas.width;
}
break;
}
}
function counTer() {
if(counter == 60) {
clearInterval(tt);
} else {
counter++;
gameCanvas.width = 400;
gameCanvas.getContext("2d").font = "18px Iceland";
gameCanvas.getContext("2d").textBaseline = "top";
gameCanvas.getContext("2d").fillText("Seconds: " + counter, 5, 5);
}
}
function handleTick() {
gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
}
Some observations:
Create the context once at the top of your code instead of every time you use it.
Set the font and textBaseline once at the top of your code instead of every time you use it.
Clear the canvas only when needed (only in handleTick).
Draw the clock-timer at the same time you draw the avatar (saves 1 canvas clearing).
Attach an onload function to avatarImage so you know the image is ready to drawImage
Here's modified code -- it's untested so you'll need to review it ;)
Good luck with your project!
var avatarX = 0;
var avatarY = 267;
var avatarImage;
var counter = 1;
var XWIDTH = 0;
var WIDTH = 400;
var dx = 5;
var tt;
var gameCanvas;
var context;
window.addEventListener('keydown', KeyDown);
function setUpGame() { //This is the function that is called from the html document.
gameCanvas = document.getElementById("gameCanvas"); //Declare a new variable & assigns it the id of the CANVAS from the html document.
context=gameCanvas.getContext("2d");
context.font = "18px Iceland";
context.textBaseline = "top";
avatarImage = new Image(); //Declaring a new variable. This is so that we can store the image at a later date.
avatarImage.onload=function(){
// avatarImage is now fully loaded and ready to drawImage
context.drawImage(avatarImage, Math.random() * 100, avatarY);
// start the timer
tt = setInterval(function(){counTer()},1000);
setInterval(handleTick, 25);
}
avatarImage.src = "img/avatar.png"; //Ditto from above.
}
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
}
break;
}
}
function counTer() {
if(counter == 60) {
clearInterval(tt);
} else {
counter++;
}
}
function handleTick() {
context.clearRect(0,0,canvas.width,canvas.height);
context.drawImage(avatarImage, avatarX, avatarY);
context.fillText("Seconds: " + counter, 5, 5);
}

Categories

Resources