본문 바로가기
프로그래밍/게임 개발

게임 개발 「 추천 편」Phaser.io 로 미니 게임 만들기

by grapedoukan 2024. 1. 18.
728x90

Phaser.io 는 HTML5 게임 빌드를 위해 특별히 설계된 무료 오픈 소스 JavaScript 프레임워크입니다. 초보자도 게임 개발 프로세스를 훨씬 쉽고 빠르게 만들 수 있는 강력한 도구 및 기능 세트를 제공합니다.

Framework에는 잘 문서화된 API와 항상 기꺼이 도움을 줄 수 있는 대규모 개발자 커뮤니티가 있습니다. 이렇게 하면 숙련된 프로그래머가 아니더라도 쉽게 배우고 사용할 수 있습니다.

Phaser는 간단한 2D 플랫포머에서 복잡한 RPG에 이르기까지 다양한 게임을 만드는 데 사용할 수 있습니다. 물리학, 애니메이션, 오디오 및 네트워킹을 포함한 다양한 기능을 지원합니다.

Phaser로 개발된 게임은 데스크톱, 노트북, 태블릿, 스마트폰을 포함하여 웹 브라우저가 있는 모든 장치에서 플레이할 수 있습니다. 고도로 최적화된 JavaScript 코드로 작성되어 구형 기기에서도 게임이 원활하게 실행됩니다.

요컨대, Phaser.io 는 재미있고 매력적인 HTML5 게임을 만들고자 하는 모든 사람에게 완벽한 강력한 게임 개발 프레임워크입니다.

로컬 환경 준비

Phaser.io 는 HTML5 프레임워크이기 때문에 로컬 환경에 대한 특별한 설정이 필요하지 않습니다.

기본 코드 편집기를 열고 그 안에 간단한 HTML5 프로젝트를 만듭니다.
그런 다음 게임의 컨테이너 역할을 할 요소를 추가하고 태그를 사용하여 Phaser 라이브러리를 포함합니다. 이 예에서는 Phaser 3.70.0을 사용합니다.<div>index.html<script>

그런 다음 게임 구성 및 로직을 포함할 별도의 JavaScript 파일을 만들고 HTML에 연결합니다.main.js

<body>
  <div id="game-holder"></div>

  <script src="//cdn.jsdelivr.net/npm/phaser@3.70.0/dist/phaser.min.js"></script>
  <script src="js/main.js"></script>
</body>

그거에요! 이제 게임 빌드를 시작할 준비가 되었습니다.

러너를 만들어 보겠습니다.

구성

파일에서 게임 구성을 정의하고 Phaser 게임을 초기화해야 합니다.main.js

게임 구성 개체에는 많은 속성이 포함되어 있지만 이 자습서에서는 렌더러, 치수, 물리 및 기본 장면만 설정해야 합니다.

Phaser 구성의 속성은 게임에서 사용하려는 렌더링 컨텍스트입니다. , 또는 일 수 있습니다.typePhaser.AUTOPhaser.CANVASPhaser.HEADLESSPhaser.WEBGL

canvas 요소의 크기는 및 속성으로 설정됩니다.widthheight

이 프로퍼티는 게임에서 환경설정과 함께 사용해야 하는 물리 시스템을 정의합니다.physics

// Phaser game config.
let gameConfig = {
  type: Phaser.CANVAS,
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    parent: "game-holder",
    width: 1600,
    height: 1200
  },
  physics: {
    default: "arcade",
    arcade: {
      gravity: {
        x: 0,
        y: 0
      }
    }
  },
  scene: [PlayGame]
}

구성을 추가한 후 main.js 다음 줄을 추가하여 Phaser 게임을 초기화해야 합니다.

// Init phaser game
new Phaser.Game(gameConfig);

장면

미니 게임의 아이디어는 러너를 만드는 것이었고, 한 방향으로 움직이는 플레이어(엘프 또는 Nisse)와 플레이어에게 다가가려고 하는 악당(Grinch)이 있습니다. 또한 플레이어와 악당 오브젝트를 놓을 표면이 필요합니다.

따라서 프로토 타입은 다음과 같습니다.

프로토타입을 위한 장면을 만들어 보겠습니다.

우선, 확장되는 클래스를 만들어야 합니다. 일반적으로 Phaser Scene 객체에는 , 및 의 세 가지 매직 함수가 포함되어 있습니다.Phaser.Scenepreloadcreateupdate

그런 다음 함수에서 자산(이미지, 스프라이트시트 및 배경)을 로드해야 합니다.preload()

export default class PlayGame extends Phaser.Scene {

  preload() {
    // Load surface image
    this.load.image("snow", "img/snow-planet.png");
    
    // Load player image
    this.load.image("player", "img/nisse.png");
    
    // Load grinch spritesheet
    this.load.spritesheet("grinch", "img/grinch.png", {
       frameWidth: 59,
       frameHeight: 88
    });
  }

  create() {}

  update() {}

}

그런 다음 함수에서 스프라이트에 대한 애니메이션을 정의하고, 표면을 그리고, 게임 오브젝트를 배치하고, 그들 사이의 충돌을 정의해야 합니다.create()

export default class PlayGame extends Phaser.Scene {

  preload() {
   ...
  }

  create() {
    // Grinch walk animation
    this.anims.create({
      key: "walk",
      frames: this.anims.generateFrameNumbers("grinch", {
        start: 0,
        end: 14,
        first: 0
      }),
      frameRate: 10,
      repeat: -1,
    });

    // Draw a big circle
    this.bigCircle = this.add.graphics();
    this.bigCircle.lineStyle(gameOptions.bigCircleThickness, gameOptions.color.lineColor, 0);
    this.bigCircle.strokeCircle(this.game.config.width / 2, this.game.config.height / 2, gameOptions.bigCircleRadius);

    // Add player image
    this.player = this.initGameCharacters("player");

    // Add grinch sprite
    this.grinch = this.initGameCharacters("grinch");
    // Play animation
    this.grinch.play("walk");
    this.grinch.body.onOverlap = true;
    this.grinch.setScale(2);

    // Collision
    this.physics.add.overlap(this.player, this.grinch, this.collider, null, this);
  }

  update() {}

}

또한 객체가 어떻게 움직이는지, 어떻게 회전하는지, 객체 속도가 어떻게 변경되는지를 정의해야 합니다.update()

컨트롤

플레이어를 달리게 하는 방법? 일반적으로 게임 개발자는 키보드 및/또는 마우스 입력을 사용하여 플레이어가 게임 월드를 이동하고 상호 작용할 수 있도록 합니다. 하지만 전략을 바꿔 마이크 입력을 활용하면 어떨까요?

마이크 입력 볼륨을 측정하고 이 값을 Nisse의 속도로 변환해 보겠습니다. 입력 볼륨이 높을수록 Nisse가 더 빨리 움직입니다. 또한 사운드 볼륨에 몇 가지 제한을 두어야 하므로 모든 소음을 사용하여 Nisse를 실행할 수 있는 것은 아닙니다.

우리는 플레이어가 자신의 캐릭터를 움직일 때 박수를 사용하기를 원합니다. Google에 따르면 평균 손뼉은 최대 60-90데시벨에 달할 수 있습니다. 플레이어가 박수를 치는 방법, 손바닥의 위치, 손이 이완되거나 긴장된 상태에 따라 최소값은 5.6dB가 될 수 있습니다. 볼륨 범위가 꽤 크기 때문에 최소값보다 약간 높은 숫자인 10dB를 사용하겠습니다. 이것은 최소 마이크 신호 볼륨입니다.

마이크 입력을 측정하기 위해 기본 JS 미디어 API를 사용합니다.

처음에는 기능이 있는 마이크에 액세스해야 합니다. 첫 번째 매개 변수는 요청할 미디어 유형을 지정하는 개체입니다. 두 번째 매개 변수는 성공 콜백 함수입니다. 마지막 매개 변수는 오류 콜백입니다.navigator.getUserMedia()

성공적인 콜백에서는 입력 스트림을 처리하고 평균 입력 볼륨을 계산해야 합니다.

export function startListening() {
  // Ensure compatibility with different browser APIs.
  navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

  // Check if browser supports accessing the microphone.
  if (navigator.getUserMedia) {
    // Request access to the microphone audio stream.
    navigator.getUserMedia({
      audio: true
    },
    // Handle successful audio stream access.
    function (stream) {
      // Create an AudioContext for audio processing.
      let audioContext = new AudioContext();

      // Create nodes for audio analysis and processing.
      let analyser = audioContext.createAnalyser();
      let microphone = audioContext.createMediaStreamSource(stream);
      let javascriptNode = audioContext.createScriptProcessor(2048, 1, 1);

      // Configure the analyzer node.
      analyser.smoothingTimeConstant = 0.8;
      analyser.fftSize = 1024;

      // Connect the audio nodes to form a processing pipeline.
      microphone.connect(analyser);
      analyser.connect(javascriptNode);
      javascriptNode.connect(audioContext.destination);

      // Function to process audio data on each processing cycle.
      javascriptNode.onaudioprocess = function () {
        // Array to hold frequency data from the analyzer.
        var array = new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(array);

        // Calculate the average frequency value.
        var values = 0;
        var length = array.length;
        for (var i = 0; i < length; i++) {
          values += (array[i]);
        }

        // Set a value based on average frequency (average input volume).
        playerOptions.micInputVolume = values / length;
      };
    },
    // Handle errors during microphone access.
    function (err) {
      console.log("The following error occured: " + err.name);
    });
  } else {
    // Log a message if browser doesn't support getUserMedia.
    console.log("getUserMedia not supported");
  }
}

그 후, 씬 함수에서 값을 처리하여 Nisse의 속도로 변환할 수 있습니다.update()playerOptions.micInputVolume

그린치의 속도도 그린치와 플레이어 사이의 거리에 따라 조정되어야 합니다. 운 좋게도 Phaser에는 두 점 사이의 거리를 계산하고 숫자 값을 반환하는 기능이 내장되어 있습니다. 따라서 Nisse와 Grinch 사이의 거리가 300 이상이면 Grinch의 속도를 높입니다.Phaser.Math.Distance.Between(x1, y1, x2, y2)

Grinch와 Nisse 사이의 충돌이 감지되면 마이크 청취를 중지하고 둘 다의 속도를 0으로 설정하십시오.

export default class PlayGame extends Phaser.Scene {

  preload() {
    ...
  }

  create() {
    ...
  }

  update() {
    // Check if microphone input volume exceeds a threshold.
    if (playerOptions.micInputVolume > gameOptions.minMicInputVolume) {
      // Calculate player velocity based on mic input volume.
      let velocity = parseFloat((playerOptions.micInputVolume / 100).toFixed(2));
      // Chech if calculated velocity lower than max allowed velocity.
      if (velocity > gameOptions.maxVelocity) {
        velocity = gameOptions.maxVelocity;
      }
      // Set player velocity.
      playerOptions.velocity = velocity;
    } else {
      // Set a negative velocity if clap isn't detected or if it is too quiet.
      playerOptions.velocity = playerOptions.negativeVelocity;
    }
  
    // If collision is detected, stop all movement.
    if (gameOptions.stopListening) {
      playerOptions.velocity = 0;
      playerOptions.speed = 0;
      grinchOptions.speed = 0;
    } else {
      // Calculate distance between player and grinch.
      let distance = Phaser.Math.Distance.Between(this.grinch.x, this.grinch.y, this.player.x, this.player.y);
      // If they're far apart, increase grinch's speed.
      if (Math.round(distance) > 300) {
        grinchOptions.speed = grinchOptions.speed + grinchOptions.velocity;
      }
    }
  
    // If player has non-zero velocity, adjust movement speed.
    if (playerOptions.velocity !== 0) {
      let improvedPlayerSpeed = playerOptions.speed + playerOptions.velocity / 100;
      // Check if calculated speed is lower that max allowed player speed.
      if (improvedPlayerSpeed > playerOptions.maxPlayerSpeed) {
        improvedPlayerSpeed = playerOptions.maxPlayerSpeed;
      }
      // Ensure player speed is positive.
      if (parseFloat(improvedPlayerSpeed.toFixed(2)) > 0) {
        playerOptions.speed = improvedPlayerSpeed;
      } else {
        // If speed becomes zero or negative, set a minimum speed.
        playerOptions.velocity = 0;
        playerOptions.speed = 0.05;
      }
    }
  
    // Character Rotation and Positioning.
    // Update previous angles for both player and grinch.
    this.player.previousAngle = this.player.currentAngle;
    this.grinch.previousAngle = this.grinch.currentAngle;
  
    // Calculate new angles based on their speeds.
    this.player.currentAngle = Phaser.Math.Angle.WrapDegrees(this.player.currentAngle + playerOptions.speed);
    this.grinch.currentAngle = Phaser.Math.Angle.WrapDegrees(this.grinch.currentAngle + grinchOptions.speed);
  
    // Apply rotation and positioning to both sprites.
    this.rotateGameCharacter(this.player);
    this.rotateGameCharacter(this.grinch);
  }

}

게임 테스트

그거에요! 게임을 테스트하려면 웹 브라우저에서 파일을 엽니다.index.html

다음은 이 튜토리얼에서 빌드한 게임의 간단한 데모입니다. 관심이 있는 경우 코드 저장소를 자유롭게 확인하십시오.

728x90