Creating multiple class instances in p5?

I’d like to create a potentially unlimited amount of instances of a certain class within my javascript document:

let trees;
const treespawn = () => {
  let x = random(50, windowWidth - 50);
  let y = random(windowHeight /22, windowHeight / 1.13);
  for (let i = 0; i < 20; i++) {
    trees[i] = new Trees(x, y);
    return trees[i];
  }
}

function draw() {
  background(0, 111, 10);
  trees.trunk();
  trees.leaves();
  trees.shudder();
}

class Trees {
  constructor(x, y) {
    stuff--stuff--stuff
  }
  trunk() {
    stuff--stuff--stuff
  }
  leaves() {
    stuff--stuff--stuff
  }
  shudder() { 
    stuff--stuff--stuff
  }
}

Explanation:

I’d like to create my Trees class object many times and have them appear on my canvas at the start of each level of the game I’m making. This would require around 20 trees per level inside the parameters set to let x and let y. In higher levels of my game I may want to increase the amount of trees.

You can see how simply writing out 20-30 variable declarations, creating those classes and calling all of the functions inside each class would be impractical.

I realise this must be done with some sort of array methodology. Can anyone help me here?

Thanks in advance!

This is as far as I’ve gotten and no console error message but I see no trees on the canvas.

function draw() {
  background(0, 111, 10);

  () => {
    let x = random(50, windowWidth - 50);
    let y = random(windowHeight /22, windowHeight / 1.13);
    for (let i = 0; i < 20; i++) {
      trees[i] = new Trees(x, y);
      trees[i].trunk();
      trees[i].leaves();
      trees[i].shudder();
    }
  }
}
1 Like
  1. You haven’t defined trees as an array:
const trees = [];
  1. There’s no setup function defining your canvas size and calling your treespawn function, unless you have that somewhere not shown here:
function setup() {
  createCanvas(1000, 1000);
  treespawn();
  background(0, 111, 10);
}
  1. You probably don’t want to use windowWidth and windowHeight for determining your tree locations in treespawn unless it’s fullscreen - width and height will use just the canvas size rather than the full window size.
  2. You aren’t redefining your random x and y for each new tree in treespawn, so they’re all going to appear in the same location, you can fix that with:
function treespawn() {
  for (let i = 0; i < 20; i++) {
    const x = random(50, width - 50);
    const y = random(height / 22, height / 1.13);
    trees[i] = new Tree(x, y);
  }
}
  1. For drawing the trees you can use a pattern like this:
function draw() {
  for (const tree of trees) {
    tree.display();
  }
}

class Tree {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  display() {
    this.constructor.trunk(this.x, this.y);
    this.constructor.leaves(this.x, this.y);
    this.constructor.shudder(this.x, this.y);
  }
  static trunk(x, y) {
    rect(x, y, 10, 20);
  }
  static leaves(x, y) {
    // draw leaves
  }
  static shudder(x, y) {
    // draw shudder
  }
}

However, I’m not sure why you’re drawing the leaves and shudder separately from the trunk; you could get rid of the three static functions and just include all three parts within the single display function.

The problem with your second codeblock, at the bottom, is that you’re defining a function within draw - you shouldn’t do this. Define the function outside, like you have with treespawn in the first codeblock, but you have to call that function in order to actually populate the array before you start draw (in setup).

1 Like
  1. yes sorry I did rectify that.
  2. yes also sorry there is a setup function I just chose not to include it.
  3. it’s a game that does take up the full viewport of a device.
    4 & 5. Here is a more extensive bit of code. As you can see If tried another method:
function setup() {
  createCanvas(windowWidth, windowHeight);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

const treespawn = () => {
  let x = random(50, windowWidth - 50);
  let y = random(windowHeight /22, windowHeight / 1.13);
  return new Trees(x, y);
}

let trees = [];
for (let i = 0; i < 20; i++) {
  trees.push(treespawn());
}

function draw() {
  background(0, 111, 10);

  trees.forEach(tree => {
    tree.trunk();
    tree.leaves();
    tree.shudder();
  });

  road();
  roadmarks();
}

class Trees {
  constructor(x, y) {
    this.x = x,
    this.y = y,
    this.l = this.x - 2,
    this.r = this.y + 2
  }
  trunk() {
    noStroke();
    fill(126, 60, 1);
    rect(this.x + 3, this.y, 14, 40);
    triangle(this.x + 10, this.y + 30, this.x - 4, this.y + 40, this.x + 24, this.y + 40);
  }
  leaves() {
    fill(71, 215, 45);
    ellipse(this.x + 3, this.y - 5, 25);
    ellipse(this.x + 10, this.y - 15, 25);
    ellipse(this.x + 17, this.y - 5, 25);
  }
  shudder() {  // axe chopping animation
    if (!this.steps) {
      this.speed = 0;
      let d = dist(mouseX, mouseY, this.x, this.y);
      if (d < 30) {
        this.steps = [0, 100, 1000, 1100, 2000, 2100].map(i => Date.now() + i);
      }
    } else if (this.steps[0] < Date.now()) {
        this.speed = 3 - Math.abs(this.speed); // Toggle between animate and pause
        this.steps.shift(); // Remove a step, since that duration has completed
    }
    if (this.x < this.l || this.x > this.r) {
      this.speed = -this.speed;
    }
    this.x += this.speed;
  }
}

At the moment I’m getting a couple of console errors back and I can’t figure out why!:
Uncaught ReferenceError: random is not defined
Uncaught ReferenceError: road is not defined

Your constructor in the Trees class has commas instead of semicolons. The reason random isn’t defined is because you’re creating the treespawn function before p5 loads (before it calls setup). You can solve that by creating the treespawn variable as a global, but defining the function inside of setup as well as populating the trees array in setup:

let treespawn;
const trees = [];

function setup() {
	createCanvas(windowWidth, windowHeight);

	treespawn = () => {
		let x = random(50, windowWidth - 50);
		let y = random(windowHeight / 22, windowHeight / 1.13);
		return new Trees(x, y);
	};

	for (let i = 0; i < 20; i++) {
		trees.push(treespawn());
	}
}

As for road and roadmarks, they aren’t anywhere in that codeblock, so those will throw undefined when you try to call them.

2 Likes

YES! That’s it! Thanks very much.

road and roadmarks are defined functions in the document. They look like this:

const road = () => {
  fill(0);
  rect(0, windowHeight / 1.13, windowWidth, windowHeight);
  fill(210);
  rect(0, windowHeight / 1.122, windowWidth, 3);
}

const roadmarks = () => {
  for (let i = 0; i <= width; i += 50) {
    fill(120);
    rect(i - 12, windowHeight / 1.065, 40, 5);
  }
}

They’re sitting below the class Trees.

Comma , is a valid operator in JS: :face_with_monocle:

We can do any sequence of expressions separated by the comma operator w/o any problem is JS. :stuck_out_tongue:

Yeah, but it screws up linting and autoformat, extra indents if you put them on separate lines like that :rofl:

I’m not getting any undefined issues with those two pasted underneath everything.