Unity 在屏幕边界显示追踪目标提示 c#
实现功能:物体A在屏幕内,物体B在屏幕外,在屏幕四周边框上显示物体B相对于A的方向提示
-
思路
1、将物体A、B的世界坐标变为屏幕坐标
2、检测屏幕坐标AB连线与屏幕四条边是否有交点
3、将屏幕交点坐标转化为UI坐标
4、将UI坐标赋给图标 -
将【两点--------->线段】,判断两线的关系并求交点
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Line2D {
//共线
public const int COLINE = 0;
//相交
public const int CROSS = 1;
//平行线
public const int PARALLEL = 2;
//无相交
public const int NOT_CROSS = 3;
private double EPS = 1e-4;
public Vector3 point1;
public Vector3 point2;
private float A;
private float B;
private float C;
public Line2D(Vector3 point1, Vector3 point2)
{
this.point1 = point1;
this.point2 = point2;
this.calcCoefficient();
}
//计算两点组成的直线的系数(Ax+By+C=0)
private void calcCoefficient()
{
this.A = this.point2.y - this.point1.y;
this.B = this.point1.x - this.point2.x;
this.C = this.point2.x * this.point1.y - this.point1.x * this.point2.y;
}
//检查是否交叉(当前线的两点,其他线的两点)
private bool checkCross(Vector3 sp1, Vector3 ep1, Vector3 sp2, Vector3 ep2)
{
if (Mathf.Max(sp1.x, ep1.x) < Mathf.Min(sp2.x, ep2.x))
{
return false;
}
if (Mathf.Min(sp1.x, ep1.x) > Mathf.Max(sp2.x, ep2.x))
{
return false;
}
if (Mathf.Max(sp1.y, ep1.y) < Mathf.Min(sp2.y, ep2.y))
{
return false;
}
if (Mathf.Min(sp1.y, ep1.y) > Mathf.Max(sp2.y, ep2.y))
{
return false;
}
Vector3 vectorA = sp1 - sp2;
Vector3 vectorB = ep2 - sp2;
Vector3 vectorC = ep2 - sp2;
Vector3 vectorD = ep1 - sp2;
double temp1 = (vectorA.x * vectorB.y - vectorA.y * vectorB.x) * (vectorC.x * vectorD.y - vectorC.y * vectorD.x);
vectorA = sp2 - sp1;
vectorB = ep1 - sp1;
vectorC = ep1 - sp1;
vectorD = ep2 - sp1;
double temp2 = (vectorA.x * vectorB.y - vectorA.y * vectorB.x) * (vectorC.x * vectorD.y - vectorC.y * vectorD.x);
if ((temp1 >= 0) && (temp2 >= 0))
{
return true;
}
return false;
}
//
private bool isDoubleEqualZero(float data)
{
if (Mathf.Abs(data) <= EPS)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 交点计算
/// </summary>
/// <param name="otherLine">其他要比较的线</param>
/// <param name="intersectantPoint">交点</param>
/// <returns>两条线的情况</returns>
public int Intersection(Line2D otherLine, out Vector2 intersectantPoint)
{
intersectantPoint = Vector2.zero;
//检查是否相交
if (!checkCross(this.point1, this.point2, otherLine.point1, otherLine.point2))
{
return Line2D.NOT_CROSS;
}
//检查是否平行
if (isDoubleEqualZero(this.A * otherLine.B - this.B * otherLine.A))
{
if (isDoubleEqualZero((this.A + this.B) * otherLine.C - (otherLine.A + otherLine.B) * this.C))
{
return Line2D.COLINE;
}
else
{
return Line2D.PARALLEL;
}
}
else
{
// C1*B2-B1*C2 A1*C2-C1*A2
// X=------------- Y=--------------
// B1*A2-A1*B2 B1*A2-A1*B2
intersectantPoint.x = (otherLine.B * this.C - this.B * otherLine.C) / (otherLine.A * this.B - this.A * otherLine.B);
intersectantPoint.y = (this.A * otherLine.C - otherLine.A * this.C) / (otherLine.A * this.B - this.A * otherLine.B);
return Line2D.CROSS;
}
}
}
- 将下面的代码绑定在指示作用的UI图片上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class ScreenEdgeTips : MonoBehaviour {
//UI
public Text distanceLabel;
//父对象的rect
public RectTransform directContainer;
//本图片标签的宽度
public float prefabWidth;
//物体A
public GameObject fromHero;
//物体B
public GameObject toHero;
//cameras
private Camera mainCamera;
public Camera uicamera;
//param
private List<Line2D> screenLines;
private void Update()
{
UpdateImp();
}
private void Start()
{
float offsetWidth = prefabWidth / 2; //偏差
float originalPoint = 0 + offsetWidth; //初始点+偏差
float correctionWidth = Screen.width - offsetWidth;//修正的屏幕宽度
float correctionHeight = Screen.height - offsetWidth;//修正的屏幕高度
Vector3 point1 = new Vector3(offsetWidth, offsetWidth, 0); // P2------------P3
Vector3 point2 = new Vector3(offsetWidth, correctionHeight, 0); // | |
Vector3 point3 = new Vector3(correctionWidth, correctionHeight, 0); // | |
Vector3 point4 = new Vector3(correctionWidth, offsetWidth, 0); // P1------------ P4
this.screenLines = new List<Line2D>();
this.screenLines.Add(new Line2D(point1, point2));
this.screenLines.Add(new Line2D(point2, point3));
this.screenLines.Add(new Line2D(point3, point4));
this.screenLines.Add(new Line2D(point4, point1));
this.mainCamera = Camera.main;
}
//点是否在屏幕内
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 Vector2 WorldToScreenPoint(Vector3 pos)
{
if (null != this.mainCamera)
{
return mainCamera.WorldToScreenPoint(pos);
}
return Vector2.zero;
}
public void UpdateImp()
{
bool isIntersce = false;
if (fromHero != null)
{
Vector2 intersecPos = new Vector2();
Vector2 fromPos = this.WorldToScreenPoint(fromHero.transform.position);
Vector2 toPos = Vector2.zero;
//Debug.Log("fromPos的屏幕坐标是=" + fromPos);
if (toHero != null)
{
toPos = this.WorldToScreenPoint(toHero.transform.position);
//Debug.Log("toPos的屏幕坐标是=" + toPos);
}
//物体B存在且物体A在屏幕内
if (toPos != Vector2.zero && this.PointIsInScreen(fromPos))
{
Line2D line = new Line2D(fromPos, toPos);
foreach (Line2D l in this.screenLines)
{
if (line.Intersection(l, out intersecPos) == Line2D.CROSS)
{
isIntersce = true;
Debug.Log("有交点 ");
break;
}
}
//有交点
if (isIntersce)
{
Vector2 finalPos = new Vector2();
//将屏幕上的交点换成UI上的交点
RectTransformUtility.ScreenPointToLocalPointInRectangle(directContainer, intersecPos, uicamera, out finalPos);
this.GetComponent<RectTransform>().anchoredPosition = finalPos;//ui的绝对布局
}
//无交点,即物体B在屏幕内,则标签位置位于物体B的上端
else {
if (this.PointIsInScreen(toPos)) {
Vector2 finalPos = new Vector2();
RectTransformUtility.ScreenPointToLocalPointInRectangle(directContainer, toPos, uicamera, out finalPos);
this.GetComponent<RectTransform>().anchoredPosition = finalPos+new Vector2(0,50);//ui的绝对布局
}
}
if (this.distanceLabel != null && toHero != null)
{
this.distanceLabel.text = String.Format("{0}米", Mathf.Round((toHero.transform.position - fromHero.transform.position).magnitude));
}
}
}
}
}
-
物体B在屏幕内的情况
-
物体B在屏幕外的情况