정보실

웹학교

정보실

javascript MelonJS로 타이핑 게임 만들기

본문

MelonJS 및 Tiled를 사용하여 JS 게임을 빌드하는 방법 알아보기


1*1Dr1junJgR8Ygo-uQsEP1A.png 


https://blog.bitsrc.io/writing-a-typing-game-with-melonjs-ef0dd42f37bf 


게임 개발은 Unity 또는 Unreal Engine4를 사용하는 사람들에게만 제한되는 것이 아닙니다. 자바 스크립트 게임 개발은 꽤 오랫동안 진행되어왔습니다. 실제로 가장 인기 있는 브라우저 (예 : Chrome, Firefox 및 Edge)의 최신 버전은 매우 흥미로운 게임 개발 기회에 적합한 고급 그래픽 렌더링 (예 : WebGL)을 지원합니다.


게임 개발을 위해 WebGL을 활용하는 법을 배우는 것은 단일 기사에서 다룰 수 있는 것이 아니며 (개인 만을 위해 작성된 전체 책이 있음) 개인적 취향에 따라, 나는 원하기 전에 프레임 워크의 도움을 사용하는 경향이 있습니다. 특정 기술을 깊이 파헤쳐 보십시오.


이것이 연구를 마친 후 MelonJS를 사용하여 이 빠른 자습서를 작성하기로 결정한 이유입니다.


MelonJS 란 무엇입니까? 


지금까지 짐작 하셨겠지만 MelonJS는 모든 주요 브라우저 (Chrome에서 Opera, 모바일 용 Chrome 및 iOS Safari를 통하는 모든 브라우저)와 완벽하게 호환되는 JavaScript 게임 엔진입니다.


그것은 내 연구 중에 매우 매력적으로 만든 일련의 기능을 가지고 있습니다.


  • 우선, 그것은 완전히 독립적이며, 그것을 작동 시키는 데 필요한 외부 의존성이 없습니다.
  • 그러나 Tiled (게임의 맵과 스테이지를 만드는 데 도움이 됨), TexturePacker (필요한 텍스처 아틀라스를 만드는 데 도움이 됨)와 같이 인생을 훨씬 쉽게 만들어 주는 여러 타사 도구와 통합되었습니다. 스프라이트 관리 단순화 및 최적화).
  • 2D 물리 엔진 통합 즉, 즉시 사용 가능한 2D 현실적인 움직임 및 충돌 감지에 액세스 할 수 있습니다. 이것은 모든 작업을 수행해야 하기 때문에 많은 작업이 필요하기 때문에 매우 중요합니다 (실제로 내 차가 아닌 매우 흥미로운 수학은 말할 것도 없습니다).
  • 사운드 API를 지원하여 뛰어난 단순성과 사운드 효과 및 배경 음악을 추가 할 수 있습니다.

이 엔진에는 다른 놀라운 기능이 있으며 웹 사이트에서 확인할 수 있지만 위의 기능은 이 기사에서 가장 중요하게 다루는 기능입니다.


팁 : 비트 (Github)를 사용하여 프로젝트 전체에서 JS 모듈 및 UI 구성 요소를 쉽게 공유하고 재사용 하고 업데이트를 제안하고 변경 사항을 동기화 하며 팀으로 빠르게 구축하십시오.


1*ODhvAjHjB8zPErBYhOFePQ.gif 


게임 계획 


타이핑 게임의 목적은 플레이어가 단어를 입력하거나 임의의 키를 누르는 것 만으로 어떤 종류의 동작을 수행하거나 수행 할 수 있는 능력을 제공하는 것입니다.


나는 어렸을 때 다시 타이핑하는 법을 배웠다는 것을 기억합니다. 아래에서 블록을 쳐서. 아래 이미지는 게임의 모습과 어떤 종류의 상호 작용을 할 수 있는 지에 대한 아이디어를 제공합니다.


1*6QwyoOAjJvSnrNY2gKv-pw.png 


재미있는 작은 게임이었지만 실제로는 platfomer가 아니었습니다. Mario가 수행 할 작업에 대해 말해야 했지만 모든 키에 항상 단일 옵션이 있었으며 결코 실패하지 않았습니다.


이 기사에서는 좀 더 흥미롭게 만들고 싶기 때문에 위와 같은 간단한 타이핑 게임을 만드는 대신 다음을 수행합니다.


한 글자로 다음 행동을 결정하는 대신, 게임은 다섯 가지 대안을 제공 할 것이며, 각각에 대해 전체 단어를 작성해야 합니다.

  1. 앞으로 이동 (move forward)
  2. 앞으로 뛰어 (jump forward)
  3. 뛰어 내리다 (jump up)
  4. 뒤로 점프 (jump backwards)
  5. 뒤로 이동 (move backwards)

다시 말해, 고전적인 화살표 기반 컨트롤 대신 입력 한 단어를 사용하여 캐릭터를 움직일 수 있습니다.


그 이외의 게임은 플레이어가 동전을 모으는 주위를 돌아 다니는 고전적인 플랫 포머가 될 것입니다. 간결하게 하기 위해 이 튜토리얼에서는 적과 다른 유형의 엔티티를 유지합니다 (사용 된 코드를 추정하고 이를 기반으로 고유 한 엔티티를 작성할 수는 있지만).


이 기사를 합리적인 길이로 유지하기 위해 단일 스테이지, 전체 범위의 이동 (즉, 5 가지 작업을 모두 수행 할 수 있음)에 몇 가지 적, 한 가지 유형의 수집 가능하고 마지막으로 초점을 맞춥니다. 당신이 뛰어 다닐 수 있는 건강한 플랫폼.


필요한 도구 


melonJS는 완전히 독립적이지만 이 과정에서 많은 도움을 줄 수 있는 몇 가지 도구가 있습니다.

  • Texture Packer: 이 기능을 사용하면 텍스처 아틀라스를 자동 생성 할 수 있습니다. 이는 모든 이미지가 압축되어 나중에 엔진이 이미지를 검색하여 원하는 대로 사용할 수 있도록 JSON 파일을 말하는 또 다른 방법입니다. 이 유틸리티가 없으면 수동으로 아틀라스를 유지 관리하는 것이 너무 클 수 있습니다.
  • Tiled: 이것이 메인 레벨 에디터가 될 것입니다. 무료로 다운로드 할 수 있지만 ( "고마워요, 다운로드로 이동하십시오"라는 링크를 찾아야 함),이 놀라운 도구의 작성자에게 1 달러만 기부 할 수 있습니다. PayPal 계정이나 직불 카드를 사용할 수 있는 경우 이와 같은 소프트웨어를 유지 관리 해야 하며 시간과 노력이 필요합니다.


이러한 도구를 사용하면 튜토리얼을 따라 진행하고 완성 할 수 있으므로 코딩을 시작하겠습니다.


기본 플랫 포머 (platformer) 


이 프로젝트를 시작하기 위해 우리가 할 수 있는 최선의 방법은 (특히 이전에 melonJS와 접촉 한 적이 없는 경우) 샘플 코드를 사용하는 것입니다. 기본적으로 엔진을 다운로드하면 체크 아웃 할 수 있는 샘플 프로젝트 세트가 제공됩니다 (예제 폴더 안에 있음).


이 예제 코드는 프로젝트를 시작하는 데 사용하는 코드입니다.

  • 코드와 관련이 없는 모든 것을 포함하는 데이터 폴더. 여기에는 소리, 음악, 이미지,지도 정의 및 글꼴이 있습니다.
  • 모든 게임 관련 코드를 저장할 js 폴더

index.html 및 index.css 파일 이들은 외부 세계와 상호 작용하기 위해 앱에 필요한 연락처가 될 것입니다.


기존 코드 이해 


지금은 데이터 폴더 안에 리소스를 남겨두고 이 예제가 어떤 종류의 헤드 스타트를 제공하는지 이해해야 합니다. 이를 위해 게임을 실행하거나 계속 읽을 수 있습니다.


게임 실행 


게임을 실행하려면 몇 가지 사항이 필요합니다.

  • 멜론 JS의 사본. 다운로드 한 경우 dist 폴더의 컨텐츠를 가져 오십시오. 폴더 안에 복사하고 다른 JS 파일과 같이 index.html 파일에서 추가하십시오.
  • npm에서 사용 가능한 http-server 모듈을 설치하십시오 (아직 없는 경우).이 폴더 내의 모든 관련 파일을 빠르게 제공 할 수 있습니다. 아직 설치하지 않은 경우 다음을 수행하십시오.

$ npm install -g http-server 


그리고 일단 설치되면 프로젝트 폴더 내에서 다음을 실행하십시오.


$ http-server 


이를 통해 http://localhost:8080을 방문하여 게임을 테스트 할 수 있습니다.


코드 검토 


게임을 플레이 하면서, 당신은 기본 (그리고 매우 어색한) 움직임을 할 수 있는 la 플랫 포머, 두 개의 다른 적들과 하나의 소장품을 볼 수 있습니다. 기본적으로 우리는 이것을 달성하는 것을 목표로 했지만 약간 다른 제어 체계를 사용했습니다.


여기서 확인할 주요 파일은 다음과 같습니다.

  • game.js: 이 파일에는 모든 초기화 코드가 포함되어 있으며 게임 그래픽이 어떻게 인스턴스화 되고 주요 제어 방식이 되는지 흥미롭습니다.
  • screens/play.js: 레벨을 설정하는 데 필요한 모든 코드가 있습니다. 그렇게 많지 않다는 것을 알게 될 것입니다. 레벨 정의는 다른 도구 (예 : Tiled)를 사용하여 수행 되므로 이 ​​코드는 이를 가능하게 합니다.
  • entities/player.js: 분명히, 당신의 주요 목표. 이 파일에는 캐릭터의 이동 코드, 충돌 반응 및 제어 키 바인딩이 포함되어 있습니다. 그렇게 크지는 않지만 가장 많은 시간을 보내고 싶은 곳입니다.
  • entities/enemies.js: 플레이어 코드 다음으로, 사전 정의 된 좌표를 기반으로 자동화 된 동작을 설정하는 방법을 볼 수 있기 때문에 관련이 있습니다.

나머지 파일은 유용하지만 그다지 중요하지 않으므로 필요할 때 파일을 볼 수 있습니다.


모든 것이 어디에서 오는지 이해 


숙제를 끝냈다면 플레이어나 적을 인스턴스화 하는 선이 없는 것 같습니다. 그리고 그들의 좌표는 어디에도 없습니다. 그러면 게임은 어떻게 그것을 이해할 수 있습니까?


레벨 에디터가 등장합니다. Tiled를 다운로드 한 경우 폴더 데이터 / 맵 내에서 map1.tmx라는 파일을 열면 다음과 같은 내용이 표시됩니다.


1*iPWRfzyhXH_tFXgGV6OSMQ.png 


화면의 중앙 부분에 실제 설계 수준이 표시됩니다. 자세히 보면 이미지 뿐만 아니라 직사각형 모양도 볼 수 있으며 일부 색상과 이름이 다릅니다. 그것들은 이름과 그들이 속한 레이어에 따라 게임 내부의 것들을 나타내는 객체입니다.


화면 오른쪽 섹션으로 이동하면 레이어 목록 (오른쪽 상단 섹션)이 나타납니다. 여러 유형의 레이어가 있습니다.

  • 이미지 레이어 : 배경 또는 전경 이미지 용
  • 오브젝트 레이어 : 충돌 오브젝트, 엔티티 및 맵에서 인스턴스화 하려는 모든 것을 의미합니다.
  • 타일 ​​레이어 : 실제 레벨을 만들기 위해 타일을 배치 할 위치입니다.

이 오른쪽 하단에는 이 지도의 타일셋이 있습니다. Tiled는 Tiled를 통해서도 만들 수 있으며 이 폴더는 tsx 확장자를 가진 같은 폴더 안에 있습니다.


마지막으로 화면 왼쪽에 속성 섹션이 있습니다. 여기에서 선택한 개체 또는 클릭 한 레이어에 대한 세부 정보가 표시됩니다. 레이어의 색상과 같은 일반 속성을 변경하여 객체의 위치를 ​​더 잘 이해하고 사용자 지정 속성을 추가 할 수 있습니다 (나중에 게임 내 엔티티의 생성자에 인수로 전달됨).


그러나 이제는 불가능합니다. 해킹을 시작하는 데 필요한 모든 것이 있습니다.


운동 계획 변경 


코딩 준비가 되었으므로 이 기사의 주요 목표에 초점을 맞추고 예제로 제공되는 코드의 작동 버전을 파악하여 타이핑 게임처럼 작동하도록 수정하려고 합니다.


즉, 변경해야 할 첫 번째 사항은 이동 구성표, 즉 컨트롤 변경입니다.


entities/player.js로 가서 init 메소드를 확인하십시오. bindKey 및 bindGamepad 호출이 많이 나타납니다. 이러한 행은 본질적으로 특정 키를 논리적 동작과 바인딩 합니다. 간단히 말해, 오른쪽 화살표 키를 누르든 D 키를 누르든 아날로그 스틱을 오른쪽으로 움직이든 코드에서 동일한 "오른쪽"동작이 트리거 됩니다.


모든 것이 사라져야 하므로 제거하지 않아도 됩니다. 동시에, 새 파일을 만들어 wordServices.js라고 부르고 이 파일 안에는 매 차례마다 단어를 반환하는 객체를 만들어 플레이어가 선택한 행동을 이해하는 데 도움이 됩니다.


/**
 * Shuffles array in place.
 * @param {Array} a items An array containing the items.
 */
function shuffle(a) {
    var j, x, i;
    for (i = a.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = a[i];
        a[i] = a[j];
        a[j] = x;
    }
    return a;
}


ActionWordsService = {

    init: function(totalActions) {
        //load words...
        this.words = [
            "test", "hello", "auto", "bye", "mother", "son", "yellow", "perfect", "game"
        ]
        this.totalActions = totalActions
        this.currentWordSet = []
    },

    reshuffle: function() {
        this.words = shuffle(this.words)
    },

    getRegionPostfix: function(word) {
        let ws = this.currentWordSet.find( ws => {
            return ws.word == word
        })
        if(ws) return ws.regionPostfix
        return false
    },

    getAction: function(word) {
        let match = this.getWords().find( am => {
            return am.word == word
        })
        if(match) return match.action
        return false
    },

    getWords: function() {
        let actions = [ { action: "right", coords: [1, 0], regionPostfix: "right"}, 
                        { action: "left", coords: [-1, 0], regionPostfix: "left"}, 
                        { action: "jump-ahead", coords: [1,-0.5], regionPostfix: "upper-right"}, 
                        { action: "jump-back", coords:[-1, -0.5], regionPostfix: "upper-left"},
                        { action: "up", coords: [0, -1], regionPostfix: "up"}
                    ]

       this.currentWordSet = this.words.slice(0, this.totalActions).map( w => {
            let obj = actions.shift()
            obj.word = w
            return obj
       })
       return this.currentWordSet
    }
}

기본적으로 이 서비스에는 섞은 단어 목록이 포함되어 있으며 getWords 메서드를 사용하여 목록을 요청할 때마다 임의의 단어 집합을 가져와 위에서 언급 한 작업 중 하나에 할당했습니다. 관련된 각 작업과 관련된 다른 특성도 있습니다.

  • coords 속성은 액션 HUD를 기준으로 텍스트를 올바른 좌표에 배치하는 데 사용됩니다 (나중에 자세히 설명).
  • regionPostfix 속성은 액션 HUD에 대한 올바른 프레임을 선택하는 데 사용됩니다 (필수적으로 작성된 액션을 강조 표시 함).

이제 게임 중간에 사용자 입력을 요청하는 방법을 알아 보겠습니다.


참고 : 계속 진행하기 전에 이 코드를 나머지 코드에서 사용할 수 있게 하려면 다른 JS 라이브러리와 마찬가지로 index.html 파일에 해당 서비스를 포함 시켜야 합니다.

<script type="text/javascript" src="js/wordServices.js"></script> 


사용자 입력을 캡처하는 방법 


melonJS가 필요하지 않기 때문에 melonJS로 사용자 입력을 캡처 하는 것은 실제로 매우 쉽습니다. 결국 이것은 웹 페이지이며 이에 대한 좋은 입력 필드를 사용하는 것이 좋습니다.


게임 요소를 사용하여 입력 필드의 동작을 모방하기 위해 잠재적으로 키 바인딩 조합을 사용할 수 있지만 입력 필드가 기본적으로 제공하는 가능한 모든 조합 및 동작 (예 : 텍스트 붙여 넣기, 선택, 문자 삭제 없이 이동 등)을 고려할 수 있습니다. ), 사용하기 위해서는 모든 프로그램을 프로그래밍해야합니다.


대신, HTML 필드에 텍스트 필드를 추가하고 CSS를 사용하여 Canvas 요소 위에 있도록 스타일을 지정할 수 있으므로 게임의 일부가 됩니다.


<body> 요소 안에 이 코드 만 있으면 됩니다.


<input type="text" id="current-word" /> 


그런 다음 이것은 전적으로 귀하에게 달려 있지만 jQuery를 사용하여 키 누르기 이벤트에 콜백을 연결하는 데 필요한 코드를 단순화하는 것이 좋습니다. 이것은 물론 바닐라 JS를 사용하여 수행 할 수 있지만 이 라이브러리에서 제공하는 구문 설탕을 선호합니다.


game.js 파일의 로드 된 메소드에 있는 다음 코드는 사용자 입력을 처리하는 것입니다.


  me.$input = $("#current-word")

  let lastWord = ''
  me.$input.keydown( (evnt) => {

      if(evnt.which == 13) {
          console.log("Last word: ", lastWord)
          StateManager.set("lastWord", lastWord)
          lastWord = ''
          me.$input.val("")
      } else {
          if(evnt.which > 20) {
              let validChars = /[a-z0-9]+/gi
              if(!String.fromCharCode(evnt.which).match(validChars)) return false
          }

          setTimeout(_ => {
              lastWord = me.$input.val() //String.fromCharCode(evnt.which)
              console.log("Partial: ", lastWord)
          }, 1)
      }
      setTimeout(() => {
          StateManager.set("partialWord", me.$input.val())
      }, 1);
  })

본질적으로 입력 요소를 캡처 하여 me 전역 객체에 저장합니다. 이 전역 변수에는 게임에 필요한 모든 것이 포함되어 있습니다.


이를 통해 키를 누를 때마다 이벤트 처리기를 설정할 수 있습니다. 보시다시피, 플레이어가 입력을 마쳤을 때 키 코드 13 (ENTER 키를 나타냄)을 확인하고 있습니다. 그렇지 않으면 유효한 문자를 입력하고 있는지 확인합니다 (단순히 특수 문자를 피하고 있습니다) melonJS에서 제공하는 기본 글꼴에 문제가 없는 문자).


마지막으로 StateManager 객체에 플레이어가 입력 한 마지막 단어를 이해하기 위한 lastWord와 현재 입력하는 내용을 이해하기 위한 partialWord의 두 가지 상태를 설정합니다. 이 두 가지 상태는 잠시 후에 중요해질 것입니다.


컴포넌트 간 데이터 공유 


이것은 많은 프레임 워크에서 일반적인 문제입니다. 구성 요소 간에 데이터를 공유하는 방법은 무엇입니까? 이 경우 게임 구성 요소의 일부로 입력을 캡처 하고 있다는 것을 알고 해당 입력을 다른 사람과 공유하는 방법은 무엇입니까?


내 솔루션은 이벤트 이미 터 역할을 하는 전역 구성 요소를 만드는 것이었습니다.


const StateManager = {

    on: function(k, cb) {
        console.log("Adding observer for: ", k)
        if(!this.observers) {
            this.observers = {}
        }

        if(!this.observers[k]) {
            this.observers[k] = []
        }
        this.observers[k].push(cb)
    },
    clearObserver: function(k) {
        console.log("Removing observers for: ", k)
        this.observers[k] = []
    },
    trigger: function(k) {
        this.observers[k].forEach( cb => {
            cb(this.get(k))
        })
    },
    set: function(k, v) {
        this[k] = v
        this.trigger(k)
    },
    get: function(k) {
        return this[k]
    }

}

코드는 매우 간단하며 특정 상태에 대해 여러 "관찰자"(콜백 함수)를 설정할 수 있으며 해당 상태가 설정되면 (즉, 변경됨) 모든 콜백이 새로운 값으로 호출됩니다.


UI 추가 


실제 레벨을 만들기 전에 마지막 단계는 기본 UI를 표시하는 것입니다. 왜? 우리는 플레이어가 움직일 수 있는 다른 방향과 입력해야 하는 단어를 표시해야 하기 때문에.


이를 위해 두 가지 UI 요소를 사용합니다.

  • 하나는 그래픽에 대한 것으로, 여러 개의 서로 다른 프레임이 있으며, 하나는 기본적으로 보통 이미지에 대한 것이고, 다른 하나는 각 방향을 "선택된"것으로 표시합니다 (ActionWordsService의 "regionPostfix"속성과 연결됨)
  • 이전 지점에서 이미지 주위에 인쇄 될 텍스트를 위한 것입니다. 이것은 또한 ActionWordsService의 "coords"속성과 관련이 있습니다.

이를 위해 js 폴더 내에서 기존 HUD.js 파일을 히치 하이킹 할 수 있습니다. 여기에 두 가지 새로운 구성 요소를 추가하겠습니다.


첫 번째 ActionControl 구성 요소는 다음과 같습니다.


game.HUD.ActionControl = me.GUI_Object.extend({
    init: function(x, y, settings) {
        game.HUD.actionControlCoords.x = x //me.game.viewport.width - (me.game.viewport.width / 2)
        game.HUD.actionControlCoords.y = me.game.viewport.height - (me.game.viewport.height / 2) + y

        settings.image = game.texture;

        this._super(me.GUI_Object, "init", [
            game.HUD.actionControlCoords.x, 
            game.HUD.actionControlCoords.y, 
            settings
        ])

        //update the selected word as we type
        StateManager.on('partialWord', w => {
            let postfix = ActionWordsService.getRegionPostfix(w)
            if(postfix) {
                this.setRegion(game.texture.getRegion("action-wheel-" + postfix))
            } else {
                this.setRegion(game.texture.getRegion("action-wheel")
            }
            this.anchorPoint.set(0.5,1)
        })

        //react to the final word
        StateManager.on('lastWord', w => {
            let act = ActionWordsService.getAction(w)
            if(!act) {

                me.audio.play("error", false);
                me.game.viewport.shake(100, 200, me.game.viewport.AXIS.X)
                me.game.viewport.fadeOut("#f00", 150, function(){})
           } else {
               game.data.score += Constants.SCORES.CORRECT_WORD
           }
        })
    }
})

그것은 많은 것 같지만 몇 가지 일을 합니다.

  1. 설정 속성에서 좌표를 가져옵니다. Tiled에서 지도를 설정하면 검토하겠습니다.
  2. 입력 된 부분 단어에 반응하는 코드를 추가합니다. 현재 작성된 단어에 대해 접미사 속성을 사용하여 올바른 프레임을 선택합니다.
  3. 그리고 최종 단어에 반응하는 코드를 추가합니다. 해당 단어와 관련된 동작 (즉, 올바른 단어)이 있는 경우 플레이어의 점수에 점수가 추가됩니다. 그렇지 않으면 화면이 흔들리고 오류가 발생합니다.

작성 될 단어 인 두 번째 그래픽 구성 요소는 다음과 같습니다.

game.HUD.ActionWords = me.Renderable.extend({
    init: function(x, y) {
        this.relative = new me.Vector2d(x, y);

        this._super(me.Renderable, "init", [
            me.game.viewport.width + x,
            me.game.viewport.height + y,
            10, //x & y coordinates
            10
        ]);

         // Use screen coordinates
        this.floating = true;

        // make sure our object is always draw first
        this.z = Infinity;
        // create a font
        this.font = new me.BitmapText(0, 0, {
            font : "PressStart2P",
            size: 0.5,
            textAlign : "right",
            textBaseline : "bottom"
        });

        // recalculate the object position if the canvas is resize
        me.event.subscribe(me.event.CANVAS_ONRESIZE, (function(w, h){
            this.pos.set(w, h, 0).add(this.relative);
        }).bind(this));

        this.actionMapping = ActionWordsService.getWords()
    },

    update: function() {
        this.actionMapping = ActionWordsService.getWords()
        return true
    },
    draw: function(renderer) {
        this.actionMapping.forEach( am => {
            if(am.coords[0] == 0 && am.coords[1] == 1) return 
            let x = game.HUD.actionControlCoords.x + (am.coords[0]*80) + 30
            let y = game.HUD.actionControlCoords.y + (am.coords[1]*80) - 30
            this.font.draw(renderer, am.word, x, y)
        })
    }
})

이 구성 요소의 무거운 부분은 그리기 방법으로 수행됩니다. init 메소드는 단순히 변수를 초기화하는 것입니다. 그리기 호출 중에는 선택한 단어를 반복하고 관련 좌표와 고정 숫자 세트를 사용하여 ActionControl 구성 요소의 좌표 주위에 단어를 배치 할 수 있습니다.


동작 제어를 위해 제안 된 디자인은 다음과 같습니다 (좌표와의 관계).

1*70Q8QlYOq2Qa70AXGWICkw.png 


물론 배경은 투명해야 하지만 요점은 알 수 있습니다.


이 이미지를 /data/img/assets/UI 폴더에 저장하면 나중에 TexturePacker를 열 때 새 이미지를 인식하여 텍스처 아틀라스에 추가 할 수 있습니다.


1*0W7roZJ_YL4mDmCIR8SvfQ.png 


위 이미지는 액션 휠의 새 이미지가 추가 된 방법을 보여줍니다. 그런 다음“Publish sprite sheet”를 클릭하고 모든 기본 옵션을 수락하십시오. 기존 아틀라스를 덮어 쓰므로 코드는 아무것도 할 필요가 없습니다. 이 단계는 텍스쳐 아틀라스가 리소스로 로드 되고 (몇 분 안에 더 많은 내용이) 애니메이션과 같은 작업에 여러 엔티티가 사용하므로 게임에서 그래픽을 추가하거나 업데이트 할 때마다 항상 이 작업을 수행해야 합니다.


Tiled와 함께 모든 것을 결합 


자, 이제 기본 사항을 다루었으므로 게임을 함께합시다. 먼저 해야 할 것 :지도.


melonJS에 포함 된 타일 및 기본 타일 세트를 사용하여 이 맵 (32x32px 타일이 있는 25x16 타일 맵)을 만들었습니다.


1*iuoamO3UZ6QYBIXKVjLsGA.png 


기본적으로 다음은 내가 사용하는 레이어 입니다.


  • HUD : HUD.ActionControl이라는 단일 요소 만 포함합니다 (이름을 동일하게 유지하는 것이 중요합니다. 잠시 후 이유를 확인할 수 있습니다). 다음 이미지는이 요소의 속성을 보여줍니다 (사용자 정의 속성에 주목).

1*7IH50uAOId48uevC3xne2A.png 

  • 충돌(collision) : 기본적으로 melonJS는 "collision"이라는 단어로 시작하는 모든 레이어를 충돌 레이어로 가정합니다. 즉, 내부의 모든 모양을 통과 할 수 없습니다. 이 모양에서는 바닥과 플랫폼에 대한 모든 모양을 정의합니다.
  • player :이 레이어에는 단순히 mainPlayer 요소 (게임 시작 부분에 플레이어를 배치해야 하는 멜론 JS에 알려주는 모양)가 포함됩니다.
  • 엔티티(entities) :이 레이어에서 동전을 추가했습니다. 다시 말하지만, 그들의 이름은 중요하므로 코드에 등록한 이름과 일치해야 하므로 동일하게 유지하십시오 (계속 읽으십시오, 잠시 후에 의미가 있습니다) ).
  • 마지막 3개의 레이어만 있으면 지도와 배경 이미지를 추가 할 수 있습니다.

준비가 되면 game.js 파일로 이동하여 loaded 메소드 내부에 다음 줄을 추가합니다.


// register our objects entity in the object pool
me.pool.register("mainPlayer", game.PlayerEntity);
me.pool.register("CoinEntity", game.CoinEntity);
me.pool.register("HUD.ActionControl", game.HUD.ActionControl);

이 라인은 엔티티 (적어도 Tiled를 사용하여 맵에 직접 배치하려는 엔티티)를 등록합니다. 첫 번째 매개 변수로 제공 한 이름은 Tiled를 사용하여 일치시켜야 하는 이름입니다.


또한 이 파일에서 onLoad 메소드는 다음과 같아야 합니다.


  onload: function() {

        // init the video
        if (!me.video.init(965, 512, {wrapper : "screen", scale : "auto", scaleMethod : "fit", renderer : me.video.AUTO, subPixel : false })) {
            alert("Your browser does not support HTML5 canvas.");
            return;
        }

        // initialize the "sound engine"
        me.audio.init("mp3,ogg");

        // set all ressources to be loaded
        me.loader.preload(game.resources, this.loaded.bind(this));
        ActionWordsService.init(5)
    },

기본적으로 965x512의 해상도를 요청합니다 (화면 높이가 지도 높이와 같을 때 효과가 좋으며 16 * 32 = 512 임). 그런 다음 ActionWordsService는 5 워드로 초기화됩니다 (이것은 이동할 수 있는 5 가지 방향입니다).


onLoad 메소드의 다른 흥미로운 라인은 다음과 같습니다.


me.loader.preload(game.resources, this.loaded.bind(this)); 


지금까지 리소스에 대해서는 다루지 않았으므로 지금은 좋은 순간입니다.


자원 파일 


게임에 필요한 모든 유형의 리소스 (예 : 이미지, 사운드, 배경 음악, JSON 구성 파일 등)를이 파일 (resources.js 파일)에 추가해야 합니다.


이 시점에서 리소스 파일의 모양은 다음과 같습니다.


game.resources = [

    { name: "tileset",         type:"image",   src: "data/img/tileset.png" },
    { name: "background",      type:"image",   src: "data/img/background.png" },
    { name: "clouds",          type:"image",   src: "data/img/clouds.png" },

    
    { name: "screen01",            type: "tmx",    src: "data/map/screen01.tmx" },

    { name: "tileset",         type: "tsx",    src: "data/map/tileset.json" },

    { name: "action-wheel",          type:"image",   src: "data/img/assets/UI/action-wheel.png" },
    { name: "action-wheel-right",          type:"image",   src: "data/img/assets/UI/action-wheel-right.png" },
    { name: "action-wheel-upper-right",    type:"image",   src: "data/img/assets/UI/action-wheel-upper-right.png" },
    { name: "action-wheel-up",             type:"image",   src: "data/img/assets/UI/action-wheel-up.png" },
    { name: "action-wheel-upper-left",     type:"image",   src: "data/img/assets/UI/action-wheel-upper-left.png" },
    { name: "action-wheel-left",           type:"image",   src: "data/img/assets/UI/action-wheel-left.png" },

    { name: "dst-gameforest",  type: "audio", src: "data/bgm/" },

    { name: "cling",           type: "audio",  src: "data/sfx/" },
    { name: "die",             type: "audio",  src: "data/sfx/" },
    { name: "enemykill",       type: "audio",  src: "data/sfx/" },
    { name: "jump",            type: "audio",  src: "data/sfx/" },

    { name: "texture",         type: "json",   src: "data/img/texture.json" },
    { name: "texture",         type: "image",  src: "data/img/texture.png" },

    { name: "PressStart2P", type:"image", src: "data/fnt/PressStart2P.png" },
    { name: "PressStart2P", type:"binary", src: "data/fnt/PressStart2P.fnt"}
];

거기에는 사용 된 타일 세트, 화면의 맵과 같은 것들이 있습니다 (이름이 확장자가 없는 파일 이름 인 방법에 주목하십시오. 이는 필수 요구 사항입니다. 그렇지 않으면 리소스를 찾을 수 없습니다).


당신의 동전 


게임의 동전은 매우 간단하지만 충돌 할 때 상황이 발생해야 하므로 코드는 다음과 같습니다.


game.CoinEntity = me.CollectableEntity.extend({

    /**
     * constructor
     */
    init: function (x, y, settings) {
        // call the super constructor
        this._super(me.CollectableEntity, "init", [
            x, y ,
            Object.assign({
                image: game.texture,
                region : "coin.png"
            }, settings)
        ]);

    },

    /**
     * collision handling
     */
    onCollision : function (/*response*/) {

        // do something when collide
        me.audio.play("cling", false);
        // give some score
        game.data.score += Constants.SCORES.COIN

        //avoid further collision and delete it
        this.body.setCollisionMask(me.collision.types.NO_OBJECT);

        me.game.world.removeChild(this);

        return false;
    }
});

코인 엔터티가 실제로 CollectibleEntity를 확장하는 방법에 주목하십시오 (이것은 엔터티에 특별한 충돌 유형을 제공하므로 플레이어가 이동하면 콜론 핸들러를 호출하는 것을 멜론 JS가 알고 있습니다). 그런 다음 onCollision 방법에서 우리는 당신이 그것을 선택할 때 빠른 소리를 재생하고 전역 점수에 포인트를 추가하고 마지막으로 세계에서 객체를 제거합니다.


완제품 


모든 것을 합치면 입력 한 단어에 따라 5 가지 방향으로 움직일 수 있는 작동하는 게임이 있어야 합니다.


다음과 같이 보일 것입니다 :


1*AeAA-d84fHFw7bByPJ0SmA.png 


이 튜토리얼은 이미 너무 길기 때문에 Github에서 이 게임의 전체 코드베이스를 확인할 수 있습니다. 궁금한 점이 있거나 아직 언급하지 않은 것을 수행하는 방법을 이해하려면 아래에 의견을 남겨주십시오.


언급하지 않은 여러 측면에 대한보다 구체적인 세부 정보가 포함 된 자습서를 더 많이 만들 예정입니다.





  • 트위터로 보내기
  • 페이스북으로 보내기
  • 구글플러스로 보내기
  • 카카오톡으로 보내기

페이지 정보

조회 23회 ]  작성일19-10-30 16:39

웹학교