4.1 场景元素生成
4.1.1 地形生成
将场景切换到pvp_001,该场景中不包含任何对象。在场景中创建一个地图Terrain,Unity编辑器自带功能完善的地形编辑器,支持以笔刷绘制的方式雕刻出山脉、峡谷、平原、高地等地形元素,也提供实时绘制地表材质纹理、树木种植、大面积草地布置等相关功能。另外,也支持地形相关资源的导入,可以让游戏场景看起来更加真实、细腻。
4.1.1.1 地形创建
先创建一个新的场景,然后打开菜单栏中的Terrain→Create Terrain命令,创建一个地形对象。如果要创建地形并实现山脉、峡谷、平原、高地、树木种植、大面积草地布置等功能的绘制,必须要了解和地形相关的知识点。
◎地形属性
选中Terrain地形对象,在Inspector中查看信息,如图4-1所示,主要包含三个组件。
图4-1
□ Transform:地形与其他的游戏对象有些不同,地形支持Transform(几何变换)组件中的Position(位置)变换,但对于Rotation(旋转)以及Scale(缩放)操作是无效的。
□ Terrain:Terrain组件共包含7个选项按钮,利用这些选项可以绘制地形起伏、地表纹理或附加细节(如树、草、石头等)。
□ Terrain Collider:地形碰撞器。
绘制地形主要依靠Terrain组件,如图4-2所示。Terrain组件包含7个功能,从左往右依次是:提高和降低地形高度(此功能配合Shift可以使地形瞬间平整),绘制目标高度,平滑高度,绘制纹理、树木、花草,地形参数设置。下面详细介绍每个功能的使用方法。
Step 01 提高和降低地形高度(见图4-2)。
图4-2
单击此选项按钮后,在展开的选项卡中,设置好Brush Size(笔刷大小)、Opacity(不透明度)的值以及绘制时使用的Brushes(笔刷),就可以在场景中用鼠标单击或拖动来绘制地形了。单击鼠标会增加高度,保持鼠标按下状态,移动鼠标,会不断地提升高度,直到其最大值。降低高度时,按住Shift键再单击鼠标即可。
Step 02 绘制目标高度(见图4-3)。
图4-3
单击第2个按钮选项,一般用于绘制平整的高地或峡谷等地形。设置好Brush Size(笔刷大小)、Opacity(不透明度)、Height(高度)的值以及绘制时使用的Brushes(笔刷),就可以利用鼠标移动场景中地形的任意部分,直到理想高度。
Step 03 平滑高度(见图4-4)。
图4-4
单击第3个按钮选项,可进入平滑高度模式。该模式用于柔化绘制的区域的高度差,使地形的起伏更加平滑。
Step 04 绘制纹理(见图4-5)。
图4-5
单击第4个按钮选项,可进入纹理绘制模式。单击Edit Textures按钮可添加、编辑、移除纹理。在添加时,可通过Select选项选择地形纹理,如图4-6所示。重复Add Terrain Texture过程,可以添加多个纹理。
图4-6
添加地形纹理以后,选择想使用的地形纹理,即可使用此纹理。拖动鼠标左键,对地形进行纹理绘制。
Step 05 绘制树木(见图4-7)。
图4-7
单击第5个按钮选项,进入植树模式。单击Edit Trees按钮,添加、编辑、移除树。重复Add Tree过程,可以添加多种树。选择使用的树,设置树的密度(Tree Density)为合适的值,即可将其绘制在场景中了。在场景中按住鼠标左键不放并拖动鼠标可在地形上连续种树。按住Shift键,然后单击地面,可以擦掉相应位置种植的树。
参数介绍(Settings):
□ Bush Size笔刷的尺寸。设置植树时笔刷的大小,取值范围为1~100。
□ Tree Density树木密度。用于控制树对象的间距。值越大,树木越密集、间距越小。取值范围为10~100。
□ Tree Height树的基准高度。值越大,树木越高,取值范围为50~200。
□ Color Variation每棵树的颜色所能够使用的随机变量值,取值范围为0~1。
Step 06 绘制花草(见图4-8)。
单击第6个按钮选项,可进入绘制花草模式。利用该模式可绘制草坪以及指定对草坪进行细节性描述的网格(Detail Mesh)。其中,Settings下的Target Strength表示目标强度,用于控制种草以及添加细节网格时所产生的影响,取值范围为0~1。
图4-8
单击Edit Details按钮,在弹出的快捷菜单项中选择Add Grass Texture命令,然后选择某种草坪纹理,并设置相关参数,单击Add按钮即可添加一种草坪,与绘制树木相似。
Step 07 地形参数设置(见图4-9)。
单击第7个按钮选项,进入地形设置模式。
图4-9
基本地形(Base Terrain)选项卡下的参数如下。
□ Draw:是否绘制基本地形。
□ Pixel Erroe:像素容差。这是在显示地形网格时允许的像素误差,是地形LOD系统的一个参数。
□ Base Map Dist:基本地图距离。设置地形贴图显示高分辨率的距离。
□ Cast Shadows:阴影。设置地形是否为投射阴影。
□ Material:材质。通过单击右侧的圆圈按钮为地形指定材质。
树及细节配置(Tree&Detail Objects)选项卡下的参数如图4-10所示。
图4-10
□ Draw:选中该项,所有的树、草和细节模型都将被渲染出来。
□ Detail Distance:细节距离。该项用于设定摄像机停止对细节渲染的距离。
□ Detail Density:细节密度。该项用于控制细节的密度。默认值为1,如果将此值调小,过密的地形细节将不会被渲染。
□ Tree Distance:树林距离。该项用于设定摄像机停止对树对象进行渲染的距离。值越高,越远的树会被渲染。
□ Billboard Start:开始广告牌。该项用于设定摄像机将树渲染为广告牌的距离。
□ Fade Length:渐变距离。该项用于控制树对象从模型过渡到广告牌的速度,如果值设置为0,模型会突变为广告牌。
□ Max Mesh Trees:最大的网格树。该项用于控制在地形上所有模型树的总数量上限。
关于风的设置(Wind Settings for Grass)选项卡下的参数如图4-11所示。
图4-11
□ Speed:速度。该项用于设定风吹过的速度。
□ Size:大小。该项用于设定风力影响的面枳。
□ Bending:弯曲。该项用于设定草木被风吹的弯曲程度。
□ Crass Tint:草的色调。该项用于设定所有草以及细节网格整体的色调。
如图4-12所示,Resolution选项卡下的参数用于添加地形对象后设置相关的属性。参数含义如下。
图4-12
□ Heightmap Resolution:高度图分辨率。
□ Detail Resolution:细节分辨率。控制草地和细节网格的地图分辨率。如果希望提高绘制性能,可将该数字设置得低一些,比如设置为512或者256。
□ Base Texture Resolution:基础纹理分辨率,用于代替有一定距离的贴图(splat map)的复合纹理分辨率。
4.1.1.2 导入地形
通过对地形设置的学习,读者可以一步一步地绘出想要的地图。游戏中的地图资源一般是由专业的美术人员精心制作出来的,作为教学产品,这里直接导入一个完整的地图,方便后续使用。将地图直接拖曳到战斗场景中,作为战斗场景的地形资源。有兴趣搭建地形的读者也可以参考课程http://books.insideria.cn/101/10。
4.1.2 英雄生成
进入战斗场景中,通过服务器端传来的英雄ID信息加载对应模型并创建英雄对象。为了方便学习,将此过程拆分为以下4步:
(1)接收消息。
(2)加载模型。
(3)创建模型。
(4)英雄管理。
4.1.2.1 接收消息
英雄生成需要依靠服务器端消息来驱动,因此,要处理生成英雄的消息。在Study文件夹中新建脚本,命名为GamePlay,连接网络,进行消息接收。
在进入战斗场景时,需要实现继续接收消息的功能,分为3步。
□ 在Start函数中调用恢复消息接收函数。
□ 在Update中处理网络连接与消息收发。
□ 重新定义消息接收的函数HandleNetMsg,处理接收的消息。需要在Awake中注册接收消息的监听器。
4.1.2.2 加载模型
消息重新接收后,可以根据消息来处理模型的显示。因此,当客户端接收到战斗英雄信息时(eMsgToGCFromGS_BroadcastBattleHeroInfo)需要加载英雄模型。消息的处理可以通过定义一个函数onNotifyBattleHeroInfo来完成。代码如下所示。
在GamePlay中定义存储模型的字典mPlayerModel,将加载的英雄模型存储到字典中。
注意
LoadModel函数是对Resources.Load资源加载方法的封装,因此英雄模型还要存储在Resources文件夹中。
Resources.Load方法仅实现资源的加载功能,并不能将资源作为对象显示到场景中。接下来介绍如何在场景中动态创建模型。
4.1.2.3 创建模型
将敌我双方的英雄模型加载并添加到字典中后,接下来要显示英雄模型,并根据服务器返回的消息设置位置与方向等信息。当服务器端返回eMsgToGCFromGS_NotifyGameObjectAppear时,在MessageHandler中定义处理函数,若用到GamePlay中变量,可以将消息广播回来,并在GamePlay中定义一个函数处理此消息。处理消息函数如下所示。
首先,英雄的模型都存储在mPlayerModel字典中,根据模型的ID可以获取每个模型。获取到模型后为模型添加组件,如果是本地玩家则添加MyPlayer组件,如果是敌方玩家,则添加Player组件。MyPlayer操控本地玩家的行为,Player操控敌方玩家。因此重新创建两个脚本Player与MyPlayer。
其次,添加组件后为模型设置场景中的位置。服务器传回来的位置与方向是封装在Pos结构体中,所以通过ConvertPosToVector3函数与ConvertDirToVector3函数将位置与方向转换成三维坐标。坐标转换过程请参考项目源代码。这里涉及坐标的转换,有关于坐标及其转换的内容请参考视频http://books.insideria.cn/101/11。
最后,需要对游戏对象的技能、血条等做初始化,所以后期对英雄进行控制需要在显示对象时进行初始化,现在仅仅是将模型显示在场景中,对于模型的控制还没有完成。接下来就要利用摇杆对模型进行控制。
4.1.2.4 英雄管理
进入战斗场景后,创建PlayersManager的脚本,负责存储管理玩家相关信息,用字典变量mPlayerDic来存储场景中所有玩家的信息,便于直接获取。因为是1v1(玩家对玩家)的游戏,也可以设置两个变量来存储英雄的信息。在玩家人数多于两个的情况下,只能通过集合或字典来存储,代码如下所示。
在创建本地玩家和敌方玩家时,类型为Player。因此,这是玩家的类型。新建一个脚本命名为Player,此脚本用来控制玩家的行为,所以作为玩家模型的组件。
游戏场景包含两个英雄:本地英雄和敌方英雄。本地英雄与敌方英雄控制方面有共同点,也有不同点。不同点是本地玩家的英雄移动由玩家操控,敌方移动由服务器传回的位置进行操控,而且本地玩家在移动时需要设置摄像机的跟随,敌方玩家则不用。共同点有很多,比如玩家释放技能,加载特效时是相同的。MyPlayer继承自Player。因此玩家的所有属性可以在Player中进行设置。