private void Awake()
        {
            myTransform = transform;

            int collidersLength = boxColliders.Length;

            if (boxColliders != null && collidersLength > 0)
            {
                collisionData = new CollisionArea[collidersLength];

                for (int i = 0; i < collidersLength; i++)
                {
                    CollisionArea ca = new CollisionArea();
                    BoxCollider   bc = boxColliders[i];
                    if (bc == null)
                    {
                        Debug.LogError("One of the Box Colliders is null!");

                        boxColliders = new BoxCollider[0];
                        return;
                    }

                    Transform bcTransform = bc.transform;
                    Vector3   localScale  = bcTransform.localScale;

                    Vector3 coll_pos = bc.transform.position;

                    Vector3 coll_size = bc.size;
                    coll_size.x *= localScale.x;
                    coll_size.y *= localScale.y;
                    coll_size.z *= localScale.z;

                    coll_pos.x -= coll_size.x / 2.0f;
                    coll_pos.y -= coll_size.y / 2.0f;
                    coll_pos.z -= coll_size.z / 2.0f;

                    ca.position = coll_pos;
                    ca.size     = coll_size;

                    collisionData[i] = ca;
                }
            }

            if (instanced)
            {
                drawArgsBuffer = new ComputeBuffer(1, 5 * sizeof(uint), ComputeBufferType.IndirectArguments);
                drawArgsBuffer.SetData(new uint[5] {
                    fishMesh.GetIndexCount(0), (uint)fishesCount, 0, 0, 0
                });

                props = new MaterialPropertyBlock();
                props.SetFloat("_UniqueID", Random.value);
            }
        }
        void InitializeFishes()
        {
            instancingBounds = new Bounds(myTransform.position, Vector3.one * 1000);

            int collidersLength = boxColliders.Length;

            if (boxColliders != null && collidersLength > 0)
            {
                collisionData = new CollisionArea[collidersLength];

                for (int i = 0; i < collidersLength; i++)
                {
                    CollisionArea ca = new CollisionArea();
                    BoxCollider   bc = boxColliders[i];
                    if (bc == null)
                    {
                        Debug.LogError("One of the Box Colliders is null!");

                        boxColliders = new BoxCollider[0];
                        return;
                    }

                    Transform bcTransform = bc.transform;
                    Vector3   localScale  = bcTransform.localScale;

                    Vector3 coll_pos = bc.transform.position;

                    Vector3 coll_size = bc.size;
                    coll_size.x *= localScale.x;
                    coll_size.y *= localScale.y;
                    coll_size.z *= localScale.z;

                    coll_pos.x -= coll_size.x / 2.0f;
                    coll_pos.y -= coll_size.y / 2.0f;
                    coll_pos.z -= coll_size.z / 2.0f;

                    ca.position = coll_pos;
                    ca.size     = coll_size;

                    collisionData[i] = ca;
                }
            }

            if (instanced)
            {
                drawArgsBuffer = new ComputeBuffer(1, 5 * sizeof(uint), ComputeBufferType.IndirectArguments);
                drawArgsBuffer.SetData(new uint[5] {
                    fishMesh.GetIndexCount(0), (uint)fishesCount, 0, 0, 0
                });
            }

            currentFishesCount = fishesCount;
            oldFishesCount     = currentFishesCount;
        }
        private void Awake()
        {
            myTransform      = transform;
            instancingBounds = new Bounds(myTransform.position, Vector3.one * 1000);

            int collidersLength = boxColliders.Length;

            if (boxColliders != null && collidersLength > 0)
            {
                collisionData = new CollisionArea[collidersLength];

                for (int i = 0; i < collidersLength; i++)
                {
                    CollisionArea ca = new CollisionArea();
                    BoxCollider   bc = boxColliders[i];
                    if (bc == null)
                    {
                        Debug.LogError("One of the Box Colliders is null!");

                        boxColliders = new BoxCollider[0];
                        return;
                    }

                    Transform bcTransform = bc.transform;
                    Vector3   localScale  = bcTransform.localScale;

                    Vector3 coll_pos = bc.transform.position;

                    Vector3 coll_size = bc.size;
                    coll_size.x *= localScale.x;
                    coll_size.y *= localScale.y;
                    coll_size.z *= localScale.z;

                    coll_pos.x -= coll_size.x / 2.0f;
                    coll_pos.y -= coll_size.y / 2.0f;
                    coll_pos.z -= coll_size.z / 2.0f;

                    ca.position = coll_pos;
                    ca.size     = coll_size;

                    collisionData[i] = ca;
                }
            }
        }
        void Start()
        {
            if (followTarget)
            {
                groupAnchor = target.position;
            }
            else
            {
                GeneratePath();
                groupAnchor = targetPositions[0];
            }

            fishesData = new FishBehaviourCPU2[fishesCount];
            if (!instanced)
            {
                fishesTransforms = new Transform[fishesCount];
            }

            for (int i = 0; i < fishesCount; i++)
            {
                fishesData[i] = CreateBehaviour();
                fishesData[i].speed_offset = Random.value * 10.0f;

                if (!instanced)
                {
                    fishesTransforms[i] = Instantiate(prefab, fishesData[i].position, Quaternion.identity).transform;
                }
            }

            if (boxColliders.Length <= 0)
            {
                collisionData = new CollisionArea[1] {
                    new CollisionArea()
                }
            }
            ;

            collisionDataLength = boxColliders.Length <= 0 ? 0 : collisionData.Length;

            if (instanced)
            {
                fishBuffer = new ComputeBuffer(fishesCount, sizeof(float) * 9);
                fishBuffer.SetData(fishesData);

                fishInstancedMaterial.SetBuffer("fishBuffer", fishBuffer);
            }
        }

        FishBehaviourCPU2 CreateBehaviour()
        {
            FishBehaviourCPU2 behaviour = new FishBehaviourCPU2();
            Vector3           pos       = groupAnchor + Random.insideUnitSphere * spawnRadius;
            Quaternion        rot       = Quaternion.Slerp(transform.rotation, Random.rotation, 0.3f);

            switch (movementAxis)
            {
            case MovementAxis.XY:
                pos.z = rot.z = 0.0f;
                break;

            case MovementAxis.XZ:
                pos.y = rot.y = 0.0f;
                break;
            }

            behaviour.position = pos;
            behaviour.velocity = rot.eulerAngles;

            behaviour.speed     = Random.Range(minSpeed, maxSpeed);
            behaviour.rot_speed = Random.Range(minRotationSpeed, maxRotationSpeed);

            return(behaviour);
        }

        void Update()
        {
            UpdateGroupAnchor();
            var time      = Time.time;
            var deltaTime = Time.deltaTime;

            for (int i = 0; i < fishesCount; i++)
            {
                FishBehaviourCPU2 fish = fishesData[i];

                Transform fish_transform = instanced ? null : fishesTransforms[i];
                if (!instanced)
                {
                    fish.position = fish_transform.position;
                }

                if (movementAxis == MovementAxis.XY)
                {
                    fish.position.z = 0.0f;
                }
                else if (movementAxis == MovementAxis.XZ)
                {
                    fish.position.y = 0.0f;
                }

                var current_pos = fish.position;
                var current_rot = instanced ? Quaternion.identity : fish_transform.rotation;

                var noise         = Mathf.PerlinNoise(time, fish.speed_offset) * 2.0f - 1.0f;
                var fish_velocity = fish.speed * (1.0f + noise * speedVariation);

                var separation = Vector3.zero;
                var alignment  = Vector3.zero;
                var cohesion   = groupAnchor;

                //@Collisions!
                Vector3 next_position = fish.position + (fish.velocity * 3) * (fish_velocity * deltaTime);
                Vector3 avoidance     = new Vector3(0, 0, 0);
                for (int c = 0; c < collisionDataLength; c++)
                {
                    CollisionArea ca = collisionData[c];

                    Vector3 collider_pos  = ca.position;
                    Vector3 collider_size = ca.size;

                    if ((next_position.x >= collider_pos.x && next_position.x <= collider_pos.x + collider_size.x) &&
                        (next_position.y >= collider_pos.y && next_position.y <= collider_pos.y + collider_size.y) &&
                        (next_position.z >= collider_pos.z && next_position.z <= collider_pos.z + collider_size.z))
                    {
                        Vector3 coll_point = collider_pos;
                        coll_point.x += collider_size.x / 2.0f;
                        coll_point.y += collider_size.y / 2.0f;
                        coll_point.z += collider_size.z / 2.0f;

                        avoidance += next_position - coll_point;
                        avoidance  = (avoidance).normalized;
                        avoidance *= force;
                    }
                }

                var nearby_fishes_count = 1;
                if (!instanced)
                {
                    var nearbyBoids = Physics.OverlapSphere(current_pos, neighbourDistance, searchLayer);
                    foreach (var boid in nearbyBoids)
                    {
                        if (boid.gameObject == fish_transform.gameObject)
                        {
                            continue;
                        }

                        var t = boid.transform;
                        separation += GetSeparationVector(current_pos, t.position);
                        alignment  += t.forward;
                        cohesion   += t.position;
                    }

                    nearby_fishes_count = nearbyBoids.Length;
                }
                else
                {
                    for (int j = 0; j < fishesCount; j++)
                    {
                        if (j == i)
                        {
                            continue;
                        }

                        FishBehaviourCPU2 other_fish = fishesData[j];

                        if ((current_pos - other_fish.position).magnitude < neighbourDistance)
                        {
                            separation += GetSeparationVector(current_pos, other_fish.position);
                            alignment  += other_fish.velocity;
                            cohesion   += other_fish.position;

                            nearby_fishes_count++;
                        }
                    }
                }

                var avg = 1.0f / nearby_fishes_count;
                alignment *= avg;
                cohesion  *= avg;
                cohesion   = (cohesion - current_pos).normalized;

                var velocity = separation + alignment + cohesion;
                velocity += avoidance;

                if (movementAxis == MovementAxis.XY)
                {
                    velocity.z = 0.0f;
                }
                else if (movementAxis == MovementAxis.XZ)
                {
                    velocity.y = 0.0f;
                }

                var ip = Mathf.Exp(-fish.rot_speed * deltaTime);
                if (!instanced)
                {
                    fish.velocity = velocity.normalized;
                    var rotation = Quaternion.FromToRotation(Vector3.forward, velocity.normalized);
                    if (rotation != current_rot)
                    {
                        fish_transform.rotation = Quaternion.Lerp(rotation, current_rot, ip);
                    }
                }
                else
                {
                    fish.velocity = Vector3.Lerp((velocity.normalized), (fish.velocity.normalized), ip);
                }

                fish.position += (instanced ? fish.velocity : fish_transform.forward) * (fish_velocity * deltaTime);

                if (!instanced)
                {
                    fish_transform.position = fish.position;
                }

                fishesData[i] = fish;
            }

            //fishBuffer.SetData(fishesData);
        }