プレイヤーはこちらの思った通りに行動してくれるとは限らない。
どこかの動画で、そんなフレーズを聞いた覚えがあったけど、このゲームでもそれは当てはまると思う。
ジェムを出すまで敵を倒すってのは、こちらが想定している動きで、必ずしもプレイヤーがそう行動してくれるわけではない。
ジェムを出せたら出せたで、同じ敵ばかりを延々を倒すかもしれない。
かといって、それをシステム的に説明するのでは味気ないし🧐
それなら、自然にそれを説明してくれる存在がいれば良い。ってわけで案内役を作成することにした。
案内役はプレイヤーに追従して動いて欲しい。川があるから飛んでるキャラが良いなと、AssetStoreでアセットを見ていたら、妖精キャラが無料であったので早速採用。
assetstore.unity.com ・・・採用したんだけど、移動モーションが良い感じではなかったので、残念ながら変更。
assetstore.unity.com
idle状態のモーションが移動させていてもしっくりくるので本採用。
案内役、イーグル君です。
イーグル君の追従だけど、プレイヤーの子にしてしまうと、あまりにピッタリと追従してしまって不自然なので、自主的にプレイヤーを追いかける様にしたい。
追従する動きについてググったら、ぴったりの記事が😆
gomafrontier.com イーグル君にアタッチするスクリプト TalkManagerの中の追従機能のメソッドとして、紹介されていたスクリプト をちょこっと改造。
基本的構造は変えていないけれど、一番最初でfollow Target、つまりプレイヤーとイーグル君の距離を測定して、
距離がある程度(0.2以下)近づいたら移動を止めて方向変換 だけにした。
これで追従してくれる様になったが、敵の中には遠距離攻撃してくる奴もいるし、あまりにプレイヤーに近いところをうろうろしていると
攻撃モーションが被ってしまう。
イーグル君には攻撃が当たらないようにColliderを設定するにしても、モーションを避けさせるのは面倒なので、
戦闘中にはイーグル君には離れてもらった方が都合が良い。
じゃあ、戦闘中ってのをどうやって判定するか。
複数の敵が同時にプレイヤーと戦闘中になることも考えると、単純なBoolではうまくいかなさそう。
色々と考えていて、ふとロックオン機能で使ったListに思い至った。
敵がプレイヤーをTargetにしたら専用のListに敵自身を登録する様にして、
そのListのCountが1以上になったらイーグル君に退避行動を取らせればうまくいきそう。
自動的にListに登録する方法は、ロックオン機能で学んだので、その応用でいけるだろう。
VIDEO www.youtube.com List、敵意のListということでHostilityListと、それを管理するManagerを作成。
敵Objectの子にあるStatusManagerがプレイヤーをTargetする機能を持っているので、そこに
hostilityListManager.HostilityList.Add(Main.gameObject);
を追加して、Listに敵自身を追加させる。
敵がプレイヤーを見失ったらListから敵自身を抜くのも忘れないように
Main.Target= null ;
for (int i = 0 ; i < hostilityListManager.HostilityList.Count; i++ )
{
if (hostilityListManager.HostilityList[i]== Main.gameObject)
{
hostilityListManager.HostilityList.RemoveAt(i);
}
}
そして、イーグル君のスクリプト Talk Managerで
hostilityListManager.HostilityList.Countが0でない時には退避行動を取らせる様にして、さらに追従行動も取らないようにしてやれば良い。
完成形の追従メソッドはこんな感じ。
void Update()
{
if (hostilityListManager.HostilityList.Count!= 0 )
{
talkanimator.SetBool("Leave" ,true );
}
DoAutoMovement();
}
protected void DoAutoMovement()
{
if (hostilityListManager.HostilityList.Count== 0 )
{
talkanimator.SetBool("Leave" ,false );
float BirdDistance= Vector3.Distance(followTarget.position,transform.position);
Quaternion move_rotation = Quaternion.LookRotation(followTarget.transform.position - transform.position, Vector3.up);
transform.rotation = Quaternion.Lerp(transform.rotation, move_rotation, 0.1f );
if (BirdDistance>0.2f )
{
float movingspeed= move_speed* BirdDistance;
movingspeed = System.Math.Min(movingspeed, move_speed);
rb.velocity = transform.forward * move_speed* BirdDistance;
}
else
{
transform.rotation= followTarget.rotation;
rb.velocity= Vector3.zero;
}
}
}
追従機能はできたので、肝心の案内。喋る機能の作成をしよう。
まずはイーグル君の発言を表示するウィンドウを作成する。
ヴァーチャルコントローラーがあるせいで、ちょっと左ずれだけどまぁいいだろう。
色々な記事や動画を見ていると、セリフは大抵の場合はScriptableObjectを作って、そこに書く様なので、
僕もそうすることにした。
Next
Talk Noが0でなければ続きのセリフがある様にしている。
発言用のメソッドはこんな感じ。
public void Talking(int talkno)
{
talkwindow.SetActive(true );
foreach (TalkData i in TalkSO.TalkEntity.talkDataList)
{
if (i.talkNo != talkno) continue ;
talkText.text= i.talkText;
_nextTalkNo= i.nextTalkno;
}
StartCoroutine(FadeIn(2.5f ,_nextTalkNo));
}
FadeInの説明だけど、どうせなら発言は一気に出るのではなく、一文字ずつ表示する様にさせたいので、
logicalbeat.jp
この記事からFadeInやSetTextAlphaを使わせてもらった。
ついでに、ここにnextTalknoの判定と次のセリフ表示の機能を、ちょこっと追加。
private IEnumerator FadeIn(float waitsec,int nextno)
{
talkText.ForceMeshUpdate(true );
TMP_TextInfo textInfo = talkText.textInfo;
TMP_CharacterInfo[] charInfos = textInfo.characterInfo;
for (var i = 0 ; i < charInfos.Length; i++ )
{
SetTextAlpha(talkText, i, 0 );
}
for (var i = 0 ; i < charInfos.Length; i++ )
{
if (char .IsWhiteSpace(charInfos[i].character)) continue ;
yield return new WaitForSeconds(0.05f );
player.TalkSE();
SetTextAlpha(talkText, i, 255 );
}
yield return new WaitForSeconds(waitsec);
talkText.text= "" ;
talkwindow.SetActive(false );
if (nextno!= 0 )
{
yield return new WaitUntil(()=> (talkwindow.activeInHierarchy!= true ));
yield return new WaitForSeconds(0.5f );
Talking(nextno);
}
}
これで追従と発言機能が完成したので、案内させたい場所で、各種のスクリプト 達からこのTalkingメソッドを呼んでやれば良いだろう。
完成形はこんなイメージ。
VIDEO youtu.be