Peanut Jams
Peanut Jams

Reputation: 384

Vector attraction when mouse pressed

Currently trying to create attraction effect on 3D cubes using createVector function. The attraction designed to trigger when mouse pressed but currently it's stating the error:

Uncaught TypeError: Cannot read property 'copy' of undefined

The code:

let cubes = [];

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  backCol = color(243, 243, 243);
 
  for (let i = 0; i < 10; i++) {
    for (let j = 0; j < 10; j++) {
      let xPos = map(i, 0, 9, 50, width - 50);
      let yPos = map(j, 0, 9, 50, height - 50);
      cubes.push(new Cubes(xPos, yPos));
    }
  }
}

function draw() {
  background(backCol);
  noFill();
  for (let cube of cubes) {
    cube.update();
  } 
  attracting();
}

function attracting() {
  for (let a = 0; a < cubes.length; a++) {
    cubes[a].attraction(mouseX,mouseY);
  }
}

class Cubes {

  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.size = 30;
    this.stroke = 70;
    this.gap = 150;
    this.shift1 = color(96);
    this.shift2 = color(244);
 
    //vector variables
    this.pos = createVector(x, y);
    this.vel = createVector(); 
    this.acc = createVector();
  }

  update() {
    this.shape();
    this.test_Color();
    
    //attraction values
    this.vel.add(this.acc);
    this.vel.limit(5);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }
  
  shape() {
    push();
    stroke(this.stroke);
    this.test_Color();
    translate(this.x - width / 2, this.y - height / 2, 0);
    this.test_rotation()
    box(this.size);
    pop();
  }

  test_Color() {
    fill(this.shift1);
  }

  test_rotation() {               
    rotateX(frameCount / 60);
    rotateY(frameCount / 60);
  }
  
  attraction(target) {
    
    //all cubes supposed to attract towards the mouse when pressed

    if (mouseIsPressed) {
      let force = p5.Vector.sub(target.pos,this.pos);
      let d = force.mag();
      d = constrain(d, 1, 25);
      var G = 50;
      var strength = G / (d * d);
      force.setMag(strength);
      if (d < 20) {
        force.mult(10);
      }
      this.vel.add(force);  
    }
  }
}

unsure what detail to add further. Unsure what detail to add further. Unsure what detail to add further. Unsure what detail to add further. Unsure what detail to add further. Unsure what detail to add further. Unsure what detail to add further.

Upvotes: 1

Views: 170

Answers (1)

julien.giband
julien.giband

Reputation: 2609

  • You're using index a for argument of Cubes.attraction which is expecting an object with a pos vector field (a Cubes ?)
  • You're trying to position your cubes using this.x / this.y, which aren't changed by update. Use the this.pos vector instead
  • You can optimize by checking for mouse pressed only once, and only then calling attraction on every cube with mouse coordinates as vector argument
  • In physics, force drives acceleration, not velocity. I changed that but maybe it was deliberate of you.
  • You should change your class name to Cube rather than Cubes

let cubes = [];

function setup() {
  /* max(...) here is just for rendering with minimum size in the snippet */
  createCanvas(max(windowWidth, 800), max(windowHeight, 600), WEBGL);
  backCol = color(243, 243, 243);
 
  for (let i = 0; i < 10; i++) {
    for (let j = 0; j < 10; j++) {
      let xPos = map(i, 0, 9, 50, width - 50);
      let yPos = map(j, 0, 9, 50, height - 50);
      cubes.push(new Cubes(xPos, yPos));
    }
  }
}

function draw() {
  background(backCol);
  noFill();
  for (let cube of cubes) {
    cube.update();
  } 
  attracting();
}

function attracting() {
  /* changed to check for mouse pressed once for all cubes */
  if (mouseIsPressed) {
    /* generating mouse position vector once for all cubes */
    const mousePosVect = new p5.Vector(mouseX, mouseY);
    for (let a = 0; a < cubes.length; a++) {
      cubes[a].attraction(mousePosVect);
    }
  }
}

class Cubes {

  constructor(x, y) {
    /* Removed useless and confusing this.x, this.y */
    this.size = 30;
    this.stroke = 70;
    this.gap = 150;
    this.shift1 = color(96);
    this.shift2 = color(244);
 
    //vector variables
    this.pos = createVector(x, y);
    this.vel = createVector(); 
    this.acc = createVector();
  }

  update() {
    this.test_Color();
    
    //attraction values
    this.vel.add(this.acc);
    this.vel.limit(5);
    this.pos.add(this.vel);
    this.acc.mult(0);
    this.shape();
  }
  
  shape() {
    push();
    stroke(this.stroke);
    this.test_Color();
    /* Used this.pos instead of this.x, this.y for positioning */
    translate(this.pos.x - width / 2, this.pos.y - height / 2, 0);
    this.test_rotation();
    box(this.size);
    pop();
  }

  test_Color() {
    fill(this.shift1);
  }

  test_rotation() {               
    rotateX(frameCount / 60);
    rotateY(frameCount / 60);
  }
  
  attraction(targetVector) {
    //all cubes supposed to attract towards the mouse when pressed

    /* Set target argument to vector,
     * moved the `if (mouseIsPressed)` condition outside */
    let force = p5.Vector.sub(targetVector,this.pos);
    let d = force.mag();
    d = constrain(d, 1, 25);
    var G = 50;
    var strength = G / (d * d);
    force.setMag(strength);
    if (d < 20) {
      force.mult(10);
    }
    /* changed to add force to acceleration
     * instead of velocity (physically accurate) */
    this.acc.add(force);  
  }
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>P5 cube attractor</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js" integrity="sha512-gQVBYBvfC+uyor5Teonjr9nmY1bN+DlOCezkhzg4ShpC5q81ogvFsr5IV4xXAj6HEtG7M1Pb2JCha97tVFItYQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    </head>
    <body>
        <!--h1>P5 test</h1-->
    </body>
</html>

Upvotes: 1

Related Questions