我想说些什么呢?大致是我自己对小游戏开发的理解吧……
关于游戏开发,我并没有经过系统的学习,也没有看过任何关于游戏开发相关的书籍和博客。我大概就是别人说的那个草台班子,游戏开发对于我来说,更像是一次次学习的研究和探索的过程。每次一尝试,我并没有想让游戏做得多完美,而是想着通过它,验证下当时的某个想法。看到它按自己想法运行了,心里想着:嘿嘿,我就说这样可以吧!
注:内容并不涉及开发教学,仅仅写些自己开发游戏过程中的想法
游戏开发初体验 - 21点纸牌
大概是12年前了吧,那时刚开始接触前端,主流的前端开发还是jQuery的天下,版本大概还是在1.3.x左右。那时候对jQuery动画产生了兴趣,就觉得几个函数就能把节点元素移来移去,觉得特别有意思。于是,我就想着:是不是可以开发一个小游戏,顺便学习掌握jQuery动画如何使用?
我的第一个游戏开发,尝试的是21点纸牌。之所以选它,是因为游戏里面纸牌最为简单,不需要复杂的动画管理;同时21点纸牌是我唯二会的纸牌游戏,另外一个是斗地主,我感觉21点纸牌输赢判定更为容易。
那时候纸牌开发最大的难点是对jQuery动画的掌握,需要让各个节点按顺序完成一系列的动作。那时候压根不知道动画队列是可以管理的,我也是采用最为粗暴的方式--一层层回调。
比如最简单的洗牌动画,可能就是这样的:
$(".desk_cards:eq(1)").animate({
left:'100px'
},250,function(){
$(this).css('z-index',3);}).animate({
left:'0px'
},250,function(){
$(this).css('z-index',1);
});
});
这对当时的我来说,已经是对动画理解的极限了!如果需要在多张运动过后,开始运动下一组动画,而这个动画又不方便放在某一个系列动作后面。我能做的,就是根据前一个动画需要消耗的时间,设置一个setTimeout定时处理函数,再开启下一个动画。
// ... 其他动画
$("#cards").animate({left:'550px'},1000,function(){
$(this).hide();
setTimeout(function({
$("#center_message").empty();
$("#cards").show().animate({
left:'0px'
},1000);
},3000); // 为了让这个动作在其他动画结束后开始运行,设置了定时
});
看上去很傻,但是很有效果,哈哈哈。总之最后,我也是勉强掌握了关于jQuery动画的一些使用吧。
HTML5游戏的尝试 - 吃豆人游戏
9年前,我无意间想到了走迷宫的问题,自己也尝试写了一个如何找迷宫的算法。当时并不知道什么A*寻径算法,只是凭直觉写出来的最短路径算法。于是,我就想呀,我能拿这个算法做些什么呢?寻址,在游戏开发里就是找对象呗,即A对象在地图上的迷宫中通过一条最便捷的路找到对象B。关于这个,我脑海里能想到的第一个游戏就是吃豆人!就是一个大饼脸(沙县小吃的logo)在地图上跑来跑去,4个幽灵在一个迷宫地图中追啊追。
那时候我刚好学习了如何用canvas绘制简单的图形,想着:如果把吃豆人游戏复刻出来,不就是可以应用到寻址算法,同时还可以巩固下canvas的使用吗?!就这样,说干就干了。
HTMl5游戏开发对我的第一个考验就是动画如何管理?因为canvas只是一个画板,所谓动画就是不停的在画板中画图又擦掉画下一帧。再不能像当年自己用jQuery那样,靠一个animate函数就能完成一个动画了。我需要一个动画管理的东西,那就姑且叫它“动画引擎”吧。
还需要什么呢?动画解决了图像的显示问题,游戏中应该还包括了玩家行为及对玩家行为的处理,也就是需要一个事件管理。事件管理可以对用户行为触发后,执行相应的反馈。我的理解是,游戏就是用户交互和程序互动的过程。
就这样,我想象中的游戏需要一样独立于业务逻辑存在的另外一个逻辑集合,它包含了动画引擎和事件管理机制。聪明的我给了它一个新的定义“游戏引擎”。
此时:
游戏引擎 = 动画管理 + 事件管理
有了对游戏引擎的初步概念后,我又深入地想了下,之后我又引入了多个概念:
元素: 画面中可以操作的对象,事件绑定及动画控制的主体,并且有状态可以运动;
地图: 游戏对象活动的范围,一个二维数组,数组的值标记地图中的类型,如:0表示路,1表示墙,2表示特殊墙(特定对象通过)
布景: 一个游戏不可能只有一个场景,比如开始和结束时的画面、不同的关卡,都需要不同的画面管理。每个画面有自己的事件逻辑和不同的对象。
时针: 由于canvas需要不断擦写,“游戏引擎”中需要有一个随着屏幕刷新递增的时针,这样所有对象根据这个时针统一进行绘制。
帧数: 每个元素运动的速度不一定一致,无法做到每次都一起绘制。这就意味着,每个元素都有自己的时间概念。A可能时针走2次绘制1次,B可能时针走3次才绘制一次。
这样,我构造出一个婴儿版本的游戏引擎了。由于开发的时候是2015年左右,ES6还没有很成熟,也就没有尝试Class去开发了,而是最元素的函数构造器和原型。
引擎的基本结构(展示关键部分)
// 定义游戏引擎
function Game(id,params){
// 定义元素构造器
var item = function(){
...
frames:1, // 对象多少帧绘制1次
times:0, // 对象的内部时针
update:function(){}, // 根据时针更新状态
draw:function(){}, // 根据时针和状态绘制元素
};
// 定义元素事件绑定方法
item.prototype.bind = function(){
};
// 定义地图构造器
var map = function(){
frames:1, // 对象多少帧绘制1次
times:0, // 对象的内部时针
};
// 定义地图原型相关方法
map.prototype.xxx = function(){
...
frames:1, // 地图多少帧绘制1次
times:0, // 地图的内部时针
update:function(){}, // 根据时针更新状态
draw:function(){}, // 根据时针和状态绘制地图
};
// 定义布景构造器
var stage = function(){
items:[], // 布景中的元素
maps:[], // 布景中的地图
};
// 定义布景事件绑定方法
stage.prototype.bind = function(){
};
// 定义布景事件创建元素方法
stage.prototype.createItem = function(){
};
// 定义布景事件创建地图方法
stage.prototype.createMap = function(){
};
// 创建布景
this.createStage = function(){
};
// 切换布景
this.setStage = function(){
};
// 下一布景
this.nextStage = function(){
};
// 初始化游戏引擎
this.init = function(){
};
}
由此可以看出,游戏引擎
直接对布景
进行管理,而布景
同时管理元素
和地图
。开发者可以对布景
和元素
进行事件绑定。游戏引擎根据事件触发的位置计算触发对象是谁,触发相应对象绑定的事件处理函数。
有了这个游戏引擎,开发游戏的业务逻辑就容易很多了。
// 创建游戏
var game = new Game('canvas');
// 创建布景
var stage1 = game.createStage();
// 创建元素
var item1 = stage1.createItem({
x:game.width/2,
y:game.height*.35,
width:100,
height:100,
frames:3,
draw:function(context){
}
});
// 对元素进行事件绑定
item1.bind('click',function(){});
// 对场景进行事件绑定
stage1.bind('click',function(){});
// 游戏初始化
game.init();
关于这款游戏,我觉得最成功的地方,除了自己弄了个“游戏引擎”外,就是对最短路径算法的应用了!
关于4个幽灵为什么能一起合作围堵玩家?
聪明机智的我,用到了一个思维转化:当每1个幽灵用最短路径算法找玩家的时候,都把另外3个幽灵当作一堵墙,于是4个幽灵就会在彼此互斥的同时向玩家靠拢,达到围堵玩家的目的。
数年后再战游戏开发 - 五子棋人机对战
半个月前,我想着好多年没有开发过游戏了,正好前段时间朋友微信推给我一个五子棋游戏,自己觉得蛮有意思的,不如再尝试下?!于是,我开始规划新游戏需要的东西。
首先,这次开发需要用上Class去定义游戏引擎中的模型,正好作为自己代码库中的代码范例。新的项目中能用的新的JavaScript的语法特征都尽量用上去。
其次,开发中使用脚本模块化开发思想。由于之前开发游戏的时候,并没有推出原生的模块化方案,当时的游戏只用了2个脚本:一个游戏引擎代码 和 一个游戏业务代码。既然这是一次尝试,那当然是把当下能安排的都安排上啦。
于是,我把当年的那个迷你游戏引擎按对象拆解为多个部分:
- Base.js - 基础对象,简单定义创建时ID自增和事件管理方法
- Item.js - 布景中的原生,继承基础对象;
- Map.js - 布景中的地图,继承基础对象;
- stage.js - 布景,继承基础对象;
- GameEngine - 游戏引擎
相比之前的改变,游戏的组成中多了一个基础对象,统一定义了事件处理机制,让过去的地图也有了事件处理的能力。这样,就可以在棋盘中落子下棋啦~
于此同时,对于游戏中的运行参数,可以配置化,管理起来也更为清晰。
除此之外,在游戏业务内容中,为了实现电脑和玩家智能对弈的过程,我需要让电脑有自己的判断逻辑。 而这个算法其实也很简单,就是通过循环判断棋盘中空白位置的获胜可能性进行评估,进行打分。选择最有优势的位置,同时站在对手的角度观察一下,对手最有利的位置自己有没有输的风险。
关于电脑在棋盘中空白位置如何打分,就要看横、竖、撇、捺四个方向的组合的情形。比如:
XOOOOX
马上就成龙了,分值最高
XOOOX
就差一步就成龙了,分值稍微低点
结合4个方向综合的分值就是最优解啦~
总结
开发三个游戏的过程,其实就是我对前端知识掌握理解并运用的过程。
- 第一个游戏,让我掌握游戏动画的运用;
- 第二个游戏,有了动画和事件管理的概念;
- 第三个游戏,模块化开发及继承等概念理解;
这三个游戏开发的过程就是一步步学习的必要经历。虽然没有学习过游戏开发的知识,也是勉强从自己的理解中窥探了一二吧~
那么……写那么多源代码是不是可以安排上了?!哈哈哈