Silverback9

#야생으로

Creative Coding 독학 제323일 2025년02월13일(목)

오늘은 태초의 vehicle 부족의 서식처를 좀더 풍성하게 만드는 날~^^* Yeah~^^*

풍성한 사운드로 가득한 아침을 자유롭게 비행하고 계셔요~^^* 공부 정리해서 돌아올게요~^^* 쓩우웅~^^*

네~^^* 태초의 vehicle 부족이 살아가는 서식처의 생태계를 좀더 풍성하게 만들기 위해서~^^* 가끔씩~^^* food를 생태계에 더 보태어 볼게요~^^*

5% 확률로 food가 보태어지면 좀 더 자연스러운 생태계가 될까요~^^*

function draw() {
   .
   .
   .
  if( random(1) < 0.05) {
     var x = random(width);
     var y = random(height);
     food.push(createVector(x, y));
  }
//0에서 1 사이의 무작위 수를 추출하여 만약 그 수가 0.05보다 작다면
//0에서 캔버스 너비 사이의 무작위 수를 추출하여 x축 좌표로 삼고
//0에서 캔버스 높이 사이의 무작위 수를 추출하여 y축 좌표로 삼아
//(x, y) 위치 좌표를 가진 벡터를 생성하여
//배열 food[]의 구성요소로 추가합니다~^^*   
   .
   .
}
 

태초의 vehicle 부족의 움직임을 좀더 빠르게 하여 되도록 음식을 많이 먹을 수 있도록 해 보겠습니다. 0.05 확률로 food가 생태계에 지속적으로 보태어 집니다.

음….독약과 음식의 숫자가 같다보니….태초의 부족이 살아남기가 참…어렵네요…

사실 독약은 쉽게 볼 수 있는 것은 아니니까요…

음식의 숫자를 늘이고 독약의 숫자는 줄여서, 좀더 자연스러운 생태계를 표현해 보겠습니다~^^* food 40개 poison 20개 생성해 보겠습니다~^^*

function setup() {
   .
   .
   . 
  for( var i = 0; i < 40; i++) {
    var x = random(width);
    var y = random(height);
    food.push(createVector(x, y));
  }
  
  for( var i = 0; i < 20; i++) {
    var x = random(width);
    var y = random(height);
    poison.push(createVector(x, y));
  }
   .
   .
   .
}

좀더 자연스러운 생태계를 관찰해 볼까요~^^* 여러번 플레이를 해보시면 food와 poison에 대한 다양한 초기 기억을 가진 태초의 vehicle 부족의 다양한 생애 패턴을 관찰해 볼 수 있을 것 같아요~~^^*

네~^^* 이제, 전체 코드를 복습 삼아 살펴 보셔도 좋을 것 같습니다~^^*

class Vehicle {
  constructor(x, y) {
    this.acceleration = createVector(0, 0);
    this.velocity = createVector(0, -2);
    this.position = createVector(x, y);

    this.health = 1;

    this.r = 4;
    this.maxspeed = 4;
    this.maxforce = 0.2;
    
    this.dna = [];
    this.dna[0] = random(-5, 5);
    //food에 대한 멀어지고픔 최대치 -5 끌림 최대치 +5 사이 무작위 값
    this.dna[1] = random(-5, 5);
    //poison에 대한 멀어지고픔 최대치 -5 끌림 최대치 +5 사이 무작위 값
  }


  update() {
    
    this.health -= 0.005;
    //건강상태 지수는 계속 낮아집니다.
    
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.maxspeed);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
  }

  applyForce(force) {
    this.acceleration.add(force);
  }
  
  behaviors(good, bad) {
     const steerG = this.eat(good, 0.1);
     //몸에 좋은 음식에 대한 반응행동 벡터 steerG입니다.
     //건강 상태 지수가 0.1 증가하겠네요~^^*
     const steerB = this.eat(bad, -0.5);
     //몸에 나쁜 음식에 대한 반응행동 벡터 steerB입니다.
     //건강 상태 지수가 0.5 하겠네요...

     steerG.mult(this.dna[0]);
     //몸에 좋은 것에 대한 기억인 dna[0]에 저장된 값을 좋은 음식에 대한 반응행동 벡터 steerG의 크기에 곱하겠습니다.  
     steerB.mult(this.dna[1]);
     //몸에 나쁜 것에 대한 기억인 dna[1]에 저장된 값을 나쁜 음식에 대한 반응행동 벡터 steerB의 크기에 곱하겠습니다. 

     this.applyForce(steerG);
     this.applyForce(steerB);
      //vehicle의 행동에 이 반응벡터 steerG와 steerB가 영향을 끼칩니다.     
  }
  
   eat(list, nutrition) {
    var record = Infinity;
    var closest = -1;
    for (var i = 0; i < list.length; i++) {
      var d = this.position.dist(list[i]);
      if(d < record) {
        record = d;
        closest = i;
      }
    }
    
    if(record < 5) {
      list.splice(closest, 1);
      this.health += nutrition;
      //영양가를 건강상태 지수에 더합니다.
      //몸에 좋은 음식의 영양가는 건강상태 지수를 높이고
      //몸에 위험한 독약의 영양가는 건강상태 지수를 낮춥니다. 
    } else if(closest > -1) {
      return this.seek(list[closest]);  
    }
    return createVector(0,0);
  }
  
  //가장 가까운 거리에 대한 신기록 record는 앞으로 점점 수치가 작아질 것이므로, 시작하기 전까지는 알 수 없는 가장 큰 값 Infinity 무한대 값으로 초기 설정하겠습니다~^^*
  //배열 list[]의 첫 구성요소 list[0]도 살펴봐야 하기 때문에, 가장 가까운 list 구성요소의 인덱스를 표현할 closest는 -1로 초기 설정하겠습니다. 
  //"IF"가장 가까운 거리에 대한 신기록 record가 5 pixel보다도 작다면, 정말 가까운 것이니까, "제가 한 번 먹어 보도록 하겠습니다~" 그런 후, 아무 행동 없는 (0,0) 벡터를 반환하겠습니다. 
  //list의 closest번째부터 구성요소 하나만을 제거하도록 하겠습니다. 즉, closest 번째 구성요소를 제거하겠습니다.
  //함수 splice()에 의해 제거된 구성요소 다음의 구성요소들이 차례로 앞으로 한 칸씩 이동하여 배열이 정돈될 것입니다~^^*
  //"ELSE IF", 즉, 5 픽셀 반경 안에 list[]의 구성요소가 없는 경우에는 
  // else if의 조건에 따라~^^* cloest가 -1보다 큰 경우만 
   //vehicle과 가장 가까운 거리에 있는 list의 구성요소인 list[closest]를 추구하도록 함수 seek를 호출하는 작업을 retur다하겠습니다~^^*
  //무한 반복 함수 draw()에 의해 함수 eat()가 계소 호출될 것입니다. 
  //더이상 배열 list[]에 구성요소가 남아있지 않게되면, for 구문이 작동되지 않습니다. "i = 0 < list.length == 0", 즉, "0 < 0" 조건이 만족될 경우가 없기 때문입니다.  
  //그래서 closest는 -1의 상태를 유지하게 됩니다. 
  //그러면 if문의 조건도 else if문의 조건도 해당되지 않아서, 먹지도 않고 탐색하지도 않고 아무 움직임이 없는 (0,0) 행동벡터를 반환합니다.

//<오늘의 주요 변화 요약>
//먹거리가 5 pixel 거리 안에 없지만, 먹거리 배열에 구성요소가 남아있을 경우, 가장 가까운 구성요소를 향해서 몸을 트는 행동 벡터 this.seek(list[closest]);를 반환합니다. 
//먹거리가 5 pixel 거리 안에 있으면, 먹거리 배열에서 그 먹거리를 삭제한 후 아무 움직임도 없는(0,0) 행동벡터를 반환합니다. 
//먹거리 배열에 아무 구성요소가 없는 경우에도 아무 행동이 없는 (0,0) 행동벡터를 반환합니다.  
  
//<nutrition 영양가와 this.health 건강상태 지수 관계>
// positive 값을 가진 nutrition은 this.health 값을 증가시키고
// negative 값을 가진 nutrition은 this.health 값을 감소시킵니다. 

 seek(target) {
    const desired = p5.Vector.sub(target, this.position); 
    desired.setMag(this.maxspeed);
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxforce); 
    return steer;
  }  
//target으로 몸을 트는 벡터를 생성하여 변수 steer에 저장한 후 maxforce 값 안으로 힘의 크기를 조정하여 steer를 반환합니다.  
  
  dead() {
    return (this.health < 0);
  }
//vehicle의 health 값이 0보다 작은 경우에 진리값 "true 참"을 반환합니다. 
//vehicle의 health 값이 0 이상인 경우에 진리값 "false 거짓"을 반환합니다.

  display() {
    let angle = this.velocity.heading() + PI / 2;
    
    push();
    translate(this.position.x, this.position.y);
    rotate(angle);
    
    
    stroke(0, 255, 0);
    line(0, 0, 0,-this.dna[0] * 20);
    stroke(255, 0, 0);
    line(0, 0, 0,-this.dna[1] * 20);
    
    var rd = color(255, 0, 0);
    var gr = color(0, 255, 0);
    var col = lerpColor(rd, gr, this.health);
    
    fill(col);
    stroke(col);
    strokeWeight(1);
    
    beginShape();
    vertex(0, -this.r * 2);
    vertex(-this.r, this.r * 2);
    vertex(this.r, this.r * 2);
    endShape(CLOSE);
    pop();
    
  }
}

var vehicles = [];
var food = [];
var poison = [];

function setup() {
  createCanvas(400, 400);
  
  for( i = 0; i < 10; i++) {
    var x = random(width);
    var y = random(height);  
    vehicles[i] = new Vehicle(x, y);
  }
  
  for( var i = 0; i < 10; i++) {
    var x = random(width);
    var y = random(height);
    food.push(createVector(x, y));
  }
  
  for( var i = 0; i < 10; i++) {
    var x = random(width);
    var y = random(height);
    poison.push(createVector(x, y));
  }
}

function draw() {
  background(51);
  
  if( random(1) < 0.05) {
     var x = random(width);
     var y = random(height);
     food.push(createVector(x, y));
  }
//0에서 1 사이의 무작위 수를 추출하여 만약 그 수가 0.05보다 작다면
//0에서 캔버스 너비 사이의 무작위 수를 추출하여 x축 좌표로 삼고
//0에서 캔버스 높이 사이의 무작위 수를 추출하여 y축 좌표로 삼아
//(x, y) 위치 좌표를 가진 벡터를 생성하여
//배열 food[]의 구성요소로 추가합니다~^^* 
  
  for(var i = 0; i < food.length; i++) {
    fill(0, 255, 0);
    noStroke();
    ellipse(food[i].x, food[i].y, 8, 8);
  }
  
  for(var i = 0; i < poison.length; i++) {
    fill(255, 0, 0);
    noStroke();
    ellipse(poison[i].x, poison[i].y, 8, 8);
  }
  
  for( i = vehicles.length -1; i >= 0; i--) {
    vehicles[i].behaviors(food, poison);
    vehicles[i].update();
    vehicles[i].display();
  
  
  if (vehicles[i].dead()) {
    vehicles.splice(i, 1);
  }
//만약 vehicles[i].dead()가 "true 참"을 반환한다면, 
//vehicles[i]를 시작으로 1개의 구성요소를 배열 vehicles[]에서 삭제하고 뒤에 있는 구성요소들을 한 칸씩 앞으로 당깁니다. 
//즉, vehicles[i]를 vehicles[]에서 삭제하고 나머지 뒷부분 구성요소들을 한 칸씩 당겨 배치하여 배열 vehicles[]를 재정렬합니다.   
  }
}

네~^^* 우리가 또 동영상 강의 하나를 마무리 해내었네요~~!!!

태초의 vehicle 부족 중 생존자가 나타난다면, 그리고 그 생존자의 삶의 경험이 다음 세대의 dna에 심어진다면, 좀 더 생존력 강한 부족으로 성장하게 될 것 같아요~~!!!

네~^^* 오늘 저와 함께~~^^* 태초의 vehicle 부족의 생태계를 더욱 풍성하게 그리고 더욱 자연스럽게 표현해 주셔서 감사합니다~~^^*

내일부터는 우리~~^^* 다음 세대에 대해서도 공부해 볼까요~~^^*

네~~^^* 좋아요~~^^* 고마워요~~^^*

오늘도

멋진 아침~

맛있는 점심과 따뜻한 저녁~

포근하고 깊은 밤 코~^^* 하시기 바래요~^^*

네~^^* 꿈은 이루어 집니다~^^*

댓글 남기기