Learning
레슨 5 / 8·20분

물리와 움직임

벡터와 물리 시뮬레이션

p5.js의 createVector()를 사용하면 위치, 속도, 가속도를 벡터로 표현하여 자연스러운 물리 시뮬레이션을 구현할 수 있습니다. 뉴턴의 운동 법칙(F=ma)을 코드로 옮기면 중력, 바람, 마찰 등 현실적인 움직임을 만들 수 있습니다.

javascript
// 물리 기반 공 튀기기
let position, velocity, acceleration;
let gravity;

function setup() {
  createCanvas(800, 600);
  position = createVector(width / 2, 100);
  velocity = createVector(2, 0);
  gravity = createVector(0, 0.3); // 중력
}

function draw() {
  background(20);

  // 물리 업데이트: 가속도 -> 속도 -> 위치
  velocity.add(gravity);       // 속도에 중력 적용
  position.add(velocity);       // 위치에 속도 적용

  // 바닥 충돌
  if (position.y > height - 20) {
    position.y = height - 20;
    velocity.y *= -0.8;  // 반발 (에너지 손실 20%)
  }

  // 벽 충돌
  if (position.x > width - 20 || position.x < 20) {
    velocity.x *= -1;
  }

  // 공 그리기
  fill(255, 100, 150);
  noStroke();
  ellipse(position.x, position.y, 40, 40);
}

파티클 시스템

javascript
let particles = [];

function setup() {
  createCanvas(800, 600);
}

function draw() {
  background(10, 20, 30);

  // 마우스 위치에서 파티클 생성
  if (mouseIsPressed) {
    for (let i = 0; i < 3; i++) {
      particles.push({
        pos: createVector(mouseX, mouseY),
        vel: p5.Vector.random2D().mult(random(1, 4)),
        life: 255,
        size: random(4, 12),
        hue: random(360),
      });
    }
  }

  // 파티클 업데이트 및 렌더링
  colorMode(HSB, 360, 100, 100, 255);
  noStroke();

  for (let i = particles.length - 1; i >= 0; i--) {
    let p = particles[i];
    p.vel.y += 0.05; // 중력
    p.pos.add(p.vel);
    p.life -= 3;

    fill(p.hue, 80, 100, p.life);
    ellipse(p.pos.x, p.pos.y, p.size);

    if (p.life <= 0) particles.splice(i, 1);
  }
}
javascript
// 인력/척력 시뮬레이션
let attractor;
let movers = [];

function setup() {
  createCanvas(800, 600);
  attractor = createVector(width / 2, height / 2);

  for (let i = 0; i < 50; i++) {
    movers.push({
      pos: createVector(random(width), random(height)),
      vel: createVector(0, 0),
      mass: random(1, 5),
    });
  }
}

function draw() {
  background(10, 25);
  attractor.set(mouseX, mouseY);

  // 인력 중심 표시
  fill(255, 50, 50);
  noStroke();
  ellipse(attractor.x, attractor.y, 20);

  for (let m of movers) {
    // 인력 계산 (뉴턴 만유인력)
    let force = p5.Vector.sub(attractor, m.pos);
    let dist = constrain(force.mag(), 5, 50);
    force.normalize();
    let strength = (10 * m.mass) / (dist * dist);
    force.mult(strength);

    // F = ma -> a = F/m
    let acc = p5.Vector.div(force, m.mass);
    m.vel.add(acc);
    m.vel.mult(0.99); // 마찰
    m.pos.add(m.vel);

    fill(100, 200, 255, 200);
    ellipse(m.pos.x, m.pos.y, m.mass * 6);
  }
}
  • createVector(x, y) — 2D 벡터 생성 (위치, 속도, 힘 표현)
  • vec.add(other) — 벡터 덧셈 (속도를 위치에 더하기 등)
  • vec.mult(scalar) — 벡터 스칼라 곱 (힘의 크기 조절)
  • p5.Vector.sub(a, b) — 새 벡터 반환 (방향 계산)
  • p5.Vector.random2D() — 무작위 방향의 단위 벡터
  • constrain(val, min, max) — 값의 범위 제한
💡

물리 시뮬레이션에서 마찰을 적용하려면 velocity.mult(0.99)처럼 속도에 1보다 작은 값을 곱합니다. 이 값이 1에 가까울수록 마찰이 적고, 0에 가까울수록 빠르게 멈춥니다.