Unity용 RTS 스타일 단위 선택

이 튜토리얼에서는 Unity에서 RTS(실시간 전략) 상자 스타일 유닛 선택을 만드는 방법을 보여 드리겠습니다.

Sharp Coder 비디오 플레이어

그럼 시작해보자!

1단계: 필요한 스크립트 생성

이 게시물에는 2개의 스크립트가 포함되어 있습니다.

SC_Selectable.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SC_Selectable : MonoBehaviour
{
    public Renderer[] renderers; //Assign all child Mesh Renderers

    public Bounds GetObjectBounds()
    {
        Bounds totalBounds = new Bounds();

        for(int i = 0; i < renderers.Length; i++)
        {
            if(totalBounds.center == Vector3.zero)
            {
                totalBounds = renderers[i].bounds;
            }
            else
            {
                totalBounds.Encapsulate(renderers[i].bounds);
            }
        }

        return totalBounds;
    }

    void OnEnable()
    {
        //Add this Object to global list
        if (!SC_SelectionManager.selectables.Contains(this))
        {
            SC_SelectionManager.selectables.Add(this);
        }
    }

    void OnDisable()
    {
        //Remove this Object from global list
        if (SC_SelectionManager.selectables.Contains(this))
        {
            SC_SelectionManager.selectables.Remove(this);
        }
    }
}

SC_SelectionManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SC_SelectionManager : MonoBehaviour
{
    public Texture topLeftBorder;
    public Texture bottomLeftBorder;
    public Texture topRightBorder;
    public Texture bottomRightBorder;

    Texture2D _borderTexture;
    Texture2D borderTexture
    {
        get
        {
            if (_borderTexture == null)
            {
                _borderTexture = new Texture2D(1, 1);
                _borderTexture.SetPixel(0, 0, Color.white);
                _borderTexture.Apply();
            }

            return _borderTexture;
        }
    }

    bool selectionStarted = false;
    Vector3 mousePosition1;

    public static List<SC_Selectable> selectables = new List<SC_Selectable>();
    List<int> selectedObjects = new List<int>();

    // Update is called once per frame
    void Update()
    {
        // Begin selection
        if (Input.GetMouseButtonDown(0))
        {
            selectionStarted = true;
            mousePosition1 = Input.mousePosition;
        }
        // End selection
        if (Input.GetMouseButtonUp(0))
        {
            selectionStarted = false;
        }

        if (selectionStarted)
        {
            // Detect which Objects are inside selection rectangle
            Camera camera = Camera.main;
            selectedObjects.Clear();
            for (int i = 0; i < selectables.Count; i++)
            {
                Bounds viewportBounds = GetViewportBounds(camera, mousePosition1, Input.mousePosition);
                if (viewportBounds.Contains(camera.WorldToViewportPoint(selectables[i].transform.position)))
                {
                    selectedObjects.Add(i);
                }
            }
        }
    }

    void OnGUI()
    {
        if (selectionStarted)
        {
            Rect rect = GetScreenRect(mousePosition1, Input.mousePosition);
            DrawScreenRectBorder(rect, 2, Color.cyan);
        }

        // Draw selection edges
        if(selectedObjects.Count > 0)
        {
            Camera camera = Camera.main;
            for (int i = 0; i < selectedObjects.Count; i++)
            {
                DrawSelectionIndicator(camera, selectables[selectedObjects[i]].GetObjectBounds());
            }
        }
    }

    void DrawScreenRectBorder(Rect rect, float thickness, Color color)
    {
        // Top
        DrawBorderRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), color);
        // Left
        DrawBorderRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), color);
        // Right
        DrawBorderRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), color);
        // Bottom
        DrawBorderRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), color);
    }

    void DrawBorderRect(Rect rect, Color color)
    {
        GUI.color = color;
        GUI.DrawTexture(rect, borderTexture);
        GUI.color = Color.white;
    }

    Rect GetScreenRect(Vector3 screenPosition1, Vector3 screenPosition2)
    {
        // Move origin from bottom left to top left
        screenPosition1.y = Screen.height - screenPosition1.y;
        screenPosition2.y = Screen.height - screenPosition2.y;
        // Calculate corners
        var topLeft = Vector3.Min(screenPosition1, screenPosition2);
        var bottomRight = Vector3.Max(screenPosition1, screenPosition2);
        // Create Rect
        return Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
    }

    Bounds GetViewportBounds(Camera camera, Vector3 screenPosition1, Vector3 screenPosition2)
    {
        Vector3 v1 = camera.ScreenToViewportPoint(screenPosition1);
        Vector3 v2 = camera.ScreenToViewportPoint(screenPosition2);
        Vector3 min = Vector3.Min(v1, v2);
        Vector3 max = Vector3.Max(v1, v2);
        min.z = camera.nearClipPlane;
        max.z = camera.farClipPlane;

        Bounds bounds = new Bounds();
        bounds.SetMinMax(min, max);
        return bounds;
    }

    void DrawSelectionIndicator(Camera camera, Bounds bounds)
    {
        Vector3 boundPoint1 = bounds.min;
        Vector3 boundPoint2 = bounds.max;
        Vector3 boundPoint3 = new Vector3(boundPoint1.x, boundPoint1.y, boundPoint2.z);
        Vector3 boundPoint4 = new Vector3(boundPoint1.x, boundPoint2.y, boundPoint1.z);
        Vector3 boundPoint5 = new Vector3(boundPoint2.x, boundPoint1.y, boundPoint1.z);
        Vector3 boundPoint6 = new Vector3(boundPoint1.x, boundPoint2.y, boundPoint2.z);
        Vector3 boundPoint7 = new Vector3(boundPoint2.x, boundPoint1.y, boundPoint2.z);
        Vector3 boundPoint8 = new Vector3(boundPoint2.x, boundPoint2.y, boundPoint1.z);

        Vector2[] screenPoints = new Vector2[8];
        screenPoints[0] = camera.WorldToScreenPoint(boundPoint1);
        screenPoints[1] = camera.WorldToScreenPoint(boundPoint2);
        screenPoints[2] = camera.WorldToScreenPoint(boundPoint3);
        screenPoints[3] = camera.WorldToScreenPoint(boundPoint4);
        screenPoints[4] = camera.WorldToScreenPoint(boundPoint5);
        screenPoints[5] = camera.WorldToScreenPoint(boundPoint6);
        screenPoints[6] = camera.WorldToScreenPoint(boundPoint7);
        screenPoints[7] = camera.WorldToScreenPoint(boundPoint8);

        Vector2 topLeftPosition = Vector2.zero;
        Vector2 topRightPosition = Vector2.zero;
        Vector2 bottomLeftPosition = Vector2.zero;
        Vector2 bottomRightPosition = Vector2.zero;

        for (int a = 0; a < screenPoints.Length; a++)
        {
            //Top Left
            if (topLeftPosition.x == 0 || topLeftPosition.x > screenPoints[a].x)
            {
                topLeftPosition.x = screenPoints[a].x;
            }
            if (topLeftPosition.y == 0 || topLeftPosition.y > Screen.height - screenPoints[a].y)
            {
                topLeftPosition.y = Screen.height - screenPoints[a].y;
            }
            //Top Right
            if (topRightPosition.x == 0 || topRightPosition.x < screenPoints[a].x)
            {
                topRightPosition.x = screenPoints[a].x;
            }
            if (topRightPosition.y == 0 || topRightPosition.y > Screen.height - screenPoints[a].y)
            {
                topRightPosition.y = Screen.height - screenPoints[a].y;
            }
            //Bottom Left
            if (bottomLeftPosition.x == 0 || bottomLeftPosition.x > screenPoints[a].x)
            {
                bottomLeftPosition.x = screenPoints[a].x;
            }
            if (bottomLeftPosition.y == 0 || bottomLeftPosition.y < Screen.height - screenPoints[a].y)
            {
                bottomLeftPosition.y = Screen.height - screenPoints[a].y;
            }
            //Bottom Right
            if (bottomRightPosition.x == 0 || bottomRightPosition.x < screenPoints[a].x)
            {
                bottomRightPosition.x = screenPoints[a].x;
            }
            if (bottomRightPosition.y == 0 || bottomRightPosition.y < Screen.height - screenPoints[a].y)
            {
                bottomRightPosition.y = Screen.height - screenPoints[a].y;
            }
        }

        GUI.DrawTexture(new Rect(topLeftPosition.x - 16, topLeftPosition.y - 16, 16, 16), topLeftBorder);
        GUI.DrawTexture(new Rect(topRightPosition.x, topRightPosition.y - 16, 16, 16), topRightBorder);
        GUI.DrawTexture(new Rect(bottomLeftPosition.x - 16, bottomLeftPosition.y, 16, 16), bottomLeftBorder);
        GUI.DrawTexture(new Rect(bottomRightPosition.x, bottomRightPosition.y, 16, 16), bottomRightBorder);
    }
}

2단계: RTS 선택 설정

  • 새로운 게임 객체를 생성하고 호출하세요. "_SelectionManager"
  • SC_SelectionManager 스크립트를 "_SelectionManager" 개체에 연결합니다.
  • 아래 텍스처를 테두리 변수에 할당합니다.

  • SC_Selectable 스크립트를 각 모델에 첨부
  • 렌더러 배열에 메시 렌더러(또는 여러 개체가 있는 경우 메시 렌더러)를 할당합니다. 이는 개체 경계를 계산하는 데 필요합니다.

이제 Play를 누르고 마우스 버튼을 누르고 있으면 선택이 시작됩니다.

선택 가능한 Objects 위에 마우스를 올려 선택하세요.

추천 기사
Unity용 2D 근접 공격 튜토리얼
Zone Controller Pro - Unity 에셋 스토어 패키지
Unity에서 새로운 HDRP 급수 시스템을 사용하는 방법
FPC Swimmer - 몰입형 수중 환경을 위한 포괄적인 Unity 에셋
Ultimate Spawner 2.0 - 판도를 바꾸는 자산
Unity용 마우스 보기 스크립트
Weather Maker - Unity 환경을 새로운 차원으로 끌어올리다