Problems with Building Placement RTS style

Hi,

I’m struggeling with the implementation of the building function like in every RTS.

I’ve 2 classes, attached to my main camera that should handel the building.

1.) BuilingsManager, which simply generates the GUI based on avaiable Prefabs:

public class BuildingManager : MonoBehaviour {

    public GameObject[] buildings;
    private BuildingPlacement buildingPlacement;

    // Use this for initialization
    void Start () {
        buildingPlacement = GetComponent<BuildingPlacement>();
    }
  
    // Update is called once per frame
    void Update () {
  
    }

    void OnGUI()
    {
        for(int i = 0; i < buildings.Length; i++)
        {
            if(GUI.Button(new Rect(Screen.width/20, Screen.height/15+Screen.height/12*i, 200,30), buildings[i].name))
            {
                buildingPlacement.SetItem(buildings[i]);
            }
        }
    }
}

And the BuildingPlacement Class, that should build the Structure on the selected point:

using UnityEngine;
using System.Collections;

public class BuildingPlacement : MonoBehaviour
{

    private Transform currentBuilding;
    private bool hasPlaced;

    // Use this for initialization
    void Start()
    {
        Cursor.visible = true;
    }

    // Update is called once per frame
    void Update()
    {

        if (currentBuilding != null && !hasPlaced)
        {

            Vector3 m = Input.mousePosition;
            m = new Vector3(m.x, m.y, transform.position.y);
            Vector3 p = GetComponent<Camera>().ScreenToWorldPoint(m);
            currentBuilding.position = new Vector3(p.x, p.y, p.z);

            if (Input.GetMouseButtonDown(0))
            {
                hasPlaced = true;

            }

        }

    }

    public void SetItem(GameObject b)
    {
        hasPlaced = false;
        currentBuilding = ((GameObject)Instantiate(b)).transform;
    }
}

There’s also the attempt to do this via raycasting:

RaycastHit hit;
            Ray ray = new Ray(transform.position, transform.forward);
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
            {
                specificVector.Set(hit.point.x, hit.collider.transform.position.y, hit.point.z);
                currentBuilding.transform.position = specificVector;
            }

Which could replace the Vector3 m part

My probem here is, that once I clicked on the GUI Button, the Item is placed floating in the air in 4 out of 5 attempts. Addtionally, if it magically allows the item to follow the mouse, it’s built somewhere in the ait instead on the ground.
I’ll attach a short video, so you’ll get a better Idea of the problem:

I hope someone can help me here

Well a lot of how to place a building will depend on how you are handling keeping track of buildings in the world. Have you developed a plan and the code for keeping track of all the buildings you have placed. Do you have some grid that these buildings will snap into? How do you detect if a building is trying to be placed over the top of another building?

I think the easiest way is to have some grid data structure that is holding all the built building info. Initially this grid will be set to all empty. Lets say you chop up your world into squares… All your buildings will need a footprint for how many squares and what shape they are taking up on your grid. Then when your user clicks barracks you use the mouse position to determine where the building would go on the ground and highlight that grid in a different color (say green). If the grid overlaps another building you can highlight the entire thing red. Now you know where you would put the building if they clicked mouse, so you just figure out that transform spot and move it up and left… so it seems like the buliding is hovering over the highlighted area.

But this will require you to develop the grid and building overlap system first.

As to the code you already have. If you just want the building to follow the cursor I think this is strange:

m = new Vector3(m.x,m.y,transform.position.y);

why are you getting a position that is the x,y position of the mouse, and the Z position is the initial barracks Y position? That just seems like a nonsensical position.
You should just use the the original m Vector ( the mouse position) and use that to get p. Don’t modify it, then your barracks will follow the mouse exactly.

Secondly I would avoid making any calls to GetComponent or FindObject in an Update()
You should just make a camera variable and call GetComponent one time in Start. Then use that camera variable to make the call to ScreenToWorldPoint

@takatok
Thank you for your answer.
I will check out this grid method asap. I cahnged the m Vector.3 Part with the Raycast code and now the placement works more precisely.
But any Idea why mostly the buildings are placed ramdomly as soon as I click the GUI Button?

One problem you may be having is the Button Down from clicking the GUI button, might still be getting seen in your Update Function

 if (Input.GetMouseButtonDown(0))
            {
                hasPlaced = true;
            }

Now GetMouseButtonDown is only true on the same frame as its clicked, you can avoid this problem by making SetBuilding wait one frame before building the building with a Coroutine like this:

public void SetItem(GameObject b)
{
        StartCoroutine(CreateBuilding(b));
}

IEnumerator CreateBuilding(GameObject b)
{
        yield return null; // Wait one frame so our button clicks clear
        hasPlaced = false;
        currentBuilding = ((GameObject)Instantiate(b)).transform;
}