Silverback9

#야생으로

Creative Coding 독학 제148일 2024년08월22일(목)

오늘은 (1) vehicle의 미래의 위치와 도로의 중앙선 사이의 거리와 (2) 도로의 폭 사이의 관계성을 관찰하여, vehicle이 도로 폭 안에서 움직이도록 하는 작업부터 시작해 보겠습니다~^^*

어제의 핵심코드에서 연결하여 공부할 것이라, 아래 동영상 강의를 처음부터 다시 또 보셔도 좋을 것 같습니다! 복습! 바람직하지 말입니다!! 오늘 이 동영상 강의를 끝내게 되네요!! 와우!!

vehicle의 미래의 위치와 도로의 중앙선 사이의 거리가 도로의 반지름보다 크면?

네~^^* vehicle이 미래 시점에 도로 폭 밖으로 벗어나게 되는 것이 예상되네요!

그러면, 문제 해결법은?

그렇죠~~^^*

도로의 중앙선을 목표로 몸을 돌리면 되겠네요?

vehicle의 미래의 위치에서 도로 중앙선 위로 수직으로 내려간 지점을 목표로, 지금 바로 몸을 돌리면, 미래 시점에 도로 폭 안에 들어가 있을 수 있겠지요?

바로 그 지점이?

그렇죠~~^^*

Scalar Projection! 그리고 어제 우리는 이 지점을 몸을 틀어 향할 목표! target이라고 부르기로 했었어요~~

vehicle의 미래 위치와 도로의 중앙선 사이의 거리가 도로의 반지름보다 작으면?

미래 시점에도 vehicle이 도로 폭 안에 들어와 있는 것이니까~ 편안히 쉬면 되어요~~^^*

그러면 이 과정을 코드로 작성해 볼게요~~^^*


    let d = p5.Vector.dist(future, target);
    if (d > path.radius) {
      return this.seek(target);
    } else {
      return createVector(0, 0);
    }

target 위치로 다가가는 함수 seek()입니다~^^*

seek(target) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.maxSpeed;
  
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.maxForce);
    return force;
  }

음 근데요…. 일정이 있는 곳 근처 까페에서 아침 일찍부터 작업을 했는데…오늘 왠일인지 저의 컴퓨터가 와이파이를 잘 받지 못하네요….

오후에 와이파이 잘 받을 수 있는 곳에서 다시 돌아올게요!

오늘도 멋진 아침! 보내시구요! 우리는 오후에 다시 만나요~~~^^*

네~~^^* 와이파이를 제 컴퓨터가 잘 수용하는 곳으로 왔습니다~^^* 점심 맛있게 드셨어요~~^^*?

이제 상호작용 요소를 준비해 보기로 하겠습니다~^^*

마우스/손끝으로 화면 속 도로의 끝 위치를 조정해 보도록 할게요~^^* 그러면 path following이 좀더 재미있어지겠지요~~^^*

  path.end.y = mouseY;

도로 폭 안에서 움직이고, 도로는 우리의 손끝을 따라 기울기를 달리하고~^^*

재미있는 path follwing 전체 코드를 저와 함께 살펴 볼까요~~^^*

알고리즘과 핵심코드에 대해 자세히 살펴보았으니, 전체 코드 안에 주석을 넣지 않도록 할게요~~^^*

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <script src="path.js"></script>
    <script src="vehicle.js"></script>
    <script src="sketch.js"></script>
  </body>
</html>
class Path {
  constructor(x1, y1, x2, y2) {
    this.start = createVector(x1, y1);
    this.end = createVector(x2, y2);
    this.radius = 20;
  }

  show() {
    stroke(255);
    strokeWeight(2);
    line(this.start.x, this.start.y, this.end.x, this.end.y);

    stroke(255, 100);
    strokeWeight(this.radius * 2);
    line(this.start.x, this.start.y, this.end.x, this.end.y);
  }
}
function findProjection(pos, a, b) {
  let v1 = p5.Vector.sub(a, pos);
  let v2 = p5.Vector.sub(b, pos);
  v2.normalize();
  let sp = v1.dot(v2);
  v2.mult(sp);
  v2.add(pos);
  return v2;
}

class Vehicle {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = 6;
    this.maxForce = 0.1;
    this.r = 16;
  }

  follow(path) {
  
    let future = this.vel.copy();
    future.mult(20);
    future.add(this.pos);
    fill(255, 0, 0);
    noStroke();
    circle(future.x, future.y, 16);

   
    let target = findProjection(path.start, future, path.end);
    fill(0, 255, 0);
    noStroke();
    circle(target.x, target.y, 16);

    let d = p5.Vector.dist(future, target);
    if (d > path.radius) {
      return this.seek(target);
    } else {
      return createVector(0, 0);
    }
  }

  seek(target) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.maxSpeed;
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.maxForce);
    return force;
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  show() {
    stroke(255);
    strokeWeight(2);
    fill(255);
    push();
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    triangle(-this.r, -this.r / 2, -this.r, this.r / 2, this.r, 0);
    pop();
  }

  edges() {
    if (this.pos.x > width + this.r) {
      this.pos.x = -this.r;
    } else if (this.pos.x < -this.r) {
      this.pos.x = width + this.r;
    }
    if (this.pos.y > height + this.r) {
      this.pos.y = -this.r;
    } else if (this.pos.y < -this.r) {
      this.pos.y = height + this.r;
    }
  }
}
let vehicle;
let path;

function setup() {
  createCanvas(400, 400);
  vehicle = new Vehicle(100, 100);
  vehicle.vel.x = 2;
  path = new Path(0, height / 2, width, height / 2);
}

function draw() {
  background(0);

  path.end.y = mouseY;

  let force = vehicle.follow(path);
  vehicle.applyForce(force);

  vehicle.edges();
  vehicle.update();
  vehicle.show();

  path.show();
}

자 그럼 이제! 드디어!

마우스/손가락을 움직여 도로의 각도를 조정하면서 vehicle이 도로 폭 안에 잘 들어오는지 관찰해 볼까요~~^^*

Path를 잘 Follow 하다보니, 어머나! Path Following 프로그램을 완성하였네요!!!

오늘도 저와 함꼐 작은 성공을 완성해 주셔서 감사합니다~~^^*

네! 마스터께서 여기서 안주하지 말고 여정을 계속 이어가라고 하시네요!

네! 다음 도전을 우리 함께 내일 맞이해 볼까요!

혹여 다음 도전이 좀 큰 산이라 오르는데 숨이 차오르게 된다고 해도!

우리 이 노래를 함께 들으며 또 다른 작은 성공을 만들어 내 볼까요~?

오늘 하루도 수고 많으셨어요~~^^* 편안한 저녁 보내시구요~^^*

우리 내일 아침 일찍 만나서 코딩 공부 함께 하는 거에요~~^^*

넵!!!! 꿈은 이루어 집니다~~!!!!

댓글 남기기