bate's blog

調べたこと実装したことなどを取りとめもなく書きます。

Boids

Fishのプレファブを作り、Fishスクリプトを追加する。

ゲーム開発者のためのAI入門

ゲーム開発者のためのAI入門

Unityライブラリ辞典 ランタイム編

Unityライブラリ辞典 ランタイム編

Boidsを使ったサンプル。

https://dl.dropboxusercontent.com/u/67579260/Unity/Boids/build.html

Main.cs
空のゲームオブジェクトに追加する。

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

public class Main : MonoBehaviour {
	
	Boid m_PlayerBoid = null;
	List<Boid> m_BoidList = null;
	
	[SerializeField]
	BoidForceCalculator m_Calculate = null;
	
	void Awake () {
		m_Calculate = new BoidForceCalculator();
		InitFish();
	}

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
		Vector3 input = Camera.main.ScreenToWorldPoint(Input.mousePosition);
		m_PlayerBoid.Position = new Vector3(input.x, 0, input.z);
		
		UpdateAI();
	}
	
	/**
	 * @brief	四面体準備.
	 */
	void InitFish() {
		m_BoidList = new List<Boid>();
		for(int i = 0; i < 10; ++i) {
			GameObject fish = FishFactory.Instance.Create();
			Fish script = fish.GetComponent<Fish>();
			script.Position = GetRandomPosition();
			m_BoidList.Add(script);
		}
		m_PlayerBoid = m_BoidList[0];
	}
	
	Vector3 GetRandomPosition() {
		float x = Random.Range(-10.0f,10.0f);
		float z = Random.Range(-10.0f,10.0f);
		return new Vector3(x, 0.0f, z);
	}
	
	void UpdateAI() {
		foreach(Boid boid in m_BoidList) {
			m_Calculate.Apply(boid, m_BoidList);
		}
	}
}


Boids.cs

using UnityEngine;
using System.Collections;

/**
 * @brief	Boid.
 */
public class Boid : MonoBehaviour {
	
	/* 速度. */
	protected Vector3 m_Velocity = Vector3.zero;
	public Vector3 Velocity {
		get { return m_Velocity; }
		set { m_Velocity = value; }
	}
	
	/* 外力. */
	protected Vector3 m_Force = Vector3.zero;
	public Vector3 Force {
		get { return m_Force; }
		set { m_Force = value; }
	}
	
	/* 位置. */
	protected Vector3 m_Position = Vector3.zero;
	public Vector3 Position {
		get { return m_Position; }
		set { m_Position = value; }
	}
	
	/* 向き. */
	protected Vector3 m_Direction = Vector3.forward;
	public Vector3 Direction {
		get { return m_Direction; }
		set { m_Direction = value; }
	}
	
	/* 摩擦. */
	protected float m_Attenuation = 0.9f;
}

Fish.cs
魚オブジェクトに追加する。

using UnityEngine;
using System.Collections;

/**
 * @brief	魚.
 */
public class Fish : Boid {
	
	void Awake () {
	}

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
		
		Vector3 prev = gameObject.transform.position;
		m_Velocity += m_Force*Time.deltaTime;
		m_Velocity *= m_Attenuation;
		m_Position += m_Velocity * Time.deltaTime;
		gameObject.transform.position = m_Position;
		Vector3 dir = m_Position - prev;
		if(dir.magnitude > 0.01f) {
			m_Direction = dir;
			gameObject.transform.rotation = Quaternion.LookRotation(m_Direction);
		}
		m_Force = Vector3.zero;
	}
}

BoidForceCalculator.cs
魚の挙動を計算する。

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

/**
 * @brief
 */
[System.Serializable]
public class BoidForceCalculator {
	
	[SerializeField]
	public float SEPARATE_DISTANCE = 1.0f;
	[SerializeField]
	public float NEAR_DISTANCE = 10.0f;
	[SerializeField]
	public float SCALE = 10.0f;
	[SerializeField]
	public float TARGET_DISTANCE = 30.0f;
	
	[SerializeField]
	public float SEPARATE_SCALE = 1.5f;
	[SerializeField]
	public float ALIGN_SCALE = 1.0f;
	[SerializeField]
	public float COHESION_SCALE = 1.0f;
	
	public void Apply(Boid boid, List<Boid> list) {
		Vector3 force = Vector3.zero;
		force += SEPARATE_SCALE * CalcSeparation(boid, list);
		force += ALIGN_SCALE * CalcAlignment(boid, list);
		force += COHESION_SCALE * CalcCohesion(boid, list);
		boid.Force = force;
	}
	
	Vector3 CalcSeparation(Boid boid, List<Boid> list) {
		Vector3 steer = Vector3.zero;
		int num = 0;
		float dist = 0.0f;
		foreach(Boid t in list) {
			if(t == boid) {
				continue;
			}
			dist = Vector3.Distance(boid.Position, t.Position);
			if(dist > 0.0f && dist < SEPARATE_DISTANCE) {
				Vector3 away_dir = Vector3.Normalize(boid.Position - t.Position)/dist;		
				steer += away_dir;
				++num;
			}
		}
		if(num > 0) {
			steer = steer/num;
		}
		if(steer.magnitude > 0) {
			steer.Normalize();
			steer *= SCALE;
			steer = steer - boid.Velocity;
		}
		return steer;
	}
	
	Vector3 CalcAlignment(Boid boid, List<Boid> list) {
		Vector3 sum = Vector3.zero;
		Vector3 steer = Vector3.zero;
		int num = 0;
		float dist = 0.0f;
		foreach(Boid t in list) {
			if(t == boid) {
				continue;
			}
			dist = Vector3.Distance(boid.Position, t.Position);
			if(dist > 0.0f && dist < NEAR_DISTANCE) {
				sum += t.Velocity;
				++num;
			}
		}
		if(num > 0) {
			sum = sum/(float)num;
			sum.Normalize();
			sum *= SCALE;
			steer = sum - boid.Velocity;
		}
		return steer;
	}
	
	Vector3 CalcCohesion(Boid boid, List<Boid> list) {
		Vector3 steer = Vector3.zero;
		Vector3 sum = Vector3.zero;
		int num = 0;
		float dist = 0.0f;
		foreach(Boid t in list) {
			if(t == boid) {
				continue;
			}
			dist = Vector3.Distance(boid.Position, t.Position);
			if(dist > 0.0f && dist < NEAR_DISTANCE) {
				sum += t.Position;
				++num;
			}
		}
		if(num > 0) {
			Boid target = list[0];
			dist = Vector3.Distance(target.Position, boid.Position);
			if(dist < TARGET_DISTANCE) {
				sum = target.Position;
			} else {
				sum = sum/(float)num;
			}
			Vector3 dir = Vector3.Normalize(sum - boid.Position);
			dir = dir * SCALE;
			steer = dir - boid.Velocity;
		}
		return steer;
	}
}

FishFactory.cs
魚を生成する。

using UnityEngine;
using System.Collections;

/**
 * @brief	魚ファクトリー.
 */
public class FishFactory {
	
	/* 四面体プレファブ. */
	GameObject m_FishPrefab = null;
	
	/* インスタンス. */
	static FishFactory s_Instance = null;
	public static FishFactory Instance {
		get {
			if(s_Instance == null) {
				s_Instance = new FishFactory();
			}
			return s_Instance;
		}
	}
	
	/**
	 * @brief	コンストラクタ.
	 */
	FishFactory() {
		m_FishPrefab = Resources.Load("Fish/Fish") as GameObject;
	}
	
	/**
	 * @brief	魚生成.
	 */
	public GameObject Create() {
		GameObject ret = GameObject.Instantiate(m_FishPrefab, Vector3.zero, Quaternion.identity) as GameObject;
		return ret;
	}
}