本文为实现类似《原神》在世界中与物品、NPC等的交互功能,在此抛砖引玉讲讲自己的实现方案。
实现思路:
简单版:
1.射线检测范围内可交互对象(可优化降低性能);
2.存在交互对象时显示交互界面;
3.点击交互界面选项进入该选项下一级UI可选交互选项;
4.点击交互选项实现其对应效果功能。
升级版:
射线检测范围内可交互对象(可优化降低性能)
1.角色设计射线检测方法
2.设计可交互对象类与对应数据表
存在交互对象时显示交互界面
1.显示交互UI,并获取射线检测存储的可交互对象数据
2.刷新UI的选项信息(图片、文字等)
点击交互界面选项进入该选项下一级UI可选交互选项,点击可选交互选项实现其对应效果功能
1.判定当前焦点状态机状态,若为非交互状态,则进入交互状态,并根据当前选项与数据表数据匹配寻找下一级(子级)的可选交互选项,然后刷新显示这些选项。如果为交互状态,则根据当前选项,调用其对应的事件方法。
再加上亿点点细节
1.创建数据库表并获取表数据(表文件、对应存储类),组合数据(交互选项与子级选项数据组合绑定一起,方便后续查找、使用);
2.角色射线检测与滚轮功能(通过“滚轮+按键”来选择当前选项);
3.设计状态机(可选);
4.设计互动UI与其具体UI管理类(UI初始化、选项事件注册等);
5.设计互动选项的事件管理类(管理选项的点击事件);
6.(难)设计互动UI管理类的具体实现功能(选项信息匹配、显示、滚轮功能的GUI焦点切换等);
7.在互动选项的事件管理类中对选项功能进行实现;
8.组合起来然后修bug。
实现效果
交互功能实现:类《原神》
由于整个代码部分比较复杂,以下展示关键代码,仅供学习参考
射线检测
/// <summary>
/// 每隔一段时间进行检测(几帧)
/// </summary>
private void RayCheck() {
if (!isCanRayCheck) {
return;
}
radiusClock += Time.deltaTime;
if(radiusClock > timeRadiusCheck) {
radiusClock = 0;
//检测
//重置数据
ListGoInteractive.Clear();
ListGoInteractiveId.Clear();
//射线检测--非常近的距离进行检测
//PS:需要对想要检测的对象设置好层级layout-FantasySpace
Collider[] cols = Physics.OverlapSphere(tf.position, radiusCheck,1<< LayerMask.NameToLayer("FantasySpace"));
//如果存在互动对象
if (cols.Length > 0) {
//如果焦点状态机不是Interactive,则显示最外层的互动UI
if (StateMgr.Instance.GetCurrentState(Machine.FocusMachine)!=State.Interactive)
{
for (int i = 0; i < cols.Length; i++)
{
//Debug.Log("检测到物体" + cols[i].name);
//将范围内所有可互动的对象(继承IInteractive接口)存入List中
if (cols[i].gameObject.GetComponent<IInteractive>() != null)
{
ListGoInteractive.Add(cols[i].gameObject.GetComponent<IInteractive>());
}
}
//如果List存在数据并且不处于具体焦点交互状态,则开启UI(显示最外层交互UI)
if (ListGoInteractive.Count > 0)
{
//初始化保存Id,便于后续UI的访问信息
GetListGoInteractiveId();
Send.SendMsg(SendType.AppearInteractiveUI, true);
}
}
}
else
{
//如果没有交互对象,且处于Interactive状态,则重置
if (StateMgr.Instance.GetCurrentState(Machine.FocusMachine) == State.Interactive) {
//没有互动对象,设置焦点状态机为isCtrl
StateMgr.Instance.GotoState(StateMgr.Machine.FocusMachine, StateMgr.State.Ctrl);
}
}
//没有检测到可交互对象则隐藏互动UI
if(cols.Length <= 0)
{
//如果List没有数据,则关闭互动UI
Send.SendMsg(SendType.AppearInteractiveUI, false);
}
}
}//RayCheck_end
滚轮功能
/// <summary>
/// 滚轮控制交互选择上下移动
/// </summary>
private void MouseScrollCtrl() {
//滚轮上下滑动
if (Input.GetAxis("Mouse ScrollWheel") < 0) {
mouseScroll -= GlobalPropertyMgr.FLO_OPOINT5;
if (mouseScroll <= -1 * GlobalPropertyMgr.FLO_MOUSESCROLLSPEED) {
Send.SendMsg(SendType.MouseScrollInteractiveUI, true);
mouseScroll = 0;
}
}
else if (Input.GetAxis("Mouse ScrollWheel") > 0) {
mouseScroll += GlobalPropertyMgr.FLO_OPOINT5;
if (mouseScroll >= GlobalPropertyMgr.FLO_MOUSESCROLLSPEED) {
Send.SendMsg(SendType.MouseScrollInteractiveUI, false);
mouseScroll = 0;
}
}
}
/// <summary>
/// 根据鼠标滚轮来进行互动选项的上下选择
/// </summary>
/// <param name="_objs"></param>
private void EventMouseScrollInteractiveUI(object[] _objs) {
print("触发");