Unity Flocking


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:

https://learn.unity.com/tutorial/flocking#


Leave a Reply

Your email address will not be published. Required fields are marked *