Instantiating Prefab Problems

I am working on an evolution game and a feature I am adding right now is the ability of the animals (which are just cubes) to have children. My issue is that while the code works successfully and they do have kids (they instantiate a "baby" cube) the "baby" cube is instantiated next to the exact same cube no matter which cube is technically having the baby. I suspect that this is because they are all using the same prefab script but I am not sure how to fix it. Below is the code that instantiates the babies.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BayBay : MonoBehaviour {

public GameObject Baby;
GameObject BabyClone;

void Update()
{

if (script.yesbaby > 1)
{
Debug.Log("heres the baby");
BabyClone = Instantiate(Baby, transform.position, transform.rotation) as GameObject;
script.yesbaby = 1;
}
}

}



This code is attached to an empty that sits in front of each cube and is parented to that cube. I assumed this code would instantiate the next "baby" cube in the location of the empty but they all are instantiated to the empty of the same cube( I have not figured out how this "permanent mother" cube is chosen) So no matter how many babies are created, whenever one decides it is time to have a baby, it still comes out of the empty of the "permanent mother" cube. Now one thing I did notice is that if I start the game with two cubes, a "permanent mother" cube is somehow chosen between those two, but later on down the line if that "permanent mother" cube dies, that seconds cube will be chosen as the next "permanent mother" rather than any of the babies either of them had. If that cube then dies, the first baby that was born will be chosen as the next "permanent mother". Any ideas?


  • William Miller(williamatics) replied

    ?

  • Jonathan Gonzalez(jgonzalez) replied

    To Instantiate an object at a specific position you can add in a second Transform. 

    This line:

    BabyClone = Instantiate(Baby, transform.position, transform.rotation) as GameObject;


    is saying, Instantiate this object at the position and rotation of the object that contains this script. You can add an additional Transform game object to you script that will be used solely for positioning/rotating these objects.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class BayBay : MonoBehaviour {
    
    public GameObject Baby;
    public Transform babyOrigin;
    GameObject BabyClone;
    
    void Update()
    {
    
    if (script.yesbaby > 1)
    {
    Debug.Log("heres the baby");
    BabyClone = Instantiate(Baby, babyOrigin.position, babyOrigin.rotation) as GameObject;
    script.yesbaby = 1;
    }
    }
    
    }
    
    

    With this added in, it'll instantiate an object based off the position/rotation of the "babyOrigin" object you assign. If you need further clarification let me know. 

  • Hunter & Tiff Eidmann(eithman) replied

    I tried what you said but I am still running into the same issue. I added a transform variable called haveBabyHere. Then I drug the empty game object that is parented to the front of the baby into that variable in the inspector panel. Here is the script and some screen shots for more context. 


    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class BayBay : MonoBehaviour {
    
    public GameObject Baby;
    GameObject BabyClone;
    private int timeForBaby;
    public Transform haveBabyHere;
    
    
    void Update()
    {
    Debug.Log(haveBabyHere);
    if (script.yesbaby > 1)
    {
    Debug.Log("heres the baby");
    BabyClone = Instantiate(Baby, haveBabyHere.position, haveBabyHere.rotation) as GameObject;
    script.yesbaby = 1;
    }
    }
    
    }


    Sorry the picture is so grainy, I can not find a way to make it look nice since it has to fit in the margins. 

  • Jonathan Gonzalez(jgonzalez) replied

     The script should be applied to the parent. The new Transform in the script that was added should be a completely different object. If you assign the same object that has the script then you're just doing the same thing as what you had initially. Here is an image of what should happen:


    The parent on the left would have the script. The "Instantiate Here" is an empty game object, and that would be used as the transform to instantiate objects from. You can then move that anywhere you want and it'll instantiate at the position/rotation of that object instead of the parent. 

    Also I should note that the "haveBabyHere" transform should not be in the exact same position as the parent move/rotate it where you want the baby to be instantiated. 

  • Hunter & Tiff Eidmann(eithman) replied

    Thank you so much! That worked perfectly! 

    Unfortunately I ran into another issue with instantiating prefabs. The "baby" prefab has a variable called "health" that is set to go down by one every time the Update method is cycled through. The "baby" prefab is set to have 20,000 health but when the "baby" is instantiated they have the same amount of health as the parent does at the time they instantiate the "baby" rather than having 20,000 like the prefab does. Do you know why they are inheriting the health from their parent rather than just instantiating an identical copy of the prefab with 20,000 health? 

  • Jonathan Gonzalez(jgonzalez) replied
    Can you post your health script? The only thing that comes to mind is that the parent/baby share the same health variable. Is that variable static? If it's static, that means that health variable is essentially 'global' and will be the same for everyone who uses it.
  • Hunter & Tiff Eidmann(eithman) replied

    Sure! Here is the code.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Health : MonoBehaviour
    {
    public float size = 1, health = 50000;
    public GameObject Baby;
    
    void Update()
    {
    
    //Health loses one every time Update is cycled through
    health -= size;
    
    //If they run out of health they die
    if (health < 0)
    {
    Destroy(Baby);
    }
    
    }
    
    
    private void OnTriggerEnter(Collider col)
    {
    switch (col.tag)
    {
    case "Frutgtable":
    
    //If they find a piece of food they gain 2500 health
    health = health + 2500;
    break;
    }
    }
    }



    I thought it might have been static too but it turns out that it isn't. The only other thing I can think of that links all the parents/children is that they all share the same prefab. Is it possible that they are all storing and inheriting their health variable from the original prefab? I don't think this is the issue because I am pretty sure instantiating a copy of the prefab disconnects it from the original but I am not entirely sure. 

  • Jonathan Gonzalez(jgonzalez) replied

    The script itself seems to be working fine. I created a prefab of a cube that uses the script and adjusted the health amount for each and they worked separately just fine with their own unique health amounts. I did make some slight modifications to your script to make it a bit cleaner and more efficient:


    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Health : MonoBehaviour
    {
        public float size = 1, health = 50000;
    
        void Update()
        {
    
            //Health loses one every time Update is cycled through
            health -= size;
    
            //If they run out of health they die
            if (health < 0)
            {
                Destroy(gameObject);
            }
    
        }
    
    
        void OnTriggerEnter(Collider col)
        {
            if(col.CompareTag("Food")){
                //If they find a piece of food they gain 2500 health
                health += 2500;
                Destroy (col.gameObject);
            }
        }
    }

    For the changes I removed the game object. If this is applied directly to the baby, and you want to destroy it, use gameObject to reference "this" game object. The OnTriggerEnter is set to private by default, same with any other method like Update. I removed the switch statement since it was just one check, using an if statement would be a bit easier. When referencing a tag of a game object that entered you can use "col.CompareTag". For the health I added the value instead of using health + 2500. Lastly I also destroyed the object that entered the trigger zone after adding the health. 

    I would recommend creating a new prefab and using that when you instantiate. It's possibly an organization issue. Test it with a few prefabs and a known good parent to drill down to the issue. 

  • Hunter & Tiff Eidmann(eithman) replied

    OK so I have figured out the problem but do not know how to fix it unfortunately. If I have an empty all by itself instantiating cubes with that health script attached to them, it works perfectly and they all have their own individual health. The issue is when I parent the empty to the cube. As soon as it is parented, every cube that is instantiated shares the same health. I unfortunately need the empty to be parented to the cube because each cube needs to be able to have it's own individual babies. Do you have any idea what it is about parenting that could be causing this problem?

  • Jonathan Gonzalez(jgonzalez) replied
    Ok so just so I'm clear. The parent has the empty game object as a child. The baby then instantiates based off that empty game object correct? Does the parent have a health script as well? The script seems to be working fine, so I can only guess that it's something to do with the parent you're using. I would recommend recreating that parent and trying it again.
  • Hunter & Tiff Eidmann(eithman) replied

    Sorry I have been a little vague. I will include a link to a video to add context. I did like you said and made a brand new cube. To the cube I attached the health script and an additional script that makes the cube run around simply called "script". Then I made an empty and made it the child of the cube. I drug that into the assets to make it a prefab. After that, I selected the empty and added a script to it that will instantiate a game object every few seconds. Then I drug the prefab of the cube/empty into that script as the game object I just mentioned. This way every few seconds a new cube/empty is instantiated and each one is capable of instantiating it's own cube/empty combo every few seconds. But since the empty is the child of the cube, all the new cube/empty combos that are instantiated have the same health as the cube/empty combo that instantiated them at the time they were instantiated. 

    Sorry this is so complicated :( I can't think of any easier way to explain it. I have been thinking about buying a mic so that I could use a screen grab program to show exactly what is going on an narrate it since debugging this seems to difficult to do with just text. 

    https://youtu.be/CrwM29lT5Ks


  • Jonathan Gonzalez(jgonzalez) replied

    It seems like it should be working based off your description, but I'm sure there's something else that you may be missing.

    Can you upload the current project you're working on? It'll be easier for me to troubleshoot it that way. Export the entire project if possible, you can do that by selecting all the folders in the project then right clicking and "export package". That'll create one unity package that I can then import myself.