JavaScript游戏开发中canvas无法按100ms间隔定时刷新如何解决?
问题根因
浏览器的JavaScript执行和页面渲染运行在同一个主线程上,同步JS代码执行时会完全阻塞后续的渲染任务。你在mainLoop里先执行board.showBoard(ctx)把绘制指令写入canvas缓冲区后,马上执行同步的深度树计算逻辑,浏览器必须等这个耗时数秒的计算任务跑完,才会把缓冲区的内容渲染到屏幕上,所以看起来玩家和电脑的落子是同时更新的。
解决方案
方案1:用Web Worker运行AI计算(最推荐)
把耗时的深度树计算逻辑独立放到Web Worker文件中,计算完全在后台线程运行,不会阻塞主线程的渲染和交互。
修改后的主线程代码示例:
function playGame() { // 初始化AI计算专属Worker const aiWorker = new Worker('ai-calculator.js'); let isAiCalculating = false; // 标记AI计算状态,避免重复触发 function doKeyDown(evt) { console.log(evt.keyCode) // 只有玩家回合、且AI没有在计算时才响应玩家操作 if (evt.keyCode > 48 && evt.keyCode < 58 && game.currentTurn === 'player' && !isAiCalculating) { game.makeMove(evt.keyCode-48); } } function mainLoop(ctx, game, board) { board.showBoard(ctx); // 每次循环优先刷新界面 // 仅在电脑回合、且AI未计算时触发走棋逻辑 if (game.currentTurn === 'computer' && !isAiCalculating) { isAiCalculating = true; // 把当前游戏状态发送给Worker计算 aiWorker.postMessage(game.getStatus()); } } // 监听Worker返回的计算结果 aiWorker.onmessage = (e) => { const aiMove = e.data; game.makeMove(aiMove); isAiCalculating = false; } Canvas.canvas = document.getElementById("myCanvas"); let game = new Game(); let board = new Board(game); window.addEventListener( "keydown", doKeyDown, true); Canvas.ctx = Canvas.canvas.getContext('2d'); setInterval(() => {mainLoop(Canvas.ctx, game, board)}, 100); }; document.addEventListener('DOMContentLoaded', playGame);
你只需要把深度树计算逻辑放到单独的ai-calculator.js文件中,收到主线程传来的游戏状态后计算走法,再通过postMessage把结果返回主线程即可。
方案2:给渲染让出执行间隙(不用Worker的折中方案)
如果AI计算逻辑不好拆分到Worker中,可以在触发AI计算前主动让出线程,让浏览器先完成当前帧的渲染:
function mainLoop(ctx, game, board) { board.showBoard(ctx); if (game.currentTurn === 'computer' && !isAiCalculating) { isAiCalculating = true; // 等待当前帧渲染完成后再执行AI计算 requestAnimationFrame(() => { setTimeout(() => { game.makeMove(0); isAiCalculating = false; }, 0) }) } }
这个方案的缺点是AI计算的几秒仍然会卡住页面交互,仅能保证玩家落子先渲染出来。
额外优化提示
你原代码中mainLoop每100ms会无差别调用game.makeMove(0),没有判断回合状态,会导致电脑重复走棋,建议给Game类增加回合状态标记,只有轮到对应角色时才触发走棋逻辑。
内容的提问来源于stack exchange,提问作者Nic Oatridge
相关产品推荐
相关产品推荐

