Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Tags
more
Archives
Today
Total
관리 메뉴

하루에 하나씩

[Phaser3] TypeScript + React에서이동 구현하기 본문

React

[Phaser3] TypeScript + React에서이동 구현하기

BGK97 2024. 9. 24. 10:06

Phaser3란

  • canvas API를 매핑한 JavaScript 기반의 게임 프레임워크
  • 유니티, 언리얼 엔진을 제외하고, 2D게임을 만드는데 유용하다!

개발에 앞서 알아야할 사실들

  • Phaser의 경우 JavaScript로 구현하기 때문에, React + TypeScript환경에서 적절하게 코드를 수정해야한다.
  • Phaser는 Scene이라는 단위로 맵, 화면을 구성한다. 게임 로직을 구성할 때 사용한다. (효과음 또는 동작 등...)

개발 진행

1. Phaser 설치

  • VScode에서 다음과 같은 명령어를 입력하여 간단하게 설치가 가능하다.
npm i phaser

 

2. 프로젝트 생성

  • Vite를 이용해서 기본 프로젝트를 생성한다! 
npx create vite@latest [프로젝트명] -- --template react-ts
  • npx create vite@latest [프로젝트명]
    • vite를 최신버전으로 설치한다. 프로젝트명으로 이름을 정하면 된다.
  • -- 
    • vite의 추가옵션 전달 시 사용
  • --template react-ts
    • react + typescript를 사용해 템플릿을 생성하는 옵션

>> 다 받은 이후에는, 본인의 프로젝트 성격에 맞게 src폴더와 vite.config.ts를 알아서 수정하면 된다!

 

*** 게임 생성에 앞서, 알아야할 사실 ***
Phaser는 자바스크립트 기반이므로, React에 맞게 변경해주어야한다!!

3. 게임 Config 생성

  • 게임 config 생성하기
const config: Phaser.Types.Core.GameConfig = {
            type: Phaser.AUTO,
            width: 800,
            height: 600,
            physics: {
                default: 'arcade',
                arcade: {
                    gravity: { x: 0, y: 0 },
                    fps: 60, // 물리 엔진의 프레임 속도 설정
                    tileBias: 16,
                    debug: true,
                }
            },
            scene: {
                preload,
                create,
                update
            }
        };
        
               // Phaser 게임 생성
        const game = new Phaser.Game(config);

        // 컴포넌트 언마운트 시 Phaser 게임 종료
        return () => {
            game.destroy(true);
        };
  • Phaser의 경우 config 설정을 통해 게임 크기및 물리 객체를 선언한다.

** Phaser3 config의 주요 옵션들

  • type
    • Phaser의 랜더링 방식을 결정한다.
    • WEBGL, CANVAS 모드가 있으며  기본 값으로는 Phaser.AUTO를 사용
  • width, height
    • phaser를 생성하는 캔버스 크기를 조절, 단위는 픽셀 단위이다.
  • background
    • phaser 배경의 색을 지정, 여기서는 배경에 사진을 삽입할 것이기 때문에 굳이 필요없음!
  • physics 
    • 물리엔진 설정에 사용하는 속성
    • arcade, matter, impact가 존재
      1. arcade - 가벼운 스타일의 2D게임의 물리엔진을 만드는데 사용!, 박스 기반
      2. matter - 복잡한 스타일의 2D게임의 물리엔진을 만드는데 사용
      3. impact - impact.js 라이브러리에서 파생, HTML 기반 게임에 최적화 되어있음
    • 이후 내부에서 gravity(중력), tileBias(타일 편향) 등을 설정하면 됨
  • scene
    • phaser에서 사용할 함수(메서드)를 선언하는 곳
  • 이후에 new Phaser.Game(config) 명령어를 통해 게임을 생성하면된다.
  • ** game.destroy(true)설정을 해주어야, 언마운트 시 게임이 종료되므로 꼭 해줄 것!!

4. 게임 배경 및 플레이어 생성 (create & preload & update)

let player: Phaser.Physics.Arcade.Sprite;
    let cursors: Phaser.Types.Input.Keyboard.CursorKeys;

    function preload(this: Phaser.Scene) {
        // 'player'라는 키로 스프라이트 시트를 로드함
        this.load.image('bg', './sky.png');
        this.load.spritesheet('player', './ninja_skeleton.png', { frameWidth: 16, frameHeight: 19});
    }

    function create(this: Phaser.Scene) {
        this.physics.world.setBounds(0, 0, 1600, 1200);
        
        const bg = this.add.image(400, 300, 'bg');
        bg.setDisplaySize(2600, 2000);

        // 'player' 스프라이트 시트를 사용해 캐릭터를 생성
        player = this.physics.add.sprite(400, 300, 'player');
        player.setCollideWorldBounds(true);
        player.setScale(2);

        this.cameras.main.setBounds(0, 0, 1600, 1200);
        this.cameras.main.startFollow(player, true, 0.1, 0.1); // 부드러운 카메라 이동

        // 'player' 키를 사용하여 애니메이션 생성
        this.anims.create({
            key: 'walk',
            frames: this.anims.generateFrameNumbers('player', { start: 0, end: 7 }),
            frameRate: 10,
            repeat: -1
        });

        cursors = this.input.keyboard!.createCursorKeys();
    }

    function update(this: Phaser.Scene) {
        player.setVelocity(0);

        let moving = false;
        const speed = 160;

        if (cursors.left.isDown) {
            player.setVelocityX(-speed);
            player.flipX = true;
            moving = true;
        } if (cursors.right.isDown) {
            player.setVelocityX(speed);
            player.flipX = false;
            moving = true;
        } if (cursors.up.isDown) {
            player.setVelocityY(-speed);
            moving = true;
        } if (cursors.down.isDown) {
            player.setVelocityY(speed);
            moving = true;
        }

        if (moving) {
            if (!player.anims.isPlaying || player.anims.currentAnim?.key !== 'walk') {
                player.anims.play('walk', true);
            }
        } else {
            player.anims.stop();
            player.setFrame(0);
        }
    }

    return <div id="phaser-game-container" />;
};
  • React의 경우 preload() 가 지원이 안되더라... 그래서 function으로 선언해주고, 이 안에 Phaser 코드를 작성한다!
  • player와 이동에 사용할 cursor를 선언해준다.
  • preload
    • 플레이어나 배경을 load하는 부분이다. 
    • 이 때, animation을 사용할 것이라면 image가 아닌 spritesheet로 선언을 해줘야 한다!
  • create
    • preload에서 생성한 플레이어나 애니메이션 등을 불러오는 구간
config에서 설정한 크기와 physic에서 설정한 크기가 다른데요???
- config에서 사용하는 값은 게임화면(사용자가 볼 수 있는 화면)의 크기를 결정하는 곳
- physics에서 설정한 Bound 값은 물리객체가 이동할수 있는 최대 크기를 설정!!! 

-> 요약하자면, config는 사용자의 가시 화면 크기, physics에서는 전체 크기를 설정한다. 
  • setDisplaySize
    • 사용한 배경의 크기를 조절하는 부분! 이미지의 크기가, 작더라도 이 옵션을 통해 확장할 수 있다!
  • physics.add.sprite와 setCollideWorldBounds
    • 함수 이름 그대로 물리객체로 추가를 하고, 물리적인 세계(게임 전체 크기)내에서만 움직이게 한다.

  • anims.create 
    • spritesheet에 대한 애니메이션을 생성한다.
    • 위 사진과 같이, 여러 프레임으로 구성되어있어야 한다!
    • key값을 설정하여 나중에 animation을 호출할 때, key값을 통해 호출
    • frames을 통해  애니메이션을 적용할 부분을 설정
    • frameRate는 프레임의 부드러운 정도를 설정하는 부분으로, 10~15가 적당
    • repeat로 반복을 설정, -1로 설정시 무한 반복
  • update
    • 게임 진행중, 변경사항이 생겼을 때 실행되는 부분
      • 움직임 혹은 물리 객체 충돌 등 부분을 설정

5. 결과 화면!

  • 다음과 같이 잘 움직이고, 배경도 잘 표현 된다.

 

6. 새로 안 사실

  • 페이저에서 이동을 구현할 때, if - else if문으로 구현하면 대각선 이동이 불가하다. 각각을 분기점으로 처리해서 그런가보다. 그래서, if만 사용하여 동시 적용이 가능하도록 하였더니 대각선 이동이 되더라!
  • player에서 애니메이션 설정할 때, frameWidth, frameHeight를 이미지 픽셀 크기에 맞추어 설정을 해야한다. 안그러면 프레임 이미지가 깨져 보여서, 이동하는데 매우매우매우매우매우매우 불편함이 느껴진다.