敵が走っているプレイヤーに気づいて追尾するでは、プレイヤーが走ると注意レベルのゲージが上がって、満タンになると敵がプレイヤーを追尾しはじめます。
走るのをやめるとゲージが下がって0になると追尾をやめますが、敵にプレイヤーが見えていなくて音で判断していると仮定すると、歩いていて足音が大きくなくても追ってくるのはおかしいと思ったので修正してみました。
敵とプレイヤーの距離も考えないようにしています。
動画では、敵に、巡回している・追っている・見失っている の3つの状態を用意して、満タンの1になると追っている状態になり、1未満になると見失っている状態にして、その後プレイヤーを見失った位置まで来るか、ゲージが0.4以下になると巡回に戻るようにしています。
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.AI;
public class AgentScript3 : MonoBehaviour
{
[SerializeField] UnityStandardAssets.Characters.FirstPerson.FirstPersonController sc_fps;
[SerializeField] Transform points;
[SerializeField] Image gauge;
[SerializeField] Text text;
[SerializeField] GameObject spherePrefab;
GameObject sphere;
Transform player;
NavMeshAgent agent;
float attention;
int state;
// Start is called before the first frame update
void Start()
{
player = sc_fps.transform;
agent = GetComponent<NavMeshAgent>();
Vector3 dest = GetDestinationRandomly();
agent.destination = dest;
sphere = Instantiate(spherePrefab, dest, spherePrefab.transform.rotation);
gauge.fillAmount = 0f;
}
// Update is called once per frame
void Update()
{
// プレイヤーが走っていないとき
if (sc_fps.GetIsWalking())
{
text.text = "P:走っていない";
attention -= 0.008f; // 注意のレベルを下げる
}
// プレイヤーが走っているとき
else
{
text.text = "P:走っている";
attention += 0.01f; // 注意のレベルを上げる
}
// 追っていない状態
if (state == 0)
{
//
if (attention <= 0f)
{
attention = 0f;
}
text.text += "\nA:巡回している";
// 1以上のとき追っている状態にする
if (attention >= 1f)
{
attention = 1f;
state = 1;
agent.destination = player.position; // プレイヤーの場所に行く
Destroy(sphere);
sphere = Instantiate(spherePrefab, player.position, spherePrefab.transform.rotation);
}
// 目的地付近で次の目的地を設定
else if (!agent.pathPending && agent.remainingDistance < 0.5f)
{
Vector3 dest = GetDestinationRandomly();
agent.destination = dest;
Destroy(sphere);
sphere = Instantiate(spherePrefab, dest, spherePrefab.transform.rotation);
}
}
// 追っている状態
else if (state == 1)
{
text.text += "\nA:追っている";
agent.destination = player.position; // プレイヤーの場所に行く
Destroy(sphere);
sphere = Instantiate(spherePrefab, player.position, spherePrefab.transform.rotation);
// 1以上のとき1
if (attention >= 1f)
{
attention = 1f;
}
// 見失っている状態にする
else
{
state = 2;
}
}
// 見失っている状態
else if (state == 2)
{
text.text += "\nA:見失っている";
// 1以上のとき追っている状態にする
if (attention >= 1f)
{
attention = 1f;
state = 1;
agent.destination = player.position; // プレイヤーの場所に行く
Destroy(sphere);
sphere = Instantiate(spherePrefab, player.position, spherePrefab.transform.rotation);
}
// 0.4で追ってない状態にする
else if (attention <= 0.4f)
{
Vector3 dest = GetDestinationRandomly();
agent.destination = dest;
Destroy(sphere);
sphere = Instantiate(spherePrefab, dest, spherePrefab.transform.rotation);
state = 0;
}
// 目的地付近で次の目的地
else if (!agent.pathPending && agent.remainingDistance < 0.5f)
{
Vector3 dest = GetDestinationRandomly();
agent.destination = dest;
Destroy(sphere);
sphere = Instantiate(spherePrefab, dest, spherePrefab.transform.rotation);
state = 0;
}
}
gauge.fillAmount = attention; // ゲージに反映
}
// ランダムで目的地を返す
Vector3 GetDestinationRandomly()
{
return points.GetChild(Random.Range(0, points.childCount)).transform.position;
}
}
まず前の記事と同様にFirstPersonControllerに、歩いているかどうかを表すbool変数のGetメソッドを作って、それを使ってプレイヤーが走っているか調べて、注意のレベルを上げ下げします。
// プレイヤーが走っていないとき
if (sc_fps.GetIsWalking())
{
text.text = "P:走っていない";
attention -= 0.008f; // 注意のレベルを下げる
}
// プレイヤーが走っているとき
else
{
text.text = "P:走っている";
attention += 0.01f; // 注意のレベルを上げる
}
その後は、敵の状態によって条件分岐して、その中で、追っているときはプレイヤーの位置を目的地に入れ続けるとか、注意のレベルが0~1の間に収まるようにするなどの処理をしたり、条件によって他の状態に遷移させたりしているだけです。
目的地には緑色のSphereを配置しています。生成するときに前のを破壊するので、常に1つしか見えません。
巡回しているときは、目的地に来ると次の目的地をランダムで設定します。
また、プレイヤーが走ってゲージが1以上になると巡回をやめて追っている状態になり、目的地がプレイヤーの位置になります。
追っている状態のときは、毎フレーム目的地をプレイヤーの位置に更新しつづけます。
プレイヤーが走るのをやめると、目的地の更新をやめるので、緑のSphereがプレイヤーについてこなくなり、敵はプレイヤーを見失っている状態になります。
敵はとりあえず見失った位置に向かいますが、ゲージが0.4以下になると、あきらめて巡回に戻ります。
見失ってすぐにそこへ敵が到達すると、ゲージが半分以上残っていても巡回に戻るようにしました。
次はプレイヤーが敵に見えているかどうかや、敵とプレイヤーの距離もゲージに反映させてみます。