Search Unity

  1. New Unity Live Help updates. Check them out here!

    Dismiss Notice

Instantiating a text panel at positions of other gameobjects.

Discussion in 'Scripting' started by dreamtocode77, Mar 25, 2020.

  1. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    Hey friends!

    Currently I have a system set up that allows me to pass strings through the "Setbattlers" function and as long as there is prefab with the same name it will load that prefab into one of the 2 positions. Cool!

    Now, what I want to do next is have a simple panel prefab with a text component instantiated for every active enemy prefab in the scene (currently 2) at the said prefabs location with the text displaying the name of the prefab.

    I am on the right path but I need assistance with some issues:

    1. Currently, 2 enemy prefabs are instantiated on play but for some reason 3 panel prefabs are being instantiated. I have an array of 3 different prefabs but will only be loading 2 of them in. I thought that maybe it was loading a panel for the inactive prefab so I removed it to test it and nope, still instantiated 3.

    2. When panels instantiate they all do so at the center and no at the position of the prefabs and stacked on top even though I have the position as the same as the enemy prefabs.

    3. Have not tried text part yet but since I know its something i will be doing in the future I might as well as now.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. [RequireComponent(typeof(BaseStats)), RequireComponent(typeof(SetHUD))]
    7. public class BattleManager : MonoBehaviour
    8. {
    9.     public BaseStats[] Enemies;
    10.     public Transform[] Positions;
    11.     public GameObject EnemyHUB;
    12.  
    13.     GameObject battleScene;
    14.     public bool battleengaged;
    15.  
    16.     SetHUD setHUD;
    17.  
    18.  
    19.     public List<BaseStats> activeBattlers = new List<BaseStats>();
    20.  
    21.     // Start is called before the first frame update
    22.     void Start()
    23.     {
    24.         battleScene = GameObject.Find("BattleScene");
    25.         battleScene.SetActive(false);
    26.         setHUD = GetComponent<SetHUD>();
    27.     }
    28.  
    29.     // Update is called once per frame
    30.     void Update()
    31.     {
    32.         if (!battleengaged)
    33.         {
    34.             if (Input.GetKeyDown(KeyCode.T))
    35.             {
    36.                 BattleStart();
    37.                 SetBattlers(new string[] { "Slogra", "Gaibon" });
    38.             }
    39.         }
    40.     }
    41.  
    42.     void BattleStart()
    43.     {
    44.         battleScene.transform.position = new Vector3(Camera.main.transform.position.x, Camera.main.transform.position.y, transform.position.z);
    45.         battleScene.SetActive(true);
    46.         battleengaged = true;
    47.     }
    48.  
    49.     void SetBattlers(string[] enemiestospawn)
    50.     {
    51.         for (int i = 0; i < enemiestospawn.Length; i++)
    52.         {
    53.             if (enemiestospawn[i] != "")
    54.             {
    55.                 for (int j = 0; j < Enemies.Length; j++)
    56.                 {
    57.                     if (Enemies[j].Name == enemiestospawn[i])
    58.                     {
    59.                         BaseStats newEnemy = Instantiate(Enemies[j], Positions[i].position, Positions[i].rotation);
    60.                         newEnemy.transform.parent = Positions[i];
    61.                         activeBattlers.Add(newEnemy);
    62.  
    63.                         foreach (BaseStats o in activeBattlers)
    64.                         {
    65.                             GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation);
    66.                             HUD.transform.SetParent(GameObject.Find("Canvas").transform, false);
    67.                         }
    68.                     }
    69.                 }
    70.             }
    71.         }

    EnemyHUB code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class SetHUD : MonoBehaviour
    7. {
    8.     Text hudtext;
    9.  
    10.     public void UpdateHUD(BaseStats baseStats)
    11.     {
    12.         hudtext = GameObject.Find("EnemyHUD").GetComponentInChildren<Text>();
    13.         hudtext.text = baseStats.Name;
    14.     }
    15. }
     
  2. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    Still going at this -

    What is the correct way to do this? It seems like it should work but its not or it kinda of is but Instantiating too many at wrong location. Should I even be using a foreach loop?

    Code (CSharp):
    1. foreach (BaseStats o in activeBattlers)
    2.                         {
    3.                             GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation);
    4.                             HUD.transform.SetParent(GameObject.Find("Canvas").transform, false);
    5.                         }
    EDIT - figured out the instantiate too many part.Foreach was runnin in another loop. Oops
     
    Last edited: Mar 25, 2020
  3. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    the foreach loop is spawning 1 panel for the first enemy and 2 panels for the 2nd. Every time an enemy is spawned it is added to activeBattlers, so each time an enemy is spawned it goes through the whole list, which is increased in size for each enemy. All you need to do is spawn the panel right after you spawn the enemy, so you can remove the foreach loop and leave the code that is inside it.
     
  4. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    Yes I found that part out! Thank you though!.

    I do have another question. I am trying to instantiate the panel as a child of a child that has the canvas as a parent. Currently the child gets moved to being a child of the Positions (array of spawn points) and that is wrong. as of right now just big red X's because of ui instantiating outside of the canvas.

    Code (CSharp):
    1. HUD.transform.SetParent(GameObject.Find("Canvas").transform.parent = Positions[i], false);
     
  5. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    I find that setting the parent in the Instantiate method works around that problem. Its easier to set the position after instantiate with the uGUI. I have a project somewhere that corrects the red x's, but I cannot seem to find it.
     
  6. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    Any luck on finding that project? Would be pretty useful right now..

    Code (CSharp):
    1. GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation);
    this spawns panels in right place but they come in as red X

    Code (CSharp):
    1. HUD.transform.SetParent(GameObject.Find("Canvas").transform, false);
    adding this fixes the red x problem but they both spawn in at the center and stacked.
     
  7. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    When you add false in the SetParent method, it resets the position. You have to set the position again after that call.
     
  8. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    Do you think you can show me? I hate to say it but I stayed up late last night and have been researching/ experimenting all morning and I have not been successful. I am thinking I might need to do something outside of my novice knowledge level.
     
  9. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    All you have to do is set the position again right after that call.

    OR

    Code (CSharp):
    1. GameObject HUD = Instantiate(EnemyHUB, GameObject.Find("Canvas").transform);
    2. HUD.transform.position = Positions[i].position;
    3. HUD.transform.rotation = Positions[i].rotation;
    I would cache the Canvas transform instead of calling GameObject.Find for every battler. It is more efficient and you can make sure using error checking that you actually have a transform.

    Code (CSharp):
    1. Transform canvasTrans = GameObject.Find("Canvas");
    2.  
    3. if (!canvasTrans) // the ! is the not opetator, if you didn't know its the same as canvasTrans == null in this instance.
    4. {
    5.     throw new UnityException("Cannot find Canvas! Unable to setup battlers!");
    6. }
     
  10. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    That didnt work. It was the first thing I tried. This is the result:
     

    Attached Files:

  11. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    How does your current code work, can you post it?
     
  12. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    This is the part of the code that pertains to the U.I. If you feel you need more the OP has the full code minus this update that removed the foreach loop.

    Code (CSharp):
    1. if (Enemies[j].Name == enemiestospawn[i])
    2.                     {
    3.                         BaseStats newEnemy = Instantiate(Enemies[j], Positions[i].position, Positions[i].rotation);
    4.                         newEnemy.transform.parent = Positions[i];
    5.                         activeBattlers.Add(newEnemy);
    6.                         GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation);
    7.                         HUD.transform.SetParent(GameObject.Find("Canvas").transform, false);
    8.                     }
    This was the very first thing i tried following the line that set the parent:
    Code (CSharp):
    1. HUD.transform.SetPositionAndRotation(Positions[i].position, Positions[i].rotation);
     
  13. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    Are you doing all of this in the UI, I mean the enemies as well?

    If you're spawning enemies into world space outside the UI, the problem is with coordinates.
    Other than that I have no idea why it wouldn't work setting the position after setting the parent.
     
  14. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    What do you mean am I doing all of this in the U.I? If you mean the U.I canvas then, the background and enemies exist outside of the canvas. While were on the topic here is a pic of my hierarchy. It is arranged correct right? When play mode the panels will become children of canvas
     

    Attached Files:

  15. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    29
    Any other suggestions? I hate to keep bumping this but I am at a total lost at how to fix this.
     
  16. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    647
    If this is how you are doing it, enemy spawn positions will not be the same in the UI. It's a different system, and can explain why everything is showing up on top of each other in the UI. Game units are based on either ppu(pixels per unit) for 2D or 1 unit = 1 meter for 3D. In the UI depends on what you have the canvas scaler set too, but its usually 1 unit = 1 pixel.

    I'm not sure how you have everything setup so its hard to judge, but you should have a separate list of positions for enemy spawns and one for the UI positions.
     
unityunity