HTML5GAME :: 'PROGRAMMING/Phaser Js' 카테고리의 글 목록 (4 Page)

달력

42025  이전 다음

  • 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

이제는 Phaser 프레임 워크에서 HTML5를 사용하여 "2048" 게임을 할 차례입니다.

트윈과 애니메이션을 만드는 것은 매우 쉽습니다. 그래서 여기에 "2048" 게임이 있습니다:

WASD 키로 플레이하고 2048 또는 그 이상으로가보십시오 !!


소스 코드를 보기 전에 흰색 PNG 이미지 만 사용하고 게임 필드 값을 1 차원 배열에 저장하고 있음을 기억하십시오.


이 스크립트를 공부하면 다음 10 가지 원칙을 배우게됩니다.


* 그래픽 애셋로드

* 스프라이트 만들기

* 텍스트를 작성하고 스타일을 지정

* 독립형 Sprite 또는 기존 Sprite의 자식으로 Sprite를 스테이지에 추가하고 제거

* 그룹 만들기

* 그룹을 통해 정렬 및 반복

* 키보드 입력 처리

* 트윈을 사용하여 애니메이션 만들기

* 콜백 관리

* 스프라이트에 색상 필터 적용


<!doctype html>

<html>

<head>

     <script src="phaser.min.js"></script>

     <style>

     body{margin:0}

     </style>

     <script type="text/javascript">

window.onload = function() {

                    // tile width, in pixels

var tileSize = 100;

// creation of a new phaser game, with a proper width and height according to tile

// size

var game = new Phaser.Game(tileSize*4,tileSize*4,Phaser.CANVAS,"",

{preload:onPreload, create:onCreate});

// game array, starts with all cells to zero

var fieldArray = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);

// this is the group which will contain all tile sprites

var tileSprites;

// variables to handle keyboard input

var upKey;

var downKey;

var leftKey;

var rightKey;

// colors to tint tiles according to their value

var colors = {

2:0xFFFFFF,

4:0xFFEEEE,

8:0xFFDDDD,

16:0xFFCCCC,

32:0xFFBBBB,

64:0xFFAAAA,

128:0xFF9999,

256:0xFF8888,

512:0xFF7777,

1024:0xFF6666,

2048:0xFF5555,

4096:0xFF4444,

8192:0xFF3333,

16384:0xFF2222,

32768:0xFF1111,

65536:0xFF0000

}

// at the beginning of the game, the player cannot move

                    var canMove=false;

// THE GAME IS PRELOADING

function onPreload() {

// preload the only image we are using in the game

game.load.image("tile", "tile.png");

}

// THE GAME HAS BEEN CREATED

function onCreate() {

// listeners for WASD keys

upKey = game.input.keyboard.addKey(Phaser.Keyboard.W);

upKey.onDown.add(moveUp,this);

     downKey = game.input.keyboard.addKey(Phaser.Keyboard.S);

     downKey.onDown.add(moveDown,this);

     leftKey = game.input.keyboard.addKey(Phaser.Keyboard.A);

     leftKey.onDown.add(moveLeft,this);

     rightKey = game.input.keyboard.addKey(Phaser.Keyboard.D);

     rightKey.onDown.add(moveRight,this);

     // sprite group declaration

tileSprites = game.add.group();

     // at the beginning of the game we add two "2"

addTwo();

addTwo();

}

// A NEW "2" IS ADDED TO THE GAME

function addTwo(){

// choosing an empty tile in the field

do{

var randomValue = Math.floor(Math.random()*16);

} while (fieldArray[randomValue]!=0)

// such empty tile now takes "2" value

fieldArray[randomValue]=2;

// creation of a new sprite with "tile" instance, that is "tile.png" we loaded

// before

var tile = game.add.sprite(toCol(randomValue)*tileSize,toRow(randomValue)*

tileSize,"tile");

// creation of a custom property "pos" and assigning it the index of the

// newly added "2"

tile.pos = randomValue;

// at the beginning the tile is completely transparent

tile.alpha=0;

// creation of a text which will represent the value of the tile

var text = game.add.text(tileSize/2,tileSize/2,"2",{font:"bold 16px Arial",

align:"center"});

                         // setting text anchor in the horizontal and vertical center

text.anchor.set(0.5);

// adding the text as a child of tile sprite

tile.addChild(text);

// adding tile sprites to the group

tileSprites.add(tile);

// creation of a new tween for the tile sprite

var fadeIn = game.add.tween(tile);

// the tween will make the sprite completely opaque in 250 milliseconds

fadeIn.to({alpha:1},250);

// tween callback

fadeIn.onComplete.add(function(){

// updating tile numbers. This is not necessary the 1st time, anyway

updateNumbers();

// now I can move

canMove=true;

})

// starting the tween

fadeIn.start();

}

// GIVING A NUMBER IN A 1-DIMENSION ARRAY, RETURNS THE ROW

function toRow(n){

return Math.floor(n/4);

}

// GIVING A NUMBER IN A 1-DIMENSION ARRAY, RETURNS THE COLUMN

function toCol(n){

return n%4;

}

// THIS FUNCTION UPDATES THE NUMBER AND COLOR IN EACH TILE

function updateNumbers(){

// look how I loop through all tiles

tileSprites.forEach(function(item){

// retrieving the proper value to show

var value = fieldArray[item.pos];

// showing the value

item.getChildAt(0).text=value;

// tinting the tile

item.tint=colors[value]

});

}

// MOVING TILES LEFT

function moveLeft(){

// Is the player allowed to move?

                         if(canMove){

                         // the player can move, let's set "canMove" to false to prevent moving again

// until the move process is done

                              canMove=false;

                              // keeping track if the player moved, i.e. if it's a legal move

                              var moved = false;

                              // look how I can sort a group ordering it by a property

     tileSprites.sort("x",Phaser.Group.SORT_ASCENDING);

     // looping through each element in the group

     tileSprites.forEach(function(item){

     // getting row and column starting from a one-dimensional array

     var row = toRow(item.pos);

     var col = toCol(item.pos);

     // checking if we aren't already on the leftmost column (the tile can't

// move)

     if(col>0){

     // setting a "remove" flag to false. Sometimes you have to

// remove tiles, when two merge into one

     var remove = false;

     // looping from column position back to the leftmost column

     for(i=col-1;i>=0;i--){

     // if we find a tile which is not empty, our search is

// about to end...

     if(fieldArray[row*4+i]!=0){

     // ...we just have to see if the tile we are landing

// on has the same value of the tile we are moving

     if(fieldArray[row*4+i]==fieldArray[row*4+col]){

     // in this case the current tile will be removed

     remove = true;

     i--;                                            

     }

     break;

     }

     }

     // if we can actually move...

     if(col!=i+1){

     // set moved to true

                                             moved=true;

                                             // moving the tile "item" from row*4+col to row*4+i+1 and (if allowed)

// remove it

                                             moveTile(item,row*4+col,row*4+i+1,remove);

     }

     }

     });

     // completing the move

     endMove(moved);

                         }

}

// FUNCTION TO COMPLETE THE MOVE AND PLACE ANOTHER "2" IF WE CAN

function endMove(m){

// if we move the tile...

if(m){

// add another "2"

     addTwo();

                         }

                         else{

                         // otherwise just let the player be able to move again

canMove=true;

}

}

// FUNCTION TO MOVE A TILE

function moveTile(tile,from,to,remove){

// first, we update the array with new values

                         fieldArray[to]=fieldArray[from];

                         fieldArray[from]=0;

                         tile.pos=to;

                         // then we create a tween

                         var movement = game.add.tween(tile);

                         movement.to({x:tileSize*(toCol(to)),y:tileSize*(toRow(to))},150);

                         if(remove){

                         // if the tile has to be removed, it means the destination tile must be multiplied by 2

                              fieldArray[to]*=2;

                              // at the end of the tween we must destroy the tile

                              movement.onComplete.add(function(){

                                   tile.destroy();

                              });

                         }

                         // let the tween begin!

                         movement.start();

                    }

                    

                    // MOVING TILES UP - SAME PRINCIPLES AS BEFORE

                    function moveUp(){

                          if(canMove){

                              canMove=false;

                              var moved=false;

     tileSprites.sort("y",Phaser.Group.SORT_ASCENDING);

     tileSprites.forEach(function(item){

     var row = toRow(item.pos);

     var col = toCol(item.pos);

     if(row>0){  

                                        var remove=false;

     for(i=row-1;i>=0;i--){

     if(fieldArray[i*4+col]!=0){

     if(fieldArray[i*4+col]==fieldArray[row*4+col]){

     remove = true;

     i--;                                            

     }

                                                  break

     }

     }

     if(row!=i+1){

                                             moved=true;

                                             moveTile(item,row*4+col,(i+1)*4+col,remove);

     }

     }

     });

     endMove(moved);

                         }

}

// MOVING TILES RIGHT - SAME PRINCIPLES AS BEFORE

                    function moveRight(){

                          if(canMove){

                              canMove=false;

                              var moved=false;

     tileSprites.sort("x",Phaser.Group.SORT_DESCENDING);

     tileSprites.forEach(function(item){

     var row = toRow(item.pos);

     var col = toCol(item.pos);

     if(col<3){

                                        var remove = false;

     for(i=col+1;i<=3;i++){

     if(fieldArray[row*4+i]!=0){

                                                  if(fieldArray[row*4+i]==fieldArray[row*4+col]){

     remove = true;

     i++;                                            

     }

     break

     }

     }

     if(col!=i-1){

                                             moved=true;

     moveTile(item,row*4+col,row*4+i-1,remove);

     }

     }

     });

     endMove(moved);

                         }

}

                    

                    // MOVING TILES DOWN - SAME PRINCIPLES AS BEFORE

                    function moveDown(){

                          if(canMove){

                              canMove=false;

                              var moved=false;

     tileSprites.sort("y",Phaser.Group.SORT_DESCENDING);

     tileSprites.forEach(function(item){

     var row = toRow(item.pos);

     var col = toCol(item.pos);

     if(row<3){

                                        var remove = false;

     for(i=row+1;i<=3;i++){

     if(fieldArray[i*4+col]!=0){

     if(fieldArray[i*4+col]==fieldArray[row*4+col]){

     remove = true;

     i++;                                            

     }

                                                  break

     }

     }

     if(row!=i-1){

                                             moved=true;

     moveTile(item,row*4+col,(i-1)*4+col,remove);

     }

     }

     });

          endMove(moved);

                         }

}

     };

</script>

    </head>

    <body>

    </body>

</html>


소스파일입니다.


Posted by 마스터킹
|

Roguelikes는 Dredmor의 Dungeons, Spelunky, Isaac의 바인딩, FTL이 광범위한 잠재 고객에게 도달하고 비평을 받고있는 게임과 함께 최근 주목을 받아 왔습니다. 작은 틈새 시장에서 하드 코어 플레이어가 오래 동안 즐기던 다양한 조합의 rockguelike 요소는 이제 많은 기존 장르에 깊이와 재연성을 가져다줍니다.




이 튜토리얼에서는 JavaScript와 HTML 5 게임 엔진 인 Phaser를 사용하여 전통적인 RogueLike 게임을 만드는 법을 배웁니다. 결국, 당신은 당신의 브라우저에서 재생할 수있는, 완벽한 기능을 갖춘 간단한 RogueLike 게임을 갖게 될 것입니다! (우리의 목적을 위해 전통적인 roguelike는 permadeath와 함께 싱글 플레이어, 무작위, 차례 기반 던전 크롤러로 정의됩니다.)



준비하기

이 자습서에서는 텍스트 편집기와 브라우저가 필요합니다. 나는 Notepad ++를 사용하고 있으며 광범위한 개발자 도구를 위해 Chrome을 선호하지만 워크 플로우는 선택한 텍스트 편집기와 브라우저에서 거의 동일합니다.


그런 다음 소스 파일을 다운로드 하고 init 폴더로 시작해야합니다. 여기에는 게임용 Phaser와 기본 HTML 및 JS 파일이 포함되어 있습니다. 현재 비어있는 rl.js 파일에 게임 코드를 씁니다.


index.html 파일은 단순히 Phaser와 앞서 언급 한 게임 코드 파일을로드합니다.

<!DOCTYPE html>

<head>

    <title>roguelike tutorial</title>

    <script src="phaser.min.js"></script>

    <script src="rl.js"></script>

</head>

</html>



초기화 및 정의

당분간은 ASCII 그래픽을 사용하게 될 것입니다. 미래에는 비트 맵 그래픽으로 바꿀 수 있지만, 지금은 간단한 ASCII를 사용하면 더 쉽게 사용할 수 있습니다.


폰트 크기, 맵의 크기 (즉, 레벨), 그리고 스폰자의 수에 대한 몇 가지 상수를 정의 해 보겠습니다.

// font size

var FONT = 32; 

// map dimensions

var ROWS = 10;

var COLS = 15;

// number of actors per level, including player

var ACTORS = 10;


Phaser를 초기화하고 키보드 키 업 이벤트를 설정해 봅시다. 

// initialize phaser, call create() once done

var game = new Phaser.Game(COLS * FONT * 0.6, ROWS * FONT, Phaser.AUTO, null, {

        create: create

});

 

function create() {

        // init keyboard commands

        game.input.keyboard.addCallbacks(null, null, onKeyUp);

}

 

function onKeyUp(event) {

        switch (event.keyCode) {

                case Keyboard.LEFT: 

                case Keyboard.RIGHT: 

                case Keyboard.UP:

                case Keyboard.DOWN:

        }

}


기본 모노 스페이스 글꼴은 높이가 60 % 정도되는 경향이 있으므로 캔버스 크기를 0.6 * 글꼴 크기 * 열 수로 초기화했습니다. 우리는 또한 Phaser에게 초기화가 끝난 직후에 create () 함수를 호출해야한다고 말하면서 키보드 컨트롤을 초기화합니다.


볼거리가 많지는 않지만 여기까지 게임을 볼 수 있습니다!



지도

타일 맵은 우리의 플레이 영역을 나타냅니다. 

// the structure of the map

var map;



가장 단순한 형태의 절차를 사용하여 맵을 생성합니다. 벽을 포함 할 셀과 바닥을 임의로 결정합니다.

function initMap() {

        // create a new random map

        map = [];

        for (var y = 0; y < ROWS; y++) {

                var newRow = [];

                for (var x = 0; x < COLS; x++) {

                     if (Math.random() > 0.8)

                        newRow.push('#');

                    else

                        newRow.push('.');

                }

                map.push(newRow);

        }

}



관련 게시물


이것은 우리에게 80%가 벽이고 나머지가 바닥 인것을 제공합니다.


키보드 이벤트 리스너를 설정 한 직후 create () 함수로 게임의 새 맵을 초기화합니다.

function create() {

        // init keyboard commands

        game.input.keyboard.addCallbacks(null, null, onKeyUp);

 

        // initialize map

        initMap();

}


여기서 데모를 볼 수 있습니다. 아직 지도를 렌더링하지 않았으므로 아무 것도 볼 수 없습니다.



화면

지도를 그릴 시간입니다! 우리의 화면은 각각 하나의 문자를 포함하는 텍스트 요소의 2D 배열입니다.

// the ascii display, as a 2d array of characters

var asciidisplay;


두 가지 모두 간단한 ASCII 문자이기 때문에 지도를 그리면 화면의 내용이 지도의 값으로 채워집니다.

function drawMap() {

    for (var y = 0; y < ROWS; y++)

        for (var x = 0; x < COLS; x++)

            asciidisplay[y][x].content = map[y][x];

}



마지막으로 맵을 그리기 전에 화면을 초기화해야합니다. 우리는 create () 함수로 돌아 갑니다.

function create() {

        // init keyboard commands

        game.input.keyboard.addCallbacks(null, null, onKeyUp);

 

        // initialize map

        initMap();

 

        // initialize screen

        asciidisplay = [];

        for (var y = 0; y < ROWS; y++) {

                var newRow = [];

                asciidisplay.push(newRow);

                for (var x = 0; x < COLS; x++)

                        newRow.push( initCell('', x, y) );

        }

        drawMap();

}

 

function initCell(chr, x, y) {

        // add a single cell in a given position to the ascii display

        var style = { font: FONT + "px monospace", fill:"#fff"};

        return game.add.text(FONT*0.6*x, FONT*y, chr, style);

}


이제 프로젝트를 실행할 때 무작위지도가 표시됩니다.




액터

다음 줄에는 플레이어 캐릭터와 패배해야 할 적들이 있습니다. 각 액터는 맵의 위치에 대해 x와 y, 히트 포인트에 대해 hp의 세 필드가있는 객체입니다.


우리는 모든 액터를 actorList 배열에 유지합니다 (첫 번째 요소는 플레이어입니다). 우리는 또한 빠른 검색을위한 키로 액터의 위치와 연관 배열을 유지하므로 어떤 액터가 특정 위치를 차지하고 있는지 찾기 위해 전체 액터리스트를 반복 할 필요가 없습니다. 이것은 우리가 이동과 전투를 코드화 할 때 우리를 도울 것입니다.

// a list of all actors; 0 is the player

var player;

var actorList;

var livingEnemies;

 

// points to each actor in its position, for quick searching

var actorMap;


우리는 모든 액터를 만들고 지도에 무작위로 자유로운 위치에 할당합니다.

function randomInt(max) {

   return Math.floor(Math.random() * max);

}

 

function initActors() {

        // create actors at random locations

        actorList = [];

        actorMap = {};

        for (var e=0; e<ACTORS; e++) {

                // create new actor

                var actor = { x:0, y:0, hp:e == 0?3:1 };

                do {

                        // pick a random position that is both a floor and not occupied

                        actor.y=randomInt(ROWS);

                        actor.x=randomInt(COLS);

                } while ( map[actor.y][actor.x] == '#' ||

actorMap[actor.y + "_" + actor.x] != null );

 

                // add references to the actor to the actors list & map

                actorMap[actor.y + "_" + actor.x]= actor;

                actorList.push(actor);

        }

 

        // the player is the first actor in the list

        player = actorList[0];

        livingEnemies = ACTORS-1;

}


액터를 보여줄 시간입니다! 우리는 모든 적을 e로, 플레이어 캐릭터를 HP의 숫자로 그립니다.

function drawActors() {

        for (var a in actorList) {

                if (actorList[a].hp > 0)

                        asciidisplay[actorList[a].y][actorList[a].x].content = a == 0?''

+ player.hp:'e';

        }

}


방금 작성한 함수를 사용하여 create () 함수에서 모든 액터를 초기화하고 그립니다.

function create() {

    ...

    // initialize actors

    initActors();

    ...

    drawActors();

}.


우리는 이제 플레이어 캐릭터와 적들이 화면에 표시되어 있는것을 볼수 있습니다.




차단 및 이동식 타일

우리는 액터가 스크린과 벽을 벗어나지 않도록해야합니다. 그래서 간단한 체크를 추가하여 주어진 액터가 어느 방향으로 걸어 갈 수 있는지 살펴 봅시다.

function canGo(actor,dir) {

    return  actor.x+dir.x >= 0 &&

        actor.x+dir.x <= COLS - 1 &&

                actor.y+dir.y >= 0 &&

        actor.y+dir.y <= ROWS - 1 &&

        map[actor.y+dir.y][actor.x +dir.x] == '.';

}



이동과 전투

우리는 마침내 약간의 상호 작용에 도달했습니다 : 

이동과 전투! 클래식 roguelikes에서 기본적인 공격은 다른 액터로 이동함으로써 트리거 되기 때문에 우리는 액터와 방향을 취하는 moveTo () 함수 (두 방향을 x와 y를 액터가 들어가는 위치로 옮깁니다).를 추가 하겠습니다.

function moveTo(actor, dir) {

        // check if actor can move in the given direction

        if (!canGo(actor,dir))

                return false;

 

        // moves actor to the new location

        var newKey = (actor.y + dir.y) +'_' + (actor.x + dir.x);

        // if the destination tile has an actor in it

        if (actorMap[newKey] != null) {

                //decrement hitpoints of the actor at the destination tile

                var victim = actorMap[newKey];

                victim.hp--;

 

                // if it's dead remove its reference

                if (victim.hp == 0) {

                        actorMap[newKey]= null;

                        actorList[actorList.indexOf(victim)]=null;

                        if(victim!=player) {

                                livingEnemies--;

                                if (livingEnemies == 0) {

                                        // victory message

                                        var victory = game.add.text(

game.world.centerX,

game.world.centerY,

'Victory!\nCtrl+r to restart',

{ fill : '#2e2', align: "center" } );

                                        victory.anchor.setTo(0.5,0.5);

                                }

                        }

                }

        } else {

                // remove reference to the actor's old position

                actorMap[actor.y + '_' + actor.x]= null;

 

                // update position

                actor.y+=dir.y;

                actor.x+=dir.x;

 

                // add reference to the actor's new position

                actorMap[actor.y + '_' + actor.x]=actor;

        }

        return true;

}



기본적으로

- 액터가 올바른 위치로 이동하려고하는지 확인합니다.

- 그 위치에 다른 액터가 있다면, 우리는 그것을 공격합니다 (HP 숫자가 0이되면 죽이기도합니다).

- 새로운 위치에 다른 액터가 없다면, 우리는 거기로 이동합니다.


우리는 또한 마지막 적을 살해 한 후 간단한 승리 메시지를 보여 주고, 우리가 유효한 이동을 수행했는지 여부에 따라 false 또는 true를 반환합니다.


이제는 onKeyUp () 함수로 돌아가서 사용자가 키를 누를 때마다 화면에서 이전 액터의 위치를 지우고 (지도를 위에 그려서) 플레이어 캐릭터를 이동하고, 위치를 찾은 다음 액터를 다시 그립니다.

function onKeyUp(event) {

        // draw map to overwrite previous actors positions

        drawMap();

 

        // act on player input

        var acted = false;

        switch (event.keyCode) {

                case Phaser.Keyboard.LEFT:

                        acted = moveTo(player, {x:-1, y:0});

                        break; 

                case Phaser.Keyboard.RIGHT:

                        acted = moveTo(player,{x:1, y:0});

                        break; 

                case Phaser.Keyboard.UP:

                        acted = moveTo(player, {x:0, y:-1});

                        break; 

                case Phaser.Keyboard.DOWN:

                        acted = moveTo(player, {x:0, y:1});

                        break;

        } 

        // draw actors in new positions

        drawActors();

}


우리는 곧 행동 변수를 사용하여 각 플레이어가 입력 한 후에 적들이 행동해야 하는지를 알 수 있습니다.




기본 인공 지능


이제 플레이어 캐릭터가 움직이고 공격하기 때문에 플레이어가 적과 거리가 6 단계 이하인 경우 매우 간단한 경로 찾기에 따라 적이 플레이를 공격하도록 하겠습니다.. (플레이어가 멀어지면 적들은 무작위로 걷습니다.)


우리의 공격 코드는 액터가 누구를 공격하는지 상관하지 않습니다. 즉, 만약 당신이 그들을 바로 정렬한다면, 적들은 플레이어 캐릭터를서로 공격 할 것입니다!

function aiAct(actor) {

        var directions = [ { x: -1, y:0 }, { x:1, y:0 }, { x:0, y: -1 }, { x:0, y:1 } ];

        var dx = player.x - actor.x;

        var dy = player.y - actor.y;

 

        // if player is far away, walk randomly

        if (Math.abs(dx) + Math.abs(dy) > 6)

                // try to walk in random directions until you succeed once

                while (!moveTo(actor, directions[randomInt(directions.length)])) { };

 

        // otherwise walk towards player

        if (Math.abs(dx) > Math.abs(dy)) {

                if (dx < 0) {

                        // left

                        moveTo(actor, directions[0]);

                } else {

                        // right

                        moveTo(actor, directions[1]);

                }

        } else {

                if (dy < 0) {

                        // up

                        moveTo(actor, directions[2]);

                } else {

                        // down

                        moveTo(actor, directions[3]);

                }

        }

        if (player.hp < 1) {

                // game over message

                var gameOver = game.add.text(game.world.centerX,

game.world.centerY,

'Game Over\nCtrl+r to restart',

{ fill : '#e22', align: "center" } );

                gameOver.anchor.setTo(0.5,0.5);

        }

}


또한 우리는 적의 한 명이 플레이어를 죽이면 게임오버 메세지를 출력하도록 하였습니다.


이제는 플레이어가 움직일 때마다 적이 움직니는 것처럼  만드는 것입니다. 이 작업은 새로운 위치에 액터를 그리기 전에 onKeyUp () 함수의 끝 부분에 다음 코드를 추가해야합니다.

function onKeyUp(event) {

        ...

        // enemies act every time the player does

        if (acted)

                for (var enemy in actorList) {

                        // skip the player

                        if(enemy==0)

                                continue;

                        var e = actorList[enemy];

                        if (e != null)

                                aiAct(e);

                } 

        // draw actors in new positions

        drawActors();

}





Posted by 마스터킹
|


점프 정리하기

이제 우리의 영웅이 뛰고 있습니다. 그러나 문제가 있습니다. 때로는 점프가 발생하지 않으며 게임에 대한 흥미가 없습니다.


몇 가지 테스트를 한 결과 리스너가 때로 잘못된 순서로 호출되었다는 것을 알게되었습니다. 빨리 클릭하면 다운 이벤트가 발생하기 전에 위 이벤트가 실행되고 즉시 파워메터의 전원이 제거됩니다. 나는 이것을 세 가지로 고정시켰습니다.


- 다운 이벤트가 트리거 될 때까지 업 이벤트를 등록하기 위해 대기 중

- 각각의 이벤트 리스너가 트리거 된 이후에 다운 및 업 이벤트 리스너 제거

- 필요에 따라 이벤트 재 등록

아래의 강조 표시된 행을 참조하십시오.

var StateMain = {

    preload: function() {

        game.load.image("ground", "images/ground.png");

        game.load.image("hero", "images/hero.png");

        game.load.image("bar", "images/powerbar.png");

        game.load.image("block", "images/block.png");

    },

    create: function() {

        this.power = 0;

        //turn the background sky blue

        game.stage.backgroundColor = "#00ffff";

        //add the ground

        this.ground = game.add.sprite(0, game.height * .9, "ground");

        //add the hero in

        this.hero = game.add.sprite(game.width * .2, this.ground.y - 25, "hero");

        //add the power bar just above the head of the hero

        this.powerBar = game.add.sprite(this.hero.x + 25, this.hero.y - 25, "bar");

        this.powerBar.width = 0;

        //start the physics engine

        game.physics.startSystem(Phaser.Physics.ARCADE);

        //enable the hero for physics

        game.physics.enable(this.hero, Phaser.Physics.ARCADE);

        game.physics.enable(this.ground, Phaser.Physics.ARCADE);

        //game.physics.arcade.gravity.y = 100;

        this.hero.body.gravity.y = 200;

        this.hero.body.collideWorldBounds = true;

        this.hero.body.bounce.set(0, .2);

        this.ground.body.immovable = true;

        //set listeners

        game.input.onDown.add(this.mouseDown, this);

    },

    mouseDown: function() {

        game.input.onDown.remove(this.mouseDown, this);

        this.timer = game.time.events.loop(Phaser.Timer.SECOND / 1000, this.increasePower, this);

        game.input.onUp.add(this.mouseUp, this);

    },

    mouseUp: function() {

        game.input.onUp.remove(this.mouseUp, this);

        this.doJump();

        game.time.events.remove(this.timer);

        this.power = 0;

        this.powerBar.width = 0;

        game.input.onDown.add(this.mouseDown, this);

    },

    increasePower: function() {

        this.power++;

        this.powerBar.width = this.power;

        if (this.power &gt; 50) {

            this.power = 50;

        }

    },

    doJump: function() {

        this.hero.body.velocity.y = -this.power * 12;

    },

    update: function() {

        game.physics.arcade.collide(this.hero, this.ground);

    }

}




이제 완벽한 점프를 하게 되었지만 다른 문제를 발견했을 것입니다. 당신은 영웅이 날 수 있도록 마우스를 계속 누를 수 있습니다. 중력과 속력을 결합하면 날아 다니는 새 게임을 만드는 데 도움이 될 것입니다. 주인공이 점프 할 때 영웅이되기를 원하기 때문에 주인공의 위치를 기록해 두십시오.


mouseDown에 대한 이벤트 리스너를 추가하기 바로 전에 create 함수에서이 행을 추가하십시오.

//record the initial position

this.startY = this.hero.y;


이제 mouseDown 함수의 위치를 확인하면됩니다. 주인공이 시작 위치에 있지 않으면, 우리는 단순히 기능을 종료하고 점프하지 않습니다.

mouseDown 함수의 시작 부분에 다음을 추가합니다.

if (this.hero.y != this.startY) {

            return;

}




이제 영웅이 뛰어 넘을 무언가를 만들 때입니다. 우리는 블록 벽을 만들고 싶습니다. 그 벽을 구성하는 모든 블록을 보유 할 그룹을 만들어 봅시다.


create 함수의 끝에서 add 합니다.

this.blocks = game.add.group();


그런 다음 해당 그룹에 임의의 수의 블록을 추가하는 함수를 만들어 보겠습니다.

makeBlocks: function() {

        var wallHeight=game.rnd.integerInRange(2, 6);

        for (var i = 0; i &lt; wallHeight; i++) {

            var block = game.add.sprite(0, -i * 25, "block");

            this.blocks.add(block);

        }

    }


결과는 다음과 같습니다.



이제 우리는 벽을 가지고 있습니다. 다음 장에서는 게임에 움직임을 추가 할 것입니다.

Posted by 마스터킹
|


물리엔진 추가하기


우리는 Phaser Js 로 Runner 게임 만들기 Part - 1 에서 주인공의 파워메터 컨트롤 부분만 제작하였습니다. 직접 주인공을 제어하고 움직이는 부분은 제작하지 않았습니다.  

그래서 주인공을 제어하고 움직이게 하기 위해서, 가장 먼저 할 일은 물리 엔진을 시작하는 것입니다. Phaser 에는 여러 개의 엔진이 있습니다. 우리는 아케이드 엔진을 사용할 것입니다.


create 함수의 끝에서 (그러나 mouseListeners 바로 위에)이 코드 라인을 배치하여 물리 엔진을 시작하십시오

//start the physics engine

game.physics.startSystem(Phaser.Physics.ARCADE);


이것은 우리가 스프라이트에 물리엔진을 추가하고 싶다는 것을 Phaser 가 알 수있게 해줍니다. 그러나 모든 단일 스프라이트에 물리를 자동으로 추가하지는 않습니다. 각 스프라이트를 활성화해야합니다. 이 코드를 다음 행에 배치하십시오.

//enable the hero for physics

game.physics.enable(this.hero, Phaser.Physics.ARCADE);


이제 우리의 스프라이트는 물리 속성을 설정할 준비가되었습니다. 영웅의 속도를 설정하는 점프 기능을 만들어 봅시다.

doJump: function() {

        this.hero.body.velocity.y = -this.power * 12;

    }


우리는 단지 y 속도를 원합니다. 그리고 그것을 위로 향하게 하기 위해서 그것을 음수로 설정하고 싶습니다. 나는 그것을 힘 (또는 PowerBar의 폭) 12 번으로 설정했습니다. 이것은 적당한 속도를 위쪽으로 내는 것처럼 보일것입니다.


다음은 stateMain.js 코드 입니다.

var StateMain = {

    preload: function() {

        game.load.image("ground", "images/ground.png");

        game.load.image("hero", "images/hero.png");

        game.load.image("bar", "images/powerbar.png");

        game.load.image("block", "images/block.png");

    },

    create: function() {

        this.power = 0;

        //turn the background sky blue

        game.stage.backgroundColor = "#00ffff";

        //add the ground

        var ground = game.add.sprite(0, game.height * .9, "ground");

        //add the hero in

        this.hero = game.add.sprite(game.width * .2, ground.y - 25, "hero");

        //add the power bar just above the head of the hero

        this.powerBar = game.add.sprite(this.hero.x + 25, this.hero.y - 25, "bar");

        this.powerBar.width = 0;

        //start the physics engine

        game.physics.startSystem(Phaser.Physics.ARCADE);

        //enable the hero for physics

        game.physics.enable(this.hero, Phaser.Physics.ARCADE);

        //set listeners

        game.input.onUp.add(this.mouseUp, this);

        game.input.onDown.add(this.mouseDown, this);

    },

    mouseDown: function() {

        this.timer = game.time.events.loop(Phaser.Timer.SECOND / 1000, this.increasePower, this);

    },

    mouseUp: function() {

        this.doJump();

        game.time.events.remove(this.timer);

        this.power = 0;

        this.powerBar.width = 0;

    },

    increasePower: function() {

        this.power++;

        this.powerBar.width = this.power;

        if (this.power &gt; 50) {

            this.power = 50;

        }

    },

    doJump: function() {

        this.hero.body.velocity.y = -this.power * 12;

    },

    update: function() {}

}


마우스를 놓을 때 볼 수 있듯이 영웅은 올라가서 거기 머물러 있습니다.





올라가는 것은 내려와야합니다.

영웅이 지구로 내려 오도록 우리는 그의 몸에 중력을 더해야합니다. 우리는 지구상의 중력을 설정할 수 있지만, 그것은 땅에도 영향을 미치고, 땅이 화면에서 떨어지게 만듭니다. 지면의 중력을 0으로 설정하여 무시할 수 있고, 영웅의 중력을 설정하는 것이 쉽습니다.

this.hero.body.gravity.y = 200;


지금 게임을 실행하면 주인공이 게임의 맨 아래를 빠져 나가는 것을 볼 수 있습니다. 위의 예에서도 영웅이 게임의 맨 위로 날아갈 수 있습니다.

다음 한 줄의 코드로 두 가지를 모두 멈출 수 있습니다.

this.hero.body.collideWorldBounds = true;


이제 영웅은 게임에 머물러 있지만 여전히 땅을 빠져 나옵니다. 그것을 수정합시다.

첫째, 우리는 지상 스프라이트가 나머지 주들에게 접근 가능하도록해야합니다.

코드를 수정합시다.

var ground = game.add.sprite(0, game.height * .9, "ground");

this.ground = game.add.sprite(0, game.height * .9, "ground");

으로 수정합니다.


그러면 로컬 스프라이트가 주 전체 스프라이트로 변경됩니다.

중요 : 또한 ground 변수를 this.ground로 변경해야합니다.


지면의 물리엔진을 활성화 합니다.

game.physics.enable(this.ground, Phaser.Physics.ARCADE);


다음으로 update 함수에서 collision 관계를 설정하십시오. 두 물체가 충돌 할 때 물리엔진이 서로 작용하게됩니다.

update: function() {

         game.physics.arcade.collide(this.hero, this.ground);

    }


지금 게임을 실행하면 주인공이 화면에서 벗어나게됩니다.


Phaser 에게 그 지면은 움직이지 않는다고 설정합니다.

this.ground.body.immovable = true;


정리된 stateMain.js 코드 입니다.

var StateMain = {

    preload: function() {

        game.load.image("ground", "images/ground.png");

        game.load.image("hero", "images/hero.png");

        game.load.image("bar", "images/powerbar.png");

        game.load.image("block", "images/block.png");

    },

    create: function() {

        this.power = 0;

        //turn the background sky blue

        game.stage.backgroundColor = "#00ffff";

        //add the ground

        this.ground = game.add.sprite(0, game.height * .9, "ground");

        //add the hero in

        this.hero = game.add.sprite(game.width * .2, this.ground.y - 25, "hero");

        //add the power bar just above the head of the hero

        this.powerBar = game.add.sprite(this.hero.x + 25, this.hero.y - 25, "bar");

        this.powerBar.width = 0;

        //start the physics engine

        game.physics.startSystem(Phaser.Physics.ARCADE);

        //enable the hero for physics

        game.physics.enable(this.hero, Phaser.Physics.ARCADE);

        game.physics.enable(this.ground, Phaser.Physics.ARCADE);

 

        this.hero.body.gravity.y = 200;

        this.hero.body.collideWorldBounds = true;

        this.ground.body.immovable = true;

        //set listeners

        game.input.onUp.add(this.mouseUp, this);

        game.input.onDown.add(this.mouseDown, this);

    },

    mouseDown: function() {

        this.timer = game.time.events.loop(Phaser.Timer.SECOND / 1000, this.increasePower, this);

    },

    mouseUp: function() {

        this.doJump();

        game.time.events.remove(this.timer);

        this.power = 0;

        this.powerBar.width = 0;

    },

    increasePower: function() {

        this.power++;

        this.powerBar.width = this.power;

        if (this.power &gt; 50) {

            this.power = 50;

        }

    },

    doJump: function() {

        this.hero.body.velocity.y = -this.power * 12;

    },

    update: function() {

         game.physics.arcade.collide(this.hero, this.ground);

    }

}


결과입니다.




Posted by 마스터킹
|


Runner게임은 모바일 게임에서 필수 요소가되었습니다. 게임 개발자는 조만간 비슷한 것을 만들어 달라고 요청할 것입니다. 게임 개발에서 수년간 일하면서 그래픽을 만들기 전에 종종 게임을 잘 코딩해야합니다. 예술가들이 멋진 예술을 끝내기를 기다리는 동안, 나는  추상적인 그래픽을 사용할것입니다. 내가 같이 일했던 한 디자이너는 이것을 "dev art"라고 불렀습니다.


다음은 내가 사용할 기본적인 기술입니다.

- 우리의 영웅, 주인공.

- 서있을 그라운드.

- 뛰어 넘을 블록.

- 점프 할 높이를 나타내는 파워메터.

첫 번째 부분에서는 기초를 설정하고 파워 미터가 작동하도록 하겠습니다.


기본 템플릿

나는 내 자신의 기본 템플릿을 사용하고 있습니다.이 기본 템플릿은 약간의 시간을 절약하기 위해 만든 기본 템플릿입니다.

여기에서 다운로드 할 수 있습니다.


이미지 추가하기

먼저 프리로드 기능에서 키와 경로를 제공하여 이미지를 미리 로드합니다.

game.load.image("ground","images/ground.png");

game.load.image("hero","images/hero.png");

game.load.image("bar","images/powerbar.png");

game.load.image("block","images/block.png");


하늘 만들기

영웅은 검은 색 사각형이므로 무대 색상을 하늘색으로 변경합시다.

이것을 create 함수에 두십시오.

//turn the background sky blue

game.stage.backgroundColor="#00ffff";


메인 파트 추가

이제 땅을 만들겠습니다. 모바일 크기가 다양하기 때문에 화면의 높이의 90 % 또는 바닥에서 10 %로 바닥을 둡니다. 나중에 조정할 수 있습니다. 주인공의 키는 25px이므로 지상과 같은 y 위치에 놓고 땅바닥에 서게 하려면 25를 빼면됩니다.

//add the ground

var ground=game.add.sprite(0,game.height*.9,"ground");

//add the hero

this.hero=game.add.sprite(game.width*.2,ground.y-25,"hero");


게임은 이제 다음과 같이 보일 것입니다.



파워 미터

파워미터는 전원이 바뀔 때마다 추가 할 수 있고 너비를 변경할 수있는 작은 비트 맵입니다. 플레이어는 마우스 또는 손가락을 누른 상태에서 mouseUp 이벤트가 호출되면 점프하여 전원을 변경합니다. 마우스가 길수록 높을수록 점프가 됩니다.


먼저 변수를 변경해야합니다.


다음 코드를 create 함수의 맨 위에 놓습니다.

this.power=0;


다음으로, 우리는 영웅의 머리 위 오른쪽에 25 픽셀, 영웅의 좌표보다 25 픽셀 위의 파워 바를 놓습니다.

//add the power bar just above the head of the hero

this.powerBar=game.add.sprite(this.hero.x+25,this.hero.y-25,"bar");

this.powerBar.width=0;


리스너에 mouseUp 및 mouseDown을 추가합니다.

//set listeners

game.input.onUp.add(this.mouseUp, this);

game.input.onDown.add(this.mouseDown, this);


mouseDown이 호출되면 타이머가 시작되어 전원이 계속 증가합니다. mouseUp이 호출 될 때 우리는 그 타이머를 멈춤니다.  우리는 Phaser.Timer.Second / 1000에 타이머를 설정할 것입니다. 이것은 타이머가 1 초에 1000 번 실행된다는 것을 의미합니다. 이렇게하면 부드러운 파워 바 효과를 얻을 수 있습니다.

mouseDown:function()

{

  this.timer=game.time.events.loop(Phaser.Timer.SECOND/1000, this.increasePower, this);

},

mouseUp:function()

{

   game.time.events.remove(this.timer);

   this.power=0;

   this.powerBar.width=0;

},


이 항목에서 마지막으로 다루는 것은 파워 변수를 증가시키고 파워 바의 너비를 변경하는 increasePower 함수를 설정하는 것입니다. 우리는 현재 50의 힘을 제한 할 것입니다.하지만 게임이 개발되면 나중에 변경할 수 있습니다.

increasePower:function()

    {

     this.power++;

     this.powerBar.width=this.power;

     if (this.power&gt;50)

     {

       this.power=50;

     }

    },


이 단원의 마지막 코드는 다음과 같습니다.

var StateMain = {

    preload: function() {

        game.load.image("ground", "images/ground.png");

        game.load.image("hero", "images/hero.png");

        game.load.image("bar", "images/powerbar.png");

        game.load.image("block", "images/block.png");

    },

    create: function() {

        this.power = 0;

        //turn the background sky blue

        game.stage.backgroundColor = "#00ffff";

        //add the ground

        var ground = game.add.sprite(0, game.height * .9, "ground");

        //add the hero in

        this.hero = game.add.sprite(game.width * .2, ground.y - 25, "hero");

        //add the power bar just above the head of the hero

        this.powerBar = game.add.sprite(this.hero.x + 25, this.hero.y - 25, "bar");

        this.powerBar.width = 0;

        //set listeners

        game.input.onUp.add(this.mouseUp, this);

        game.input.onDown.add(this.mouseDown, this);

    },

    mouseDown: function() {

        this.timer = game.time.events.loop(Phaser.Timer.SECOND / 1000, this.increasePower, this);

    },

    mouseUp: function() {

        game.time.events.remove(this.timer);

        this.power = 0;

        this.powerBar.width = 0;

    },

    increasePower: function() {

        this.power++;

        this.powerBar.width = this.power;

        if (this.power &gt; 50) {

            this.power = 50;

        }

    },

    update: function() {}

}


결과는 다음과 같습니다.










Posted by 마스터킹
|

가끔은 1 시간 만에 게임을 쓰기 위해 도전하고 싶습니다.

게임 내용은 먼저 컴퓨터가 4개의 다른 색상의 판넬에서 터치를 하게됩니다. 그리고 플레이어는 컴퓨터가 진행한 터치를 그대로 따로 하면 됩니다.



소스 코드는 다음과 같습니다.

var StateMain = {

    preload: function() {

        game.load.spritesheet("squares","images/squares.png",250,250);

        game.load.audio("note1","audio/1.mp3");

        game.load.audio("note2","audio/2.mp3");

        game.load.audio("note3","audio/3.mp3");

        game.load.audio("note4","audio/4.mp3");

        game.load.audio("bang","audio/bang.mp3");

    },

    create: function() {

        //define an array to hold the random song

        this.songArray = [];

        //define the notes and sound effects

        var note1 = game.add.audio("note1");

        var note2 = game.add.audio("note2");

        var note3 = game.add.audio("note3");

        var note4 = game.add.audio("note4");

        this.bang = game.add.audio("bang");

        //play the notes on an array for easy indexing

        this.noteArray = [note1, note2, note3, note4];

        //lock the click for while the song is playing

        this.clickLock = true;

        //make the blocks

        var red = this.getBlock(0);

        var blue = this.getBlock(1);

        var yellow = this.getBlock(2);

        var green = this.getBlock(3);

        //

        //put the blocks in a group

        //this will make it easy to center all the blocks

        //

        this.blocks = game.add.group();

        this.blocks.add(red);

        this.blocks.add(blue);

        this.blocks.add(yellow);

        this.blocks.add(green);

        //

        //position the blocks

        //all the postions were set at 0

        //so we just need to set some of the positions

        //

        blue.x = 250;

        yellow.y = 250;

        green.y = 250;

        green.x = 250;

        //make the blocks 80% of the width of the screen

        this.blocks.width = game.width * .8;

        //scale the height to match the width

        this.blocks.scale.y = this.blocks.scale.x;

        //center the blocks on the screen

        this.blocks.x = game.width / 2 - this.blocks.width / 2;

        this.blocks.y = game.height / 2 - this.blocks.height / 2;

        //alpha the blocks

        this.resetBlocks();

        //add a note to the song

        this.addToSong();

        //play the song

        this.playSong();

    },

    /**

     * [getBlock make a sprite, and add the click listener]

     * @param  {[type]} frame [this is the color of the block]

     * @return {[group]}  

     */

    getBlock: function(frame) {

        var block = game.add.sprite(0, 0, "squares");

        block.frame = frame;

        block.inputEnabled = true;

        block.events.onInputDown.add(this.clickBlock, this);

        return block;

    },

    /**

     * [resetBlocks turn all the blocks to 20% alpha]

     * @return {[none]}

     */

    resetBlocks: function() {

        this.blocks.forEach(function(block) {

            block.alpha = .2;

        });

    },

    /**

     * [addToSong push a random note on the songArray]

     */

    addToSong: function() {

        var s = game.rnd.integerInRange(0, 3);

        this.songArray.push(s);

    },

    playSong: function() {

        //set the playIndex to negative one

        this.playIndex = -1;

        //start the timer to play the notes

        this.songTimer = game.time.events.loop(Phaser.Timer.SECOND, this.nextNote, this);

    },

    nextNote: function() {

        //alpha the blocks

        this.resetBlocks();

        //advance the playIndex

        //we start at -1 so the first time the playIndex will be 0

        this.playIndex++;

        //if the play index is equal to the songArray length then

        //song is over

        //

        if (this.playIndex == this.songArray.length) {

            this.clickLock = false;

            this.clickIndex = 0;

            game.time.events.remove(this.songTimer);

            return;

        }

        //get the number of the note from the song

        var note = this.songArray[this.playIndex];

        //play the note

        this.playNote(note);

    },

    playNote: function(note) {

        //get the sound object from the note array

        var sound = this.noteArray[note];

        sound.play();

        //get the block that goes with the sound

        var block = this.blocks.getChildAt(note);

        //light it up!

        block.alpha = 1;

        //turn off the lights after a 1/4 of a second delay

        game.time.events.add(Phaser.Timer.SECOND / 4, this.resetBlocks, this);

    },

    clickBlock: function(target) {

        //if the song is playing return

        if (this.clickLock == true) {

            return;

        }

        //get the frame from the square clicked

        var index = target.frame;

        //if the index is equal to the number

        //in the songArray then the user

        //has pressed the right notes

        if (index == this.songArray[this.clickIndex]) {

            //get the note from the songArray

            var note = this.songArray[this.clickIndex];

            //play the note

            this.playNote(note);

            this.clickIndex++;

            //if the clickIndex is equal to the songArray length we have

            //reached the end of the song

            if (this.clickIndex == this.songArray.length) {

                this.clickLock = true;

                this.playIndex = -1;

                this.addToSong();

                this.playSong();

                return;

            }

        } else {

            //wrong note!

            //Game Over

            this.bang.play();

            this.clickLock = true;

            game.state.start("StateOver");

        }

    },

    update: function() {}

}



Posted by 마스터킹
|


Toast메시지는 페이드 인 및 페이드 아웃되는 문자 메시지입니다. 유리를 올린 사람과 축배를하고 유리를 다시 내리는 것과 비슷하기 때문에 토스트 메시지라는 이름이 붙어 있습니다.


여기에 예제가 있습니다.

var StateMain = {

    preload: function() {

        game.load.image("toastBack", "images/back.png");

        game.load.image("btnToast", "images/btnGenerate.png");

    },

    create: function() {

 

        //SET UP TEST BUTTON

     this.btnToast=game.add.sprite(game.world.centerX,game.height*.25,"btnToast");

     this.btnToast.anchor.set(0.5,0.5);

     this.btnToast.inputEnabled=true;

     this.btnToast.events.onInputDown.add(this.testToast,this);

    },

    testToast: function() {

        this.btnToast.visible=false;

        //get an instance of a toast object

        //and place it in postion

        var toast = this.makeToast("TEST!");

        toast.x = game.world.centerX;

        toast.y = game.world.height * .8;

        //fade in the toast object

        this.fadeIn(toast);

    },

    makeToast: function(message) {

        var toastGroup = game.add.group();

        var toastText = game.add.text(0, 0, message);

        var toastBack = game.add.sprite(0, 0, "toastBack");

        toastBack.width = game.width * .9;

        //

        //

        //SET ANCHORS

        toastText.anchor.set(0.5, 0.5);

        toastBack.anchor.set(0.5, 0.5);

        //

        //ADD THE TEXT AND SPRITE GRAPHIC TO THE TOAST GROUP

        toastGroup.add(toastBack);

        toastGroup.add(toastText);

        toastGroup.alpha = 0;

        //

        return toastGroup;

    },

    fadeIn: function(obj) {

        var tween = game.add.tween(obj).to({

            alpha: 1

        }, 1000, Phaser.Easing.Linear.None, true);

        tween.onComplete.add(this.delay, this);

    },

    delay:function(obj)

    {

        //WHEN TWEEN IS DONE PAUSE HERE FOR DELAY

        //SET A FADE OBJECT IN THE SCOPE OF THE STATE,

        //SINCE WE CAN NOT PASS THE OBJECT IN THE TIMER

     this.fadeObj=obj;

     game.time.events.add(Phaser.Timer.SECOND*2, this.delayDone, this);

    },

    delayDone:function()

    {

        //NOW THAT DELAY IS DONE CALL FADE OUT

     this.fadeOut(this.fadeObj);

    },

    fadeOut: function(obj) {

        var tween = game.add.tween(obj).to({

            alpha: 0

        }, 1000, Phaser.Easing.Linear.None, true);

        tween.onComplete.add(this.fadeDone, this);

    },

    fadeDone:function()

    {

        this.btnToast.visible=true;

    },

    update: function() {}

}



Posted by 마스터킹
|

최근에 나에게 매우 유용했던 것은 두 물체 사이의 각도를 구하거나 마우스와 중심 문자 사이의 각도를 얻을 수있게하는 것입니다. 예를 들어, 클릭하고 발사하려는 우주선이있는 게임에서 우주선을 돌릴 수있는 각도를 알아야합니다. 각도를 얻는 실제 수학은 약간 복잡하고 나는 그것을 이해하는 척하지는 않지만, 내가 가지고있는 매우 유용한 코드가 있습니다.

getAngle: function(obj1, obj2) {

// angle in radians

var angleRadians = Math.atan2(obj2.y - obj1.y, obj2.x - obj1.x);

// angle in degrees

var angleDeg = (Math.atan2(obj2.y - obj1.y, obj2.x - obj1.x) * 180 / Math.PI);

return angleDeg;

},


각도를 얻으려면이 두 객체를 전달해야합니다.

var angle = this.getAngle (goodGuy, monster);


다음은 우주선을 돌리기 위해 마우스와 우주선 사이의 각도를 사용하는 예입니다.

우주선이 마우스를 가리 키도록하려면 아무 곳이나 클릭하십시오.


그리고 예제 코드는 다음과 같습니다.

var StateMain = {

    preload: function() {

        game.load.image("ship", "images/ship.png");

    },

    create: function() {

        this.ship = game.add.sprite(game.world.centerX, game.world.centerY, "ship");

        this.ship.anchor.set(0.5, 0.5);

        game.input.onUp.add(this.clickCanvas, this);

    },

    clickCanvas: function() {

        //make an object from the mouse postion

        var obj1 = {

            x: game.input.x,

            y: game.input.y

        };

        //get the angle between the mouse and the ship and assign that to the

        //ship's angle

        this.ship.angle = this.getAngle(obj1, this.ship);

    },

    getAngle: function(obj1, obj2) {

        //I use the offset because the ship is pointing down

        //at the 6 o'clock position

        //set to 0 if your sprite is facing 3 o'clock

        //set to 180 if your sprite is facing 9 o'clock

        //set to 270 if your sprite is facing 12 o'clock

        //

        offSet = 90;

        // angle in radians

        var angleRadians = Math.atan2(obj2.y - obj1.y, obj2.x - obj1.x);

        // angle in degrees

        var angleDeg = (Math.atan2(obj2.y - obj1.y, obj2.x - obj1.x) * 180 / Math.PI);

        //add the offset

        angleDeg += offSet;

        return angleDeg;

    },

    update: function() {}

}





Posted by 마스터킹
|

이 비디오에서는 23 줄의 코드만으로 Phaser에서 미사일을 만들고 촬영하는 방법을 살펴 봅니다.


기본적인 생각은 그룹 내부에 스프라이트 미사일을 만드는 것입니다. 스프라이트를 마우스의 x 위치에 놓고 y 위치를 게임 높이 아래에 놓습니다. 그런 다음 업데이트 기능을 수행하는 동안 그룹을 반복하고 각 미사일의 y 위치에서 빼냅니다.




Posted by 마스터킹
|

자바스크립트 클래스


나는 바위 밑에서 살고있는 것처럼 느껴진다.

Javascript 클래스는 1 년 넘게 사용하었습니다. 이것은 모든 것을 바꿉니다! 설명하겠습니다.


지난 16 년 동안 게임을 만드는 대부분의 시간은 Flash를 사용하여 이루어졌습니다. Flash의 Actionscript 언어에 대한 좋은 점 중 하나는 그것이 매우 잘 구조화되어 있다는 것입니다. 클래스를 사용하고, 다른 클래스를 확장하고, 잘 관리 할 수있는 부분으로 분류 할 수 있습니다. 

html5로 옮기기에 대한 나의 가장 큰 반대 중 하나는 클래스가 없었기 때문입니다. 우리는 항상 javascript 클래스를 다음과 같이 사용할수 있습니다.

function Apple (type) {

    this.type = type;

    this.color = "red";

}

Apple.prototype.getInfo = function() {

    return this.color + ' ' + this.type + ' apple';

};

var myApple=new Apple("macintosh");

console.log(myApple.getInfo());


자, 이것은 완벽하게 잘 작동했습니다. 문제는 대부분 정리할 수있는 문제 중 하나였습니다. 이 방법으로 계속 작성했거나 복잡한 개체에 대한 이 게시물에서 사용한 것과 비슷한 개체를 반환하는 함수를 사용했습니다.


그러나, 방금 Javascript에서 실제 클래스를 사용할 수 있다는 것을 알았습니다. 기술적으로 내가 읽은 바로는 이것이 "Syntax Sugar"라는 것입니다. Javascript에서는 모든 것이 똑같이 작동하지만, 다르게 쓸 수는 있습니다. 그것은 나를 위해 충분합니다. 이제 우리는 apple 클래스를 다음과 같이 작성할 수 있습니다.

Class Apple

{

constructor(type)

{

    this.type=type;

    this.color=red;

}

getInfo()

{

   return this.color + ' ' + this.type + ' apple';

}

} 

var myApple=new Apple("macintosh");

console.log(myApple.getInfo());


Phaser 에서 클래스 사용하기

자, Phaser가 무엇을 의미합니까?

그것은 우리가 할 수있는 것을 의미합니다 :

- Phaser 클래스를 확장하는 클래스 작성

- 훨씬 더 재사용 가능한 코드 작성하기

- 코드를보다 효율적으로 정리


그리드 만들기!

예를 들어, 많은 게임과 프로젝트에 그리드가 필요합니다. 그리드에는 객체가 추가되어야하고 객체를 행과 열로 정렬해야합니다. Phaser에서는 그룹을 사용하여 각 개체를 그룹에 추가 한 다음 forEach를 사용하여 그룹 전체를 반복합니다. 그리드를 사용해야 할 때마다 프로젝트 폴더에 복사 할 수있는 클래스를 작성하고 싶습니다.


먼저 Grid라는 클래스를 만들어야합니다.

class Grid {

    constructor() {    

    }

}

두 번째 단계는 Grid 클래스가 Phaser의 그룹 클래스를 확장하도록 만드는 것입니다. 

extends 키워드를 사용하여이 작업을 수행 할 수 있습니다.


Phaser의 그룹 클래스는 Grid 클래스의 부모가됩니다.

class Grid extends Phaser.Group {

    constructor() {         

    }

}


마지막 단계는 super 명령을 사용하고 게임의 인스턴스를 매개 변수로 전달하여 부모 (Phaser.Group)의 생성자를 호출하는 것입니다. 이것은 "game"이라는 Phaser 게임 내에서 코드를 실행한다고 가정합니다. super가 생성자 내부에서 호출되는 첫 번째 것이 맞는지 확인하십시오.

class Grid extends Phaser.Group {

    constructor() {

     super(game);  

    }

}


현재 우리의 그리드는 Phaser 그룹보다 더 많은 것을하지 않습니다. 그런데 왜이 곤경에 가야합니까? 이제 우리는 우리 자신의 사용자 정의 함수와 변수를 추가 할 수 있습니다!


먼저 "cols"라는 생성자에 매개 변수를 전달하여 그리드에 포함시킬 열 수를 표시 한 다음이를 클래스 변수로 설정해 보겠습니다.

class Grid extends Phaser.Group {

    constructor(cols) {

     super(game);  

     this.cols=cols;

    }

}


항목을 추가 한 후에는 그리드에 정렬해야하므로 이제 해당 기능을 만들어 보겠습니다. 참고 : 이것은 동일한 크기의 항목에서만 잘 작동합니다.

arrange()

    {

     //current row count

     var row=0;

     //current column count

     var col=0;

     //use the group's built in for each to loop

     //through all the children

     this.forEach(function(item)

     {

     //set the position based

     //on the row, the column

     //and the height of the item

     item.y=row*item.height;

     item.x=col*item.width;

 

     //advance the column count

     col++;

     //if the column count is equal to the

     //number of columns set

     if (col==this.cols)

     {

     //go to the next row

     row++;

     //reset the column to 0

     col=0;

     }

     }.bind(this));

     //use bind(this) to keep the 'this' keyword

     //to mean the class

    }


이제 다음과 같이 그리드 클래스를 사용할 수 있습니다.

var grid=new Grid(3);

//add nine sprites

grid.add(sprite1);

....

grid.add(sprite9);

grid.arrange();


스프라이트를 확장하는 것은 어떨까요?


조금 더 많은 작업이 필요하지만 다음과 같이 할 수 있습니다.

class LetterBox extends Phaser.Sprite {

    constructor() {

     super(game,0,0,"letters",0);

 

     //add to stage right away

     game.add.existing(this);

    }

    }


스프라이트의 슈퍼 생성자에서 매개 변수는 다음과 같습니다.

super(game,x,y,"library_key",frame_number);


game.add.existing은 호출 한 후 곧바로 스프라이트를 스테이지에 추가합니다.

var letter=new LetterBox();


클래스의 인스턴스를 그룹에 추가하려는 경우 생략 할 수 있습니다.


다음은 Grid 및 LetterBox 클래스와 몇 가지 추가 함수가 추가 된 예제입니다.


StateMain.js

var StateMain = {

    preload: function() {

        //preload the sprite sheet

        game.load.atlasJSONHash('letters', "images/letters.png", "images/letters.json");

    },

    create: function() {

        //make a new grid

        //with 5 columns

        //

        var grid = new Grid(5);

        //

        //make 25 letter boxes

        //

        for (var a = 0; a &lt; 25; a++) {

            //

            //make a new letter box

            //

            var lb = new LetterBox();

            //

            //set a random letter

            //

            lb.setRand();

            //

            //add the letter to the grid

            //

            grid.add(lb);

        }

        //arrange the grid

        grid.arrange();

        //fit to the stage

        grid.fitStage();

        //put the grid in the center

        grid.center();

    },

    update: function() {}

}


The Grid Class – grid.js

class Grid extends Phaser.Group {

    constructor(cols) {

        super(game);

        this.cols = cols;

    }

    arrange() {

        //current row count

        var row = 0;

        //current column count

        var col = 0;

        //use the group's built in for each to loop

        //through all the children

        this.forEach(function(item) {

            //set the position based

            //on the row, the column

            //and the height of the item

            item.y = row * item.height;

            item.x = col * item.width;

            //advance the column count

            col++;

            //if the column count is equal to the

            //number of columns set

            if (col == this.cols) {

                //go to the next row

                row++;

                //reset the column to 0

                col = 0;

            }

        }.bind(this));

        //use bind(this) to keep the 'this' keyword

        //to mean the class

    }

    fitStage(per = 100) {

        this.width = game.width * (per / 100);

        this.scale.y = this.scale.x;

    }

    center() {

        this.x = game.width / 2 - this.width / 2;

        this.y = game.height / 2 - this.height / 2;

    }

}


The Letter Box Class – letterBox.js

class LetterBox extends Phaser.Sprite {

    constructor() {

        super(game, 0, 0, "letters", 0);

        //add to stage right away

        //game.add.existing(this);

    }

    /**

     * set by passing a lower case letter

     */

    setLetter(letter) {

        var index = letter.charCodeAt(0);

        this.frame = index - 97;

    }

    /**

     * set a random letter

     */

    setRand() {

        var index = game.rnd.integerInRange(0, 26);

        this.frame = index;

    }

}


Posted by 마스터킹
|