티스토리 뷰

현재 개발중인 RTS 기반의 게임

 

기능 목록

9. 유닛 이동

- [ ] 유닛이 겹쳐지면 안되고 각자의 크기대로 공간을 차지한다.

  - [x] 유닛을 생성할 때 가운데를 기준으로 겹치지 않게 생성된다.
  - [x] 유닛을 이동할 때 겹쳐지면 안되고 이동하려는 위치 중심으로 움직여야 한다.

 

유닛이 겹쳐지지 않게 생성하고 이동했을 때도 서로 겹쳐지지 않게 기능을 구현하려고 했다.

 

처음에 2차원 배열을 활용한 그리드 기반으로 구현해볼까 했지만 지금 만들려는 게임에는 배열처럼 한 칸 한 칸 움직이는 것이 아닌 스무스하게 움직이게 기능이 되어야 하기 때문에 픽셀 시스템 기반으로 만들어야 했다.

 

따라서 Unit 클래스에서 현재 위치와 겹치는 다른 유닛이 있다면 위치를 조정하도록 기능을 구현했다.

 

Unit.js

class Unit {
  #stDx = -1;
  #stDy = -1;

  #dx = [1, 0, -1, 0];
  #dy = [0, 1, 0, -1];

  constructor(
    { position = { x: 0, y: 0 } },
    canvas,
    animalKind,
    classType,
    gameRound
  ) {
    this.position = position;
    this.width = 16;
    this.height = 16;
    this.radius = 16;
    this.center = {
      x: this.position.x + this.width / 2,
      y: this.position.y + this.height / 2,
    };
  }



// ... 기타 이동 및 공격 코드

  setPosition(newPosition) {
    this.center = newPosition;
    this.position = {
      x: this.center.x - this.width / 2,
      y: this.center.y - this.height / 2,
    };
  }

  findEmptySpace(units) {
    let count = 1;
    let x = this.center.x;
    let y = this.center.y;
    let foundEmptySpace = false;
    while (!foundEmptySpace) {
      let startX = this.center.x + this.radius * count * this.#stDx;
      let startY = this.center.y + this.radius * count * this.#stDy;

      for (let dir = 0; dir < 4; dir += 1) {
        for (let i = 1; i <= count * 2; i++) {
          let overlapping = false;
          for (const unit of units) {
            // console.log(unit.center.x, unit.center.y);
            if (unit !== this && this.isOverlapping(unit, x, y)) {
              overlapping = true;
              break;
            }
          }
          if (!overlapping) {
            foundEmptySpace = true;
          } else {
            startX += this.radius * this.#dx[dir];
            startY += this.radius * this.#dy[dir];

            x = startX;
            y = startY;
          }
        }
      }
      count += 1;
    }
    return { x, y };
  }


  isOverlapping(otherUnit, x, y) {
    const dx = x - otherUnit.center.x;
    const dy = y - otherUnit.center.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return distance < this.radius;
  }
}

export default Unit;

 

 

 

isOverlapping

먼저 isOverlapping부터 살펴보자면 다른 유닛을 매개변수로 받고 확인하고자 하는 x위치와 y위치 또한 받아서 

dx = 확인하고자 하는 x - 다른 유닛의 센터값 x 

dy = 확인하고자 하는 y - 다른 유닛의 센터값 y

 

distance는 피타고라스의 정리를 이용해서 두 위치 상의 거리 값입니다.

 

현재 radius , 자신의 범위보다 두 위치상의 거리가 가깝다면 겹치므로 true, 아니라면 false를 반환하는 함수입니다.

 

  isOverlapping(otherUnit, x, y) {
    const dx = x - otherUnit.center.x;
    const dy = y - otherUnit.center.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return distance < this.radius;
  }

 

이제 isOverlapping 함수를 이용해서 적합한 위치를 반환하는 함수를 이용해야 합니다.

 

 

findEmptySpace

 

findEmptySpace에서는 

 

기능목록에서

- [ ] 유닛을 생성할 때 가운데를 기준으로 겹치지 않게 생성된다.

이 부분을 만족하기 위해 가운데에 생기고 그 주위를 감싸가며 점점 커지는 형태로 구현하려고 하였습니다.

 

따라서 방향은 동서남북 4방향으로 해주었고 dx, dy 배열에 정보를 담았습니다.

 

변수는 dir이며 startX, startY는 시작위치를 정해주었는데 가운데를 기준으로 왼쪽 위를 시작점으로 잡았습니다.

 

가운데를 감싸주려면 몇 번째 칸인지에 따라 반복해야 하는 횟수가 다르기에 count라는 변수를 이용했습니다.

 

첫 번째 회전에서는 총 8칸이 필요합니다.

 

 

let count=1;

 

그리고 dir이 적용되었기 때문에 우선 dir 은 4번 count는 2번이 필요합니다. 

 

dir이 적용되어 오른쪽부터 오른쪽오른쪽 아래아래 왼쪽왼쪽 위위 순서로 진행됩니다.

 

그림으로 표현하자면 이런 그림입니다. 

 

 

그래서 반복문 코드는 다음과 같이 설정했습니다.

for(let dir=0;dir<4;dir+=1){
	for(let repeat=1;repeat<=count*2;repeat+=1)
    {
    // 코드
    }
}

 

적합한 위치를 찾을 때까지 반복해야 하므로 while문으로 시작하고 적합한 위치를 찾으면 멈추고 x, y 값을 반환하는 함수입니다.

 

이때 아까 만든 isOverlapping을 이용하여 자기 자신이 아니며 겹치는지 확인하며 함수를 이어나갑니다.

 

findEmptySpace(units) {
    let count = 1;
    let x = this.center.x;
    let y = this.center.y;
    let foundEmptySpace = false;
    while (!foundEmptySpace) {
      let startX = this.center.x + this.radius * count * this.#stDx;
      let startY = this.center.y + this.radius * count * this.#stDy;

      for (let dir = 0; dir < 4; dir += 1) {
        for (let i = 1; i <= count * 2; i++) {
          let overlapping = false;
          for (const unit of units) {
            // console.log(unit.center.x, unit.center.y);
            if (unit !== this && this.isOverlapping(unit, x, y)) {
              overlapping = true;
              break;
            }
          }
          if (!overlapping) {
            foundEmptySpace = true;
          } else {
            startX += this.radius * this.#dx[dir];
            startY += this.radius * this.#dy[dir];

            x = startX;
            y = startY;
          }
        }
      }
      count += 1;
    }
    return { x, y };
  }

 

setPosition

이 함수는 findEmptySpace를 이용하여 받은 x, y값으로 유닛의 position을 재설정해주는 함수입니다.

  setPosition(newPosition) {
    this.center = newPosition;
    this.position = {
      x: this.center.x - this.width / 2,
      y: this.center.y - this.height / 2,
    };
  }

 

 

 

 

 

유닛 생성

 유닛을 생성할 때 유닛을 미리 만든 후 적합한 위치인지 확인하여 재설정해줍니다.

// 유닛 생성모드
addEventListener('keydown', (event) => {
  if (event.code === 'Space' && coins >= GAME_DATA.unitPrice) {
    const unit = new Unit(
      {
        position: {
          x: canvas.width / 2,
          y: canvas.height / 2,
        },
      },
      c,
      animalString,
      classString,
      gameRound
    );

    const newPosition = unit.findEmptySpace(units);
    unit.setPosition(newPosition);
    units.push(unit);
  }
});

 

이동 또는 게임 진행 중 항상 체크

 

frames 없이 1 프레임 당 함수를 호출하게 되면 기능저하가 일어나거나 유닛 위치 재정리가 제대로 작동하지 않아서 frames 변수를 이용하여 10 프레임당 호출되도록 하였습니다.

  // 게임 진행 중
  if (frames == 10) {
    units.forEach((unit) => {
      const newPosition = unit.findEmptySpace(units);
      console.log(newPosition);
      unit.setPosition(newPosition);
    });
    frames = 0;
  }

 

 

기능 작동 동영상

 

 

 

 

 

마무리

 

제가 도대체 어떻게 만들어야 할지 모를 때 알려준 사이트를 소개하려고 합니다. 

 

 

이동할 때 유닛끼리 겹치지 않게 하는 지 물어보는 질문
친절한 설명과 작동 원리 코드

 

 

인공지능 AI가 알려준 코드.. 대단하다! 이를 응용해서 제 게임에 맞게 재 구성하였습니다. 인공지능의 성능에 대해 엄청나게 놀랐었고 구글링을 해도 잘 못 찾은 정보를 javascript 언어로 알려준다는 점이 매우 매우 매우 좋았습니다!

class Unit {
  constructor(x, y, radius) {
    this.x = x;
    this.y = y;
    this.radius = radius;
  }

  moveTo(x, y, units) {
    this.x = x;
    this.y = y;
    for (const unit of units) {
      if (unit !== this && this.isOverlapping(unit)) {
        const newPosition = this.findEmptySpace(units);
        this.x = newPosition.x;
        this.y = newPosition.y;
      }
    }
  }

  isOverlapping(otherUnit) {
    const dx = this.x - otherUnit.x;
    const dy = this.y - otherUnit.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return distance < this.radius + otherUnit.radius;
  }

  findEmptySpace(units) {
    let x = this.x;
    let y = this.y;
    let foundEmptySpace = false;
    while (!foundEmpty

 

 

 

 

ChatGPT: Optimizing Language Models for Dialogue

We’ve trained a model called ChatGPT which interacts in a conversational way. The dialogue format makes it possible for ChatGPT to answer followup questions, admit its mistakes, challenge incorrect premises, and reject inappropriate requests. ChatGPT is

openai.com

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함