Auf der Unity Hompage findet sich praktischerweise ein simples Tutorial mit Videoanleitung, welches Flocking-Algorithmen verständlich machen soll. Da meine C# Kenntnisse noch recht bescheiden sind, hat mich das Ganze doch etwas Zeit und nerven gekostet, aber nun, endlich, habe ich es vollbracht.
Hier der Code des FlockingManagers und des Flock Files mit Erklärung:
Code Flock manager:
using UnityEngine;
public class FlockManager : MonoBehaviour {
//Erstellt den Flock Manager
//Zugang zum Fish Game Object
public GameObject fishPrefab;
//Variierbare Startnummer an Fischen
public int numFish = 20;
//Array für die Erstellung der Fische
public GameObject[] allFish;
//Räumliche Limits
public Vector3 swimLimits = new Vector3(5.0f, 5.0f, 5.0f);
//Gemeinsame Zielposition
public Vector3 goalPos;
//Variabel verstellbare Parameter in Unity
[Header("Fish Settings")]
[Range(0.0f, 5.0f)]
public float minSpeed;
// Minimum speed range
[Range(0.0f, 5.0f)]
public float maxSpeed;
// Maximum speed range
[Range(1.0f, 10.0f)]
public float neighbourDistance;
//Distanz zum Nachbarn
[Range(0.0f, 5.0f)]
public float rotationSpeed;
//Rotationsgeschwindigkeit der FishPrefabs
void Start() {
// Erstellung der Fische
allFish = new GameObject[numFish];
//looped und instantiiert (Fisch)
for (int i = 0; i < numFish; ++i) {
//Erstellt Position des Fisches → Flockmanager+randomVektor
Vector3 pos = this.transform.position + new Vector3(Random.Range(-swimLimits.x, swimLimits.x),
Random.Range(-swimLimits.x, swimLimits.x),
Random.Range(-swimLimits.x, swimLimits.x));
allFish[i] = (GameObject)Instantiate(fishPrefab, pos, Quaternion.identity);
allFish[i].GetComponent<Flock>().myManager = this;
}
//Ziel zu dem sich die Fische hinbewegen
goalPos = this.transform.position;
}
//Wird pro Frame upgedatet
void Update() {
//dem Ziel wird jedes mal ein neuer randomisierter Faktor hinzugefügt
if (Random.Range(0.0f,
100.0f) < 10.0f) {
goalPos = this.transform.position + new Vector3(Random.Range(-swimLimits.x
, swimLimits.x),
Random.Range(-swimLimits.x, swimLimits.x),
Random.Range(-swimLimits.x, swimLimits.x));
}
}
}
Code Flocking:
using UnityEngine;
public class Flock : MonoBehaviour {
//Zugriff auf den Flock Manager
public FlockManager myManager;
//Initial Speed vom fisch
float speed;
// Bool um die Raumlimitation zu checken
bool turning = false;
void Start() {
// Random speed für jeden einzelnen Fisch
speed = Random.Range(myManager.minSpeed, myManager.maxSpeed);
}
//Wird pro Frame upgedated
void Update() {
// Bbox des Manager Cubes
Bounds b = new Bounds(myManager.transform.position, myManager.swimLimits * 2.0f);
// wenn der Fisch auf Widerstand trifft oder an die Grenzen des Cubes kommt, muss er umdrehen
RaycastHit hit = new RaycastHit();
Vector3 direction = Vector3.zero;
if (!b.Contains(transform.position)) {
turning = true;
direction = myManager.transform.position - transform.position;
} else if (Physics.Raycast(transform.position, this.transform.forward * 50.0f, out hit)) {
turning = true;
// Debug.DrawRay(this.transform.position, this.transform.forward * 50.0f, Color.red);
direction = Vector3.Reflect(this.transform.forward, hit.normal);
} else {
turning = false;
}
// Test ob gedreht wird
if (turning) {
// Drehen zum Cube Center
transform.rotation =
Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation(direction),
myManager.rotationSpeed * Time.deltaTime);
} else {
// 10% chance of altering prefab speed
if (Random.Range(0.0f, 100.0f) < 10.0f) {
speed = Random.Range(myManager.minSpeed, myManager.maxSpeed);
}
// 20& chance of applying the flocking rules
if (Random.Range(0.0f, 100.0f) < 20.0f) {
ApplyRules();
}
}
transform.Translate(0.0f, 0.0f, Time.deltaTime * speed);
}
void ApplyRules() {
GameObject[] gos;
gos = myManager.allFish;
Vector3 vcentre = Vector3.zero;
Vector3 vavoid = Vector3.zero;
float gSpeed = 0.01f;
float nDistance;
int groupSize = 0;
foreach (GameObject go in gos) {
if (go != this.gameObject) {
nDistance = Vector3.Distance(go.transform.position, this.transform.position);
if (nDistance <= myManager.neighbourDistance) {
vcentre += go.transform.position;
groupSize++;
if (nDistance < 1.0f) {
vavoid = vavoid + (this.transform.position - go.transform.position);
}
Flock anotherFlock = go.GetComponent<Flock>();
gSpeed = gSpeed + anotherFlock.speed;
}
}
}
if (groupSize > 0) {
// Find the average centre of the group then add a vector to the target (goalPos)
vcentre = vcentre / groupSize + (myManager.goalPos - this.transform.position);
speed = gSpeed / groupSize;
Vector3 direction = (vcentre + vavoid) - transform.position;
if (direction != Vector3.zero) {
transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation(direction),
myManager.rotationSpeed * Time.deltaTime);
}
}
}
}
Flocking Tutorial: