放置NPC

我们先在SceneMainCity放上NPC的prefab,然后在NPC的前方放上定位点,这个定位点就是Player导航的时候和NPC对话的站位。

然后在场景中放一个MapRoot对象,在上面挂载MainCityMap脚本,并打上“MapRoot”标签,这个脚本用来记录各个定位点的位置。

NPC放置

1
2
3
4
5
6
7
8
9
using UnityEngine;

namespace DarknessWarGodLearning
{
public class MainCityMap : MonoBehaviour
{
public Transform[] NpcPosTrans;//在Inspector上指定各个定位点
}
}

读取MapRoot内的数据

MainCitySys中获取进入主城的位置信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private Transform[] npcPosTrans;

public void EnterMainCity()
{
//...
resSvc.AsyncLoadScene(mapData.sceneName, () =>
{
//...

GameObject map = GameObject.FindGameObjectWithTag("MapRoot");
MainCityMap mainCityMap = map.GetComponent<MainCityMap>();
npcPosTrans = mainCityMap.NpcPosTrans;

//...

});
}

导航网格的设置

Unity的Navigation完全是依靠标记为static的物体的Mesh Renderer内部的Mesh生成的,在烘焙导航网格时,我们需要先关闭整个场景的物体,然后再打开场景的碰撞盒,并且打开碰撞盒的Mesh,用碰撞盒的Mesh来烘焙导航网格。

烘焙好后,我们给Player的Prefab添加上Nav Mesh Agent组件,并将Angular Speed设为360。关闭掉Nav Mesh Agent组件,我们希望在点击Guide按钮的时候再开启Nav Mesh Agent

MainCitySys中拿到Nav Mesh Agent组件:

1
2
3
4
5
6
7
8
9
10
private NavMeshAgent nav;

private void LoadPlayer(MapCfg mapData)
{
GameObject player = resSvc.LoadPrefab(PathDefine.AssassinCityPlayerPrefab,true);
//...

nav = player.GetComponent<NavMeshAgent>();
}

进行任务导航,在MainCitySys里面添加RunTask方法

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
private AutoGuideCfg curTaskData;
private bool isNaviguiding = false;

private bool isNaviguiding = false;

public void SetMoveDir(Vector2 dir)//转动摇杆时停止导航
{
StopNavTask();
//...
}

public void OpenInfoWnd()//打开角色面板时停止导航
{
//...
StopNavTask();
}

public void RunTask(AutoGuideCfg autoGuideCfg)//在MainCityWnd调用
{
if(autoGuideCfg != null)
{
curTaskData = autoGuideCfg;
}
//解析任务数据
nav.enabled = true;//防止Player在和NPC过近时开启导航找不到nav;
if (curTaskData.npcID != -1)
{
float dis = Vector3.Distance(playerController.transform.position, npcPosTrans[autoGuideCfg.npcID].position);
if(dis < 0.5f)
{
isNaviguiding = false;
nav.isStopped = true;
playerController.SetAnimTreeBlend(Constants.BlendIdle);
nav.enabled = false;

OpenGuideWnd();
}
else
{
isNaviguiding=true;
nav.enabled = true;
nav.speed = Constants.PlayerMoveSpeed;
nav.SetDestination(npcPosTrans[autoGuideCfg.npcID].position);
//nav.stoppingDistance = 0.5f;
playerController.SetAnimTreeBlend(Constants.BlendWalk);
}
}
else
{
OpenGuideWnd();
}
}
private void StopNavTask()
{
if (isNaviguiding)
{
isNaviguiding = false;
nav.isStopped = true;
playerController.SetAnimTreeBlend(Constants.BlendIdle);
nav.enabled = false;
}
}
private void IsArrivedNavPos()//在Update里面检测距离
{
float dis = Vector3.Distance(playerController.transform.position, npcPosTrans[curTaskData.npcID].position);
if (dis < 0.5f)
{
isNaviguiding = false;
nav.isStopped = true;
playerController.SetAnimTreeBlend(Constants.BlendIdle);
nav.enabled = false;

OpenGuideWnd();
}
}
private void OpenGuideWnd()
{
//To Do
}
private void Update()
{
if (isNaviguiding)
{
playerController.SetCam();
IsArrivedNavPos();
}
}