炮爽2 html5,HTML5入门习作2:炮台

上一篇中,初学html5动画的我在canvas画布上用彩色小球模拟了烟花喷射的效果,功能虽简单,但在制作中涉及到了几个要点:计算、绘制、显示。而且实现了可兼容旧版的requestAnimationFrame方法,以达到更平滑的动画效果。

接下来,我准备在上一篇的基础上加以改进,实现一个有简单互动的小游戏。

当然,只有小球是远远不够用的,在边调试边开发的过程中,下列功能也逐一实现:

封装动画框架到单独js文件(代码复用);

生成多边形(绘制炮台);

渐变填充(绘制防卫区);

角度计算(发射扇面形散布的炮弹);

碰撞检测(炮弹击中目标,以及目标撞墙后的反弹);

鼠标事件(实时调整炮口方向);

以及之前实现过的渐隐对象,用于显示摧毁后的爆炸效果、全屏闪烁以及文字提示。

那么,先看看效果再讲解代码吧。

操作提示:

鼠标控制炮口方向,炮弹自动发射;

击坠10、20、40、80、160……个目标时会升级,升级后某个炮台威力会增加,相应的,敌人数量也会增加;

上方中央的“@”表示HP,敌人触底一次则减少一格;

GameOver后,点击屏幕重新开始。

(使用移动设备的朋友可能占了便宜,因为手机的竖屏玩起来炮火比较集中)

OK,下面是解说时间。由于代码较长,就不全贴出来了,可以右击上面的iframe直接查看源码。

首先是动画框架。在上一篇中,下一帧的请求、帧数计算、时间统计等等都和主程序放在了一起,容易混淆。所以我把它单独抽到一个anime.js里。

里面有两个主要方法,一个是外部用于建立动画对象的createAnime(),其参数为回调函数;另一个是内部执行动画帧的_animeFrame()。另外还有一系列动画状态、帧数等参数。

这些操作的封装避免了主程序和动画控制产生混淆。使用时,只需执行createAnime()建立一个动画对象,然后调用它的start()方法即可开始运行,调用stop()则停止;每帧自动执行回调函数时会传递两个参数,分别是此帧之前经过的时间,以及上一秒帧数,供主程序使用。

//建立动画对象

function createAnime(callback) {

if (!callback) return null;

var anime = new Object();

anime.starttime//动画开始时间

anime.fps;//上一秒帧数

anime.secondstarttime;//当前秒开始时间(用于统计每秒的帧数)

anime.framestarttime;

anime.nextfps;

anime.status = "idle";

anime.start = function () {

if (this.status == "running") return;

this.status = "running";

if (this.status == "idle") this.starttime = new Date();//初次启动才计时

this.secondstarttime = new Date();

this.framestarttime = new Date();

this.fps = 0;//上一秒帧数

this.nextfps = 0;

_animeFrame(this, callback);

};

anime.stop = function () {

this.status = "stopped";

};

return anime;

}

function _animeFrame(anime, callback) {

anime.nextfps++;//累加帧数

var currenttime = new Date()

if (currenttime - anime.secondstarttime >= 1000) {//满1秒则为帧数赋值,清空计数

anime.fps = anime.nextfps;

anime.nextfps = 0;

anime.secondstarttime = currenttime;

}

var framespan = currenttime - anime.framestarttime;//计算两帧间隔

anime.framestarttime = currenttime;

callback(framespan, anime.fps);

if (anime && anime.status == "running") {

requestAnimationFrame(function () {

_animeFrame(anime, callback);

}

);

}

}

当然,之前处理requestAnimationFrame兼容性那段代码也要加上。

说完了框架,接下来就是炮台程序本身了。

其实主题结构和之前演示烟花的基本类似,只是每帧动画时处理的内容多了些。

首先是init()初始化代码,设置画布尺寸,并使用画布长宽值的较小者作为基准尺寸basesize,此值十分重要,后面的目标、子弹、炮台等各物体的尺寸及大小均与此值成比例;

初始化之后,生成动画对象并执行start()开始,每一帧主要处理下面这些对象数组:

var cannons = new Array();//炮台

var bullets = new Array();//子弹

var targets = new Array();//射击目标

var blasts = new Array();//爆炸的目标

var messages = new Array();//消息对象

除了炮台数量为固定的(根据画面的长宽比决定炮台数量)之外,其余对象数组在每帧中都可能有所变化。所以对每个数组都采取了类似下列方式的处理:

var newbullets = new Array();

for(var i in bullets){

...

if (...) newbullets.push(bullets[i]);

}

bullets = newbullets;

即添加可以留到下一帧的对象,然后交换新旧数组。

各数组间的逻辑关系是这样的:

炮台(cannon)根据所指的方向和指定时间间隔,以扇形角度发射指定数量子弹(bullet);

子弹沿指定方向和速度飞行,如飞出画布则移除;子循环遍历各个目标(target),如发生碰撞(两者中心距离小于两者半径之和)则移除子弹,并设置目标为击中状态;

击中数达到指定次数则升级并显示消息(Message);

遍历目标数组。如击中,则移除目标并在原地添加一个爆炸(blast);如未击中则继续飞行,碰到左右边框则反弹,碰到底边框的话也产生爆炸并扣HP,同时也显示警告消息;

各爆炸随时间推移会变大并变淡,淡至透明则移除;

显示当前消息,消息文字在中间出现,慢慢向上移动并变淡,淡至透明则移除;

等待下一帧。

以上每一步都是计算和绘制交叉进行;

实际代码的顺序与上文描述并不一致,首先遍历的是爆炸,然后是目标和子弹,最后是炮台和消息,这是为了保持图层绘制时的上下关系;

画布绑定鼠标mousemove事件,获取鼠标坐标,并通过计算使所有炮台瞄准此坐标点;

HP减至0则游戏结束并停止动画。此时会添加一个鼠标点击事件,点击后重新开始并移除此事件,以免再次误击。

这些说明可能有些粗略,不过对阅读代码还是有帮助的。尽管我在边开发边修改时没有进行进一步的封装和面向对象化,但按顺序阅读可能会更简单些。

下一作品预计改进的地方:

使用离屏画布,避免把运算和呈现放在一起;

解决更高速下的碰撞检测(即两个对象已经飞过了碰撞点,但两帧之间应该有过碰撞),如果离屏画布的帧速仍然不足以解决,只好考虑计算两线段的最小间距了;

更方便使用的颜色和透明遮罩算法;

贴图而不仅仅使用几何图形;

适当将对象特性封装起来,而不是完全在主程序中写循环算法。

以及一点开发感想:

程序写起来不是难事,但数值调整真的很不容易……就这么几个简单参数,包括炮台数量、子弹发射速度、间隔、目标入场数量等等,我调了十几次才勉强达到“既不被秒杀,又不会长生不死”的地步,而且也不很满意。游戏设计很不容易啊。

2+

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值