Script uses transform.position of Prefab instead of Instance

The script is attached to a prefab (“build space”).
I instantiate a number of them at different positions (from a different Script). That works as intended, I get all the instances where I want.
Clicking on one of the Instances opens a UI Menu:

	private void OnMouseDown() {
		if (!EventSystem.current.IsPointerOverGameObject()) {
			Instantiate(buildMenuCanvas, transform.position, transform.rotation);
			Debug.Log("BuildSpace at Position " + transform.position);
		} 	
	}

This works as intended, and the transform.position is also what I expect (i.e. of the instance that was clicked).

Clicking a button on the UI calls the Build function, which should do 3 things:

  • close the UI (works)

  • Destroy the “build space” Instance that was clicked on

  • Instantiate a “building” at the position of the “build space”

     public void Build() {
     	Instantiate(building, transform.position, transform.rotation);
     	Debug.Log("Instantiated Building at " + transform.position);
     	Destroy(GameObject.FindGameObjectWithTag("UIPopup"));
     	Destroy(this.gameObject);
     }
    

The “buildings” get instantiated, but not at the position of the “build space” that was clicked, but at the position of the “build space” Prefab. Also, the “build space” Instance is not destroyed.

It seems the Build function runs on the prefab instead of the instance. (I also get a ‘destroying assets is not permitted’ error). As I said above, in OnMouseDown, the position is correct. Could it be that it is because the referenced Object for the Buttons OnClick is the Prefab?
How can I make sure that the Build function is called on the Instance that was clicked on?

You got it! This is because the referenced Object for the Button’s OnClick is the prefab.

In order for the Button to dynamically call the Build method on the correct “build space” prefab, you need to dynamically assign the OnClick listener at runtime. The precise way to do this is going to differ a bit depending on how you’ve implemented things. Also, unrelatedly, I really recommend having one “buildMenuCanvas” that you turn on/off and move around the screen instead of creating/deleting a new one every time you open the menu—creation/deletion is expensive!

So the gist is going to be:

  1. Give your UI menu controller a public ActivateBuildMenu method, that takes in the BuildSpace script. In this ActivateBuildMenu method:
    • turn the build menu’s gameObject on,
    • change its position based on the screenspace of the build space instance, and
    • assign an onClick listener to the Button that calls the Build method on the passed BuildSpace script (this will tell you how to assign listeners at runtime). Also, have this listener turn the UI menu controller off, so that your BuildSpace doesn’t need to be responsible for the state of UI elements!
  2. In your UI menu controller’s OnDisable, have it clear the button of all listeners.
  3. Change your BuildSpace script to have a serialized reference to your UI menu controller, and have it call ActivateBuildMenu(this) instead of Instantiating the menu object.

Again, the precise implementation can be moved around a bit, but that’s the gist of it.