← Log

I am Ironman: 게임 화면 구현

I am Ironman 게임 개발기· 3 / 7
  1. 1 — I am Ironman: 프로젝트 소개
  2. 2 — I am Ironman: 프로젝트 세팅
  3. 3 — I am Ironman: 게임 화면 구현
  4. 4 — I am Ironman: 콘텐츠 모션과 애니메이션
  5. 5 — I am Ironman: 콘텐츠 물리 충돌 처리
  6. 6 — I am Ironman: 게임 씬 구성
  7. 7 — I am Ironman: 게임 배포와 프로젝트 점검

본격적인 게임 개발을 위해 화면에 표시될 캐릭터와 무기 등 콘텐츠를 생성하고 관리하는 로직을 구현했다. 각 로직은 확장/수정의 용이성을 감안해 최대한 독립적으로 운영될 수 있도록 아키텍처를 설계했다.

콘텐츠 설정

게임에 사용되는 콘텐츠의 이미지와 기본 속성을 설정했다.

이미지

  • 배경

    우주 배경

  • 캐릭터

    아이언맨 울트론 울트론2 바위 울트론3

  • 무기

    리펄서 유니빔 적 공격

기본 속성

  • 타입: 콘텐츠 종류
  • 이동 속도: 게임 프레임 당 이동 거리
  • 체력: 콘텐츠 생명력
  • 모드: 이미지 애니메이션 상태
  • 충돌 감지 영역: 콘텐츠 충돌 감지 범위 정의

콘텐츠 화면 구현

정의된 콘텐츠가 실제 게임 화면에 생성되고 관리되는 과정을 구현했다.

아키텍처

게임 화면에 표시되는 모든 콘텐츠 객체의 생성은 아래와 같은 구조로 이루어져 있다.

  • 콘텐츠 정의 → 생성/관리 → (복수 콘텐츠 관리) → 화면 구현
  1. 콘텐츠 정의(object): 콘텐츠의 특성에 맞는 Phaser의 내장 클래스를 상속받아 클래스에 추가적인 속성을 정의해 커스터마이징한다.
  2. 생성/관리(manager): 콘텐츠를 관리하는 클래스에서 해당 콘텐츠 object 인스턴스를 생성하고 그 내부에서 인스턴스를 관리한다.
  3. 복수 콘텐츠 관리(group): manager에서 복수의 콘텐츠 객체를 관리해야할 경우 생성된 인스턴스의 관리 역할을 group으로 이관한다.
  4. 화면 표시(scene): 콘텐츠의 화면 표시가 필요한 씬에서 manager 인스턴스를 생성해 콘텐츠 생성과 관리 작업을 동시에 처리한다.

코드 작성

  • 콘텐츠 정의

    // objects/characters/ironman.ts
    import Phaser from 'phaser';
    ...
    
    export default class Ironman extends Phaser.GameObjects.Image {
      private _type: HeroType;
      private speed: number;
      private health: number;
      public mode: IronmanMode;
      public collisionZones!: CollisionZoneGroup;
      private sound: { [key: string]: Phaser.Sound.BaseSound } = {};
    
      constructor(scene: Phaser.Scene, x: number, y: number, texture: string) {
        super(scene, x, y, texture);
    
        // 요소 설정
        this._type = HeroType.IRONMAN;
        this.speed = speedConfig.ironman;
        this.health = healthConfig.ironman;
        this.mode = IronmanMode.NORMAL;
        this.sound.repulsor = this.scene.sound.add('repulsor');
    
        scene.add.existing(this); // 화면 구현
    
        const scale = (scene.game.canvas.width * scaleConfig.ironman) / this.width;
        this.setScale(scale); // 이미지 사이즈 비율 설정
    
        this.createCollisionZones();
      }
    
      // 충돌 감지 영역 생성
      private createCollisionZones() {
        ...
      }
    
      // 사운드 재생
      public playSound(mode: IronmanMode) {
        return this.sound[mode].play();
      }
    }

    Phaser.GameObjects.Image를 상속받아 준비된 캐릭터 이미지 객체를 생성하고 게임 내에서 필요한 이동속도, 체력, 기본 모션, 효과음 등 다양한 속성의 초기화를 설정했다. (‘충격 감지 영역’은 다른 문서에서 다룰 예정)

  • 생성/관리

    // managers/characters/ironmanManager.ts
    import Phaser from 'phaser';
    ...
    
    export default class IronmanManager {
      private scene: Phaser.Scene;
      private timerHandler: TimerHandler;
      private ironman!: Ironman;
    
      constructor(scene: Phaser.Scene) {
        this.scene = scene;
    
        this.create();
      }
    
      // 아이언맨 생성
      public create() {
        const gameWidth = this.scene.game.canvas.width; // 현재 화면 너비
        const gameHeight = this.scene.game.canvas.height; // 현재 화면 높이
    
        this.ironman = new Ironman(
          this.scene,
          gameWidth / 4,
          gameHeight / 2,
          ImageTexture.IRONMAN_NORMAL // 이미지 텍스쳐 키값
        );
      }
    
      // 아이언맨 가져오기
      public get() {
        return this.ironman;
      }
    }

    생성된 콘텐츠 객체는 해당 매니저 클래스에서 관리하고 필요에 따라 메서드를 통해 객체를 외부에 제공한다.

  • 복수 콘텐츠 관리

    import GroupManager from '../groupManager';
    ...
    
    export default class EnemyManager {
      private groupManager: GroupManager;
      private enemies: Phaser.GameObjects.Group;
      private enemy: { [key in EnemyType]?: Enemy } = {};
      ...
    
      constructor(
        scene: Phaser.Scene,
        groupManager: GroupManager,
        ...
      ) {
        this.scene = scene;
        this.groupManager = groupManager;
        this.enemies = groupManager.get(GroupType.ENEMIES); // 미리 준비된 그룹 인스턴스 불러오기
    
        for (const enemy of Object.values(EnemyType)) {
          this.create(enemy);
        }
      }
    
      // 빌런 생성
      public create(enemytype: EnemyType) {
        const gameWidth = this.scene.game.canvas.width;
        const gameHeight = this.scene.game.canvas.height;
    
        const textureMap = {
          [EnemyType.ULTRON1]: ImageTexture.ULTRON1_NORMAL,
          [EnemyType.ULTRON2]: ImageTexture.ULTRON2_NORMAL,
          [EnemyType.ULTRON3]: ImageTexture.ULTRON3_NORMAL,
          [EnemyType.ROCK]: ImageTexture.ROCK_NORMAL,
        };
    
        // 빌런 생성
        const enemy = new Enemy(this.scene, 0, 0, textureMap[enemytype], enemytype);
    
        // 빌런 랜덤 위치 설정
        const targetWidth = enemy.displayWidth;
        const targetHeight = enemy.displayHeight;
        const randomY = Phaser.Math.Between(
          targetHeight / 2,
          gameHeight - targetHeight / 2
        );
        enemy.setPosition(gameWidth - targetWidth / 2, randomY);
    
        // 빌런 그룹에 추가
        this.enemies.add(enemy);
      }
    }

    콘텐츠 별로 미리 준비해둔 준비된 그룹 인스턴스(enemies)를 가져오고, 콘텐츠가 생성될 때마다 해당 그룹의 add()메서드로 인스턴스 실체 관리를 맡긴다.

  • 화면 표시

    // scenes/playScene.ts
    import Phaser from 'phaser';
    import IronmanManager from '../managers/charaters/ironmanManager';
    ...
    
    export default class PlayScene extends Phaser.Scene {
      private ironmanManager!: IronmanManager;
      ...
    
      constructor() {
        super({ key: 'PlayScene' });
      }
    
      preload() {}
    
      create() {
        // 아이언맨 관리자
        this.ironmanManager = new IronmanManager(this, ...);
        ...
      }
    
    ...
    

    Phaser.Scene 내장 메서드인 create()는 해당 씬에 시작될 때 1회 호출된다. 이 메서드 내에서 매니저 인스턴스를 생성하면 화면에 콘텐츠를 구현할 수 있다.

참고 자료

  • [1] Getting Started with Phaser 3
목차