AimSystem Unity瞄准场景中的物体并进行交互的系统

2022-08-29 15:31:34 浏览数 (1)

最终效果:

当我们对准场景中的一个物体时,可以显示这个物体的名称或者对这个物体的描述。如图所示,我们对准了场景中的一个桌子,对这个桌子的描述:is a aimable gameobject. 会显示在一旁。除此之外,我们可以添加瞄准进入事件,瞄准离开事件,瞄准停留事件,鼠标点击事件。

思路:

实现该系统所需要编写的几个内容

1.AimSystem 其实就是一个射线检测系统,不停的去检测场景中可以进行瞄准的物体。

2.IAimableObject 可以瞄准的物体所继承的接口。

3.AimSystemPanel UI界面 用来显示瞄准物体的名称或者描述。

AimSystem:

代码语言:javascript复制
using KFramework;
using UnityEngine;

/// <summary>
/// 瞄准系统
/// </summary>
public class AimSystem : MonoBehaviour
{
public static AimSystem Instance { get; private set; }

private void Awake() => Instance = this;

//瞄准距离 (即射线检测的距离、检测范围)
readonly float mAimDistance = 5f;
/// <summary>
/// 当前所瞄准的物体
/// </summary>
public IAimableObject CurrentAimableObject { get; private set; }
//射线
    Ray ray;
//是否检测到碰撞
bool isHit;
//碰撞信息
    RaycastHit hit;

/*  射线起点
     *  从屏幕正中央发出射线
     *  (0.5f,0.5f) 
     */
    Vector2 mCentralPoint = Vector2.one * 0.5f;

//UI界面
    AimSystemPanel panel;

private void Start()
    {
//打开界面
        panel = UIMgr.OpenPanel<AimSystemPanel>(prefabName: "Resources/AimSystemPanel");
    }

public void AimEnter(string str) => panel.AimEnter(str);

public void AimExit() => panel.AimExit();

private void Update()
    {
//从主相机视角中央发出射线
        ray = Camera.main.ViewportPointToRay(mCentralPoint);
//是否检测到碰撞
        isHit = Physics.Raycast(ray, out hit, mAimDistance);
if (isHit)
        {
/*  所检测到的物体是否继承 IAimableObject 接口
             *  即是否为可瞄准物体   
             */
            GameObject aimTarget = hit.collider.gameObject;
            IAimableObject aimObject = aimTarget.GetComponent<IAimableObject>();

//如果可瞄准物体所设定的可被检测距离 小于 当前实际距离 将其置为空 表示没有检测到
            (null != aimObject && aimObject.AimDistance < hit.distance).Invoke(() => aimObject = null);

//如果与当前可瞄准物体不一致 表示检测到新的可瞄准物体
if (aimObject != CurrentAimableObject)
            {
/* 
                 * 如果上一个可瞄准物体不为空  执行其瞄准离开事件
                 * 将当前可瞄准物体 赋值为新检测到的
                 * 执行当前可瞄准物体的 瞄准进入事件
                 */
                CurrentAimableObject?.OnAimExit();
                CurrentAimableObject = aimObject;
                CurrentAimableObject?.OnAimEnter();
            }
//如果点击鼠标左键且当前可瞄准物体不为空  执行其鼠标点击事件
            Input.GetMouseButtonDown(0).Invoke(() => CurrentAimableObject?.DoInteract());
        }
/* 
         * 此处表示没有碰撞信息
         * 如果当前可瞄准物体不为空
         * 执行其瞄准离开事件 并将其置为空
         */
else
        {
if (null != CurrentAimableObject)
            {
                CurrentAimableObject.OnAimExit();
                CurrentAimableObject = null;
            }
        }
//如果当前可瞄准物体不为空 不停执行其瞄准停留事件
        CurrentAimableObject?.OnAimStay();
    }

private void OnDestroy()
    {
        Instance = null;
//卸载UI界面
if(Application.isPlaying)
            UIMgr.ClosePanel<AimSystemPanel>();
    }
}

IAimableObject:

代码语言:javascript复制
/// <summary>
/// 可瞄准物体接口
/// </summary>
public interface IAimableObject
{
/// <summary>
/// 瞄准距离(可被检测的距离)
/// </summary>
float AimDistance { get; }
/// <summary>
/// 物体的名称或对其描述
/// </summary>
string AimDescription { get; }
/// <summary>
/// 瞄准进入事件
/// </summary>
void OnAimEnter();
/// <summary>
/// 瞄准离开事件
/// </summary>
void OnAimExit();
/// <summary>
/// 瞄准停留事件
/// </summary>
void OnAimStay();
/// <summary>
/// 鼠标点击事件
/// </summary>
void DoInteract();
}

界面制作只需要一个Text文本框,当瞄准进入时显示其AimDescription,当瞄准离开时将Text文本框内容清空。

下面是继承接口的一个实现:

代码语言:javascript复制
using System;
using UnityEngine;

/// <summary>
/// 可瞄准物体基类
/// </summary>
public class AimableObject : MonoBehaviour, IAimableObject
{
//瞄准进入事件 可以进行事件的追加和删除
private Action mOnEnterEvent;
//瞄准离开事件 可以进行事件的追加和删除
private Action mOnExitEvent;

//瞄准距离(可被检测的距离)  子类去重写
protected virtual float AimDistance => 5f;
//物体的名称或对其描述  子类去重写
protected virtual string AimDescription => name;

/// <summary>
/// 瞄准进入事件
/// </summary>
protected virtual void OnAimEnter()
    {
        mOnEnterEvent?.Invoke();
        AimSystem.Instance.AimEnter(AimDescription);
    }
/// <summary>
/// 瞄准离开事件
/// </summary>
protected virtual void OnAimExit()
    {
        mOnExitEvent?.Invoke();
        AimSystem.Instance.AimExit();
    }
/// <summary>
/// 瞄准停留事件
/// </summary>
protected virtual void OnAimStay() { }
/// <summary>
/// 鼠标点击事件
/// </summary>
protected virtual void DoInteract() { }

float IAimableObject.AimDistance => AimDistance;
string IAimableObject.AimDescription => AimDescription;
void IAimableObject.OnAimEnter() => OnAimEnter();
void IAimableObject.OnAimExit() => OnAimExit();
void IAimableObject.OnAimStay() => OnAimStay();
void IAimableObject.DoInteract() => DoInteract();

protected virtual void OnDestroy()
    {
        mOnEnterEvent = null;
        mOnExitEvent = null;
    }

/// <summary>
/// 对瞄准进入事件进行追加
/// </summary>
/// <param name="action"></param>
public void AppendAimEnterEvent(Action action)
    {
if (null == mOnEnterEvent) mOnEnterEvent = action;
else
        {
            Delegate[] delegates = mOnEnterEvent.GetInvocationList();
if (!Array.Exists(delegates, v => v == (Delegate)action))
                mOnEnterEvent  = action;
        }
    }
/// <summary>
/// 对瞄准离开事件进行追加
/// </summary>
/// <param name="action"></param>
public void AppendAimExitEvent(Action action)
    {
if (null == mOnExitEvent) mOnExitEvent = action;
else
        {
            Delegate[] delegates = mOnExitEvent.GetInvocationList();
if (!Array.Exists(delegates, v => v == (Delegate)action))
                mOnExitEvent  = action;
        }
    }
/// <summary>
/// 对瞄准进入事件进行删除
/// </summary>
/// <param name="action"></param>
public void DeleteAimEnterEvent(Action action)
    {
if (null == mOnEnterEvent) return;
        Delegate[] delegates = mOnEnterEvent.GetInvocationList();
if (Array.Exists(delegates, v => v == (Delegate)action))
            mOnEnterEvent -= action;
    }
/// <summary>
/// 对瞄准离开事件进行删除
/// </summary>
/// <param name="action"></param>
public void DeleteAimExitEvent(Action action)
    {
if (null == mOnExitEvent) return;
        Delegate[] delegates = mOnExitEvent.GetInvocationList();
if (Array.Exists(delegates, v => v == (Delegate)action))
            mOnExitEvent -= action;
    }
}

Example:

代码语言:javascript复制
using UnityEngine;

public class AimableObjectExample : AimableObject
{
protected override string AimDescription => "Is a aimable gameobject.";

protected override float AimDistance => 4.5f;

protected override void OnAimEnter()
    {
base.OnAimEnter();
        Debug.Log("OnAimEnter");
    }

protected override void OnAimExit()
    {
base.OnAimExit();
        Debug.Log("OnAimExit");
    }

protected override void DoInteract()
    {
        Debug.Log("DoInteract");
    }

protected override void OnAimStay()
    {
        Debug.Log("OnAimStay");
    }
}

0 人点赞