Recent Posts
Recent Comments
«   2024/07   »
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
Today
Total
관리 메뉴

DH의 개발 공부로그

[TypeScript] JavaScript 슈팅게임에 TypeScript 적용하기! #7 - 마무리 본문

TypeScript

[TypeScript] JavaScript 슈팅게임에 TypeScript 적용하기! #7 - 마무리

DeveloperDH 2023. 3. 3. 20:57
728x90

Key Event

키이벤트는 원래 keyEvent함수 안에서 onKeyUponKeyDown을 한번에 해주었는데,
게임이 끝나고 초기화를 해줄 때 키이벤트를 삭제해주기 위해서 세분화 했습니다.
onKeyUponKeyDownevent인자를 받는데 그 타입은 KeyboardEvent로 지정을 해주었습니다.

private keyEvent(): void {
  if (!gameOver) {
    document.addEventListener("keydown", this.onKeyDown);
    document.addEventListener("keyup", this.onKeyUp);
  }
}
private onKeyUp = (event: KeyboardEvent): void => {
  if (event.code === "Space") {
    this.createBullet(); // 총알 생성
  }

  delete keysDown[event.key];
}
private onKeyDown = (event: KeyboardEvent): void => {
  keysDown[event.key] = true;
}

총알 & 운석 생성

총알과 운석 생성 함수에서는 Type Narrowing을 이용해서 쉽게 오류를 해결했습니다.

private createBullet(): void {
  const { spaceShip } = this;

  let b = new Bullet();
  if (spaceShip instanceof SpaceShip) {
    b.init(spaceShip);
  }
}
private createMeteor(): void {
  interval = setInterval(() => {
    let meteor = new Meteor();
    meteor.init(this.canvas.width);
  }, 1000);
}

update() & quit()

update()도 마찬가지로 Type Narrowing을 이용해서 쉽고, 간단하게 변환을 하였습니다.
게임이 종료되면 실행이 될 quit()은 다음과 같이 초기화 해주었습니다.

private update(): void {
  const { spaceShip } = this;
  if (spaceShip instanceof SpaceShip) {
    if ("ArrowRight" in keysDown) {
      spaceShip.x += 4;
      // right
    };

    if ("ArrowLeft" in keysDown) {
      spaceShip.x -= 4;
      // left
    };

    if ("ArrowUp" in keysDown) {
      spaceShip.y -= 4;
      // right
    };

    if ("ArrowDown" in keysDown) {
      spaceShip.y += 4;
      // left
    };

    if (spaceShip.x <= 0) {
      spaceShip.x = 0;
    } else if (spaceShip.x >= this.canvas.width - 57) {
      spaceShip.x = this.canvas.width - 57;
    };

    if (spaceShip.y <= 0) {
      spaceShip.y = 0;
    } else if (spaceShip.y >= this.canvas.height - 65) {
      spaceShip.y = this.canvas.height - 65;
    };
  }

  for (let i = 0; i < bulletList.length; i++) {
    if (bulletList[i].alive) {
      bulletList[i].update();
      bulletList[i].checkHit();
    } else {
      bulletList.splice(i, 1);
    };
  };

  for (let i = 0; i < meteorList.length; i++) {
    if (spaceShip instanceof SpaceShip) {
      meteorList[i].update(spaceShip, this.canvas.height);
    }
  };

  if (spaceShip instanceof SpaceShip) {
    // 레벨 체크
    spaceShip.update();
  }
}
private quit(): void {
  clearInterval(interval);
  game = null;
  this.spaceShip = null;
  document.removeEventListener("keydown", this.onKeyDown);
  document.removeEventListener("keyup", this.onKeyUp);
  score = 0;
  bulletList = [];
  meteorList = [];
  gameOver = false;
  keysDown = {}
}

addEventListener()

마지막으로 게임을 실행하고, 게임설명서를 보기 위한 버튼에 addEventListener에는
Optional Chaining 문법을 이용해서 작업을 하였습니다.
아래의 코드들은 Game클래스에 속해있는 것이 아니며, 조건문으로 감싸기 보다는,
Optional Chaining을 이용해서 원하는 DOM객체에 접근했을 때 undefined 또는 null
아닌 경우에 실행 하는 것이 좋은 코드라고 생각하여 다음과 같이 작업을 했습니다.

$gameBtn?.addEventListener("click", () => {
  game = new Game();
});

$manualBtn?.addEventListener('click', () => {
  if ($manualScreen instanceof HTMLDivElement) {
    $manualScreen.classList.add('on');
  }
})

$okBtn?.addEventListener('click', () => {
  if ($manualScreen instanceof HTMLDivElement) {
    $manualScreen.classList.remove('on');
  }
})
728x90
Comments