Unity3D开发(四)

Unity3d实现目标追踪


welcome

前言

在合金装备5 幻痛中玩家可以自己定义目标地点,标记会显示目标与玩家的大致方向和距离


合金装备5 幻痛中的目标追踪

Unity实现

使用两个脚本来实现目标追踪 CreatMark.cs 挂载在想要追踪的目标身上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreatMark : MonoBehaviour {
//自己定义一个UGUI的image并在其中添加text文本用来显示距离
public GameObject mark;
//确保场景中存在UGUI的根节点
private GameObject canvans;
// Use this for initialization
void Start () {
canvans = GameObject.Find("Canvas");
GameObject g = Instantiate(mark);
//sendmessage传递当前脚本的载体的Transform
g.SendMessage("getTarget",transform);
g.transform.SetParent(canvans.transform);
}
}

CArrowLookAt.cs用来控制目标追踪UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CArrowLockAt : MonoBehaviour
{
public Transform target; //目标
public Transform self; //自己
public Text text;
public float direction; //箭头旋转的方向,或者说角度,只有正的值
public Vector3 u; //叉乘结果,用于判断上述角度是正是负
float devValue = 10f; //离屏边缘距离
float showWidth; //由devValue计算出从中心到边缘的距离(宽和高)
float showHeight;
// 初始化
void Start()
{
//获取标记上的文本组件
text = gameObject.GetComponentInChildren<Text>();
// 获取主摄像机的Transform
self = GameObject.Find("PlayerCamera").GetComponent<Transform>();
showWidth = transform.parent.GetComponent<RectTransform>().rect.width / 2 - devValue;
showHeight = transform.parent.GetComponent<RectTransform>().rect.height / 2 - devValue;
}
void Update()
{
if (target)
{
//更新距离
if (Time.frameCount % 10 == 0)
if (target && self)
{
text.text = (int)Vector3.Distance(target.transform.position, self.transform.position) + "m";
}
// 每100帧都重新计算一次
if (Time.frameCount % 100 == 0)
{
showWidth = transform.parent.GetComponent<RectTransform>().rect.width / 2 - devValue;
showHeight = transform.parent.GetComponent<RectTransform>().rect.height / 2 - devValue;
}
// 计算向量和角度
Vector3 forVec = self.forward; //计算本物体的前向量
Vector3 angVec = (target.position - self.position).normalized; //本物体和目标物体之间的单位向量
Vector3 targetVec = Vector3.ProjectOnPlane(angVec - forVec, forVec).normalized; //这步很重要,将上述两个向量计算出一个代表方向的向量后投射到本身的xy平面
Vector3 originVec = self.up;
direction = Vector3.Dot(originVec, targetVec); //再跟y轴正方向做点积和叉积,就求出了箭头需要旋转的角度和角度的正负
u = Vector3.Cross(originVec, targetVec);
direction = Mathf.Acos(direction) * Mathf.Rad2Deg; //转换为角度
u = self.InverseTransformDirection(u); //叉积结果转换为本物体坐标
// 给与旋转值
//transform.rotation = originRot * Quaternion.Euler(new Vector3(0f, 0f, direction * (u.z > 0 ? 1 : -1)));
// 计算当前物体在屏幕上的位置
Vector2 screenPos = Camera.main.WorldToScreenPoint(target.position);
//if(Vector3.Dot(forVec, angVec) < 0)
// 不在屏幕内的情况
if (screenPos.x < devValue || screenPos.x > Screen.width - devValue || screenPos.y < devValue || screenPos.y > Screen.height - devValue || Vector3.Dot(forVec, angVec) < 0)
{
Vector3 result = Vector3.zero;
if (direction == 0) //特殊角度0和180直接赋值,大家知道tan90会出现什么结果
{
result.y = showHeight;
}
else if (direction == 180)
{
result.y = -showHeight;
}
else //非特殊角
{
// 转换角度
float direction_new = 90 - direction;
float k = Mathf.Tan(Mathf.Deg2Rad * direction_new);
// 矩形
result.x = showHeight / k;
if ((result.x > (-showWidth)) && (result.x < showWidth)) // 角度在上下底边的情况
{
result.y = showHeight;
if (direction > 90)
{
result.y = -showHeight;
result.x = result.x * -1;
}
}
else // 角度在左右底边的情况
{
result.y = showWidth * k;
if ((result.y > -showHeight) && result.y < showHeight)
{
result.x = result.y / k;
}
}
if (u.z > 0)
result.x = -result.x;
}
transform.localPosition = result;
}
else // 在屏幕内的情况
{
transform.position = screenPos;
}
}
else
{
//标记被清除时销毁自身
Destroy(gameObject);
}
}
//获取目标的Transform组件 创建该GameObject时在 CreatMark 中调用
public void getTarget(Transform t)
{
target = t;
Vector3 t2 = new Vector3(0,0,0);
t2 = t.position;
//对于新增的标记将y轴坐标增大到玩家视线平面
if (t2.y <= 0)
t2.y += 33;
target.position = t2;
}
}

实现效果:


最终效果

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 前言
  2. 2. Unity实现