- Home /
[C#] Check position of multiple GameObjects with mouse click position
Hey guys,
I'm really new to programming and I've been bashing my head against the wall with this. I've searched for existing questions/answers but seeing as I don't fully understand this, they're of very little help as I get errors I have no idea how to fix.
The goal: (Imagine a Plants vs Zombies style game) When placing a defender, first check to see if there's already a defender in that square. If not, place a defender. If so, communicate that it's not allowed.
My question: How do I store the positions of the objects in the placedDefenders array and then check them against the position of the mouse click (roundedPos)?
I have the GameObject array working properly, but then figuring out how to get the positions of those objects is where I come undone (let alone checking them against the mouse click position).
Here's my script, I've deleted all the crud where I was trying to store the positions in another array because it was getting very sloppy. I hope it makes sense.
I appreciate any help.
using UnityEngine;
using System.Collections;
public class DefenderSpawner : MonoBehaviour {
Camera myCamera;
GameObject defenderParent;
void Start () {
myCamera = Camera.main;
defenderParent = GameObject.Find ("Defenders");
if (defenderParent == null) {
defenderParent = new GameObject ("Defenders");
}
}
void OnMouseDown () {
// Coordinate stuff
Vector2 rawPos = CalculateWorldPointOfMouseClick ();
Vector2 roundedPos = SnapToGrid (rawPos);
// Arrays for placement checking
Vector3[] placedDefenderPositions;
GameObject[] placedDefenders;
placedDefenders = GameObject.FindGameObjectsWithTag ("Defender");
// Instantiate Defender and parent it in hierarchy
GameObject defender = Instantiate (Button.selectedDefender, roundedPos, Quaternion.identity) as GameObject;
defender.transform.parent = defenderParent.transform;
}
Vector2 SnapToGrid (Vector2 rawWorldPos) {
float newX = Mathf.RoundToInt (rawWorldPos.x);
float newY = Mathf.RoundToInt (rawWorldPos.y);
return new Vector2 (newX, newY);
}
Vector2 CalculateWorldPointOfMouseClick () {
float mouseX = Input.mousePosition.x;
float mouseY = Input.mousePosition.y;
float distanceFromCamera = 10f;
Vector3 cameraPoints = new Vector3 (mouseX, mouseY, distanceFromCamera);
Vector2 worldPos = myCamera.ScreenToWorldPoint (cameraPoints);
return worldPos;
}
}
Answer by alexi123454 · Jul 22, 2015 at 03:37 PM
You can get the position of any gameObject in Unity by calling theGameObject.transform.position. This gives you the world-space position of the object. You could check them against the mouse position by doing a for loop like so:
for (int i=0;i<placedDefenders.Length;i++)
{
Vector3 objectPosition = placedDefenders[i].transform.position;
Vector2 objectPosition2D = new Vector2(objectPosition.x, objectPosition.y);
if (SnapToGrid(objectPosition2D) == roundedPos)
{
//One of the defenders is in this spot!
NotifyPlayerFunction();
break;
}
}
Thank you very much for the reply, alexi123454. I didn't know how to convert the Vector3 to the Vector2 and the roundedPos check makes sense to me in seeing it, but I wouldn't have been able to come up with that myself.
Below is my updated On$$anonymous$$ouseDown method. I've just added debug logs to figure out how it works and while it logs out correctly, it's still placing the defender in an occupied spot. If I move the instantiate code into the for loop it doesn't log anything or spawn the defender, either way. I tried adding the instantiate code to an else statement but that didn't work either. I suspect it's to do with me not understanding how a break works. Would you $$anonymous$$d explaining where I would instantiate the defender, please?
void On$$anonymous$$ouseDown () {
// Coordinate stuff
Vector2 rawPos = CalculateWorldPointOf$$anonymous$$ouseClick ();
Vector2 roundedPos = SnapToGrid (rawPos);
// Arrays for placement checking
GameObject[] placedDefenders;
placedDefenders = GameObject.FindGameObjectsWithTag ("Defender");
for (int i=0; i<placedDefenders.Length; i++) {
Vector3 objectPosition = placedDefenders [i].transform.position;
Vector2 objectPosition2D = new Vector2 (objectPosition.x, objectPosition.y);
if (SnapToGrid (objectPosition2D) == roundedPos) {
//One of the defenders is in this spot!
//NotifyPlayerFunction();
Debug.Log ("Placement is bad");
break;
}
}
// Instantiate Defender and parent it in hierarchy
GameObject defender = Instantiate (Button.selectedDefender, roundedPos, Quaternion.identity) as GameObject;
defender.transform.parent = defenderParent.transform;
Debug.Log ("Placement is fine");
}
Break is just a keyword that will break out of any loop that the program is currently in. In the case of the code I wrote for you, the break statement would exit the for loop after an object has been found, meaning it doesn't have to check the rest of the objects.
The reason your code is still spawning the defender in the occupied spot is because, even after it finds that the placement is bad, the function continues along, and still spawns the defender afterward.
The easiest way to solve this would be to replace "break;" with "return;". This will cause the function to stop, rather than just the for loop, meaning that if an object is found it the spot, the program will never actually get to the point where the defender is spawned.
For the sake of clean code, I would also split up some of the stuff that you function is doing. Try something like this:
void On$$anonymous$$ouseDown ()
{
// Coordinate stuff
Vector2 rawPos = CalculateWorldPointOf$$anonymous$$ouseClick ();
Vector2 roundedPos = SnapToGrid (rawPos);
// Arrays for placement checking
GameObject[] placedDefenders;
placedDefenders = GameObject.FindGameObjectsWithTag ("Defender");
for (int i=0; i<placedDefenders.Length; i++)
{
Vector3 objectPosition = placedDefenders [i].transform.position;
Vector2 objectPosition2D = new Vector2 (objectPosition.x, objectPosition.y);
if (SnapToGrid (objectPosition2D) == roundedPos)
{
BadPlacement();
return;
}
}
GoodPlacement();
}
void BadPlacement()
{
Debug.log("Placement is bad");
}
void GoodPlacement(Vector2 aPosition)
{
Debug.log("Placement is good");
GameObject defender = (GameObject)Instantiate(Button.selectedDefender, aPosition, Quaternion.identity);
defender.transform.parent = defenderParent.transform;
}
Generally, it's a good idea to make a function only do one thing. This way, you have one function to do the position check, and then one function to handle the spawning of the object. This also allows you to put different functionality easily (if you want to pop up a text box saying "Hey, you can't put a defender there!" or something like that).
Thank you very much for the effort and explanations. After passing roundedPos into GoodPlacement() it works perfectly.
I really appreciate the time you took to not only solve my issue but make everything clear as well.
Just out of curiosity, is it possible to only mark your most recent reply as the answer?
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
How do I smoothly sync players position over the network in Unity3D(UNet) without bugs? 0 Answers
Locating proper gameObject to which player has to jump 1 Answer
How to get the position that is closest to a target within a given radius 0 Answers