1

私はJavaScriptの学習を始めたばかりで、すでにいくつかの問題に直面しています。矢印キーで赤いボックスを動かし、黒い背景がある非常に単純な「ゲーム」があります。同じ質問についていくつかのトピックを見ましたが、一部の人はバッファを作成すると言い、残りの半分はバッファを作成しないと言っています。とにかく、ちらつきがあるのでコードに何か問題があると思いましたが、それはとても単純なので、そうすべきではないと思います。

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

canvas.width = 800;
canvas.height = 600;

var mySprite = {
    x: 200,
    y: 200,
    width: 50,
    height: 50,
    color: '#c00'
};
var yvel = 0;
var xvel = 0 ;
var moved = false;
var mspeed = 15;

var keysDown = {};
window.addEventListener('keydown', function(e) {
    keysDown[e.keyCode] = true;
});
window.addEventListener('keyup', function(e) {
    delete keysDown[e.keyCode];
});

function update() {
    if (37 in keysDown) {
    moved = true;
    if(xvel > -mspeed){
        xvel -= 0.5;
        }
    }
    if (38 in keysDown) {
    moved = true;
        if(yvel > -mspeed){
        yvel -= 0.5
        }
    }
    if (39 in keysDown) {
    moved = true;
        if(xvel < mspeed){
        xvel += 0.5
        }
    }
    if (40 in keysDown) {
    moved = true;
        if(yvel < mspeed){
        yvel += 0.5
        }
    }
    mySprite.x += xvel;
    mySprite.y += yvel;
    if(moved == false){
        xvel = xvel * 0.95;
        yvel = yvel * 0.95;
        }
    moved = false;

}

function render() {
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = mySprite.color;
    ctx.fillRect(mySprite.x, mySprite.y, mySprite.width, mySprite.height); 
}

function run() {
    update();   
    render();
}

setInterval(run, 1000/60);

ここで試すことができます: w3school 私のコードをコピーしてタグ内に配置し、"MyCanvas" という名前を "canvas" に変更します。

4

1 に答える 1

4

実際、ダブル バッファリングは主要なブラウザですでに有効になっています。あなたがする必要があるのは、画面と同期することです。それは requestAnimationFrame を提供するものです
:

requestAnimationFrame のポリフィルは次のとおりです。

requestAnimationFrame (rAF) はまだ標準ではありません。すべての「ベンダー」、つまりブラウザ (Chrome、FF、Safari など) には独自の名前が付いています。
ポリフィルは、ベンダーに関係なく、環境に関数を「インストール」するコードです。ここで、window.requestAnimationFrame を使用して requestAnimationFrame にアクセスできるようになり、ベンダーを気にする必要がなくなります。
|| operator は 'scan' として機能します: 最初の 'true' ( == true) 式で停止し、残りの式を評価せずにそれを返します。たとえば、msRequestAnimationFrame が定義されている場合、最後の関数定義は評価されません。
requestAnimationFrame が見つからない場合のフォールバックがひどいことを追加する必要があります: setTimeout の精度は非常に低いですが、ありがたいことに rAF が見つかります: Chrome (接頭辞なし)、Safari (Webkit)、IE >9 ( ms)、Firefox (moz)、Opera (o)。)

// requestAnimationFrame polyfill
var  w=window, foundRequestAnimationFrame  =    w.requestAnimationFrame ||
                w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
                w.mozRequestAnimationFrame    || w.oRequestAnimationFrame  ||
                      function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame  = foundRequestAnimationFrame ;

更新とレンダリングを交換した後 (より良い方法)、実行ループは次のようになります。

function run() {
   render();
   update();   
   window.requestAnimationFrame(run);
}

run();

いずれにせよ、あなたが興味を持っている場合は、ここでJavaScriptゲームループについて説明しました: http://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/

なぜタイマーが必要なのですか?run は run の遅延呼び出しによって終了するため、上記のコードは run を何度も呼び出します: run は、次に表示が利用可能になったときに呼び出されます (通常は 1 秒あたり 60 回)。そして、Date.now() で現在時刻を知ることができます。

ゲーム ループ、rAF、および処理の問題に関する詳細については、上記のリンクを参照してください。

リファクタリング後のコードは次のとおりです。

(フィドルはこちら: http://jsbin.com/uvACEVA/2/edit )

var canvasWidth = 800, canvasHeight = 600;

var canvas = document.getElementById('canvas');
canvas.width = canvasWidth;   canvas.height = canvasHeight;
var ctx = canvas.getContext('2d');

// player definition

var mySprite = {
    x: 200,
    y: 200,
    xvel : 0,
    yvel : 0,
    velIncr : 0.5,
    velAttenuation : 0.95,
    maxVel : 15,
    width: 50,
   height: 50,
   color: '#c00',
  moved : false
};

mySprite.update = function (dt) {
  if (keysDown[37]) {
     this.moved = true;
     if(this.xvel > -this.maxVel){
        this.xvel -= this.velIncr;
      }
  }
  if (keysDown[38]) {
      this.moved = true;
      if(this.yvel > -this.maxVel){
         this.yvel -= this.velIncr;
      }
  }
  if (keysDown[39]) {
      this.moved = true;
      if(this.xvel < this.maxVel){
       this.xvel += this.velIncr;
      }
  }
  if (keysDown[40]) {
      this.moved = true;
      if(this.yvel < this.maxVel){
         this.yvel += this.velIncr;
      }
   }
                     // to have a frame-rate independant game,
                     // compute the real dt in run() and ...
  this.x += this.xvel; // ... use this.x += this.xvel * dt;
  this.y += this.yvel; // ...     this.y += this.yvel * dt;

  if(this.moved == false){
    this.xvel *= this.velAttenuation;
    this.yvel *= this.velAttenuation;
  }
  this.moved = false;
};

mySprite.draw = function(ctx) {
   ctx.fillStyle = this.color;
   ctx.fillRect(this.x, this.y, this.width, this.height); 
};

// Keyboard handling

var keysDown = [];

window.addEventListener('keydown', function(e) {
     keysDown[e.keyCode] = true;
     e.preventDefault();
     e.stopPropagation();
});

window.addEventListener('keyup', function(e) {
     keysDown[e.keyCode] = false;
     e.preventDefault();
     e.stopPropagation();
 });

// requestAnimationFrame polyfill

var  w=window, foundRequestAnimationFrame  =    w.requestAnimationFrame ||
                w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
                w.mozRequestAnimationFrame    || w.oRequestAnimationFrame  ||
                      function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame  = foundRequestAnimationFrame ;


// main animation loop

function update(dt) {
    mySprite.update(dt); // only one for the moment.
}

function render(ctx) {
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    mySprite.draw(ctx);
}

var dt = 16;  // milliseconds elapsed since last call.
              // ideally you should compute it in run(), i didn't for simplicity
function run() {
   render(ctx);
   update(dt);   
   window.requestAnimationFrame(run);
}

run();
于 2013-08-28T15:40:35.850 に答える