-
Notifications
You must be signed in to change notification settings - Fork 1
/
AICharacterControl2.cs
256 lines (237 loc) · 10.1 KB
/
AICharacterControl2.cs
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//
// 移動先の指定、目標の位置による動作の決定、視線追従
//
// 参考 NavMesh設定
// Base 0, Speed 3, Angluer 360, Accelaration 16, Stopping 1.0, AutoBreak Y, Radius 0.3, Height 1.2
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class AICharacterControl2 : UdonSharpBehaviour
{
public UnityEngine.AI.NavMeshAgent agent { get; private set; }
public ThirdPersonCharacter2 character { get; private set; }
public Animator animator { get { return GetComponent<Animator>(); } }
// オブジェクトの指定
[SerializeField] public GameObject[] _target = new GameObject[9]; // 目標のオブジェクト(必須)
[SerializeField] public AudioSource _ashioto_sound; // 足音のサウンド(任意)
// 変更できるパラメーター
[SerializeField, Range(0f, 2f)] float _nyaa_high = 2.00f; // じゃれる高さ・上
[SerializeField, Range(0f, 2f)] float _nyaa_mid = 0.98f; // じゃれる高さ・中
[SerializeField, Range(0f, 2f)] float _nyaa_low = 0.37f; // じゃれる高さ・下
[SerializeField, Range(0f, 90f)] float _nyaa_angle = 30f; // じゃれる角度(正面から)
[SerializeField, Range(0f, 2f)] float _nyaa_distance = 0.5f; // じゃれる最大距離(正面)
[SerializeField, Range(0f, 2f)] float _nyaa_hdistance = 0.2f; // じゃれる最大距離(頭付近)
[SerializeField, Range(1,99)] int _delay = 15; // 追従の遅延 1=0.02秒、15=0.3秒
// 目標を追いかけるスピード
[SerializeField, Range(0f, 5f)] float _agent_speed_item = 3.0f; // ねこじゃらしとぬいぐるみ
[SerializeField, Range(0f, 5f)] float _agent_speed_roomba = 2.05f; // ルンバ
// ルンバを追いかける距離
[SerializeField, Range(0f, 2f)] float _roomba_stop = 0.5f; // 止まる距離
[SerializeField, Range(0f, 2f)] float _roomba_run = 1.2f; // 走り始める距離
// 視線追従の設定(0~1.0)
float _ikall = 1.00f;
float _ikall_default = 1.00f;
float _ikbody = 0.36f;
float _ikhead = 0.55f;
float _ikeye = 1.0f;
float _ikmotion = 1.0f;
// 変数
int _selected = 0; // 選択中の目標
int _next_select = -1; // 次に選択する目標
float _next_cooltime = 0f; // 目標切り替え時のクールタイム
Vector3[] _dtargetpos_hists = new Vector3[100]; // 遅延用 目標座標の履歴
Vector3 _dtargetpos; // 遅延後の目標座標
// 初期設定
private void Start()
{
agent = GetComponentInChildren<UnityEngine.AI.NavMeshAgent>();
character = GetComponent<ThirdPersonCharacter2>();
agent.updateRotation = false;
agent.updatePosition = true;
}
// フレームごとの処理
private void Update()
{
bool skip = false;
// 有効な目標に切り替える
if (_next_select == -1)
{
for (var i=0; i<_target.Length; i++)
{
if (_target[i] != null && _target[i].activeSelf)
{
skip = true;
if (_selected != i + 1)
{
_next_select = i + 1;
_next_cooltime = (i == (_target.Length-1)) ? 0.7f : 0.2f; // ルンバのときはクール時間長く
}
break;
}
}
if (_next_select == -1 && !skip) _selected = 0;
}
else
{
// クールタイム
_next_cooltime -= Time.deltaTime;
if (_next_cooltime < 0f)
{
_selected = _next_select;
_next_select = -1;
fill_delay_target_buffer(_target[_selected-1].transform.position);
_ikall = _ikall_default;
}
else
{
_ikall -= Time.deltaTime / 0.15f; // 首が急に戻るのを防止する
if (_ikall < 0f)
{
_ikall = 0f;
_selected = 0;
}
}
}
// 移動先の指定、目標の位置による動作の決定
if (_selected > 0)
{
// 目標をセット
agent.SetDestination(_dtargetpos);
if (agent.remainingDistance > agent.stoppingDistance)
{
// 目標までの距離がある場合
character.Move2(agent.desiredVelocity, 0);
ashioto(true);
}
else
{ // 停止範囲内に入った場合
int nyaa = check_target_near();
character.Move2(Vector3.zero, nyaa);
ashioto(false);
}
}
else
{
// 目標が非アクティブの場合はその場で停止
agent.SetDestination(agent.transform.position);
character.Move2(Vector3.zero, 0);
ashioto(false);
}
// 目標を追いかけるスピードの設定
if (_selected > 0)
{
//debug_remain = agent.remainingDistance;
//debug_stop = agent.stoppingDistance;
if (_selected == 1)
{
// ねこじゃらしの追いかけ速度/距離
agent.speed = _agent_speed_item;
agent.stoppingDistance = 1.0f;
}
else if (_selected == 9)
{
// ルンバに近づいたら止まってしばらく見つめて、遠くに行ったら走り始める処理
agent.speed = _agent_speed_roomba;
if (agent.stoppingDistance == _roomba_stop && agent.remainingDistance <= _roomba_stop + 0.2f)
{
agent.stoppingDistance = _roomba_run; //2.5
}
else if (agent.stoppingDistance == _roomba_run && agent.remainingDistance > _roomba_run - 0.2f)
{
agent.stoppingDistance = _roomba_stop; //0.5
}
else if (agent.stoppingDistance != _roomba_stop && agent.stoppingDistance != _roomba_run)
{
agent.stoppingDistance = _roomba_run; //2.5
}
}
else
{
// ぬいぐるみの追いかけ速度/距離
agent.speed = _agent_speed_item;
agent.stoppingDistance = 0.65f;
}
}
}
// [SerializeField] private float debug_remain;
// [SerializeField] private float debug_stop;
// 0.02秒毎の実行
private void FixedUpdate()
{
delay_target(); // 目標の遅延処理
}
// 目標の遅延処理
private void delay_target()
{
if (_selected > 0 && _target[_selected-1] != null)
{
_dtargetpos = _dtargetpos_hists[0]; // Shift()が使えないのなんで?
for (var i = 1; i < _dtargetpos_hists.Length; i++)
{
_dtargetpos_hists[i - 1] = _dtargetpos_hists[i];
}
_dtargetpos_hists[_delay] = _target[_selected-1].transform.position;
if (_dtargetpos == null) _dtargetpos = _target[_selected-1].transform.position;
}
}
private void fill_delay_target_buffer(Vector3 pos)
{
for (var i = 0; i < _dtargetpos_hists.Length; i++)
{
_dtargetpos_hists[i] = pos;
}
}
// 目標が視界内の一定距離 & 高さにあるか?(0=範囲外 1=高い位置 2=低い位置)
private int check_target_near()
{
int nyaa = 0;
Vector3 eyeDir = this.transform.forward; // プレイヤーの視線ベクトル
Vector3 playerPos = this.transform.position; // プレイヤーの位置
Vector3 targetPos = _target[_selected-1].transform.position; // 目標の位置(遅延なし)
//Vector3 targetPos = _dtargetpos; // 目標の位置(遅延あり)
float height = targetPos.y - playerPos.y; // 目標の高さ
eyeDir.y = 0;
playerPos.y = 0;
targetPos.y = 0;
float angle = Vector3.Angle((targetPos - playerPos).normalized, eyeDir);
float distance = Vector3.Distance(playerPos, targetPos);
//Debug.Log("**** angle=" + angle + " / distance=" + distance + " / height=" + height);
if (distance >= 0.1f && distance < _nyaa_distance && angle < _nyaa_angle) // 正面にある場合
{
if (height > _nyaa_mid && height <= _nyaa_high) nyaa = 1; // 高い位置にある
else if (height > _nyaa_low && height <= _nyaa_mid) nyaa = 2; // 低い位置にある
}
else if (height > _nyaa_mid && height <= _nyaa_high && distance < _nyaa_hdistance) // 頭付近にある場合
{
nyaa = 1;
}
return nyaa;
}
// 足音を鳴らす
private void ashioto(bool newstat)
{
if (_ashioto_sound != null)
{
if (newstat && !_ashioto_sound.isPlaying)
{
_ashioto_sound.pitch = (agent.speed / 3.0f);
_ashioto_sound.Play();
}
else if (!newstat && _ashioto_sound.isPlaying)
{
_ashioto_sound.Stop();
}
}
}
// 視線追従
private void OnAnimatorIK(int layerIndex)
{
if (_selected > 0 && _target[_selected-1] != null && _selected > 0 && agent.remainingDistance < 3.0f) //目標がセットされ距離が3m以内
{
animator.SetLookAtWeight(_ikall, _ikbody, _ikhead, _ikeye, _ikmotion); // 全体,体,頭,目,モーション
//animator.SetLookAtPosition(_target[_selected-1].transform.position); // 遅延なしver
animator.SetLookAtPosition(_dtargetpos); // 遅延ありver
}
}
}