I am working on a school assignment to make a small game using HTML5 canvas and Javascript. The idea is based on space invaders but with kites (I know really badass). So I got the kite moving with key press and now I want it to show a different image when pressing left and right (kites bend to the left and right).
I got the left kite image on the screen but it also shows the normal kite. Also there is still no difference when pressing keys left and right.
Do I need to add another function to draw the left kite separately? Or add an if statement when key is true draw image ? This could all be wrong but I am kind of stuck here.
Here is my code
const FPS = 30;
var canvas = null;
var context = null;
window.onload = init ;
function init() {
canvas = document.getElementById('gameCanvas');
context = canvas.getContext('2d');
setInterval(loop, 1000 / FPS);
// register keyboard events
window.addEventListener("keydown", onKeyDown);
window.addEventListener("keyup", onKeyUp);
// Initialize the player object
player.vx = 0;
player.vy = 0;
player.image = new Image();
player.image.src = "./kite.png";
player.imgLeft = new Image();
player.imgLeft.src = "./kite-links.png";
player.x = canvas.width/2 - player.image.width/2;
player.y = canvas.height/1.3 - player.image.height/2;
player.speed = 10;
}
var player = Object(); // Initialize a player object
// Things to do when keys are down
function onKeyDown(event)
{
if (event.keyCode >= 37 && event.keyCode<=39)
event.preventDefault(); // prevent arrow keys from scrolling the page
switch (event.keyCode) {
case 37: player.vx = -1; break; // left key pressed
case 38: player.vy = -1; break; // up key pressed
case 39: player.vx = 1; break; // right key pressed
}
}
// Things to do when keys are up
function onKeyUp(event)
{
switch (event.keyCode) {
case 37: case 39: player.vx = 0; break; // left or right key released
case 38: player.vy = 0; break; // up or down key released
}
}
function update(){
// update all your objects here
player.x += player.vx * player.speed; // update player position
player.y += player.vy * player.speed;
}
function draw(){
// clear the screen
context.clearRect(0, 0, canvas.width, canvas.height);
// draw all your objects here
context.drawImage(player.image, player.x, player.y, player.image.width, player.image.height);
context.drawImage(player.imgLeft, player.x, player.y, player.image.width, player.image.height);
}
function loop() {
update();
draw();
}
You can have each of the images you want (up, right, and left) saved as properties on the player object, and change the main image (the one that is being drawn) whenever the player changes direction.
init:
//default to straight in init()
player.image = new Image();
player.image.src = "./kite.png";
player.imgUp = new Image();
player.imgUp.src = "./kite.png";
player.imgLeft = new Image();
player.imgLeft.src = "./kite-links.png";
player.imgRight = new Image();
player.imgRight.src = "./kite-right.png";
onKeyDown:
switch (event.keyCode) {
case 37: player.vx = -1; player.image = player.imgLeft; break; // left key
case 38: player.vy = -1; player.image = player.imgUp; break; // up key
case 39: player.vx = 1; player.image = player.imgRight; break; // right key
}
You also don't need to draw all of the images inside draw() like you have now. You only need to draw player.image
Related
I'm using socket.io in combination with Express to render a canvas multiplayer game, I wanted to use sprites and pixel art since the beginning, but when my canvas updates my sprite flickers a bit.
Here's the socket.io server code which first adds the connected player to players dictionary and then checks for movement emit in order to update players' position on the canvas :
let players = {};
io.on('connection', function (socket) {
players[socket.id] = { x: 300, y: 300};
socket.on('disconnect', function(){
delete players[socket.id];
});
socket.on('movement', function(data) {
let player = players[socket.id] || {};
if (data.left) {
player.x -= 5;
}
if (data.up) {
player.y -= 5;
}
if (data.right) {
player.x += 5;
}
if (data.down) {
player.y += 5;
}
});
});
// Here the server emits\send the "state" of the canvas
// looping passing the dictionary of players
setInterval(function() {
io.emit('state', players);
}, 1000 / 60);
And here's the client, where the problematic code is (imo):
var socket = io();
var width = window.innerWidth;
var height = window.innerHeight;
var movement = {
up: false,
down: false,
left: false,
right: false
}
document.addEventListener('keydown', function(event) {
switch (event.keyCode) {
case 65: // A
movement.left = true;
console.log("A");
break;
case 87: // W
movement.up = true;
console.log("W");
break;
case 68: // D
movement.right = true;
console.log("D");
break;
case 83: // S
movement.down = true;
console.log("S");
break;
}
});
document.addEventListener('keyup', function(event) {
switch (event.keyCode) {
case 65: // A
movement.left = false;
break;
case 87: // W
movement.up = false;
break;
case 68: // D
movement.right = false;
break;
case 83: // S
movement.down = false;
break;
}
});
/* end movements */
let canvas = document.getElementById('canvas');
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext('2d');
socket.on('state', function(players) {
// Clear the canvas so that the previous positions are erased
ctx.clearRect(0, 0, width, height);
// Everytime you update (frame) the state of the canvas draw all players on it
for (let id in players) {
let player = players[id];
// Drawing with fillRect = no flickering
//ctx.fillRect(player.x, player.y, 100, 100);
// Drawing my sprite image = flickering!
let image = new Image();
image.onload = function() {
ctx.mozImageSmoothingEnabled = true;
ctx.webkitImageSmoothingEnabled = true;
ctx.msImageSmoothingEnabled = true;
ctx.imageSmoothingEnabled = true;
ctx.drawImage(image, player.x, player.y);
};
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAqCAYAAABYzsDTAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAUxJREFUWEftlk0KwjAQhb1CL9EjiD+4cyciint37goiiFAE1y506xkEV56gB/AC4jXcuIq8YmRME5MJqSJUeBT/vnnzOmlSq/3yNes3BDQfNHMthq1cy1H7TV4eK/hbbEmvLiDXWNJxR0BO2ZcKVxzAkU1OptUfvaD340qI07ogfO5Fpo6/Bu92NwJCJ7h6OZcxoAPqPAgcwPN2muceHH47pEIqOPy6T7RwOjXe01LBC6Max7GA5EKS73FF5uQ73phTkA5OC0dRxFtEX4F/ci1juewmPOd02aMA7YRGAjALLsGmmxkETh+3NufO7l0jkfMHcJZluawzyYUDCLA1f07eqktnuEve1ggKu/FzA3bY7e35WqrL54rpRMA2T/9QKhyF1AKmguwu1FOWrhgbSiG6Y5wXUJd3qfBgUZjaDXYTdQX+H15KB7p5t47jAzRfydFOtcn9AAAAAElFTkSuQmCC';
}
});
setInterval(function() {
socket.emit('movement', movement);
}, 1000 / 60);
The thing is that whenever I clean the canvas with ctx.clearRect(0, 0, width, height);in my loop, I get some flickering, but only when using an image, if I use canvas shapes I get none.
Why is that so?
How do I avoid that flickering? I really need to use sprites and simple shapes are a no-no.
Is there a better way to "loop" in multiplayer using socket.io that I'm missing? (I read about animationframe but I didn't really understand how to implement it with socket.io).
Any little help is appreciated I've been struggling a lot with it.
You actually have a larger problem in your code than this small flickering.
You are sending image data over the network every 60th of a second.
You won't be able to reach 60FPS animation on every end with such logic.
The first thing to do, is to create an assets manager, where you will load the required spritesheets for the game.
In this case, it seems to be some skins. So load once an HTMLImage with a sprite-sheet that will contain all your skins sprites before the game even starts and make this HTMLImage available for your rendering loop, e.g by setting it as a global of your game's function.
const assets = {
skin: new Image();
};
assets.skin.onload = e => tellTheGameWeAreReady();
assets.skin.src = 'path/to/skin_spritesheet.png';
Then what you will send over the socket is only the coordinates inside this sprite-sheet that the other players will have to draw.
Since your HTMLImage containing this sprite-sheet will already have loaded, all you have to do in the socket.on('state' handler is to draw the corresponding part of the sprite sheet at given coordinates.
socket.on('state', function(players) {
ctx.clearRect(0, 0, width, height);
for( let id in players ) {
let player = players[id];
/*//socket should emit each 'player' as
{
x: x_position of player in world,
y: y_position of player in world,
skin: {
x: x_position of to be displayed sprite in the sprite-sheet
y: y_position of to be displayed sprite in the sprite-sheet
w: width of to be displayed sprite in the sprite-sheet
h: height of to be displayed sprite in the sprite-sheet
}
}
*/
ctx.drawImage( assets.skins,
player.skin.x,
player.skin.y,
player.skin.w,
player.skin.h,
player.x,
player.y,
player.skin.w,
player.skin.h
);
}
});
My game is colegame.herokuapp.com and I was wondering if I could make it so that it is impossible to go off canvas as you cannot see anyone if they are off canvas. For my players I use emoji's as to make it easier to code and less time consuming. Does anyone know how to make collision detection based off of colours? Thanks and please provide the code used for it as I am a beginner. The size of my canvas is based off of the size of the screen. It is always a square though.
You need to restrict the position of your player based on the size of the canvas.
Here's an example with a 100x100 world, with a 10x10 player.
Click on the black box, and use the arrow keys of your keyboard to move the "player".
var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
var playerPosition = [45, 45];
if (canvas.getContext) {
draw();
window.addEventListener('keydown', move, true);
}
function move(evt)
{
switch (evt.keyCode) {
case 37: // left
playerPosition[0] -= 5;
break;
case 38: // up
playerPosition[1] -= 5;
break;
case 39: // right
playerPosition[0] += 5;
break;
case 40: // down
playerPosition[1] += 5;
break;
}
// restricting the movement of the player happens here
if (playerPosition[0] < 0) playerPosition[0] = 0;
if (playerPosition[0] > 90) playerPosition[0] = 90;
if (playerPosition[1] < 0) playerPosition[1] = 0;
if (playerPosition[1] > 90) playerPosition[1] = 90;
draw();
}
function draw() {
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, 100, 100);
ctx.fillStyle = "#ff0000";
ctx.fillRect(playerPosition[0], playerPosition[1], 10, 10);
}
#my-canvas {
width: 100px;
height: 100px;
}
<canvas id="my-canvas"></canvas>
Here is the fiddle.
Small rectangle will be created to simulate a bullet when the spacebar(keycode 32) is pressed. I encountered some problems: How to move them yo the top (decrease the y coordinate)?
Can anyone help me? Thx!
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var ps = false;
init();
function init(){
context.rect((cw-5)/2, ch-5, 5, 5);
context.fill();
update();
}
function update(){
if(ps){
playerShoot();
}
requestAnimationFrame(update);
}
function playerShoot(){
var b = new bullet(2);
}
function bullet(speed){
this.speed = speed;
speed++;
context.ellipse((cw-1)/2, ch-10-speed, 1, 3, 0, 0, Math.PI*2);
context.fill();
}
document.addEventListener("keydown", function(e){
switch(e.keyCode){
case 32:
ps = true;
break;
};
});
document.addEventListener("keyup", function(e){
switch(e.keyCode){
case 32:
ps = false;
break;
};
});
I've explained a lot of the code in the comments in the code itself.
A couple of other points:
Some browsers (including mine, i.e. Firefox v44.0.2) don't draw ellipses. So I've made your bullet another rectangle.
I used fillRect instead of rect just because I know that better.
I redrew the bullet by drawing over the old one with the opaque background color. However, you could also clear the rectangle around the previous bullet if you wanted.
You incremented speed in your example. That's probably not what you want from a conceptual point of view, even if you had gotten the visual results that you want. I suspect you want your bullets to move at a constant speed. Therefore, the speed variable should be constant, i.e. not change. Rather, you should use the speed constant to regularly change the position of the bullet. I changed bulletY, which is the vertical position of the bullet.
For simplicity, I've only allowed one bullet on the screen at a time.
I've limited the code to running 500 cycles. That's mostly to just not annoy Stack Overflow users who try the code...they don't want an infinite loop happening.
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var ps = false;
// some new variables
var bulletShowing = false; // is a bullet currently showing?
var bulletY; // the vertical position of the bullet
var speed = 8; // the bullet speed
var time = 500; // the time remaining
init();
function init() {
// draw background
context.fillStyle = "yellow";
context.fillRect(0, 0, cw, ch);
// draw gun
context.fillStyle = "black";
context.fillRect((cw - 5) / 2, ch - 5, 5, 5);
// update the scene
update();
}
function update() {
if (ps) {
playerShoot();
}
// if a bullet is supposed to be showing then, well, show it
if (bulletShowing) {
// redraw the bullet (erase the old, draw the new)
drawBullet();
// if the bullet has gone off-screen, allow a new shot
if (bulletY < -5) {
bulletShowing = false;
}
}
// give a visual indicator of time remaining
document.querySelector("div").innerHTML = "Time: " + time;
// decrement the time
time -= 1;
// if there is still time remaining, do it all again
if (time >= 0) {
requestAnimationFrame(update);
}
}
function playerShoot() {
// indicate a bullet will now be showing
bulletShowing = true;
// start the bullet out near the gun
bulletY = ch - 10;
}
function drawBullet() {
// erase the old bullet by drawing over it with the background color
// this rectangle is slightly larger than the bullet itself
// to ensure the entire old bullet is drawn over
context.fillStyle = "yellow";
context.fillRect((cw - 1) / 2 - 2, bulletY - 1, 5, 7);
// move the bullet position
bulletY -= speed;
// draw the new bullet
context.fillStyle = "black";
context.fillRect((cw - 1) / 2 - 1, bulletY, 3, 5);
}
document.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 32:
// only allow one bullet on the screen at a time
// (for the sake of coding simplicity)
if (!bulletShowing) {
ps = true;
}
break;
};
});
document.addEventListener("keyup", function(e) {
switch (e.keyCode) {
case 32:
ps = false;
break;
};
});
#myCanvas {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, 5%);
background-color: #cccccc;
z-index: -1;
}
<p>Click on the canvas, then use the space bar to fire bullets one at a time.</p>
<div></div>
<canvas id="myCanvas" width=300 height=150></canvas>
I have a basic game moving a sprite around a map using Quintus html5 game. I have the sprite moving from square to square (64px) with a keypress fine. I want the player to move one square also when a button is click on the webpage.
I am unsure how to call the step method in Quintus from a button click.
Outside the canvas I have a <button id="move-player">Move Player One Square</button>
document.getElementById("move-player").addEventListener("click", function( event ) {
//move player one square by possibly calling PlayerControls step
});
Quintus player code
Q.component("PlayerControls", {
// default properties to add onto our entity
defaults: {speed: 32, direction: 'null'},
// called when the component is added to
// an entity
added: function () {
var p = this.entity.p;
// adding default properties
Q._defaults(p, this.defaults);
// every time our entity steps, call its step method
this.entity.on("step", this, "step");
},
step: function (dt) {
// entity's properties
var p = this.entity.p;
var total_points = Q.state.get("points");
if (total_points >= 4) {
Q.state.set("points", 4);
Q.stageScene("GameOverMsg", 2);//display game over message
}//inner if
// direction from the input will help in animation
p.direction = Q.inputs['left'] ? 'left' :
Q.inputs['right'] ? 'right' :
Q.inputs['up'] ? 'up' :
Q.inputs['down'] ? 'down' : null;
window.onkeyup = function (e) {
if (e.keyCode == 37)//left
{
p.x -= p.speed;
move_collision = "left";
}
else if (e.keyCode == 38)//up
{
if (!(p.y <= 36)) {
p.y -= p.speed;
move_collision = "up";
}
}
else if (e.keyCode == 39)//right
{
p.x += p.speed;
move_collision = "right";
}
else if (e.keyCode == 40)//down
{
if (!(p.y >= 480)) {
p.y += p.speed;
move_collision = "down";
}
}
};
// movement
switch (move_collision) {
case "left":
p.x -= p.speed;
move_collision = "";
break;
case "right":
p.x += p.speed;
move_collision = "";
break;
case "up":
p.y -= p.speed;
move_collision = "";
break;
case "down":
if (!(p.y >= 480)) {
p.y += p.speed;
}
move_collision = "";
break;
default:
break;
}
}
});
//Player Sprite
Q.Sprite.extend("Player",
{
init: function (properties) {
properties.sheet = "snailspritesheet";
properties.sprite = "snail";
properties.frame = 4;
this._super(properties);
this.add("2d,PlayerControls,animation,tween");
Consider that step function is a kind of callback function.
That function is called periodically with timer.
So, there are two method to move a player
Make a state machine: change state when button is clicked and interleave the code to step function for moving player
Make a trigger function to move a player with global variable: for example,
var player;
Q.scene("start",function(stage) {
stage.insert(new Q.UI.Button({
asset: 'move.png',
x: Q.width/2,
scale: 0.5,
y: 370
}, function() {
player.x += 10;
player.y -= 10;
})
);
}
I hope it is useful to you.
How do I run a sprite animation when pressing the left or right arrow keys in JavaScript? Here's my code:
var avatarX = 0;
var avatarY = 240;
var avatarImage;
var counter = 1;
var XWIDTH = 0;
var WIDTH = 400;
var dx = 5;
var tt;
var gameCanvas;
var context;
var moving;
var animationCounter = 1;
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.addEventListener('load', startLoop, false);
avatarImage.src = "img/ships.png"; //Ditto from above.
}
function startLoop() {
console.log("Detecting whether moving to the right is: " + moving);
if(moving == 0) {
gameLoop();
}
}
function gameLoop() {
setTimeout(gameLoop, 100);
handleTick();
}
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 0;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 1;
}
break;
}
}
function counTer() {
if(counter == 60) {
clearInterval(tt);
} else {
counter++;
}
}
function handleTick() {
context.clearRect(0,0,gameCanvas.width,gameCanvas.height);
context.drawImage(avatarImage, 32*animationCounter, 0, 32,32, avatarX, avatarY, 64, 64);
context.fillText("Seconds: " + counter, 5, 5);
context.fillText("1 is Right, 2 is Left, 0 is idle: " + moving, 20, 20);
animationCounter++
if(animationCounter >1) {
animationCounter = 0;
}
}
There are many ways to implement animation. The one i use is pretty easy and looks something like this:
var player = {
x: 0,
y: 0,
width: 50,
height: 100,
sprite: {
column: 0,
row: 0,
height: 50,
width: 100
},
image: PlayerImage,
animation: {
active: true, //Determines if the animation is running or not.
counter: 0,
progress: 0,
sequence: []
}
}
//This functions fires when the left arrow is pressed.
var onLeft = function(){
player.animation.sequence = [{column: 0, row: 0, t: 12}, {column: 1, row: 0, t: 12}, {column: 2, row: 0, t: 12}];
player.animation.active = true;
}
//This functions fires when the right arrow is pressed.
var onRight = function(){
player.animation.sequence = [{column: 0, row: 1, t: 12}, {column: 1, row: 1, t: 12}, {column: 2, row: 1, t: 12}];
player.animation.active = true;
}
//This function fires when no arrow are pressed.
var standingStill = function(){
player.animation.active = false;
}
var handleTick = function(){
//Clear the canvas.
context.canvas.width = context.canvas.width;
//If the animation is active.
if(player.animation.active){
//If the counter >= tick in the sequence. If not, just keep on increasing the counter value.
if(player.animation.counter >= player.animation.sequence[player.animation.progress]){
player.animation.counter = 1;
if(player.animation.progress >= player.animation.sequence.length - 1){
player.animation.progress = 0;
}else{
player.animation.progress++;
}
var currentFrame = player.animation.sequence[player.animation.progress];
//Change player.sprite column and row.(new frame)
player.sprite.column = currentFrame.column;
player.sprite.row = currentFrame.row;
}else{
player.animation.counter++;
}
}
context.drawImage(player.image, player.sprite.column * player.sprite.width, player.sprite.row * player.sprite.height, player.sprite.width, player.sprite.height, player.x, player.y, player.width, player.height)
}
The sequence is an array that contains objects that look like that: {column: 0, row: 0, t: 12} Every object is a frame. The column value is the current x of the sprite, the row is the y of the sprite. The script automatically creates the x and the y value, so all you need to add is value like 0, 1, 3, 5, and so on.(Just which frame it is on either x or y axis.) So for example, if the column is 0 and the row is 0, this is the frame that is in the top-left corner(the first frame). The t value stands for tick, this determines how many ticks there has to happend before the animation goes to the next frame.
Player.sprite also has properties width and height, that's the width and the height of the frame, it is often the same value as the width and height of the player object.
You have to make your own player.animation.sequence in onLeft and onRight function so it animates how you want it to animate.
I hope you understood what i meant. This might seem complicated, but it really isn't and if you don't get it now, don't worry, you'll get it eventually. If you really have difficulties with understanding, just ask.
Edit: First of all i highly recommend using objects to store information about entities. It makes game making much easier and cleaner. Just look at the player object, it's much easier to get the x simply by writing player.x than PlayerX. It looks more professional too. :) And when you have to give a lot of information about your entity you don't have to pass many arguments, you pass the whole object. Example:
//Entity properties stored in separate variable.
function animate(EntityX, EntityY, EntitySpriteX, EntitySpriteY, ...){
var x = EntityX;
//And so on...
}
//Entity stored in an object.
function animate(Entity){
var x = Entity.x;
//And so on...
}
Anyway, back to your question. First, we have to add variables that store information about our sprite.
var avatarSpriteColumn = 0; //Sprite frame on the x axis.
var avatarSpriteRow = 0; //Sprite frame on the y axis.
var avatarSpriteWidth = 50; //The width of a frame.
var avatarSpriteHeight = 100; //The height of a frame.
We also have to add variables that store the information about the animation.
var animationActive = false; //A variable that controls if the animation is 'running'.
var animationCounter = 0; //How many frames(ticks) have passed since the last frame(animation frame) has changed. (I'm not good at describing variables. :P)
var animationProgress = 0; //Current animation frame.
var animationSequence = []; //Array that stores the sequence of animation, as i explained.
Then, in your handleTick function you have to add a code that will animate the sprite. You have to add this code before you draw your entity.
//If the animation is active.
if(animationActive){
//If the counter >= tick in the sequence. If not, just keep on increasing the counter value.
if(animationCounter >= animationSequence[animationProgress]){
animationCounter = 1;
//Reset the progress, so that next time another animation frame shows up.
if(animationProgress >= animationSequence.length - 1){
animationProgress = 0;
}else{
animationProgress++;
}
//Select information about the current animation frame and store it in a variable so it is easier to access.
var currentFrame = animationSequence[animationProgress];
//Change player.sprite column and row.(new frame);
avatarSpriteColumn = currentFrame.column;
avatarSpriteRow = currentFrame.row;
}else{
animationCounter.counter++;
}
}
Ok, now you have a code that animates the sprite, but how do we run it? Well, i see you have a variable called moving. It tells us in what direction the player is moving and if it is moving at all. It looks like you implemented it a little bit wrong. Right now your function that operates the keys looks like this:
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 0;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 1;
}
break;
}
}
The variable is supposed to return 1 if the entity is going to the right, 2 if it is going to the left and 0 if the entity is standing still, right? Right now it shows 0 when the entity is moving to the right and 1 when it is moving to the left. It also doesn't show us if the entity is idle. We have to fix it. Change it to something like this:
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 1;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 2;
}
break;
default:
moving = 0;
}
}
Ok, now we have to add this code to the handleTick function. This code starts the animation and changes the sequence.:
if(moving == 1){ //Moving in the right direction.
animationSequence = []; //Animation of moving in the right direction. Change the sequence to your own.
animationActive = true; //Run the animation.
}else if(moving == 2){ //Moving to the left.
animationSequence = []; //Animation of moving to the left. Change the sequence to your own.
animationActive = true; //Run the animation.
}else{
animationActive = false; //Stops the animation, but the last frame stays.
/*
Alternatively, if you want a separate frame or animation that is animating when the entity is standing, you run this code.
animationSequence = []; // Your sequence. If you want a single frame, with no animation just add one frame to the sequence.
animationActive = true;
*/
}
Now, the last thing we have to do is to draw the entity. In your case, this will look something like this:
context.drawImage(avatarImage, avatarSpriteColumn * avatarSpriteWidth, avatarSpriteRow * avatarSpriteHeight, avatarWidth, avatarHeight, avatarX, avatarY, 64, 64);
In the end your whole code will look something like this:
var avatarX = 0;
var avatarY = 240;
var avatarImage;
var counter = 1;
var XWIDTH = 0;
var WIDTH = 400;
var dx = 5;
var tt;
var gameCanvas;
var context;
var moving;
var animationCounter = 1;
var avatarSpriteColumn = 0; //Sprite frame on the x axis.
var avatarSpriteRow = 0; //Sprite frame on the y axis.
var avatarSpriteWidth = 50; //The width of a frame.
var avatarSpriteHeight = 100; //The height of a frame.
var animationActive = false; //A variable that controls if the animation is 'running'.
var animationCounter = 0; //How many frames(ticks) have passed since the last frame(animation frame) has changed. (I'm not good at describing variables. :P)
var animationProgress = 0; //Current animation frame.
var animationSequence = []; //Array that stores the sequence of animation, as i explained.
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.addEventListener('load', startLoop, false);
avatarImage.src = "img/ships.png"; //Ditto from above.
}
function startLoop() {
console.log("Detecting whether moving to the right is: " + moving);
if(moving == 0) {
gameLoop();
}
}
function gameLoop() {
setTimeout(gameLoop, 100);
handleTick();
}
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 1;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 2;
}
break;
default:
moving = 0;
}
}
function counTer() {
if(counter == 60) {
clearInterval(tt);
} else {
counter++;
}
}
function handleTick() {
context.clearRect(0,0,gameCanvas.width,gameCanvas.height);
if(moving == 1){ //Moving in the right direction.
animationSequence = []; //Animation of moving in the right direction. Change the sequence to your own.
animationActive = true; //Run the animation.
}else if(moving == 2){ //Moving to the left.
animationSequence = []; //Animation of moving to the left. Change the sequence to your own.
animationActive = true; //Run the animation.
}else{
animationActive = false; //Stops the animation, but the last frame stays.
/*
Alternatively, if you want a separate frame or animation that is animating when the entity is standing, you run this code.
animationSequence = []; // Your sequence. If you want a single frame, with no animation just add one frame to the sequence.
animationActive = true;
*/
}
//If the animation is active.
if(animationActive){
//If the counter >= tick in the sequence. If not, just keep on increasing the counter value.
if(animationCounter >= animationSequence[animationProgress]){
animationCounter = 1;
//Reset the progress, so that next time another animation frame shows up.
if(animationProgress >= animationSequence.length - 1){
animationProgress = 0;
}else{
animationProgress++;
}
//Select information about the current animation frame and store it in a variable so it is easier to access.
var currentFrame = animationSequence[animationProgress];
//Change player.sprite column and row.(new frame);
avatarSpriteColumn = currentFrame.column;
avatarSpriteRow = currentFrame.row;
}else{
animationCounter.counter++;
}
}
context.drawImage(avatarImage, avatarSpriteColumn * avatarSpriteWidth, avatarSpriteRow * avatarSpriteHeight, avatarWidth, avatarHeight, avatarX, avatarY, 64, 64);
context.fillText("Seconds: " + counter, 5, 5);
context.fillText("1 is Right, 2 is Left, 0 is idle: " + moving, 20, 20);
}
The only thing you have to do right now is to make your own animationSequences and check if it works, let me know if you have any problems with that.
Of course, the code i am using is more complicated and has more "abilities" and is easier to use(the code behind is more complicated), but hopefully this will help you.
I must also apoligize for making this thing seem so complicated, when it's not. I am bad at explaining.