基于Unity3D的塔防游戏设计与实现
2015-04-18郭祖江
刘 俊,郭祖江
沈阳化工大学计算机科学与技术学院,辽宁沈阳 110142
近几年来,Android平台游戏、iPhone平台游戏以及Web的网页游戏迅猛发展,已经成为带动游戏产业发展的中坚力量。遗憾的是目前除了少数的作品成功外,大部分的游戏属宣传攻势往往大于其内容品质,加之玩家体验游戏时,对游戏的沉浸感和操作性的要求也逐渐提高。在这种局面下,3D游戏成为当前游戏的发展热点。而本文所采用的Unity3D游戏引擎提供了创造高质量的3D游戏和真实视觉效果的核心技术,为开发3D游戏提供了强大的驱动力。
本文基于Unity3D游戏引擎,研究游戏中AI设计过程的主要思路和主要算法实现,通过高效复杂的AI系统以及特定的shaderLab(着色器)编程开发一款基于Unity3D的3D塔防类游戏,使之具备丰富的画面而且保证游戏运行的流畅度。实践证明,本文所讨论的关键技术可以应用于增强现实、游戏开发等多个领域,具有一定的实际应用价值。
1 游戏场景
塔防游戏的基本玩法比较类似,在场景中我方有一个基地,敌人从场景的一侧出发,沿着相对固定的路线攻打基地。我方可以在地图上布置防守单位,攻击前来进攻的敌人,防止他们闯入基地。
本游戏的场景通常比较简单,就像一个棋盘格,可以在上面摆放防守单位,并专门留给敌人一个通道。在本游戏中,使用二维数组来表示场景中的格子,每个格子只有两种状态,允许摆放防守单位或不允许。
这是一个使用Terrain制作的简单场景,场景中间绘制了一条通道,敌人将从通道的左侧出发,目的地是通道右侧的房子,它是我们的基地。在通道以外的地方我们可以设置防守单位进攻敌人
塔防游戏的场景游戏固定的模式,它由一个二维的单元格组成,每个格子的用途可能不同,通常是下列用途之一:
1)专用于摆放防守单位。
2)无法摆放防守单位,也不允许敌人通过。
3)专用于敌人通过。
摄像机始终由上至下俯视游戏场景,按 住鼠标左键并移动可以移动摄像机的位置。
2 AI系统的实现
在BuildMap函数中,首先创建保存场景信息的二维数组,默认每个单元格都是可以摆放防守单位。然后在当前场景中找到所有Tag名为gridnode的游戏体,将其属性赋予和它位置相同的单元格。
在OnDrawGizmos函数中,我们使用线段绘制场景中的单元格,并将不能放置防守单位的单元格绘制为红色,这个功能主要是帮助我们预览场景单元格的状态。在OnDrawGizmos中绘制的图案并不会出现在最后的游戏界面中。
我们创建一个游戏管理器GameManager函数,它由几个作用,包括UI显示,控制鼠标操作和显示调试信息等。
正在塔防游戏中,敌人通常不需要智能寻路,而是按照一条预设的路线行动。我们为敌人创建一条前进路线,这条路线是预设的,敌人将从一个路点到达另一个路点,在PathNode脚本中,主要是通过SetNext函数设置它的子路点。
图1 是否可以摆放防守单位的单元格
图2 路点
敌人一共有两种,一种在陆地上前进,另一种则会飞行。我们将先创建前一种,然后继承它的大部分属性和函数,略加修改完成另一种。在Enemy脚本中,定义了敌人的一些基本属性,如生命值、移动速度、类型等,它由一个路点属性作为当前的出发点。在其MoveTo函数中,敌人向当前路点的子节点前进,当距离子路点较近时,即将子路点作为当前路点,再向下移个路点前进。注意这里计算敌人与子路点的距离时没有计算Y轴,因为我们希望空中的敌人飞到路点上方即认为是到达该路点。当敌人走到最后的路点,即是到达我方基地,销毁自身,并使基地减少一点生命值。接下来创建另一个飞行敌人的脚本AirEnemy,它继承了Enemy脚本的大部分功能,只添加一个Fly函数,作用是当高度小于2时向上飞行。
敌人生成器(EnemySpawner):塔防游戏的敌人通常是成批出现,一波接着一波,因为敌人数量众多,所以需要一个生成器按预先设置的顺序生成不同的敌人。为了提高工作效率,我们将在Excel中设置每一波出现的敌人,然后将其导出为XML格式,敌人生成器读取这个XML文件,按其设置生成敌人。实际上我们也可以在Unity的Inspector窗口设置组件的数值,但对于一个复杂的项目来说,这么做会使项目的维护变得困难,而游戏策划又偏爱使用Excel表格,所以可以先在Excel中设置数据,再将其导入Unity,这会是一个非常好的选择。
本游戏中唯一的防守单位是个袍塔(Defender),我们先准备一个ArrayList,将所有敌人遍历都存进去,这样很容易查找到任何一个敌人,当敌人进入它的攻击范围便会开火。在这个脚本中,FindEnemy函数主要用于查找进入其攻击范围的敌人,然后在这些敌人中再选出一个生命值最低的敌人作为目标敌人。Attack函数向敌人攻击,这里只是很简单地调用了敌人的SetDamage函数减少敌人的生命值。当创建了一个防守单位后,它所处的单元格则被当前防守单位占据,不能再创建其他东西,因此我们在Start函数中改变了防守单位所处单元格的属性。
生命条(LifeBar):敌人在受到攻击的时候,生命条可以显示它受到多少伤害,和提示它的剩余生命值。在Project窗口的Rawdata/Lifebar目录下引入了一个LifeBar.fbx文件,这是一个普通的平面模型,并附有一张贴图,贴图由简单的黄色和红色组成,我们将使用改变模型UV的方式,使黄色表示剩余生命值,红色表示失去的生命值。
在LifeBar脚本中,Ini函数负责初始化,主要是获取生命条模型的UV。在UpdateLife函数中,我们根据当前生命值和最大生命值计算出一个比例,然后调用Pad函数左右移动UV的位置,当生命值越低,黄色显示的越少,红色显示的越多。SetPosition函数负责设置生命条的位置,因为生命条始终要伴随乡音敌人的移动,并出现在它的上方,所以这个函数设置了一个yoffset参数,使生命条的位置向上偏移一定距离。
3 UV旋转纹理动画shaderLab
shaderLab主要用Cg语言来开发。Cg语言(C for Graphics)是为GPU编程设计的高级着色器语言,由NVIDIA公司开发。Cg极力保留C语言的大部分语义,并让开发者从硬件细节中解脱出来,Cg同时也有一个高级语言的其他好处,如代码的易重用性,可读性得到提高,编译器代码优化。下段代码实现了图片中纹理自动旋转的功能。
此处用到图形旋转三角函数数学公式
x1=cos(angle)*x-sin(angle)*y;
y1=cos(angle)*y+sin(angle)*x;
其中x,y表示物体相对于旋转点旋转angle的角度之前的坐标,x1,y1表示物体旋转angle后相对于旋转点的坐标。
4 游戏实现
4.1 代码实现
Unity3D游戏引擎能够快速开发,并具有高的扩展性,其脚本不仅支持JavaScript,而且支持C#,还支持C编写的DLL插件,可以提高代码的重复使用率。本文主要采用Cg语言开发ShadeLab, C#语言开发游戏C#由Unity3D的脚本默认继承自MonoBehavior,而MonoBehavior来源于Mono框架(属于.Net的跨平台框架)。
图3 生命条UV贴图
图4 自动转轮在场景中的显示
图5 自动转轮的UV贴图
4.2 游戏展示
本文实现了一个塔防类游戏,玩家可以通过键盘鼠标控制角色在场景中进行自由游戏。
我方基地有10点生命值,敌人攻入基地一次减少1点生命值,当生命值为零,游戏失败。敌人以波数的形式向我方基地进攻,每波由若干敌人组成。在这个实例中,一共有10波,当成功击退敌人10波的进攻则游戏胜利。
图6 游戏运行过程
敌人有两种,一种是在陆地上行进的装甲车,一种是飞行在空中的飞行器。每消灭一个敌人 获取一定点数,点数用于创建防守单位。
我们这里只完成一种基本类型的防卫单位,它是炮塔,一旦敌人进入它的攻击范围便会开火,同时每新建一个防守单位也会扣除一定点数以期望用最少的炮塔消灭最多的敌人为分数高。
图7 游戏运行过程
[1]卢金浩,张帅,伍传敏.三明学院信息工程学院.
[2]Unity Technologies ,Unity 4.x从入门到精通.中国铁道出版社.
[3]宣雨松.Unity3D游戏开发.人民邮电出版社.