티스토리 뷰
#11 Scene 만들기2 - LoadingScene, PlayingScene, player, background
RadderNepa 2023. 5. 14. 19:32- create function : 게임이 실행됐을 때 첫 번째로 실행되는 function
- update function : create function 이후에 계속해서 실행되는 function
● LoadingScene
- LoadingScene은 게임에서 쓰일 asset들을 load 할 scene이다.
- load 해 놓은 asset은 전역적으로 사용 가능하다.
- '루트폴더/src/scenes' 폴더를 만든 후 해당 폴더 안에 scene 파일을 만들면 된다.
- scenes 폴더 내에 LoadingScene.js를 생성해보자(나는 저번 글에서 이미 생성함)
// asset import
import Phaser from "phaser";
import fontPng from "../assets/font/font.png";
import fontXml from "../assets/font/font.xml";
import bgImg1 from "../assets/images/background.png";
import bgImg2 from "../assets/images/background-2.png";
import bgImg3 from "../assets/images/background-3.png";
import beamImg from "../assets/images/beam.png";
import explosionImg from "../assets/spritesheets/explosion.png";
import playerImg from "../assets/spritesheets/player.png";
import expUpImg from "../assets/spritesheets/expUp.png";
import mobImg1 from "../assets/spritesheets/mob1.png";
import mobImg2 from "../assets/spritesheets/mob2.png";
import mobImg3 from "../assets/spritesheets/mob3.png";
import mobImg4 from "../assets/spritesheets/mob4.png";
import lionImg from "../assets/spritesheets/lion.png";
import catnipImg from "../assets/spritesheets/catnip.png";
import clawWhiteImg from "../assets/spritesheets/claw-white.png";
import clawYellowImg from "../assets/spritesheets/claw-yellow.png";
import beamOgg from "../assets/sounds/beam.ogg";
import scratchOgg from "../assets/sounds/scratch.ogg";
import hitMobOgg from "../assets/sounds/hitMob.ogg";
import growlOgg from "../assets/sounds/growl.ogg";
import explosionOgg from "../assets/sounds/explosion.ogg";
import hurtOgg from "../assets/sounds/hurt.ogg";
import expUpOgg from "../assets/sounds/expUp.ogg";
import nextLevelOgg from "../assets/sounds/nextLevel.ogg"
import gameOverOgg from "../assets/sounds/gameover.ogg";
import gameClearOgg from "../assets/sounds/gameClear.ogg";
import pauseInOgg from "../assets/sounds/pauseIn.ogg";
import pauseOutOgg from "../assets/sounds/pauseOut.ogg";
// 사용할 모든 asset(image, sprite image, audio, font 등)을 load해 놓는다.
export default class LoadingScene extends Phaser.Scene {
constructor() {
// super에 파라미터로 넘겨주는 string이 해당 scene(= 여기서는 LoadingScene)의 identifier(식별자)가 된다.
super("bootGame");
}
// asset들을 load하는 부분
preload() {
// ("식별자", import한 asset이름) --> "큰따옴표"안에 있는 string이 해당 asset의 identifier(식별자)가 된다.
// IMAGES
this.load.image("background1", bgImg1);
this.load.image("background2", bgImg2);
this.load.image("background3", bgImg3);
this.load.image("beam", beamImg);
// SPRITESHEETS
this.load.spritesheet("player", playerImg, {
frameWidth: 32, // 개발자가 직접 설정해주는 값(오브젝트 크기 조절)
frameHeight: 36, // 개발자가 직접 설정해주는 값(오브젝트 크기 조절)
});
this.load.spritesheet("mob1", mobImg1, {
frameWidth: 28,
frameHeight: 28,
});
this.load.spritesheet("mob2", mobImg2, {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet("mob3", mobImg3, {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet("mob4", mobImg4, {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet("lion", lionImg, {
frameWidth: 48,
frameHeight: 64,
});
this.load.spritesheet("explosion", explosionImg, {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet("claw_white", clawWhiteImg, {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet("claw_yellow", clawYellowImg, {
frameWidth: 32,
frameHeight: 32,
});
this.load.spritesheet("catnip", catnipImg, {
frameWidth: 64,
frameHeight: 64,
});
this.load.spritesheet("exp-up", expUpImg, {
frameWidth: 16,
frameHeight: 16,
});
// AUDIOS
this.load.audio("audio_beam", beamOgg);
this.load.audio("audio_scratch", scratchOgg);
this.load.audio("audio_hitMob", hitMobOgg);
this.load.audio("audio_growl", growlOgg);
this.load.audio("audio_explosion", explosionOgg);
this.load.audio("audio_expUp", expUpOgg);
this.load.audio("audio_hurt", hurtOgg);
this.load.audio("audio_nextLevel", nextLevelOgg);
this.load.audio("audio_gameOver", gameOverOgg);
this.load.audio("audio_gameClear", gameClearOgg);
this.load.audio("audio_pauseIn", pauseInOgg);
this.load.audio("audio_pauseOut", pauseOutOgg);
// FONT
this.load.bitmapFont("pixelFont", fontPng, fontXml);
}
// load 시간이 많이 걸리지 않아 LoadingScene이 화면에 직접적으로 보일 일은 없지만
// 혹시 모르니 “Loading game…”이라는 문구를 적어주었다.
// create function 처음에 최초 1회만 실행된다. / update fimction은 계속해서 실행된다.
create() {
this.add.text(20, 20, "Loading game...");
// create가 됐을 때 PlayingScene을 start 한다.
this.scene.start("playGame"); // playGame = PlayingScene.js의 identifier(식별자)
// MOBS
this.anims.create({
key: "mob1_anim",
frames: this.anims.generateFrameNumbers("mob1"),
frameRate: 12,
repeat: -1,
});
this.anims.create({
key: "mob2_anim",
frames: this.anims.generateFrameNumbers("mob2"),
frameRate: 12,
repeat: -1,
});
this.anims.create({
key: "mob3_anim",
frames: this.anims.generateFrameNumbers("mob3"),
frameRate: 12,
repeat: -1,
});
this.anims.create({
key: "mob4_anim",
frames: this.anims.generateFrameNumbers("mob4"),
frameRate: 12,
repeat: -1,
});
this.anims.create({
key: "lion_anim",
frames: this.anims.generateFrameNumbers("lion"),
frameRate: 12,
repeat: -1,
});
this.anims.create({
key: "lion_idle",
frames: this.anims.generateFrameNumbers("lion", {
start: 0,
end: 0,
}),
frameRate: 1,
repeat: 0,
});
// PLAYERS
this.anims.create({
key: "player_anim",
frames: this.anims.generateFrameNumbers("player"),
frameRate: 12,
repeat: -1,
});
this.anims.create({
key: "player_idle",
frames: this.anims.generateFrameNumbers("player", {
start: 0, // 0번째 순서의 이미지로 시작(이미지가 자연스럽게 이어져 보이도록하기 위해)
end: 0, // 0번째 순서의 이미지로 끝(이미지가 자연스럽게 이어져 보이도록하기 위해)
}),
frameRate: 1,
repeat: 0,
});
// EFFECT
this.anims.create({
key: "explode",
frames: this.anims.generateFrameNumbers("explosion"),
frameRate: 20,
repeat: 0,
hideOnComplete: true,
});
// ATTACKS
this.anims.create({
key: "scratch_white",
frames: this.anims.generateFrameNumbers("claw_white"),
frameRate: 20,
repeat: 0,
hideOnComplete: true,
});
this.anims.create({
key: "scratch_yellow",
frames: this.anims.generateFrameNumbers("claw_yellow"),
frameRate: 20,
repeat: 0,
hideOnComplete: true,
});
this.anims.create({
key: "catnip_anim",
frames: this.anims.generateFrameNumbers("catnip"),
frameRate: 20,
repeat: -1,
});
// EXP UP ITEMS
this.anims.create({
key: "red",
frames: this.anims.generateFrameNumbers("exp-up", {
start: 0,
end: 0,
}),
frameRate: 20,
repeat: 0,
});
this.anims.create({
key: "blue",
frames: this.anims.generateFrameNumbers("exp-up", {
start: 1,
end: 1,
}),
frameRate: 20,
repeat: 0,
});
this.anims.create({
key: "yellow",
frames: this.anims.generateFrameNumbers("exp-up", {
start: 2,
end: 2,
}),
frameRate: 20,
repeat: 0,
});
this.anims.create({
key: "green",
frames: this.anims.generateFrameNumbers("exp-up", {
start: 3,
end: 3,
}),
frameRate: 20,
repeat: 0,
});
}
}
- constructor의 super 안에 담긴 string은 해당 scene의 identifier(식별자)가 된다.
- 해당 identifier를 이용해 scene을 실행, 정지, 멈출 수 있다.
※ 주의 : class 이름이 아니라 identifier를 이용하는 것이다. 혼동하지 말자
cf)
[spriteimage 애니메이션 생성]
import playerImg from "../assets/spritesheets/player.png";
export default class LoadingScene extends Phaser.Scene {
constructor() {
super("bootGame");
}
preload() {
// SPRITESHEETS
this.load.spritesheet("player", playerImg, {
frameWidth: 32, // 개발자가 직접 설정해주는 값(오브젝트 크기 조절)
frameHeight: 36, // 개발자가 직접 설정해주는 값(오브젝트 크기 조절)
});
}
create() {
this.anims.create({
key: "player_idle",
frames: this.anims.generateFrameNumbers("player", {
start: 0, // 0번째 순서의 이미지로 시작(이미지가 자연스럽게 이어져 보이도록하기 위해)
end: 0, // 0번째 순서의 이미지로 끝(이미지가 자연스럽게 이어져 보이도록하기 위해)
}),
frameRate: 1,
repeat: 0,
});
}
}
========================================================================================================
1. key: 해당 애니메이션의 key(= identifier, 식별자)가 된다.
--> 해당 key 값을 가지고 애니메이션을 컨트롤
2. frames: 'this.anims.generateFrameNumbers("load한 spritesheet의 key")'로 입력
--> 어떤 것을 애니메이션의 프레임으로 사용할 것인지 지정
--> start, end를 설정해 같은 spritesheet로 여러 애니메이션을 만들 수 있다.
3. frameRate: 초당 프레임 수를 설정(디폴트 : 24) = 1초에 어느정도 반복 할 것이냐
4. repeat: 애니메이션 반복 횟수(-1 = 무한 반복)
5. hideOnComplete: true로 설정하면 애니메이션이 끝난 후 이미지가 화면에서 사라진다.(디폴트 = false)
바울랩, 위니브
제주 ICT 코딩 컴퓨터학원, 연구원, 출판사
paullab.co.kr
- start와 end 옵션은 위 사이트의 건물 이미지 움직임처럼 구현하기 위해서 설정한 옵션이다.
- 이미지를 잘라내 빠르게 보여줘서 마치 이미지가 움직이는 것처럼 보이게 하는 것이다.
● PlayingScene
- PlayingScene을 만들기 전에 그 안에 들어가는 background 관련 기능과 player를 먼저 만들어야한다.
- 루트폴더/src/characters 폴더를 만든 후 해당 폴더 안에 player, mob 등의 캐릭터 클래스 파일들을 만들어보자
1.
- characters 폴더안에 Player.js 파일을 만들자
[src/characters/Player.js]
import Phaser from "phaser";
import Config from "../Config";
// Player가 Arcade 물리엔진의 영향을 받도록 extends
export default class Player extends Phaser.Physics.Arcade.Sprite {
// 어느 scene에 Player가 나올지 정해줘야 하므로 argument로 scene을 받는다.
// 참고로 scene의 constructor는 parameter가 없다.
constructor(scene) {
// 화면의 가운데에 player를 추가
// 마지막 "player" string이 identifier(식별자)이다. by LoadingScene.js
super(scene, Config.width / 2, Config.height / 2, "player");
// scene.add.existing : scene에 오브젝트 추가
scene.add.existing(this);
// scene.physics.add.existing : scene의 물리엔진에 오브젝트를 추가
scene.physics.add.existing(this);
// scale 프로퍼티를 조절해 크기 조절(디폴트: 1)
this.scale = 2;
// depth를 조절해 어떤 오브젝트가 앞에 오고 뒤에 올지 설정
// CSS의 z-index와 비슷한 개념(디폴트: 0)
this.setDepth(20); // 10단위로 조절하는게 좋다.
// 해당 오브젝트가 물리적으로 어느정도의 면적을 차지할 지 설정하는 함수(디폴트는 이미지 사이즈)
// 하지만 디폴트는 추후 몹을 추가했을 때 잘 부딪히는 느낌이 드므로 원본 이미지보다 약간 작게 설정
this.setBodySize(28, 32);
}
}
2.
- 루트폴더/src/utils 폴더를 만들자
- 해당 폴더에는 각종 유용한 기능을 하는 함수들의 코드를 집어넣는다.
- 개발자가 유용하거나 자주 사용할 함수를 직접 작성해서 모아놓는 곳이다.
- utils 폴더 내에 backgroundManager.js를 생성
[src/utils/backgroundManager.js]
import Config from "../Config";
/**
* scene의 배경화면을 설정하는 함수
* @param {Phaser.Scene} scene
* @param {string} backgroundTexture
*/
export function setBackground(scene, backgroundTexture) {
// tileSprite : 게임 화면이 background image보다 큰 경우 background image를 타일처럼 붙여서 보여주는 이미지
// (CSS의 background-repeat: repeat; 같은 느낌)
scene.m_background = scene.add.tileSprite(
0,
0,
Config.width,
Config.height,
backgroundTexture
).setOrigin(0, 0); // setOrigin : tileSprite 자체의 원점(0, 0) 위치를 설정해주는 함수
}
- 붉은색이 게임 화면이고 하늘색이 background image 일때 background image가 작으면 게임 화면을 채우기 위해 바둑판식으로 background image를 다닥다닥 붙여 게임 화면을 채우는 것이다.
https://rexrainbow.github.io/phaser3-rex-notes/docs/site/tilesprite/
Tile sprite - Notes of Phaser 3
Tile sprite Introduction Display of repeating texture, built-in game object of phaser. Usage Load texture scene.load.image(key, url); Reference: load image Add tile sprite object var image = scene.add.tileSprite(x, y, width, height, textureKey); Add tile s
rexrainbow.github.io
3.
- src/scenes 폴더 내에 PlayingScene.js 파일 생성
- 위에서 만든 Player, setBackground function import
cf) LoadingScene의 create function이 실제 플레이 될 애니메이션 위주로 add했다면 PlayingScene의 create는 사운드 위주로 add 돼있다.
import Phaser from "phaser";
import Player from "../characters/Player";
import { setBackground } from "../utils/backgroundManager";
export default class PlayingScene extends Phaser.Scene {
constructor() {
super("playGame");
}
create() {
// 사용할 sound들을 추가해놓는 부분
// load function : 어떤 scene에서든 전역적으로 asset을 사용할 수 있도록 load (LoadingScene.js 참고)
// add function : 해당 scene에서 asset을 사용할 수 있도록 scene의 멤버 변수로 추가할 때 사용
// cf) this.xxxx 에서 xxxx 자리에 적힌 것들이 멤버 변수들이다.
this.sound.pauseOnBlur = false;
this.m_beamSound = this.sound.add("audio_beam");
this.m_scratchSound = this.sound.add("audio_scratch");
this.m_hitMobSound = this.sound.add("audio_hitMob");
this.m_growlSound = this.sound.add("audio_growl");
this.m_explosionSound = this.sound.add("audio_explosion");
this.m_expUpSound = this.sound.add("audio_expUp");
this.m_hurtSound = this.sound.add("audio_hurt");
this.m_nextLevelSound = this.sound.add("audio_nextLevel");
this.m_gameOverSound = this.sound.add("audio_gameOver");
this.m_gameClearSound = this.sound.add("audio_gameClear");
this.m_pauseInSound = this.sound.add("audio_pauseIn");
this.m_pauseOutSound = this.sound.add("audio_pauseOut");
// player를 m_player라는 멤버 변수로 추가
// new Player(this) --> (Player class의 인스턴스 생성, 자기 자신(PlayingScene)을 scene으로 전달)
this.m_player = new Player(this); // characters/Player.js
// PlayingScene의 background 설정
// 여기서 "background1"은 LoadingScene에서 load한 asset이다. --> this.load.image("background1", bgImg1);
setBackground(this, "background1"); // backgroundManager.js의 함수
}
}
● webpack 확장자명 추가
- 근데 이렇게 신나게 작성한 후 npm start를 하면 정작 아래와 같은 오류가 발생한다.
- 이것은 webpack 때문 발생하는 오류인데 정확히는 .ogg 확장자 파일을 처리하지 못 해서 그런것이다.
- webpack/base.js 파일을 보자, 현재 아래와 같다.
const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
mode: "development",
devtool: "eval-source-map",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: [/\.vert$/, /\.frag$/],
use: "raw-loader"
},
{
test: /\.(gif|png|jpe?g|svg|xml)$/i,
use: "file-loader"
}
]
},
plugins: [
new CleanWebpackPlugin({
root: path.resolve(__dirname, "../")
}),
new webpack.DefinePlugin({
CANVAS_RENDERER: JSON.stringify(true),
WEBGL_RENDERER: JSON.stringify(true)
}),
new HtmlWebpackPlugin({
template: "./index.html"
})
]
};
- 아래 부분을 수정해야 파일을 제대로 로드할 수 있다.(확장자에 ogg가 없기 때문에 오류 발생)
as-is --> test: /\.(gif|png|jpe?g|svg|xml)$/i,
to-be --> test: /\.(gif|png|jpe?g|svg|xml|mp3|ogg)$/i,
- 수정 후 서버를 다시 돌려보자, 아래의 화면이 나오면 정상적으로 된 것이다.
cf) 현재 폴더 구조
'흥미 > phaser' 카테고리의 다른 글
#13 움직이는 Player 2(무한 배경 구현) (0) | 2023.05.23 |
---|---|
#12 움직이는 Player 1 (0) | 2023.05.16 |
#10 Scene 만들기1 (0) | 2023.05.12 |
#9 템플릿 다운 (0) | 2023.05.07 |
#8 캐릭터 움직이기 tutorial (0) | 2023.05.06 |
- Total
- Today
- Yesterday
- SQL
- node.js
- 자료구조
- Phaser3
- nosql
- 코딩테스트
- spring
- jpa
- SpringBoot
- API
- 코테
- Advanced Stream
- git
- Spring Boot
- Phaser
- DART
- MySQL
- 운영체제
- MongoDB
- Stream
- Java8
- 프로세스
- 프로그래머스
- java
- OS
- 알고리즘
- 메모리
- db
- 빅데이터 분석기사
- 빅데이터
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |