I am Ironman: 콘텐츠 모션과 애니메이션
I am Ironman 게임 개발기· 4 / 7
콘텐츠의 인스턴스를 생성하는 것만으로는 게임을 ‘플레이’ 할 수 없다. 콘텐츠의 상황별 모션과 애니메이션, 컨트롤 등 다양한 메커니즘이 설정되어 있어야 기본적인 플레이 화면이 구현된다.
기본적인 모션과 애니메이션, 컨트롤 로직을 정리해본다.
애니메이션
콘텐츠 모드에 따라 이미지를 교체하며 애니메이션 효과를 표현했다.
* 애니메이션 효과 적용
준비한 캐릭터의 이미지 스프라이트 파일에는 캐릭터의 연속된 동작이 처리되어 있지 않아 이미지 스프라이트 애니메이션을 적용할 수 없었다.
대안책으로 모션마다 이미지를 따로 준비해 이미지를 체인지하는 방식으로 애니메이션 효과를 적용했다.애니메이션 모습

애니메이션 구현 코드
-
모드 변경과 이미지 전환
// managers/characters/ironmanManager.ts ... // 아이언맨 모드 변경 처리 public transform(mode: IronmanMode, callback?: () => void) { // 아이언맨 모드 변경 this.setTexture(mode); // 모드별 사운드 재생 if (mode !== IronmanMode.NORMAL || IronmanMode.REPULSOR) this.ironman.playSound(mode); // 콘텐츠 인스턴스에 설정되어 있는 사운드 재생 // 모드 유지 타이머 설정 this.timerHandler.handleIronmanMode(mode, () => { this.setTexture( mode === IronmanMode.GATHER ? IronmanMode.BEAM : IronmanMode.NORMAL ); if (callback) callback(); // () => 빔 발사 후 제거 }); } // 아이언맨 모드 변경 private setTexture(mode: IronmanMode) { if (this.ironman) { // 이미지 키값 정리 const textures = { [IronmanMode.NORMAL]: ImageTexture.IRONMAN_NORMAL, [IronmanMode.REPULSOR]: ImageTexture.IRONMAN_REPULSOR, [IronmanMode.HIT]: ImageTexture.IRONMAN_HIT, [IronmanMode.GATHER]: ImageTexture.IRONMAN_GATHER, [IronmanMode.BEAM]: ImageTexture.IRONMAN_BEAM, }; // 이미지 변경 this.ironman.setTexture(textures[mode]); // 아이언맨 사이즈 재설정 this.ironman.setSize(this.ironman.frame.width, this.ironman.frame.height); // 아이언맨 모드 업데이트 this.ironman.mode = mode; } }메인 캐릭터인 아이언맨은 기본, 공격, 피격, 스킬 등 모드에 따라 각기 다른 이미지를 사용한다.
모드 변경이 필요할 경우 아이언맨 매니저 클래스의
transform()메서드를 호출해 이미지를 변경하며, 대부분의 경우 기본 모드로 자동 복귀할 수 있도록 있도록 타이머 이벤트를 함께 등록한다.적 NPC와 발사 무기도 동일한 로직을 적용하기 위해, 각각의 매니저 클래스에 동일한 기능의 메서드를 정의해 두었다.
모션과 컨트롤: 이동
콘텐츠별로 이동 방식은 사용자 입력 또는 자동 로직으로 나뉜다.
모션(컨트롤) 설정
- 아이언맨: 상하좌우(방향키 컨트롤 이동)
- 적: 게임 화면의 우 → 좌(자동 이동)
- 리펄서 무기: 아이언맨의 손 위치로부터 좌 → 우(자동 이동)
- 적 리펄서 무기: 울트론1의 손 위치로부터 우 → 좌(자동 이동)
- 유니빔 무기: 아이언맨의 명치 위치에 고정(아이언맨 이동 팔로우)
모션 구현 코드
-
방향키 입력 이벤트 설정
// handlers/keyHandler.ts import Phaser from 'phaser'; import IronmanControlManager from '../managers/charaters/ironmanControlManager'; ... export default class KeyHandler { private scene: Phaser.Scene; private ironmanControlManager: IronmanControlManager; private ironman: Ironman; private cursors: Phaser.Types.Input.Keyboard.CursorKeys; ... constructor( scene: Phaser.Scene, ironmanControlManager: IronmanControlManager ... ) { this.scene = scene; this.ironmanControlManager = ironmanControlManager; this.ironman = this.ironmanManager.get(); this.cursors = this.scene.input.keyboard!.createCursorKeys(); ... } ... // 아이언맨 위치 업데이트 public updateIronmanPosition() { if (!this.ironman?.active) return; this.handleKeyCursor(); } // Cursors 키 이벤트 private handleKeyCursor() { if (this.cursors.left.isDown) // 왼쪽 방향키 this.ironmanControlManager.updatePosition(false, false); if (this.cursors.right.isDown) // 오른쪽 방향키 this.ironmanControlManager.updatePosition(false, true); if (this.cursors.up.isDown) // 위쪽 방향키 this.ironmanControlManager.updatePosition(true, false); if (this.cursors.down.isDown) // 아래쪽 방향키 this.ironmanControlManager.updatePosition(true, true); } }키 이벤트 설정 클래스에서는 방향키 입력에 따라 플레이어 캐릭터의 위치를 업데이트하는 메서드를 호출한다. 이 클래스의 외부로 공유 할
updateIronmanPosition메서드를 씬의update메서드에 등록하여 매 프레임마다 방향키 입력을 감지하고 캐릭터의 위치를 실시간으로 업데이트한다.* createCursorKeys() input.keyboard의 'on' 메서드는 keydown과 keyup 이벤트를 통해 키 입력을 감지하지만, 방향키를 누르고 있는 동안 지속적으로 상태를 확인하지 않는다. 따라서 'createCursorKeys' 메서드를 이용해 매 프레임마다 키 입력을 감지하는 게 효율적이다. -
캐릭터 위치 업데이트
// managers/characters/ironmanControlManager.ts ... // 아이언맨 이동 public updatePosition(isUpdown: boolean, isPlus: boolean) { const speed = this.scene.game.canvas.width * (isPlus ? speedConfig.ironman : -speedConfig.ironman); // 아이언맨 위치 변경 isUpdown ? this.ironman.setY(this.ironman.y + speed) : this.ironman.setX(this.ironman.x + speed); // 이동 제한 범위 설정 this.setMovementBounds(); }방향키 입력 이후 호출된 매니저 클래스의
updatePosition메서드에서 캐릭터의 이동 속도를 반영하여 위치를 실시간으로 업데이트한다.* 메인 캐릭터 한정으로 생성/관리와 컨트롤 기능의 클래스를 분리해두었다. -
그 외 콘텐츠 모션
// managers/characters/enemyManager.ts ... // 빌런 이동 public updatePosition() { if (this.enemies!.getLength() <= 0) return; this.enemies.children.entries.forEach((child) => { const enemy = child as Enemy; const speed = this.scene.game.canvas.width * enemy.getSpeed(); const type = enemy.getType(); // 빌런 위치 변경 enemy.setX(enemy.x - speed); // 화면을 벗어날 경우 if (enemy.x < 0) { ... } }); }// managers/weapons/repulsorManager.ts ... // 리펄서 이동 public updatePosition() { if (this.repulsors.getLength() <= 0) return; this.repulsors!.children.entries.forEach((child) => { const repulsor = child as Repulsor; const speed = this.scene.game.canvas.width * repulsor.getSpeed(); // 리펄서 위치 변경 repulsor.setX(repulsor.x + speed); // 일정 간격/화면을 벗어나면 리펄서 제거 if ( repulsor.x > this.scene.game.canvas.width || repulsor.x > repulsor.getInitialPos().x + this.scene.game.canvas.width * intervalConfig.repulsorFireDistance ) ... }); }적과 무기 콘텐츠의 이동 메서드 또한 씬의
update메서드에 등록해 매 프레임마다x의 위치를 업데이트한다. 각 콘텐츠가 게임 화면을 벗어나면 해당 인스턴스는 제거된다.그 외 다른 콘텐츠들도 비슷한 로직을 갖는다.
모션과 컨트롤: 공격
공격 모션은 특정 키 입력 또는 타이머 이벤트로 트리거된다.
모션(컨트롤) 설정
- 리펄서 발사:
Space-Bar키 입력 - 유니빔 발사:
B키 입력 - 적 리펄서 발사: 타이머 이벤트(자동 생성)
모션 구현 코드
-
특정 키 입력 이벤트 설정
// handlers/keyHandler.ts ... constructor( scene: Phaser.Scene, ironmanControlManager: IronmanControlManager ... ) { ... this.createKeyEvents(); } // 키 함수 할당 private createKeyEvents() { this.scene.input.keyboard!.on('keydown-SPACE', this.handleKeySpace, this); this.scene.input.keyboard!.on('keydown-B', this.handleKeyB, this); } // Space bar 키 이벤트 private handleKeySpace() { this.ironmanControlManager.attackRepulsor(); } // B 키 이벤트 private handleKeyB() { this.ironmanControlManager.attackBeam(); }Space-Bar랑B키의 입력 이벤트에 공격 모션을 설정해두었다. 해당 메서드들은 씬의update메서드에는 별도로 등록할 필요 없다. -
공격 이벤트
// managers/weapons/repulsorManager.ts ... // 리펄서 발사 public fire() { if ( this.repulsors.getLength() >= maxConfig.repulsor || this.scene.time.now <= this.lastRepulsorFireTime + intervalConfig.repulsorFireTime ) return; // 리펄서 생성 위치 지정 const posX = this.ironman.x + this.ironman.displayWidth * 0.62; const posY = this.ironman.y + this.ironman.displayHeight * -0.33; const repulsor = new Repulsor( this.scene, posX, posY, ImageTexture.REPULSOR ); // 리펄서 발사 시간 스템프 this.lastRepulsorFireTime = this.scene.time.now; // 그룹 추가 this.repulsors.add(repulsor); this.weapons.add(repulsor); // 리펄서 사운드 재생 // 아이언맨 공격 감지 핸들러 등록 }// managers/weapons/beamManager.ts ... // 빔 발사 public fire() { // 발사 const beam = new Beam(this.scene, 0, 0, ImageTexture.BEAM); this.beam = beam; this.beams.add(this.beam); this.weapons!.add(this.beam); // 아이언맨 공격 감지 핸들러 등록 }각 무기 발사 메서드가 호출되면 발사 주체(캐릭터)의 위치를 기반으로 해당 무기의 인스턴스를 생성해 화면에 구현하고, 기능별 그룹에 추가해 이동과 충돌 등 부수 기능에 활용된다.
모션과 애니메이션 구현 화면
참고 자료
- [1] Getting Started with Phaser 3↗