Unity チュートリアルのタワーディフェンステンプレートを触ってみる(37)

Unity チュートリアルのタワーディフェンステンプレートを触ってみる(36)では NavigationNodes に追加した FixedNodeSelector について解説を行った。今回は NavigationNodes に追加した Node で作成される AreaMeshCreator の解説を行っていく。

1.タワーディフェンステンプレートのステージの設定編 – AreaMeshCreator.cs

AreaMeshCreator.cs について稚拙ながら解説

「AreaMeshCreator.cs」は「Assets/Scripts/TowerDefense/MeshCreator/AreaMeshCreator.cs」の指しておりスクリプトについては以下の通り。内容としては Mesh を構成しているポイントの取得および操作を行っており、Triangle と MeshObject というクラスを内包しており、その中でも MeshObject は 複数の Triangle から構成されている。

[cce_csharp]using System;
using System.Collections.Generic;
using System.Linq;
using Core.Extensions;
using UnityEngine;
using Random = UnityEngine.Random;

namespace TowerDefense.MeshCreator
{
	/// <summary>
	/// Creates a Mesh that represents an area
	/// </summary>
	[Serializable]
	public class AreaMeshCreator : MonoBehaviour
	{
		[HideInInspector]
		public MeshObject meshObject;

		public Transform outSidePointsParent;

		/// <summary>
		/// The parent transform of points in the mesh
		/// </summary>
		public Transform pointsCenter
		{
			get
			{
				if (outSidePointsParent == null)
				{
					var points = new GameObject("Points");
					outSidePointsParent = points.transform;
					outSidePointsParent.SetParent(transform, false);
					outSidePointsParent.eulerAngles = new Vector3(90, 0, 0);
				}
#if UNITY_EDITOR
				outSidePointsParent.hideFlags = HideFlags.HideInHierarchy;
#endif
				return outSidePointsParent;
			}
		}

#if UNITY_EDITOR
		/// <summary>
		/// Gets an array of the Transforms of points in this mesh - only used by editor script
		/// </summary>
		public Transform[] pointsTransforms
		{
			get
			{
				Transform[] childern = new Transform[pointsCenter.childCount];
				int length = pointsCenter.childCount;
				for (int i = 0; i < length; i++)
				{
					childern[i] = pointsCenter.GetChild(i);
				}
				return childern;
			}
		}
#endif

		/// <summary>
		/// Get a list of Vector3s that correspond to the positions of the points in this mesh
		/// </summary>
		/// <returns>List of Points</returns>
		public List<Vector3> GetPoints()
		{
			return GetChildrenPositions(pointsCenter);
		}

		/// <summary>
		/// Gets a random Vector3 that lies inside the mesh object
		/// </summary>
		/// <returns>Random point</returns>
		public Vector3 GetRandomPointInside()
		{
			return transform.TransformPoint(meshObject.RandomPointInMesh());
		}

		/// <summary>
		/// Forces all points to have a local "y" position of 0
		/// Makes them coplanar
		/// </summary>
		public void ForcePointsFlat()
		{
			int length = pointsCenter.childCount;
			for (int i = 0; i < length; i++)
			{
				Transform t = pointsCenter.GetChild(i);
				Vector3 position = t.localPosition;
				position.z = 0;
				t.localPosition = position;
			}
		}

		List<Vector3> GetChildrenPositions(Transform parent)
		{
			int length = parent.childCount;
			List<Vector3> points = new List<Vector3>();
			for (int i = 0; i < length; i++)
			{
				points.Add(parent.GetChild(i).position);
			}
			return points;
		}

#if UNITY_EDITOR
		void OnDrawGizmos()
		{
			int count = pointsCenter.childCount;
			for (int i = 0; i < count - 1; i++)
			{
				Vector3 from = pointsCenter.GetChild(i).position;
				Vector3 to = pointsCenter.GetChild(i + 1).position;
				Gizmos.DrawLine(from, to);
			}
			// last to first
			Vector3 last = pointsCenter.GetChild(count - 1).position;
			Vector3 first = pointsCenter.GetChild(0).position;
			Gizmos.DrawLine(last, first);
		}
#endif
	}

	[Serializable]
	public class Triangle
	{
		public Vector3 v0;

		public Vector3 v1;
		
		public Vector3 v2;

		public float area;

		/// <summary>
		/// Represents a Triangle in a mesh
		/// </summary>
		/// <param name="v0">First Point</param>
		/// <param name="v1">Second Point</param>
		/// <param name="v2">Third Point</param>
		public Triangle(Vector3 v0, Vector3 v1, Vector3 v2)
		{
			this.v0 = v0;
			this.v1 = v1;
			this.v2 = v2;

			// Precalculate area
			float a = Vector3.Distance(v0, v1), b = Vector3.Distance(v1, v2), c = Vector3.Distance(v2, v0);
			float s = (a + b + c) / 2;
			area = Mathf.Sqrt(s * (s - a) * (s - b) * (s - c));
		}
	}

	/// <summary>
	/// Contains the triangles of the mesh
	/// Calculates the mesh area
	/// Can get a random point within the mesh area
	/// </summary>
	[Serializable]
	public class MeshObject
	{
		public List<Triangle> triangles;

		public float completeArea;

		public MeshObject(List<Triangle> triangles)
		{
			this.triangles = triangles;
			completeArea = this.triangles.Sum(x => x.area);
		}

		/// <summary>
		/// Gets a random point in the mesh
		/// </summary>
		/// <returns>Random point</returns>
		public Vector3 RandomPointInMesh()
		{
			Triangle randomTriangle = triangles.WeightedSelection(completeArea, t => t.area);
			float x = Random.value, y = Random.value;
			if (x + y >= 1)
			{
				x = 1 - x;
				y = 1 - y;
			}
			float z = 1 - x - y;
			var randomBaryCentricPoint = new Vector3(x, y, z);
			Vector3 cartesianPoint = (randomBaryCentricPoint.x * randomTriangle.v0) + (randomBaryCentricPoint.y *
			                                                                           randomTriangle.v1) +
			                         (randomBaryCentricPoint.z * randomTriangle.v2);
			return cartesianPoint;
		}
	}
}[/cce_csharp]

24行目 : 「public Transform pointsCenter」は outSidePointsParent 変数に設定がなければ、新たに Point という名称でゲームオブジェクトを作成して位置設定を行っている。

46行目 : 「public Transform[] pointsTransforms」は Point の子を配列として、すべての Transform を取得している。

65行目 : 「public List<Vector3> GetPoints()」は GetChildrenPositions の処理を呼び出している。

74行目 : 「public Vector3 GetRandomPointInside()」は以降に解説する MeshObject クラスの RandomPointInMesh の処理を呼び出している。

83行目 : 「public void ForcePointsFlat()」は強制的に z 座標を 0 に修正している。

95行目 : 「List<Vector3> GetChildrenPositions(Transform parent)」は引数として与えたれた Transform から子の Position を取得し、Point のリストを作成している。

英語のサイトとなってしまうが内部クラスについてはこちらを参照してほしい

125行目 : 「public class Triangle」は新たに Traiangle というクラスを定義していることを指している。本来クラスは 1 ファイルにつき、原則 1 個を作成するのである。しかしながら、このクラスはインナークラスまたは内部クラスと呼び他のクラスに対して公開したくないもしくは公開しなくても良いクラスを定義する際に使用する。もちろん普段どおり一つの *.cs (これを例とするなら、Triangle.cs)ファイルを用意しても何ら問題はない。

141行目 : 「public Triangle(Vector3 v0, Vector3 v1, Vector3 v2)」はコンストラクタである。コンストラクタとは『コンストラクタ(構築子[1]: constructor)は、オブジェクト指向プログラミング言語で新たなオブジェクトを生成する際に呼び出されて内容の初期化などを行なう関数あるいはメソッドのことである。対義語はデストラクタ

オブジェクトの生成は、

  1. メモリ割当(: allocation
  2. 初期化(: initialization

の二段階を経て行われるが、コンストラクタを持つプログラミング言語ではメモリ割当は言語機能に組み込まれ、初期化用のコードのみを記述するのが普通である。』(Wikipedia より引用)

とあるが分かりづらいと思われるので、Unity に置き換えると Instantiate に近い処理を行う関数イメージと捉えてもらうと良いと思う。Unity で新たにインスタンスを生成する際は Instantiate  関数を呼び出し新たなゲームオブジェクトを作成しているが、MonoBehaviour を継承していない(ゲームオブジェクトではない)クラスでは Instantiate  を行うことができない。そのかわりとして new クラス名(これを例とするなら、 new Triangle(v0, v1, v2)) として新たにオブジェクトの生成を行っているということである。

コンストラクタについてはこちらを参照してほしい

160行目 : 「public class MeshObject」は Traiangle と同様に MeshObject というクラスを定義していることを指している。

166行目 : 「public MeshObject(List<Triangle> triangles)」は Traiangle と同様に MeshObject のコンストラクタである。

176行目 : 「public Vector3 RandomPointInMesh()」はメッシュの中からランダムな地点を算出している。

%d人のブロガーが「いいね」をつけました。