개발일지/TIL(Today I Learned)

2024-12-26

프린스 알리 2024. 12. 26.

내일배움캠프 Node.js 트랙 43일차

<타워 디펜스> 클라이언트 로직 작성하기

게임 화면 - 와이어 프레임

개발 체크리스트

  • 피버 타임 구현 ✅ 2024-12-25 => 구현 방식 : 기존 데미지의 1.5배, 기존 사정거리의 1.2배
  • 피버 타임 게이지 바 구현 ✅ 2024-12-25
  • 첫 타워는 중간값에. 그 다음에 구매하는 타워는 좌표를 지정해서 설치해야 한다. ✅ 2024-12-25
  • 타워 설치할 때 범위 표시 ✅ 2024-12-25 => 마우스오버를 하면 범위가 표시되게
  • 설치가 안 될 때는 색깔로 알 수 있게 구현
  • 타워 구매 대기열 UI 제작(랜덤한 타워가 아래쪽 UI에 생성되도록) ✅ 2024-12-25
  • 타워 자세히 보기 UI 제작(타워 정보 표시, 업그레이드와 판매 버튼 출력) ✅ 2024-12-26
  • 필요한 핸들러 이벤트가 있다면 팀원에게 보고

(1) 코드 구조 훑어 보기

(2) 피버 타임 구현

let feverTriggered = false; // 피버 모드 실행 여부를 확인하는 플래그

// towerControl에 정의된 tower 배열에 대해 메서드 실행
towerControl.towers.forEach(async (tower) => {
    tower.draw();
    tower.updateCooldown();
    // ... 중략 ...
        if (!feverTriggered && killCount === 20 && killCount !== 0) {
    towerControl.towers.forEach(async (tower) => {
        feverTriggered = true;
        console.log("fever time start");
        await tower.feverTime();
        console.log("fever time end");
        killCount = 0; // killCount 초기화
        ableToMoveRound = true;

        // 피버 모드가 끝난 후 플래그 초기화
        setTimeout(() => {
            feverTriggered = false;
        }, 5000); // feverTime 메서드 실행 시간과 일치하도록 설정
    });
}

(1) feverTriggered는 함수가 중복 실행되는 것을 막기 위해 정의된 플래그이다. (프레임마다 게임 루프가 돌고 있어서 플래그가 없으면 중복 실행 위험이 커진다!)

(2) killCount가 20일 때 tower.feverTime()  메서드를 실행한다.
(3) killCount를 초기화 하고 5초 후에 feverTriggered를 초기화한다.

async feverTime() {
    this.feverMode = true;

    this.damage = 1.5 * this.originalDamage;
    this.range = 1.2 * this.originalRange;

    return new Promise((resolve) => {
        setTimeout(() => {
            this.damage = this.originalDamage;
            this.range = this.originalRange;
            this.feverMode = false;

            resolve();
        }, 5000);
    });
}

(1) tower.feverTime() 메서드가 실행될 때 feverMode 플래그를 true로 설정한다.

(2) feverMode가 true인 시간 동안 타워의 데미지가 1.5배가 되고 타워의 사정거리가 1.2배가 된다.
(3) feverMode 플래그는 5초 후에 false로 초기화된다.(데미지와 사정거리도 초기화)

(3) 타워 구매 대기열 구현하기

getTowerqueue() {
    if (this.towerqueue.length === 5) {
        return this.towerqueue;
    }

    while (this.towerqueue.length < 5) {
        const index = this.getRandomNumber(0, this.towerImages.length - 1);
        this.towerqueue.push({
            image: this.towerImages[index],
            name: towerData.data[index].name,
            cost: towerData.data[index].cost,
        });
    }
}

drawqueue(ctx, canvas) {
    // ... 생략 ...
    // 이미지 그리기
    this.towerqueue.forEach((tower, index) => {
        ctx.drawImage(tower.image, currentX, queueY, imageWidth, imageHeight);

        ctx.font = "16px Arial";
        ctx.fillStyle = "white";

        // 비용 텍스트
        ctx.fillText(
            `${tower.cost}G`,
            currentX + imageWidth + textOffsetX, // 이미지 오른쪽
            queueY + 40 // 이미지 높이에 맞게 조정
        );

        // 이름 텍스트
        ctx.fillText(
            `${tower.name}`,
            currentX + imageWidth + textOffsetX, // 이미지 오른쪽
            queueY + 70 // 두 번째 줄 (간격 추가)
        );

        // 영역 업데이트
        currentX += imageWidth + towerPadding;
    });
}

(1) 타워 에셋 배열의 길이를 기준으로 랜덤한 숫자를 생성한다.
(2) (1)에서 구한 숫자를 index로 활용하여 towerqueue에 대기열에 삽입할 타워 이미지를 생성한다.

buyqueueTower(x, y, queueIndex) {
    const towerName = this.towerqueue[queueIndex].name;
    const index = towerData.data.findIndex((data) => data.name === towerName);

    const image = this.towerImages[index];
    const damage = towerData.data[index].damage;
    const range = towerData.data[index].range;
    const cost = towerData.data[index].cost;
    const type = towerData.data[index].type;
    const id = this.id;
    const newTower = new Tower(
        this.ctx,
        x,
        y,
        damage,
        range,
        cost,
        image,
        type,
        id
    );

    this.id++;

    return newTower;
}

(3) HTML의 캔버스에서 UI 창을 구현하고, 타워 이미지를 클릭하면 buyqueueTower 메서드가 실행된다.
(4) buyqueueTower 메서드에서 생성된 newTower 객체가 새로운 타워로서 반환된다.

(3) 타워 정보창 구현하기

// game.js
// 타워를 클릭했을 때 자세히 보기 창을 띄우기
if (tower.isClicked) {
    tower.showTowerInfo();
}

// tower.js
showTowerInfo() {
    const infoX = this.x - this.width - 10; // 타워 왼쪽에 표시
    const infoY = this.y;

    this.ctx.fillStyle = "rgb(0, 0, 0)";
    this.ctx.fillRect(infoX, infoY, 180, 150); // 정보창 배경

    this.ctx.fillStyle = "white";
    this.ctx.font = "14px Arial";
    this.ctx.fillText(`ID: ${this.id}`, infoX + 10, infoY + 20);
    this.ctx.fillText(`Damage: ${this.damage}`, infoX + 10, infoY + 40);
    this.ctx.fillText(`Range: ${this.range}`, infoX + 10, infoY + 60);
    this.ctx.fillText(`Level: ${this.level}`, infoX + 10, infoY + 80);

    // 업그레이드 버튼
    this.ctx.fillStyle = "rgb(255, 255, 255)";
    this.ctx.fillRect(infoX + 10, infoY + 100, 80, 20);
    this.ctx.fillStyle = "black";
    this.ctx.fillText("UPGRADE", infoX + 15, infoY + 115);

    // 판매 버튼
    this.ctx.fillStyle = "rgb(255, 255, 255)";
    this.ctx.fillRect(infoX + 110, infoY + 100, 50, 20);
    this.ctx.fillStyle = "black";
    this.ctx.fillText("SELL", infoX + 115, infoY + 115);
}

(1) 타워 이미지를 클릭하면 tower.isClicked 플래그가 활성화된다.
(2) 게임 루프가 실행되던 도중에 tower.isClicked가 활성화되면 tower.showTowerInfo() 메서드를 실행한다.
(3) 타워의 정보창이 캔버스 위에 그려진다.

'개발일지 > TIL(Today I Learned)' 카테고리의 다른 글

2024-12-27  (1) 2024.12.27
2024-12-24  (0) 2024.12.24
2024-12-23 <복잡한 IOCP 쉽게 이해하기>  (4) 2024.12.23
2024-12-22 <전송 계층>  (1) 2024.12.22
2024-12-20 <트러블 슈팅>  (1) 2024.12.20

댓글