오브젝트 포물선 그리기

ju_hong·2024년 8월 22일
0

포물선, 수학 이용

목록 보기
1/1

시작

우리는 게임을 플레이하다가 보면 종종 화살이나 폭탄이 포물선을 그리며 날라가는 장면을 심심치않게 확인할 수 있다.
나도 언젠가는 구현을 해봐야겠다! 라는 생각을 가졌으나 세시간의 씨름 끝에 결국 쥐선생을 사용하여 구현완료 :)
구현으로 끝나는건 맞지 않으니 로직을 분석해서 해보자

로직 목적

현재 생존하고있는 몬스터들 중에 가장 군집하고 있는 위치를 찾아 해당 포지션에 포물선을 그려 폭탄을 던진다.

그렇다면 순서는
1.군집되어 있는 위치를 찾아 할당
2.해당 위치에 폭탄이 이동하는 로직
3.Physics를 통하여 데미지를 가하는 로직
크게 이러한 플로우로 되어 있다.

 private void OnEnable()
    {
        if (SpawnManager.instance.alive_monsters.Count > 0)
        {
            Vector3 bestPosition = Vector3.zero; // 몬스터가 가장 많이 몰려 있는 곳의 위치
            float bestDensity = 0f; // 가장 많은 밀집도를 가진 구역의 밀집도
        float searchRadius = 7f; // 플레이어 주변의 탐색 반경
        float gridCellSize = 2f; // 밀집도를 계산할 그리드 셀 크기

        Dictionary<Vector3, int> densityMap = new Dictionary<Vector3, int>(); // 위치별 몬스터 밀집도

        foreach (var monster in SpawnManager.instance.alive_monsters)
        {
            // 플레이어와 몬스터 간의 거리 계산
            float playerToMonsterDistance = Vector3.Distance(DB.instance.player.transform.position, monster.transform.position);

            // 플레이어와의 거리가 7 미만인 몬스터만 고려
            if (playerToMonsterDistance < searchRadius)
            {
                // OnAi가 활성화된 몬스터만 고려
                if (!monster.OnAi || DB.instance.player.target == monster)
                {
                    continue; // OnAi가 활성화되지 않은 경우 건너뛰기
                }

                // 그리드 셀 위치 계산 (정규화된 위치)
                Vector3 gridPosition = new Vector3(
                    Mathf.Floor(monster.transform.position.x / gridCellSize) * gridCellSize,
                    Mathf.Floor(monster.transform.position.y / gridCellSize) * gridCellSize,
                    Mathf.Floor(monster.transform.position.z / gridCellSize) * gridCellSize
                );

                // 해당 그리드 셀에 몬스터 추가 (밀집도 계산)
                if (densityMap.ContainsKey(gridPosition))
                {
                    densityMap[gridPosition]++;
                }
                else
                {
                    densityMap[gridPosition] = 1;
                }

                // 가장 밀집된 구역을 업데이트
                if (densityMap[gridPosition] > bestDensity)
                {
                    bestDensity = densityMap[gridPosition];
                    bestPosition = gridPosition;
                }
            }
        }

        // 가장 밀집된 구역의 중심 위치를 target에 할당
        if (bestDensity > 0)
        {
            target = bestPosition; // target은 가장 많이 몰린 곳의 중심 위치
        }
    }

    StartMoveToTarget(target);
}

기존에 내가 작성했던 코드와 쥐선생의 힘이 합쳐진 로직이다.
확인하면 몬스터가 가장 많이 몰려있는 곳을 저장하기 위한 변수 bestPosition이 가장 메인이다.

큰틀로 보면 소환되어 있는 몬스터에서 카메라 내의 범위인 7을 제한하고
밀집도를 계산하기 위한 Dicitionary를 Vector3타입으로 선언한다.
또한 카운팅을 하기 위해 int형으로 값을 할당할 수 있도록 하고.
상대의 위치를 계산할때 그리드를 이용해 계산한다. 여기서 그리드는 3D 공간에서 일정한 크기로 나누는것을 이야기한다.
우리는 이 3D공간의 일정한 크기인 그리드를 디셔너리에 저장하여 가장 많이 저장된 포지션을 사용할 것이다.
이렇게보면 되게 간단한 로직이지만 아직 내 머릿속에서는 쉽지 않았나보다ㅠㅠ..

오브젝트 포물선으로 움직이기

IEnumerator MoveToPositionParabola(Vector3 startPosition, Vector3 targetPosition, float duration, float height)
{
    float elapsed = 0f;

    // 초기 위치 및 타겟 위치의 X 정보만 사용
    float startX = startPosition.x;
    float targetX = targetPosition.x;

    while (elapsed < duration)
    {
        // 경과 시간 비율
        float t = elapsed / duration;

        // X 축에서 선형 이동
        float currentX = Mathf.Lerp(startX, targetX, t);

        // 포물선 형식으로 Z 좌표 계산 (포물선 공식 사용)
        float currentZ = Mathf.Lerp(startPosition.z, targetPosition.z, t);
        float parabolaZOffset = Mathf.Sin(t * Mathf.PI) * height; // 포물선 높이 계산

        // 현재 위치 업데이트 (X는 선형, Z는 포물선)
        transform.position = new Vector3(currentX, transform.position.y, currentZ + parabolaZOffset);

        // 시간 증가
        elapsed += Time.deltaTime;
        yield return null;
    }

    // 도착 위치를 마지막으로 설정 (정확히 타겟에 도달하도록)
    transform.position = new Vector3(targetPosition.x, transform.position.y, targetPosition.z);
}

여기서 우리는 선형보간에 대해서 먼저 알아야한다.
선형보간에서 선형은 Line을 의미하고 보간은 두점을 연결하는 방법이다.
Unity 에서는 선형보간을 제공해주는 메소드가 있는데
바로 Mathf.Lerp다.
첫번째 인자로 시작위치 두번째 인자로 도착 위치를 넣어주고 세번째는 0~1의 값을 넣어 보간이 진행되도록 한다.
그렇게 현재 X와Z축을 움직여 포물선을 그려야 하는 나는
지속된 시간/목표 시간 으로 현재 선형의 위치와 포지션을 넣어 해당 오브젝트가 이동하도록 구현했다.

그리고 수포자였던 나에게 가장 어려운 Mathf.Sin..
과거 취준생일때 3D 프로젝트를 처음 접했을때 사용해본 삼각함수이다.
삼각함수는 Cos,Sin,tan가 있다. 일단 여기서 Sin은 높이를 구하는 공식이다.
또한 Sin의 함수는 2π로 이루어져 있는데 우리는 포물선을 사용할거니 π만 사용한다고 보면 된다.
여기서 Mathf.PI는 반원을 그리고 있으니 우리는 0~1값인 t를 곱해 포물선 높이를 구할 수 있다.
여기서 우리는 포물선을 Z축에서 그려나갈 것이므로 Z축에 선형보간된 벡터 + 삼각함수 Sin을 통해서 계산된 벡터를 더해 포물선을 그리는 것 처럼 표현할 수 있다.

지금 적으면서 느꼈는데 어느정도는 내가 작성할 수 있는 코드였던 것 같다. 하지만 내가 고민한다면 많은 시간을 소요할텐데 시간대비 과연 효율이 있을까 라는 의구심이 들기도 한다.
맥시멈 시간을 잡고 개발을 진행하다가 안되면 GPT를 이용하는 방법으로 진행해야겠다.

profile
카페러버의 게임개발 도전기

0개의 댓글