Seleção de unidade estilo RTS para Unity
Neste tutorial, mostrarei como criar uma seleção de unidade estilo caixa RTS (Real-Time Strategy) em Unity.
Então vamos começar!
Etapa 1: crie os scripts necessários
Esta postagem apresenta 2 scripts:
SC_Selecionável.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);
}
}
Etapa 2: configurar a seleção RTS
- Crie um novo objeto de jogo e chame-o "_SelectionManager"
- Anexe o script SC_SelectionManager ao objeto "_SelectionManager"
- Atribua as texturas abaixo às variáveis de borda:
- Arraste e solte os objetos que você pretende selecionar na cena (no meu caso usei os modelos do pacote RPG/FPS Game Assets for PC/Mobile Industrial Set v2.0 do Asset Store)
- Anexe o script SC_Selectable a cada modelo
- Atribua Mesh Renderer (ou Mesh Renderers se houver vários objetos) à matriz Renderers, isso é necessário para calcular os limites do objeto.
Agora pressione Play e segure o botão do mouse para iniciar a seleção.
Passe o mouse sobre os Objetos selecionáveis para selecioná-los.