티스토리 뷰
javascript RTS Game 에서 유닛끼리 겹쳐지지 않게 설정하기 && chatGPT의 활용
YG - 96년생 , 강아지 있음, 개발자 희망 2023. 1. 9. 13:18
기능 목록
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
'자바스크립트' 카테고리의 다른 글
Node.js와 ws로 직접 WebSocket 서버 만들기 (0) | 2023.01.15 |
---|---|
HTTP vs WebSockets 의 특징 및 차이점 (0) | 2023.01.14 |
javascript ERROR Handler Function 템플릿 (0) | 2022.12.16 |
Prettier + ESLint + Airbnb Style을 package.json 변경없이 셋팅하는 방법 (0) | 2022.12.12 |
[23년 업데이트] graphQL에 관하여 // graphQL 이용해보기 (0) | 2022.03.27 |
- Total
- Today
- Yesterday
- React
- javascript
- Storybook
- 노개북
- nextjs
- 아차산
- nodejs
- 우아한테크코스
- CLASS
- NextApiRequest
- 스토리 북
- jest
- error
- 노마드코더
- 프리온보딩
- NextRequest
- env
- createPortal
- import/order
- TopLayer
- 초보
- 윤성우 열혈C프로그래밍
- 원티드
- WSL2
- C언어
- 위코드
- 프론트앤드
- 북클럽
- electron
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |