2009-12-23 39 views
17

Có cách nào đơn giản để tạo vòng lặp trò chơi bằng JavaScript không? một cái gì đó giống như ...Cách tốt nhất cho trò chơi vòng lặp đơn giản trong Javascript?

onTimerTick() { 
    // update game state 
} 
+0

Trong thế giới hướng sự kiện JavaScript có thể bạn không cần phải làm điều này. Tại sao bạn nghĩ rằng bạn cần điều này? –

+10

@Jon, bạn sẽ làm cách nào để cập nhật trò chơi khi không có sự kiện nào được kích hoạt? Nhiều trò chơi đang làm việc ngay cả khi bạn không ... – James

Trả lời

22
setInterval(onTimerTick, 33); // 33 milliseconds = ~ 30 frames per sec 

function onTimerTick() { 
    // Do stuff. 
} 
+0

Bạn có thể muốn đảo ngược thứ tự của dòng đầu tiên và phần còn lại. ;) – Amber

+1

Tiếp tục, làm cho nó 33ms ... ;-) –

+1

Xong;) ... @Dav, tại sao? – James

4

Yep. Bạn muốn setInterval:

function myMainLoop() { 
    // do stuff... 
} 
setInterval(myMainLoop, 30); 
3

Điều này có làm được không?

setInterval(updateGameState, 1000/25); 

Trường hợp 25 là FPS mong muốn của bạn. Bạn cũng có thể đặt ở đó số lượng mili giây giữa các khung hình, với tốc độ 25 khung hình/giây sẽ là 40ms (1000/25 = 40).

+1

bạn prolly muốn vượt qua chức năng, không phải kết quả của nó –

+1

Bạn đang phải, cố định. –

+0

không xử lý trôi và sẽ gây ra độ trễ. Không sử dụng webkit –

28

Có một số lượng đa dạng cách để đạt được điều này sử dụng JavaScript tùy thuộc vào ứng dụng của bạn. Một setInterval() hoặc thậm chí với một câu lệnh while() sẽ thực hiện thủ thuật. Điều này sẽ không hoạt động cho một vòng lặp trò chơi. JavaScript được trình duyệt giải thích, vì vậy nó dễ bị gián đoạn. Gián đoạn sẽ làm cho việc chơi trò chơi của bạn trở nên khó chịu.

Thuộc tính webkitRequestAnimationFrame của CSS3 nhằm khắc phục điều này bằng cách tự quản lý vòng lặp kết xuất. Tuy nhiên đây vẫn không phải là cách hiệu quả nhất để làm điều này và sẽ dễ bị jitters nếu bạn có rất nhiều đối tượng được cập nhật.

Đây là một trang web tốt để bắt đầu với:

http://nokarma.org/2011/02/02/javascript-game-development-the-game-loop/index.html

Trang web này có một số thông tin tốt về những điều cơ bản thực hiện một vòng lặp game. Nó không chạm vào bất kỳ loại thiết kế hướng đối tượng nào bằng bất kỳ phương tiện nào. Cách chính xác nhất để đạt được thời gian chính xác là sử dụng chức năng ngày tháng.

while ((new Date).getTime() > nextGameTick && loops < maxFrameSkip) { 
    Game.update(); 
    nextGameTick += skipTicks; 
    loops++; 
} 

Điều này không tính đến cách setTimeout trôi ở tần số cao. Điều này cũng sẽ dẫn đến những thứ không đồng bộ và trở nên bồn chồn. JavaScript sẽ trôi qua +/- 18ms mỗi giây.

var start, tick = 0; 
var f = function() { 
    if (!start) start = new Date().getTime(); 
    var now = new Date().getTime(); 
    if (now < start + tick*1000) { 
     setTimeout(f, 0); 
    } else { 
     tick++; 
     var diff = now - start; 
     var drift = diff % 1000; 
     $('<li>').text(drift + "ms").appendTo('#results'); 
     setTimeout(f, 990); 
    } 
}; 

setTimeout(f, 990); 

Bây giờ, hãy đặt tất cả điều này vào ví dụ làm việc. Chúng tôi muốn chèn vòng trò chơi của chúng tôi vào vòng lặp được quản lý của WebKit. Điều này sẽ giúp làm mịn đồ họa được hiển thị. Chúng tôi cũng muốn chia nhỏ chức năng vẽ và cập nhật. Điều này sẽ cập nhật các đối tượng trong cảnh dựng hình của chúng ta trước khi tính toán khi khung tiếp theo sẽ được vẽ. Vòng lặp trò chơi cũng nên bỏ qua các khung vẽ nếu cập nhật mất nhiều thời gian.

index.html

<html> 
    <head> 
     <!--load scripts--> 
    </head> 
    <!-- 
     render canvas into body, alternative you can use div, but disable 
     right click and hide cursor on parent div 
    --> 
    <body oncontextmenu="return false" style="overflow:hidden;cursor:none;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;"> 
     <script type="text/javascript" charset="utf-8"> 
      Game.initialize(); 
      window.onEachFrame(Game.run); 
     </script> 
    </body> 
</html> 

Game.js

var Game = {}; 

Game.fps = 60; 
Game.maxFrameSkip = 10; 
Game.skipTicks = 1000/Game.fps; 

Game.initialize = function() { 
    this.entities = []; 
    this.viewport = document.body; 

    this.input = new Input(); 

    this.debug = new Debug(); 
    this.debug.initialize(this.viewport); 

    this.screen = new Screen(); 
    this.screen.initialize(this.viewport); 
    this.screen.setWorld(new World()); 
}; 

Game.update = function(tick) { 
    Game.tick = tick; 
    this.input.update(); 
    this.debug.update(); 
    this.screen.update(); 
}; 

Game.draw = function() { 
    this.debug.draw(); 
    this.screen.clear(); 
    this.screen.draw(); 
}; 

Game.pause = function() { 
    this.paused = (this.paused) ? false : true; 
}; 

/* 
* Runs the actual loop inside browser 
*/ 
Game.run = (function() { 
    var loops = 0; 
    var nextGameTick = (new Date).getTime(); 
    var startTime = (new Date).getTime(); 
    return function() { 
     loops = 0; 
     while (!Game.paused && (new Date).getTime() > nextGameTick && loops < Game.maxFrameSkip) { 
      Game.update(nextGameTick - startTime); 
      nextGameTick += Game.skipTicks; 
      loops++; 
     } 
     Game.draw(); 
    }; 
})(); 

(function() { 
    var onEachFrame; 
    if (window.requestAnimationFrame) { 
     onEachFrame = function(cb) { 
      var _cb = function() { 
       cb(); 
      requestAnimationFrame(_cb); 
      }; 
      _cb(); 
     }; 
    } else if (window.webkitRequestAnimationFrame) { 
     onEachFrame = function(cb) { 
      var _cb = function() { 
      cb(); 
      webkitRequestAnimationFrame(_cb); 
      }; 
      _cb(); 
     }; 
    } else if (window.mozRequestAnimationFrame) { 
     onEachFrame = function(cb) { 
      var _cb = function() { 
       cb(); 
       mozRequestAnimationFrame(_cb); 
      }; 
      _cb(); 
     }; 
    } else { 
     onEachFrame = function(cb) { 
      setInterval(cb, Game.skipTicks); 
     }; 
    } 

    window.onEachFrame = onEachFrame; 
})(); 

Thậm chí Thông tin

Bạn có thể tìm thấy một ví dụ làm việc đầy đủ, và tất cả các mã ở đây. Tôi đã chuyển đổi câu trả lời này thành một khung javascript có thể tải xuống, bạn có thể xây dựng các trò chơi của mình.

https://code.google.com/p/twod-js/

+2

-1 Đây là một mớ hỗn độn không được định dạng kém, được thể hiện kém chất lượng của câu trả lời với vô số lỗi chính tả và ngữ pháp. Vui lòng đọc [Cách trả lời cách thực hiện] (http://stackoverflow.com/help/how-to-answer). – Joe

+0

tôi đã sửa ngữ pháp và thêm mã ví dụ tốt hơn. –

+1

Câu trả lời của bạn là câu trả lời đúng nhất và cập nhật nhất trên trang, nhưng bây giờ bạn có quá nhiều mã ví dụ. Người đọc trong tương lai sẽ không quan tâm đến bố cục HTML, world.js, player.js, camera.js hoặc tile.js. Vui lòng chỉ bao gồm các phần tử của câu trả lời của bạn có liên quan đến việc xử lý thời gian cho vòng lặp trò chơi bằng cách sử dụng requestAnimationFrame. –

10

requestAnimationFrame là một thay thế tuyệt vời có sẵn trong hầu hết các trình duyệt hiện nay. Nó làm những gì bạn đang làm với setInterval, nhưng trong một nướng theo cách. Có lẽ điều đẹp nhất về nó là nó chỉ chạy trong khi tab của bạn được tập trung. Đó có thể là lý do không phải để sử dụng nó nếu bạn muốn mọi thứ chạy trong nền, nhưng thường (đặc biệt là các vòng kết xuất chỉ quan trọng khi được xem) thật tuyệt vời khi không sử dụng tài nguyên khi tab không hoạt động.

Cách sử dụng khá đơn giản:

function gameLoop(){ 
    window.requestAnimationFrame(gameLoop); 
    Game.update(); 
} 

Tôi ghét phải gửi vào chủ đề cũ, nhưng đây là một kết quả google hàng đầu cho "vòng đấu javascript" vì vậy nó thực sự cần bao gồm requestAnimationFrame.

Pollyfill cho các trình duyệt cũ sao chép từ paulirish:

(function() { 
    var lastTime = 0; 
    var vendors = ['ms', 'moz', 'webkit', 'o']; 
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 
     window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 
     window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 
            || window[vendors[x]+'CancelRequestAnimationFrame']; 
    } 

    if (!window.requestAnimationFrame) 
     window.requestAnimationFrame = function(callback, element) { 
      var currTime = new Date().getTime(); 
      var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 
      var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
       timeToCall); 
      lastTime = currTime + timeToCall; 
      return id; 
     }; 

    if (!window.cancelAnimationFrame) 
     window.cancelAnimationFrame = function(id) { 
      clearTimeout(id); 
     }; 
}()); 

Deeper giải thích và sử dụng lời khuyên về creativeJS

Các vấn đề liên quan