Unity与C++网络游戏开发实战:基于VR、AI与分布式架构 【3.8】

10.14 战场场景中主视角UI系统功能开发

        在训练系统中,如何控制人物进行操作?如何使用UI瞄准器开动枪火进行攻击?如何使用二维的UI操作功能面板并且与3D世界进行交互?本节就来解决这些问题。下面将介绍3D战场世界中UI系统的开发,具体操作步骤如下所述。
        (1)打开战场场景BattleGame,然后在Project视图中搜索UI_GameMain预制体,它提供整个场景的UICamera。叠加渲染对应的UI预制体UI_MainInGame2,调整UICamera对UI资源渲染的层和3D世界Camera。它处理渲染层的过滤,在屏幕Game视图上渲染出场景和UI的叠加,如图10-20所示。

        (2)在整个战场环境中,UI需要有效显示出整个3D世界所需要的操作控件,并且提供一些辅助UI面板的启动入口。我们可以把整个战场环境中的UI分化成为不同的结构组件,在Hierachy视图中打开UI_MainInGame2面板的根节点。具体结构如下:
·ScrollView,是一个可以拖动的滑动面板。作为左上角地图的组件,从二维态势的面板上,缩放显示了任务的地点、当前任务和车辆移动所在的位置。
·右上角的任务事件和训练时间,使用Label在心跳函数中工作,保持了和当前系统训练时间同步跳动的计算。
·左下角使用LiaotianPanel作为根节点,包含了整个战场环境文字UI聊天面板的交互功能。
·右下角使用PlayerObj作为根节点,包含了当前控制的仿真人物的整个数据和环境,包含了人物的HP、子弹数量、车辆操作和车辆速度等与人相关的数据。
·UI中心使用MiaozhunSp作为根节点,使用Sprite和Animation的组合表现了枪口瞄准器。
·右上角的CarEuipmentBtn、CameraChangeBtn和WuqiBtn等按钮,提供了单击事件,并且作为启动其他UI面板的功能入口。针对这么多功能的集合UI系统,我们也需要开发针对这个的UI面板,所使用的对象类DlgNewInGame负责这个操作面板。在这段代码类中,大家需要关注以下几点:
·整个DlgNewInGame生命周期中的关键函数,在10.13节已为大家介绍过。
·关注3D场景中人物和车辆的坐标计算,它是如何映射到地图UI
功能里面去,并且实现在心跳函数Update中,地图和3D场景中的信息实时同步。
·聊天功能面板的一些功能,实际上在10.13节中已经开发过了,这里只是结合相关的功能到这个面板当中。
·人物和UI面板的互动操作,是基于UI中使用了Game.Instance.GetHostPlayer()来获取HostPlayer这个仿真人物类(见第7章中的相关介绍),通过它相关的函数和属性来操作人员。
·车辆和UI面板的互动操作,是基于UI操作HostCar相关的函数和属性,来操作和读取车辆数据,并且在Update中通过事件来同步车辆信息。
·中心部分的开枪,是基于HostPlayer的状态机来控制UI动画的播放情况。
·关注如何使用CarEuipmentBtn和CameraChangeBtn来打开其他UI面板,实际上使用方法是基于
Game.Instance.UIDlgMan.ShowDialog(DlgCarEquipment.NAME,true)这样的函数调用,直接启动其他动态UI组件所开发的UI面板。
具体代码示例如下:

//DlgNewMainInGame.cs 战场UI系统控制类
using BlGame.GameData;
using proto.data;
using proto.data.sourcedoc;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace FintolClient
{
public class DlgNewMainInGame : DlgBase
{
//UI预制体资源名
public const string NAME = "UI_MainInGame2";
//当前事件
private UILabel TimeLabel;
public UILabel TrainTimeDie
{
get { return TimeLabel; }
}
//任务标题private UILabel TitleLable;
//车辆UI资源节点
private GameObject CarObj;
//车辆相关信息
//----------------------此处省略部分代码------------------
---
//初始化UI和事件处理
protected override void Init()
{
//-----------------------此处省略部分代码------------------
}
//单击AI增加UI启动DlgaAddAIPlayer的UI面板
void OnClickAddAIButton(GameObject sender)
{
Game.Instance.UIDlgMan.ShowDialog(DlgAddAIPlayer.NAME, true);
}
//启动天气控制的UI面板
void OnClickChangeWeatherButoon(GameObject sender)
{
Game.Instance.UIDlgMan.ShowDialog(DlgChangeWeather.NAME, true);
}
//断线重连功能(发送服务器信息)
void OnClickReconnectBtn(GameObject sender)
{
//---------------------此处省略部分代码------------------
}
//启动退出游戏功能
void OnClickExitGameBtn(GameObject sender)
{
//---------------------此处省略部分代码------------------
}
//单击发送聊天消息
void OnClickSendBtn(GameObject sender)
{
//---------------------此处省略部分代码------------------
}
//聊天面板UI信息条动态添加
//---------------------此处省略部分代码------------------
//车辆装备UI面板
DlgBase dlgcarquipment;
//武器UI面板
DlgBase dlgweapon;
//单击启动关闭装备UI面板void OnClickCarEquipmentBtn(GameObject sender)
{
if (dlgcarquipment == null ||
!dlgcarquipment.IsShow())
{
dlgcarquipment =
Game.Instance.UIDlgMan.ShowDialog
(DlgCarEquipment.NAME, true);
}
else
{
dlgcarquipment.Show(false);
}
}
void OnClickWuqiBtn(GameObject sender)
{
if (dlgweapon == null || !dlgweapon.IsShow())
{
dlgweapon =
Game.Instance.UIDlgMan.ShowDialog(DlgWeapon.
NAME, true);
}
else
{
dlgweapon.Show(false);
}
}
//视角切换UI面板控制
DlgBase camerachange;
void OnCameraChange(GameObject sender)
{
if (camerachange == null || !camerachange.IsShow())
{
camerachange =Game.Instance.UIDlgMan.ShowDialog
(DlgCameraChange.NAME,true);
//
Game.Instance.ElsePlayerMng.Dic.Count.ToString(NAME);
}
else
{
camerachange.Show(false);
}
}
//UI面板销毁函数
new void OnDestroy()
{
//-------------------此处省略部分代码---------------------}
//控制瞄准器
Vector3 miaozhundefaultvec = Vector3.one;
int frameinverval = 0;
bool ismiaozhunani = false;
//瞄准器动画控制
IEnumerator MiaozhunAni()
{
while (ismiaozhunani)
{
frameinverval++;
if (frameinverval == 5)
{
MiaozhunSp.transform.localScale =
miaozhundefaultvec / 2;
}
else if (frameinverval == 10)
{
MiaozhunSp.transform.localScale =
miaozhundefaultvec;
frameinverval = 0;
}
yield return null;
}
}
//----------------------此处省略部分代码-------------------
--
//根据当前人物状态机更新UI人物信息和UI展示状况
public void UpdateHostPlayerAniState(PlayerState
playerstate)
{
if (playerstate == PlayerState.ShiftWalk ||
playerstate ==
PlayerState.LieDown)
{
BandunToggle.value = true;
}
else
{
ZhanliToggle.value = true;
}
//控制人物的射击UI的动画控制
if (_isfirsmode)
{
if (playerstate == PlayerState.Attack ||
playerstate ==
PlayerState.RunAttack){
if (!ismiaozhunani)
{
ismiaozhunani = true;
MiaozhunSp.GetComponent<Animator>
().SetBool
("ismiaozhun", true);
//MiaozhunSp.GetComponent<Animator>
().Play(0);
}
}
else
{
if (ismiaozhunani)
{
ismiaozhunani = false;
MiaozhunSp.GetComponent<Animator>
().SetBool
("ismiaozhun", false);
}
}
}
}
//更新任务时间
//--------------------此处省略部分代码-------------------
//--------------------此处省略部分代码-------------------
//心跳控制(UI生命周期中的每一帧更新)
void Update()
{
//-------------------此处省略部分代码-------------------
}
List<GameObject> redmaplist = new List<GameObject>();
#region 更改瞄准图片的透明度
//--------------------此处省略部分代码-------------------
#endregion
#region 初始化地面尺寸
void FindMapObj()
{
if (MapObj == null)
{
GameObject obj = GameObject.Find(Tagterrian);
if (obj)
{MapObj = obj;
}
}
if (MapObj == null)
{
return;
}
TerrainCollider collider =
MapObj.GetComponent<TerrainCollider>();
if (!collider)
{
return;
}
TerrianWidth = collider.bounds.size.x;
TerrianLength = collider.bounds.size.z;
WidthCheck = TerrianWidth / 2;
LengthCheck = TerrianLength / 2;
UpdateMap();
}
#endregion
#region 更新小地图
void UpdateMap()
{
float updataAngle;
HostPlayerObj =
Game.Instance.GetHostPlayer().RootObj;
if (HostPlayerObj == null)
{
return;
}
float vecPos_x = HostPlayerObj.transform.position.x;
float vecPos_z = HostPlayerObj.transform.position.z;
float vecPos_y = HostPlayerObj.transform.position.y;
//HostPlayerObj.transform.position = new
Vector3(vecPos_x,
vecPos_y, vecPos_z);
float host_x = vecPos_x / 60000 * SmallMap_x;
//获得小地图上目标的x位置
float host_y = vecPos_z / 60000 * SmallMap_y;
//获得小地图上目标的y位置
#region 计算小箭头朝向
Vector3 pos = MapObj.transform.position -
HostPlayerObj.
transform.position;
Vector3 pos_horizontal = new Vector3(vecPos_x,vecPos_y, 0) –
HostPlayerObj.transform.position;
//目标物体的正方向与到原点向量的角度
float angle = Vector3.Angle(pos,
HostPlayerObj.transform.
forward);
//目标物体与到此物体垂直水平轴的点的角度
float angle2 = Vector3.Angle(pos_horizontal,
HostPlayerObj.
transform.forward);
//目标物体正前方
Vector3 posNew = HostPlayerObj.transform.position +
HostPlayerObj.transform.forward;
if (vecPos_z > 0)
{
updataAngle = posNew.x > vecPos_x ? -(180 -
angle2) : 180 -
angle2;
}
else if (vecPos_z < 0)
{
updataAngle = posNew.x > vecPos_x ? -angle2 :
angle2;
}
else
{
if (posNew.z > 0)
{
updataAngle = posNew.x > vecPos_x ? -(90 -
angle) :
90 - angle;
}
else
{
updataAngle = posNew.x > vecPos_x ? -(90 +
angle) :
angle - 90;
}
}
#endregion
if (blueprefab)
{
//更新坐标
blueprefab.transform.localPosition = new
Vector3(host_x,
host_y, 0);//更新坐标
blueprefab.transform.localEulerAngles = new
Vector3(0, 0,
updataAngle);
}
foreach(GameObject obj in redmaplist)
{
obj.SetActive(false);
}
int index = 0;
foreach(ElsePlayer ep in
Game.Instance.ElsePlayerMng.Dic.Values)
{
if (ep.RootObj == null)
{
continue;
}
if(index >= redmaplist.Count)
{
GameObject copyobj =
GameObject.Instantiate(redprefab.
gameObject) as GameObject;
copyobj.transform.parent =
blueprefab.transform.parent;
copyobj.transform.localScale = Vector3.one;
redmaplist.Add(copyobj);
}
redmaplist[index].SetActive(true);
redmaplist[index].transform.localPosition =
new Vector3(ep.RootObj.transform.position.x / 60000 *
SmallMap_x, ep.RootObj.transform.position.z / 60000 *
SmallMap_y, 0);
index++;
}
}
#endregion
#region 望远镜模式
//--------------------此处省略部分代码--------------
#endregion
#region 获得场景逻辑
//--------------------此处省略部分代码--------------
}
}

10.15 VR开发——VR可以使用的VRGUI

        本章完成了在整个仿真训练系统中,UI面板的开发和整个系统的交互处理。这些UI都是基于二维环境下,通过鼠标的单击或者键盘的处理,来实现和整个场景的交互。
        在现代仿真系统和游戏的发展下,已经出现了VR相关操作和系统的开发。VR系统的UI开发是一个全新的领域。如何实现在整个仿真VR环境下面的UI开发呢?本节就为大家引申介绍这部分的开发,结合NGUI升华实现VR环境下的UI框架——VRGUI。
        VR主流的UI操作交互方式有两种:一种是通过设备提供手持设备,通过设备的仿真在3D环境下发射一条射线进行操作;第二种是使用视线与人的头盔保持同步,从视线中间进行射线检测,来操作3D渲染的UI,如图10-21所示。

        基于NGUI开发VRGUI的原理是什么呢?在10.4节介绍过,NGUI的原理其实是基于UICamera来渲染并且发射屏幕射线,然后传播到3D世界环境的一种射线检测方式。
        VRGUI的方式就是把NGUI制作成为3D资源,然后通过3D世界的Camera来渲染,提供一个3D世界的射线检测器,检测UI的操作状况,并且提供事件返回给各个UI组件。
        在我们的仿真系统里面也提供了这样一个射线的核心组件,并且改造过NGUI的各个组件代码的检测方式。具体的射线管理检测类,可以打开工程中的代码VRRayManager进行学习,主要关注以下几个方面:
·public GameObject hoveredObject,射线检测碰撞到的UI3D对象。
·static public bool Raycast(TriggerTouch touch,Vector3 inipos, Vector3dir),通过发射射线器发射的射线心跳检测对应的3D物体,并且实时同步给hoveredObject,以及通知UI组件信息。
·public void Update(float deltetime),射线心跳管理,实时检测射线操作管理。
·void ProcessPress(bool pressed,float click,float drag),操作功能键外设处理,同步事件给NGUI。
·NGUI中各个组件对于VR射线的同步消息处理。这一节涉及的知识点多,复杂度较高,大家有兴趣可以去查阅本书附带的工程中关于VR操作的代码,具体代码此处暂时不做详细解读。

10.16 本章小结与分析

        通过本章的学习,完成了客户端的实战开发。本章主要为大家介绍了UI交互系统的开发,整个客户端实战介绍了4大部分:人物系统开发、场景系统开发、资源和功能组件开发、交互系统开发。这里总结一下本章的学习内容,并进行深入分析和思考。
        (1)NGUI的操作和渲染原理是什么?在NGUI中如何使用图集支持各个组件的渲染?如何优化UI组件?在UI组件中,读者也需要仔细了解UILabel、UISprite、UIPane、UIButton、UIRoot和UICamera等基础组件的功能。
        UIButton其实也是使用碰撞体和UILabel等结合开发出的一个组件。读者可以研究一下自定义UI组件,比如使用UIScrollView和其他组件的结合,实现一个3D翻转的相册UI组件。
        (2)结合NGUI开发动态UI功能面板所使用的基类DlgBase。请思考几个问题:这个类的生命周期可以分为哪几个部分?在不同的生命周期,它承担了UI功能在整个系统中的哪些功能?使用这个基类的时候,应该在哪些函数中重写,并且开发相应的功能代码?DlgBase是如何基于ResourcesManager实现UI资源功能的动态加载和管理?这样的开发模式——工厂模式的应用结构是什么样的?如何管理UI资源的引用?基于这个模式,大家可以再研究一下资源的热更新。在开发网络游戏的时候,如何通过网络处理UI的版本动态更新资源并保持依赖作用?
        (3)本章开发了仿真系统的各个UI面板,为大家介绍了登录UI、大厅人物系统UI和战场场景交互UI三个面板的开发。实际上在整个系统中还有许多小型的和大型的UI功能的开发。在这个开发基础上,大家要学会把UI面板分类,哪些是工具类UI,比如消息弹窗;哪些是可以通过已开发的UI进行组合,这样才能节约开发工作量。我们需要使用UI管理组合分布的方式,让系统开发更加高效。
        (4)除了这类两维的UI表现形式,本章还引申讲解了基于VR系统和交互的开发方式,并且介绍了对应的开发原理。在本书配套资源的工程代码里有对应的实现,读者可以去研究一下。
针对VR系统,目前市场上有多种多样的硬件,如何去实现切换SDK并适应各种VR的UI操作?这就要基于data数据和SDK的封装技术,再结合VRGUI控件来一同实现。
        以上就是本章的重点内容总结,请读者把整个客户端的知识点和实战开发总结回顾一下,结合自己的理解,对应改造一下整个系统。读者还需要理解本书配套资源的工程代码中其他小功能的代码及其实现。
        通过本章的学习,相信读者能很快地掌握客户端开发的总体框架和知识体系。在下一篇章中,我们将进入服务器的学习,一同加油!

相关推荐

  1. Unity-游戏

    2024-05-10 08:36:02       62 阅读
  2. 分布式任务调度:架构、原理实践

    2024-05-10 08:36:02       38 阅读
  3. Unity文字游戏开发日志(2)——存档读档

    2024-05-10 08:36:02       53 阅读
  4. [游戏开发][Unity] 导出Xcode工程,完成调试发布

    2024-05-10 08:36:02       40 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-10 08:36:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-10 08:36:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-05-10 08:36:02       87 阅读
  4. Python语言-面向对象

    2024-05-10 08:36:02       96 阅读

热门阅读

  1. 信息系统架构_2.信息系统架构分类

    2024-05-10 08:36:02       36 阅读
  2. vlan和vxlan的区别与联系,以及他们对应的设备

    2024-05-10 08:36:02       58 阅读
  3. python的zip函数

    2024-05-10 08:36:02       37 阅读
  4. How to Install PySpark on Mac(Mac上安装PySpark)

    2024-05-10 08:36:02       31 阅读
  5. cpp Easy Timer

    2024-05-10 08:36:02       33 阅读
  6. 华为ICT学院教师指南(HCAI认证)结课测试

    2024-05-10 08:36:02       40 阅读
  7. 可视化3个10分类

    2024-05-10 08:36:02       38 阅读