개요
에셋(Assets) 이란 이미지, 사운드 효과, 텍스트를 포함하여 게임상에 삽입되는 모든 것을 의미합니다.
Phaser는 게임 내에서 사용되는 애셋을 사용하기 전에 먼저 로딩하도록 구성되어 있습니다. 제작하는 게임의 규모가 커짐에 따라 사용되는 애셋의 양이 늘게 되며, 로딩에 소요되는 시간이 점점 늘어나게 됩니다.
게이머는 모든 작업이 완료되기를 기다려야 하는데, 이때 기다리는 게이머를 위하여 화면에 대기해야 하는 정도를 표시해주어야 합니다.
이러한 화면을 로딩 화면이라고 하며, 이번 포스트에서는 Phaser를 이용하여 로딩 화면을 만들어 보도록 하겠습니다.
사전 준비
Phaser 를 실행할 수 있는 환경을 구성해야 합니다. 자신의 PC에서 본 프로젝트를 수행한다면 Getting Start With Phaser를 참고하여 웹서버를 PC에 구축할 수 있습니다.
저의 경우는 stackblitz를 사용해서 개발환경을 설정하였습니다.
상세한 내용은Angular 와 Phaser 함께 사용하기 (1/2)를 참고합니다.
Phaser의 개발환경을 구성한 후, 다음 코드를 app.components.ts
에 작성하도록 합니다.
import { Component, OnInit } from '@angular/core';
export class Preload extends Phaser.Scene {
constructor() {
super({
key: 'preload',
});
}
public preload() {
}
public create() {
}
public update() {
}
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
public game: Phaser.Game;
public readonly gameConfig: GameConfig = {
title: "Phaser Preload @ Angular",
version: "0.0.1",
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'content',
}
ngOnInit() {
this.game = new Phaser.Game(this.gameConfig);
this.game.scene.add('preload', new Preload(), true);
}
}
우선 Phaser 게임에서 사용할 구성을 추가하였습니다.
Phaser는 WebGL과 Canvas 두 가지 렌더링 방식을 지원합니다. WebGL 은 Canvas에 비하여 성능이 좋지만 모든 브라우저가 지원하지는 않기 때문에 일반적으로 type
항목은 AUTO 를 선택하게 됩니다. AUTO를 선택하면 WebGL 이 가능한 경우 자동으로 WebGL로 렌더링 되게 됩니다.
parent
항목은 Phaser 가 게임을 rendering 할 위치를 설정합니다. 항목의 id 값이 여기에서 설정한 항목과 일치하는 위치에 게임을 렌더링 합니다. 그렇지 않은 경우는 Phaser 가 해당 id 값을 갖는 <canvas>
를 생성한 후 렌더링합니다.
이 외에도 title
, version
, width
, height
, background
항목을 설정하고 마지막으로 이 구성을 새로운 Game 인스턴스의 생성 시 사용하도록 Phaser.Game
의 인자로 제공합니다.
생성된 게임에 scene을 추가하도록 합니다 scene 은 preload()
, create()
, update()
3개의 매소드로 구성된 클래스입니다.
각 매소드의 기능은 이후에 살펴보도록 하겠습니다.
이제 소스코드를 브라우저에서 실행하면, 다음과 같이 800x600 크기의 하늘색 화면이 표시됩니다. (이는 앞서 설정한 구성항목에서 background
속성을 통해 지정하였기 때문입니다.)
애셋(Assets) 불러오기
이제 프로젝트의 기본적인 설정이 완료되었기 때문에 앞서 작성하였던 preload()
매소드에 내용을 작성합니다.
간단히 아래의 이미지를 로딩하도록 하겠습니다. 로컬 PC에 넣은 경우는 Angular 프로젝트의 src/assets
폴더에 이미지 파일을 복사하도록 합니다. 저의 경우는 StackEdit를 사용하기 때문에 이전 포스트에서 알아본 것과 같이 GitHub Pages 서비스를 사용하여 외부에서 이미지를 로딩하도록 하겠습니다.
이미지를 화면에 표시하려면 preload와 create 메서드를 다음과 같이 갱신합니다.
public preload() {
this.load.baseURL = 'https://yungjoong.github.io/';
this.load.image('logo', 'angular.png');
}
public create() {
var logo = this.add.image(400, 300, 'logo');
}
브라우저를 재실행하여 코드를 실행하면, 다음과 같이 화면 중앙에 불러온 이미지가 표시됩니다.
로딩 화면 만들기
이렇게 하나의 애셋만 로딩할 경우는 큰 문제가 없지만 실제로는 게임을 구성하는 다수의 애셋을 로딩해야 합니다.
참고 실제 상황에서는 여러 애셋을 로딩하겠지만, 간단히 동작만 살펴볼 수 있도록 하나의 이미지를 여러 번 로딩하는 것으로 대체하도록 하겠습니다.
function preload() {
this.load.baseURL = 'https://yungjoong.github.io/';
this.load.image('logo', 'angular.png');
for (let i=0; i<500; i++) {
this.load.image('logo'+i, 'angular.png');
}
}
function create() {
const logo = this.add.image(400, 300, 'logo');
}
브라우저를 다시 실행하면 앞서 와 달리 한참 후에 이미지가 표시됩니다.
여러분의 게임을 실행한 게이머는 이 시간 동안 아무 변화가 없는 화면으로 인하여 답답함을 느낄 것입니다.
이를 해결해보도록 하겠습니다.
Phaser의 preload 함수 내부에 다음의 코드를 삽입합니다.
public preload() {
this.load.baseURL = 'https://yungjoong.github.io/';
this.load.image('logo', 'angular.png');
for (let i=0; i<500; i++) {
this.load.image('logo'+i, 'angular.png');
}
this.load.on('progress', function (value) {
console.log(value);
});
this.load.on('complete', function () {
console.log('complete');
});
}
이 코드는 Phaser의 LoaderPlugin에서 발생하는 이벤트를 수신하는 데 사용합니다. Phaser는 파일을 로드할 때마다 progress 이벤트를 발생시키고 모든 로딩 작업이 완료되면 complete 이벤트를 발생시키는데, 우리는 이것을 기준으로 진행상황을 표시할 수 있습니다.
브라우저를 통해 코드를 실행합니다. 브라우저의 개발도구의 콘솔 창을 열어보면 다음과 같은 숫자가 나열되는 것을 확인할 수 있습니다. Phaser 문서에 따르면 progress는 0과 1 사이의 실수 값으로 현재 로딩의 진행상태를 나타낸다고 합니다. 모든 로딩이 완료되면, 이 값은 1이 됩니다. 결국 이 값을 이용하여 진행률을 표시할 수 있습니다.
progress 이벤트를 통해 수신하는 값을 확인했으니 본격적으로 로딩 상태를 Phaser의 그래픽 기능을 사용하여 표현해 보겠습니다. 아래의 코드를 preload 함수의 가장 상단에 추가합니다.
let progressBar = this.add.graphics();
let progressBox = this.add.graphics();
progressBox.fillStyle(0x222222, 0.8);
progressBox.fillRoundedRect(240, 450, 320, 50, 5);
이제 추가한 graphic 요소를 progress 이벤트가 발생할 때마다 이벤트의 값을 막대의 길이에 반영하여 새로 그리도록 합니다. 이번 예시는 300을 진행 막대의 전체 길이로 하였습니다.
this.load.on('progress', function (value:number) {
// console.log(value);
progressBar.clear();
progressBar.fillStyle(xffffff, 1);
progressBar.fillRoundedRect(250, 460, 300*value, 30, 5);
});
이 코드를 실행하면 의도와 같이 화면에 막대가 증가하는 것을 볼 수 있습니다.
그리고 마지막으로 complete 이벤트가 발생하면 해당 로딩 막대를 제거합니다.
this.load.on('complete', function () {
// console.log('complete');
progressBar.destroy();
progressBox.destroy();
});
브라우저를 재실행하여 코드를 실행하게 되면 애셋의 로딩을 의미하는 진행 막대가 증가하는 것을 확인할 수 있습니다. 또한 모든 파일의 로딩이 완료된 후, 진행상황을 나타내는 막대가 화면에서 제거되는 것을 확인할 수 있습니다.
진행률 문자로 표시하기
막대를 통하여 진행상황을 나타내는 것으로 사용자의 불편함을 제거하였습니다. 하시만 해당 막대의 진행률을 좀 더 상세히 표시하면 좋을 것 같습니다.
이를 위해서 텍스트 객체를 추가하고 이를 로딩바 위에 값을 경신해가면서 표시해보도록 하겠습니다.
아래의 코드를 preload 함수에 추가하여 텍스트 객체를 추가합니다.
let width = this.cameras.main.width;
let height = this.cameras.main.height;
let percentText = this.make.text({
x: width/2,
y: (height/2)+175,
text: '0%',
style: {
font: '18px monospace',
fill: '#ffffff'
}
});
percentText.setOrigin(0.5, 0.5);
이제 문자를 갱신하기 위해 progress 이벤트를 처리하는 부분에 아래의 코들를 추가합니다.
percentText.setText(Math.route(value*100) + '%');
역시 complete 이벤트 발생 시 진행 막대와 함께 제거하도록 합니다.
percentText.destroy();
다시 한번 코드의 내용을 요약해보며,
새로운 Text GameObject를 생성하였습니다.
그리고 progress 이벤트 핸들러에서 이 텍스트 객체의 내용을 수신한 value를 백분율로 변환하여 갱신하였습니다.
마지막으로 complete 이벤트 핸들러가 호출되면 관련 객체를 제거하였습니다.
이제 브라우저를 재실행하면 막대가 늘어가며, 진행률이 함께 표시됩니다. 그리고 모든 애셋의 로딩이 완료되면 create()
함수가 호출되어 로고가 화면에 표시되는 것을 알 수 있습니다.
결론
여기까지 phase의 로딩 화면을 만들어 보았습니다. 이를 통해서 여러 애셋의 로딩을 위한 시간을 확보할 수 있으며, 사용자에게 적절한 정보를 제공할 수 있게 되었습니다.
참고
'모듈, 프레임웍 > Phaser' 카테고리의 다른 글
Angular - Phaser 통합 (0) | 2020.04.11 |
---|---|
Phaser3 의 기본기능 및 퍼즐게임 예시 (3) | 2020.03.25 |
Angular 와 Phaser 함께 사용하기 (2/2) - 벽돌깨기 게임 만들기 (0) | 2019.01.27 |
Angular 와 Phaser 함께 사용하기 (1/2) (0) | 2019.01.06 |