Javascript Sluggish player movement - javascript

UPDATE
changed it to the following and noticed speed improvements. The issue now is that the player will just slide without animating the frames.
var animator, frames;
animator = window.setInterval(function(){
if(currentFrame == totalFrames){
clearInterval(animator);
currentFrame = 0;
update();
isMoving = 0;
return;
}
xPosition += x;
yPosition += y;
frames = window.requestAnimationFrame(animator);
currentFrame++;
update();
},frames);
Some of the issues I am currently facing are: the map edges code section is completely broken. I am just trying to make it so that the player cannot move beyond the canvas.width/canvas.height. Also, my player movement is very sluggish and unresponsive. I think it's because of the isMoving check I added. I want to be able to move much smoother. Right now the character takes so long to move that I feel as if I am lagging. Also, for some reason, sometimes it'll move more than one time. It is completely random when it happens. Any help would be appreciated
var playerSprite = new Image();
playerSprite.src = "male.png";
var playerWidth = 64;
var playerHeight = 64;
var currentFrame = 0;
var totalFrames = 8;
var moveDistance = 4; // move 4 pixels
var xPosition = 300;
var yPosition = 200;
var direction = 2; // south, options: 0 - 3
var isMoving = 0;
var canvas, context;
window.addEventListener("load", function(){
canvas = document.getElementById('map');
context = canvas.getContext('2d');
})
function draw(){
context.drawImage(playerSprite,currentFrame * playerWidth, direction* playerHeight ,playerWidth,playerHeight,xPosition,yPosition,playerWidth,playerHeight);
}
function update()
{
clearMap();
draw();
}
function move(x, y){
if(isMoving)return;
isMoving = 1;
if(x > 0) direction = 3;
else if(x < 0) direction = 1;
if(y > 0) direction = 2;
else if(y < 0) direction = 0;
//update direction no matter what, implemented
// in order for directions to update
// when changing directions in map edges
//update();
/* Broken
if(xPosition + playerWidth + x > canvas.width)return; //works
else if(xPosition - x < 0)return; // player gets stuck
if(yPosition + playerHeight + y > canvas.height)return; //works
else if(yPosition - y < 0)return; // player gets stuck
//xPosition += x;
//yPosition += y;
*/
//actual animation update
var animator;
animator = window.setInterval(function(){
if(currentFrame == totalFrames){
clearInterval(animator);
currentFrame = 0;
update();
isMoving = 0;
return;
}
xPosition += x;
yPosition += y;
currentFrame++;
update();
},1000/16);
}
function clearMap(){
context.clearRect(0, 0, canvas.width, canvas.height);
}
function keyPress(e)
{
if(currentFrame == totalFrames){
currentFrame = 0;
}
switch(e.keyCode){
case 38: move(0, -moveDistance); break;
case 40: move(0, +moveDistance); break;
case 39: move(+moveDistance, 0); break;
case 37: move(-moveDistance, 0); break;
}
}
window.addEventListener("load", update, false);
window.addEventListener("keydown",keyPress);

Main points I changed:
No use of setInterval anywhere. Instead we let the browser handle the FPS at a rate it can handle using requestAnimationFrame.
One central game loop (update()). Before, you were doing a bunch of calculations and starting new background loops every time you press a key. That's bad. If someone were to mash the arrow keys, the browser would have to process 100+ setIntervals in the background.
Instead of doing any calculation in the key events, we're just using a variable to keep track of which buttons are pressed. Then in the game loop, which happens each frame, we can move the player a few pixels if an arrow key is held.
Exercises for you:
The animation is insanely fast because the player-frame is advanced every game-frame. Slow it down!
If a faster computer runs at 60fps, the player will move 60 * 4 = 240 pixels every second. If a slower computer runs at 20fps, the player will only move 20 * 4 = 80 pixels every second. That's actually a huge difference. To make your game run consistently regardless of platform, you should move the player more or less depending on how fast the game is running. Here's a good article to get you started. Also the requestAnimationFrame documentation will be helpful.
Here's the code:
var playerSprite = new Image();
playerSprite.src = "male.png";
var playerWidth = 64;
var playerHeight = 64;
var currentFrame = 0;
var totalFrames = 8;
var direction = 2; // south, options: 0 - 3
var moveDistance = 4; // move 4 pixels
var xPosition = 300;
var yPosition = 200;
var left = 0,
right = 0,
up = 0,
down = 0;
var canvas, context;
window.addEventListener("keydown", keyPress);
window.addEventListener("keyup", keyRelease);
window.addEventListener("load", function(){
canvas = document.getElementById('map');
context = canvas.getContext('2d');
// tells the browser to call update() as soon as it's ready
// this prevents lockups, and also the browser regulates the FPS
window.requestAnimationFrame(update);
});
function update() {
// EVERYTHING game related happens in update (except listening for key events).
// This keeps everything organized, and prevents any lag/slowdown issues
// handles player movement and animation
movePlayer();
// handles all drawing
draw();
// lets the browser know we're ready to draw the next frame
window.requestAnimationFrame(update);
}
function movePlayer() {
if(left) {
xPosition -= moveDistance;
direction = 1;
}
if(right) {
xPosition += moveDistance;
direction = 3;
}
if(up) {
yPosition -= moveDistance;
direction = 0;
}
if(down) {
yPosition += moveDistance;
direction = 2;
}
// all this code happens every frame
// in english: if we're moving, advance to the next frame
if(left || right || up || down) {
currentFrame ++;
if(currentFrame == totalFrames) currentFrame = 0;
}
}
function draw() {
// clear the map
context.clearRect(0, 0, canvas.width, canvas.height);
// draw the next frame
context.drawImage(playerSprite, currentFrame * playerWidth, direction * playerHeight,
playerWidth, playerHeight,
xPosition, yPosition,
playerWidth, playerHeight);
}
// keyPress and keyRelease ensure that the variables are
// equal to 1 if pressed and 0 otherwise.
function keyPress(e)
{
switch(e.keyCode){
case 38: up = 1; break;
case 40: down = 1; break;
case 39: right = 1; break;
case 37: left = 1; break;
}
}
function keyRelease(e)
{
switch(e.keyCode){
case 38: up = 0; break;
case 40: down = 0; break;
case 39: right = 0; break;
case 37: left = 0; break;
}
}

EDIT: Forgot to mention that I opted to include delta on my player's movement instead of on my update() redraws to prevent the whole game from running at 20 fps.
Forgot to post my revised and fully functional code. Thanks #Entity for helping me out <3. As you can see, I've begun to take more liberties and experimenting as well.
var fps = 20, fpsInterval = 1000/fps, now, then = Date.now(), delta;
var moving = {38:0, 40:0, 39:0, 37:0} // north, south, east, west
function move(direction, toggle){moving[direction] = toggle}
function keyDown(e){move(e.keyCode,1)}
function keyUp(e){move(e.keyCode,0)}
function playerMovement(){
now = Date.now();
delta = now - then;
if(delta > fpsInterval){
then = now - (delta % fpsInterval);
// north = 38, south = 40, east = 39, west = 37
// stop movement stall from opposite directions
if(moving[38] && moving[40]){move(40,0)}
if(moving[39] && moving[37]){move(37,0)}
// flip order to change diagonal rendering mode
if(moving[38]) {direction = 0; yPosition -= moveDistance};
if(moving[40]) {direction = 2; yPosition += moveDistance};
if(moving[39]) {direction = 3; xPosition += moveDistance};
if(moving[37]) {direction = 1; xPosition -= moveDistance};
if(moving[38] || moving[40] || moving[39] || moving[37]) currentFrame++;
if(currentFrame == totalFrames) currentFrame = 0;
}
}

Related

How would I make a randomly generated collider for a game?

Catbus Game code
I want to add colliders into my game, so that my main character, the catbus, will jump over them. When he hits them, it will show a game over. I want to be able to randomly generate them, as they come from the right side, and move to the left side. Like the Dinosaur game that shows up when the wifi goes out. I'd like to implement a picture to serve as the collider. The collider that I plan to use goes by:
var tinyToto
I have no idea how to make a collider such as that, nor how to create a random generator. Any help is much appreciated, even if it is only a small portion of it.
Code is as follows:
let img; //background
var bgImg; //also the background
var x1 = 0;
var x2;
var scrollSpeed = 4; //how fast background is
let bing; //for music
let cat;
var mode; //determines whether the game has started
let gravity = 0.2; //jumping forces
let velocity = 0.1;
let upForce = 7;
let startY = 730; //where cat bus jumps from
let startX = 70;
var font1; //custom fonts
var font2;
p5.disableFriendlyErrors = true; //avoids errors
function preload() {
bgImg = loadImage("backgwound.png"); //importing background
bing = loadSound("catbus theme song.mp3"); //importing music
font1 = loadFont("Big Font.TTF");
font2 = loadFont("Smaller Font.ttf");
}
function setup() {
createCanvas(1000, 1000); //canvas size
img = loadImage("backgwound.png"); //background in
x2 = width;
bing.loop(); //loops the music
cat = {
//coordinates for catbus
x: startX,
y: startY,
};
catGif = createImg("catgif.gif"); //creates catbus
catGif.position(cat.x, cat.y); //creates position
catGif.size(270, 100); //creates how big
mode = 0; //game start
textSize(50); //text size
}
function draw() {
let time = frameCount; //start background loop
image(img, 0 - time, 0);
image(bgImg, x1, 2, width, height);
image(bgImg, x2, 2, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 <= -width) {
x1 = width;
}
if (x2 <= -width) {
x2 = width;
} //end background loop
fill(128 + sin(frameCount * 0.05) * 128); //text colour
if (mode == 0) {
textSize(20);
textFont(font1);
text("press SPACE to start the game!", 240, 500); //what text to type
}
fill("white");
if (mode == 0) {
textSize(35);
textFont(font2);
text("CATBUS BIZZARE ADVENTURE", 90, 450); //what text to type
}
cat.y = cat.y + velocity; //code for jumping
velocity = velocity + gravity;
if (cat.y > startY) {
velocity = 0;
cat.y = startY;
}
catGif.position(cat.x, cat.y);
}
function keyPressed() {
if (keyCode === 32 && velocity == 0) {
//spacebar code
mode = 1;
velocity += -upForce;
}
}

Is there a way to space correctly a string of text in p5.js?

I saw many times really cool examples of kinetic typography. In these examples every letter is a particle. The text is a particle system and it may be subject to various forces such gravity or even centrifugal force. These systems are made in Processing and in p5.js.
I am building an interactive screen for the web filled with text using p5.js, inspired by these example of kinetic typography.
When the user moves the cursor on the text this start bounce all around the screen.
I translated the sketch from Processing to p5.js and I have noticed this problem related to the spacing of the text in the setup() function.
In Processing the code looks like this and it function correctly.
I want to focus on this section of the Processing code:
void setup() {
size(640, 360);
//load the font
f = createFont("Arial", fontS, true);
textFont(f);
// Create the array the same size as the String
springs = new Spring[message.length()];
// Initialize Letters (Springs) at the correct x location
int locx = 40;
//initialize Letters (Springs) at the correct y location
int locy = 100;
for (int i = 0; i < message.length(); i++) {
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
//boudaries of text just to make it a nice "go to head"
if (locx >= 360) {
locy+=60;
locx = 40;
}
}
}
You can see the result
As you can see the parameter
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
does its job, spacing the letters.
However when I translate this sketch in P5.js, I don't get the same nice spacing.
This is the same section but is the p5.js code:
function setup() {
createCanvas(640, 360);
noStroke();
textAlign(LEFT);
// Create the array the same size as the String
springs = new Array(message.length);
// Initialize Letters (Springs) at the correct x location
var locx = 10;
//initialize Letters (Springs) at the correct y location
var locy = 120;
for (var i = 0; i < message.length; i++) {
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
//boudaries of text just to make it a nice "go to head"
if(locx>= 390){
locy+= 60;
locx= 40;
}
}
}
The result is show
I am sure, in the p5js code, that there is a problem regarding this part:
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
Because I tried to fix it multiplying the value of the locx as shown here:
springs[i] = new Spring(locx*5, locy, 40, springs, i, message.charAt(i));
I then got
which can seems correct, but I'm sure it is not.
At this point I have no idea how to fix it, it must be something of p5.js I'm unaware of.
Any help is greatly appreciate.
//Edit
As suggested in the comment here you can find the Spring class written in p5.js:
// Spring class
class Spring {
constructor (_x, _y, _s, _others, _id, letter_) {
// Screen values
this.x_pos = this.tempxpos = _x;
this.y_pos = this.tempypos = _y;
this.size = _s;
this.over = false;
this.move = false;
// Spring simulation constants
this.mass = 8.0; // Mass
this.k = 0.2; // Spring constant
this.damp =0.98; // Damping
this.rest_posx = _x; // Rest position X
this.rest_posy = _y; // Rest position Y
// Spring simulation variables
//float pos = 20.0; // Position
this.velx = 0.0; // X Velocity
this.vely = 0.0; // Y Velocity
this.accel = 0; // Acceleration
this.force = 0; // Force
this.friends = _others;
this.id = _id;
this.letter = letter_;
}
update() {
if (this.move) {
this.rest_posy = mouseY;
this.rest_posx = mouseX;
}
this.force = -this.k * (this.tempypos - this.rest_posy); // f=-ky
this.accel = this.force / this.mass; // Set the acceleration, f=ma == a=f/m
this.vely = this.damp * (this.vely + this.accel); // Set the velocity
this.tempypos = this.tempypos + this.vely; // Updated position
this.force = -this.k * (this.tempxpos - this.rest_posx); // f=-ky
this.accel = this.force / this.mass; // Set the acceleration, f=ma == a=f/m
this.velx = this.damp * (this.velx + this.accel); // Set the velocity
this.tempxpos = this.tempxpos + this.velx; // Updated position
if ((this.overEvent() || this.move) && !(this.otherOver()) ) {
this.over = true;
} else {
this.over = false;
}
}
// Test to see if mouse is over this spring
overEvent() {
let disX = this.x_pos - mouseX;
let disY = this.y_pos - mouseY;
let dis = createVector(disX, disY);
if (dis.mag() < this.size / 2 ) {
return true;
} else {
return false;
}
}
// Make sure no other springs are active
otherOver() {
for (let i = 0; i < message.length; i++) {
if (i != this.id) {
if (this.friends[i].over == true) {
this.velx = -this.velx;
return true;
}
}
}
return false;
}
//the springs collides with the edges of the screen
box_collision() {
if(this.tempxpos+this.size/2>width){
this.tempxpos = width-this.size/2;
this.velx = -this.velx;
} else if (this.tempxpos - this.size/2 < 0){
this.tempxpos = this.size/2;
this.velx = -this.velx;
}
if (this.tempypos+this.size/2>height) {
this.tempypos = height-this.size/2;
this.vely = -this.vely;
} else if (this.tempypos- this.size/2 < 0) {
this.tempypos = this.size/2;
this.vely = -this.vely;
}
}
//the springs collides with each other
collide() {
for (var i = this.id + 1; i < message.length; i++) {
var dx = this.friends[i].tempxpos - this.tempxpos;
var dy = this.friends[i].tempypos - this.tempypos;
var distance = sqrt(dx*dx + dy*dy);
var minDist = this.friends[i].size/2 + this.size/2;
if (distance < minDist) {
var angle = atan2(dy, dx);
var targetX = this.tempxpos + cos(angle) * minDist;
var targetY = this.tempypos + sin(angle) * minDist;
var ax = (targetX - this.friends[i].tempxpos) * 0.01;
var ay = (targetY - this.friends[i].tempypos) * 0.01;
this.velx -= ax;
this.vely -= ay;
this.friends[i].velx += ax;
this.friends[i].vely += ay;
}
}
}
//display the letter Particle
display() {
if (this.over) {
fill(255, 0, 0);
} else {
fill(255);
}
noStroke();
textSize(fontS);
//for debugging
// ellipse(this.tempxpos, this.tempypos, this.size, this.size);
text(this.letter, this.tempxpos, this.tempypos);
}
pressed() {
if (this.over) {
this.move = true;
} else {
this.move = false;
}
}
released() {
this.move = false;
this.rest_posx = this.x_pos;
this.rest_posy = this.y_pos;
}
}
And here you can find the link to the P5.js editor with the code:
Spring text p5js code
note: I had to fix the Spring class because I didn't realize that all of my functions where initialized in the constructor. Now the functions that compose the Class are outside the constructor. I still haven't figured out how to fix the spacing problem.
I managed to resolve the issue.
As far as I understood the code was correct from the beginning.
What I missed through the journey was that I have to set the font size in the function setup() if I want to display the text on the screen correctly.
You can still see the result by checking the link to the p5.js editor I posted previously.

Javascript game, make spaceship shoot on pressdown with interval

I am working on my gaming skills (mainly with arrays) to generate enemies and now bullets to take them down. I was able to set-up bullets while testing, but were visible only when I had a key pressed (space bar let's say) and with no interval in between, so the browser was not able to take that many at one point.
Is there any simple way to make the ship fire bullets with interval in between (not to load the browser that much) and maybe upon going to the enemy[i].x / y location to delete an enemy and the bullet can disappear ?
Here is the cleaned as much as possible code I have for now (HTML and JS file. Have some images as well and will provide URL to the game to check it if needed - http://sarahkerrigan.biz/spaceship
<!DOCTYPE html>
<html>
<head>
<title>Space Ship</title>
</head>
<body>
<h3>Space Ship</h3>
<canvas id="canvas" width="1000" height="600"></canvas>
<script src="spaceship.js"></script>
</body>
</html>
And here is the spaceship.js file:
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
//-------------------------------
// load images
var player = new Image();
var enemy = new Image();
var bullet = new Image();
player.src = "images/player.png";
enemy.src = "images/enemy.png";
bullet.src = "images/fire.png";
//-------------------------------
// vars
var score = 0;
var pause = 0;
var playerY = 300;
var playerX = 100;
var upPressed = false;
var downPressed = false;
var leftPressed = false;
var rightPressed = false;
// audio
var fire = new Audio();
var hit = new Audio();
fire.src = "sounds/fire.mp3";
hit.src = "sounds/hit.mp3";
//-------------------------------
// on key down
document.addEventListener("keydown", keyDownHandler);
function keyDownHandler(e) {
if (e.keyCode == 87) {
upPressed = true;
}
if (e.keyCode == 83) {
downPressed = true;
}
if (e.keyCode == 65) {
leftPressed = true;
}
if (e.keyCode == 68) {
rightPressed = true;
}
}
// on key up
document.addEventListener("keyup", keyUpHandler);
function keyUpHandler(e) {
if (e.keyCode == 87) {
upPressed = false;
}
if (e.keyCode == 83) {
downPressed = false;
}
if (e.keyCode == 65) {
leftPressed = false;
}
if (e.keyCode == 68) {
rightPressed = false;
}
}
//-------------------------------
function moveUp() {
if (playerY <= canvas.height - canvas.height){
}
else{
playerY -= 6;
}
}
function moveDown() {
if (playerY >= canvas.height - player.height){
}
else{
playerY += 6;
}
}
function moveLeft() {
if (playerX <= canvas.width - canvas.width){
}
else{
playerX -= 6;
}
}
function moveRight() {
if (playerX >= canvas.width - player.width){
}
else{
playerX += 6;
}
}
//-------------------------------
// Enemy coordinates
var enemies = [];
enemies[0] = {
x: cvs.width,
y: 0
};
//-------------------------------
// reload page
function reLoad() {
location.reload(); // reload the page
}
//-------------------------------
// draw images
function draw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (upPressed === true) {
moveUp();
}
if (downPressed === true) {
moveDown();
}
if (leftPressed === true) {
moveLeft();
}
if (rightPressed === true) {
moveRight();
}
//-------------------------------
for (var i = 0; i < enemies.length; i++) {
//draw the enemy
ctx.drawImage(enemy, enemies[i].x, enemies[i].y);
// enemy movement speed
enemies[i].x -= 3;
if (enemies[i].x == 880) {
enemies.push({
x: cvs.width,
y: Math.floor(Math.random() * enemy.height) * 10 - enemy.height
});
}
// detect collision
// if enemy hits player
if (playerX + player.width >= enemies[i].x && playerX <= enemies[i].x + enemy.width && (playerY <= enemies[i].y + enemy.height && playerY + player.height >= enemies[i].y)) {
pause = 1;
}
}
//-------------------------------
//draw the player
ctx.drawImage(player, playerX, playerY);
//draw score
ctx.fillStyle = "#fff";
ctx.font = "20px Verdana";
ctx.fillText("Destroyed ships : " + score + "$", 10, cvs.height - 20);
function onPause() {
if (pause >= 1) {
hit.play();
ctx.fillStyle = "#df8a62";
ctx.fillRect(150, 150, 280, 100);
ctx.fillStyle = "#000";
ctx.font = "20px Verdana";
ctx.fillText("You died:", 165, 170);
document.addEventListener("keydown", reLoad);
} else if (pause <= 0) {
requestAnimationFrame(draw);
}
}
onPause();
}
draw();
You want to use time intervals instead of listeners.
var myVar = setInterval(timeCycle, 50);
function timeCycle() {
//all the stuff you currently have listeners for.
}
This way when the time interval happens it will execute your key presses just once. Then if you want to change your rate of fire you add something like this:
setInterval(timeCycle, 50);
rateOfFire = 5;
shootCoolDown = 0;
function timeCycle() {
if (shootPressed === true) {
if(shootCoolDown === 0){
shootCoolDown = rateOfFire;
shoot();
}
}
if (shootCoolDown > 0){
shootCoolDown --;
}
}
This way it will shoot once every 5 game cycles (or 4-rounds per second in this case).
There are more fancy things you can do to create a delta-time system to offset lag by changing your sim rate based on the time it takes your timeCycle to execute, but that tends to be much more complex and easy to mess up, so I would not suggest going down that rabbit hole for beginners.
[EDIT]
So, I've seen several questions recently about deltaTime, but don't see any good examples of how to implement it; so, here is a basic example I threw together. To implement it, just replace the GAME STUFF part with your actual code of what happens in a game cycle, and run all of your time-based values through the delta() function, and it will convert your values from to units per second to units per currentFrame.
My game us under a load of <input type="text" id="lag" value="100000000"> operations per frame.<br>
My speed is = <input type="text" id="speed" value="500"> px per second<br>
I moved <span id="adjusted"></span>px this frame.<br>
FPS: <span id="fps"></span>
<script>
function wrapDelta(lastTime){
var d = new Date();
var n = d.getSeconds()*1000 + d.getMilliseconds();
if (lastTime >= n) {
lastTime -= 60000;
}
return n - lastTime;
}
function delta(input){
return input * deltaCoeff / 1000;
}
var d = new Date();
var ed = new Date();
var endTime = d.getSeconds()*1000 + d.getMilliseconds();
var startTime = d.getSeconds()*1000 + d.getMilliseconds();
var deltaCoeffMin = 25;
var deltaCoeff = deltaCoeffMin;
setInterval(function () {
d = new Date();
startTime = d.getSeconds()*1000 + d.getMilliseconds();
// START GAME STUFF
var lag = Math.round(Math.sqrt(document.getElementById('lag').value)); //because comparing large numbers caused a wierd lag spike at from 9999999 to 10000000
var speed = document.getElementById('speed').value;
document.getElementById('adjusted').innerHTML = delta(speed);
document.getElementById('fps').innerHTML = (1000/deltaCoeff).toFixed(2);
var i; var j; var k; for (i=0; i<lag; i++){ for (j=0; j<lag; j++){ k = 1234567*1.1;}} //This is just a random math loop to simulate the lag cause by actual game stuff
// END GAME STUFF
ed = new Date();
endTime = ed.getSeconds()*1000 + ed.getMilliseconds();
deltaCoeff = endTime - startTime;
if (deltaCoeff < deltaCoeffMin){deltaCoeff = deltaCoeffMin;}
} , deltaCoeffMin);
</script>
The call to requestAnimationFrame will run the draw function only at a rate supported by the monitor and only if the computer is fast enough. If the code is running slow, it will automatically skip a call to the draw function every now and then. Therefore, the draw function should only contain the rendering code and no logic.
You should first put any code that updates the game's state into another function called update. This function will be called at a consistent-ish rate using setInterval:
function update() {
// read inputs
// move objects
// detect collisions
// etc.
// render a new frame only if the browser is done drawing the previous one
requestAnimationFrame(draw);
}
// run the update function 60 times per second
var updateInterval = setInterval(update, 1000 / 60);
It's always good to store the updateInterval so that we can stop the game from running completely with clearInterval(updateInterval), but you might never need to use it.
Now that you have a somewhat consistent game speed, you can set a cooldown on the shooting like this:
if (fireCooldown > 0) {
fireCooldown -= 1;
}
if (/* holding the fire key */ && fireCooldown === 0) {
// create a projectile in front of the player ship
fireCooldown = 30;
}
You'll need to declare that variable somewhere first with var fireCooldown = 0; somewhere, but it should get you started.
As mentioned by Jake Holzinger in the comments, setInterval isn't 100% accurate and the update function might be called a few milliseconds later than expected. You'd have to check the time between two calls yourself using Date objects or other means if you wanted to time stuff perfectly, but I doubt this is necessary for a simple shooter game.

How do I get the X and Y coordinates of an image that I move around the canvas

As I move the image of player.hero around the canvas I would like a variable that holds the current x and y pos of the hero. So I can make zombie image move towards current position of hero. Thanks and if my code so far is terrible please suggest amendments thanks.
(function() {
var canvas, context, width, height, speed = 8;
var interval_id;
var zombies = [];
var bullets = [];
var moveLeft = false;
var moveRight = false;
var moveUp = false;
var moveDown = false;
var player = {
x : 0,
y : 0,
width : 35,
height : 60,
hero : new Image(),
};
for (var i = 0; i < 10; i += 1){
var zombie = {
x : 10,
y : 10,
undead : new Image(),
targetToGox : 0,
targetToGoy : 0,
};
zombies.push(zombie);
}
var mouse = {
x : 0,
y : 0,
}
document.addEventListener('DOMContentLoaded', init, false);
function init() {
canvas = document.querySelector('canvas');
context = canvas.getContext('2d');
width = canvas.width;
height = canvas.height;
player.x = width / 2 - 18;
player.y = height / 2 - 30;
player.hero.src = 'hero.png';
zombie.undead.src = 'zombie.png';
//context.drawImage(player.hero, player.x, player.y);
window.addEventListener("keydown", activate,false);
window.addEventListener("keyup",deactivate,false);
//window.addEventListener("mouseover", drawImagePointingAt, false);
interval_player = window.setInterval(drawPlayer, 33);
}
function drawPlayer() {
context.clearRect(0 ,0 ,width, height);
context.drawImage(player.hero,player.x, player.y);
//******** need zombie to go to position of player.hero******///
context.drawImage(zombie.undead (somthing for x and y coordinats of player.hero);
// stops player moveing beyond the bounds of the canvas
if (player.x + player.width >= width) {
moveRight = false
}
if (player.y + player.height >= height) {
moveDown = false
}
if (player.x <= 0) {
moveLeft = false
}
if (player.y <= 0) {
moveUp = false
}
if (moveRight) {
player.x += speed;
}
if (moveUp) {
player.y -= speed;
}
if (moveDown) {
player.y += speed;
}
if (moveLeft){
player.x -= speed;
}
function activate(event) {
var keyCode = event.keyCode;
if (keyCode === 87){
moveUp = true;
}
else if (keyCode === 68){
moveRight = true;
}
else if (keyCode === 83){
moveDown = true;
}
else if (keyCode === 65){
moveLeft = true;
}
}
function deactivate(event) {
var keyCode = event.keyCode;
if (keyCode === 87){
moveUp = false;}
else if (keyCode === 68){
moveRight = false;}
else if (keyCode === 83){
moveDown = false;}
else if (keyCode === 65){
moveLeft = false;}
}
function getRandomNumber(min, max) {
return Math.round(Math.random() * (max - min)) + min;
}
function stop() {
clearInterval(interval_player);
}
})();
This is a pretty long wall of text about why I think it would be better that you restructured your code instead of "... getting the X and Y coordinates of an image that I move around the screen".
The end of this post contains a script that tries to show how you might go about doing that.
You asked about your code's quality. Your code is not terrible for a new programmer, but you are falling into some classic traps will be painful as your codebase gets larger.
An example of this might be keeping variables for each of the possible directions your player should move after a keypress (which is manipulated & used in separate functions). The problem with this is that when you decide to change any aspect of this system, it's going to crumble.
Instead, consider having an object representing the player which contains it's own internal logic for 'moving' by changing it's own coordinates.
I cannot emphasize this idea enough - Excellent hackers always give themselves a room to work. You shouldn't ever (for example), make the Player directly manipulate the game drawing routines. This is such a pervasive concept that there are actually words in software engineering for different facets of this organizational principle (words like 'Encapsulation' or 'Loose coupling' or 'Law of Demeter'). It's that important.
This leads me to another point: Global variables.
This is a tougher one because it's one where all programmers eventually make hypocrites of themselves if they are too critical of it (especially if they are doing low-level work). Still, it's best to consolidate whatever global variables you do have, and perhaps make functions that serve as their interface to the 'outside world'.
In your game, this would mean moving like moveLeft into a "game loop" that simply checks all of the 'objects' coordinates, clearing the screen & drawing those objects appropriately.
Another important idea here is that 'duplicate' functionality should share a single method. In your case, this would entail that both Player and Zombie become instances of some more abstract category which I'll name GameObject. This allows you to write all your major functions once for GameObject and 'inherit' their functionality inside of the Player and Zombie (there are many other, perhaps even better, ways to accomplish this without prototypes or inheritance at all)
In consideration of all of this, I tried to spend 20 minutes whipping up something that can hopefully give you something to work from. If this is totally unrelated to what you were going for, at the very least you can notch another round of possibly pointless internet pontification under your belt.
My code's "inheritance" is done in a very plain Javascript style, even in spite of the fact there are no less than a dozen 'new and improved' ways to share implementation details between code in JS, each with great variety in their depth of adherence to the principles of either prototypical or object oriented programming.
I cannot hope to cover Stamps, jSL, even Javascript's now infamous new 'class' keyword, so I would advise you read up about these and perhaps put them to profitable use yourself, but for now I'm sticking with the basics.
const ZOMBIE_COUNT = 10
function GameState() {
this.player = null;
this.enemies = []
}
var Game = new GameState() // our global game state
// An abstract 'game object' or character
function GameObject({x, y, image}) {
this.x = x
this.y = y
this.image = image
}
GameObject.prototype.draw = function() {
Game.ctx.fillStyle = this.color
Game.ctx.fillRect(this.x, this.y, 10, 10)
}
GameObject.prototype.moveLeft = function(n) { if(this.x > 0) this.x -= n }
GameObject.prototype.moveRight = function(n) { if(this.x < Game.width) this.x += n }
GameObject.prototype.moveDown = function(n) { if(this.y < Game.height) this.y += n}
GameObject.prototype.moveUp = function(n) { if(this.y > 0) this.y -= n }
function Player({x, y, width}) {
GameObject.call(this, {x, y}) // setup x, y & image
this.color = 'red'
}
Player.prototype = Object.create(GameObject.prototype, {})
function Zombie({x, y, target}) {
GameObject.call(this, {x, y}) // setup x, y & image
this.target = target // target contains the player
this.color = 'blue'
}
Zombie.prototype = Object.create(GameObject.prototype, {})
Zombie.prototype.moveToPlayer = function() {
let {x, y} = Game.player
// very basic 'movement' logic
if (this.x < x) {
this.moveRight(1/4)
} else if (this.x > x) {
this.moveLeft(1/4)
}
if (this.y > y) {
this.moveUp(1/4)
} else if (this.y < y) {
this.moveDown(1/4)
}
}
function init() {
var canvas = document.getElementById('canvas')
if (canvas.getContext) {
var ctx = canvas.getContext('2d')
} else {
console.log("No canvas")
return -1
}
let {width, height} = canvas
// Setup our game object
Game.width = width
Game.height = height
Game.ctx = ctx
// Create our player in the middle
Game.player = new Player({x: width / 2, y: height / 2})
// Create our enemies
for(let i = 0; i < ZOMBIE_COUNT; i++) {
Game.enemies.push(new Zombie({x: Math.random() * width | 0, // truncate our value
y: Math.random() * height | 0}))
}
game_loop()
}
function game_loop() {
window.requestAnimationFrame(game_loop)
Game.ctx.fillStyle = 'white'
Game.ctx.fillRect(0, 0, Game.width, Game.height);
Game.player.draw()
Game.enemies.map(enemy => {
enemy.moveToPlayer()
enemy.draw()
})
}
function process_key(ev) {
let speed = 3
let key = ev.keyCode
if (key === 68)
Game.player.moveRight(speed)
if (key === 87)
Game.player.moveUp(speed)
if (key === 65)
Game.player.moveLeft(speed)
if (key === 83)
Game.player.moveDown(speed)
}
window.addEventListener('keydown', process_key, false);
init()
canvas { border: 3px solid #333; }
<canvas id="canvas" width="400" height="400"></canvas>
I assume you mean this line?
//******** need zombie to go to position of player.hero******///
context.drawImage(zombie.undead (somthing for x and y coordinats of player.hero);
I would change the code to something like:
function init() {
...
interval_player = window.setInterval(updateGame, 33);
}
function updateGame() {
context.clearRect(0 ,0 ,width, height);
updatePlayer();
for (let zombie of zombies) {
updateZombie(zombie);
}
function updatePlayer() {
// stops player moveing beyond the bounds of the canvas
if (player.x + player.width >= width) {
moveRight = false
}
if (player.y + player.height >= height) {
moveDown = false
}
if (player.x <= 0) {
moveLeft = false
}
if (player.y <= 0) {
moveUp = false
}
if (moveRight) {
player.x += speed;
}
if (moveUp) {
player.y -= speed;
}
if (moveDown) {
player.y += speed;
}
if (moveLeft){
player.x -= speed;
}
context.drawImage(player.x, player.y);
}
function updateZombie(zombie) {
// Move zombie closer to player
if (zombie.x > player.x)
zombie.x -= zombieSpeed;
else if (zombie.x < player.x)
zombie.x += zombie.Speed;
if (zombie.y > player.y)
zombie.y -= zombieSpeed;
else if (zombie.y < player.y)
zombie.y += zombie.Speed;
context.drawImage(zombie.undead, zombie.x, zombie.y);
}
This line here:
zombie.undead.src = 'zombie.png';
will only change the last zombie created. You really need to move that:
for (var i = 0; i < 10; i += 1) {
var zombie = {
x : 10,
y : 10,
undead : new Image(),
targetToGox : 0,
targetToGoy : 0,
};
zombie.undead.src = 'zombie.png';
zombies.push(zombie);
}

how to move an image on canvas using keyboard arrow key in html5

I m Drawing the image on canvas with this code
and it successfully draw the image on canvas now i want to move the image on canvas for that i write the code i check that if the right key of my keyboard is pressed i will increment the x coordinate of an image if left key is pressed i will decrement the x coordinate but image is not moving on the canvas
player = new Image();
player.src = "game_character.png";
context.drawImage(player,player.x * wallDim + wallDim ,player.y * wallDim + wallDim ,50,50);
how to move an image on canvas
var handleInput = function(event, keyState) {
switch(event.which) {
case 37: { // Left Arrow
keyDown.arrowLeft = keyState;
break;
}
case 38: { // Up Arrow
keyDown.arrowUp = keyState;
break;
}
case 39: { // Right Arrow
keyDown.arrowRight = keyState;
break;
}
case 40: { // Down Arrow
keyDown.arrowDown = keyState;
break;
}
}
}
/**
* physics
*
* This function contains the basic logic for the maze.
*/
var physics = function() {
console.log("physics ");
console.log("first condition "+keyDown.arrowRight +player.x+1);
if(keyDown.arrowLeft && player.x-1 >= 0 && map[player.y][player.x-1] != 1) {
player.x--;
redraw = true;
}
if(keyDown.arrowUp && player.y-1 >= 0 && map[player.y-1][player.x] != 1) {
player.y--;
redraw = true;
}
if(keyDown.arrowRight && player.x+1 < map[0].length && map[player.y][player.x+1] != 1) {
console.log("arrow right");
player.x++;
redraw = true;
}
if(keyDown.arrowDown && player.y+1 < map.length && map[player.y+1][player.x] != 1) {
player.y++;
redraw = true;
}
if(keyDown.arrowRight && player.x+1 >= map[0].length)
{
player.x++;
document.getElementById("canvas_div").style.display="none";
document.getElementById("end_screen_div").style.display="block";
//alert("completed");
}
}
/**
* draw
*
* This function simply draws the current state of the game.
*/
var draw = function() {
// Don't redraw if nothing has changed
if(!redraw)
return;
context.clearRect(0, 0, cols, rows);
context.beginPath();
// Draw the maze
for(var a = 0; a < rows; a++) {
for(var b = 0; b < cols; b++) {
switch(map[a][b]) {
case C.EMPTY: context.fillStyle = colors.empty; break;
case C.WALL: context.fillStyle = colors.wall; break;
}
context.fillRect(b * wallDim, a * wallDim, wallDim, wallDim); // x, y, width, height
}
}
// Draw the player
/* context.fillStyle = colors.player;
context.arc(
player.x * wallDim + wallDim / 2, // x position
player.y * wallDim + wallDim / 2, // y position
wallDim / 2, // Radius
0, // Starting angle
Math.PI * 2, // Ending angle
true // antiClockwise
);*/
player = new Image();
player.src = "game_character.png";
context.drawImage(player,player.x * wallDim + wallDim ,player.y * wallDim + wallDim ,50,50);
var firstplayer=new Image();
firstplayer.src="top_character01.png";
context.drawImage(firstplayer,680,0,60,60);
var secondplayer= new Image();
secondplayer.src="top_character02.png";
context.drawImage(secondplayer,750,0,60,60);
context.fill();
context.closePath();
redraw = false;
}
In your draw method, you reinitialize the player each time :
player = new Image();
player.src = "game_character.png";
So you erase the player.x modified by your event handler.
You should initialize the player only once, outside the draw function. You can move the initialization like this :
var player = new Image();
player.src = "game_character.png";
var draw = function() {
There is absolutely no need to call player.src = "game_character.png"; inside the draw function.
As a general rule, when dealing with animation, try to remove all what you can from the draw function, which should be as fast as possible.
You will need to redraw the canvas each time. Something like this:
function init()
{
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
x = canvas.width / 2; //align to centre of the screen
y = canvas.height / 2; //same as above
speed = 5; //speed for the player to move at
width = 50; //width of the player
height = 50; //height of the player
playerimage = new Image();
playerimage.src = "path/to/image/for/player"; //path to the image to use for the player
canvas.addEventListener("keypress", update);
}
function update(event)
{
if (event.keyCode == 38)
{
y -= speed; //going up
}
if (event.keyCode == 40)
{
y += speed; //going down
}
if (event.keyCode == 37)
{
x -= speed; //going left
}
if (event.keyCode == 39)
{
x += speed; //going right
}
render();
}
function render()
{
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(playerimage, x, y, width, height);
}
I haven't tested it, so I don't know whether it works and there may be some mistakes here and there. It should work though! If nothing else, it will (hopefully) give you an idea of one way in which you can go about doing it...

Categories

Resources