文明瑶++钟炫凯
[摘 要]游戏开发者总是容易被现实世界的各种物理逻辑,真实规律所束缚。文章讲述一种新的游戏开发思维,介绍如何利用魔术思维开发游戏,从而使程序在不改变需求的情况下,更有效率,更加灵活,扩展性更高。研究通过对基于Unity3D的《立方杀阵》游戏的优化,证明了这种思维的可行性和有效性。
[关键词]Unity3D 魔术思维 预制体
中图分类号:P208 文献标识码:A 文章编号:1009-914X(2016)02-0353-02
1 引言
与一般应用不同,游戏中常需模拟现实世界、甚至超现实世界的动作效果,这对游戏开发者提出了更高的要求。例如在射击游戏中,如何为用户提供逼真的射击感受十分重要。在进行游戏开发时,很多难题,不如换一个视角来观察,换一种方式来解决,可能获得意想不到的效果。文章提出一种从魔术思维出发的方式,通过一些伪技巧,把一些真实需求魔术化地呈现给玩家。让游戏开发不仅仅是一种技术,更提升为一种艺术。
2 游戏《立方杀阵》开发前期遇到的困扰
(1)要让玩家感觉到房间数量巨大,有种身处巨型密室的感觉,然而场景中构建过多房间会导致游戏帧数低于10帧。
《立方杀阵》是从电影《异次元杀阵》中获取灵感进而改编的独立游戏。根据电影中的设定,该杀阵是一个由26×26×26=17576个小立方体房间组成的巨型立方体密室。所以,如果要让玩家可以在密室中像电影一样逃脱,并获得乐趣,显然必须有非常多的立方体房间才可以满足游戏的需求。然而,在Unity3D中,经过测试,当立方体房间超过4×4×4=64个房间的时候,还没运行游戏,就已经开始卡频了。64个房间,即立方体每个方向4个房间,无法让玩家感受密室的庞大。
(2)子弹速度过快导致无碰撞穿越。
Unity3D中,物体的移动速度过快,极大可能导致在碰撞另外的物体时,产生穿越,从而无法发射碰撞检测。根据射击系统的需求,子弹是必须出现的。如果子弹速度不够快,那么玩家的射击体验将会大大下降,游戏沉浸感会大大减少。子弹速度要达到真实世界的子弹速度,又会导致穿越现象,有时候射击了物体但没反应。
3 现实逻辑实现需求
(1)现实逻辑解决需求1:使玩家体验到身处许多房间的巨型密室。
Unity3D的游戏脚本为基于Mono的Mono脚本,一个基于.NET Framework的开源语言,因此程序员可用C#,JavaScript等加以编写[1]。文章将采用C#语言进行示例。
在游戏世界放置数量足够多的房间。用代码生成场景如下:
public void InstantiateAllRoom(int Num,GameObject RoomPrefab){
int nowNum=0; //当前房间个数
for(int y=0;y for(int z=0;z for(int x=0;x //实例化一个房间在对应的(X,Y,Z)坐标上 GameObject g=(GameObject)Instantiate(RoomPrefab,newVector3(x,y,z), RoomPrefab.transform.localRotation); g.name="第"+nowNum+"个"; //为房间命名 nowNum+=1; //数量加一 } } } } 这种思维会真实模拟出电影《异次元杀阵》的世界,让游戏场景可以按照电影的世界来构造,使游戏世界非常直观。但时间复杂度较高,复杂度为O(Num3)。 在CPU为Intel酷睿i52450M,内存4GB,显卡NVIDIA GeForce GT 520M(1G显存)的电脑环境下测试,当房间数量达到10000个时,游戏运行对CPU占用的时间为每帧37.06ms,对摄像机渲染值(消耗显存)占有率达到了80%左右。生成26*26*26规模的立方场景测试游戏实际运行帧数,帧数运行效果如下图1所示: 图1中左侧的Hierarchy面板中表示游戏场景有17576个房间,游戏场景输出了当前游戏帧数。可以看到在房间数量超过1万间的情况下,当前Draw Call(Unity每次在准备数据并通知GPU渲染的过程称为一次Draw Call)为27次,帧数低于10帧,流畅度较低。 (2)现实逻辑解决需求2:子弹达到真实世界射击体验 当射击事件触发时,实例化一颗子弹的预制物体,赋予该预制子弹物体的加速度与方向,使其向目标发射。当子弹进入目标触发范围,或者与目标发生碰撞时,产生击中事件。游戏对象如果需要感应碰撞,那么必须给其添加碰撞器[2]。手动添加Unity3D的碰撞器即可。子弹射击效果实现代码如下: public void Fire(GameObject Bullet,float Force){ //在当前位置实例化一颗子弹 GameObject g=(GameObject)Instantiate(Bullet,transform.localPosition,Bullet.transform.localRotation); //在子弹身上作用一个向前的力使其飞行 g.GetComponent } 但是这种方法,每次射击实例化一个物体,多次射击后,为了防止内存逐渐增多,每颗子弹都要实现延迟销毁。然而Unity3D中的Destroy()函数对子弹的销毁并不彻底,被销毁的物体本身在内存中保留有网格渲染缓存,直到进行场景切换才彻底清空。随着游戏运行时间的增加,内存消耗增加,并且该方法也没有达到射击体验的需求。
4 魔术思维实现需求的方式
(1)魔术思维实现需求1:使玩家体验到身处许多房间的巨型密室。
魔术师戒指握在手心,在观众眼前轻轻吹一口气,再打开手,戒指消失了,接下来魔术师让观众打开桌子上的盒子,观众发现,戒指赫然出现在盒子里。
这个魔术里面用到了2种思维,分别是错误引导思维和预制体思维。要解决立方杀阵游戏场景房间过多问题,用两种思维即可解决。
首先利用错误引导思维,在游戏介绍或者开头CG上告诉玩家这是个巨型立方体。在游戏开始时让玩家处于一个已初始化的房间中。玩家在开门前,无法得知房间外的情况,事实上此时只有一个房间。
而预制体思维说的是盒子里的戒指其实一开始就存在,和消失的戒指一模一样,早就放在盒子里,必要的时候,才会让它出现给观众看到。同样,事先构建好立方体房间的一种单独个体,预制在资源文件中,作为预设体,可以理解为是一个游戏对象及其组件的集合,目的是使游戏对象及资源能够被重复使用[3]。使用该预制对象,就可以创建一个交互式的3D场景[4]。当玩家开启某个门时,实例化一个立方体房间在对应的下一个房间位置上,当门被完全打开时,玩家看到的,是另一个立方体房间。以此类推,如此,多房间体验需求就解决。
玩家在房间中进行游戏时,类似于虚拟漫游项目的观房,使用动态的、有交互功能的三维观 , 能够使整个看房过程更加生动,使用户有一种身临其境的感觉[5]。同样,在有交互性与三维的前提下,这种开发思维也达到了身临其境的房间体验。
无论使用哪种方式,都会导致一个问题,便是随着玩家打开的房间越来越多,超过64个房间时,会导致游戏很卡。解决方式是利用改进的先进先出算法删除旧房间。由于《立方杀阵》中玩家经过8个房间所需的时间和房间周期性位移设定的时间几乎相同,此时即使第一个房间因被删除而发生变化也不会出现不合理的空间现象,所以《立方杀阵》中的房间设定最大值为8。模块代码如下:
public void InstantiateOneRoom(Vector3 RoomTransform,GameObject RoomPrefab){
bool canInstantiateOneRoom=true; //该位置可否生成房间的标志
if(NowRoomNum>=AllRoom.Length){NowRoomNum=0;} //数组序号重置
for(int i=0;i
//遍历房间数组,看目前该位置是否已经存在房间,如果存在则不可以再次生成房间
if(AllRoom[i].name==""+RoomTransform){canInstantiateOneRoom=false;break;}
}
if(canInstantiateOneRoom){ } //生成房间
}
生成房间代码时间复杂度为:O(AllRoom.Length)。对比第一种方法, O(AllRoom.Length)远远小于O(Num3)。
经过测试,当房间数量为8间时,游戏运行对CPU占用的时间为每帧0.1ms,对比前一开发方案减少了36%。对摄像机渲染值(消耗显存)占有率达到1.2%左右。对比前一开发方案,减少了78.8%。很明显,该方案较好的优化了该游戏,使游戏更加顺畅。当生成8个房间,测试实际游戏运行的帧数,如图2所示:
图2中左侧的Hierarchy面板中表示游戏场景有8个房间,游戏场景输出了当前游戏帧数,可以看到当前情况下, Draw Call为8次,帧数达到59.9帧,流畅度极高。
(2)魔术思维实现需求2:子弹达到真实世界射击体验
魔术思维开发方式:魔术师将女助手四肢呈一个大字绑在一个大转盘上面,在转盘慢速旋转的情况下,魔术师蒙上眼镜,将10把飞刀一个个准确的扔到女助手身体旁边的转板上,狠狠的插了进去,丝毫不伤及女助手。
其实,魔术师只是在假装扔飞刀,接下来,事先安装在转盘背面的弹簧装置,从转盘背后反方向弹出一把插在转板上的飞刀。
实例化子弹每次都会产生一次内存损耗。为解决该问题,采用魔术思维,以Unity3D里的一种射线替代子弹。玩家射击时,出现一条可控制距离(用于不同枪械射击距离)的射线,而射线只出现一帧,那一帧立刻判断射线是否射中目标,是否触发击中事件,模拟了真实世界子弹的射击速度,避免了检测丢失的现象。射线方案射击功能模块代码如下:
public void Fire(float Distance){
RaycastHit hit=new RaycastHit(); //射线发射相关信息
Camera camaer=Camera.main; //主摄像机
Vector3 t=camaer.transform.position+camaer.transform.forward*Distance; //射线目标坐标
//由摄像机到终点的方向发射一条射线
Ray ray1 =new Ray(camaer.transform.position,t-camaer.transform.position);
if(Physics.Raycast(ray1,out hit,Distance)){
//当发射射线时,在开发者界面绘制一条绿色的线
Debug.DrawRay (ray1.origin,t-camaer.transform.position, Color.green);
}
}
对于各种模拟子弹飞行时的特效火线,或者击中墙壁等地形时的石灰飞溅效果,都可以在射击事件和击中事件中在对应位置实例化这个效果,AI射击方面也可以用射线判定。
对两种思维实现射击的效果进行测试,同时运行游戏20分钟。在实例化子弹的方案中,即使销毁了子弹物体,由于缓存的作用,游戏内存的占有率一直逐渐增加,20分钟时达到了250M左右。而使用射线方案的情况下,内存占有率一直在205M左右浮动。由此可知,随着游戏的持续运行,用射线代替实例化子弹的方案更优。
5 其他问题的魔术思维优化方案
(1)需实现第一人称手拿着物品,但不显示手部的美工模型与动画。
把物品模型的2/3出现在屏幕左或右下方(表示左右手),1/3隐藏在屏幕下方外界,主角拿着物品走动时,应使物品有频率抖动效果。
(2)场景初始化过于突兀。
一个游戏场景需要初始化许多物体,出现逐渐渲染周围世界的效果,或者主角模型的坐标需要重置等。这些效果会导致玩家看到一些如突然瞬移,一棵树突然出现等奇怪的现象,降低游戏沉浸度。在场景开始时,保留读取界面的UI图片或背景,使其与读取界面无缝结合,等待场景物体初始化完成后(一般不会超过2秒),让玩家按下某个按键来开始游戏,可掩盖一些不应该出现的效果,使游戏平滑过场。
6 总结与展望
通过对《立方杀阵》一部分游戏模块的分析可知,在合理的情况下,使用魔术思维的游戏开发方式让游戏的程序结构更加灵活,效率更高,为游戏需求的实现增加了更多的考虑方向,更让游戏开发从技术提升为另一种领域的艺术。
但魔术思维并不适应所有的游戏开发需求,某些需求如果需要非常清楚的展示整个效果的实现过程,那么魔术思维这种伪实现方式便无法发挥作用,希望未来能继续改进,使魔术思维的运用更加广泛,完善。
参考文献
[1] 俞亮,俞文心.基于Unity的Web游戏设计与实现[J].计算机光盘软件与应用,2014(8):225-226.
[2] 王彩玲,刘瑞香,宋钊.基于Unity3D的虚拟校园漫游的设计与实现[J]. 科技视界,2015,5.
[3] Unity Technologies.Unity4.x 从入门到精通[M].北京:中国铁道出版社,2011,11.
[4] 方凯.在Unity3D中实现区域触发交互[J].科技信息,2012,28.
[5] 丁妹,胡志秋.虚拟建筑模型场景漫游系统[J].信息技术与信息化, 2005,1.