{"id":867,"date":"2019-12-12T00:28:19","date_gmt":"2019-12-12T00:28:19","guid":{"rendered":"https:\/\/www.danielparente.net\/en\/2019\/12\/12\/unity-3d-rts-style-unit-selection\/"},"modified":"2019-12-12T00:28:19","modified_gmt":"2019-12-12T00:28:19","slug":"unity-3d-rts-style-unit-selection","status":"publish","type":"post","link":"https:\/\/www.danielparente.net\/en\/2019\/12\/12\/unity-3d-rts-style-unit-selection\/","title":{"rendered":"Unity 3D RTS Style Unit Selection"},"content":{"rendered":"<p> [ad_1]<br \/>\n<\/p>\n<div id=\"item-body-wrapper\">\n<p>In this post I will be showing how to create a RTS (Real-Time Strategy) box style selection in <a href=\"http:\/\/prf.hn\/click\/camref:1011l4LKS\/destination:https:\/\/store.unity.com\/products\/unity-plus\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Unity 3D<\/a>.<\/p>\n<p>So let&#8217;s begin!<\/p>\n<h3>Step 1: Create the necessary scripts<\/h3>\n<p>This post features 2 scripts:<\/p>\n<p><strong>SC_Selectable.cs<\/strong><\/p>\n<pre class=\"language-csharp\"><code>using System.Collections;&#13;\nusing System.Collections.Generic;&#13;\nusing UnityEngine;&#13;\n&#13;\npublic class SC_Selectable : MonoBehaviour&#13;\n{&#13;\n    public Renderer[] renderers; \/\/Assign all child Mesh Renderers&#13;\n&#13;\n    public Bounds GetObjectBounds()&#13;\n    {&#13;\n        Bounds totalBounds = new Bounds();&#13;\n&#13;\n        for(int i = 0; i &lt; renderers.Length; i++)&#13;\n        {&#13;\n            if(totalBounds.center == Vector3.zero)&#13;\n            {&#13;\n                totalBounds = renderers[i].bounds;&#13;\n            }&#13;\n            else&#13;\n            {&#13;\n                totalBounds.Encapsulate(renderers[i].bounds);&#13;\n            }&#13;\n        }&#13;\n&#13;\n        return totalBounds;&#13;\n    }&#13;\n&#13;\n    void OnEnable()&#13;\n    {&#13;\n        \/\/Add this Object to global list&#13;\n        if (!SC_SelectionManager.selectables.Contains(this))&#13;\n        {&#13;\n            SC_SelectionManager.selectables.Add(this);&#13;\n        }&#13;\n    }&#13;\n&#13;\n    void OnDisable()&#13;\n    {&#13;\n        \/\/Remove this Object from global list&#13;\n        if (SC_SelectionManager.selectables.Contains(this))&#13;\n        {&#13;\n            SC_SelectionManager.selectables.Remove(this);&#13;\n        }&#13;\n    }&#13;\n}<\/code><\/pre>\n<p><strong>SC_SelectionManager.cs<\/strong><\/p>\n<pre class=\"language-csharp\"><code>using System.Collections;&#13;\nusing System.Collections.Generic;&#13;\nusing UnityEngine;&#13;\n&#13;\npublic class SC_SelectionManager : MonoBehaviour&#13;\n{&#13;\n    public Texture topLeftBorder;&#13;\n    public Texture bottomLeftBorder;&#13;\n    public Texture topRightBorder;&#13;\n    public Texture bottomRightBorder;&#13;\n&#13;\n    Texture2D _borderTexture;&#13;\n    Texture2D borderTexture&#13;\n    {&#13;\n        get&#13;\n        {&#13;\n            if (_borderTexture == null)&#13;\n            {&#13;\n                _borderTexture = new Texture2D(1, 1);&#13;\n                _borderTexture.SetPixel(0, 0, Color.white);&#13;\n                _borderTexture.Apply();&#13;\n            }&#13;\n&#13;\n            return _borderTexture;&#13;\n        }&#13;\n    }&#13;\n&#13;\n    bool selectionStarted = false;&#13;\n    Vector3 mousePosition1;&#13;\n&#13;\n    public static List&lt;SC_Selectable&gt; selectables = new List&lt;SC_Selectable&gt;();&#13;\n    List&lt;int&gt; selectedObjects = new List&lt;int&gt;();&#13;\n&#13;\n    \/\/ Update is called once per frame&#13;\n    void Update()&#13;\n    {&#13;\n        \/\/ Begin selection&#13;\n        if (Input.GetMouseButtonDown(0))&#13;\n        {&#13;\n            selectionStarted = true;&#13;\n            mousePosition1 = Input.mousePosition;&#13;\n        }&#13;\n        \/\/ End selection&#13;\n        if (Input.GetMouseButtonUp(0))&#13;\n        {&#13;\n            selectionStarted = false;&#13;\n        }&#13;\n&#13;\n        if (selectionStarted)&#13;\n        {&#13;\n            \/\/ Detect which Objects are inside selection rectangle&#13;\n            Camera camera = Camera.main;&#13;\n            selectedObjects.Clear();&#13;\n            for (int i = 0; i &lt; selectables.Count; i++)&#13;\n            {&#13;\n                Bounds viewportBounds = GetViewportBounds(camera, mousePosition1, Input.mousePosition);&#13;\n                if (viewportBounds.Contains(camera.WorldToViewportPoint(selectables[i].transform.position)))&#13;\n                {&#13;\n                    selectedObjects.Add(i);&#13;\n                }&#13;\n            }&#13;\n        }&#13;\n    }&#13;\n&#13;\n    void OnGUI()&#13;\n    {&#13;\n        if (selectionStarted)&#13;\n        {&#13;\n            Rect rect = GetScreenRect(mousePosition1, Input.mousePosition);&#13;\n            DrawScreenRectBorder(rect, 2, Color.cyan);&#13;\n        }&#13;\n&#13;\n        \/\/ Draw selection edges&#13;\n        if(selectedObjects.Count &gt; 0)&#13;\n        {&#13;\n            Camera camera = Camera.main;&#13;\n            for (int i = 0; i &lt; selectedObjects.Count; i++)&#13;\n            {&#13;\n                DrawSelectionIndicator(camera, selectables[selectedObjects[i]].GetObjectBounds());&#13;\n            }&#13;\n        }&#13;\n    }&#13;\n&#13;\n    void DrawScreenRectBorder(Rect rect, float thickness, Color color)&#13;\n    {&#13;\n        \/\/ Top&#13;\n        DrawBorderRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), color);&#13;\n        \/\/ Left&#13;\n        DrawBorderRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), color);&#13;\n        \/\/ Right&#13;\n        DrawBorderRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), color);&#13;\n        \/\/ Bottom&#13;\n        DrawBorderRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), color);&#13;\n    }&#13;\n&#13;\n    void DrawBorderRect(Rect rect, Color color)&#13;\n    {&#13;\n        GUI.color = color;&#13;\n        GUI.DrawTexture(rect, borderTexture);&#13;\n        GUI.color = Color.white;&#13;\n    }&#13;\n&#13;\n    Rect GetScreenRect(Vector3 screenPosition1, Vector3 screenPosition2)&#13;\n    {&#13;\n        \/\/ Move origin from bottom left to top left&#13;\n        screenPosition1.y = Screen.height - screenPosition1.y;&#13;\n        screenPosition2.y = Screen.height - screenPosition2.y;&#13;\n        \/\/ Calculate corners&#13;\n        var topLeft = Vector3.Min(screenPosition1, screenPosition2);&#13;\n        var bottomRight = Vector3.Max(screenPosition1, screenPosition2);&#13;\n        \/\/ Create Rect&#13;\n        return Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);&#13;\n    }&#13;\n&#13;\n    Bounds GetViewportBounds(Camera camera, Vector3 screenPosition1, Vector3 screenPosition2)&#13;\n    {&#13;\n        Vector3 v1 = camera.ScreenToViewportPoint(screenPosition1);&#13;\n        Vector3 v2 = camera.ScreenToViewportPoint(screenPosition2);&#13;\n        Vector3 min = Vector3.Min(v1, v2);&#13;\n        Vector3 max = Vector3.Max(v1, v2);&#13;\n        min.z = camera.nearClipPlane;&#13;\n        max.z = camera.farClipPlane;&#13;\n&#13;\n        Bounds bounds = new Bounds();&#13;\n        bounds.SetMinMax(min, max);&#13;\n        return bounds;&#13;\n    }&#13;\n&#13;\n    void DrawSelectionIndicator(Camera camera, Bounds bounds)&#13;\n    {&#13;\n        Vector3 boundPoint1 = bounds.min;&#13;\n        Vector3 boundPoint2 = bounds.max;&#13;\n        Vector3 boundPoint3 = new Vector3(boundPoint1.x, boundPoint1.y, boundPoint2.z);&#13;\n        Vector3 boundPoint4 = new Vector3(boundPoint1.x, boundPoint2.y, boundPoint1.z);&#13;\n        Vector3 boundPoint5 = new Vector3(boundPoint2.x, boundPoint1.y, boundPoint1.z);&#13;\n        Vector3 boundPoint6 = new Vector3(boundPoint1.x, boundPoint2.y, boundPoint2.z);&#13;\n        Vector3 boundPoint7 = new Vector3(boundPoint2.x, boundPoint1.y, boundPoint2.z);&#13;\n        Vector3 boundPoint8 = new Vector3(boundPoint2.x, boundPoint2.y, boundPoint1.z);&#13;\n&#13;\n        Vector2[] screenPoints = new Vector2[8];&#13;\n        screenPoints[0] = camera.WorldToScreenPoint(boundPoint1);&#13;\n        screenPoints[1] = camera.WorldToScreenPoint(boundPoint2);&#13;\n        screenPoints[2] = camera.WorldToScreenPoint(boundPoint3);&#13;\n        screenPoints[3] = camera.WorldToScreenPoint(boundPoint4);&#13;\n        screenPoints[4] = camera.WorldToScreenPoint(boundPoint5);&#13;\n        screenPoints[5] = camera.WorldToScreenPoint(boundPoint6);&#13;\n        screenPoints[6] = camera.WorldToScreenPoint(boundPoint7);&#13;\n        screenPoints[7] = camera.WorldToScreenPoint(boundPoint8);&#13;\n&#13;\n        Vector2 topLeftPosition = Vector2.zero;&#13;\n        Vector2 topRightPosition = Vector2.zero;&#13;\n        Vector2 bottomLeftPosition = Vector2.zero;&#13;\n        Vector2 bottomRightPosition = Vector2.zero;&#13;\n&#13;\n        for (int a = 0; a &lt; screenPoints.Length; a++)&#13;\n        {&#13;\n            \/\/Top Left&#13;\n            if (topLeftPosition.x == 0 || topLeftPosition.x &gt; screenPoints[a].x)&#13;\n            {&#13;\n                topLeftPosition.x = screenPoints[a].x;&#13;\n            }&#13;\n            if (topLeftPosition.y == 0 || topLeftPosition.y &gt; Screen.height - screenPoints[a].y)&#13;\n            {&#13;\n                topLeftPosition.y = Screen.height - screenPoints[a].y;&#13;\n            }&#13;\n            \/\/Top Right&#13;\n            if (topRightPosition.x == 0 || topRightPosition.x &lt; screenPoints[a].x)&#13;\n            {&#13;\n                topRightPosition.x = screenPoints[a].x;&#13;\n            }&#13;\n            if (topRightPosition.y == 0 || topRightPosition.y &gt; Screen.height - screenPoints[a].y)&#13;\n            {&#13;\n                topRightPosition.y = Screen.height - screenPoints[a].y;&#13;\n            }&#13;\n            \/\/Bottom Left&#13;\n            if (bottomLeftPosition.x == 0 || bottomLeftPosition.x &gt; screenPoints[a].x)&#13;\n            {&#13;\n                bottomLeftPosition.x = screenPoints[a].x;&#13;\n            }&#13;\n            if (bottomLeftPosition.y == 0 || bottomLeftPosition.y &lt; Screen.height - screenPoints[a].y)&#13;\n            {&#13;\n                bottomLeftPosition.y = Screen.height - screenPoints[a].y;&#13;\n            }&#13;\n            \/\/Bottom Right&#13;\n            if (bottomRightPosition.x == 0 || bottomRightPosition.x &lt; screenPoints[a].x)&#13;\n            {&#13;\n                bottomRightPosition.x = screenPoints[a].x;&#13;\n            }&#13;\n            if (bottomRightPosition.y == 0 || bottomRightPosition.y &lt; Screen.height - screenPoints[a].y)&#13;\n            {&#13;\n                bottomRightPosition.y = Screen.height - screenPoints[a].y;&#13;\n            }&#13;\n        }&#13;\n&#13;\n        GUI.DrawTexture(new Rect(topLeftPosition.x - 16, topLeftPosition.y - 16, 16, 16), topLeftBorder);&#13;\n        GUI.DrawTexture(new Rect(topRightPosition.x, topRightPosition.y - 16, 16, 16), topRightBorder);&#13;\n        GUI.DrawTexture(new Rect(bottomLeftPosition.x - 16, bottomLeftPosition.y, 16, 16), bottomLeftBorder);&#13;\n        GUI.DrawTexture(new Rect(bottomRightPosition.x, bottomRightPosition.y, 16, 16), bottomRightBorder);&#13;\n    }&#13;\n}<\/code><\/pre>\n<h3>Step 2: Setup RTS Selection<\/h3>\n<ul>\n<li>Create new Game Object and call it _SelectionManager<\/li>\n<p>&#13;<\/p>\n<li>Attach SC_SelectionManager script to _SelectionManager Object<\/li>\n<p>&#13;<\/p>\n<li>Assign textures below to border variables:<\/li>\n<p>&#13;\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/03d\/03dedd46f018cf568fbe84842046c2d8.png\" alt=\"\" width=\"32\" height=\"32\"\/><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/591\/591c9743187eb44105aaf0556908677f.png\" alt=\"\" width=\"32\" height=\"32\"\/><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/781\/7810fb69bbb0b4c43bc896abf1d450bb.png\" alt=\"\" width=\"32\" height=\"32\"\/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/107\/10717cdfd58f75e0cbe42030282f3f4e.png\" alt=\"\" width=\"32\" height=\"32\"\/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/ad1\/ad1ae2b2354c20b8d09d65fd2e431328.png\" alt=\"\" width=\"419\" height=\"111\"\/><\/p>\n<ul>\n<li>Drag and drop the Objects you intend to select to Scene (In my case I used models from the Asset Store, check the link below)<\/li>\n<p>&#13;\n<\/ul>\n<p><a href=\"https:\/\/assetstore.unity.com\/packages\/3d\/environments\/industrial\/rpg-fps-game-assets-for-pc-mobile-industrial-set-v2-0-86679?aid=1011l4LKS\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">RPG\/FPS Game Assets for PC\/Mobile (Industrial Set v2.0)<\/a><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/b31\/b3124824d99666bbf5ddf00376f9f19d.png\" alt=\"\" width=\"773\" height=\"349\"\/><\/p>\n<ul>\n<li>Attach SC_Selectable script to each model<\/li>\n<p>&#13;<\/p>\n<li>Assign Mesh Renderer (or Mesh Renderers if there is multiple Objects) to Renderers array, this is needed to be able calculate Object bounds.<\/li>\n<p>&#13;\n<\/ul>\n<p>Now press Play and hold mouse button to begin selection.<\/p>\n<p>Hover over the selectable Objects to select them.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sharpcoderblog.com\/public\/images\/b3c\/b3c700354e691f0968071bd48c0587c8.jpg\" alt=\"\" width=\"1000\" height=\"600\"\/><\/p>\n<\/p><\/div>\n<p>[ad_2]<br \/>\n<br \/><a href=\"https:\/\/sharpcoderblog.com\/blog\/unity-3d-rts-style-unit-selection\" target=\"_blank\" rel=\"noopener\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>[ad_1] In this post I will be showing how to create a RTS (Real-Time Strategy) box style selection in Unity 3D. So let&#8217;s begin! Step 1: Create the necessary scripts This post features 2 scripts: SC_Selectable.cs using System.Collections;&#13; using System.Collections.Generic;&#13; using UnityEngine;&#13; &#13; public class SC_Selectable : MonoBehaviour&#13; {&#13; public Renderer[] renderers; \/\/Assign all child [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":868,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_post_was_ever_published":false},"categories":[18],"tags":[],"class_list":["post-867","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gamedev"],"blocksy_meta":[],"jetpack_featured_media_url":"https:\/\/e928cfdc7rs.exactdn.com\/info\/uploads\/sites\/3\/2019\/12\/Unity-3D-RTS-Style-Unit-Selection-scaled.jpg?strip=all","jetpack_shortlink":"https:\/\/wp.me\/p2TFCd-dZ","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/posts\/867","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/comments?post=867"}],"version-history":[{"count":0,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/posts\/867\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/media\/868"}],"wp:attachment":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/media?parent=867"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/categories?post=867"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/tags?post=867"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}