본문 바로가기
  • ANALOG CODE
  • AnalogCode
개발(인터렉티브)

Phaser 로 자바스크립트 게임 만들기 (지속 업데이트)

by 아날로그코더 2023. 4. 2.
반응형

Phaser를 이용하여 자바스크립트로 간단한 게임을 만들고 블로그에서 플레이 가능하도록 하였다. Phaser를 이용하여 어떻게 게임을 개발하는지를 설명하고, 이 포스팅에 개발과정과 결과물을 지속적으로 업데이트 해나갈 예정이다. 개발중인 게임은 아래에서 동작한다.
 

Zoombie Shooter 게임


Phaser

Phaser는 HTML5 Game Framework이다. Javascript를 이용하여 웹에서 구동되는 게임을 만들 수 있다. Canvas와 WebGL 모두 지원한다. PhaserPixi.js 중에 고민하다가 Phaser가 아무래도 Framework을 표방해서 그런건지 Scene Manager 기능이 포함되어 있어서 Phaser를 선택하였다.

프로젝트 생성 (Webpack)

게임을 만드는데 자바스크립트 파일 하나에 모든 소스를 넣어서 개발할게 아니므로 번들링을 위해 Webpack으로 프로젝트 세팅을 하였다.
 

// npm project 초기화
$ npm init

// webpack proejct 생성
$ npx webpack init

// Phaser 모듈 추가
$ npm install phaser

폴더구조

.
├── asset/
├── src/
│   └── index.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js

webpack.config.js 설정

기본 웹팩 설정에서 필요한 몇가지 설정을 추가한다.

phaser 빌드 제외

webpack externals을 통해 phaser가 빌드되서 번들에 포함되는 걸 제외시켰다.

phaser는 이미 번들링된 모듈이므로 굳이 또 빌드를 해서 번들에 포함시키기보다는 index.html에서 <scrtip> 태그로 로드하게 만들었다. 이것을 위한 webpack 설정이다.

externals: {
    phaser: 'Phaser'
}

index.html

phaser.min.js 파일을 다운받아서 asset 폴더에 넣고 html파일에서 로드한다.

<script src="./asset/lib/phaser.min.js"></script>

 

빌드시 dist에  asset  복사

실행시 필요한 asset 파일 복사

plugins: [
    new CopyWebpackPlugin({
        patterns: [
          { from: 'asset', to: 'asset'}
        ]
    })  
]

 

Asset 다운로드

사실 괜찮은 에셋을 먼저 찾고 게임컨셉을 잡았다. 우리는 그림을 그릴 실력도 시간도 없기 때문에 무료 게임 리소스 사이트를 탐색하면서 아이디어를 도출하였다.

Zombie 리소스

  • appear : 나타나는 애니메이션
  • attack : 공격 애니메이션
  • idle : 가만히 있는 애니메이션
  • die : 죽는 애니메이션
  • walk : 걷는 애니메이션

필요한 기본 동작이 모두 있다. 좀비 애니메이션 리소스를 다운받아 가져왔다.


* 무료 게임에셋 사이트: https://opengameart.org

Spritesheet

애니메이션을 만들기 위한 스프라이트 이미지가 한장한장 별도의 파일로 되어있다. 이렇게되면 이미지 파일이 10개라면 10번의 http 요청을 해야하는 부하가 있다. 애니메이션 스프라이트 파일을 하나로 합치자.
이것을 위해 spritesheet라는 규격화된 구조가 있다. 무료 온라인 Spritesheet generator 툴을 이용하여 생성하였다.

아래가 이렇게 만들어진 spritesheet 이미지 파일이다.


출현 이미지

appear

 

공격 이미지

attack

 

제자리에 서있는 이미지

idle

 

죽는 이미지

die

 

걸어가는 이미지

walk

 

이제 리소스가 준비되었으니 동작을 하게 만들자.

 


개발일지(지속적으로 추가예정)

여기부터는 개발과정을 계속 추가할 계획이다. 개발이 될 수록 위에 나타나는 게임 영역도 업데이트되서 플레이되도록 만들 것이다.


1️⃣ 개발 1일차

 

✅ Phaser 시작

phaser game 생성

// src/index.js
import Phaser from 'phaser'

var game = new Phaser.Game({
  type: Phaser.AUTO,
  scale: {
    mode: Phaser.Scale.FIT,
    width: 1280,
    height: 720,
    autoCenter: Phaser.Scale.Center.CENTER_HORIZONTALLY
  }
})

이렇게 하면 body 밑에 canvas가 생성이 되고 검은배경만 나온다.

 

✅ 애니메이션 테스트

애니메이션 생성

뭔가 애니메이션이 있어야 게임이 만들어지는 느낌이 들거 같아서 애니메이션을 만드는 과증을 먼저 만들어 보았다.

spritesheet를 로드하고 애니메이션을 생성하여 재생을 시킨다.

import Phaser from 'phaser'

var game = new Phaser.Game({
  type: Phaser.AUTO,
  scale: {
    mode: Phaser.Scale.FIT,
    width: 1280,
    height: 720,
    autoCenter: Phaser.Scale.Center.CENTER_HORIZONTALLY
  },
  scene: {
    preload: preload,
    create: create
  }
})

function preload () {
  // 애니메이션 이미지 파일로부터 spritesheet를 로드한다. 이름은 zombie_attack 이다.
  this.load.spritesheet('zombie_attack', './asset/img/zombie/attack.png', { frameWidth: 372, frameHeight: 324 })
}
function create () {
  // spritesheet에서 애니메이션을 만든다.
  this.anims.create({
    key: 'zombie_attack',
    frames: this.anims.generateFrameNumbers('zombie_attack'),
    frameRate: 10,
    repeat: -1
  })
  
  // 스프라이트를 만들고 애니메이션을 플레이한다.
  const z = this.add.sprite(800, 300, 'zombie_attack')
  z.play('zombie_attack')
}


✅ Scene 생성

게임은 Scene System 이라는 것이 있다. 영화의 Scene처럼 독립적인 Scene을 개발하고 Scene간에 전환을 하도록 만드는 구조이다.

Phaser의 Scene에서는 preload, create, update 메소드가 존재한다.

preload 에서는 리소스를 로드하는 처리를 넣고

create는 리소스를 가지고 필요한 객체를 생성하는 코드를 넣는다.

update는 시간이 지나면서 캐릭터위 위차가 바뀐다는지와 같은 상태값을 변경하는 코드를 넣는다.

 

여기서 2개의 Scene을 만들었다.

  • Intro Scene : 게임 시작 Scene
  • Main Scene : 게임 플레이 Scene

Intro Scene에서 아무곳이나 터치하면 Main Scene으로 전환하게 하였다.

Intro Scene

게임 타이틀과 좀비 애니메이션을 하나 넣어서 무슨 게임인지 대략 알 수 있게 한다.

"Tap to continue"란 문구를 넣고 tweens를 이용하여 깜빡이게 만든다.

import MainScene from './MainScene'

export default class IntroScene extends Phaser.Scene {
  constructor() {
    super({key: 'Intro'}) // Scene마다 고유의 key 값이 필요하다.
  }

  preload() {
    this.load.spritesheet('zombie_idle', './asset/img/zombie/idle.png', { frameWidth: 200, frameHeight: 308 })
  }

  create() {
    this.anims.create({
      key: 'zombie_idle',
      frames: this.anims.generateFrameNumbers('zombie_idle'),
      frameRate: 10,
      repeat: -1
    })
    this.add.text(this.game.scale.width/2, 200, 'Zoombie Shooter', { fontFamily: 'Arial', fontSize: 80, color: 'red' }).setOrigin(0.5, 0.5)
    this.add.sprite(1000 ,500).play('zombie_idle')
    this.textTap = this.add.text(this.game.scale.width/2, 500, 'Tap to continue', { fontFamily: 'Arial', fontSize:50, color: 'white' }).setAlpha(1).setOrigin(0.5, 0.5)

    this.input.on('pointerdown', (point) => {
      this.scene.start('Main') // Main Scene으로 이동한다.
    })

    this.tweens.add({
      targets: this.textTap,
      alpha: 0, 
      duration: 500,
      ease: 'Linear',
      yoyo: true,
      repeat: -1
    })
  }
}

중요한 것은 아래 코드이다. scene을 전환하는 함수이다.

this.scene.start('Main')

 

 

Main Scene

여기서는 실제 게임을 플레이할 수 있도록 만들 예정이다.

일단은 필요한 리소스를 모두 로드하고 애니메이션을 만들어두었다.

그리고 화면 중앙에 좀비가 나오는 애니메이션이 나타나도록 하였다. 

export default class MainScene extends Phaser.Scene {
  constructor() {
    super({key: 'Main'})
  }
  preload() {
    this.load.spritesheet('zombie_appear', './asset/img/zombie/appear.png', { frameWidth: 220, frameHeight: 288 })
    this.load.spritesheet('zombie_attack', './asset/img/zombie/attack.png', { frameWidth: 372, frameHeight: 324 })
    this.load.spritesheet('zombie_die', './asset/img/zombie/die.png', { frameWidth: 444, frameHeight: 292 })
    this.load.spritesheet('zombie_idle', './asset/img/zombie/idle.png', { frameWidth: 200, frameHeight: 308 })
    this.load.spritesheet('zombie_walk', './asset/img/zombie/walk.png', { frameWidth: 200, frameHeight: 312 })
  }
  create() {
    this.anims.create({
      key: 'zombie_appear',
      frames: this.anims.generateFrameNumbers('zombie_appear'),
      frameRate: 3,
      repeat: -1
    })
    this.anims.create({
      key: 'zombie_attack',
      frames: this.anims.generateFrameNumbers('zombie_attack'),
      frameRate: 10,
      repeat: -1
    })
    this.anims.create({
      key: 'zombie_die',
      frames: this.anims.generateFrameNumbers('zombie_die'),
      frameRate: 10,
      repeat: -1
    })
    this.anims.create({
      key: 'zombie_idle',
      frames: this.anims.generateFrameNumbers('zombie_idle'),
      frameRate: 10,
      repeat: -1
    })
    this.anims.create({
      key: 'zombie_walk',
      frames: this.anims.generateFrameNumbers('zombie_walk'),
      frameRate: 10,
      repeat: -1
    })
    const z = this.add.sprite(640, 300)
    z.setOrigin(0.5, 0.5)
    z.play('zombie_appear')
  }
}

그리고 Phaser Game 생성시에 Scene 옵션에 넣어준다.

import MainScene from './Scenes/MainScene'
import IntroScene from './Scenes/IntroScene'

var game = new Phaser.Game({
  ...
  scene: [IntroScene, MainScene]
})

 

1일차는 여기까지이다. 아직은 게임으로서는 아무런 기능이 없다.

사실 어떤 게임을 만들지도 못정했다. 일단 좀비 이미지가 맘에 들어서 일단 여기까지 만든것이다.^^;

대충 좀비들이 몰려와서 공격을 하고 주인공이 총으로 쏴서 죽이는 게임이 될 것 같다.

 

다음 시간부터 MainScene을 만들면서 게임을 할 수 있도록 개발할 것이다.

 

 

 


2️⃣ 개발 2일차 (예정)

 

반응형

댓글