Phaser 프레임 워크를 사용하여 'Gravity Quest'를 어떻게 개발 했나요? - Part 3

비주얼 및 사운드
이전 문서에서, Gravity Quest에서 사용 된 충돌 감지에 대해 논의 했으므로 (게임의 게임 플레이에 대한 부분을 마무리 함) 이 문서에서는 Gravity Quest의 비주얼과 사운드가 어떻게 생겨 났는지 설명합니다.

Gravity Quest의 경우 영상과 사운드에 관한 저의 목표는 매우 낮았습니다. 나는 결코 예술가가 아니 었습니다. 솔직히 뛰어난 시각 효과와 사운드를 만드는 데 드는 노력을 두려워했습니다. 따라서 내 목표는 일관된 경험을 창조하는 것 - Gravity Quest에 뚜렷한 느낌을 주기위한 것입니다.


스프라이트 페인팅

여러 가지 이유로 픽셀 아트 스타일을 추구하기로 결정했습니다. 첫째, 픽셀 아트는 제약을 유도하며 응집력있는 스타일을 만드는 데 도움이됩니다. 둘째, 픽셀 아트는 많은 주목을 받고있는 이 글의 일부이며 결국에는 게임의 미래로 여겨지기도합니다. 셋째, 제한된 양의 픽셀로 인해 비주얼을 만드는 데 필요한 노력이 줄어들 것이라고 추론했습니다 (이는 매우 순진한 생각이었습니다). 이 훌륭한 기사는 픽셀 아트와 어떤 관계가 있는지 설명합니다.

게임의 스프라이트를 만들기 위해 필자는 주로 Mac OS 용 Pixen 응용 프로그램을 사용했습니다. Apple App Store에서 구하거나 GitHub에서 무료로 구할 수있는 xCode 프로젝트에서 컴파일 할 수 있습니다. Pixen은 잘 선택된 드로잉 도구 세트, 작업 할 레이어 및 애니메이션 제작을 지원하는 픽셀 아트를 제작하도록 설계되었습니다.

아트 워크 만들기는 연속적인 반복으로 표시되었습니다. 예를 들어, 이미지 1은 우주 비행사의 일부 버전을 보여줍니다. 
이미지 1 : 우주 비행사의 일부 버전

게임의 스프라이트에 사용할 모양과 색상에 영감을 얻기 위해 Google의 이미지 검색을 사용하여 소행성, 폭발물 또는 우주선과 같은 이러한 객체의 사진을 얻었습니다.


애니메이션

애니메이션에 많은 노력을 기울였습니다. 확실히 제한적 임에도 불구하고 게임에 애니메이션이 있습니다. Novae는 이미지 2에서 보여지는 것처럼 깜박임 현상이 나타나며, 이미지가 항상 활성화 되도록 합니다. 반 중력력 필드는 이미지 3 에서 처럼 중력파 확대를 지속적으로 방출하며 Pixen의 원 도구로 구속력이 필요합니다. 확장된 광선의 투명도가 높아져서 사라지는 것을 시뮬레이트하기 때문에 마지막 두 프레임이 흐리게 보입니다. 미네랄은 이미지 4에서 볼 수 있듯이 자주 번쩍 거립니다. 외계인은 이미지 5에서 볼 수있는 것처럼 살아 있다는 것을 암시하기 위해 조명을 돌립니다. UFO가 우주 비행사를 따라 가면이 표시등이 빨간색으로 바뀝니다. 마지막으로, 우주 비행사는 이미 이미지 1에 표시된 몇 가지 최소 애니메이션을 특징으로합니다.
이미지 2 : 노바 애니메이션


이미지 3 : 반 중력 영역 애니메이션


이미지 4 : 광물 애니메이션


이미지 5 : 외계인 애니메이션

애니메이션은 목록 1의 nova 및 우주 비행사에 표시된 것처럼 preload 함수에서 애니메이션 프레임을 포함하는 스프라이트 시트를 먼저 로드하여 Phaser에서 사용할 수 있습니다. 모든 프레임의 너비와 높이가 32 픽셀임을 추가로 정의합니다. 이 코드와 다음 코드는 문서에서 제시 한 게임 플레이 구현, 특히 충돌 감지에 대한 내용을 확장합니다.
...
// instead of: game.load.image('nova', 'assets/nova.png');
game.load.spritesheet('nova', 'assets/nova_animation.png', 32, 32);
// instead of: game.load.image('astronaut', 'assets/astronaut.png');
game.load.image('astronaut', 'assets/astronaut_animation.png', 20, 32);
...
목록 1 : 우주 비행사와 우주 비행사를위한 애니메이션 프레임을 포함하는 스프라이트 시트 로딩.

그런 다음 create 함수 내에서 목록 2와 같이 해당 스프라이트를 만들 때 애니메이션이 정의됩니다. 애니메이션을 만들 때 프레임을 재생해야하는 순서를 정의 할 수 있습니다. nova의 경우, 정의 된 애니메이션이 즉시 시작됩니다. 애니메이션을 시작할 때 프레임 속도 (여기 : 5)와 애니메이션을 반복할지 여부 (여기 : true)를 지정할 수 있습니다.
...
// (randomly) place novae and add to group:
this.novae = game.add.group();
for (var i = 0; i < 3; i++) {
 var nova = game.add.sprite(
  game.rnd.integerInRange(100, game.world.width - 100),
  game.rnd.integerInRange(100, game.world.height - 100),
  'nova');
 nova.anchor.setTo(0.5, 0.5);

 // define and start animation:
 nova.animations.add('flicker', [0,1,2,3,2,1]);
 nova.animations.play('flicker', 5, true);

 this.novae.add(nova);
};
...
// create astronaut at the center of the world:
this.astronaut = game.add.sprite(game.world.width * 0.5, game.world.height * 0.5, 'astronaut');
this.astronaut.animations.add('idle', [0]);
this.astronaut.animations.add('firing', [1]);
...
목록 2 : 애니메이션 정의 및 시작.

우주 비행사의 경우 update 함수에서 중력총이 발사 될 때 애니메이션이 재생됩니다. 따라서 코드는 목록 3과 같이 중력총이 발사되었는지 여부를 확인하는 데 사용되는 if-else 문에 배치됩니다.
...
if(game.input.activePointer.isDown){
 ...
 // animate astronaut:
 this.astronaut.animations.play('firing', 1, false);
} else {
 ...
 // animate astronaut:
 this.astronaut.animations.play('idle', 1, false);
}
...
목록 3 : 중력 총 발사시 우주 비행사 애니메이션 재생

전반적으로 나는 픽셀 아트에 대한 나의 순진한 아이디어가 틀렸다고 Gravity Quest의 비주얼을 제작함으로써 배웠습니다. 픽셀 수가 제한되어 있어도 반드시 노력이 줄어들지는 않습니다. 오히려 유도 된 구속 조건은 디자이너가 원하는 모양을 만들고, 관련된 세부 사항에 초점을 맞추고, 적절한 비율로 못 박는 것을 더욱 어렵게 만듭니다.


타이포그래피

타이포그래피의 경우, 나는 pixelart-esque 모양을 위해 무료 (상업용으로도 사용 가능) 글꼴 실크 스크린을 선택했습니다. Silkscreen은 TrueType 형식으로 제공되고 Phaser는 비트 맵 글꼴이 필요합니다. 트루 타입 글꼴에서 비트 맵 글꼴을 만들려면 littera 비트 맵 글꼴 생성기를 사용했습니다. 주어진 글꼴 파일에 대해 비트 맵 글꼴 스프라이트 시트를 구성 가능한 크기 및 색상으로 출력합니다. 색상 (흰색 또는 검은 색)과 크기가 다른 여러 가지 변형 된 실크 스크린 비트 맵 글꼴을 만들었습니다.


시각 효과

시각적 효과가 제대로 적용되면 덤덤한 게임을 진정으로 흥미로운 것으로 만들 수 있습니다. 여기에 Gravity Quest에서 사용 된 시각 효과에 대한 설명이 있습니다.


파티클 이미터

일부 효과는 Phaser의 파티클 이미터에 달려 있습니다. 파티클 이미터는 개발자 정의 방식으로 여러 개의 스프라이트 인스턴스를 생성합니다. 비, 폭발, 연기, 스파크 링 등과 같은 효과를 구현하는 데 사용할 수 있습니다. Gravity Quest에서는 중력 총의 사용을 나타내는 데 하나의 입자 방사체가 사용됩니다. 다음 코드는이 시리즈의 두 번째 기사에서 Gravity Quest의 게임 플레이 구현을 논의 할 때 제시되는 코드를 확장합니다. Listing 4는 preload 함수에서 필요한 스프라이트를 로드하는 방법을 보여준다. 두 입자는 이미지 6에 나와 있습니다.


이미지 6 : 중력총 이미터의 입자.
game.load.image('gun_particle_1', 'assets/gun_particle_1.png');
game.load.image('gun_particle_2', 'assets/gun_particle_2.png');
목록 4 : 중력총 이미터용 입자로드.

Listing 5는 gunEmitter가 create 함수에서 정의된 방법을 보여준다. 처음 두 매개 변수는 이미터 위치를 지정합니다. 이미터는 위치가 지속적으로 업데이트되므로 처음에는 게임에 위치합니다. thrid 매개 변수는 이미 터에서 최대 입자 수를 정의합니다. 다음 줄에서는 이미로드 된 입자 스프라이트가 이미터에 사용되도록 정의됩니다.
...
this.gunEmitter = game.add.emitter(0, 0, 25);
this.gunEmitter.makeParticles(['gun_particle_1', 'gun_particle_2']);
...
목록 5 : create 함수에서 중력총용 파티클 이미터 정의하기

이미터를 정의하면 우주 비행사가 중력총을 발사하는 데 사용할 수 있습니다 (Listing 6 참조). 제시된 코드는 중력총이 발사되었는지 여부를 결정하는 데 사용되는 if-else 문으로 update 함수내에서 실행됩니다. 우선 이미터의 위치는 우주 비행사에게 가장 가까운 소행성의 가장자리 (중력총에 의해 목표 된 것)로 업데이트됩니다. 다음으로 이미터가 이미 실행 중이 아니면 시작됩니다. 마지막으로 이미터의 알파와 모든 입자의 알파는 우주 비행사가 가장 가까운 소행성까지의 거리에 따라 설정됩니다. 이렇게하면 중력총이 멀리에서 발사되는 경우 빛이 반짝 일 수 있습니다.
...
if(game.input.activePointer.isDown){
 ...
 // emit particles:
 this.gunEmitter.x = closestAsteroid.x - Math.cos(angle) * closestAsteroid.width * 0.5;
 this.gunEmitter.y = closestAsteroid.y - Math.sin(angle) * closestAsteroid.width * 0.5;
 if(!this.gunEmitter.on){
  this.gunEmitter.start(false, 100, 15);
 }
 this.gunEmitter.alpha = 1 - distance / 250; // make emitter visible
 ...
} ...
목록 6 : 우주 비행사가 중력총을 발사 할 때 이미터 시작하기.

이 효과를 완료하기 위해 이미터는 중력총이 더 이상 발사되지 않으면 중지됩니다 (7). 다시 이 코드는 중력 총 발사 여부를 결정하는 데 사용되는 if-else 문이update 함수에서 사용됩니다 .
...
if(game.input.activePointer.isDown){
 ...
} else {
 ...
 // stop particle emitter:
 this.gunEmitter.on = false;
}
...
목록 7 : 중력총이 더 이상 발사되지 않을 때 이미터를 멈춤.

Gravity Quest의 파티클 이미터는 외계인이 충돌 할 때 우주 비행사가 노벨, 열 활성 소행성 또는 외계인에 의해 살해되거나 우주선의 연기를 만들때 사용됩니다. 전반적으로, 그들은 Phaser로 만든 게임에 생명을 불어 넣는 다목적 도구입니다.


시차 스크롤링

2D 게임에서 일반적으로 사용되는 또 다른 효과는 시차 스크롤입니다. 즉, 카메라가 움직일 때 배경이 전경의 대상과 다른 속도로 움직여 심도있는 효과를 만들어냅니다. Phaser는 수평 시차 스크롤링 (종종 platformer 스타일의 게임에서 사용됨)을 구현하는 기능을 제공합니다. 그러나 Gravity Quest에서는 우주 비행사가 수평 또는 수직으로 움직일 수 있으므로 내 자신의 솔루션을 구현하게되었습니다.

작동하려면 먼저 카메라 동작을 활성화해야 합니다. 그렇게 하기 위해서, 게임 세계의 범위는 목록 8에있는 것처럼 create 함수에서 실제 게임보다 크게 만들어야한다. (참고 : 이것은 무작위 적으로 데모에서 훨씬 더 분산 된 소행성과 nova로 이어질 것입니다).
...
// make the world larger than the actual canvas (for camera to be able to follow):
game.world.setBounds(0, 0, 1280, 960);
...
목록 8 : 게임월드 경계 설정.

다음으로, create 함수에서 게임의 카메라는 목록 9와 같이 우주 비행사를 따라 가도록 설정 했으며, Phaser에는 여러 가지 FOLLOW 모드가 있습니다. FOLLOW_TOPDOWN_TIGHT는 우주 비행사의 움직임이 카메라에 의해 수평 및 수직으로 밀접하게 따르는 것을 의미합니다.
...
// make camera follow player:
game.camera.follow(this.astronaut, Phaser.Camera.FOLLOW_TOPDOWN_TIGHT);
...
목록 9 : 우주 비행사를 따라가는 카메라 설정.

시차 스크롤링이 작동하려면 배경 스프라이트를 preload 함수에서 로드하고 목록 10에 표시된 것처럼 작성 함수에 타일 스프라이트로 배치해야합니다. 타일 스프라이트에서 정의 된 스프라이트는 수평 및 수직으로 반복됩니다.
...
// load background sprite in the preload function:
game.load.image('background', 'assets/background.png');
...
// place background in the create function:
this.background = game.add.tileSprite(0, 0, game.world.width, game.world.height, 'background');
...
목록 10 : 배경로드 및 배치

시차 스크롤링을 구현하기 위해, 먼저, create 함수에서 카메라 위치는 목록 11에 표시된대로 저장됩니다. 이 마지막 위치는 시차 스크롤을 구현하기 위해 다음 update 루프에서 사용되기 때문에 cameraLastX 및 cameraLastY로 표시됩니다.
...
// store cameras position:
this.cameraLastX = this.camera.x;
this.cameraLastY = this.camera.y;
...
목록 11 : create 함수에 카메라 위치를 저장.

update 함수에서 카메라의 현재 위치는 목록 12에 표시된 마지막 저장된 위치와 비교할 수 있습니다. 위치가 변경되면 배경이 이동되고 새 위치가 저장됩니다.
...
// compare current camera position with last one and shift background if it differs:
if(game.camera.x !== this.cameraLastX){
 this.background.x -= 0.2 * (this.cameraLastX - game.camera.x);
 this.cameraLastX = game.camera.x;
}
if(game.camera.y !== this.cameraLastY){
 this.background.y -= 0.2 * (this.cameraLastY - game.camera.y);
 this.cameraLastY = game.camera.y;
}
...
목록 12 : 현재 카메라 위치를 마지막 저장된 위치와 비교하여 배경을 이동.


소리
소리 때문에 나는 주로 몇 달 동안 Gravity Quest에서 수집한 품질 원천에 의존했습니다. 내 Assets은 다음과 같습니다.

- UniversalSoundFX는 (상용) 프로젝트 / 게임에서 로열티 없이 사용할 수있는 3000 가지 이상의 사운드 라이브러리입니다. Richard Davey (Phaser의 제작자)가 한 번 그것에 대해 트윗하기 때문에 나는 그것에 대해 알게되었습니다. 나는 다양한 게임에 맞는 다양한 사운드를 고려하여 20$를 좋은 투자로 생각합니다.
- Sfrx와 그 기반의 Bfrx는 임의로 특정 스타일의 사운드를 만들고 다양한 속성과 관련하여 미세 조정할 수있는 도구입니다. 나는 이 도구들을 익히지 못했지만 그들이 생성 한 무작위 소리 중 2 개가 최종 게임으로 만들어졌습니다.
- CC 믹서는 크리에이티브 커먼즈 라이선스에 따라 음악을 저장하는 곳입니다. 이 사이트에서는 제목이나 제작자에 관한 노래를 검색하고 노래를 올바르게 사용하는 데 필요한 것이 무엇인지 명확하게 명시합니다 (상업적으로). Gravity 퀘스트는 Javolenus의 Nickleus가 게임 메뉴를 제공하는 'C95 - 일상 유지 임무'와 소개를위한 Karsten Holy Moly의 'Space Intro'를 제공합니다. 두 곡 모두 엔딩 점수에 나열됩니다.
- Nosoapradio는 Deceased Superior Technician (DST)의 음악 보관소입니다. 노래는 (상업용) 게임에서 사용할 수 있으며 웹 사이트에는 적합한 노래를 선택하기위한 매우 훌륭한 필터링 인터페이스가 있습니다. Gravity 퀘스트는 Outro에서 'ALightIntro'를 특징으로하며,이 곡은 엔딩 스코어에 표시됩니다.


확실히 게임에서 사용할 수있는 (무료) 오디오 파일의 훨씬 좋은 소스가 있습니다. 

플레이 가능한 데모


결론
게임을 위한 훌륭한 비주얼과 사운드를 만드는 일은 마찬가지로 어렵습니다. Gravity Quest를 만들기 퀘스트의 비주얼과 사운드는 완벽하지는 않지만, 게임의 논리를 코딩하는 것보다 시간이 더 걸렸습니다. 이 문서가 시작하기에 흥미로운 자료를 제공하기 바랍니다. 이 기사에서 링크 된 데모의 완전한 주석 소스 코드는 GitHub에서 일반적으로 제공되는 것과 같습니다. 다음 문서에서는 참여 방식을 사용하여 Gravity Quest의 레벨을 만드는 방법에 대해 설명하겠습니다.

댓글 없음:

댓글 쓰기