您的核心问题是您没有任何中央游戏循环。这在任何使用“实时”事件和交互的游戏中都是必不可少的。不过别担心,这通常是新手游戏制作者犯的第一个错误。以下一些内容可能会引起您的兴趣:
问题的原因
您的问题是,当您发射导弹时,您正在为该导弹创建一个时间循环,这确实会继续不断地吸引您的导弹;然而,你也只是在用户按下一个键时才清除你的游戏显示——这意味着你的导弹画成一条线。你可以让这个系统为一枚导弹和一名玩家工作,但如果你开始添加更多的射弹和敌人,你会遇到越来越多的问题。当您的游戏有一个单一的更新“节拍”(即游戏循环)时,思考和编码要容易得多,而不是每个实体都有自己的更新“节拍”,然后您必须对其进行同步。
一个很好的类比是想想简单的旧现实。在现实生活中,时间并不仅仅在某事发生时才开始出现(除非你开始真正进入量子力学、时空和大爆炸!)——相反,时间总是在流逝,新的实体/物体会随之出现期间互动。一旦这些对象已经消失,时间仍然会继续(除非您订阅任何基于随机观察者的理论)。
解决方案
你需要改变你对更新/重绘的看法。你需要考虑你的对象存在于一个世界中,它们每个都有一个节拍或滴答声来改变它们的位置或显示,在那个滴答声之后,整个游戏屏幕都会更新……而且——通常——你应该有每秒至少发生 24 个这样的滴答声(为了欺骗人眼,使屏幕上绘制的静态对象实际上在移动)。
因此,与其为每个子弹创建一个新的间隔,不如从一个控制整个游戏重绘的单一全局间隔开始。有几种方法可以做到这一点,最简单的方法是setInterval
像在代码中那样使用,但是这种方法也有缺点——实现这一点的更现代的方法是使用window.requestAnimationFrame
:
然后,一旦你让这个循环每秒触发 24 次(或更多),你就可以处理添加到你的世界的所有实体。这通常意味着拥有某种集中的列表来跟踪每个实体。这完全取决于您,有些人只会保留一个列表,即实体数组;其他人会将该列表分解为更具描述性的术语,例如bullets
, players
, enemies
... 等等。下面是一些伪代码来说明您的游戏循环的外观:
var display = {/* object that controls your canvas */};
var entities = [];
var entity = {/* object/constructor represents a game entity i.e. player */}
var gameloop = function(){
display.clear();
for( var i=0; i<entities.length; i++ ) {
entities[i].update();
}
};
显然,上面没有任何内容entities
。这就是其他代码的来源,以准备entities
包含您的播放器和世界上已经存在的一些其他项目;以及随着游戏的进行添加和删除实体的更多代码,即子弹,例如:
entities.push( new entity({
type: 'player',
image: 'ship.png'
}) );
您应该设计每个实体以支持名为 的方法update
,因此无论您添加什么新实体,游戏循环都会自动支持它。这个update
方法应该负责在世界各地移动该实体(无论它是什么),如果您愿意,可以在世界上绘制实体 - 但是通常最好将您的update
和draw
调用分开,以便所有实体在重绘之前完成移动发生——这有助于计算碰撞:
var gameloop = function(){
display.clear();
for( var i=0; i<entities.length; i++ ) {
entities[i].update();
}
for( var i=0; i<entities.length; i++ ) {
entities[i].draw();
}
};
如何处理键盘交互
这是另一个棘手的事情,人们通常认为在基于 HTML 的游戏中很容易,因为您已经有了要监听的关键事件。但是,您永远不应该将按键直接链接到更新代码......否则您会得到演示中发生的情况,其中“按键重复”触发会导致奇怪的动作。
你应该做的是听按键,就像你正在做的那样,但是controls
用按键的当前状态更新一个对象(或类似的东西)。然后在您的实体update
方法中,您侦听您的实体应响应并执行该操作的关键状态。这会使按键与动作错位,避免按键重复,并意味着事情保持在游戏的“节拍”内,它还允许您更具体地控制对该按键(或组合键)的反应。下面的代码又是一个说明:
var controls = {};
$(document).on(
'keydown': function(e){
controls[e.keyCode] = true;
},
'keyup': function(e){
delete controls[e.keyCode];
}
);
然后在您希望检查密钥状态时,您可以轻松使用:
if ( controls[32] ) {
/// space has been pressed
}
结论
所以总的来说,我建议通读 HTML5 游戏教程,查看热门链接,然后将您的游戏重新设计为该设计。你会发现它更容易使用。