javascript – HTML5 Canvas游戏循环增量时间计算
作者:互联网
我是游戏开发的新手.目前我正在为js13kgames比赛做一场比赛,因此游戏应该很小,这就是我不使用任何现代流行框架的原因.
在开发我的无限游戏循环时,我发现了几篇文章和建议来实现它.现在它看起来像这样:
self.gameLoop = function () {
self.dt = 0;
var now;
var lastTime = timestamp();
var fpsmeter = new FPSMeter({decimals: 0, graph: true, theme: 'dark', left: '5px'});
function frame () {
fpsmeter.tickStart();
now = window.performance.now();
// first variant - delta is increasing..
self.dt = self.dt + Math.min(1, (now-lastTime)/1000);
// second variant - delta is stable..
self.dt = (now - lastTime)/16;
self.dt = (self.dt > 10) ? 10 : self.dt;
self.clearRect();
self.createWeapons();
self.createTargets();
self.update('weapons');
self.render('weapons');
self.update('targets');
self.render('targets');
self.ticks++;
lastTime = now;
fpsmeter.tick();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
};
所以问题在于自我.我最终发现第一个变体不适合我的游戏,因为它永远增加,武器的速度也随之增加(例如this.position.x =(Math.cos) (this.angle)* this.speed)* self.dt; ..
第二个变体看起来更合适,但是它对应于这种循环(http://codeincomplete.com/posts/2013/12/4/javascript_game_foundations_the_game_loop/)吗?
解决方法:
您的游戏引擎的一个很好的解决方案是在对象和实体中思考.您可以将世界上的一切都视为对象和实体.然后你想制作一个游戏对象管理器,它将包含你所有游戏对象的列表.然后,您希望在引擎中创建一个通用的通信方法,以便游戏对象可以创建事件触发器.游戏中的实体(例如玩家)不需要任何固有的东西来获得渲染到屏幕或进行碰撞检测的能力.您可以在游戏引擎正在寻找的实体中简单地制作常用方法.然后让游戏引擎按照自己的意愿处理实体.游戏中的实体可以在游戏中随时创建或销毁,因此您不应在游戏循环中对任何实体进行硬编码.
您将希望游戏引擎中的其他对象响应引擎已收到的事件触发器.这可以使用实体中的方法来完成,游戏引擎将检查该方法是否可用以及是否将事件传递给实体.不要将任何游戏逻辑硬编码到引擎中,这会影响可移植性并限制您稍后扩展游戏的能力.
您的代码的问题首先是您调用不同的对象呈现和更新不正确的顺序.您需要调用所有更新,然后按顺序调用所有渲染.另一个是你将对象硬编码到循环中的方法会给你带来很多问题,当你想让其中一个对象不再出现在游戏中,或者你想稍后在游戏中添加更多对象时.
您的游戏对象将具有update()和render(),您的游戏引擎将在对象/实体中查找该函数并在每帧中调用它.您可以非常喜欢并使引擎工作,以便在调用之前检查游戏对象/实体是否具有这些功能.例如,您可能希望对象具有update()但从不向屏幕呈现任何内容.您可以通过在调用引擎之前进行引擎检查来使游戏对象功能可选.对所有游戏对象都有一个init()函数也是很好的做法.当游戏引擎启动场景并创建对象时,它将通过在第一次创建对象时调用游戏对象init()开始,然后每个帧调用update(),这样你就可以拥有一个只在创建时运行一次的函数和另一个每帧运行.
实际上不需要增量时间作为window.requestAnimationFrame(frame);会给你~60fps.因此,如果您要跟踪帧数,您可以知道已经过了多少时间.然后,游戏中的不同对象(基于游戏中的设定点以及帧数是多少)根据其新帧数确定其所做的事情的持续时间.
window.requestAnimationFrame = window.requestAnimationFrame || function(callback){window.setTimeout(callback,16)};
gameEngine = function () {
this.frameCount=0;
self=this;
this.update = function(){
//loop over your objects and run each objects update function
}
this.render = function(){
//loop over your objects and run each objects render function
}
this.frame = function() {
self.update();
self.render();
self.frameCount++;
window.requestAnimationFrame(frame);
}
this.frame();
};
我已经创建了一个位于https://github.com/Patrick-W-McMahon/Jinx-Engine/tree/Dev的完整游戏引擎,如果你在https://github.com/Patrick-W-McMahon/Jinx-Engine/blob/Dev/JinxEngine.js查看代码,你会看到一个完整功能的游戏引擎在javascript中构建100%.它包括事件处理程序,并允许使用事件调用堆栈传递到引擎的对象之间的动作调用.查看一些示例https://github.com/Patrick-W-McMahon/Jinx-Engine/tree/Dev/examples,您将看到它是如何工作的.引擎可以运行大约100,000个对象,每帧渲染并以60fps的速率执行每帧.这是在核心i5上测试的.不同硬件可能有所不同鼠标和键盘事件内置于引擎中.传递给引擎的对象只需要监听引擎传递的事件.目前正在为更复杂的游戏构建场景管理和多场景支持.该引擎还支持高像素密度屏幕.
查看我的源代码应该可以帮助您构建功能更全面的游戏引擎.
我还想指出,当你准备重新绘制而不是之前(也就是在游戏循环结束时)时,你应该调用requestAnimationFrame().一个很好的例子,你不应该在循环开始时调用requestAnimationFrame(),如果你使用的是画布缓冲区.如果你在开始时调用requestAnimationFrame(),然后开始绘制到画布缓冲区,你可以最终绘制一半的新帧,另一半是旧帧.这将在每个帧上发生,具体取决于完成缓冲区相对于重绘周期所需的时间(60fps).但与此同时,你最终会重叠每一帧,这样当缓冲区自身循环时,缓冲区会变得更加混乱.这就是为什么当缓冲区完全准备好绘制到画布时,你应该只调用requestAnimationFrame().通过在结尾处使用requestAnimationFrame(),如果缓冲区未准备好绘制,则可以跳过重绘,因此每个重绘都按预期绘制. requestAnimationFrame()在游戏循环中的位置有很大的不同.
标签:javascript,html5,canvas,game-engine,timedelta 来源: https://codeday.me/bug/20190715/1466940.html