Block in canvas dissapeared for one frame - javascript

I'm trying to build Snake from scratch using Javascript. But when I use the arrow keys to get it from 1 part of the canvas to the other part of the canvas it dissapears for 1 frame, how to resolve this? You can try it on: https://annedegraaff.nl/snake/
<canvas id="snake" width="400" height="400">
</html>
<script>
var canvas;
var canvasContext;
var ball1X = 12.5;
var ball1Y = 12.5;
window.onload = function() {
canvas = document.getElementById('snake');
canvasContext = canvas.getContext('2d');
var framesPerSecond = 60;
setInterval(function() {
draw();
move();
}, 1000/framesPerSecond);
}
function move() {
window.onkeydown = function(e) {
var key = e.keyCode ? e.keyCode : e.which;
if (ball1X < 12.5) {
ball1X += 395;
}else if (ball1X > 385) {
ball1X -= 395;
}
if (key == 39) {
ball1X += 10;
}else if (key == 37) {
ball1X -= 10;
}else if (key == 40) {
ball1Y += 10;
}else if (key == 38) {
ball1Y -= 10;
}
}
}
function draw() {
canvasContext.fillStyle = 'green';
canvasContext.fillRect(0,0,canvas.width,canvas.height);
canvasContext.fillStyle = 'black';
canvasContext.fillRect(ball1X,ball1Y,10,10);
}
</script>

Logic error in keyEvent handler
Though not directly evident where the problem is in the given code I am assuming it is the test for edges in the keydown handler. There are also other ting being done incorrectly that will present additional problems and difficulties as you develop the game.
Your bug
In your keyboard function you test if the ball is close to the edge and if so you move it to the other size. Looks like you move it too far and thus can not be seen.
The following is a quick fix. Move the test to after the ball has been moved and make sure the the move to the other side does not put it too far so that it is moved again on the next event.
window.onkeydown = function(e) {
var key = e.keyCode ? e.keyCode : e.which;
if (key == 39) {
ball1X += 10;
}else if (key == 37) {
ball1X -= 10;
}else if (key == 40) {
ball1Y += 10;
}else if (key == 38) {
ball1Y -= 10;
}
if (ball1X < 12.5) {
ball1X += 400;
}else if (ball1X > 400-12.5) {
ball1X -= 400;
}
}
Other problems.
Use requestAnimationFrame for animations not setInterval
Key event listeners should only record the keyboard state as they have nothing to do with the game and dont know what to do with the keys pressed. The game code should use the keyboard state and its own current state to work out what to do with each key
Use addEventListener add events as directly setting event can be overwritten
Encapsulate your game inside a function so that all the variables and functions are isolated from the global names space and you can easily insert the game into any page.
Use objects to group properties and function together. Eg you had ballX, ballY and most like will add other properties each will have a ball prefix. By creating an object named ball and adding properties like x,y you can get access to the balls x, y with via a reference ball.x, ball.y or var b = ball; b.x += 1;`. Once an object has been defined you can make many copies easily.
Change the key handler to hold the key state of only the keys you are interested in. You only want to know if the key is down so listen to key up and down setting a flag to true when a key is down.
And other stuff
Rewrite
A quick rewrite showing a better way to implement what you had. It is a recommendation only. It is a little longer than you had it and is not the only way, but if you write like this it will be easier as the game develops.
See comments for the reasons and what does what.
// create onload event handler as a function and encapsulate all variables and functions to key global name space clean
function start(){
// create canvas and context
const canvas = document.getElementById("snakeCanvas");
const ctx = canvas.getContext("2d"); // formaly canvasContext;
// get the size as we use that a lot
const width = canvas.width;
const height = canvas.height;
requestAnimationFrame(mainLoop); // will call mainloop after this function (start) has run
// create an object to hold all related properties and
// functions for the ball
const ball = { // position ball in center
x : width / 2 | 0, // the or zero ( | 0) rounds down to nearest integer
y : height / 2 | 0,
size : 10,
speed : 10,
draw() { // function to draw the ball
ctx.fillStyle = 'black';
ctx.fillRect(this.x - this.size / 2,this.y - this.size / 2, this.size, this.size);
},
update() { // moves the ball
if (keys.up === true) { this.y -= this.speed }
if (keys.down === true) { this.y += this.speed }
if (keys.left === true) { this.x -= this.speed }
if (keys.right === true) { this.x += this.speed }
// get half size
const hSize = this.size / 2;
// check for edges and move to other side of canvas
if(this.x + hSize < 0) { this.x += width }
if(this.x - hSize > width) { this.x -= width }
if(this.y + hSize < 0) { this.y += height }
if(this.y - hSize > height) { this.y -= height }
},
}
// the background function clears and displays the background
function background(){
ctx.fillStyle = 'green';
ctx.fillRect(0,0,width,height);
}
// Object to hold the current keyboard state
const keys = {
up : false,
down : false,
left : false,
right : false,
map : new Map([ // use a Map to find keys
[39,"right"], // key code and string name of keys.name
[37,"left"],
[40,"down"],
[38,"up"],
])
}
// the key event listener
function keyEvents(event){
// get key code as a string
const keyCode = event.keyCode ? event.keyCode : event.which;
// get if avalible the key name from the map
const key = keys.map.get(keyCode);
// if a key is mapped set its state
if(key){
keys[key] = event.type === "keydown";
event.preventDefault(); // pervent default action
}
}
// listent to the keyboard events and set the keyboard state
["keydown","keyup"].forEach(eventName => addEventListener(eventName, keyEvents));
// for the stackoverflow snippet we need to get focus to
// hear any of the key events
focus();
function mainLoop(time){ // time is automatic and in ms (1/1000th second)
background(); // call the background function that clears and displays the background
// update and draw the ball object
ball.update();
ball.draw();
requestAnimationFrame(mainLoop); // request next frame in 1/60th second
}
}
// when loaded start the game
addEventListener("load", start);
<canvas id="snakeCanvas" width="400" height="400">

Related

Issues with keyIsDown() in p5js

I'm trying to make a basic 2d game with p5js and p5.play. An issue that seems to cause issues every time I try to do anything is the keyIsDown function. Is there a way to determine if a key is down before pressing it? If I used
upKey = keyIsDown(UP_ARROW);
upKey will show as undefined until I press the up arrow. Is there any way to assign the respective boolean values to these types of things prior to pressing them?
As of now, my game will not properly work until I have pressed every involed key one time.
The keyIsDown() function checks if the key is currently down, i.e. pressed. It can be used if you have an object that moves, and you want several keys to be able to affect its behaviour simultaneously, such as moving a sprite diagonally.
Note that the arrow keys will also cause pages to scroll so you may want to use other keys for your game.. but if you want to use arrow keys this is the code snippet from the reference page
let x = 100;
let y = 100;
function setup() {
createCanvas(512, 512);
}
function draw() {
if (keyIsDown(LEFT_ARROW)) {
x -= 5;
}
if (keyIsDown(RIGHT_ARROW)) {
x += 5;
}
if (keyIsDown(UP_ARROW)) {
y -= 5;
}
if (keyIsDown(DOWN_ARROW)) {
y += 5;
}
clear();
fill(255, 0, 0);
ellipse(x, y, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
To implement similar logic without the use of arrow keys you will need to determine the key code of the keys you want to use.
Here is an example that uses awsd keys and also logs out the key code of the currently pressed key.
let x = 50;
let y = 50;
function setup() {
createCanvas(512, 512);
}
function keyPressed(){
console.log(keyCode);
}
function draw() {
if (keyIsDown(65)) {
x -= 5;
if (x < 0) x = 0;
}
if (keyIsDown(68)) {
x += 5;
if (x > width) x = width;
}
if (keyIsDown(87)) {
y -= 5;
if (y < 0) y = 0;
}
if (keyIsDown(83)) {
y += 5;
if ( y > height) y = height;
}
clear();
fill(255, 0, 0);
ellipse(x, y, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>

How to add a rectangle here? [duplicate]

As the title suggests, I am having trouble with object collision...
I am currently working on a 2d Html5 canvas game using JavaScript. I know how to keep the "player" object from going outside the width/height of the game canvas, and i know how to do something when the player collides with an object (such as a power up or enemy or whatever) but i just don't know how to make a "solid" object meaning when the player hits the solid object, the player just stops, and cannot go through the solid object.
This is what I have now (not all the code just what I feel is relevant, sorry if it's too much/too little.:
var canvasPlayer = document.getElementById('canvasPlayer');
var ctxPlayer = canvasPlayer.getContext('2d');
var canvasWalls = document.getElementById('canvasWalls');
var ctxWalls = canvasWalls.getContext('2d');
function checkKeyDown(e) {
var keyID = (e.keyCode) || e.which;
if (keyID === 38 || keyID === 87) { // up arrow OR W key
if (!player1.isDownKey && !player1.isLeftKey && !player1.isRightKey) {
player1.isUpKey = true;
e.preventDefault();
} }
if (keyID === 39 || keyID === 68) { //right arrow OR D key
if (!player1.isDownKey && !player1.isLeftKey && !player1.isUpKey) {
player1.isRightKey = true;
e.preventDefault();
} }
if (keyID === 40 || keyID === 83) {//down arrow OR S key
if (!player1.isUpKey && !player1.isLeftKey && !player1.isRightKey) {
player1.isDownKey = true;
e.preventDefault();
} }
if (keyID === 37 || keyID === 65) {//left arrow OR A key
if (!player1.isDownKey && !player1.isUpKey && !player1.isRightKey) {
player1.isLeftKey = true;
e.preventDefault();
}
}
}
Walls.prototype.draw = function (){
ctxWalls.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,this.drawX,this.drawY,this.width,this.height);
this.checkHitPlayer();
};
Walls.prototype.checkHitPlayer = function() {
if (this.drawX > player1.drawX &&
this.drawX <= player1.drawX + player1.width &&
this.drawY >= player1.drawY &&
this.drawY < player1.drawY + player1.height) {
player1.isUpKey = false;
player1.isDownKey = false;
player1.isRightKey = false;
player1.isLeftKey = false;
}
};
This works... except when trying to go up or left, the player only moves maybe 2-3 pixels, so it takes 3 left or up arrows to go left or up. As well the player can move straight through the wall which is not what i want. Any help is much appreciated sorry if i included too much or not enough code. Oh, i also forgot to mention the game is a puzzle game, and I have it set-up so a player can only move one direction at a time until hitting a wall.
If you just want your player to stop when the reach a wall, you can apply some math:
For example: assume your player is a 10px by 10px rectangle and the right wall's X position is 200.
The X position of the right side of the rectangle is calculated like this:
var playerRightSide = player.x + player.width;
You can test if the player has reached the wall like this:
if( playerRightSide >= 200 )
If the user tries to push their player beyond the wall, you would hold the player to the left of the wall using the players X position.
if( playerRightSide >= 200 ) { player.x = 190; }
The 190 is the wall's X position (200) minus the player's width (10).
Read further if you're interested in doing more advanced collision testing.
Many basic game collisions can be classified into 3 types:
Circle versus Circle collision
Rectangle versus Rectangle collision
Rectangle versus Circle collision
Here’s an illustration of how to detect each of these common collisions.
Assume you define a circle like this:
var circle1={
x:30,
y:30,
r:10
};
Assume you define a rectangle like this:
var rect1={
x:20,
y:100,
w:20,
h:20
};
You can detect Circle vs Circle collisions like this...
...Using this Circle vs Circle collision-test code:
// return true if the 2 circles are colliding
// c1 and c2 are circles as defined above
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
You can detect Rectangle vs Rectangle collisions like this...
...Using this Rectangle vs Rectangle collision-test code:
// return true if the 2 rectangles are colliding
// r1 and r2 are rectangles as defined above
function RectsColliding(r1,r2){
return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
}
You can detect Rectangle vs Circle collisions like this...
...Using this Rectangle vs Circle collision-test code:
// return true if the rectangle and circle are colliding
// rect and circle are a rectangle and a circle as defined above
function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.w/2));
var dy=Math.abs(circle.y-(rect.y+rect.y/2));
if( dx > circle.r+rect.w2 ){ return(false); }
if( dy > circle.r+rect.h2 ){ return(false); }
if( dx <= rect.w ){ return(true); }
if( dy <= rect.h ){ return(true); }
var dx=dx-rect.w;
var dy=dy-rect.h
return(dx*dx+dy*dy<=circle.r*circle.r);
}
For example, you can use these collision tests to respond to a player touching a power-up cube:
// create a circular player object
// that's located at [30,30] and has a radius of 10px
var player={x:30,y:30,r:10};
// create a rectangular power-up at position [200,30]
var powerup={x:200, y:30, w:20, h:20};
// Let's say the user keys the player to coordinate [200,35]
// (touching the power-up)
player.x = 220;
player.y = 35;
// you can test if the circular player is touching the rectangular power-up
if( RectCircleColliding(powerup,player) ) {
// the player has collided with the power-up, give bonus power!
player.power += 100;
}
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/u6t48/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
ctx.fillStyle="lightgray";
ctx.strokeStyle="skyblue";
// top collision circle vs circle
var circle1={x:30,y:30,r:10};
var circle2={x:70,y:40,r:10};
var circle3={x:100,y:30,r:10};
var direction1=1;
// middle collision rect vs rect
var rect1={x:20,y:100,w:20,h:20};
var rect2={x:50,y:110,w:20,h:20};
var rect3={x:90,y:100,w:20,h:20};
var direction2=1;
// bottom collision rect vs circle
var circle4={x:30,y:200,r:10};
var rect4={x:50,y:205,w:20,h:20};
var circle5={x:100,y:200,r:10};
var direction3=1;
function drawAll(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawCircle(circle1);
drawCircle(circle2);
drawCircle(circle3);
drawCircle(circle4);
drawCircle(circle5);
drawRect(rect1);
drawRect(rect2);
drawRect(rect3);
drawRect(rect4);
}
function drawCircle(c){
ctx.beginPath();
ctx.arc(c.x,c.y,c.r,0,Math.PI*2,false);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function drawRect(r){
ctx.beginPath();
ctx.rect(r.x,r.y,r.w,r.h);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
// return true if the 2 circles are colliding
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
// return true if the 2 rectangles are colliding
function RectsColliding(r1,r2){
return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
}
// return true if the rectangle and circle are colliding
function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.w/2));
var dy=Math.abs(circle.y-(rect.y+rect.h/2));
if( dx > circle.r+rect.w/2 ){ return(false); }
if( dy > circle.r+rect.h/2 ){ return(false); }
if( dx <= rect.w ){ return(true); }
if( dy <= rect.h ){ return(true); }
var dx=dx-rect.w;
var dy=dy-rect.h
return(dx*dx+dy*dy<=circle.r*circle.r);
}
var fps = 15;
function animate() {
setTimeout(function() {
requestAnimFrame(animate);
// circle vs circle
circle2.x = circle2.x+direction1;
if( CirclesColliding(circle2,circle1) || CirclesColliding(circle2,circle3) ){
direction1=-direction1;
}
// rect vs rect
rect2.x = rect2.x+direction2;
if( RectsColliding(rect2,rect1) || RectsColliding(rect2,rect3) ){
direction2=-direction2;
}
// rect vs circle
rect4.x = rect4.x+direction3;
if( RectCircleColliding(rect4,circle4) || RectCircleColliding(rect4,circle5) ){
direction3=-direction3;
}
drawAll();
}, 1000 / fps);
}
animate();
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

Why do my laser rays flicker?

I'm trying my hands at some simple game programming in Javascript and have come to realize I need to change the way I handle sprites. The only question is, "how"?
I have a hero that moves around with the arrow keys and fires laser rays with WASD. This is how I define rays:
function Ray (x, y, width, height, direction, index) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.direction = direction;
this.index = index;
this.speed = 512;
this.disabled = false;
}
The index just indicates where in an array of rays (heh) it is being stored. I currently have a hard-coded limit of 5 simultaneous rays, although the other restrictions (screen size, ray size, speed, hero size etc) shouldn't allow for more than 4:
var rays = [];
var numberOfRays = 0;
var rayLimit = 5;
var shotClock = 300;
And so, in the update() function that gets called by the game loop, I have listeners for the WASD keys. They look like this:
// D
if (68 in keysDown && numberOfRays <= rayLimit && Date.now() - lastShootTime > shotClock) {
lastShootTime = Date.now();
var newRayIndex = findFreeRay();
rays[newRayIndex] = new Ray(hero.x + hero.width + 12, hero.y + hero.height / 2, rayImage.width, rayImage.height, 'right', newRayIndex);
numberOfRays++;
}
(findFreeRay() just returns the lowest unused or disabled (off the screen) index in rays[])
Earlier in the update() method (I have also tried putting it later) I have the logic for updating ray movement:
rays.forEach(function(ray) {
if (ray != null && !ray.disabled) {
switch(ray.direction) {
case 'right':
ray.x += ray.speed * modifier;
break;
case 'left':
ray.x -= ray.speed * modifier;
break;
case 'up':
ray.y -= ray.speed * modifier;
break;
case 'down':
ray.y += ray.speed * modifier;
break;
}
}
});
Finally, there is the image for the ray (actually, one for horizontal rays and another one for vertical rays). Currently, I am using one Image object of each globally, that the existing rays share. But I have also tried, without much luck, to create individual image objects for every ray.
// Ray images
var rayReady = false;
var rayImage = new Image();
rayImage.onload = function() {
rayReady = true;
};
rayImage.src = "images/ray.png";
var rayVertReady = false;
var rayVertImage = new Image();
rayVertImage.onload = function() {
rayVertReady = true;
};
rayVertImage.src = "images/ray_vert.png";
And here is how they get drawn:
if (rayReady && rayVertReady && numberOfRays > 0) {
rays.forEach(function(ray) {
if (ray.x > canvas.width
|| ray.x + ray.width < 0
|| ray.y > canvas.height
|| ray.y + ray.height < 0) {
numberOfRays--;
ray.disabled = true;
}
else if (ray.direction == 'right' || ray.direction == 'left'){
ctx.drawImage(rayImage, ray.x, ray.y);
}
else {
ctx.drawImage(rayVertImage, ray.x, ray.y);
}
});
}
The problem
After shooting only a few rays, new ones start to either flicker and disappear, or stay invisible altogether. They actually exist as gameplay objects though, as they can hit targets.
What likely causes this flickering?
(credit to Matt Hackett for the base of this code)
fiddle: http://jsfiddle.net/Vr3MW/

Getting a gun to fire

I'm trying to make a top down shooter game to learn about coding in HTML/JS/CSS.
I've got a canvas, my player moves and rotates on the canvas, but I'm having some trouble getting his gun working. I've got this with the aid of tutorials and searching through other stackoverflow posts, but this one I can't seem to fix alone.
I have defined a variable 'gunfire' which is set to 1 if the left mouse button is pressed and is otherwise 0, in my function draw() I have an if statement that should draw a rectangle in front of my sprite (to represent bullets) when the left mouse button is pressed.
The problem I have is that the bullets appear at all times, independent of whether the mouse button is pressed. If anyone can point out what it is that I'm doing wrong then I would be very grateful, here's the code (the canvass is created in separate HTML/CSS files):
var turn = 0;
var frameRate = 24;
var main_x = 0,
main_y = 0,
move_x = 0,
move_y = 0;
var gunfire = 0;
var speed = 4;
function keyPress(e) {
if (e.keyCode == 68) { //d
move_x = speed;
}
if (e.keyCode == 65) { //a
move_x = -speed;
}
if (e.keyCode == 83) { //s
move_y = speed;
}
if (e.keyCode == 87) { //w
move_y = -speed;
}
if (e.keyCode == 1) { //left mouse
gunfire = 1;
}
}
function keyRelease(e) {
if (e.keyCode == 68) { //d
move_x = 0;
}
if (e.keyCode == 65) { //a
move_x = 0;
}
if (e.keyCode == 83) { //s
move_y = 0;
}
if (e.keyCode == 87) { //w
move_y = 0;
}
if (e.keyCode == 1) { //left mouse
gunfire = 0;
}
}
function move() {
main_x += move_x;
main_y += move_y;
if (main_x < -220) {
main_x = -220
}
if (main_y < -220) {
main_y = -220
}
if (main_x > 220) {
main_x = 220
}
if (main_y > 220) {
main_y = 220
}
}
function mouseMove(e) {
var mouseX, mouseY;
if (e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
} else if (e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
mouseX = mouseX - (top_canvas.width / 2) - main_x;
mouseY = mouseY - (top_canvas.height / 2) - main_y;
window.radians = Math.atan2(mouseY, mouseX);
//document.getElementById("info").innerHTML = radians
}
var background = new Image();
background.src = "assets/background3.jpg";
var player1 = new Image();
player1.src = "assets/player1.png";
function draw() { //draws all content on the canvas
ctx_1.save();
ctx_1.clearRect(0, 0, top_canvas.width, top_canvas.height);
ctx_1.drawImage(background, 0, 0);
ctx_1.translate(top_canvas.width / 2 + main_x, top_canvas.height / 2 + main_y);
ctx_1.rotate(turn);
ctx_1.drawImage(player1, -25, -25, 50, 70);
if (gunfire = 1) {
ctx_1.fillStyle = "#000";
ctx_1.fillRect(0, -10, 200, 20);
}
ctx_1.restore();
}
function gameLoop() {
// all functions to update go here
draw();
turn = window.radians
move();
}
function init() {
window.top_canvas = document.getElementById("top_canvas");
window.ctx_1 = top_canvas.getContext("2d");
setInterval(gameLoop, 1000 / frameRate);
//event fires every time the mouse moves
top_canvas.addEventListener("mousemove", mouseMove, false);
window.addEventListener("keydown", keyPress, false);
window.addEventListener("keyup", keyRelease, false);
}
window.addEventListener("load", init, false);
First of all, as Kippie points out, you're setting gunfire to 1 each time which results in a true statement. It must be changed to == or === (latter preferable) or simply drop the comparison as the non-zero value would be true:
if (gunfire) {
...
}
Second, you are checking for mouse clicks inside a key handler and assuming keyCode would indicate a mouse button click. keyCode 1 is not related to mouse clicks at all and no mouse clicks will end up here.
Third, you are not having any event handler for mouse clicks...
To solve these issues (in addition to the first) add an event handler for mouse clicks:
top_canvas.addEventListener("mousedown", mouseDown, false);
window.addEventListener("mouseup", mouseUp, false);
Then inside the handlers (only showing for mouse down):
function mouseDown(e) {
if (e.button === 0) { // check left mouse-button
gunfire = 1;
}
}
The reason for using window for mouse up is that in case your mouse pointer is outside the canvas element the up event won't register and the gun will continue to fire. Using the window object will allow you to register up event even in this situation.
if (gunfire = 1)
You're using the assignment operator, rather than doing a comparison.
Use === instead.

Collisions in simple javascript game

I'm writing a simple game in javascript and I'm wondering what the best way to handle collisions between the player and the world objects.
<script>
var isJumping = false;
var isFalling = false;
var w = 1;
var recwidth = 400;
var recheight = 400;
var xpos = 50;
var ypos = 279;
window.onload = function() {
var FPS = 30;
var ground = new myObject();
setInterval(function() {
clear();
draw();
ground.draw(0, 325);
ground.draw(125,325)
}, 1000/FPS);
};
function myObject(){
this.draw = function drawground(groundx, groundy){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
//context.fillRect(xpos,ypos,100,100);
var img=new Image()
img.src="ground.png"
img.onload = function() {
context.drawImage(img,groundx,groundy)}
}
};
function jump()
{
var t=.1;
isJumping=true;
var jumpint= setInterval(function() {
yup = 12*t-(5*t*t);
ypos= ypos - yup;
t = t + .1
if(yup < 0)
{
isJumping = false;
isFalling = true;
clearInterval(jumpint);
jumpint = 0;
fall();
return;
}
}, 20);
}
function fall()
{
t=.10
var fallint= setInterval(function() {
ydown = (5*t*t);
ypos= ypos + ydown;
t = t + .1
if(ypos > 275)
{
isFalling == false;
clearInterval(fallint);
fallint = 0;
return;
}
}, 20);
}
function changex(x){
xpos = xpos + (x);
//clear();
//draw();
}
function changey(y){
ypos = ypos + (y);
//clear();
//draw();
}
function draw(){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
var img=new Image()
img.src="character.png"
img.onload = function() {
context.drawImage(img,xpos,ypos)}
}
function clear(){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
context.clearRect(0,0, canvas.width, canvas.height);
}
document.onkeydown = function(event) {
var keyCode;
if(event == null)
{
keyCode = window.event.keyCode;
}
else
{
keyCode = event.keyCode;
}
switch(keyCode)
{
// left
case 37:
//left
changex(-5);
break;
// up
case 38:
// action when pressing up key
jump();
break;
// right
case 39:
// action when pressing right key
changex(5);
break;
// down
case 40:
// action when pressing down key
changey(5);
break;
default:
break;
}
}
</script>
So, as you can see I'm creating two objects so far, and the player stops falling at any arbitrary point. I feel collisions at this stage wont be too difficult, but once I start adding more I feel it's going to get more difficult. I'm not going to be using the instance of the object with the same image for each instance of the object, so at some point I'm going to change the myobject function to be able to accept the image as a parameter, and then checking for collisions will be a bit more tricky. I also plan on making this into a side scroller, so once one end the map is hit it changes into the next area, which is going to cause performance issues. If I'm checking for collisions on every single object in the entire game every interval I imagine things are going to get slow. What is going to be the best way to limit the number of collisions checked? Obviously, if the object isn't on screen there is no need to check it, but is there a way to limit that. I'm thinking of making an array for every frame of the game, and filling that array with it's objects. Then, only check the array the of the frame the player is currently in. Is this feasible or still going to cause too many issues? Any help is greatly appreciated.
If you want pixel perfect collisions, I have some plain javascript code that worked for me with canvas2d rendering context.
function collide(sprite, sprite2, minOpacity=1) {
// Rectangular bounding box collision
if (sprite.x < sprite2.x + sprite2.width && sprite.x + sprite.width > sprite2.x && sprite.y < sprite2.y + sprite2.height && sprite.y + sprite.height > sprite2.y) {
// Finds the x and width of the overlapping area
var overlapX = (this.rect.x > other.rect.x) ? [this.rect.x, (other.rect.x + other.rect.width) - this.rect.x + 1] : [other.rect.x, (this.rect.x + this.rect.width) - other.rect.x + 1];
// Finds the y and height of the overlapping area
var overlapY = (this.rect.y + this.rect.height > other.rect.y + other.rect.height) ? [this.rect.y, (other.rect.y + other.rect.height) - this.rect.y + 1] : [other.rect.y, (this.rect.y + this.rect.height) - other.rect.y + 1];
// Creates a canvas to draw sprite.image to
var spriteImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
var spriteImageCanvasContext = spriteImageCanvas.getContext("2d");
// Draws sprite.image to spriteImageCanvasContext
spriteImageCanvasContext.drawImage(this.image, sprite.x, sprite.y, sprite.width, sprite.height);
// Creates a canvas to draw sprite2.image to
var sprite2ImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
var sprite2ImageCanvasContext = otherImageCanvas.getContext("2d");
// Draws sprite2.image to sprite2ImageCanvasContext
sprite2ImageCanvasContext.drawImage(sprite2.image, sprite2.x, sprite2.y, sprite2.width, sprite2.height);
// Loops through the x coordinates in the overlapping area
for (var x = overlapX[0]; x <= overlapX[0] + overlapX[1]; x++) {
// Loops through the y coordinates in the overlapping area
for (var y = overlapY[0]; y <= overlapY[0] + overlapY[1]; y++) {
if (/* Checks if the pixel at [x, y] in the sprite image has an opacity over minOpacity input */ thisImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity && /* Checks if the pixel at [x, y] in the sprite2 image has an opacity over minOpacity input */ otherImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity) {
return true;
};
};
};
};
}
Or if you just want rectangular collision, use the first if statement in the function.

Categories

Resources