canvas only target certain particles for clearing - javascript

I have this code that stacks canvas particles in a lane and then resets the whole canvas when one lane is full.
It does this by setting "particles = []"
I want to modify this behavior by only resetting the canvas for that particular lane. Is it possible?
$(document).ready(function () {
"use strict";
var c = document.getElementById("c"),
ctx = c.getContext("2d"),
WIDTH = c.width = window.innerWidth,
HEIGHT = c.height = window.innerHeight;
var particles = [],
particle = null,
particleCount = 3,
radius = 0,
numParticles = [0, 0, 0, 0, 0, 0],
colors = ["#00FF6D", "E8D90C", "#FF5900", "#C00CE8", "#0D90FF"];
var Vector = function (x, y) {
this.x = x || 0;
this.y = y || 0;
};
Vector.prototype = {
constructor: Vector,
add: function (v) {
this.x += v.x;
this.y += v.y;
},
sub: function (v) {
this.x -= v.x;
this.y -= v.y;
},
mul: function (v) {
this.x *= v.x;
this.y *= v.y;
}
};
var Particle = function (position, velocity, radius, lane, color) {
this.position = position;
this.velocity = velocity;
this.radius = radius;
this.baseRadius = radius;
this.angle = 3;
this.lane = lane;
this.color = color;
};
Particle.prototype = {
constructor: Particle,
update: function (lane) {
this.radius = 3;
this.angle += 10;
this.position.add(this.velocity);
// CHECKS FIRST IF THERE'S ALREADY A PARTICLE IN THE LANE AND THEN SHORTENS THE STOP LENGTH
if (this.position.x > WIDTH - (numParticles[this.lane] + 1) * 120) {
// IF THERE IS ALREADY A PARTICLE ON A LANE THE NUMBER OF PARTICLES PER IS INCREASED
if (this.velocity.x > 0) {
console.log(numParticles[this.lane] + " particles in this lane")
numParticles[this.lane]++;
if (numParticles[this.lane] > 8) {
numParticles[this.lane] = 0;
particles = [];
}
// STOPS THE PARTICLE
this.velocity.x = 0;
}
}
},
render: function (ctx) {
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 5; j++) {
ctx.beginPath()
ctx.fillStyle = this.color;
ctx.arc(this.position.x - i * 12, (this.position.y - 30) + j * 12, this.radius + 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
}
};
function addParticle(lane) {
radius = 3;
particle = new Particle(
new Vector(-radius, lane * (HEIGHT) / 5),
new Vector(5),
radius,
lane,
colors[Math.round(Math.random() * 4)]
);
particles.push(particle);
}
requestAnimationFrame(function loop() {
requestAnimationFrame(loop);
ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0, len = particles.length; i < len; i++) {
particle = particles[i];
particle.update();
particle.render(ctx);
}
});
//
// ─── SOCKETIO CONNECTION TO NODE WEBSERVER RUNNING OPENCV ───────────────────────
//
var socket = io.connect('http://localhost:8000');
// SEND "CONNECTED" MESSAGE ONCE SUCCSEFULLY CONNECTED
socket.on('connect', function () {
console.log('connected');
});
socket.on('message', function (msg) {
console.log(msg);
// IF NUMBER OF CARS DETECTED IS MORE THAN 0, A PARTICLE WILL BE ADDED RANDOMLY TO ONE OF THE 4 LANES
if (msg >= 0) {
addParticle(Math.round((Math.random() * 3) + 1));
}
});});

Filtering arrays
If you have an array of particles and want to remove some items that satisfy some condition, there are a few ways to do it.
Array.filter
Create a new array using Array.filter
var particles = []; // fill with particles
// remove particles in lane 3 using arrow function
particles = particles.filter(particle => particle.lane !== 3);
// or using via function
particles = particles.filter(function(particle){ return particle.lane !== 3});
This method does create a new array so incurs some penalties due to GC (Garbage collection) and allocation overheads. Also if you have more than one referance to the array you need to up date all references.
var particles = [];
const obj = {
particles : particles, // referance to the array particles.
}
// remove particles in lane 3
particles = particles.filter(particle => particle.lane !== 3)
// the array referenced by obj.particles still has the unfiltered array
obj.particles = particles; // update reference
Array.splice to filter
When there is need to performance you can use Array.splice to remove items from the array. It also has the advantage of not creating a new referance that you would have to propagate.
for(var i = 0; i <particles.length; i++){ // must use particles.length
if(particles[i].lane === 3){
particles.splice(i--,1); // decrease i so you do not skip an item
// as slice will move the index of all
// items above i down one
}
}
// do not use a variable to store the length in the for loop
var len = particles.length
for(var i = 0; i < len; i++){ // will cause unexpected behaviors because
// the array length will change
Flag active
For the best performance you would assign a flag to all particles that determines if the particle is updated and rendered.
for(var i = 0; i <particles.length; i++){
particles[i].active = ! particles[i].lane === 3;
}
Before you update and render you would check if active is true
// in render and update loops
for(var i = 0; i <particles.length; i++){
if(particles[i].active){
particles[i].update(); // or render()
}
}
There is of course the fact you have a growing array. To stop the uncontroled growth of the array you change the way you set the particle properties adding a method to the particle object.
function Particle(){}; // constructor
Particle.prototype.set = function(pos,vel,foo,bar){
this.active = true;
this.velocity = vel;
//... and the other properties
}
The when you add a particle you first check if there is an inactive particle in the particles array and use that by setting its properties with the set function. If there are no inactive particles then you just create a new particle and add it to the particles array setting its properties with set.
function addParticle(pos,vel,foo,bar){
var i;
for(i = 0; i < particles.length; i++){
if(! particles[i].active){
particles[i].set(pos,vel,foo,bar);
return;
}
}
// no inactive particles create a new one
particles.push(
new Particle().set(pos,vel,foo,bar);
);
}

Related

How to modify p5js code to work in instance mode

The following code works, but I want to be able to have multiple canvasses with different background colors and sizes etc. - how do I modify the code to turn it in to a p5 instance?- I can't get the instance to recognize the external class 'ellipse' and the drawCircles, and setInterval functions
let circles = [];
let circlesStatic = [];
let counter = 0;
function setup() {
createCanvas(400, 400);
setInterval(expandCircle, 1000);
}
function draw() {
background(0);
drawCircles();
}
/////////////////////////////////////////////////////////////////////////////
function expandCircle() {
let e = new Ellipse(random(100), random(100), counter);
circles.push(e);
//console.log("circlesStatic ", circlesStatic);
//console.log(counter);
counter++;
}
///////////////////////////////////////////////////////////////////////////////
class Ellipse {
// constructor
constructor(x, y, counter) {
this.x = x; // copy x argument value to the instance (this) x property
this.y = y; // copy x argument value to the instance (this) x property
// current size - continuously updated
this.size = 10;
// minimum size
this.minSize = 10;
// maximum size
this.maxSize = 30;
// change speed for size (how much will the size increase/decrease each frame)
this.sizeSpeed = 3;
// internal frameCount replacement
this.tick = 0;
this.counter = counter;
// this.fill=(255,0,0);
}
isStatic() {
return this.sizeSpeed === 0;
}
render() {
// if the size is either too small, or too big, flip the size speed sign (if it was positive (growing) - make it negative (shrink) - and vice versa)
if (this.size < this.minSize || this.size > this.maxSize) {
this.sizeSpeed *= -1;
}
// increment the size with the size speed (be it positive or negative)
this.size += this.sizeSpeed;
//console.log(this.sizeSpeed);
if (this.size <= this.minSize) {
circlesStatic[this.counter] = [];
circlesStatic[this.counter][0] = this.x;
circlesStatic[this.counter][1] = this.y;
this.sizeSpeed = 0;
}
ellipse(this.x, this.y, this.size, this.size);
}
}
////////////////////////////////////////////////////////////////////////////////
function drawCircles() {
for (let i = 0; i < circles.length; i++) {
circles[i].render();
if (circles[i].isStatic()) {
circles.splice(i, 1);
i--;
}
}
for (let i = 0; i < circlesStatic.length; i++) {
strokeWeight(0);
var popColour = (255, 255, 200);
fill(popColour);
circle(circlesStatic[i][0], circlesStatic[i][1], 1);
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>

The particles aren't showing up on screen

I don't know why my particles won't show up on the screen, when I play the sketch everything is frozen in place and particles don't come out. Particles are supposed to come out from the circles, while the circle is an audio visualizer of the song. I'm following a tutorial and making a few tweaks on my own so I can actually learn the code, but I'm stuck on showing the particles on the screen.
const { Part } = require("c:/users/12403/.vscode/extensions/samplavigne.p5-vscode-1.2.8/p5types");
var song
var fft
var img
var particles = []
function preload(){
song = loadSound('Clair de Lune With Rain.mp3')
img = loadImage('rain.jpg')
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES)
imageMode(CENTER)
img.filter(BLUR, 2)
fft = new p5.FFT()
}
function draw() {
background(0)
stroke(255)
strokeWeight(3)
noFill()
translate(width/2, height/2)
image(img, 0, 0, width, height)
fft.analyze()
amp = fft.getEnergy(20,200)
var wave = fft.waveform()
for (var t = -1; t <= 1; t += 2){
beginShape()
for(var i = 0; i <= 180; i+= 0.3){
var index = floor (map(i, 0, width, 0 , wave.length-1))
var r = map(wave[index], -1, 1, 150, 350)
var x = r*sin(i) * t
var y = r*cos(i)
vertex(x,y)
}
endShape()
}
var p = new Particle()
particles.push(p)
for(var i=0; i<particles.length; i++){
particles[i].show()
}
}
function mouseClicked(){
if (song.isPlaying()){
song.pause()
}
else{
song.play()
loop()
}
}
class Particle{
constructor(){
this.pos = p5.Vector.random2D().mult(250)
}
show(){
noStroke()
fill(random)
ellipse(this.pos.x, this.pos.y, 4)
}
}
Your code was crashing because this is invalid:
fill(random)
The fill function expects a color or number, and random is a function, so you need to invoke the random function to get a numeric value. Something like fill(random(0, 255)).
With that fixed your sketch runs and draws the particles, but there is clearly some logic missing:
you create one new particle per frame
each new particle gets a random position around a circle with a radius of 200
each particle's position has nothing to do with the audio and never changes over time
you never remove particles
You question is much to vague to figure out what you actually want to have happen, and you clearly need to take a few more steps to make it happen (such as setting each particle's position according to the audio FFT values, or updating each particle's position each frame). Also You definitely need a way to limit the number of particles, otherwise your sketch will eventually grind to a halt.
var song
var fft
var img
var particles = []
function preload() {
// Audio licensed Creative Commons Attribution Non-Commercial
// by apoxode https://apoxode.bandcamp.com/releases
// http://dig.ccmixter.org/files/Apoxode/63948
song = loadSound('https://www.paulwheeler.us/files/Apoxode_-_halcyon.mp3');
img = loadImage('https://www.paulwheeler.us/files/windows-95-desktop-background.jpg');
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES)
imageMode(CENTER)
img.filter(BLUR, 2)
fft = new p5.FFT()
}
function draw() {
background(0)
stroke(255)
strokeWeight(3)
noFill()
translate(width / 2, height / 2)
image(img, 0, 0, width, height)
fft.analyze()
amp = fft.getEnergy(20, 200)
var wave = fft.waveform()
for (var t = -1; t <= 1; t += 2) {
beginShape()
for (var i = 0; i <= 180; i += 0.3) {
var index = floor(map(i, 0, width, 0, wave.length - 1))
var r = map(wave[index], -1, 1, 150, 350)
var x = r * sin(i) * t
var y = r * cos(i)
vertex(x, y)
}
endShape()
}
var p = new Particle()
particles.push(p)
for (var i = 0; i < particles.length; i++) {
particles[i].show()
}
}
function mouseClicked() {
if (song.isPlaying()) {
song.pause()
} else {
song.play()
loop()
}
}
class Particle {
constructor() {
this.pos = p5.Vector.random2D().mult(250)
}
show() {
noStroke()
fill(random())
ellipse(this.pos.x, this.pos.y, 4)
}
}
<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>

How to constantly generate a moving shape with Javascript and Canvas

I'm currently developing a small game for my capstone project. In the game, the user tries to avoid rectangles of random sizes the move from the right side of the screen to the left at a set speed.
It's built using object-oriented Javascript, and I've assigned it an anonymous function, however, I can't seem to get it to generate a shape and animate it more than the initial time the function is called. The problem can be solved if I create more than one object, but I would like this function to run automatically and generate more than just the first rectangle.
I've tried to call the function with an interval to force it to re-run the function with no results. I also attempted to separate the initialization function to call it with a parameter to generate the number of shapes given to it.
This is the function that generates the shape with the initial call, and determines the color, size, and location as well as draws it on the canvas.
var randomRectangle = function(){
this.init = function() {
this.speed = 4;
this.x = canvas.width-50;
this.y = Math.floor(Math.random()*280) + 40;
this.w = Math.floor(Math.random()*200) + 50;
this.h = Math.floor(Math.random()*150) + 20;
this.col = "#b5e61d";
}
this.move = function(){
this.x -= this.speed;
}
this.draw = function(num){
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
This is where the object is initialized and the loop generates all objects and animations on the canvas.
randRecs = new randomRectangle();
randRecs.init();
function loop(){
draw.clear();
player.draw();
player.move();
wall1.draw();
wall2.draw();
randRecs.draw();
randRecs.move();
}
var handle = setInterval(loop, 30);
I expected the rectangle to continuously be generated at a new y-coordinate with a new size, then move from the right side of the screen to the left. However, only one rectangle is created and animated.
var list = [];
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var randomRectangle = function() {
this.init = function() {
this.speed = 4;
this.x = canvas.width - 50;
this.y = Math.floor(Math.random() * 280) + 40;
this.w = Math.floor(Math.random() * 200) + 50;
this.h = Math.floor(Math.random() * 150) + 20;
this.col = "#b5e61d";
}
this.move = function() {
this.x -= this.speed;
// restart x position to reuse rectangles
// you can change the y value here to a new random value
// or you can just remove with array.splice
if (this.x < -50) this.x = canvas.width - 50;
}
this.draw = function(num) {
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
function loop() {
draw.clear();
//player.draw();
//player.move();
//wall1.draw();
//wall2.draw();
// call the methods draw and move for each rectangle on the list
for (var i=0; i<list.length; i++) {
rec = list[i];
rec.draw();
rec.move();
}
}
// spawn any number of new rects in a specific interval
var rectsPerSpawn = 1;
function addRects() {
for (var i=0; i<rectsPerSpawn; i++) {
if (list.length < 100) {
var rec = new randomRectangle();
list.push(rec);
rec.init();
}
}
}
// every half second will spawn a new rect
var spawn = setInterval(addRects, 500);
var draw = {
clear: function () {
ctx.clearRect(0,0,canvas.width,canvas.height);
},
rectangles: function (x, y, w, h, col) {
ctx.fillStyle = col;
ctx.fillRect(x,y,w,h);
}
}
var handle = setInterval(loop, 30);
<canvas></canvas>

Matter.js Collision Not Detecting

I'm trying to practice using matter.js to create top down levels Bomberman style.
Right now I want to get my circle, which is controlled by arrow keys to move and bump into static squares but it is just going through them. Did I set it up incorrectly? I have been coding for three months, so I might be quite slow sorry!
var Engine = Matter.Engine,
World = Matter.World,
Bodies = Matter.Bodies;
var engine = Engine.create();
var world = engine.world;
var player;
var rocks = [];
var cols = 7;
var rows = 7;
function setup() {
createCanvas(750, 750);
Engine.run(engine);
player = new Player(300, 300, 25);
var spacing = width / cols;
for (var j = 0; j < rows; j++) {
for (var i = 0; i < cols; i++) {
var r = new Rocks(i * spacing, j * spacing);
rocks.push(r);
}
}
}
function draw() {
background(51);
Engine.update(engine);
for (var i = 0; i < rocks.length; i++) {
rocks[i].show();
}
player.show();
player.move();
}
function Player(x, y, r) {
this.body = Bodies.circle(x, y, r);
this.r = r;
World.add(world, this.body);
this.show = function () {
ellipse(x, y, this.r * 2);
}
this.move = function () {
if (keyIsDown(RIGHT_ARROW))
x += 10;
if (keyIsDown(LEFT_ARROW))
x -= 10;
if (keyIsDown(UP_ARROW))
y -= 10;
if (keyIsDown(DOWN_ARROW))
y += 10;
x = constrain(x, this.r, height - this.r);
y = constrain(y, this.r, width - this.r);
}
}
function Rocks(x, y, w, h, options) {
var options = {
isStatic: true
}
this.body = Bodies.rectangle(x, y, h, w, options);
this.w = w;
this.h = h;
this.size = player.r * 2;
World.add(world, this.body);
this.show = function () {
rect(x, y, this.size, this.size);
}
}
I think the problem is that you player is not drawn in the same position that the physics engine thinks its at.
in your Player function after the initialization of x and y the rest all need to be this.body.position.x and this.body.position.y. Otherwise you're changing where the picture is drawn at but not where the player actually is.
I'm not entirely sure what all you want me to point out besides that but also I think you want to disable gravity with engine.world.gravity.y = 0 and I was trying to fix the constrain function because as I tested it it wasn't working, I wasn't able to fix it but I'd recommend just making static boundary objects for the walls and just don't draw them.
Also matter.js processes the locations of objects from their centers. When drawing the objects you either have to take that into consideration or switch the mode to ellipseMode(CENTER);, 'rectMode(CENTER);` .. etc.
I hope this helps

Game slows down after a while, potential data leak?

I'm making a javascript game using p5 and p5.play and for some reason after the simulation runs for more than 20 seconds the entire thing really slows down. I think it may be a data leak but am unsure of what is truly harming performance.
Update: After some poking around I found that I was successfully removing the sprites from the plain group however there is some other Object called allSprites that is collecting the sprites no matter what. How I remove said sprites from this object is still unknown.
Update: Came up with way to remove them from object, still did not fix data leak.
var players;
var deadly;
var powerUp;
var plain;
var penguinSheet;
var penguinAnimation;
var willLoad;
var harmless;
var score = 0;
function preload(){
penguinSheet = loadSpriteSheet('./assets/BoxPenguinSprites.png', 64, 64, 3);
penguinAnimation = loadAnimation(penguinSheet);
}
function setup() {
createCanvas(700, 600);
// players = new Group();
// deadly = new Group();
// powerUp = new Group();
// harmless = new Group();
plain = new Group();
useQuadTree(true);
player = createSprite(100, 100, 20, 20);
player.debug = true;
}
function update(){
for (var i = 0; i < 10; i++) {
var c = createSprite(
random(width), 0,
random(25, 75), random(10, 30));
c.addSpeed(random(1,3), 90);
c.shapeColor = color(random(30, 50), random(180, 220), random(80, 130));
plain.add(c);
}
// for (var k = 0; k < 3; k++) {
// var d = createSprite(
// random(width), 0,
// random(25, 75), random(10, 30));
// d.addSpeed(random(1,3), 90);
// d.shapeColor = color(random(0, 30), random(150, 160), random(50, 80));
// harmless.add(d);
// }
}
function cleanUp(){
for (var j = 0; j < plain.length; j++){
if (plain[j].newPosition.y > height){
plain.remove(plain[j]);
}
}
// for (j = 0; j< harmless.length; j++){
// if (harmless[j].newPosition.y > height){
// harmless.remove(harmless[j]);
// }
// }
}
function pixleBuffer(){
var offsetX = 0;
var offsetY = 0;
if (keyIsDown(LEFT_ARROW)){
offsetX = -3;
}
if (keyIsDown(RIGHT_ARROW)){
offsetX = 3;
}
if (keyIsDown(UP_ARROW)){
offsetY = -3;
}
if (keyIsDown(DOWN_ARROW)){
offsetY = 3;
}
player.setCollider("rectangle", offsetX , offsetY, 20, 20);
}
//
function draw() {
if (frameCount == 1 || frameCount % 100 == 0){
update();
}
// player.overlap(harmless);
// plain.collide(harmless);
// harmless.collide(plain);
// harmless.collide(harmless);
player.collide(plain);
plain.collide(plain);
if(keyIsDown(LEFT_ARROW)){
player.position.x -= 3
}
if(keyIsDown(RIGHT_ARROW)){
player.position.x += 3
}
if(keyIsDown(UP_ARROW)){
player.position.y -=3
}
if(keyIsDown(DOWN_ARROW)){
player.position.y += 3
}
pixleBuffer();
clear();
cleanUp();
drawSprites();
}

Categories