[240222]TIL

응징·2024년 2월 22일
0

TIL

목록 보기
32/36
post-thumbnail

Flock 알고리즘 공부

오브젝트 생성및 집단 관리

using UnityEngine;

public class Flock : MonoBehaviour
{
    public GameObject c_prefebFish;  //하위 프리팹
    public GameObject p_prefebFish;  //상위 프리팹
    public int m_Fish = 30;   // 생성할 물고기

    public int Boundary = 7;  //물고기가 자유롭게 움직일수 있는 경계
    public GameObject[] Fishes;
    public Vector3 TargetPosition = Vector3.zero;

    public Vector3 centerPosition; //기준이 되는 좌표

    void Start()
    {
        Fishes = new GameObject[m_Fish];

        TargetPosition = centerPosition;

        for (int i = 0; i < m_Fish; i++)
        {
            Vector3 position = centerPosition + new Vector3(
                Random.Range(-Boundary, Boundary),
                Random.Range(-Boundary, Boundary),
                Random.Range(-Boundary, Boundary)
            );

            GameObject fish = (GameObject)Instantiate(c_prefebFish, position, Quaternion.identity);

            fish.transform.parent = p_prefebFish.transform; //자식 오브젝트로 엮음
            Fishes[i] = fish;
        }
    }

    void Update()
    {
        GetTargetPosition();
    }

    void GetTargetPosition()
    {
        if (Random.Range(1, 10000) < 50)
        {
            TargetPosition = new Vector3(
                Random.Range(TargetPosition.x -Boundary, TargetPosition.x + Boundary),
                Random.Range(3, 13),
                Random.Range(TargetPosition.z - Boundary, TargetPosition.z + Boundary)
            );
        }
    }
}

물고기의 생성과 전체 물고기의 타겟 관리를 하는 스크립트

각 오브젝트의 자유도

using UnityEngine;

public class FishController : MonoBehaviour
{
	public float MaxSpeed = 2.0f;  //최고 스피드
	public float MaxTurnSpeed = 0.5f;  //회전력
	public float Speed;   //초기속도
	public float NeighborDistance = 3.0f;  //주변 물고기와의 거리
	private bool IsTurning = false;

	private Flock flock;

	void Start()
	{
		Speed = Random.Range(0.5f, MaxSpeed);  //물고기 초기 속도 설정
		flock = GetComponentInParent<Flock>(); //부모 스크립트
	}

	void Update()
	{
		GetIsTurning();

		if (IsTurning) //물고기가 영역안에 들어가 있을때
		{
			Vector3 direction = Vector3.zero - transform.position; 
			transform.rotation = Quaternion.Slerp(transform.rotation,
				Quaternion.LookRotation(direction),
				TurnSpeed() * Time.deltaTime);
			Speed = Random.Range(0.5f, MaxSpeed);
		}
		else
		{
			if (Random.Range(0, 5) < 1)
				SetRotation();
		}

		transform.Translate(0, 0, Time.deltaTime * Speed);
	}

	//물고기가 경계에 다다르면 회전 상태를 설정
	void GetIsTurning()
	{
		//물고기가 영역안에 들어있을때임
		if (Vector3.Distance(transform.position, Vector3.zero) >= flock.Boundary)
		{
			IsTurning = true;
		}
		else
		{
			IsTurning = false;
		}
	}

	//물고기의 회전을 설정
	//주변 물고기들의 위치와 속도를 고려하여 그룹의 중심으로 이동
	void SetRotation()
	{
		GameObject[] fishes;
		fishes = flock.Fishes; 

		Vector3 center = Vector3.zero;  //그룹의 중심
		Vector3 avoid = Vector3.zero;   //회피를 위한 백터
		float speed = 0.1f;   //그룹의 평균 속도

		Vector3 targetPosition = flock.TargetPosition; 

		float distance;
		int groupSize = 0;

		for (int i = 0; i < fishes.Length; i++)
		{
			if (fishes[i] != gameObject) //자신외 물고기들 검사
			{
				// 주변 물고기 사이 거리 계산
				distance = Vector3.Distance(fishes[i].transform.position, transform.position);

				//주변 물고기가 일정 범위내 있을경우
				if (distance <= NeighborDistance)
				{
					center += fishes[i].transform.position; //그룹의 중심을 업데이트
					groupSize++;

					//주변 물고기가 너무 가까운 경우
					if (distance < 0.75f)
					{
						//회피 벡터를 업데이트
						avoid += (transform.position - fishes[i].transform.position);
					}

					//주변 물고기의 평균 속도를 업데이트
					FishController anotherFish = fishes[i].GetComponent<FishController>();
					speed += anotherFish.Speed;
				}
			}
		}

		//주변에 물고기가 있을 경우:
		if (groupSize > 0)
		{
			//그룹의 중심을 계산하고 이동 목표 지점으로 향하도록
			center = center / groupSize + (targetPosition - transform.position);
			Speed = speed / groupSize; //물고기의 평균 속도를 업데이트

			Vector3 direction = (center + avoid) - transform.position;  //이동방향을 구함
			if (direction != Vector3.zero) //이동 방향이 존재한다면 아래의 코드를 실행
			{
				//Quaternion.Slerp는 현재 회전에서 목표 회전까지의 회전을 부드럽게 보간
				transform.rotation = Quaternion.Slerp(transform.rotation,
					Quaternion.LookRotation(direction),
					TurnSpeed() * Time.deltaTime);
			}
		}
	}

	//회전속도 랜덤으로 반환
	float TurnSpeed()
	{
		return Random.Range(0.2f, MaxTurnSpeed);
	}
}

주변 물고기의 거리에 따라 회전과 스피드가 달라지는 구조 회전은 센터를 중점으로 바라보면서 주변 물고기의 거리에 따라 회전이 조금씩 달라짐.

특정 레이어를 회피

using UnityEngine;

public class AvoidLayer : MonoBehaviour
{
    public LayerMask obstacleLayer;
    public float avoidanceRadius = 5f;
    public float avoidanceForce = 10f;
    public float maxSpeed = 5f;

    private void FixedUpdate()
    {
        Vector3 avoidanceDirection = Vector3.zero;

        // 레이캐스트를 이용하여 피해야 할 레이어의 장애물 감지
        RaycastHit[] hits = Physics.SphereCastAll(transform.position, avoidanceRadius, transform.forward, 0f, obstacleLayer);

        foreach (RaycastHit hit in hits)
        {
            // 피해야 할 레이어의 장애물이 감지된 경우, 피하는 방향 계산
            Vector3 obstacleDirection = transform.position - hit.transform.position;
            avoidanceDirection += obstacleDirection.normalized * (avoidanceRadius - obstacleDirection.magnitude);
        }

        // 피하는 방향을 현재 위치에 반영하여 이동
        transform.position += avoidanceDirection.normalized * maxSpeed * Time.deltaTime;
    }
}

특정 레이어와 일정 거리 가까워지면 - 힘으로 밀어내는 스크립트

&

  • 물고기의 개개인의 자유도가 너무 떨어짐. 집단이 너무 칼각으로 움직임
  • 물고기 간의 거리 계산도 랜덤 값으로 줬으면 어땠을까?
  • 물고기의 움직임 타겟이 피해야하는 레이어 안에 있으면 물고기들이 고장남. 이것을 어떻게 감지하고 다시 타겟을 정하게 할지 고민해보기
profile
Unity 개발 위주로 정리합니다

0개의 댓글