Unity: Trigger, Collider, UI & NPC Interaction

Unity: Trigger, Collider, UI & NPC Interaction

触发器与碰撞器 Trigger & Collider:

Unity中的物体可以根据双方的触发器与碰撞器产生交互。
触发器与碰撞器有三种不同的状态,Enter,Stay与Exit。

1
2
3
4
5
6
7
8
//触发器事件, Tigger
private void OnTriggerEnter(Collider other)
private void OnTriggerStay(Collider other)
private void OnTriggerExit(Collider other)
//碰撞器事件, Collision
private void OnCollisionEnter(Collision collision)
private void OnCollisionStay(Collision collision)
private void OnCollisionExit(Collision collision)

不同的移动方法与触发器和碰撞器产生的交互也不相同:

Math Function:

  • 遇到目标即使是碰撞器,依然穿透
  • 不管自身是否是碰撞器或者触发器,当它接触到目标的时候(碰撞器/触发器),此时交互没有反应

Character Controller:

  • 碰撞器:有阻挡效果,没有办法接收碰撞器Collision事件
  • 触发器:可穿透,可以接收Tigger事件

Rigidbody:

  • 刚体自身的Collider如果是碰撞器,则只响应碰撞器事件。
  • 刚体自身的Collider如果是触发器,则同时响应碰撞器和触发器事件。

角色交互 NPC Interaction:

实现角色之间的交互,当玩家接近NPC时,NPC会面向玩家。当玩家远离NPC时,NPC会回归自己的朝向。

当NPC的触发器与玩家接触时(Enter),调用OnTriggerEnter(),修改枚举状态。
计算NPC到玩家的方向向量,将NPC旋转指向玩家。

当NPC的触发器与玩家分离时(Exit), 调用OnTriggerExit(),修改枚举状态。
将NPC的方向恢复到原有的方向向量。

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NPC : MonoBehaviour
{
public enum emTriggerType
{
None,
Enter,
Exit,
}
private Transform m_tfPlayer;
private emTriggerType m_emTriggerType;
private float m_fRotateSpeed;
private float m_fTimer;

private Quaternion m_srcRotation;

// Start is called before the first frame update
void Start()
{
m_tfPlayer = GameObject.FindObjectOfType<Player>().transform;
m_emTriggerType = emTriggerType.None;
m_fRotateSpeed = 3f;
m_fTimer = 0f;
m_srcRotation = transform.rotation;

SphereCollider trigger = transform.gameObject.AddComponent<SphereCollider>();
trigger.isTrigger = true;
trigger.radius = 10f;
}

// Update is called once per frame
void Update()
{
if(m_emTriggerType == emTriggerType.Enter)
{
//玩家位置减去NPC位置,得到非单位化的向量dir,几何意义是NPC指向玩家位置的方向向量
Vector3 dir = m_tfPlayer.transform.position - transform.position;
Quaternion targetQ = Quaternion.LookRotation(dir, Vector3.up);
transform.rotation = Quaternion.Lerp(transform.rotation, targetQ, Time.deltaTime * m_fRotateSpeed);

m_fTimer += Time.deltaTime; //计时器

if (m_fTimer >= 1f) {
m_emTriggerType = emTriggerType.None; //置空占位
m_fTimer = 0;
}
}
else if(m_emTriggerType == emTriggerType.Exit)
{
transform.rotation = Quaternion.Lerp(transform.rotation, m_srcRotation, Time.deltaTime * m_fRotateSpeed);
m_fTimer += Time.deltaTime;

if (m_fTimer >= 1f)
{
m_emTriggerType = emTriggerType.None;
m_fTimer = 0;
}
}
}

private void OnTriggerEnter(Collider other)
{
if (other.transform != m_tfPlayer) return;
m_emTriggerType = emTriggerType.Enter;
//Debug.Log("Trigger enter");

//弹出对话
}

private void OnTriggerExit(Collider other)
{
if (other.transform != m_tfPlayer) return;
m_emTriggerType = emTriggerType.Exit;
//Debug.Log("Trigger exit");

//关闭对话
}
}

UI元素添加 UI Component:

添加UI组件:

调用Resource下的相对路径文件。
导入UnityEngine.UI包。

将UI对象实例化,并挂载到附节点上。
从Resource中获得Text,Image等UI对象。

根据场景决定显示NPC名字还是HP血条。

在进行显示时,需要将NPC和玩家的世界坐标系位置转化到屏幕坐标系,将名字显示在屏幕。

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //UI的命名空间

public class Unity_Entity : MonoBehaviour
{
private GameObject m_objModel;

private Text m_txtCharacterName;
private GameObject m_objHPBar;
private Image m_imgHP;

public Vector3 mv3Offset;
// Start is called before the first frame update
void Start()
{
GameObject uiUnit = Resources.Load<GameObject>("Prefabs/UI/Unit_Entity"); //获取UI单元
uiUnit.SetActive(false);

mv3Offset = new Vector3(0f, 2f, 0f);

m_objModel = Instantiate<GameObject>(uiUnit); //实例化对象
m_objModel.transform.parent = GameObject.Find("UnitEntityRoot").transform; //将对象添加到附节点

m_txtCharacterName = m_objModel.transform.Find("Context/txtCharacterName").GetComponent<Text>(); //获取名称
m_objHPBar = m_objModel.transform.Find("Context/HPBar").gameObject;
m_imgHP = m_objModel.transform.Find("Context/HPBar/hp").GetComponent<Image>(); //获取血条图片资源

m_txtCharacterName.text = this.gameObject.name;
m_objHPBar.SetActive(!SceneManager.Instance.isMainScene); //不在主场景时才激活血条
}

// Update is called once per frame
void Update()
{
m_objModel.transform.position = Camera.main.WorldToScreenPoint(this.transform.position + mv3Offset);
//从世界坐标系转换为屏幕坐标系,将血条显示在屏幕上
}
}

为了判断我们的场景,我们需要创建一个SceneManager并挂载到Manager上。
当当前场景是主场景时返回true。

场景判断:

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneManager : MonoBehaviour
{
public static SceneManager Instance;
public bool isMainScene
{
get //get和set可以封装public的field(Member Variable)
{
UnityEngine.SceneManagement.Scene curScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
//由于Unity已经有ScenceManager类,因此需要额外注明命名空间
return curScene.name == "MainScene";
}
}
// Start is called before the first frame update
void Awake() //Awake的执行顺序优先于Start()
{
Instance = this;
}

// Update is called once per frame
void Update()
{

}
}
Author

Xander

Posted on

2022-05-14

Updated on

2022-05-13

Licensed under

Comments