3D Real-Time-Strategy (RTS) Game Tutorial – Unity3D (C#).
Part I: Building Placement
In this tutorial series, we will develop the RTS basics such as building placement, camera controls, unit controls, creating units etc. Lets start with Building placement.
As you know from legends of RTS games, some different buildings can be built.
Open Unity and create a new 3D project. Most of RTS games have a top camera. So, create a terrain, create a directional light and change camera position for looking downward. You can add grass texture to terrain if you want. Also, you can use some building objects or just cubes for now. I used a street package named “Cartoon City” by Razu Ahmed ( http://bit.ly/1cmYnX2 ) that you can find out lots of objects. Add 2 buildings to the scene and create 2 empty game objects as their parents. For each parent, add Collider and Rigidbody. Create prefabs and delete objects in scene.
First of all, we need a Building class which includes health, game object and name. Create a C# script named Building.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class Buildings{ public int health; public string name; public GameObject avatar; public Buildings(int _health, string _name, GameObject _avatar){ health = _health; name = _name; avatar = _avatar; } } |
Add Generic library because we are going to use list type.
1 |
using System.Collections.Generic; |
Now, create a list for game objects and a list for Building class objects.
1 2 3 |
public List<Building> buildingList = new List<Building> (); public Buildings newBuilding; public GameObject[] buildings; |
We are going to add 3D building objects manually. Create buildings according to these objects.
1 2 3 4 5 6 7 |
void Start(){ for (int i = 0; i < buildings.Length; i ++) { newBuilding = new Buildings (100, buildings[i].name, buildings[i] ); buildingList.Add(newBuilding); } } |
That’s it. Our building class is ready. Now, create 3 C# scripts to check availability, place buildings and create GUI: Available, Placement, Manager. Open Manager script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private Building buildingClass; private bool isClickable; void Start () { buildingClass = GetComponent<Building> (); isClickable = true; } public void SetClickable(bool w){ isClickable = w; } void OnGUI(){ for (int i = 0; i < buildingClass.buildings.Length; i ++) { if (GUI.Button(new Rect(50 * 2 * i, 100 , 75, 30), buildingClass.buildings[i].name) && isClickable){ isClickable = false; } } } |
This script gets names from Building class and creates buttons.
Open Available script. Add generic library for using lists.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public List<Collider> colliders = new List<Collider> (); private bool isSelected; private Building buildingClass; void Start () { buildingClass = GetComponent<Building> (); } void OnTriggerEnter(Collider c){ if (c.tag == "Building") { colliders.Add(c); } } void OnTriggerExit(Collider c){ if (c.tag == "Building") { colliders.Remove(c); } } public void SetSelected(bool b){ isSelected = b; } void OnGUI(){ if (isSelected) { GUI.Button(new Rect(100, 200, 100, 50), name); } } |
Collider list keeps count of colliding that provides if it is available to place a building. Count increase if the new building collides any object that has Building tag and count decrease if the new building exits from the collision. Now open Placement script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
public LayerMask bMask; private Transform currentBuilding; private Available available; private bool isPlaced; private Available availableX; private Building newBuilding; private Manager clickable; void Update () { Vector3 mPos = Input.mousePosition; mPos = new Vector3 (mPos.x, mPos.y, transform.position.y); Vector3 pos = camera.ScreenToWorldPoint (mPos); if (currentBuilding != null && !isPlaced) { currentBuilding.position = new Vector3 (pos.x, 0, pos.z); if (Input.GetMouseButtonDown (0)) { if (IsAvailable ()) { isPlaced = true; clickable = gameObject.GetComponent<Manager> (); clickable.SetClickable(true); } } } else { if (Input.GetMouseButtonDown(0)){ RaycastHit hit = new RaycastHit(); Ray ray = new Ray(new Vector3(pos.x, 10, pos.z), Vector3.down); if (Physics.Raycast(ray, out hit, Mathf.Infinity, bMask)){ if(availableX != null){ availableX.SetSelected(false); } hit.collider.gameObject.GetComponent<Available> ().SetSelected(true); availableX = hit.collider.gameObject.GetComponent<Available> (); } else{ if(availableX != null){ availableX.SetSelected(false); } } } } } public void SetItem(GameObject g){ isPlaced = false; currentBuilding = ((GameObject)Instantiate(g)).transform; available = currentBuilding.GetComponent<Available> (); } bool IsAvailable(){ if (available.colliders.Count > 0) { return false; } return true; } |
In this section, we assign the building position when it is instantiated by clicking the button via SetItem() function. Also, we get the function when clicking on a building with ray. It is just like an invisible laser from camera to the downward. It returns something if it hits any object. IsAvailable() checks if the new building collides anything. Open Manager script and edit it as seen below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private Building buildingClass; private Placement placement; private bool isClickable; void Start () { placement = GetComponent<Placement> (); buildingClass = GetComponent<Building> (); isClickable = true; } public void SetClickable(bool w){ isClickable = w; } void OnGUI(){ for (int i = 0; i < buildingClass.buildings.Length; i ++) { if (GUI.Button(new Rect(50 * 2 * i, 100 , 75, 30), buildingClass.buildings[i].name) && isClickable){ isClickable = false; placement.SetItem(buildingClass.buildings[i]); } } } |
Now, attach Manager, Placement and Building to Main Camera and attach Available to each prefab. Do not forget to assign your prefabs. Also you can add grid to your buildings. Create a Grid script and attach it to each prefab. For more information about Grid function, see Grid Tutorial.
You can download the source code here.
©Coffee Break Codes – 3D Real Time Strategy Game Tutorial – Unity3D (C#)
Any hope for part 2?
Will be:)
Abi başarılarının devamını diliyorum 🙂
Cok tesekkur ederim:)
Hello. Please make part 2. I been searching for this ages and i found one 🙂 and please teach how to do AI at the end :).
Hi I have followed this to the letter but I am getting this error: ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
System.Collections.Generic.List`1[Building+Buildings].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/List.cs:633)
Building.Start () (at Assets/Scripts/Building.cs:32)
Any Ideas why this is happening or is it because it’s not finished yet?
Thanks.
Did you use generic library and did you add buildings manually to the public buildings list?
In the placement script I’m getting a error with this line: ‘Vector3 pos = camera.ScreenToWorldPoint(mPos);’.
I think it’s because i need to use GetComponent(). But that isn’t working either.
Any ideas?
Also when you place the Availible script onto the building what do you place in the colliders part?
Fixed the camera part. I needed to change that line to: Vector3 pos = GetComponent().ScreenToWorldPoint (mPos);’
Excellent work! Thank you for sharing the very useful tutorial.
Any have this small project generate on MEGA ?
please I need this