Issues with keyIsDown() in p5js - javascript

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>

Related

p5.js Array: Multiple Objects getting Spliced instead of Single

I'm trying to create a moving array of decorative circles, and as soon as a circle moves off the edge of the screen I would like to remove it from the array. I have tried both splicing and filtering from the array – however, as soon as I do this, all the circles in the array get deleted, instead of just the intended one. Checked multiple sources and tried different thinkgs but I simply can't figure out what's going wrong. You can have a look at the demo here in the p5.js editor: https://editor.p5js.org/devalladares/sketches/v6bWAiISM
Main code here if it helps:
function setup() {
//Create new Circle
for (let i = 0; i < circleNumber; i++) {
circles.push(new createCircle())
}
}
function draw() {
for (let i = circles.length - 1; i >= 0; i--) {
circles[i].update(i * 200)
//This is the problem: Simply cannot figure it out
//Splicing
if (circles[i].isDead()) {
circles.splice(i, 1)
}
}
//Filtering
// circles = circles.filter(x => !x.isDead())
}
//Class to create Circle
class createCircle {
constructor() {
this.circle = []
this.origin = i * gap + emptySpace2
this.pos = 0
this.xPos = width / 5
this.create()
}
create() {
for (let i = 0; i < ringNumber; i++) {
this.circle.push(new circleRing())
}
}
update(dist) {
push()
this.xPos -= xSpeed
this.pos = this.xPos + dist / 1.8
translate(this.pos, this.origin / 1.15)
scale(0.6, 0.6)
for (let circles of this.circle) {
circles.update()
}
pop()
}
isDead() {
if (this.pos < 40) {
return true;
} else {
return false;
}
}
}
Thanks in advance!!
Your issue isn't the most obvious thing in the world, but it comes from your for-loop and how you're updating your circles' x positions:
for (let i = circles.length - 1; i >= 0; i--) {
circles[i].update(i * 200)
When you delete a circle, the subsequent circles all shift down one index in your array, meaning if you delete the circle at index 0, the circle at index 1 now becomes the circle at index 0, which when you apply your cricles[i].update(i * 200) in the next call to draw(), i*200 will evaluate to 0, which causes your circle to shift into the "kill zone".
Instead, you store an internal circleNum property in your circles when you create them, and adjust your position based on that:
class createCircle {
constructor(circleNum) {
this.circle = [];
this.pos = 0;
this.xPos = width / 5;
this.circleNum = circleNum;
this.create();
}
...
}
Then when you create your circle, pass in the index:
//Create new Circle
for (let i = 0; i < circleNumber; i++) {
circles.push(new createCircle(i))
}
also, change how you call .update():
circles[i].update(200);
and lastly, use circleNum when calculating the new pos (as circleNum will remain consistent unlike i):
this.pos = this.xPos + (this.circleNum*dist) / 1.8;

How to detect cursor collision with multiple moving objects in javascript?

I'm making a game where the player will move using their cursor and will have to dodge objects. Each object has a hitbox, so I want to be able to update the number of lives whenever the cursor hits one. I'm just not sure what an efficient way is to keep track of all the hitboxes. I'd also like to record the object that the cursor collided with, so that bonus objects can be counted. The objects constantly respawn and move off the canvas, so I think keeping an array of all the hitboxes would pretty quickly get out of hand. Here's some code with some pseudo code for what I want to do.
canvas.addEventListener("mousemove", e => {
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
if (somehitboxX<=mouseX<=somehitboxX + 50 and somehitboxY<=mouseY<=somehitboxY + 50){
doSomething
}
});
EDIT: Instead of having my cursor keep track of all the hitboxes, would it be possible to have the hitboxes keep track of the cursor? Maybe by adding an event listener to each object? If that's possible, how would I go about doing it?
EDIT: Here's some code to get a better idea of what I'm working with. The emoji objects are what need to be dodged by the player. Each one has an emoji, a score, coordinates, speed, a hitbox, and a boolean that says whether it's been hit or not (though I'm not sure how to control it yet since that's what I need help with).
function Emoji(emojicon, score, x, y, speed) {
this.emojicon = emojicon;
this.score = score;
this.x = x;
this.y = y;
this.speed = speed;
this.hitbox = { hbx: x, hby: y - 50, hbWidth: 50, hbHeight: 50 };
this.hit = false;
}
The emoji objects are initialized like this:
var xPos = 0;
for (var i = 0; i < 9; i++) {
var emo = new Emoji("😜", 5, xPos, 520, 1);
emojis.push(emo);
xPos += 55;
}
emojis.forEach(emoji => drawEmoji(emoji));
}
And then Emoji.prototype.update updates both the coordinates of the emoji and its hitbox:
Emoji.prototype.update = function() {
if (this.y < 520) {
this.y -= this.speed;
this.hitbox.hby = this.y - 50;
} else {
var flip = Math.floor(Math.random() * 1000);
if (flip == 1) {
this.y -= this.speed;
this.hitbox.y = this.y - 50;
}
}
};
I hope this is enough code to get an idea of what I have so far.

Block in canvas dissapeared for one frame

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">

Rotating a canvas object with keyboard

it's my first question.
I'm doing an Asteroids game copy and I've started with spaceship movement, but
I've got a problem with rotating it in canvas in js. The problem is how to stop rotation after pressing key? It's rotating in two directions, but when I release the key, object returns to its initial state.
here is code:
window.addEventListener('keydown',doKeyDown,true);
window.addEventListener('keyup',doKeyUp,true);
//var x = canvas.width/2;
//var y = canvas.height/2;
var keys = new Array();
function doKeyDown(evt){
keys[evt.keyCode] = true;
}
function doKeyUp(evt){
keys[evt.keyCode] = false;
}
var context = document.getElementById('pageCanvas').getContext('2d');
var angle = 0;
var angle2 = 0;
function convertToRadians(degree) {
return degree*(Math.PI/180);
}
function incrementAngle() {
angle -= 10;
if(angle > 360) {
angle = 0;
}
}
function decrementAngle(){
angle2 += 10;
if(angle2>360){
angle2 = 0;
}
}
function drawRandomlyColoredRectangle() {
context.save();
context.clearRect(0,0,500,500);
fillStyle = "#000000";
context.fillRect(0,0,500,500);
incrementAngle();
decrementAngle();
context.translate(200,200);
if(37 in keys && keys[37]){
context.rotate(convertToRadians(angle));
console.log("lewo");
};
if (39 in keys && keys[39]){ //right
//x += dx/5;
//rotacja w prawo
context.rotate(convertToRadians(angle2));
console.log("prawo");
};
context.fillStyle = "green";
context.fillRect(-25,-25,50,50);
context.restore();
}
setInterval(drawRandomlyColoredRectangle, 20);
and fiddle http://jsfiddle.net/tomasthall/covyjaLb/2/
Help, please :c
Just store the current value of angle in the variable and use it in each rendering frame.
if(37 in keys && keys[37]){
decrementAngle();
};
if (39 in keys && keys[39]){
incrementAngle();
};
(...)
context.rotate(convertToRadians(angle));
Check and study other fixes and read some tutorials/articles before asking next questions...
http://jsfiddle.net/covyjaLb/3/

Two second delay on key down event [duplicate]

Here is my problem: http://testepi.kvalitne.cz/test/
I do not want the delay between a keypress and the movement of the square. I would also like to know how to move diagonally (pressing two keys at same time).
My code:
$(function(){
document.addEventListener("keydown", move, false);
var x = 0;
var y = 0;
function move(event){
if(event.keyCode==37){
x -= 10;
$("#square").css("left", x);
}
if(event.keyCode==39){
x += 10;
$("#square").css("left", x);
}
if(event.keyCode==38){
y -= 10;
$("#square").css("top", y);
}
if(event.keyCode==40){
y += 10;
$("#square").css("top", y);
}
}
});
First, to avoid the keypress/repeat delay, you have to wrap your program in a loop, and make the state of the keyboard available inside the scope of that loop, secondly to monitor multiple keypresses you need to keep track of individual keydown and keyup events:
var x = 0;
var y = 0;
// store the current pressed keys in an array
var keys = [];
// if the pressed key is 38 (w) then keys[38] will be true
// keys [38] will remain true untill the key is released (below)
// the same is true for any other key, we can now detect multiple
// keypresses
$(document).keydown(function (e) {
keys[e.keyCode] = true;
});
$(document).keyup(function (e) {
delete keys[e.keyCode];
});
// we use this function to call our mainLoop function every 200ms
// so we are not relying on the key events to move our square
setInterval( mainLoop , 200 );
function mainLoop() {
// here we query the array for each keyCode and execute the required actions
if(keys[37]){
x -= 10;
$("#square").css("left", x);
}
if(keys[39]){
x += 10;
$("#square").css("left", x);
}
if(keys[38]){
y -= 10;
$("#square").css("top", y);
}
if(keys[40]){
y += 10;
$("#square").css("top", y);
}
}
If you are trying to implement game-like 2d sprite movement, I suggest you have a notion of x and y velocity, rather than moving the sprite a fixed amount on keypress.
So on keypress, add or subtract from x or y velocity.
var xvel = 0,
yvel = 0,
x = 0,
y = 0;
setInterval(function () {
y += yvel;
x += xvel;
$("#square").css("left", x);
$("#square").css("top", y);
}, 16); //run an update every 16 millis
document.addEventListener("keydown", move, false);
function move(event){
if(event.keyCode==37){
xvel -= 10;
}
if(event.keyCode==39){
xvel += 10;
}
if(event.keyCode==38){
yvel -= 10;
}
if(event.keyCode==40){
yvel += 10;
}
}
You would also need to worry about a keyup event, however, as the velocity would stay until you cancelled it out.
You can use setInterval to update the position of the sprite every x milliseconds. (Game tick)
To move diagonally, just add/subtract from the velocity of both x and y simultaneously.
This is just an example, but there are more examples out there on sprite movement.
have you looked here? you should be able to do diagonal moving by checking if multiple keys have been pressed down without being released.

Categories

Resources