Search Unity

Simple game object instantiation creates multiple GameObjects

Discussion in 'Scripting' started by SirLich, Mar 31, 2016.

?

Was my question easy to read and understand?

Poll closed Apr 7, 2016.
  1. Yes, it was good and easy to read.

    0 vote(s)
    0.0%
  2. Less information please. Simple question = simple answer.

    0 vote(s)
    0.0%
  3. Please provide more information, this was hard to understand.

    0 vote(s)
    0.0%
  4. No, it was badly formatted and hard to read.

    0 vote(s)
    0.0%
Multiple votes are allowed.
  1. SirLich

    SirLich

    Joined:
    Feb 1, 2016
    Posts:
    5
    Hello,
    This is my first post on the Unity forums, so, if I broke any rules please let me know!


    The Backstory:
    I am currently learning Unity, and so many of the things I am building are proof-of-concept. My most recent challenge for myself was learning to instantiate GameObjects at the position of, and when I click on other GameObjects. I built myself a chessboard and modeled some chess pieces. Then, when I click on the chessboard squares, a chess piece should be instantiated and centered on the square I clicked on.

    The problem:
    When I click on a square, instead of instantiating one chess piece, it instead instantiates lots. Maybe 10-20. I can test this by looking in my project window where it shows all the GameObjects present in the scene. After clicking on the board, the number of "rookPiecePrefab(Clone)"'s in the viewer increases by a lot. However, I can visually only see one piece, leading me to believe that all of the new GameObjects are stacked in exactly the same position.

    The Code:
    Each square on the GameBoard is its own GameObject. This is the code I use to create the chess-board:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class BuildBoard : MonoBehaviour {
    6.     public GameObject darkTile;
    7.     public GameObject lightTile;
    8.     private int boardWidth = 8;
    9.     private int boardDepth = 8;
    10.     private int size = 4;
    11.     private bool dark;
    12.     public void createBoard(int x, int y, int z)
    13.     {
    14.         lightTile = Resources.Load("lightGameBoardPrefab") as GameObject;
    15.         darkTile = Resources.Load("darkGameBoardPrefab") as GameObject;
    16.         Debug.Log("createBoard was called!");
    17.         for (int i = 0; i < boardDepth; i++)
    18.         {
    19.             for (int j = 0; j < boardWidth; j++)
    20.             {
    21.                 if (dark)
    22.                 {
    23.                     Instantiate(darkTile, new Vector3(x + (size * j), y, z + (size * i)), Quaternion.identity);
    24.                     dark = false;
    25.                 }
    26.                 else
    27.                 {
    28.                     Instantiate(lightTile, new Vector3(x + (size * j), y, z + (size * i)), Quaternion.identity);
    29.                     dark = true;
    30.                 }
    31.             }
    32.             dark = !dark;
    33.         }
    34.     }
    35. }
    36.  
    The prefab lightGameBoardPrefab and darkGameBoardPrefab (The ones I instantiate to build my board at run time) have an attached script to handle the clicks later on. Here is the script I attach:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class PlaceChessPieces : MonoBehaviour {
    6.     public GameObject piece;
    7.     private int x;
    8.     private int y;
    9.     private int z;
    10.     void Start () {
    11.         Debug.Log("A instance of PlaceChessPieces was initialized");
    12.         piece = Resources.Load("rookPiecePrefab") as GameObject;
    13.     }
    14.     void Update()
    15.     {
    16.         if (Input.GetMouseButtonDown(0))
    17.         {
    18.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    19.             RaycastHit hit;
    20.  
    21.             if (Physics.Raycast(ray, out hit, 100))
    22.             {
    23.                 Vector3 pos = hit.transform.position;
    24.                 Debug.Log(hit.transform.gameObject.name);
    25.                 Instantiate(piece, new Vector3(0,0.5f,0) + pos, Quaternion.Euler(-90, 0, 0));
    26.             }
    27.         }
    28.     }
    29.     void OnMouseDown()
    30.     {
    31.         //Debug.Log("That tickels!");
    32.         //Instantiate(piece, new Vector3(pos,0,0), Quaternion.Euler(-90, 0, 0));
    33.     }
    34. }
    35.  
    The photos:
    seen below is a picture of
    1) My project viewer after clicking 5 times. The number of rookPiecePrefab(clone) shown is around 10% of all the GameObjects that were accidentally created.
    2) My game-view after clicking 5 times.
    . Unity2.PNG
    Unity1.PNG
     
  2. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    969
    Just try to select one, and then move it in the scene view, and you'll be able to see the others below.

    Your problem is that the script that instantiates the pieces are attached to every square of the board. So when you click, every square will instantiate it's own piece.

    You're on the right track with the commented out code in OnMouseDown. This should fire only when the specific square is clicked, and you should get only one piece on the square you clicked.
     
    SirLich likes this.
  3. SirLich

    SirLich

    Joined:
    Feb 1, 2016
    Posts:
    5
    Hello!
    Thanks for the reply and the information. The main issue I have with OnMouseDown is that I can't seem to return the GameObject from it. I need the GameObject I clicked on so that I can, for example, know the position to place the new chess piece, along with other operations. From the reading and exploring I have been doing I have gotten the impression that OnMouseDown cannot return the GameObject, and that it is a relic of old Unity functionality that is on its way out the door. Maybe this is incorrect. As you correctly pointed out, having 64 scripts does not work when every single one of them creates a gameobject. Instantiating the chess board with those scripts attached was old functionality that was necessary to work with the OnMouseDown strategy. I see though, that with my new ray-cast theory that I should change it.
    Thanks again.
    Any tips on getting the GameObject I clicked on returned in the OnMouseDown function?
    Thanks! Cheers!

    ~Liam
     
  4. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    You already have the gameobject, as the script is attached to the tile that you are clicking. In your OnMouseDown function:

    Code (csharp):
    1. Instantiate(piece, transform.position + new Vector3(0,0.5f,0), Quaternion.Euler(-90, 0, 0));
    On any Monobehavior script, 'transform' will return the Transform component of the gameObject that the script is attached to, and 'gameObject' will reference the gameObject directly.

    If you are using the technique of Raycasting from the camera to get your clicks, the script should not reside across multiple individual prefabs, but should exist at a higher level, such as on the Camera or a single empty GameObject in charge of adding pieces. In this case though, OnMouseDown seems more appropriate.