Skip to main content

ひょこ生まれ!

ゲームマネージャーを編集し、卵が無事に巣まで届いたら、ひょこが生まれ、ランダムの目的地まで歩くようにしましょう。

ランダムに歩く

アイディアとして、ひょこが現在位置からある半径いないのランダム位置を選択する。ただし、その位置がナビゲーションメッシュの中にあること保証できないので、ランダム位置を選択し、そこから最も近いナビゲーションメッシュの位置を求める:

image.pngimage.png

このため、Unity の2つの機能を使用

単位円のランダム位置単位円以内の

 ランダム位置

Vector2 point = Random.onUnitCircle;insideUnitCircle;

・ナビゲーションメッシュ

 に一番近い点を探す

// position        :定めた位置
// hit             :探索の結果
// distance        :探索の最大距離
// NavMesh.AllAreas:探索したいナビゲーションメッシュの種類(AllAreas = すべてのメッシュ)
NavMesh.SamplePosition(position, out var hit, distance, NavMesh.AllAreas);

スクリプト

以下のスクリプトをひょこのプレハブに付けてください:

// ランダムに歩く
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回動くが、着いたら止まるので、次々に新しい位置を設定しましょう。考えとしては「しばらくに動ていなければ、新しい目的地にする」。この作戦で、閉じた扉で動かなくなった場合でも、別の位置に移動ができる。実現するには:

  1. 前回の Update と今の Update の位置の差分を求める
  2. 差分(移動量)が大きければ、まだ動いている → なにもしない
  3. 差分(移動量)が小さければ、止まっている → タイマー開始
  4. タイマーが切れたら → 別の目的地にする
// ランダムに歩く
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();
    }
}