ヒキニートがゲームを作るブログ

Unityでゲームを作る過程を投稿します

NPCの移動と武器【UnityでRTSを作る 3】

今日はNPCの移動及び武器を実装しました。

モデル

まず最初に、NPCにプレイヤーと同じモデルを使うとややこしいので、
区別するためにNPCのモデルを作りました。
f:id:MackySoft:20170415164121j:plain

NPCの移動処理

これは前回の記事で紹介したCubeKunクラスを継承させてMoveTo(移動のための汎用関数)にターゲットの位置を渡してあげるだけなので簡単でした。

NPC.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MackySoft.CubeKunWars {
	public class NPC : CubeKun {

		private new void Update () {
			if (target) MoveTo(target.position);
		}
    
	}
}

武器

弾の処理

武器から発射する弾のコードです。
弾のステータスと単純な移動処理だけです。

Bullet.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MackySoft.CubeKunWars {

	[RequireComponent(typeof(BoxCollider))]
	public class Bullet : MonoBehaviour {
		
		public Transform Tr {
			get { return _Tr ? _Tr : (_Tr = transform); }
		}
		private Transform _Tr;

		public int power = 20;
		public float speed = 10;

		private void Update () {
			Tr.Translate(Tr.forward * speed * Time.deltaTime,Space.World);
		}
		
	}
}

武器による弾の発射

武器には弾の発射と、その処理を一定間隔でループさせるためのコルーチンを実装します。

Weapon.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MackySoft.CubeKunWars {
	public class Weapon : MonoBehaviour {
		
		public Transform[] firingPoints;

		public float interval = 0.5f;

		[Header("Bullet Settings")]
		public Bullet bullet;
		public int power = 20;
		public float speed = 5;
		public float time = 5;
    
		private Coroutine shootCoroutine = null;

		private void Start () {
			ShootStart();
		}

		public void ShootStart () {
			if (shootCoroutine == null)
				shootCoroutine = StartCoroutine(ShootCoroutine());
		}

		public void ShootStop () {
			if (shootCoroutine == null) return;
			shootCoroutine = null;
			StopCoroutine(shootCoroutine);
		}

		public void Shoot () {
			for (int i = 0;firingPoints.Length > i;i++) {
				var ins = Instantiate(bullet);
				ins.Tr.position = firingPoints[i].position;
				ins.Tr.rotation = firingPoints[i].rotation;
				ins.power = power;
				ins.speed = speed;
				Destroy(ins.gameObject,time);
			}
		}

		private IEnumerator ShootCoroutine () {
			while (true) {
				Shoot();
				yield return new WaitForSeconds(interval);
			}
		}

	}
}

体力

武器があっても減る体力が無いと意味がないので、体力を実装するHealthクラスを用意します。

Health.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace MackySoft.CubeKunWars {
	
	[Serializable]
	public class OnValueChangedEvent : UnityEvent<int> { }

	public class Health : MonoBehaviour {
		
		[SerializeField]
		private int _Value = 100;

		[Header("Events")]
		public OnValueChangedEvent onValueChanged;
		public UnityEvent onDie;

		public int Value {
			get { return _Value; }
			set {
				_Value = value > 0 ? value : 0;
				onValueChanged.Invoke(Value);
				if (IsDead) onDie.Invoke();
			}
		}

		public bool IsDead {
			get { return Value == 0; }
		}

		private void OnValidate () {
			Value = _Value;
		}

	}
}

それに従ってCubeKunクラスに「弾に当たった時にダメージを受ける」処理を追加しました。

CubeKun.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Pathfinding;

namespace MackySoft.CubeKunWars {

	[RequireComponent(typeof(Health))]
	[RequireComponent(typeof(Rigidbody))]
	public abstract class CubeKun : AIPath {

		public float sleepVelocity = 0.4F;
		
		public Transform Tr { get; private set; }
		public Rigidbody Rigid { get; private set; }
		public Health Health { get; private set; }
		
		protected override void Awake () {
			base.Awake();
			Tr = transform;

			Rigid = GetComponent<Rigidbody>();
			Rigid.constraints = RigidbodyConstraints.FreezeAll;

			Health = GetComponent<Health>();
			Health.onDie.AddListener(() => Destroy(gameObject));
		}
		
		protected virtual void OnCollisionEnter (Collision collision) {
			var bullet = collision.gameObject.GetComponent<Bullet>();
			if (bullet) {
				Health.Value -= bullet.power;
				Destroy(bullet.gameObject);
			}
		}

		public void MoveTo (Vector3 position) {
			if (canMove) {
				var dir = CalculateVelocity(GetFeetPosition());
				RotateTowards(targetDirection);

				dir.y = 0;
				if (dir.sqrMagnitude > sleepVelocity * sleepVelocity) {

				} else {
					dir = Vector3.zero;
				}
				Tr.Translate(dir * Time.deltaTime,Space.World);
			}
		}

		public override Vector3 GetFeetPosition () {
			return Tr.position;
		}

	}
}