p5 js "can't read property 'show()' of undefined" - javascript

What I am trying to do in this code is to take one dead particle, and make live ones cling to it and become dead in the process. At the end I would wish to have something that looks nice and is just a pleasurable program to watch.
But I have a problem...
My code will freeze at seemingly random points while running it and say "can't read property 'show()' of undefined."
The error is on line 18 when it tries to run the "live[i].show();"
sometimes it happens right away and sometimes it takes awhile but it always happens. Any help is appreciated.
Thank you in advance.
var live = [];
var dead = [];
function setup() {
createCanvas(windowWidth, windowHeight);
background(255);
for (var i = 0; i < 100; i++) {
live.push(new particle(random(width), random(height), 1));
}
dead.push(new particle(width / 2, height / 2, 0))
}
function draw() {
background(255);
if (live.length > 0) {
for (var i = 0; i < live.length; i++) {
live[i].update(i);
live[i].show();
}
}
for (var i = 0; i < dead.length; i++) {
dead[i].show();
}
}
function particle(x, y, l) {
this.x = x;
this.y = y;
this.l = l;
this.speed = 5;
this.c = 200;
this.s = 50;
}
particle.prototype.update = function(dex) {
this.speed *= this.l;
this.x += random(-this.speed, this.speed)
this.y += random(-this.speed, this.speed)
if (this.x < 0) {
this.x = width;
} else if (this.x > width) {
this.x = 0;
}
if (this.y < 0) {
this.y = height;
} else if (this.y > height) {
this.y = 0;
}
for (var i = 0; i < dead.length; i++) {
if (dist(dead[i].x, dead[i].y, this.x, this.y) < this.s && dist(dead[i].x, dead[i].y, this.x, this.y) > 0) {
dead.push(new particle(this.x, this.y, 0));
live.splice(dex, 1);
}
}
}
particle.prototype.show = function() {
this.c *= this.l;
strokeWeight(this.s);
stroke(this.c);
point(this.x, this.y);
}

If you replace loop in the draw method with map it will solve your problem.
function draw() {
background(255);
live.map(function(curr,i){
curr.update(i);
curr.show();
})
dead.map(function(curr){
curr.show();
})
}
here is the plunkr of the updated code https://plnkr.co/edit/j5ueccmK4o7kbNKfeYQJ The error was caused because it was trying to access array larger than the maximum index of the array.
This is caused because you are mutating the array using splice in update method and it reduces its length while the for loop with previous length tries to loop the array beyond its length causing undefined.
Edit:
It is better to reduce the mutation in the code to avoid these errors.

Related

Endless game: How to spawn objects randomly on 4 lines wit p5.js

I am creating an endless racing game with p5.js and I am currently facing the biggest problem in its development. I need the other cars/obstacles to spawn randomly but in an ordered fashion, each one in its lane and leaving enough space for the player's car to drive through. Do you have any suggestion how can I achieve this? Thanks in advance.
What I managed to do so far is to create classes of obstacles, set their position and give them different speeds. But this doesn't create randomness of course.
class Obstacle1 {
constructor() {
this.x = 120
this.y = 0;
this.speed = 2
}
draw() {
image(this.imgObst1, this.x, this.y, 60, 120)
this.y += this.speed
if (this.y >= height){
this.y = 0
}
}
preload() {
this.imgObst1 = loadImage('/resources/player/car.png')
}
}
Try that:
let ob = [];
function setup() {
createCanvas(400, 400);
for(let i = 0; i < 4; i ++){
coords[i] = i*120;
}
for(let i = 0; i < 3; i ++){
ob[i] = new Obstacle1();
}
}
function draw() {
background(0);
for(let i = 0; i < 3; i ++){
ob[i].draw();
}
}
class Obstacle1 {
constructor() {
this.x = random(coords);
this.y = 0;
this.speed = 2
}
draw() {
rect(this.x, this.y, 60, 120)
this.y += this.speed
if (this.y >= height){
this.y = 0
}
}
}
if I understand correctly this should work.
I recommend you create a pickRandom() function
Ex-
function pickRandom(){
x= random(20,width-20)
}
Although you will need to adjust the code for this particular instance
I would also recommend you add a if statement with pickRandom() function in it.

How can i simulate a rigid body at micro level using p5.js?

I just learned that any particle of a rigid body when viewed from another particle's reference frame (of the same body) performs only circular motion, no matter what chaotic path the body is following! So I tried to simulate the same.
I created 100 particles closely located to form a body, and made them follow any arbitrary path , but now i can't get how to stick to one of the particle's reference frame & watch others.
This is what i have done (which i think is of no use):
var path = [];
var particles = [];
class Particle {
constructor(x_, y_) {
this.x_ = x_;
this.y_ = y_;
this.x = 0;
this.y = 0;
this.pathIndex = 0;
this.draw = () => {
fill(360, 100, 100);
circle(this.x, this.y, 1);
}
this.update = () => {
if (this.pathIndex >= path.length) {
this.pathIndex = 0;
} else {
this.x = (path[this.pathIndex].x + this.x_);
this.y = (path[this.pathIndex].y + this.y_);
this.pathIndex++;
}
this.draw();
}
}
}
function setup() {
createCanvas(400, 400);
colorMode(HSB);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
particles.push(new Particle(i * 2, j * 2));
}
}
}
function draw() {
background(50);
fill(0);
noStroke();
for (let i = 0; i < path.length; i++) {
circle(path[i].x, path[i].y, 5);
}
for (let i = 0; i < particles.length; i++) {
particles[i].update();
}
}
function mouseDragged() {
path.push({
x: mouseX,
y: mouseY
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
Click and drag to create a path.
For a real rigid body, each particle can have it's own velocity, and i think i can use relative velocity concept to move to a single particle's reference frame & then i can set other's velocity as:
v(1/2) = v1 - v2
Where v1, v2 are there original velocity and v(1/2) is velocity of 1 w.r.t. 2.
But again i don't have any idea to get particle's velocity.
How can i proceed with this, any help is greatly appreciated.

JavaScript only running when class file contains parenthesis in name

I'm new to JavaScript and wanted to code a game I had previously coded in Java. I was coding with the p5.js library, and using the Atom text editor. The game I was making was Asteroids and it was working fine. I had figured out how to add the ship and the lasers. Then I added the code for the actual asteroids. (I'm not sure if this is relevant or not, but as I said before, I was copying the code from a Java project and when I went to add the asteroids I basically copied the whole thing over, without saving it until I was done.) After I added them, I tried to run it, and I said "asteroid is not defined", because I forgot to add a reference to it in the HTML. However, after I ran it again, the page stopped loading and eventually crashed (I got the "Aw, Snap! Something went wrong when displaying this webpage" error). I thought it was the HTML at first, but when I added the class to the main script, it still crashed. After an hour, I realized that if I added parenthesis to the class name (class ship(){}) then the page would load. However, I would get a syntax error as expected. The same thing would happen if I declared the object as the class like you would do in Java (Ship ship;), it would run but it would say "Uncaught SyntaxError: Unexpected identifier". I'm not sure how to get around this, or why not adding parenthesis to a class name would cause the program not to run.
Attached are the first part of my main script, the constructor of my ship class, and my asteroid class because the problems only started after adding it.
Any help or solutions to this problem would be greatly appreciated.
Main Script:
let ship;
let bullets = [];
let asteroids = [];
// Left Right Boost Slow Shoot
let keyDown = [false, false, false, false, false];
function setup(){
window.canvas = createCanvas(800, 600);
ship = new Ship(width/2, height/2);
for(let i = 0; i = 4; i++){
asteroids.push(new asteroid(random(width), random(height), 100));
}
frameRate(60);
}
function draw(){
background(0);
checkKeys();
ship.update();
ship.show();
for(let i = bullets.length-1; i >= 0; i--){
bullets[i].update();
bullets[i].show();
if(!bullets[i].alive){
bullets.splice(i, 1);
}
}
for(let i = asteroids.length-1; i >= 0; i--){
asteroids[i].update();
asteroids[i].show();
}
}
Ship Class:
class Ship{
constructor(x, y){
this.x = x;
this.y = y;
this.velX = 0;
this.velY = 0;
this.midX = this.x + 15;
this.midY = this.y + 20;
this.heading = 0;
this.rotation = 0;
this.boosting = false;
}
Asteroid Class:
class asteroid{
constructor(x, y, size){
this.x = x;
this.y = y;
this.size = size;
this.velX = random(-2, 3);
this.velY = random(-2, 3);
if(this.velX == 0){
this.velX++;
}
if(this.velY == 0){
this.velY++;
}
this.points = random(4, 12);
this.xPoints = [];
this.yPoints = [];
this.offsets = [];
for(let i = 0; i < this.points; i++){
this.offsets[i] = random(-size/5, (size/5)+1);
}
}
update(){
this.x += this.velX;
this.y += this.velY;
}
show(){
stroke(255);
beginShape();
for(let i; i < this.points; i++){
let angle = i*(360/this.points);
let px = (this.size/2 + this.offsets[i]) * cos(angle);
let py = (this.size/2 + this.offsets[i]) * sin(angle);
this.xPoints[i] = px + this.x;
this.yPoints[i] = py + this.y;
vertex(px, py);
}
endShape(CLOSE);
}
}
Running the code, I got the same error. Turns out your ship class has no closing bracket } for the class, only for the constructor. Adding that and now I get no errors
let ship;
let bullets = [];
let asteroids = [];
// Left Right Boost Slow Shoot
let keyDown = [false, false, false, false, false];
function setup(){
window.canvas = createCanvas(800, 600);
ship = new Ship(width/2, height/2);
for(let i = 0; i = 4; i++){
asteroids.push(new asteroid(random(width), random(height), 100));
}
frameRate(60);
}
function draw(){
background(0);
checkKeys();
ship.update();
ship.show();
for(let i = bullets.length-1; i >= 0; i--){
bullets[i].update();
bullets[i].show();
if(!bullets[i].alive){
bullets.splice(i, 1);
}
}
for(let i = asteroids.length-1; i >= 0; i--){
asteroids[i].update();
asteroids[i].show();
}
}
class Ship{
constructor(x, y){
this.x = x;
this.y = y;
this.velX = 0;
this.velY = 0;
this.midX = this.x + 15;
this.midY = this.y + 20;
this.heading = 0;
this.rotation = 0;
this.boosting = false;
}
} //<--- I added this bracket
class asteroid{
constructor(x, y, size){
this.x = x;
this.y = y;
this.size = size;
this.velX = random(-2, 3);
this.velY = random(-2, 3);
if(this.velX == 0){
this.velX++;
}
if(this.velY == 0){
this.velY++;
}
this.points = random(4, 12);
this.xPoints = [];
this.yPoints = [];
this.offsets = [];
for(let i = 0; i < this.points; i++){
this.offsets[i] = random(-size/5, (size/5)+1);
}
}
update(){
this.x += this.velX;
this.y += this.velY;
}
show(){
stroke(255);
beginShape();
for(let i; i < this.points; i++){
let angle = i*(360/this.points);
let px = (this.size/2 + this.offsets[i]) * cos(angle);
let py = (this.size/2 + this.offsets[i]) * sin(angle);
this.xPoints[i] = px + this.x;
this.yPoints[i] = py + this.y;
vertex(px, py);
}
endShape(CLOSE);
}
}

Brackets p5.js "Uncaught TypeError: Cannot read property 'offscreen' of undefined"

I am learning about Neural Networks(and learning JS at the same time) at the moment and wanted to start with a little game in Javascript to implement my learned stuff. So first things first, create a game, shouldn't be too hard right? Well.. Looks like I am too blind to find the mistake, so I would be really glad if someone could help me.
I am getting the following error message: "Uncaught TypeError: Cannot read property 'offscreen' of undefined".
It occurs when the player dies. I guess it has to do with the array being cleared when "resetting" and maybe the for-loop then still tries to delete the offscreen pipe, but it cant, as the array is empty or something like this. Dont know how to fix that tho..
https://thumbs.gfycat.com/WanDifficultAnaconda-size_restricted.gif
The Game is just an easy "objects come flying to you and you gotta jump over them".
Here's the whole code, it really isnt much:
sketch.js
let obstacle;
let player;
let obstacles = [];
let score = 0;
let highscore = 0;
function setup() {
createCanvas(600, 600);
obstacle = new Obstacle();
player = new Player();
}
function draw() {
background(0, 128, 128);
for(let i = obstacles.length-1; i >= 0; i--) {
if(obstacles[i].hits(player)) {
reset();
}
if(obstacles[i].offscreen()) {
obstacles.splice(i, 1);
}
obstacles[i].show();
obstacles[i].update();
}
player.show();
player.update();
score++;
currentScore();
newhighscore();
if(frameCount % 100 == 0) {
obstacles.push(new Obstacle());
}
}
function keyPressed() {
if(keyCode == 87 && player.y + player.h == height) {
player.jump();
}
}
function reset() {
if(score > highscore) {
highscore = score;
}
obstacles = [];
score = 0;
player = new Player();
}
function currentScore() {
strokeWeight(0);
stroke(102,205,170);
fill(14,47,68);
textSize(24);
text(score, 10, 30);
}
function newhighscore() {
strokeWeight(0);
stroke(102,205,170);
fill(14,47,68);
textSize(16);
text(highscore, 11, 48);
}
player.js
class Player {
constructor() {
this.x = 100;
this.h = 30;
this.w = this.h;
this.y = height - this.h;
this.gravity = 0.5;
this.lift = -9;
this.velocity = 0;
}
show() {
fill(0);
rect(this.x, this.y, this.w, this.h);
}
update() {
this.velocity += this.gravity;
this.y += this.velocity;
if(this.y + this.h > height) {
this.y = height - this.h;
this.velocity = 0;
}
}
jump() {
this.velocity += this.lift;
}
}
obstacle.js
class Obstacle {
constructor() {
this.x = width;
this.h = random(30, 50);
this.w = this.h;
this.y = height-this.h;
this.speed = 5;
}
show() {
noStroke();
fill(255, 115, 115);
rect(this.x, this.y, this.w, this.h);
}
update() {
this.x -= this.speed;
}
offscreen() {
if(this.x < -width) {
return true;
} else {
false;
}
}
hits(player) {
if(player.x <= this.x + this.w && player.x + player.w >= this.x) {
if(player.y + player.h >= this.y) {
return true;
} else {
return false;
}
}
}
}
My Solution
Explanation
Yeah you had the reason correct, when reset() is called, it changes to obstacles = [] but the
for(let i = obstacles.length-1; i >= 0; i--) {
if(obstacles[i].hits(player)) {
reset();
...*Continued*
}
in your draw function still continues. So you just had to add break; in the hit condition after reset. See in the link I've uploaded it works completely fine.
for(let i = obstacles.length-1; i >= 0; i--) {
if(obstacles[i].hits(player)) {
reset();
break;
...*Continued*
}
When you add break, the for loop terminates and moves down to the functions that follow. Since obstacles = [] is empty, undefined error is not shown as empty indexes are not accessed.

Check if two items overlap on a canvas using JavaScript

I have a canvas, and I'm using geolocation, google maps and openweathermap to get the weather of where the user is. The weather will be taken and cause the game I'm making to also use that weather...
How can I check if two squares that are spawned onto the canvas overlap? This is the code for the rain, which looks nothing like rain at the moment...
function rectangle(x, y, w, h) {
var randomx = Math.floor(Math.random() * canvas.width - 50);
this.x = randomx || x || 0;
this.y = y || 0;
this.w = w || 0;
this.h = h || 0;
this.expired = false;
this.draw = function() {
cx.fillStyle = "blue";
cx.fillRect(this.x, this.y, this.w, this.h);
};
this.update = function() {
this.y++;
if (y > canvas.height) {
this.expired = true;
}
};
}
var rectangles = new Array();
function newRect() {
rectangles.push(new rectangle(window.randomx, window.y, 10, 10));
}
var timing = 0;
function loop() {
cx.clearRect(0, 0, canvas.width, canvas.height);
if (timing % 10 === 0) {
newRect();
}
for (var i = 0, l = rectangles.length; i < l; i++) {
rectangles[i].update();
rectangles[i].draw();
if (rectangles[i].expired) {
rectangles.splice(i, 1);
i--;
}
}
timing++;
requestAnimFrame(loop);
}
loop();
My assumption, is that I need to perform a 'hit test' to see if two rectangles in the array are in the same perimeter... I guess where my this.draw and this.update function are I should do something like...
this.hitTest = function () {
//Maths to check here
};
Then in the forloop do...
rectangles[i].hitTest();
But I'm not sure on the Maths or where to go from there...
Any help would be appreciated, thanks in advance!
You can extend your rectangle object like this:
function rectangle(x, y, w, h) {
... existing code here ...
}
rectangle.prototype.intersects = function(rect) {
return !( rect.x > (this.x + this.w) ||
(rect.x + rect.w) < this.x ||
rect.y > (this.y + this.h) ||
(rect.y + rect.h) < this.y);
}
Based on this code by Daniel Vassallo, adopted for your object.
Now simply call the function on the rectangle you want to compare with:
if ( rectangles[x].intersects(rectangles[y]) ) {
... they intersect ...
}
To check if a new rectangle intersects with an existing you can do:
function isOverlapping(myNewRect, rectangles) {
for(var i = 0, r; r = rectangles[i]; i++) {
if (myNewRect.intersect(r)) return true;
}
return false;
}

Categories

Resources