In this video we'll build a simple unit selection system. It includes single selection, and multiple selections with shift click and also a drag selection.
I've watched a bunch of these systems explained and this one was the clearest and easiest to follow. I fucking LOVE the way that you outline something like a conditional as a whole before you start filling in the conditions, and how you add comments in the script. It's so much easier for a noob like me to follow what you're doing when you say "here we have a few conditions, here's what they all are and what they need to do. Ok, now lets do the first one". So many tutorials just tell you a condition, fill it out, then tell you the next one. Awesome content.
Wow ! This was SO useful ! Thank you a lot man ! I'm currently learning Unity for fun and that was a clear and thorough explanation. I'm really looking forward to your next RTS tutorial. Maybe unit movement ? I am struggling with this one.
Awesome, awesome tutorial! Here's a little tip to clean up some of the UnitSelection code, functionalize adding and removing units from your selection like this: private void addSelection(GameObject unitToAdd) { unitsSelected.Add(unitToAdd); unitToAdd.transform.GetChild(0).gameObject.SetActive(true); unitToAdd.GetComponent().enabled = true; } private void removeSelection(GameObject unitToRemove) { unitsSelected.Remove(unitToRemove); unitToRemove.transform.GetChild(0).gameObject.SetActive(false); unitToRemove.GetComponent().enabled = false; } And then your other functions get a little cleaner like so: public void ClickSelect(GameObject unitToAdd) { DeselectAll(); addSelection(unitToAdd); } public void ShiftClickSelect(GameObject unitToAdd) { if (unitsSelected.Contains(unitToAdd)) { removeSelection(unitToAdd); } else { addSelection(unitToAdd); } } public void DragSelect(GameObject unitToAdd) { if (!unitsSelected.Contains(unitToAdd)) { addSelection(unitToAdd); } }
Awesome, mate! I always run into this.. I finish a tutorial and then look back and find better methods of doing it lol.. Thanks for taking the time to refactor some of it :)
This video was exactly what I was looking for, and you do a great job of explaining things. I come from a JavaScript background, and the way you teach things is great
+1 sub, thank you, we (I'm not the only one I think) want more RTS tutorials, like "give orders to selected units", "building", "ressources", "skills" :)
I know this is an old video, but it is a *W*. It is simple enough to follow that I easily refactored the logic to fit my project. You just got yourself a subscriber.
I AM having issues. I tried capsule collider and mesh collider but still can click select anything. Could you please give me some more hint? I'm using empty game object, then multiple cubes in the object, I added the colliders to the empty game object. Is this not the way?
Diablo? Where is Warcraft? xD This tutorial is a treasure xD It was hard to find one good working or not having outside scripts ... So Thank You :) Hail to the RTS!! xD
Great tutorial! I love the way you code and of explaining. The only really small thing is i don't understand why you didn't set the Deselect() function since you created it. Apart this really nice tutorial!
I have got a problem :( I'am at 9:00 and testing UnitList and Units Selected, I have the number of units on my scene but not the selected units, I think it's not working when I click on them EDIT : found the problem, I have setup my GameObject (soldier/unit) and change the "default" layer to "Clickable"
Great video, was a little fast, but that is perfectly fine when you get such a great outcome like what I got. I have two questions though. Anyway to make it so the units make a formation when you click so they don't try and push into each other all the time when trying to get to the destination? And is there a reason for the Deselect function in the UnitSelections script, there is nothing in it?
First question: Yes I am still currently trying to work out a good way to avoid clumping.. Currently I have something like finding the center point of all units and added that as an offset to each units destination.. it works sorta but not what I want.. When I come up with a good system I'll make a part 2.. 2nd: Yes that was intended to be the deselection method but the setup ended up not using it. Sorry I left it in there.. :P
Sorry Its so fast.. I try to keep my videos under 20 minutes long.. I try to explain everything the best I can so you can pause and copy things down.. and *know* what your copying..
Thanks for the tutorial! I followed it step by step, but I got stuck at 9:20, but when I click on the units, they are not added to the "Units Selected" list. Any idea what I might be missing? edit: got it to work! I had a typo where it assigned the camera
Thanks for this. I know it's been a while, but I was wondering if you had any thoughts on how to ignore enemy units. I tried to add this myself by putting enemy units in a new layer called "enemy", which works great for click and shift-click select, but I this is when I realized drag select doesn't check if units are in the clickable layer. I cannot for the life of me figure out how to check the unit's layer when doing drag select. Any tips would be great!
For anyone stuck on this. First create another type of unit and assign it a different layer other than "Clickable." In the SelectUnits() method of the UnitDrag script, modify it as such: void SelectUnits() { // loop through all the units foreach (var unit in UnitSelections.Instance.unitList) { // if unit is with the dragged rectangle if(selectionBox.Contains(myCam.WorldToScreenPoint(unit.transform.position))) { if(unit.layer == 8) // if any unit is within the dragged area, then add the units UnitSelections.Instance.DragSelect(unit); } } } There's an added if statement that checks the unit's layer. The layers are assigned a value between 0 and 31, my clickable layer was 8. Just match your clickable layer to its number in your layer list.
Man thanks for that tutorial im newbie...So i have questions...I don't know why but my character at start of game use navmesh without selecting them. so I don't need to select them and they move when I click on ground... Some chance to help how to fix it? thank you.
so I ran into an issue at the end. The groundMarker appears on the ground whenever I click, not only when units are selected. I wanted to add an if() statement into the part of unitClick.cs that checks if unitsSelected from UnitSelections.cs has members or not. if its empty, no groundmarker appears. However, Im not able to reference the unitSelected list in the unitClick.cs script. Any ideas? Im a complete fish-out-of-water so the answer is probably painfuly obvious.
sure u can.. you can use the instance reference to it.. if (Input.GetMouseButtonDown(1)) { RaycastHit hit; Ray ray = myCam.ScreenPointToRay(Input.mousePosition); if(Physics.Raycast(ray, out hit, Mathf.Infinity, ground) && UnitSelections.Instance.unitsSelected.Count > 0) { groundMarker.transform.position = hit.point; groundMarker.SetActive(false); groundMarker.SetActive(true); } } so you could add the " && UnitSelections.Instance.unitsSelected.Count > 0 " and then set the marker if u want to
Wrote the same code, but it doesn't work for me, the raycast just doesn't hit the clickable layer. Any idea what's wrong? Followed the steps in the video, except I used cubes instead of capsules...
1) On the right corner of Unity click on "Layers" (just between "Account" and "Layout") 2) Edit a new layer and use the same name "Clickable" 3) On the inspector of your Units/Soldiers/Capsusles... 4) Change the layer default to your new Layer you have created before
thats the tricky part.. i still havent found a great solution but so far i been experimenting with offsets.. so i loop thru the unitsSelected and find the center point of all the units.. then i assign the unit an offset to that center point and move it towards the destination + offset it looks something like this.. but this still needs alot of work foreach (var unit in UnitSelection_og.Instance.unitsSelected) { centerPointOfUnits += unit.transform.position; } centerPointOfUnits /= UnitSelection_og.Instance.unitsSelected.Count; offset = transform.position - centerPointOfUnits; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, ground)) { theAgent.SetDestination(hit.point + offset / 3); }
@@SpawnCampGames but wouldnt that set it to never reach its real destination? even from the first unit that gets there, its not going to arrive at the hit point of your ray. i think the foreach statement has to be rethought. like keeping track of how many units were sent to a location. and then we feed that information to every AI that is heading to the same destination. for every AI that has already reached its destination, we set an offset to every ai that follows. ima game dev myself and maybe we could work on something (41k subs here). the only time we would use the foreach statement, is when we want to know how many units has already reached its designated destination, right?
Hey, grat vid! I was wondering, I'm trying to translate this to the new input system, is there a way to do it or should try a totally different approach?
Sorry mate, I haven't used the new input system yet. I tried it out once and I couldn't get the mouse deltas to update correctly.. might be time for me to revisit it.
@@SpawnCampGames don't worry! Ended up merging like 4 tutorials in one script and finally got it working, only thing left is selecting units when you release the drag selection, but I think it's a layermask thing
@@Jeamar glad u got it working... it wasn't too difficult to swap to the new input system was it? im curious if i should start learning it now or wait until the old system is closer to being deprecated
@@SpawnCampGames I'd say to start now and swap to the new input system, imo it's harder to work around some stuff with it but in the end you can make better and more readable code with it, and also a lot of QOL stuff it brings I still can't make the drag selection work tho lmao EDIT: I was too dumb to realize I wasn't enabling the child representing the unit was selected, so TLDR I made it work lol
I Have error like below : "SetDestination" can only be called on an active agent that has been placed on a NavMesh. UnityEngine.AI.NavMeshAgent:SetDestination(Vector3) UnitMovement:Update() (at Assets/Scripts/UnitMovement.cs:25) any idea ? thanks.
I have random amounts of success with adding/removing units from the Unit List when shift clicking. Sometimes I can shift click them and they are added or removed right away, and sometimes I need to double/triple/quad click a unit for it to add or remove. Regular clicks select the units properly and responsively. If anyone has a suggestion as to why I'd appreciate it!
In case anyone else has this issue, I used GetMouseButton instead of GetMouseButtonDown and it was registering multiple clicks from one mouse press because the frames are so fast.
You may have got the code when the calculations are made to determine if you are dragging a certain way wrong. Maybe review that part of the video and see what you messed up on
if u mean the actual image i just used photoshop / could use gimp and i just drew out with my mouse a circle.. and then a smaller one inside and deleted it.. leaving a stylized little ring.. in unity i think i may have used a 3d Quad rotate it the right way up and dragged the imported 2D sprite onto it, creating a material.. then i may have changed some transparency settings and made prefab out of it..
Gonna comment this for anyone in the future because I was stuck on this for ages: If your drag select code is correct but the UI is acting all weird, change it to a raw image instead of an image. Don't know why this works, maybe because this video is 2 years old.
hey. i really like your channel . could you help me ! i want make a 2D PUZZLE GAME like LIMBO . so i dont know how i can start. i have many problems about physics and creating 2d layers for scenes .. so could you make a tutorial video ? :(
I cant get my units to become selected, I've been working on it for hours. I'm thinking it has something to do with my camera, I'm pretty sure my script is the same as yours. I started a fresh project and went through your tutorial again and still couldn't get them to select. Here's my UnitClick Script just incase it is something I'm overlooking: using UnityEngine; public class UnitClick : MonoBehaviour { private Camera myCam; public LayerMask clickable; public LayerMask ground; void Start() { myCam = Camera.main; } void Update() { if (Input.GetMouseButtonDown(0)) { RaycastHit hit; Ray ray = myCam.ScreenPointToRay(Input.mousePosition); if(Physics.Raycast(ray, out hit, Mathf.Infinity, clickable)) { //if we hit a clickable object if (Input.GetKey(KeyCode.LeftShift)) { //shift clicked UnitSelections.Instance.ShiftClickSelect(hit.collider.gameObject); } else { //normal clicked UnitSelections.Instance.ClickSelect(hit.collider.gameObject); } } else { //if we didn't && we're not shift clicking if(!Input.GetKey(KeyCode.LeftShift)) { UnitSelections.Instance.DeselectAll(); } } } } } I have been looking up how to use debugging to check if my camera is actually sending the raycast but I'm new to all of this some I'm not sure how to add it into my code. Also to note my ground and units are on the correct layers and the units are coming up in the "unit list" just not the "units selected" when clicked on. Any help would be much appreciated :)
@@arickthompson9833 I'm guessing you don't need this info anymore since it was 1 year ago but I forgot to assign the layer masks in the unity window lol
My units are being added when I hold shift and click, if i just click on them it doesn't work. I wrote a debug.log in both the update method in the unit click script and a debug.log in the unit selection after unitsSelected.Add(unitToAdd); and I get the debug message so it's not throwing errors and it's calling the ClickSelect(GameObject unitToAdd). and it's just not adding to the list. Makes no sense that it's working when i hold shift but not just normal clicking. UnitClick script void Update() { if (Input.GetMouseButtonDown(0)) { RaycastHit hit; Ray ray = myCam.ScreenPointToRay(Input.mousePosition); if(Physics.Raycast(ray,out hit, Mathf.Infinity, AiVisibility)) { // If we hit a target if (Input.GetKey(KeyCode.LeftShift)) { UnitSelections.Instance.ShiftClickSelect(hit.collider.gameObject); } else { UnitSelections.Instance.ClickSelect(hit.collider.gameObject); Debug.Log("Unit Selected"); } } UnitSelection script public void ClickSelect(GameObject unitToAdd) { DeselectAll(); unitsSelected.Add(unitToAdd); Debug.Log("Should be working"); } Any help would be appreciated
@@SpawnCampGames Same Problem. Shift works, but without shift it just execute the -> if (!Input.GetKey(KeyCode.LeftShift)) { UnitSelections.Instance.DeselectAll(); } -> in UnitClick. No clue why.
Edit: So i think its all a little bit buggy, I tried this code here for debugging -> if (Physics.Raycast(ray, out hit, 2000, unit)) { Debug.Log("YEP"); if (Input.GetKey(KeyCode.LeftShift)) { UnitSelections.Instance.ShiftClickSelect(hit.collider.gameObject); } else { UnitSelections.Instance.ClickSelect(hit.collider.gameObject); } } else { Debug.Log("NOPE"); if (!Input.GetKey(KeyCode.LeftShift)) { UnitSelections.Instance.DeselectAll(); } } } -> And the Output was NOPE YEP NOPE NOPE NOPE -> for just one click. So the raycasting or something is very buggy here. And when I do shift, it does the same, but it doesnt delete the list because of the shift if statement "if (!Input.GetKey(KeyCode.LeftShift))"
@@xamexer Thanks for beginning to debug.. I never noticed this and I'll take a look this afternoon w/ ur example from above to try to work out whats going on..
@@SpawnCampGames Oh no, I found the issue and I feel ashamed xDDD. My Soldier had Unit Click on them for some reason, thats why the script played multiple times. Dont know how that happend. Thank you so much for the vid haha
I found this video really helpfull but I'm new to unity and like the animations that u made and u cutted the video I did not understand nor I found how to make, and I believe that is giving me some errors because my code is the same as your's but some stuff doesn't happen, could you possibly help me?
Sure, just let me know.. what Error is displayed in the Editor Console Window.. What line numbers does it point to.. And what part of the script.. C# is a strongly typed language.. meaning capitalizations, and punctuations are very important..
Do you have a discord server that maybe I could enter so I can show you what the problem is? But in short words is basically when I drag, the units are not getting selected. And I reviewed the code 3 times and I did not find any diference.
if theres no errors in the console.. then most likely. it is a code issue.. its probably an issue where the gameobject / components aren't set correctly @@tiagopetinga6496
did you bake the navmesh? the unit has a navmeshagent.. and then making sure you set a destination of ur navmesh agent? docs.unity3d.com/ScriptReference/AI.NavMeshAgent.SetDestination.html
Its a bad idea to setup everything before you start showing the code. People wanting to learn need to see why things are being added and what they are as they are needed.
I had a problem and it was because UnitSelection.DeselectAll() was throwing a NullReferenceException. (What was very odd was that *the editor was not showing this exception* it only appeared when I tried debugging). On a whim I tried changing the contents of DeselectAll() to this: public void DeslectAll() { if(unitsSelected.Any()) { unitsSelected.ForEach(unit => unit.transform.GetChild(0).gameObject.SetActive(false)); unitsSelected.Clear(); } } Protecting the ForEach from trying to set the set the Quad to inactive fixed it for me. Hope this helps you. P.S you will need to add using System.Linq; to the file to use .Any()
if u guys have problem with select a object just change if statment this way : if(Mouse.current.leftButton.wasPressedThisFrame) with this its only works just 1 time and selected object is not removing immediately
Hi there! First of all thank you so much for the video! It helped me a lot. I am currently stuck with the Drag Function and I think my problem is with these last few lines void DrawSelection() { if (Input.mousePosition.x < startPosition.x) { selectionBox.xMin = Input.mousePosition.x; selectionBox.xMax = startPosition.x; } else { selectionBox.xMin = startPosition.x; selectionBox.xMax = Input.mousePosition.x; } if (Input.mousePosition.y < startPosition.y) { selectionBox.yMin = Input.mousePosition.y; selectionBox.yMax = startPosition.y; } else { selectionBox.yMin = startPosition.y; selectionBox.yMax = Input.mousePosition.y; } } void SelectedUnits() { foreach (var unit in UnitSelection.Instance.unitList) { if (selectionBox.Contains(myCam.WorldToScreenPoint(unit.transform.position))) { UnitSelection.Instance.DragClickSelect(unit); } } } I made sure that everything is correct but it seems like none of my units are being selected.
Had the same issue, I forgot to place an exclamation mark in UnitSelections script: public void DragSelect(GameObject unitToAdd) { if (!unitsSelected.Contains(unitToAdd)) //