팀 프로젝트 개발 일지:

ggm-_-·2024년 12월 20일
1

TIL (Tody I Learn)

목록 보기
47/51

2024.12.19(목)

  • UnitFormationManager.cs
using System.Collections.Generic;
using UnityEngine;

public class UnitFormationManager
{
    // 병력을 생성하고 배치하는 메서드
    public void CreateAndArrangeUnits(GameObject soldierPrefab, Transform parentTransform, int soldierCount, Vector2Int gridDimensions, string formationType = "Rectangle")
    {
        if (soldierPrefab == null)
        {
            Debug.LogError("Soldier prefab is null!");
            return;
        }

        if (parentTransform == null)
        {
            Debug.LogError("Parent transform is null!");
            return;
        }

        if (gridDimensions.x <= 0 || gridDimensions.y <= 0)
        {
            Debug.LogError("Invalid grid dimensions! Both rows and columns must be greater than 0.");
            return;
        }

        ClearExistingUnits(parentTransform);

        switch (formationType.ToLower())
        {
            case "rectangle":
                ArrangeRectangleFormation(soldierPrefab, parentTransform, soldierCount, gridDimensions);
                break;

            case "triangle":
                ArrangeTriangleFormation(soldierPrefab, parentTransform, soldierCount);
                break;

            default:
                Debug.LogError("Unsupported formation type: " + formationType);
                break;
        }
    }

    // 직사각형 형태로 배치
    private void ArrangeRectangleFormation(GameObject soldierPrefab, Transform parentTransform, int soldierCount, Vector2Int initialGridDimensions)
    {
        Collider soldierCollider = soldierPrefab.GetComponent<CapsuleCollider>();
        if (soldierCollider == null)
        {
            Debug.LogError("Soldier prefab does not have a CapsuleCollider component!");
            return;
        }

        float spacingBuffer = 0.5f; // 최소 간격 버퍼
        float xSpacing = 2 * ((CapsuleCollider)soldierCollider).radius + spacingBuffer; // 가로 간격
        float zSpacing = ((CapsuleCollider)soldierCollider).height + spacingBuffer;    // 세로 간격

        // 비율에 따라 행과 열 계산
        float aspectRatio = (float)initialGridDimensions.x / initialGridDimensions.y;
        int targetColumns = Mathf.CeilToInt(Mathf.Sqrt(soldierCount * aspectRatio));
        int targetRows = Mathf.CeilToInt((float)soldierCount / targetColumns);

        int createdSoldiers = 0;
        for (int i = 0; i < targetRows; i++)
        {
            for (int j = 0; j < targetColumns; j++)
            {
                if (createdSoldiers >= soldierCount)
                {
                    return;
                }

                Vector3 position = parentTransform.position + new Vector3(j * xSpacing, 0, i * zSpacing);
                position = CalculateNonOverlappingPosition(position, soldierPrefab, parentTransform);
                GameObject soldier = Object.Instantiate(soldierPrefab, position, Quaternion.identity, parentTransform);
                soldier.name = $"Soldier_{createdSoldiers + 1}";
                createdSoldiers++;
            }
        }
    }

    // 삼각형 형태로 배치
    private void ArrangeTriangleFormation(GameObject soldierPrefab, Transform parentTransform, int soldierCount)
    {
        float spacingBuffer = 0.5f; // 최소 간격 버퍼

        Collider soldierCollider = soldierPrefab.GetComponent<Collider>();
        if (soldierCollider == null)
        {
            Debug.LogError("Soldier prefab does not have a Collider component!");
            return;
        }

        float xSpacing = soldierCollider.bounds.size.x + spacingBuffer;
        float zSpacing = soldierCollider.bounds.size.z + spacingBuffer;

        int createdSoldiers = 0;
        int rowCount = 0;

        while (createdSoldiers < soldierCount)
        {
            rowCount++;
            int soldiersInRow = rowCount;

            // Ensure at least one soldier in the last row
            if (createdSoldiers + soldiersInRow > soldierCount)
            {
                soldiersInRow = soldierCount - createdSoldiers;
            }

            for (int j = 0; j < soldiersInRow; j++)
            {
                float offsetX = -(soldiersInRow - 1) * xSpacing / 2; // 중심 정렬
                Vector3 position = CalculateNonOverlappingPosition(parentTransform.position + new Vector3(j * xSpacing + offsetX, 0, rowCount * zSpacing), soldierPrefab, parentTransform);
                GameObject soldier = Object.Instantiate(soldierPrefab, position, Quaternion.identity, parentTransform);
                soldier.name = $"Soldier_{createdSoldiers + 1}";
                createdSoldiers++;

                if (createdSoldiers >= soldierCount)
                {
                    return;
                }
            }
        }
    }

    // 콜라이더를 고려한 위치 계산
    private Vector3 CalculateNonOverlappingPosition(Vector3 proposedPosition, GameObject soldierPrefab, Transform parentTransform)
    {
        Collider prefabCollider = soldierPrefab.GetComponent<CapsuleCollider>();
        if (prefabCollider == null)
        {
            return proposedPosition;
        }

        Vector3 offset = Vector3.zero;
        int maxAttempts = 10;
        float minimumSpacing = ((CapsuleCollider)prefabCollider).radius * 2 + 0.5f; // 최소 간격

        for (int attempt = 0; attempt < maxAttempts; attempt++)
        {
            bool isOverlapping = false;

            foreach (Transform child in parentTransform)
            {
                Collider childCollider = child.GetComponent<CapsuleCollider>();
                if (childCollider != null)
                {
                    float distance = Vector3.Distance(
                        childCollider.ClosestPoint(proposedPosition + offset),
                        proposedPosition + offset
                    );

                    if (distance < minimumSpacing)
                    {
                        isOverlapping = true;
                        offset += new Vector3(0.1f, 0, 0.1f);
                        break;
                    }
                }
            }

            if (!isOverlapping)
            {
                return proposedPosition + offset;
            }
        }

        Debug.LogWarning("Could not find a non-overlapping position after max attempts.");
        return proposedPosition + offset;
    }

    // 기존 병사들을 제거하는 메서드
    private void ClearExistingUnits(Transform parentTransform)
    {
        foreach (Transform child in parentTransform)
        {
            Object.Destroy(child.gameObject);
        }
    }

    // 병력 프리셋 클래스
    public class FormationPreset
    {
        public string Name { get; private set; }
        public Vector2Int GridDimensions { get; private set; }

        public FormationPreset(string name, Vector2Int gridDimensions)
        {
            Name = name;
            GridDimensions = gridDimensions;
        }
    }

    // 프리셋 리스트를 제공하는 메서드
    public List<FormationPreset> GetAvailablePresets()
    {
        return new List<FormationPreset>
        {
            new FormationPreset("Square Formation", new Vector2Int(5, 5)),
            new FormationPreset("Line Formation", new Vector2Int(10, 1)),
            new FormationPreset("Rectangle Formation", new Vector2Int(8, 3)),
            new FormationPreset("Triangle Formation", new Vector2Int(0, 0)) // Triangle doesn't need grid dimensions
        };
    }
}
profile
미숙한 초보 게임 개발자

2개의 댓글

comment-user-thumbnail
5일 전

아... 코드 너무많이 봐서 현기증날거 같아요....

1개의 답글