Circle Collision

Dette er et eksempel på, hvordan man kan benytte p5.Vector klassen til at simulere kollision mellem 2 cirkler.

Den resulterende hastighedsvektor efter kollisionen beregnes ved at benytte reflektion om normalvektoren til de sammenstødende cirkelperiferier.

Reflektionen kan beregnes ved hjælp af formlen

$$\vec r = \vec d - 2 (\vec d \cdot \vec n) \vec n$$

hvor \( \vec d \) er den indkommende vektor, \( \vec n = \frac{\vec n}{|\vec n|}\) er en normaliseret normal-vektor til cirkelperiferien, og \( \vec r \) er reflektionen af \( \vec d \) omkring \( \vec n \).

Se også implementationen af reflect().

Udledning af formlen

Reflektion af vektor omkring overfladenormal.

\(\vec n\) er en normal vektor til overfladen, og denne er normaliseret dvs. $$\vec n = \frac{\vec n}{|\vec n|}$$

Projektionen \(\vec p\) af \(\vec d\) på overfladens normalvektor \(\vec n\) findes ved hjælp af skalar produktet $$\vec p = (\vec d \cdot \vec n)\vec n$$

Vektoren \(\vec e\) bruges som hjælp i beregningen

$$\vec e = \vec d - \vec p$$

Da indfaldsvinkel og udfaldsvinkel er ens, og desuden er størrelsen af den indgående vektor og reflektionen ens \(|\vec d| = |\vec r|\) fås

$$\vec r = -\vec d + 2\vec e$$

Derefter kan formlerne kombineres

$$ \begin{aligned} \vec r &= -\vec d + 2(\vec d - \vec p) \\ &= -\vec d + 2(\vec d - (\vec d \cdot \vec n)\vec n) \\ &= -\vec d + 2\vec d - 2(\vec d \cdot \vec n)\vec n \\ &= \vec d - 2(\vec d \cdot \vec n)\vec n \end{aligned} $$

Struktur af programmet

Eksemplet består er opdelt i 3 filer, som er inkluderet i html filen således:

<script src="ball.js"></script>
<script src="bat.js"></script>
<script src="sketch.js"></script>

Det overordnede programflow styres i sketch.js.

let ball;
let bat;
function setup() {
  createCanvas(windowWidth, windowHeight);
  ball = new Ball(width / 2, height / 2)
  bat = new Bat(0,0)
}

function draw() {
  background(220);
  ball.render();
  ball.update();

  bat.render();
  bat.update();
  bat.collision(ball)
}

Filen ball.js indeholder en klassen Ball.

class Ball {
  constructor(x, y) {
    this.pos = createVector(x, y)
    this.vel = createVector(12, 3)
    this.r = 40
    this.isColliding = false
    this.collisionHandled = false
  }

  update() {
    this.pos.add(this.vel);
    this.edges();
  }

  edges() {
    const x = this.pos.x;
    if (x - this.r < 0 || x + this.r >= width) {
      this.vel.x = -this.vel.x
    }
    const y = this.pos.y;
    if (y - this.r < 0 || y + this.r >= height) {
      this.vel.y = -this.vel.y
    }
  }

  render() {
    push();
    if (this.isColliding) {
      strokeWeight(5)
    }
    if (this.collisionHandled) {
      fill('green')
    }
    circle(this.pos.x, this.pos.y, this.r * 2);
    pop();
  }
}

Filen bat.js indeholder en klassen Bat.

class Bat {
  constructor(x, y) {
    this.pos = createVector(x, y)
    this.r = 60
  }

  update() {
    this.pos = createVector(mouseX, mouseY)
  }

  render() {
    push();
    fill('red')
    circle(this.pos.x, this.pos.y, this.r * 2);
    pop();
  }

  collision(other) {
    const distance = this.pos.dist(other.pos);

    other.isColliding = distance < this.r + other.r
    if (other.isColliding) {
      if (!other.collisionHandled) {
        this._resolveCollision(other);
        other.collisionHandled = true;
      }
    } else {
      other.collisionHandled = false;
    }
  }

  _resolveCollision(other) {
    const surfaceNormal = p5.Vector.sub(this.pos, other.pos);
    other.vel.reflect(surfaceNormal);
  }
}

Bemærk hvordan reflect() benyttes til beregning af hastigheden efter kollisionen.

Demo

Prøv det kørende eksempel

Materiale