A Simple Game —— ChainReaction
真的越来越喜欢web前端了。
本来一直觉得 游戏开发是个很高深的话题,虽然之前也会写点动态的网页,运动的对象,跳动的小球之类的简单动画,直到我遇到了JavaScript,jquery库。其实,最开始学习 JavaScript,感觉这是一门杂乱无章的弱语言,没有 C 和 C++ 或者 Java 那样有条理,可是,越是深入了解JavaScript,越会喜欢她的“自由豪放”。尤其是 HTML5 的出现改变了我对的看法。到了html5的时代,各种犀利的设计就更明显的需要 JavaScript了。看了一些小游戏设计,对动画那部分很有兴趣,于是写了一个HTML5 + JavaScript的网页版小游戏。
游戏名字叫 ChainReaction (连锁反应),这个游戏是看到有人在ipad上面玩的游戏,觉得好玩,自己实现一遍。游戏是这样的,一群小球在区域内弹来弹去,玩家鼠标点击一个地方,在一个圆的范围内,小球碰上就会爆炸,爆炸的过程中其他小球碰上也会发生爆炸,这就叫连锁反应。每一关爆破一定数量的小球就算胜利。
我的设计中一共有12关,数值增长比较平和,运气不是太差的话都能一次通关。这样的设计不是为了挑战,主要是为了给工作后的朋友缓解压力。我们轻松的一点就爆炸一片,好比是压力释放一样,不会很刺激,但是让玩家很轻松舒服,我个人很喜欢这样的游戏理念。
下面分别是游戏级别第1关和第10关的开始界面:
下面看看小球的定义:
function Circle(x, y, xSpeed, ySpeed, radius, color, liveTime, state){
//圆心坐标
this.x = x;
this.y = y;
//运动速度
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
//半径
this.radius = radius;
//颜色
this.color = color;
//生存计数器
this.liveTime = liveTime;
//状态
this.state = state;
}
其中生存计数器是要和状态结合使用的,状态分为如下5个状态:
//小球状态
var SMALL = 0;
var BIG = 1;
var EXPEND = 2;
var END = 3;
var DIS_VISIBLE = 4;
当处于 EXPEND 状态的时候,就说明小球进入爆炸状态,这时候生存计数器就用上了。计数器是个倒计时,计时到零,小球进入DIS_VISIBLE状态。这时候小球就不再渲染出来。
小球大小相同(个人觉得大小相同比较美观,当然大小不同也可以玩,改几行代码就ok),颜色不同,看看小球的初始化就知道了,代码在ObjectMgr.js中:
for(i = 0; i < g_StepsArr[g_Steps].ballsNum; i++){
//_Util.dump_obj(_Color.color_str(new Color(Math.random(), Math.random(), Math.random())));
var raduis = _Util.random_range(SMALL_RADIUS1, SMALL_RADIUS2);
this.circles.push(new Circle(
_Util.random_range(raduis*2, this.canvasWidth-2*raduis),
_Util.random_range(raduis*2, this.canvasHeight-2*raduis),
_Util.random(SPEED_MIN, SPEED_MAX),
_Util.random(SPEED_MIN, SPEED_MAX),
raduis,
_Color.color_rgba_str(new Color1(Math.random(), Math.random(), Math.random(), 0.8)),
MID_LIVE_TIME,
SMALL
));
}
其中g_StepsArr负责我们关卡的管理,有这一关的小球数,和通关需要爆破的小球数。总之,这里按照本关需要的小球数初始化小球,可以看到里面有很多的随机函数使用。小球的半径处于如下两个数之间:
var SMALL_RADIUS1 = 3;
var SMALL_RADIUS2 = 10;
通过random_range来进行这个范围随机。我们看到速度也是随机的,范围是:
var SPEED_MIN = 10;
var SPEED_MAX = 50;
颜色中的 color_rgba_str 函数的第四个参数说明我们的每个小球的透明度是0.8,这样我们就能在爆破的时候,或弹动的时候仍然看清其他小球。这段代码就说到这。
主要流程就在Main.js中。负责初始化,渲染和循环。
首先设置了一堆全局变量。
var g_ObjectMgr = null;
var g_MouseEventDispatch = new MouseEventDispatch();
var g_MouseMgr = null; //g_MouseMgr在g_ObjectMgr初始化后才初始化
//当前关
var g_Steps = 1;//todo 显示出来
//关卡数组
var g_StepsArr = [];
g_StepsArr = stepsInit();
//爆炸开始标识
var _ExpendStart = false;
if(DEBUG){
_CircleLib.test();
}
var _Main = { 。。。}
包括关卡数组,当前关数,爆炸标识等。 _Main 是个很大的结构。我更宁愿把它当做单例来思考。主要是,它包括了渲染,这个渲染不具通用性,仅此一例就够。当然以后其实我会更多考虑通用性的设计,尽量不设计成这种单例。
这个_Main 结构中有画布 canvas , 初始化函数,每关调用一次,它来负责2d对象的初始化、游戏对象的初始化、鼠标监听初始化。然后就是进入我们的循环。循环很简单,就干四件事情:
/*
* 循环绘图
* 1.清空画面
* 2.游戏对象关系处理
* 3.渲染出来
* 4.循环调用
*/
step: function(){
this.clear();
g_ObjectMgr.step();
this.render();
_this = this;
this._st = setTimeout(function(){
_this.step();
}, 50);
}
看看下一关干什么:
/*
* 下一关
*/
nextStep: function(){
clearTimeout(this._st);
if(this.canvas.getContext)
{
g_MouseEventDispatch.start();
this.initObjects();
this.step();
}
}
这个函数就是清除计时器,重新分配事件,初始化对象。然后进入循环,为什么是这样呢?清除计时器以使我们之前的循环停止。因为我们马上有新的循环了,其实事件可以看做有两个状态,我们按下鼠标的时候,这个事件就不可用了,下次使用必须初始化。小球数量变了,必须按照本关的需求来初始化。进入循环,新的循环开始。
看看再玩一次(本级)按钮的调用:
我们再看一下ObjectMgr.js中的爆炸检查函数:
expendCheck: function(){
if(_ExpendStart){
this.expendNum = _CircleLib.intersect(this.circles, g_MouseMgr.mouseCircle);
// _Util.dump_obj(g_StepsArr[g_Steps])
if(this.expendNum >= g_StepsArr[g_Steps].killNum){
var next = g_Steps + 1;
alert("成功爆破超过"+this.expendNum+"个小球,恭喜进入第"+ next + "关,\n\
下一关需要爆破" +g_StepsArr[g_Steps + 1].killNum + "个小球");
g_Steps++;
_Main.init();
}
}
}
还记得_ExpendStart 这个变量的意思么?就是说鼠标是否按下了,按下的话我们就要检查是否有小球撞上鼠标范围或撞上爆炸中的小球。其实这里的调用_CircleLib.intersect 这个函数是有些小问题的,它是通过引用修改的当前小球的状态。总之,这个函数检查了爆炸小球的数量,一旦爆炸小球的数量符合本关的要求,那么就可以进入下一关,可以看到我们进入下一关的提示是一个对话框。
点击确定,我们玩下一关
随着游戏关卡的上升,小球多了很多,找个好点的位置,能捕捉很多小球。看一下爆炸过程吧:
有想法就去尝试,这是一种更好的态度,更好的学习、思维方式。它会是网络极佳的生存方式,你喜欢并且去行动就是对的。
最后,希望我的web前端之路可以越走越远,喜欢,坚持,加油!