Unity/정보

[Unity] Boids 군집 알고리즘

달시_Dalsi 2023. 10. 16. 14:39

Boids 군집 알고리즘 소개

Boids는 인공 생명 알고리즘으로, 개체 간 상호 작용을 통해 동물의 군집 움직임을 표현하는데 사용한다.  

 

Boids는 각 개체마다 다음 3개의 규칙을 가지고 움직인다.

separation: 무리와 충돌하지 않게 반대 방향으로 이동
alignment: 무리의 평균 방향으로 조향
cohesion: 무리의 평균 위치를 향해 이동

 

boids가 여러 방법이 있는데 그 중 아래 영상을 참고로 만들어볼것이다.

아래 영상은 군집의 타겟이 있으며 이를 중점으로 따라가는 형식이다.

https://www.youtube.com/watch?v=_CTzKiVTFPQ 

 

Boids 구현

1. Boids 코드

public class Boids : MonoBehaviour
{
    [SerializeField] float forwardSpeed;
    [SerializeField] float searchRadius_cohesion_alignment;
    [SerializeField] float searchRadius_separation;
    [SerializeField] float rotateSpeed;

    [SerializeField] float targetWeight;
    [SerializeField] float alignmentWeight = 1.0f;
    [SerializeField] float cohesionWeight = 1.0f;
    [SerializeField] float separationWeight = 1.0f;

    Transform Target;
    List<Collider> list_colliders = new List<Collider>();

    public void getTarget(Transform target)
    {
        Target = target;
    }

    // Update is called once per frame
    void Update()
    {
        moveBoid();
    }

    void moveBoid()
    {
        // 범위내 콜라이더 확인
        if (list_colliders.Count > 0)
        {
            list_colliders.Clear();
        }
        Collider[] colliders = Physics.OverlapSphere(transform.position, searchRadius_cohesion_alignment);
        for (int i = 0; i < colliders.Length; i++)
        {
            if (colliders[i].CompareTag("Bird"))
            {
                list_colliders.Add(colliders[i]);
            }
        }
        colliders = list_colliders.ToArray();


        // cohesion, alignment, separation 저장할 변수 초기화
        Vector3 totalCenter = Vector3.zero;
        Vector3 totalDir = Vector3.zero;
        Vector3 totalSeparation = Vector3.zero;
        Vector3 BoidDir = Vector3.zero;
        Vector3 cohesionDirection = Vector3.zero;
        Vector3 Vec_toTarget = Vector3.zero;
        int totalSeparationCount = 0;

        foreach (Collider collider in colliders)
        {
            // alignment 무리 방향으로 조정
            totalDir += collider.transform.forward;

            // cohesion 무리 중심 위치로 이동
            totalCenter += collider.bounds.center;

            // separation 무리와 충돌하지 않게 반대 방향으로 이동
            if (Vector3.Distance(transform.position, collider.transform.position) <= searchRadius_separation)
            {
                totalSeparation += transform.position - collider.transform.position;
            }
            totalSeparationCount++;
        }

        if(totalSeparationCount > 0)
        {
            totalDir /= colliders.Length;
            totalCenter /= colliders.Length;
            cohesionDirection = totalCenter - transform.position;

            Vec_toTarget = Target.position - transform.position;
            totalDir += targetWeight * Vec_toTarget.normalized;

            BoidDir = alignmentWeight * totalDir + cohesionWeight * cohesionDirection + separationWeight * totalSeparation;
            BoidDir.Normalize();

            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(BoidDir), rotateSpeed * Time.deltaTime);
        }
        transform.position += transform.forward * forwardSpeed * Time.deltaTime;
    }
}

2. Spawner 코드

public class Spawner_scr : MonoBehaviour
{
    [SerializeField] Transform Target;
    [SerializeField] GameObject prefabs_bird;
    [SerializeField] int spawn_Count;
    [SerializeField] int spawn_radius;

    private void Awake()
    {
        for (int i = 0; i < spawn_Count; i++)
        {
            //Random.onUnitSphere 반지름 1을 가진 구의 공간에서의 랜덤한 위치
            Vector3 pos_randOnSpherePos = Random.onUnitSphere * spawn_radius;
            Vector3 rot_randOnSpherePos = Random.onUnitSphere * 100f;
            GameObject obj = Instantiate(prefabs_bird, pos_randOnSpherePos, Quaternion.Euler(rot_randOnSpherePos));
            obj.transform.GetComponent<Boids>().getTarget(Target);
        }
    }
}

 

결과

움직이는 목표와 지정된 목표지점으로 각자 잘 흩어져서 모이는걸 확인할 수 있다.