在沙盒游戏里,能自由建造是很重要的特点,比如说风靡全球的《我的世界》,用一个个方块就能搭建出规模宏大的世界。甚至有偏激的人说,没有自由建造,就不是一个真正的沙盒游戏。的确,沙盒游戏的魅力有很大一部分是能自由构建一个游戏世界。看着自己一砖一瓦搭建起一个城堡世界会很有成就感的。
现如今的手游,大多数就是一个争斗和炫耀的世界。不管是传奇类的狂霸拽酷,还是连连看,消消乐等好友排名,就是消费国人的虚荣心。其实,游戏是第九艺术,要上升到艺术的角度。在游戏里,玩家需要一种情感的宣泄和寄托以及体验。
说了这么多,还是回到正题。我是如何在自己编写的独立游戏《中世纪之路》里实现建筑物的摆放呢?
在实现这个功能的时候,我首先考虑的不是代码。而是考虑其他游戏是如何实现这样功能的,有那些可以借鉴的地方。我首先想到了《魔兽争霸》里建筑物的摆放,可以自由的拖放。然后想到了《部落冲突》(简称COC)里的建筑放置方式,在COC里建筑是按格子摆放的,在一个平面上。
在我的游戏《中世纪之路》里,我既不想让玩家一砖一瓦的搭建建筑,又想让玩家一下就把建筑整体放置到地上了。我觉得像搭积木那样,既可以节省玩家时间,又有建造的乐趣。选定了这种方式后,我就要考虑建筑模块的数据储存方式了:我需要储存单个模块的三维空间里的坐标值(x,y,z的值),同时为了让模型选择,我还需要储存模型的旋转角度数。
然后,我还需要考虑如何用鼠标去执行这个操作。我构想的方案是:点击背包里的物品后,一个模型就动态产生,然后跟随鼠标在地面移动。然后按"E"键就放置到地面上鼠标所指的位置(E键放置,是我学《兽人必须死》得来的)。而组件的旋转呢?我考虑的是让鼠标滚轮(也就是中键)滚动时,就绕着Y轴旋转。当然如果更近一步,可以做到绕三个轴都能自由旋转。具体操作见下图:
通过上面的操作,我们可以看到代码的实现效果很好,完美达到了我们的需求。不过想通了上面的操作原理后,还需要我们动脑筋来构思,如何用代码来实现这些操作功能。这对新手来说可能过难了点,但对于有经验的开发者就能比较快的找到近似的解决方案,然后加以改进。
我首先想到的是我曾经在手机上做过2D积木的搭建功能。把我们的操作动作拆解开来,无非就是三个步骤:
1.第一次按下手指或者鼠标,找到初始坐标,让物体动态出现在坐标位置上。
2.然后判断移动情况,让物体跟随鼠标或者手指的移动。
3.最后抬起手指或者鼠标,让物体固定在最后的坐标位置,把坐标数据写入到文本或者数据库里。
想清楚了这三个步骤,我们就心里底了,我们只要实现了这三步操作代码,基本上物体摆放功能就可以实现了。
在我实现第一个步骤的时候,我就遇到了个问题。我之前在写搭积木游戏的时候是2D的,坐标很好获取。但是在《中世纪之路》里,我可是要获取的是鼠标在地面上的坐标点啊。我开始用的是两行代码:
Vector3 mousePosition= Input.mousePosition; //获取鼠标所在的坐标 Vector3 mouseWorldPosition =Camera.main.ScreenToWorldPoint(mousePosition) ; //把鼠标的坐标变成3D游戏世界里的空间坐标
我以为我的思路是没问题的,结果运行代码一看。哈哈,建筑物完全不是摆在地面上的啊,是在空中的啊。后来查了相关文档才知道,这个鼠标位置是鼠标在屏幕这个立体面上的位置(你可以想象屏幕是一个透明的立体墙,这个墙是树立在地面上的)。再说得专业点,是从主摄像机为起点,鼠标所指为终点的一个射线,与屏幕所在立体面相交的点。对于这个立体面的直观感觉,大家可以看看在3D空间里2D UI界面所在的那个面。
所以,上面两行代码是不能够解决问题的。不过这么一分析,我就接触到了射线的概念。我转念一想,如果我找到这个射线和地面相交的焦点不就行了嘛!我们的思路就变成了以下的伪代码:
1.创建一个从主摄像机为起点,鼠标所指为终点的一个射线Ray。
2.找到Ray和地面Terrain的交点position。
3.把物体的坐标动态创建于position。
核心代码如下:
if (Input.GetMouseButton(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if(Physics.Raycast(ray, out hit)) { if(hit.transform.name=="Terrain") { position = hit.point;//得到与地面碰撞点的坐标 } } }
解决这个核心难题后,移动的代码就很好写了。只要在手指或者鼠标移动的时候,动态更新物体的坐标为新的position就可以了。旋转物体的代码也可以写到一起,动态更新物体的旋转度就可以了。这段代码很容易写了,我就不贴出来了,新手可以锻炼自我动手能力。老手早就不用看在眼里了。当然需要提醒的是这段代码是需要在update函数里去运行的。
最后的固定物体坐标的代码,也就演变成了把最后的position记录于文本或者写入数据库了。这些代码都不是有多难写的。
最后回顾下,我们整个解决问题的思路:
构思操作步骤和方式->分解操作步骤->用代码实现分解后的操作步骤->完善和修正代码
如果大家问我要整个代码,坦白的说:我觉得"授人于鱼,不如授于渔"。大家能获取正确解决问题的思路就够了,然后记住核心代码就可以了。做程序员到一个“手中无剑,心中有剑”的境界就够了。当然新手还是多练练剑,比划比划下招式。
PS:游戏DEMO试玩群:198035671 Unity3d技术交流群:308185833 斗鱼游戏开发直播地址:www.douyutv.com/unity3d