简介

游戏中经常会需要实现屏幕外标记指示的功能,这样能方便玩家快速定位目标方位,尤其在多人网络游戏中出现(守望先锋,彩虹六号,Apex英雄等等):

屏外显示实例

具体实现

现在使用Unity实现,其实要复杂可以很复杂,这里肯定用简单直接点的,核心功能是Unity的Unity - Scripting API: Camera.WorldToScreenPoint (unity3d.com)https://docs.unity3d.com/ScriptReference/Camera.WorldToScreenPoint.html

这个API可以直接将特定物体在屏幕上的相对坐标输出出来,真是非常Nice!

在Unity Canvas中加入一个Image便可,箭头的话放入子集,可能需要一个空物体做parent来当作旋转轴心,然后在Canvas组件中加入如下脚本,然后在Unity中指定每个变量。部分解析在代码注释中,不保证是最佳解决方法,但是确实能用。注意你可能需要移除ModifyScale方法,除非你使用DOTWEEN

using DG.Tweening;
using UnityEngine;
using UnityEngine.EventSystems;

/*
 * Copyright (c) [2023] [Lizhneghe.Chen https://github.com/Lizhenghe-Chen]
 * https://blog.csdn.net/DUYIJIU/article/details/98366918
 * https://indienova.com/indie-game-development/unity-off-screen-objective-marker/
 */

public class ScreenUIBase : MonoBehaviour
{
    public Transform user;
    [SerializeField] protected Vector3 screenPosition;
    [SerializeField] protected Vector3 screenBound;
    [SerializeField] protected Vector2 arrowDirection;
    [SerializeField] protected RectTransform targetImageArrow;
    private readonly Vector3 _headUIPositionOffset = new(0, 2, 0);
    private UnityEngine.Camera _camera;
    private float _screenBoundOffset;

    public virtual void Start()
    {
        _camera = UnityEngine.Camera.main;
        _screenBoundOffset = GetComponent<RectTransform>().sizeDelta.x / 1.5f;
        MouseHoverEffect();
    }

    public virtual void Pop()
    {
        gameObject.SetActive(true);
    }

    public virtual void Hide()
    {
        Debug.Log("Hide");
        gameObject.SetActive(false);
    }

    public virtual void LateUpdate()
    {
        if (!user) return;
        UpdatePositionOnScreen();
        // ModifyScale();
    }

    private void MouseHoverEffect()
    {
        //add mouse hover event to scale the UI
        EventTrigger eventTrigger = gameObject.AddComponent<EventTrigger>();
        EventTrigger.Entry entry = new EventTrigger.Entry { eventID = EventTriggerType.PointerEnter };
        entry.callback.AddListener((data) => { ModifyScale(true); });
        eventTrigger.triggers.Add(entry);
        entry = new EventTrigger.Entry { eventID = EventTriggerType.PointerExit };
        entry.callback.AddListener((data) => { ModifyScale(false); });
        eventTrigger.triggers.Add(entry);
    }

    private void UpdatePositionOnScreen()
    {
        screenPosition =
            _camera.WorldToScreenPoint(user.position + _headUIPositionOffset);

        (screenBound.x, screenBound.y) = (Screen.width, Screen.height); //get the screen size

        if (screenPosition.z < 0) //if the target is behind the camera, flip the screen position
        {
            screenPosition = -screenPosition;
            //this is to avoid small probability that the target is behind the camera and the screen position is flipped, but the y position is still in the screen
            screenPosition.y = screenPosition.y > screenBound.y / 2 ? screenBound.y : 0;
        }

        //clamp the screen position to the screen bound
        transform.position = new Vector2(
            Mathf.Clamp(screenPosition.x, _screenBoundOffset, screenBound.x - _screenBoundOffset),
            Mathf.Clamp(screenPosition.y, _screenBoundOffset, screenBound.y - _screenBoundOffset));

        //optional, rotate the arrow to point to the target:
        arrowDirection =
            targetImageArrow.transform.position -
            screenPosition; //get the direction of the arrow by subtracting the screen position of the target from the screen position of the arrow
        // Debug.Log(Arrowdirection.magnitude);
        if (Mathf.Abs(arrowDirection.x + arrowDirection.y) < 0.1f)
        {
            //disable the arrow when the target is too close to the center of the screen
            targetImageArrow.gameObject.SetActive(false);
            //TargetImageArrow.transform.up = Vector2.zero;
        }
        else
        {
            targetImageArrow.gameObject.SetActive(true);
            targetImageArrow.transform.up = arrowDirection;
        }
    }

    private void ModifyScale(bool larger)
    {
        // //use DOTWEEN to animate the icon
        //Do Scale to 1.5f or 1 and keep
        transform.DOScale(larger ? Vector3.one * 3f : Vector3.one, 0.2f);
    }
}

实际效果展示,略糊,蓝绿色图标

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐