Using mousePressed on the same object twice? - javascript

I have a program where I want a circle to change color when it's pressed on and then if it's pressed again to change back to the original color.
I have managed to change it on the first click but don't know how to change it back when I click it again.
Do anyone have any tips on how I can change back the color or change to a different color when clicked
the second time?
Current code:
// My function for changing the color when clicked.
function mousePressed () {
for (var i = 0; i < 10; i++) {
bubbles[i].interact();
}
}
// function inside the class for the circle
// to determine distance and what color.
interact () {
let d = dist (this.x, this.y, mouseX, mouseY);
if (d < this.r) {
this.col = 0;
}
}

If you can reproduce your problem in a codepen or similar sandbox it would be easier for someone to help you solve it.
2 things spring to mind:
You could use element.classlist.toggle() to toggle a class on/off and style that class.
Or you could extend your code like so:
if (d < this.r) {
if (this.col === 0) {
this.col = 1; // I'm guessing this is the correct value
} else {
this.col = 0;
}
}

Related

Is there a way to continuously move an object in P5 via user input?

I'm pretty sure after looking through the documentation that this isn't what the framework is intended to do, but I've got a student I'm tutoring who really wants to move an object drawn on a P5 canvas using input, like using the arrow keys.
What I was able to figure out is the following:
let value = 0;
function setup() {
// Create a canvas with a specified width and height
createCanvas(400, 400);
// Fill in background color
background("blue");
}
function draw() {
background(200);
rectMode(CENTER);
translate(value, value, value);
translate(150, 150, 150)
rect(0, 0, 20, 20);
}
function keyPressed() {
while(true) {
if (keyCode == LEFT_ARROW) {
value = 20;
}
}
}
But of course, this uses an infinite loop and is therefore less than ideal. Does anyone know of a better way to achieve this?
On my system (Chrome, MacOS), keyPressed is called repeatedly if I hold the left-arrow key. Below is a really simple example. Focus into the snippet and hold down your left arrow key - observe that the xPos decreases every frame.
You need to actually do something in your keyPressed method. Right now, the value is just being assigned repeatedly, and definitely, you don't want to have an infinite loop.
For the record, according to the docs, keyPressed isn't guaranteed to work this way on all systems, so you may need more complex logic depending on what system(s) you want to run this on.
function setup() {
createCanvas(640, 360);
}
let xPos = 100;
function draw() {
background(0,0,255);
fill(255,0,0);
ellipse(xPos,50,10,10);
}
function keyPressed(event) {
if (keyCode == LEFT_ARROW) {
xPos -= 1;
} else if (keyCode == RIGHT_ARROW) {
xPos += 1;
}
// this prevents default browser behavior
event.preventDefault();
return false;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.3.3/p5.min.js"></script>
let acl;
let pos;
let value = 0;
function setup() {
acl = createVector(0,0);
pos = createVector(0,0);
// Create a canvas with a specified width and height
createCanvas(400, 400);
// Fill in background color
background("blue");
}
function draw() {
background(200);
rectMode(CENTER);
rect(pos.x, pos.y, 20, 20);
pos.add(acl);
}
function keyPressed() {
if (keyCode == LEFT_ARROW) {
acl.set(-1,0)
}
if(keyCode == RIGHT_ARROW){
acl.set(1,0)
}
}

Pathfinding animation in phaser

So, I'm developing a small game in Phaser for the very first time.
The Idea is to move a character inside a dungeon using pathfinding (with easystar.js).
I used the source-code of this tutorial as a quick-starter:
https://www.dynetisgames.com/2018/03/06/pathfinding-easystar-phaser-3/
Here Pathfinding is functioning well but the character is constantly idling with no animations at all;
So I created this sprite with 4-way walking animation:
The idea was to attaching to each tween, used by easystar to create the path to follow, the correct animation, based on the position of the sprite compared to every tween; like this:
Attaching the animation to every tween:
Game.moveCharacter = function(path){
// Sets up a list of tweens, one for each tile to walk, that will be chained by the timeline
var tweens = [];
for(var i = 0; i < path.length-1; i++){
var ex = path[i+1].x;
var ey = path[i+1].y;
tweens.push({
targets: Game.player,
x: {value: ex*Game.map.tileWidth, duration: 200},
y: {value: ey*Game.map.tileHeight, duration: 200},
onUpdate: animatePath(ex*Game.map.tileWidth, ey*Game.map.tileHeight)
});
}
Game.scene.tweens.timeline({
tweens: tweens,
onComplete: stopAnimation
});
};
Function for choosing the correct animation:
function animatePath(x, y)
{
if(y < playerPosY)
{
playerPosY -= 32;
console.log("up");
Game.player.anims.play('up', true);
}
else if(y > playerPosY)
{
playerPosY += 32;
console.log("down");
Game.player.anims.play('down', true);
}
else if(x < playerPosX)
{
playerPosX -= 32;
console.log("left");
Game.player.anims.play('left', true);
}
else if (x > playerPosX)
{
playerPosX += 32;
console.log("right");
Game.player.anims.play('right', true);
}
}
Where "32" is the dimension of each tile.
Function to stop the animation once the character is arrived:
function stopAnimation()
{
Game.player.anims.stop(null, true);
}
Reading the logs, the path appears to be read correctly.
Problem is: the only animation that shows up is the one of the final tween, as if it overwrites all the others.
How can I force all animations to shows up?
Here's a video of what's going on
https://www.youtube.com/watch?v=z6IUzv4BJG8&ab_channel=FrancescoPeruzzi

Adding and removing to an array

I wasn't sure how to word this, because I don't actually know what exactly causes this bug. I'm trying to put together a simple Asteroids knockoff.
When the player shoots, a new object (Bullet) is created using array.push(...). Once this bullet goes beyond the canvas (out of bounds), it is deleted using array.splice(...);
The problem is that the bullets are moving in unpredictable ways. I don't know how to word it so here's the full code (working, including html/css): https://pastebin.com/tKiSnDzX
Hold spacebar for a few seconds (to shoot) and you'll see the issue clearly. You can also use A/D to turn and W to go forward.
Here's what I think is happening. The code runs fine as long as there is only one bullet on the screen (in the array). This means that either the incorrect element is being deleted or the values that go into the constructor of the object are messed up somewhere along the way.
Exhibit A (bullet constructor and its methods):
function Bullet(x,y,rot,vel) {
this.x = x;
this.y = y;
this.rot = rot;
this.vel = (vel+5);
this.move = function() {
this.x += this.vel*Math.cos(this.rot-Math.PI/2);
this.y += this.vel*Math.sin(this.rot-Math.PI/2);
}
this.draw = function() {
engine.circle(this.x, this.y, 4, "black");
var c = engine.canvas.getContext('2d');
c.translate(this.x, this.y);
c.rotate(this.rot);
c.beginPath();
c.strokeStyle="#00FF00";
c.strokeRect(-5, -5, 10, 10);
c.closePath();
c.stroke();
}
}
Exhibit B (function that creates/deletes the bullets):
shoot: function() {
if(engine.keyDown.sp == true) {
if(this.fire > 20) {
engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
this.fire = 0;
} else {
this.fire++
}
}
for(i = 0; i < engine.bullets.length; i++) {
engine.bullets[i].move();
engine.bullets[i].draw();
if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5
|| engine.bullets[i].y > engine.canvas.height+5 || engine.bullets[i].y < -5) {
console.log('bullet gone, '+i);
engine.bullets.splice(i, 1);
}
}
}
the array is declared like so: bullets: []
Thank you for any answers.
How about just tag any bullets that need to die as you come across them in your loop with something like engine.bullets[i].dead = true; Then, at the end outside the loop, filter out the dead bullets with engine.bullets = engine.bullets.filter(b => !b.dead);
You're using splice inside a for loop. When you remove element index 1, then element index 2 becomes index 1, and element index 3 becomes index 2. But your i variable is already 1, so the next iteration of the loop goes to the new index 2. But the new index 2 is the original index 3, and the original index 2 is skipped. You might be better served by a linked list:
var bulletList = null;
function bullet(){
this.next = bulletList;//if there was a list, record that
if (this.next)//and set that one to point back to this
this.next.prev = this;
bulletList = this;//place new bullet at start of list
this.previous = null;
this.draw = function(){
//do stuff here
this.next && this.next.draw();//call next item in the list, if any
if (shouldIBeRemoved){//placeholder, obviously
if (this.next && this.prev){//remove from middle of list
this.prev.next = this.next;
this.next.prev = this.prev;
}
else if (this.next && !this.prev){//remove from beginning of list
bulletList = this.next;
this.next.prev = null;
}
else if (this.prev && !this.next){//remove from end of list
this.prev.next = null;
}
else if (!this.prev && !this.next){//remove only item in list
bulletList = null;
}
}
}
}
then to draw every bullet, simply call:
if (bulletList)
bulletList.draw();
The problem was that I was translating the context in which all the bullets are, each time a new bullet was created. This meant that each bullet would be moved by x and y away from the previous one. It made it seem like the bullets were being created where they weren't supposed to be. The "unpredictability" was caused by the fact that the bullet takes on the player's rotation, so whenever a new bullet was created, it's rotation was increased by however much the player rotated before the new bullet was fired, on top of the previous bullet's rotation.
Putting context.save(); before the translation/rotation of the bullet's hitbox and context.restore(); after it perfectly solved the issue:
Bullet.draw = function() {
engine.circle(this.x, this.y, 4, "black");
if(hitbox == true) {
var c = engine.canvas.getContext('2d');
c.save();
c.translate(this.x, this.y);
//c.rotate(this.rot);
c.beginPath();
c.strokeStyle="#00FF00";
c.strokeRect(-5, -5, 10, 10);
c.closePath();
c.stroke();
c.restore();
}
}
Someone else mentioned that I was using array.splice(); in a for loop. This made it so that when a bullet (i) is deleted, the bullet right after (i+1) is moved one index back (i). So that bullet was essentially skipped over.
I could notice this sometimes when looking at the bullets while one was deleted- They "jumped" ahead.
The solution was to put i -= 1 after bullets.splice(i, 1);. This makes the next iteration of the loop go one index back, solving the occassional stuttering of the bullets:
shoot: function() {
if(engine.keyDown.sp == true) {
if(this.fire > 5) {
engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
this.fire = 0;
}
}
if(this.fire<=5) {
this.fire++
}
for(i = 0; i < engine.bullets.length; i++) {
engine.bullets[i].move();
engine.bullets[i].draw();
if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5
|| engine.bullets[i].y > engine.canvas.height+5 || engine.bullets[i].y < -5) {
engine.bullets.splice(i, 1);
i-=1;
}
}
}
Thanks for all the answers.

problems with translating a p5js sketch into processing

I translate a p5js scetch to processing because I want to generate a mov file. The scetch is based on a tutorial from daniel shiffman on kadenze but I´ve got a problem now with a boolean output. How can I code this piece of p5js code in processing?
function draw() {
for( var i = 0; i < particles.length; i++) {
if(particles[i].isDead()) {
//code
}
}
}
function Particles() {
this.isDead = function() {
var distance = p5.Vector.sub(attractor.pos, this.pos);
var d = distance.mag();
if(d < 5) {
return true;
} else {
return false;
}
}
}
First I tried it with void, but void hasn´t got an output. Then I tried something like this with boolean but it also doesn´t work.
void setup() {
//code
}
void draw () {
for (int i = 0; i < particles.length; i++) {
if(particles[i].isDead()) {
//code
}
}
}
Class Particle {
Particle() {
//code
}
boolean isDead() {
PVector distance = PVector.sub(a.location, location);
float d = distance.mag();
if(d < 5) {
return true;
} else {
return false;
}
}
}
It will be great if somebody can help me.
regards mattias
The first problem is that you aren't ever actually doing anything if isDead() returns true. You need to actually put code inside that if statement, or maybe move that if statement to somewhere that makes logical sense.
For example, you could modify your display() function inside your Mover class to draw in green when the Mover is dead:
void display() {
if (isDead()) {
fill(0, 255, 0);
} else {
fill(255, 0, 0);
}
stroke(0);
strokeWeight(2);
ellipse(location.x, location.y, 10, 10);
}
That's just an example, and what you actually do depends on what you want to happen when a Mover is dead.
But even if you make that change, you'll notice that a Mover only dies when it reaches the middle of the gray circle. That's because of this if statement inside your isDead() function:
PVector distance = PVector.sub(a.location, location);
float d = distance.mag();
if (d < 5) {
return true;
} else {
return false;
}
You're measuring the distance between the center of the gray circle and the center of each little red circle. But you're only returning true if the d < 5. The problem with that is the gray circle has a diameter of 50, so if you want the little red circles to die when they enter the gray circle, you have to factor that diameter into your calculations. Try using if(d < 30) instead. I got 30 by dividing the diameter 50 by 2 and then adding 5 for the small circle size. You might have to play with it to get the effect you're looking for.
By the way, you might be interested in the dist() function, which returns the distance between two points. More info can be found in the reference.

Getting 'Uncaught type error' in a p5.js sketch that I can't figure out

I'm working with a very simple example of a p5.js that was part of the supplemental material for Learning Processing. They provide .js versions of all of the examples from the book, and my data viz project will go on the web. What I want to do is use this simple example to act as a template for when I create my actual data viz. I want to get the basic animation working first before I add a bunch of other code.
Here is the code I'm working with:
var message = "random characters flying home!";
// An array of Letter objects
var letters;
function setup() {
createCanvas(400, 200);
// Load the font
textFont("Georgia", 20);
// Create the array the same size as the String
letters = [];
// Initialize Letters at the correct x location
var x = 50;
var y = height/2;
for (var i = 0; i < message.length; i++) {
letters[i] = new Letter(x, y, message.charAt(i));
x += textWidth(message.charAt(i));
}
}
function draw() {
background(255);
for (var i = 0; i < letters.length; i++) {
// Display all letters randomly
letters[i].random();
}
// If the mouse is pressed the letters return to their original location
if (mouseIsPressed) {
letters[i].display();
}
}
function Letter(x, y, letter) {
// The object knows its original " home " location
// As well as its current location
this.homex = this.x = x;
this.homey = this.y = y;
this.letter = letter;
this.theta = 0;
// Bring the letters back to their original position
this.display = function() {
fill(0);
textAlign(LEFT);
this.x = this.homex;
this.y = this.homey;
text(this.letter, this.x, this.y);
}
// Position the letters randomly
this.random = function() {
this.x += random(width);
this.y += random(height);
this.theta += random(-0.1, 0.1);
}
// no longer using this function, but it was part of the original 'if' statement
// At any point, the current location can be set back to the home location by calling the home() function.
//this.home = function() {
//this.x += lerp(this.x, this.homex, 0.05);
//this.y += lerp(this.y, this.homey, 0.05);
//this.theta += lerp(this.theta, 0, 0.05);
//text(this.letter);
}
};
ISSUE 1: What it's supposed to do is initially display individual letters on the canvas. And it does that. But I also get the following error in my console:
sketch.js:31 Uncaught TypeError: Cannot read property 'home' of undefined
sketch.js:31 is the line at the end of the 'if' statement under draw(). My question is what is 'home' referring to and how can I fix it.
ISSUE 2: What's supposed to happen when mouseIsPressed is the letters move into their correct configuration, i.e., "random characters flying home!" But nothing happens when I press down on the mouse.
Your code does not produce the errors you mentioned.
Instead, running your code produces an unexpected token: } error because you have an extra } at the end of your code. Get rid of it.
At that point you have a different error: sketch.js:29 Uncaught TypeError: Cannot read property 'display' of undefined. Looking at your draw() function, which includes line 29, we see this:
function draw() {
background(255);
for (var i = 0; i < letters.length; i++) {
// Display all letters randomly
letters[i].random();
}
// If the mouse is pressed the letters return to their original location
if (mouseIsPressed) {
letters[i].display();
}
}
Notice that your if(mousePressed) statement is after the for loop. What do you expect the value of i to be when you get to the if statement? Since it's outside the loop, i is out of scope, so its value is undefined! That's why you're getting this error.
To fix it, you need to rearrange your if statements so that they happen inside the loop:
function draw() {
background(255);
for (var i = 0; i < letters.length; i++) {
if (mouseIsPressed) {
// If the mouse is pressed the letters return to their original location
letters[i].display();
} else {
// Display all letters randomly
letters[i].random();
}
}
}
This gets rid of your errors, but your random logic is still off. You're just adding random values to your letters, so they fly off the screen and you can't see them.
Instead, add smaller values to their positions, and make sure you reset their positions when the user clicks.

Categories