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

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;
		}

	}
}

プレイヤーの移動【UnityでRTSを作る 2】

前回の記事ではざっくりと開発計画をしましたが、
ついに今回ゲーム制作が始まります。

今回はプレイヤーの移動です。

モデル

まずはプレイヤーのモデルの紹介です。

f:id:MackySoft:20170413205955j:plain
キューブ君(CubeKun)と呼びます。
ゲーム制作を始めた当初に作ったキャラクターです。

このキューブ君がプレイヤーとなります。

そしてこのプロジェクトの名前は「CubeKun Wars」となりました。
(変更の可能性もあります)

カメラ

まず最初にカメラを制御するためのコードを書きます。
トップダウン型のゲームなのでカメラが下に向いている前提のコードです。

CameraController.cs

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

namespace MackySoft.CubeKunWars {
	public class CameraController : MonoBehaviour {

		public Transform target;
		public float height = 10;

		private Transform tr;

		void Start () {
			tr = transform;
		}
		
		void Update () {
			if (!target) return;
			tr.position = target.position + Vector3.up * height;
		}
	}
}

移動処理

経路探索にはA* Pathfinding Projectを使用します。

CubeKun.cs

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

using Pathfinding;

namespace MackySoft.CubeKunWars {

	public abstract class CubeKun : AIPath {

		public float sleepVelocity = 0.4F;
		
		public Transform Tr { get ; private set; }

		protected override void Awake () {
			base.Awake();
			Tr = transform;
		}

		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;
		}

	}
}


Player.cs

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

namespace MackySoft.CubeKunWars {
	public class Player : CubeKun {

		public Transform Pointer { get; private set; }

		protected override void Awake () {
			base.Awake();
			canMove = false;
			Pointer = new GameObject("pointer").transform;
			target = Pointer;

			Camera.main.orthographic = true;
		}

		private new void Update () {
			canMove = Input.GetMouseButton(0);
			canSearch = canMove;
			if (canMove) {
				Pointer.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
				MoveToPointerPosition();
			}
		}

		public void MoveToPointerPosition () {
			MoveTo(Pointer.position);
		}

	}
}

動画

クリックし続けるとキューブ君がカーソルのある方向へ向かいます。

Make RTS with Unity Part.1[Player Mover]

どんなゲームを作るか【UnityでRTSを作る 1】

これから作り始めるRTSっぽいゲームの話をします

開発日記を始めるわけ

「誰にも見せずやってても続かなかった」からです。
2年間ゲームを作ってて一番開発が続いたのが、
YouTubeに進歩を投稿していたプロジェクトです。(今はありませんがブログも同時進行でした)

そんなわけで今一度、開発日記を初めようと思います。

ゲームの内容

タイトル通り、RTSのような群衆同士をを戦わせるゲームを作ります。
ただし通常のRTSと違って、プレイヤーは神様ではなく、
NPCと同じようにユニットとして存在し、戦うことができます。

ルール

  • お互いにマップに配置された拠点を取り合いながら戦います
  • 拠点はユニットのスポーン、弾薬の補充など、戦略的に重要な意味を持ちます
  • 先に相手を全滅させたら勝ちです

開発手順

メインシーンから作る

タイトル画面などのUIが主体となるシーンは後回しです。
めんどくさいうえに大した達成感がないからです。

小さく作る

最初から立派なものを作らず、モデルは少なく、UIもてきとーに作ります。
まずは最低限のゲームサイクルを作ります。

使用するアセット

現在使用予定のアセットです。

A* Pathfinding Project

ユニットの移動に使用します。

Chronos

ポーズ、倍速処理に使用します。

Dynamic CullingGroup

製作中の自作アセットです。
ランダムマップになる予定なので、動的なカリングをするために使用します。

あいさつ

はじめまして。

これからUnityでゲームを作る過程を投稿してしていきます。

 

「へぇ~!ヒキニートでもゲーム作れるんだ。じゃあ俺でも作れるじゃん!」

的な勇気をゲーム開発者たちに与えていきます。

 

twitter.com

 

www.youtube.com

 

github.com