戦闘【UnityでRTSを作る 5】
前回の記事では陣営を実装しました。
今回は陣営が分かれたNPCに戦闘を行わせたかったので戦闘を実装します。
攻撃目標を見つける
Linqを利用して攻撃目標を見つける関数を用意しました。
最も近い敵を見つけられます。
関数を呼ぶたびにFindObjectsOfTypeは避けたかったので、
シーンのCubeKunはリストに入れます。
CubeKun.cs
public static List<CubeKun> list = new List<CubeKun>(); protected override void OnEnable () { base.OnEnable(); list.Add(this); } protected new void OnDisable () { base.OnDisable(); list.Remove(this); } public CubeKun GetTarget () { return list.Where(c => c != this && Team != c.Team). OrderBy(c => Vector3.Distance(GetFeetPosition(),c.GetFeetPosition())). FirstOrDefault(); }
そしてCubeKunにTargetという抽象プロパティを追加します。
CubeKun.cs
public abstract CubeKun Target { get; set; }
Playerには自動プロパティで実装しました。
Updateで攻撃目標が存在しない場合に攻撃目標を探します。
Update関数をオーバーライドしていますが、これは後述のエイミングの実装によるものです。
ちなみに、プレイヤーの移動を実装した記事から移動方法が変化しています。
Player.cs
public override CubeKun Target { get; set; } private RaycastHit hit; protected override void Update () { base.Update(); if (!Target) Target = GetTarget(); canMove = Input.GetMouseButton(0); canSearch = canMove; if (canMove && Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hit)) { Pointer.position = hit.point; MoveToPointerPosition(); } }
NPCにはTargetのSetterにtarget(経路探索の移動目標)を共に更新する処理を実装しています。
プレイヤーはポインターの方へ移動しないといけませんが、
NPCは攻撃目標に向かえばいいからです。
NPC.cs
public CubeKun Target { get { return _Target; } set { _Target = value; target = value ? value.Tr : null; } } private CubeKun _Target = null; protected override void Update () { base.Update(); if (target) { MoveTo(target.position); } else { Target = GetTarget(); } }
エイミング
この記事では腕を回転させることで狙いを定めさせます。
そのため、最初に腕の参照をしなければいけません。
腕の参照
これが標準的なキューブ君の階層です。
腕の名前が「Arm_L」と「Arm_R」です。
左腕には「MachineGun」という名の武器を持たせています。
CubeKunに腕のTransformの参照を持たせる配列を用意します。
CubeKun.cs
public Transform[] Arms { get; private set; }
さらにAwake関数に腕を見つける処理を追加します。
Arms = new Transform[] { Tr.FindChild("Arm_L"), Tr.FindChild("Arm_R") };
腕を回転させる
Targetの方向に腕を回転させるためにUpdate関数を用意します。
CubKun.cs
public float armSpeed = 10; protected virtual new void Update () { if (Target) { for (int i = 0;Arms.Length > i;i++) { var dir = Target.Tr.position - Arms[i].position; dir.y = 0; var newDir = Vector3.RotateTowards( Arms[i].forward, dir, armSpeed * Time.deltaTime, 0 ); Arms[i].rotation = Quaternion.LookRotation(newDir); } } else { for (int i = 0;Arms.Length > i;i++) { Arms[i].localRotation = Quaternion.Euler(Vector3.zero); } } }
armSpeedが腕の回転速度を決めます。
攻撃目標が存在する場合は、狙いを定め、存在しない場合はデフォルトの値に戻します。
完成イメージ
余談ですが弾がやたらと見えづらかったのでTrailRendererを追加しました。
腕の回転でエイミング実装した pic.twitter.com/LdhTvAUYtY
— Macky (@macky_soft) 2017年4月19日