实现类似原神指引目标位置效果

时间:2025-01-25 19:44:06
using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ScreenEdgeTips : MonoBehaviour { /// <summary> 图片父物体,最好是全屏的Panel </summary> private RectTransform directContainer; /// <summary> 玩家自己或者摄像机标志位 </summary> private GameObject _cameraTarget; /// <summary> 目标物体</summary> public GameObject TargetObj; /// <summary> 主摄像机 </summary> public Camera mainCamera; /// <summary> UI摄像机 </summary> public Camera uicamera; private List<Line2D> screenLines; /// <summary> 在画面内的UI </summary> public Sprite InImage; /// <summary> 画面外的UI </summary> public Sprite OutImage; public InOrOut ImageType = InOrOut.None; RectTransform rect; private Image image; Vector2 finalPos; private Vector2 lookPos; public void Init() { _cameraTarget = new GameObject("Target"); _cameraTarget.transform.parent = mainCamera.transform; _cameraTarget.transform.localPosition = new Vector3(0, 0, 0.5f); directContainer = transform.parent.GetComponent<RectTransform>(); rect = GetComponent<RectTransform>(); image = GetComponent<Image>(); finalPos = new Vector2(); } private void Update() { UpdateImp(); UpdateLookAt(); } private void Start() { Init(); InitWidth(0); } private void InitWidth(float width) { Vector3 point1 = new Vector3(width, width); Vector3 point2 = new Vector3(width, Screen.height- width); Vector3 point3 = new Vector3(Screen.width- width, Screen.height- width); // P2------------P3 Vector3 point4 = new Vector3(Screen.width- width, width); // | | this.screenLines = new List<Line2D>(); // | | this.screenLines.Add(new Line2D(point1, point2)); // P1------------ P4 this.screenLines.Add(new Line2D(point2, point3)); this.screenLines.Add(new Line2D(point3, point4)); this.screenLines.Add(new Line2D(point4, point1)); } /// <summary> /// 点是否在屏幕内 /// </summary> /// <param name="pos"></param> /// <returns></returns> private bool PointIsInScreen(Vector3 pos) { if (pos.x <= this.screenLines[0].point1.x || pos.x >= this.screenLines[1].point2.x || pos.y <= this.screenLines[0].point1.y || pos.y >= this.screenLines[1].point2.y) { return false; } return true; } //世界坐标转换为屏幕坐标 private Vector3 WorldToScreenPoint(Vector3 pos) { if (null != this.mainCamera) { return mainCamera.WorldToScreenPoint(pos); } return Vector3.zero; } public void UpdateImp() { if (_cameraTarget != null) { Vector3 fromPos = this.WorldToScreenPoint(_cameraTarget.transform.position); Vector3 toPos = WorldToScreenPoint(TargetObj.transform.position); if (PointIsInScreen(toPos))//如果目标在屏幕内 { if (toPos.z<0) { ChangeImage(InOrOut.Out); CacleIntersce(fromPos); } else { ChangeImage(InOrOut.In); RectTransformUtility.ScreenPointToLocalPointInRectangle(directContainer, toPos, uicamera, out finalPos); rect.anchoredPosition = finalPos;//ui的绝对布局 } } else { ChangeImage(InOrOut.Out); CacleIntersce(fromPos); } } } private void CacleIntersce(Vector3 fromPos) { Vector2 intersecPos = Vector2.zero; Vector3 localpos = _cameraTarget.transform.InverseTransformPoint(TargetObj.transform.position); Vector3 screenpos = new Vector3(localpos.x, localpos.y, 0);//根据场景坐标不同可以调整 lookPos = fromPos + screenpos.normalized * 10000; Line2D line2 = new Line2D(fromPos, lookPos); foreach (Line2D l in this.screenLines) { if (line2.Intersection(l, out intersecPos) == Line2D.CROSS) { break; } } RectTransformUtility.ScreenPointToLocalPointInRectangle(directContainer, intersecPos, uicamera, out finalPos); rect.anchoredPosition = finalPos;//ui的绝对布局 } /// <summary> /// 改变图片 /// </summary> private void ChangeImage(InOrOut type) { if (type == ImageType) { return; } switch (type) { case InOrOut.In: image.sprite = InImage; break; case InOrOut.Out: image.sprite = OutImage; break; case InOrOut.None: break; default: break; } ImageType = type; } private void UpdateLookAt() { if (TargetObj != null) { if (ImageType == InOrOut.Out) { rect.right = lookPos.normalized;//根据图片轴向改变 } } } } public enum InOrOut { In, Out, None }