HTML5GAME :: HTML5GAME

달력

92025  이전 다음

  • 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

Game.Runner는 update () + draw ()에서 60fps 프레임 루프를 제공합니다. 해당 프로세스 내에서 (이 경우 퐁에서) 게임 자체, 기본 메뉴로 시작, 게임을 시작이 승자, 또는 사용자가 이를 포기 때까지 게임을 실행하는 사용자 입력을 기다립니다, 그런 다음 메뉴로 돌아가서 반복하십시오.



초기화

Pong 게임의 경우 사용자에게 다음과 같은 정보를 제공하는 '메뉴'를 표시하기 위해 일부 이미지를 사용합니다.

- 싱글 플레이어 게임의 경우 '1'을 누르십시오.


- 더블 플레이어 게임의 경우 '2'를 누르십시오.

이것은 GameRunner와 Pong 관계에 미묘한 비틀기를 도입합니다. 이미지로드는 비동기 프로세스이므로 이미지가 로드 될 때까지 이미지를 표시 할 수 없으므로 이미지가 완전히로드 될 때까지 GameRunner를 루프에 넣지 않아도됩니다.

이 문제를 해결하기 위해 Pong initialize 메서드는 콜백 패턴을 사용하여 이미지로드가 완료된 시점을 알 수 있으며 해당 프로세스가 완료 될 때까지 GameRunner에 start ()를 알리지 않습니다.

initialize: function(runner, cfg) {
  Game.loadImages(Pong.Images, function(images) {
    this.cfg         = cfg;
    this.runner      = runner;
    this.width       = runner.width;
    this.height      = runner.height;
    this.images      = images;
    this.playing     = false;
    this.scores      = [0, 0];
    this.menu        = Object.construct(Pong.Menu,   this);
    this.court       = Object.construct(Pong.Court,  this);
    this.leftPaddle  = Object.construct(Pong.Paddle, this);
    this.rightPaddle = Object.construct(Pong.Paddle, this, true);
    this.ball        = Object.construct(Pong.Ball,   this);
    this.runner.start();
  }.bind(this));
},

키보드 입력


키보드 이벤트가 발생하면 GameRunner는 게임의 onkeydown () 또는 onkeyup () 메소드 (있는 경우)를 자동으로 호출하므로 Pong 게임이 적절한 키를 감지하고 적절하게 게임을 시작하거나 중지 할 수 있습니다.

onkeydown: function(keyCode) {
  switch(keyCode) {
    case Game.KEY.ZERO: this.startDemo();            break;
    case Game.KEY.ONE:  this.startSinglePlayer();    break;
    case Game.KEY.TWO:  this.startDoublePlayer();    break;
    case Game.KEY.ESC:  this.stop(true);             break;
    case Game.KEY.Q:    this.leftPaddle.moveUp();    break;
    case Game.KEY.A:    this.leftPaddle.moveDown();  break;
    case Game.KEY.P:    this.rightPaddle.moveUp();   break;
    case Game.KEY.L:    this.rightPaddle.moveDown(); break;
  }
},
onkeyup: function(keyCode) {
  switch(keyCode) {
    case Game.KEY.Q: this.leftPaddle.stopMovingUp();    break;
    case Game.KEY.A: this.leftPaddle.stopMovingDown();  break;
    case Game.KEY.P: this.rightPaddle.stopMovingUp();   break;
    case Game.KEY.L: this.rightPaddle.stopMovingDown(); break;
  }
},

또한 사용자가 패들의 위나 아래를 움직일 수있는 입력을 감지합니다.



게임 시작하기


이제 키보드 입력을 감지 할 수 있으므로 게임을 시작할 수 있습니다.

startDemo:         function() { this.start(0); },
startSinglePlayer: function() { this.start(1); },
startDoublePlayer: function() { this.start(2); },
start: function(numPlayers) {
  if (!this.playing) {
    this.scores = [0, 0];
    this.playing = true;
    this.ball.reset();
    this.runner.hideCursor();
  }
},

경기 도중


게임이 진행되는 동안 update () 메소드는 채점 된 시점과 승자를 선언하고 게임을 중단할 시기를 감지해야합니다.

update: function(dt) {
  this.leftPaddle.update(dt, this.ball);
  this.rightPaddle.update(dt, this.ball);
  if (this.playing) {
    var dx = this.ball.dx;
    var dy = this.ball.dy;
    this.ball.update(dt, this.leftPaddle, this.rightPaddle);
    if (this.ball.left > this.width)
      this.goal(0);
    else if (this.ball.right < 0)
      this.goal(1);
  }
},
goal: function(playerNo) {
  this.scores[playerNo] += 1;
  if (this.scores[playerNo] == 1) {
    this.menu.declareWinner(playerNo);
    this.stop();
  }
  else {
    this.ball.reset(playerNo);
  }
},

게임 중지


우승자가 선언되면 게임이 중지되고 ESC 키를 치는 사용자에 대한 응답으로 게임을 중지 할 수도 있습니다.

stop: function(ask) {
  if (this.playing) {
    if (!ask || this.runner.confirm('Abandon game in progress ?')) {
      this.playing = false;
      this.runner.showCursor();
    }
  }
},

여기에서 데모를 통해 진행중인 게임 루프를 볼 수 있습니다.



Posted by 마스터킹
|

Part1에서 GameRunner를 사용하는것을 보았습니다. 이제 튀어 나오는 공을 그리는 것 외에는 아무것도 하지 않는 Pong 게임 객체를 정의 할 수 있습니다. 우리가해야 할 일은 다음을 구현하는 것입니다.

- initialize()

- update()

- draw()

Pong = {
  initialize: function(runner, cfg) {
    this.cfg    = cfg;
    this.runner = runner;
    this.width  = runner.width;
    this.height = runner.height;
    this.court  = Object.construct(Pong.Court,  this);
    this.ball   = Object.construct(Pong.Ball,   this)
    this.runner.start();
  },
  update: function(dt) {
    this.ball.update(dt);
  },
  draw: function(ctx) {
    this.court.draw(ctx);
    this.ball.draw(ctx);
  },
  Court: {
    // <snip>
  },
  Ball: {
    // <snip>
  }

Pong.Court


Pong.Court 객체는 벽을 그리는 방법을 알수 있습니다.

Court: {
  initialize: function(pong) {
    var w  = pong.width;
    var h  = pong.height;
    var ww = pong.cfg.wallWidth;
    this.walls = [];
    this.walls.push({x: 0,    y: 0,      width: w,  height: ww});
    this.walls.push({x: 0,    y: h - ww, width: w,  height: ww});
    this.walls.push({x: 0,    y: 0,      width: ww, height:  h});
    this.walls.push({x: w-ww, y: 0,      width: ww, height:  h});
  },
  draw: function(ctx) {
    ctx.fillStyle = 'white';
    for(var n = 0 ; n < this.walls.length ; n++)
      ctx.fillRect(this.walls[n].x, this.walls[n].y, this.walls[n].width, this.walls[n].height);
  }
}

Pong.Ball


볼 오브젝트는 움직이는 방법과 그려지는 방법을 알수 있습니다.

Ball: {
  initialize: function(pong) {
    this.pong    = pong;
    this.radius  = pong.cfg.ballRadius;
    this.minX    = pong.cfg.wallWidth + this.radius;
    this.minY    = pong.cfg.wallWidth + this.radius;
    this.maxX    = pong.width  - pong.cfg.wallWidth - this.radius;
    this.maxY    = pong.height - pong.cfg.wallWidth - this.radius;
    this.x       = Game.random(this.minX, this.maxX);
    this.y       = Game.random(this.minY, this.maxY);
    this.dx      = (this.maxX - this.minX) / (Game.random(1, 10) * Game.randomChoice(1, -1));
    this.dy      = (this.maxY - this.minY) / (Game.random(1, 10) * Game.randomChoice(1, -1));
  },
  update: function(dt) {
    this.x = this.x + (this.dx * dt);
    this.y = this.y + (this.dy * dt);
    if ((this.dx > 0) && (this.x > this.maxX)) {
      this.x = this.maxX;
      this.dx = -this.dx;
    }
    else if ((this.dx < 0) && (this.x < this.minX)) {
      this.x = this.minX;
      this.dx = -this.dx;
    }
    if ((this.dy > 0) && (this.y > this.maxY)) {
      this.y = this.maxY;
      this.dy = -this.dy;
    }
    else if ((this.dy < 0) && (this.y < this.minY)) {
      this.y = this.minY;
      this.dy = -this.dy;
    }
  },
  draw: function(ctx) {
    var w = h = this.radius * 2;
    ctx.fillStyle = 'white';
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI, true);
    ctx.fill();
    ctx.closePath();
  }
}

멀티 볼 데모는 여기에서 찾을 수 있습니다.

Posted by 마스터킹
|

게임 러너 루프

GameRunner는 매우 일반적입니다. 자바 스크립트 사전 요구 사항 (아래 참조)과 기본 게임 루프를 제공합니다.

- 주자 초기화

- 캔버스 front, back 버퍼 생성

- 게임 인스턴스 생성

- 60fps 루프 시작


루프 반복마다 :

- game.update () 호출 - 마지막 프레임 이후 dt 타이머 간격 제공

- game.draw () 호출 - 그리기 위한 버퍼 캔버스 컨텍스트 제공

- 플립 백 및 프론트 버퍼

- 프레임 속도 통계 업데이트

그리고 그게 전부입니다. 여기에서 데모를 찾을 수 있습니다.


Javascript 사전 요구 사항

제 3 자 라이브러리가 필요없는 프레임 워크를 만들고 싶습니다. 이 게임은 HTML <canvas>를 기반으로하고 최신 브라우저에서만 지원되기 때문에 jQuery 나 prototype.js와 같은 전체 라이브러리를 포함 할 필요는 없습니다.

그러나 우리가 필요로하는 몇 가지 전제 조건이 있습니다.

- 캔버스

- Function.bind

- Object.create

- Object.extend

- addEventListener

- DOMContentLoaded

- 이미지 로더


캔버스

우리는 HTML5 <canvas> 요소를 사용하여 게임을 제작하고 있습니다. 우리는 HTML5 <canvas> 요소가 필요합니다! 이것은 모든 최신 브라우저에서 지원되므로 아무런 문제가 없어야합니다.

IE의 이전 버전은 대개 제 3 자 라이브러리 (excanvas.js)를 필요로하지만, IE9는 기본 캔버스를 가지고 있기 때문에 이전 버전의 IE에 대한 지원을 중단하게됩니다.


Function.bind

이 메소드는 ECMAScript 5의 표준입니다. 이것은 자바 스크립트 개발에서 가장 중요한 메소드 중 하나이며, 콜백 (예 : 이벤트 핸들러)으로 사용되는 인스턴스 메소드가 올바른 세트를 갖도록하기 위해 사용됩니다. 그것도 메서드를 합성 할 때 인자를 사용합니다.

이 방법은 대부분의 최신 브라우저에서 제공되지만 구형 브라우저를 제공하기에 쉽기 때문에 샘플 구현은 mozilla에서 제공합니다

if (!Function.prototype.bind) {
  Function.prototype.bind = function(obj) {
    var slice = [].slice,
        args  = slice.call(arguments, 1),
        self  = this,
        nop   = function () {},
        bound = function () {
          return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
        };
    nop.prototype   = self.prototype;
    bound.prototype = new nop();
    return bound;
  };
}

Object.create

또 다른 ECMAScript5 표준은 특정 프로토 타입을 사용하여 객체의 구성을 캡슐화합니다.

자바 스크립트에서 OOP 클래스 구조 패턴을 모방하기 위한 많은 시도가 있었지만, 많은 경우에 이들을 완전히 피하고 자바 스크립트의 원형을 받아들이는 것이 더 간단 할 수 있습니다.

다시 말하지만, 현대의 브라우저는이 방법을 제공 할 것이지만 필요할 때 쉽게 구현할 수 있습니다.

if (!Object.create) {
  Object.create = function(base) {
    function F() {};
    F.prototype = base;
    return new F();
  }
}

Object.create에서 빠진 것은 어떤 종류의 생성자 함수에 인수를 전달하는 기능입니다. 나는 표준 Object.create 에 이 추가 동작을 제공하는 Object.construct를 추가하려고합니다.

if (!Object.construct) {
  Object.construct = function(base) {
    var instance = Object.create(base);
    if (instance.initialize)
      instance.initialize.apply(instance, [].slice.call(arguments, 1));
    return instance;
  }
}

Object.extend


표준은 아니지만 일반적으로 jQuery 또는 프로토 타입과 같은 라이브러리에서 제공합니다. 한 개체의 모든 속성을 다른 개체의 속성으로 복사하는 기능은 매우 유용 할 수 있습니다.

if (!Object.extend) {
  Object.extend = function(destination, source) {
    for (var property in source) {
      if (source.hasOwnProperty(property))
        destination[property] = source[property];
    }
    return destination;
  };
}

addEventListener


물론 우리는 DOM 이벤트에 응답 할 수 있어야합니다. 오래된 브라우저를 지원하기위한 여러 가지 방법이 있지만 <canvas>를 사용하여 최신 브라우저 만 지원하므로 표준 addEventListener를 지원할 수 있습니다 (예 : Internet Explorer 9에서도 지원)



DOMContentLoaded


페이지를 로드 할 준비가되면 jQuery 및 prototype.js와 같은 타사 라이브러리에서도 문제가 해결되지만 최신 브라우저 만 지원하기 때문에 이러한 브라우저가 DOMContentLoaded 이벤트를 지원함을 알 수 있습니다 (예. IE9는 이것을 지원합니다)



이미지 로더


<canvas> 기반 게임의 한 가지 문제는 이미지가 로드 될 때까지 이미지를 사용할 수 없다는 것입니다. 로드가 완료 될 때까지 이미지를 사용하지 않으려면 여러 이미지를로드하고 마지막 이미지 로드가 완료되면 콜백을 수행하는 도우미 메서드가 필요합니다.

loadImages: function(sources, callback) { /* load multiple images and callback when ALL have finished loading */
  var images = {};
  var count = sources ? sources.length : 0;
  if (count == 0) {
    callback(images);
  }
  else {
    for(var n = 0 ; n < sources.length ; n++) {
      var source = sources[n];
      var image = document.createElement('img');
      images[source] = image;
      Game.addEvent(image, 'load', function() { if (--count == 0) callback(images); });
      image.src = source;
    }
  }
}


Posted by 마스터킹
|