Constant not staying constant? - javascript

I made a code with the intention of having a square appear where the mouse is pressed down, stay in that spot despite mouse movement and not disappear when the mouse it released.
THIS IS P5.JS ! https://p5js.org/reference/
Instead, the square follows the mouse until it is released then it disappears!
I believe that my code keeps declaring a new constant and deleting the old one every time the shoot() function is run.
var clocker = 0;// NOT YET USED
var player = {
x:400,
y:400,
};
function shoot(x1, y1, x2, y2, speed, range, power, playerDirection){
var bulletAlive = true;
var bulletDamage = power;
const startX = x1;
const startY = y1;
const destX = x2;
const destY = y2;
var bulletX = startX;
var bulletY = startY;
if(bulletAlive){
fill(0,100,200);
rect(destX-12.5,destY-12.5,25,25);
};
};
function setup() {
createCanvas(1000,650);
}
function draw() {
background(204,204,204);
if(mouseIsPressed){
shoot(player.x,player.y,mouseX,mouseY,2,100,0,"right");
}
}
Perhaps I am using const wrong. If so how should I use it? How can I make it so that destX and destY don't change? (Don't follow mouse or disappear)
PS: sorry for the miscellaneous information, this is supposed to build up to simple bullet physics.

It sounds like there is some confusion about scoping, and there is probably a better way to think about this problem.
First let's look at what is going wrong, and talk through a few details to explain why.
Just like variables (let, var), constants are always declared in a specific scope.
Scopes are like containers for constants and variables. Scopes are private, that is they cannot be accessed from the outside. Scopes can be created and destroyed.
When you declare a constant directly inside a function, the scope is the function itself (like startX inside shoot). (Note that if you declare a constant inside an if statement or other block, the scope is the block. That's not the case here, though.)
Function scopes are created each time the function is called, and destroyed when the function is finished executing.
Each time a function is called and its scope is created, all constants (and variables) are reinitialized with new values.
A constant appearing in your code may have different values during different function calls. It is only constant during its lifetime, which in your case is a single given execution of the function.
This is why your constants aren't staying constant. You are calling shoot() repeatedly while the mouse is down, and so the constants are repeatedly being recreated and assigned new values.
With this information, hopefully you can see the problems with the current approach. As for a solution, let's think about what exactly is happening. shoot() is an action that should be triggered when the user issues a "shoot" command, such as a mouse click. The draw() function is a continuous event triggered to say "hey, update the screen". Putting the shoot action inside the draw event is kind of a mis-match of intentions and is the root of struggles like this.
Instead, let's introduce the idea of a bullet object. A bullet has an x and a y value. A bullet is created when the user shoots, and is given a specific x and y value at the moment of creation. None of this happens inside draw, it happens in another event listener such as "click".
The job of draw is to check to see if there is an active bullet, and if there is one, draw it at the specified x and y coordinate. If there is no bullet, do nothing. (Of course you might need to draw other things as well, but that's unrelated to drawing the bullet).
Keeping object creation and object drawing separate makes it easier to have the kind of control you're looking for.
Edit: Adding some code examples
Here's what the code would look like to do exactly what you asked, using the bullet object idea above. The inline comments should explain each step.
// This is where we'll store an active bullet object.
// The `shoot()` function is responsible for setting this.
// `draw()` is responsible for rendering the bullet.
// Initially we'll set the value to `null` to explicitly
// indicate that there is no bullet.
let activeBullet = null;
// The purpose of `shoot()` is to create a bullet
// and make it available to be rendered.
function shoot(x, y) {
// Create the bullet object.
const newBullet = {
x: x,
y: y,
size: 25
};
// Set the active bullet to the new bullet. This will
// cause any previously active bullet to disappear.
activeBullet = newBullet;
}
// P5 functions
// ------------
function setup() {
createCanvas(1000, 650);
}
// Shoot when the player clicks.
function mousePressed() {
shoot(mouseX, mouseY);
}
function draw() {
// Always start with a blank canvas.
clear();
// If there is an active bullet, draw it!
// (`null` is "falsy", objects are "truthy", so the
// `if` statement will only run after the `activeBullet`
// variable is assigned a bullet object.)
if (activeBullet) {
fill(0, 100, 200);
rect(
activeBullet.x - activeBullet.size / 2,
activeBullet.y - activeBullet.size / 2,
activeBullet.size,
activeBullet.size
);
}
}
You also mentioned you wanted to build up to simple bullet physics. Just to show how the bullet object idea works nicely, here's a demo where you can click to shoot multiple bullets, they all move independently, and collide with a wall at which point they are removed. There's a lot more involved in building games, but hopefully it's an inspiring starting point :)
// Store canvas dimensions globally so we have easy access.
const canvasWidth = 1000;
const canvasHeight = 650;
// We'll add a "wall" object so we have something the bullets can
// collide with. This value is the X position of the wall.
const wallX = canvasWidth - 200;
// Instead of a single bullet, using an array can accommodate
// multiple bullets. It's empty to start, which means no bullets.
// We can also use `const` for this, because we won't ever assign
// a new value, we'll only modify the contents of the array.
const activeBullets = [];
function shoot(x, y) {
// Create the bullet object.
const newBullet = {
x: x,
y: y,
size: 25,
speed: 4
};
// Instead of overwriting a single bullet variable, we'll push
// the new bullet onto an array of bullets so multiple can exist.
activeBullets.push(newBullet);
}
// P5 functions
// ------------
function setup() {
createCanvas(canvasWidth, canvasHeight);
}
// Shoot when the player clicks.
function mousePressed() {
shoot(mouseX, mouseY);
}
function draw() {
// Always start with a blank canvas.
clear();
// Draw our "wall".
fill(50);
rect(wallX, 0, 60, canvasHeight);
// Set the fill color once, to use for all bullets. This doesn't
// need to be set for each bullet.
fill(0, 100, 200);
// Loop through the array of bullets and draw each one, while also
// checking for collisions with the wall so we can remove them. By
// looping backwards, we can safely remove bullets from the array
// without changing the index of the next bullet in line.
for (let i=activeBullets.length-1; i>=0; i--) {
// Grab the current bullet we're operating on.
const bullet = activeBullets[i];
// Move the bullet horizontally.
bullet.x += bullet.speed;
// Check if the bullet has visually gone past the wall. This
// means a collision.
if (bullet.x + bullet.size / 2 > wallX) {
// If the bullet has collided, remove it and don't draw it.
activeBullets.splice(i, 1);
} else {
// If the bullet hasn't collided, draw it.
rect(
bullet.x - bullet.size / 2,
bullet.y - bullet.size / 2,
bullet.size,
bullet.size
);
}
}
}

The const declaration exists only within the scope of shoot. So once the shoot function is finished executing, startX startY destX destY, being const, are deleted.
Possible fix:
var didShootAlready = false;
var startX, startY, destX, destY;
function shoot(/*params*/){
if(!didShootAlready){
didShootAlready = true;
startX = x1;
startY = y1;
destX = x2;
destY = y2;
}
//do the rest
}

Related

How to make a hole in a polygon with CreateJs?

As you can see on attached image it has the rhombus with the ellipse inside which is almost transparent.
But this is just an image.
How can I create this with createjs?
A more detailed description of the problem:
What you see in the picture is not exactly what I need.
Ideally, my task is to make two triangles out of this rhombus with an ellipse inside.
The ellipse should create some kind of transparency in the triangle so that all the elements that will be under this triangle are visible through.
My implementation:
I make a triangle according to this example:
(thank to this fiddle)
(createjs.Graphics.Polygon = function(x, y, points) {
this.x = x;
this.y = y;
this.points = points;
}).prototype.exec = function(ctx) {
// Start at the end to simplify loop
var end = this.points[this.points.length - 1];
ctx.moveTo(end.x, end.y);
this.points.forEach(function(point) {
ctx.lineTo(point.x, point.y);
});
};
createjs.Graphics.prototype.drawPolygon = function(x, y, args) {
var points = [];
if (Array.isArray(args)) {
args.forEach(function(point) {
point = Array.isArray(point) ? {x:point[0], y:point[1]} : point;
points.push(point);
});
} else {
args = Array.prototype.slice.call(arguments).slice(2);
var px = null;
args.forEach(function(val) {
if (px === null) {
px = val;
} else {
points.push({x: px, y: val});
px = null;
}
});
}
return this.append(new createjs.Graphics.Polygon(x, y, points));
};
stage = new createjs.Stage("demoCanvas");
poly1 = new createjs.Shape();
poly1.graphics.beginFill("Red").drawPolygon(0,0,10,10,10,40,40,30,60,5,30,0);
poly1.x = 10;
poly1.y = 10;
stage.addChild(poly1);
stage.update();
(if there is a more convenient or even correct way to make a triangle and this will help in solving my problem, I will gladly accept your solution).
Next, I simply overlay the ellipse drawn with drawEllipse on top of this triangle.
I understand that I may be doing something wrong, and that is why I am here.
Any help will be accepted!
I assume you are using the Graphics API to draw your content. If so, you simply need to ensure the "hole" draws with reverse winding. This just means the shape needs to be drawn in the reverse direction.
For example, the Canvas2D rect method draws clockwise, so to subtract from them, the instructions need to be drawn in the other direction.
var s = new createjs.Shape();
s.graphics.beginFill("red")
.drawRect(0,0,300,300) // Draw a square
// Then draw a smaller square
.moveTo(100,100) // Top left
.lineTo(100,200) // Bottom left
.lineTo(200,200) // Bottom right
.lineTo(200,100) // Top right
.lineTo(100,100) // Top left
.closePath(); // Optional if you are done
The drawEllipse has an anticlockwise parameter which does the trick as well. Here is a jsfiddle sample, which actually draws it the other way (small cut-out first), but with the same result.
UPDATE
In order for the shape to "cut out" the other one, it has to be part of the same graphics instance, and part of the same path instructions. If you closePath() after any drawing instructions, any new instructions are drawn on top of that without cutting it out. Using separate shape instances does this automatically.
Using the updated code, I added a simple drawEllipse() call using default clockwise winding, and it cut out the circle: https://jsfiddle.net/lannymcnie/yd25h8se/ -- Note that I scaled up the coordinates from above x10 to make it more visible.
Cheers,

Full control of many objects Javascript

I am working on a game in javascript that spawns and terminates shapes. Therefore, I need full control over these shapes that are being spawned. I want to do the following, but I am not sure on how to do it:
I want to define a constructor Shape.
Then, I want to make a function so that when it is called, it applies to ALL of the Shape in existence and being able to change each ones attribute. (For example, I call function moveAll() and all of the shapes' this.y increments by one.)
I also want to be able to terminate a specific Shape with a function, or add a Shape with a function.
My main problem is this:
var x = new Shape();
var y = new Shape();
...
I don't want to make a million variables. I need a way to make tons of shapes at once and still be able to control each one individually. And if I made an array, how would I control everything individually?
Anything helps, I just need a basis to understand the concept of constructors. Thanks in advance!
You could make an array of shapes:
let shapes = []
// this will create 6 shapes
for(let i of [0,1,2,3,4,5]) {
shapes.push(new Shape())
}
If a shape has an id you can terminate it:
// incrementing id counter
let id = 0
// x and y are optional starting positions
function Shape(x, y) {
this.id = id++
this.y = y || 0
this.x = x || 0
}
// remove the shape from the array
function terminate(id) {
shapes = shapes.filter(shape => shape.id !== id)
}
// or add a shape
function spawn() {
shapes.push(new Shape())
}
Now you have your shapes.
The simplest way to move all shapes would be something like this:
function moveAll() {
shapes.forEach(shape => shape.y++)
}
Runtime for this will increase as the number of shapes increase.
updated moveAll, per request
function moveAll() {
shapes.forEach(shape => {
if(shape.type === true) {
shape.y++
} else {
shape.y--
}
})
}

Drag canvas images/pieces on a chess board

I am trying to build a chess game using Javascript(ES 6) and Canvas.
I have built a basic chessboard and also rendered pawns on top of it.
Now I intend to either click on the pawn or drag it to make a move.
document.addEventListener('click',board_click)
or
canvas.addEventListener('click', board_click, false);
is the way I manage to listen to these events.
Now how would I know where I have clicked? I figured I can try to get the current position of the click, but how will I know what item is there at the present location.
Any libraries or already implemented logics will also be helpful
I have rendered my pawns like this
const drawPawns = (white, black, ctx, boardDimension, allPieces) => {
const rows = [0, 1, 2, 3, 4, 5, 6, 7];
const cols = [1, 6];
let pawn;
let side;
// boardDimension = 90;
cols.forEach((col) => {
rows.forEach((row) => {
// pawn = Pawn.call(this, 'black');
side = col === 1 ? 'black' : 'white';
// That little tinkering is to center align the Pawn in the tile
pawn = new Pawn(side, row * boardDimension + 30, col * boardDimension + 5, true);
spriteImage.draw(ctx, pawn.canvasPosition, pawn.x, pawn.y);
allPieces.push(pawn);
});
});
};
The pawn.canvasPostion is to draw the image(using Sprite), whereas x and y are the places where its coordinates are. Now how would I get this particular Coor ordinates?
The listener callback (your board_click function) gets the browser event as an argument. This event object contains a bunch of data, including the clicked element and the coordinates of the click.
Since you are using canvas, you should attach the click listener on the canvas element. Canvas does not have a state graph, which means it has no concept of objects in the canvas - it is just a bunch of pixels. This means it is your responsibility to keep track of which piece is where.
Since you are already placing the pieces on the board, you know exactly where they are!
The normal way to do this is to get the click position, map that to a grid cell on the board, and then check whether anything was on that cell. How exactly this will happen depends on the data structure that describes your game.
For illustrative purposes let's assume that the chess pieces reside in an array, and each piece knows its cell coordinates (for chess it probably makes sense to do the opposite - each cell would know whether it has a piece on it, but bear with me).
var cellSize = 16; // let's assume a cell on your board is 16x16 pixels
var pieces = []; // this keeps all chess pieces
function board_click(evt) {
// get cell coordinates
var cellX = Math.floor(evt.clientX/cellSize);
var cellY = Math.floor(evt.clientY/cellSize);
var piece = pieces.find((p) => {
return p.x == cellX && p.y == cellY;
});
if (piece) {
// a piece was clicked
} else {
// an empty cell was clicked
}
}
I hope this is enough to get you started.
Edit: had forgotten to divide the mouse coordinates by the cell size...

Have particles appear OnClick function in p5.js

New to p5.js and trying to learn more every day. Basically, I am currently learning about particle systems and objects, and getting confused by the amount of code. Anyways, I'd like that on function mousePressed(), an array of particles (particle system) that I've created appears. It'd also be awesome if the particle system could trail the position of the mouse. So, basically, if you click your mouse on the screen particles appear in that position, and also trail your mouse.
I can't figure out what I'm missing in the code. I feel kind of lost about what half of it is even doing (my professor wrote a lot of it). When I add in the mousePressed function, everything goes to pot. I feel like I'm too overwhelmed to even know what's missing. Any help, plus detailed insight into what I need to do and why that solution works would be super appreciated. Thank you!
var particles = [];
var now = null;
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(255, 25, 25);
function mousePressed() {
particles.push(new Particle(new p5.Vector(mouseX, mouseY)));
//particles.push(new Particle(new p5.Vector(width / 2, height / 1.5)));
for (var i = 0; i < particles.length; i++) {
// if our particle is dead, remove it
if (particles[i].lifespan <= 0) {
//splice is a way of removing a specific
//element from an array
particles.splice(i, 2);
} else {
particles[i].update();
particles[i].display();
}
//this.particle = new ParticleSystem(createVector(mouseX, mouseY));
// patricles.push(p);
}
}
}
function Particle(loc) {
this.loc = loc;
this.acc = new p5.Vector();
this.vel = new p5.Vector(random(-100, 100), random(-2, 0));
this.lifespan = 555;
}
Particle.prototype = {
constructor: Particle,
update: function() {
this.vel.add(this.acc);
this.loc.add(this.vel);
this.lifespan -= 4.0;
},
display: function() {
stroke(random(0), this.lifespan);
fill(random(255), random(255), random(255))
ellipse(this.loc.x, this.loc.y, 20, 20);
}
}
First of all, your mousePressed() function is inside your draw() function. That doesn't make a ton of sense. You want your mousePressed() function to be at the same level as your draw() function.
function draw(){
//draw code here
}
function mousePressed(){
//mousePressed code here
}
If I were you, I would start smaller. Can you create a program that draws a single ellipse? Can you then make it so that single ellipse appears when you click the mouse? Then can you have that ellipse follow the mouse? Only if you can get that working perfectly by itself, then you should start thinking about adding multiple ellipses.
You're trying to go from your end goal and work backwards, which is just going to confuse you. Instead, start from the simplest sketch possible and take one small step at a time. Then if you get stuck you can post an MCVE along with a specific question, and it'll be easier to help you.

resetting canvas strokes each re-iteration through the function(animation). how?

At the moment the lines stick permanently creating a drawing. I want the line to be drawm and reset everytime the function is called via setInterval() therefore creating a brain-wave ripple like animation.
Here's my current source code:
var rippleEffect = function(){
var co = [];
for(var i=0; i<=5; i++){
var r = Math.floor(Math.random()*250);
co.push(r);
//each time through the loop throws/pushes 6 random numbers to array 'co' for bezier curve.
}
var canvas = document.getElementById('brainWave');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(500, 75);
context.bezierCurveTo(co[0], co[1], co[2], co[3], co[4], co[5]);
context.lineWidth = 2;
// line color
context.strokeStyle = '#444';
context.stroke(); //exicute the stroke based on the structions we've provided.
};
setInterval(ripple,100); //this re-calls the functions every few miller-seconds.
is .clearRect() some part of the solution?
I'm not quite sure what you're trying to do, but here are some observations on your code:
clearRect is essential if you don't want the drawings to accumulate on top of each other.
setInterval is calling "ripple", but it should be calling "rippleEffect".
var canvas and var context should be pulled outside-before rippleEffect (they just need to be created once, not with every loop.
Totally random control points for a Bezier curve yields spaghetti, not brain-waves.
Good luck with your project :)

Categories