How to remove a 'bullet ' sprite after it has reached mouseX and mouseY? [P5.Play] - p5.js

I'm making a game using P5.Play, where I have 3 obelisks; one on the left, one on the right and one in the middle. I have made it so if my mouse position is within a specific area of the canvas, the obelisk nearest will shoot towards my mouse position. However I am struggling to find a way to get the sprite to explode once it has reached the x and y point at which when I clicked the mouse. I won't post my full code because it's for an assignment but here is a snippet.
function mousePressed() {
if (mouseX < width / 3) {
bullet = createSprite(obelisks[0].position.x, obelisks[0].position.y - 60, 20, 20)
} else if (mouseX > width / 3 && mouseX < width - width / 3) {
bullet = createSprite(obelisks[1].position.x, obelisks[1].position.y - 60, 20, 20)
} else {
bullet = createSprite(obelisks[2].position.x, obelisks[2].position.y - 60, 20, 20)
}
// if bullet position is less than the distance between firing point and cursor position, then remove bullet??
bullet.addImage(bulletSprite);
bullet.attractionPoint(10, mouseX, mouseY);
bullet.rotateToDirection = true;
}

First of all, thank you for introducing me to p5.play!
There's a couple of things to think about here, but you're very close. From reading the documentation there's a function on the sprite object called overlapPoint(pointX, pointY) which:
Checks if the given point is inside the sprite's collider.
This returns a boolean. We can leverage this to determine if our bullet has reached its destination.
First of all, let's define the destinationX and destinationY properties on our object:
function mousePressed() {
...
bullet = createSprite(width/2, height, 10, 10);
let destinationX = mouseX;
let destinationY = mouseY;
bullet.attractionPoint(10, destinationX, destinationY);
bullet.destinationX = destinationX;
bullet.destinationY = destinationY;
}
Now, we can use these properties to determine if we've hit the overlap point:
function draw() {
background(220);
drawSprite(obelisk);
if (bullet) {
drawSprite(bullet);
if (bullet.overlapPoint(bullet.destinationX, bullet.destinationY)) {
bullet.remove();
}
}
}
Here's a working, simplified example:
let obelisk;
let bullet;
function setup() {
createCanvas(400, 400);
obelisk = createSprite(width/2, height, 50, 200);
}
function draw() {
background(220);
drawSprite(obelisk);
if (bullet) {
drawSprite(bullet);
if (bullet.overlapPoint(bullet.destinationX, bullet.destinationY)) {
bullet.remove();
}
}
}
function mousePressed() {
bullet = createSprite(width/2, height, 10, 10);
let destinationX = mouseX;
let destinationY = mouseY;
bullet.attractionPoint(10, destinationX, destinationY);
bullet.destinationX = destinationX;
bullet.destinationY = destinationY;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/molleindustria/p5.play/lib/p5.play.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
And here's a link to a p5.js sketch I created, to help you further.
Hopefully, this points you in the right direction. I might also recommend that you create an array of bullets, such: let bullets = [] and add to this array when you shoot, this would prevent the deletion of a bullet when shooting in rapid succession. Or you could maybe even look into p5.play's grouping object.

Related

Creating a mask layer in p5js

I would like to draw a pattern, and then only have that pattern ‘show through’ where it overlaps with a shape that I specify. Similar to how a mask layer works in Photoshop. Does anyone know how I can approach this?
I would use this 4 step process:
create your mask with beginShape()/endShape and a beginContour()/endContour() in the middle for the area to be shown. You do this on a buffer.
function setup() {
createCanvas(400, 400);
background(30)
fill(200,50,60)
msk = createGraphics(width,height)
pattern = createGraphics(width,height)
pixelDensity(1)
msk.beginShape();
// Exterior part of shape, clockwise winding
msk.vertex(0, 0);
msk.vertex(400, 0);
msk.vertex(400, 400);
msk.vertex(0, 400);
// Interior part of shape, counter-clockwise winding
msk.beginContour();
msk.vertex(20, 20);
msk.vertex(50, 220);
msk.vertex(120, 380);
msk.vertex(370, 320);
msk.vertex(240, 160);
msk.vertex(350, 40);
msk.endContour();
msk.endShape(CLOSE);
mPixs = msk.loadPixels()
}
Then create a different buffer for the pattern
for (let c=0; c<9; c++) {
for (let r=0; r<9; r++) {
pattern.circle(width/8*c,height/8*r,40)
}
}
Now load pixels from the mask and use the alpha value of each pixel on the alpha level of each corresponding pixel on the pattern.
pattern.loadPixels()
msk.loadPixels()
for (let i=0; i < pattern.pixels.length; i+=4){
pattern.pixels[i+3] = msk.pixels[i+3]
}
pattern.updatePixels()
Finally, just add the resulting buffer to your main canvas with image(pattern,0,0)
Take a look at this working example carefully coded just for you! :D
let msk, pattern, mPixs
function setup() {
createCanvas(400, 400);
background(30)
fill(200,50,60)
msk = createGraphics(width,height)
pattern = createGraphics(width,height)
pixelDensity(1)
msk.beginShape();
// Exterior part of shape, clockwise winding
msk.vertex(0, 0);
msk.vertex(400, 0);
msk.vertex(400, 400);
msk.vertex(0, 400);
// Interior part of shape, counter-clockwise winding
msk.beginContour();
msk.vertex(20, 20);
msk.vertex(50, 220);
msk.vertex(120, 380);
msk.vertex(370, 320);
msk.vertex(240, 160);
msk.vertex(350, 40);
msk.endContour();
msk.endShape(CLOSE);
mPixs = msk.loadPixels()
}
function draw() {
for (let c=0; c<9; c++) {
for (let r=0; r<9; r++) {
pattern.circle(width/8*c,height/8*r,40)
}
}
for (let c=0; c<9; c++) {
for (let r=0; r<9; r++) {
let xo=random(-5,5), yo=random(-5,5)
circle(width/8*c+xo,height/8*r+yo,50)
}
}
pattern.loadPixels()
msk.loadPixels()
for (let i=0; i < pattern.pixels.length; i+=4){
pattern.pixels[i+3] = msk.pixels[i+3]
}
pattern.updatePixels()
image(pattern,0,0)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="sketch.js"></script>
</body>
</html>

how can i make all sprites move to different attraction points using p5.js /p5.play

I am creating a game and have sprites falling from the top of the canvas and destroying towers at the bottom of the canvas. I am trying to use attractionPoint on all the different towers so the sprites are moving towards the towers. I have currently gotten one sprite to move to a tower using attactionPoint but the remaining sprites continue to move in a straight path. Is there a way I am able to set multiple attraction points so the sprites are moving toward all towers?
see code below:
function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
asteroid = createSprite(random(0, 1000), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);
asteroidArray.push(asteroid);
}
}
function drawAsteroid() {
// draws asteroids moving down and checking collision
rock.overlap(turret, explode);
for (let i = 0; i < asteroidArray.length; i++) {
if (asteroidArray[i].position.y > height) {
asteroidArray[i].position.y = 0;
asteroidArray[i].position.x = random(0, 800);
}
asteroidArray[i].addSpeed(3, 90);
asteroid.attractionPoint(1, 100, 740);
asteroid.attractionPoint(1, 400, 740);
asteroid.attractionPoint(1, 700, 740);
}
}
function explode(sprite, obstical) {
//code for explosion and collision
sprite.remove();
obstical.remove();
}
You're very close but there's a couple things that aren't quite correct. In drawAsteroid you're setting the attractionPoint on only the lastly created asteroid, and doing it 3 times and calling it - i assume - in the draw method so it would get updated each frame. We only really want the drawAsteroid function for its respawning logic, so let's rename this method to updateAsteroid rather than drawAsteroid because we're going to call drawSprites(rock); in the draw method (rock is the group you've created for your asteroids).
When we set the attractionPoint we only want to do that once and that's when we create the asteroid:
function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
let asteroid = createSprite(random(0, width), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);
let tower = floor(random(3)) // i know there's only 3 asteroids, you could do something better here
asteroid.attractionPoint(asteroid.maxSpeed, towers[tower].position.x, towers[tower].position.y);
}
}
So we're just randomly targeting a tower from the group.
I've set up a small example that should point you in the right direction.
// Find my blog at https://codeheir.com/
// I do a lot of p5.js stuff that might interest you!
let numAsteroids = 10;
let towers;
let rock;
function setup() {
createCanvas(400, 400);
rock = Group();
towers = Group();
createTowers();
createAsteroids();
}
function draw() {
background(220);
drawSprites(rock);
drawSprites(towers);
updateAsteroids();
}
function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
let asteroid = createSprite(random(0, width), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);
let tower = floor(random(3))
asteroid.attractionPoint(asteroid.maxSpeed, towers[tower].position.x, towers[tower].position.y);
}
}
function updateAsteroids() {
// draws asteroids moving down and checking collision
//rock.overlap(turret, explode);
for (let i = 0; i < rock.length; i++) {
if (rock[i].position.y > height) {
rock[i].position.y = 0;
rock[i].position.x = random(0, width);
}
}
}
function createTowers() {
towers.push(createSprite(40, height - 30, 40, 90));
towers.push(createSprite(width/2, height - 30, 40, 90));
towers.push(createSprite(width-40, height - 30, 40, 90));
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/molleindustria/p5.play/lib/p5.play.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
I've also created a p5.js sketch to point you in the right direction.

How to have scalable image with viewport in p5js?

I'm making a website for my design studio and I would like to set image sizes with the p5js language.
Here, I made a function that allows each mouse click to display an image. The problem is that once the image is displayed, it appears at the indicated dimensions, adapt to the screen size but the ratio is not good. The image is either too wide or too extended.
so what do you need to do to display an image with the right original width/height ratio
All your feedback is appreciated.
Thanks in advance !
let works = []
function preload() {
for (let i = 0; i < 6; i++) {
works[i] = loadImage("img/work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++
image(works[counter%6], mouseX, mouseY, windowWidth/2, windowHeight/2);
}
The image function can take 3, 5, 7, or 9 parameters, and in each case it has different behavior:
image(img, x, y) - Draw the entire image with it's original size at the specified location
image(img, x, y, width, height) - Draw the entire image, scaled to the specified width and height without preserving aspect ratio, at the specified position.
image(img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) - Draw a specific part of the image, scaled to the specified width and height without preserving aspect ratio, at the specified position.
The function of the dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight parameters is explained by this diagram:
In order to draw an image scaled relative to the window size constrained to the aspect ratio of the window, you will need to determine how best to crop your images.
let works = []
function preload() {
works[0] = loadImage("https://www.paulwheeler.us/files/no_idea_why.jpg");
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++
let img = works[counter % works.length];
let windowAspect = width / height;
let imageAspect = img.width / img.height;
let w, h;
// This code naively crops the bottom or right edge of the image as necessary. Obviously there are other ways to limit the image size.
if (windowAspect >= imageAspect) {
// Our window is wider than our image, we need to constrain the height of the image
w = img.width;
h = w / windowAspect;
} else {
// Our window is narrower than or image, we need to constrain the width of the image
h = img.height;
w = h * windowAspect;
}
image(img, mouseX, mouseY, windowWidth / 2, windowHeight / 2, 0, 0, w, h);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
</body>
</html>

The createSlider() function creates the slider outside of the canvas. How to position the slider inside the canvas?

I'm trying to implement a slider in a canvas using p5.js. If there are no HTML elements in the body of the webpage, then the canvas and the slider are on the top-left corner of the webpage.
However, when I change the position of the canvas, the slider does not stay inside the canvas. The position method sets the position of the slider ABSOLUTE with respect to the webpage.
How do I set the position of the slider inside the canvas?
This is the bare-minimum code to replicate the issue.
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>p5.js example</title>
<style> body {padding: 0; margin: 0;} </style>
<script src="../p5.min.js"></script>
<script src="../addons/p5.dom.min.js"></script>
<script src="../addons/p5.sound.min.js"></script>
<script src="sketch.js"></script>
</head>
<body>
<div>
<p><object width="150" height="150" vspace="100"></object>
This is some text. This is some text. This is some text.</p>
</div>
</body>
</html>
The sketch.js file is:
var slider;
function setup() {
slider = createSlider(0, 255, 100);
slider.position(10, 10);
slider.style('width', '80px');
}
function draw() {
var val = slider.value();
background(val);
}
p5.js' createSlider() function creates an <input /> range HTML element which is different from the <canvas/> element it uses to render the other methods.
You could use absolute positioning in tandem with z-index to have the canvas with a lower z-index and the slider with a higher z-index (on top of the canvas), both with the absolute positions you need.
If you really do want the slider rendered inside the canvas element you could roll your own slider class using the p5.js drawing functions and mouse/touch events, but it wouldn't be as CPU efficient as using an <input /> range HTML element.
Here is the Processing > Examples > Topics > GUI > Scrollbar example ported to p5.js:
/**
* Scrollbar.
*
* Move the scrollbars left and right to change the positions of the images.
*/
let slider;
function setup() {
createCanvas(640, 360);
noStroke();
slider = new HScrollbar(0, height/2-8, width, 16, 16);
}
function draw() {
background(255);
fill(0);
text("slider position: " + slider.getPos().toFixed(2),width/2,height/3);
slider.update();
slider.display();
}
class HScrollbar {
constructor(xp, yp, sw, sh, l) {
this.swidth = sw;
this.sheight = sh;
let widthtoheight = sw - sh;
this.ratio = sw / widthtoheight;
this.xpos = xp;
this.ypos = yp-this.sheight/2;
this.spos = this.xpos + this.swidth/2 - this.sheight/2;
this.newspos = this.spos;
this.sposMin = this.xpos;
this.sposMax = this.xpos + this.swidth - this.sheight;
this.loose = l;
this.over = false;
this.locked = false;
}
update() {
if (this.overEvent()) {
this.over = true;
} else {
this.over = false;
}
if (mouseIsPressed && this.over) {
this.locked = true;
}
if (!mouseIsPressed) {
this.locked = false;
}
if (this.locked) {
this.newspos = constrain(mouseX-this.sheight/2, this.sposMin, this.sposMax);
}
if (abs(this.newspos - this.spos) > 1) {
this.spos = this.spos + (this.newspos-this.spos)/this.loose;
}
}
overEvent() {
if (mouseX > this.xpos && mouseX < this.xpos+this.swidth &&
mouseY > this.ypos && mouseY < this.ypos+this.sheight) {
return true;
} else {
return false;
}
}
display() {
noStroke();
fill(204);
rect(this.xpos, this.ypos, this.swidth, this.sheight);
if (this.over || this.locked) {
fill(0, 0, 0);
} else {
fill(102, 102, 102);
}
rect(this.spos, this.ypos, this.sheight, this.sheight);
}
getPos() {
// Convert spos to be values between
// 0 and the total width of the scrollbar
return this.spos * this.ratio;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
Please, check this:
CSlider - Canvas Slider for p5.js with scale() support, mostly supported standard p5 slider calls:
https://editor.p5js.org/Comissar/sketches/IHUYuzjR

HTML5: How can I make my canvas forget old pixels?

I'm trying to make a canvas which can be operated via the mouse (dragging, drawing, clicking...), which would seem to be a simple task. Currently I have it drawing a line from where the mouse was pressed to where ever it is now, until it is released. But all the old versions of the line continue to be drawn regardless of whether I clear the canvas. I've tried using beginPath/closePath and fill instead of stroke; both approaches caused the line to never appear. Is there some... "delimiter" of draw operations that I haven't been able to find?
The source may be viewed and tested here, and I've also included it below.
<!DOCTYPE html>
<html>
<head>
<title>Canvas</title>
</head>
<body>
<canvas id="test" width="640" height="480"></canvas>
<script type="text/javascript">
(function (){
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
function getMousePos(obj, evt){
// get canvas position
var top = 0;
var left = 0;
while (obj && obj.tagName != 'BODY') {
top += obj.offsetTop;
left += obj.offsetLeft;
obj = obj.offsetParent;
}
// return relative mouse position
var mouseX = evt.clientX - left + window.pageXOffset;
var mouseY = evt.clientY - top + window.pageYOffset;
return {
x: mouseX,
y: mouseY
};
}
var canvasElement = document.getElementById('test');
if(canvasElement.getContext){
var canvas = canvasElement.getContext('2d');
var start = null;
var end = null;
var drawing = false;
canvasElement.addEventListener('mousedown', function (event){
var mousePos = getMousePos(canvasElement, event);
start = mousePos;
end = mousePos;
drawing = true;
}, false);
canvasElement.addEventListener('mousemove', function (event){
if(drawing){
end = getMousePos(canvasElement, event);
}
}, false);
function release(event){
drawing = false;
}
canvasElement.addEventListener('mouseup', release, true);
var FPS = 30;
setInterval(function() {
canvas.clearRect(0, 0, canvasElement.width, canvasElement.height);
if(drawing && start != null){
canvas.moveTo(start.x, start.y);
canvas.lineTo(end.x, end.y);
canvas.stroke();
}
}, 1000/FPS);
}
})();
</script>
</body>
</html>​
You need to create a path with beginPath and closePath, like so:
canvas.clearRect(0, 0, canvasElement.width, canvasElement.height);
if(drawing && start != null){
canvas.beginPath();
canvas.moveTo(start.x, start.y);
canvas.lineTo(end.x, end.y);
canvas.closePath();
canvas.stroke();
}
Otherwise you'll just keep adding lines to the existing path.

Resources