我正在使用 Node.js 构建多人游戏。为了不断更新所有游戏的状态,我执行以下操作
while (true) {
for (const game of games) {
game.update()
}
}
这显然会导致节点事件循环被阻塞。这里正确的技术是什么?
游戏是实时的,所以需要不断更新。
这样,所有代码都将在主线程中运行,因此如果您创建一个带有 true 的 while 循环,它将完全阻塞主线程。简单的方法是每秒调用更新函数 X 次。实现此目的的一种方法是:
const FRAMES_PER_SECOND = 20;
const REFRESH_TIME = 1000/FRAMES_PER_SECOND;
setTimeout(updateGames, REFRESH_TIME):
function updateGames()
{
for (const game of games) {
game.update()
}
}
这种方法应该可行,但请注意,它不能很好地适应多个游戏,因为如果处理时间超过一帧的分配时间,它将锁定整个应用程序。
解决此问题的另一种方法是使用Web Workers。您可以使用一组工作人员,甚至每场比赛使用一个工作人员。
对所有游戏使用一个 Web Worker,您可以执行以下操作:
main.js
// Create a new web worker
const worker = new Worker('worker.js');
// Add an event listener to receive messages from the worker
worker.addEventListener('message', (event) => {
const { games } = event.data;
console.log('Received updated games from worker:', games);
});
// Function to send a message to the worker to update the games
function updateGames() {
worker.postMessage('updateGames');
}
// Set a timer to periodically update the games
setInterval(updateGames, REFRESH_TIME);
worker.js
// Initialize the games array
const games = [];
// Function to update the games
function updateGames() {
for (const game of games) {
game.update();
}
// Send the updated games back to the main script
self.postMessage({ games });
}
// Event listener to receive messages from the main script
self.addEventListener('message', (event) => {
const message = event.data;
if (message === 'updateGames') {
updateGames();
}
});
更好的方法是使用一个工作池,每个工作人员更新一款游戏。
您似乎面临 Node.js 编程中的一个常见问题:用 while 循环阻塞事件循环。在多人游戏等实时应用程序中,您需要保持事件循环响应以处理传入事件、I/O 操作和其他异步任务。使用像您所展示的那样的连续循环来阻止事件循环可能会导致性能不佳和无响应。
要正确处理实时多人游戏的连续更新而不阻塞事件循环,您可以使用一种称为“游戏循环限制”的技术。这涉及设置一个以一致速率运行的受控循环,并允许事件循环在更新之间处理其他任务。以下是实现这一目标的方法:
使用setInterval或setTimeout: 您可以使用 setInterval 或 setTimeout 函数来安排定期更新,而不是使用连续的 while 循环。这样,您可以控制游戏循环运行的速率,防止它完全阻塞事件循环。
function gameLoop() {
for (const game of games) {
game.update();
}
}
// Schedule the game loop to run every X milliseconds
const updateInterval = 1000 / 60; // 60 FPS
setInterval(gameLoop, updateInterval);
在上面的示例中,gameLoop函数将以每秒 60 次的速率调用(假设目标为 60 FPS)。
将 requestAnimationFrame 用于浏览器游戏: 如果您的游戏在浏览器环境中运行,则可以使用 requestAnimationFrame 函数,该函数旨在与浏览器的渲染循环同步。这对于流畅的动画和避免过多的 CPU 使用特别有用。
function gameLoop(timestamp) {
for (const game of games) {
game.update();
}
// Request the next frame
requestAnimationFrame(gameLoop);
}
// Start the game loop
requestAnimationFrame(gameLoop);
请记住根据您的游戏要求和服务器性能调整更新间隔。目标是在平滑更新和不压垮服务器资源之间取得平衡。
通过使用其中一种技术,您可以确保游戏状态不断更新,同时允许 Node.js 事件循环处理其他任务并保持响应能力。