ひょこ生まれ!
ゲームマネージャーを編集し、卵が無事に巣まで届いたら、ひょこが生まれ、ランダムの目的地まで歩くようにしましょう。
ランダムに歩く
アイディアとして、ひょこが現在位置からある半径いないのランダム位置を選択する。ただし、その位置がナビゲーションメッシュの中にあること保証できないので、ランダム位置を選択し、そこから最も近いナビゲーションメッシュの位置を求める:
このため、Unity の2つの機能を使用
|
・単位円以内の ランダム位置 |
|
|
・ナビゲーションメッシュ に一番近い点を探す |
|
スクリプト
以下のスクリプトをひょこのプレハブに付けてください:
// ランダムに歩く
public class RandomWalk : MonoBehaviour
{
// 円の半径
[SerializeField]
private float radius;
// ナビゲーションエージェント
private NavMeshAgent agent;
// 目的地を設定
private void SetDestination()
{
// ランダム位置
Vector2 point = Random.onUnitCircle * radius;
// 3Dにする
Vector3 target = new Vector3(point.x, 0, point.y);
// ナビゲーションメッシュに一番近い点にする
NavMesh.SamplePosition(target, out var hit, 100, NavMesh.AllAreas);
// 目的地にする
agent.SetDestination(hit.position);
}
private void Start()
{
// コンポーネントを取得
agent = GetComponent<NavMeshAgent>();
SetDestination();
}
}
確認のため、ひょこ1羽を手で追加し、動きを見てみましょう
次々の位置を設定
これで1回動くが、着いたら止まるので、次々に新しい位置を設定しましょう。考えとしては「しばらくに動ていなければ、新しい目的地にする」。この作戦で、閉じた扉で動かなくなった場合でも、別の位置に移動ができる。実現するには:
- 前回の Update と今の Update の位置の差分を求める
- 差分(移動量)が大きければ、まだ動いている → なにもしない
- 差分(移動量)が小さければ、止まっている → タイマー開始
- タイマーが切れたら → 別の目的地にする
// ランダムに歩く
public class RandomWalk : MonoBehaviour
{
// 円の半径
[SerializeField]
private float radius;
// ナビゲーションエージェント
private NavMeshAgent agent;
// 最後の位置
private Vector3 lastPosition;
// 止まっている時間
private float stopTimer;
// SetDestination()が変わらない
private void Start()
{
// コンポーネントを取得
agent = GetComponent<NavMeshAgent>();
SetDestination();
// 現在の位置を保管
lastPosition = transform.position;
}
private void Update()
{
// 現在の位置
Vector3 nowPosition = transform.position;
// 差分を計算
float distance = Vector3.Distance(nowPosition, lastPosition);
// 最後の位置を上書きする
lastPosition = nowPosition;
// 少しでも移動していれば、問題ない
if (distance > 0.01f)
{
stopTimer = 0; // タイマーをリセット
return; // 終了
}
// タイマーを更新
stopTimer += Time.deltaTime;
// 0.5秒以上動いていなければ、新しい位置を求める
const float waitTime = 0.5f;
if (stopTimer > waitTime)
{
stopTimer = 0;
SetDestination();
}
}
}
GameManager の更新
テスト用のひょこをシーンから削除し、卵が巣まで行ったら、ひょこが生まれるようにしましょう。
public class GameManager : MonoBehaviour
{
// ひょこのプレハブ
[SerializeField]
private GameObject chickPrefab;
// (省略)
// 卵が巣に届いたときのイベントを受け取る
private void OnEggDetected(GameObject egg)
{
// 廃止イベント登録解除する
EggHP eggHp = egg.GetComponent<EggHP>();
eggHp.Destroyed -= OnEggDestroyed;
// 卵の位置でひょこを生成する
GameObject copy = Instantiate(chickPrefab);
copy.transform.position = egg.transform.position;
// 卵を廃止し、次のを生成
Destroy(egg);
CreateEgg();
}
}
