I have a GameObject.Instantiate() method that create a Page game object. The Page game object then have a Start() method where it also instantiate some new game objects.
My issue is that the game objects that are created in the Start() method get created twice. First it get's created as children of the Page game object (which I want, that works correct), but then it also creates the same game objects in the root of the scene.
I've tried to add Debug.Log("Calling start method...") to Start() to see if the method gets called twice, however this does not seem to be the case. It does not output the text twice to the log.
Navigation.cs
void navigateTo(Page page) {
...
// Here we instantiate the Page.
this.currentPage = GameObject.Instantiate (page.gameObject, this.content.transform).GetComponent<Page>();
}
Page.cs
void Start () {
// Here we create a child GameObject. This get's created both as a child of
// the Page game object, but also in the root of the scene.
GameObject tableGO = new GameObject ("Table");
GameObject instance = GameObject.Instantiate(tableGO, this.gameObject.transform) as GameObject;
...
}
The problem is that new GameObject ("Table") creates a gameObject which is without parent and GameObject.Instantiate(tableGO, this.gameObject.transform) as GameObject; also creates a gameObject but in the second case you use the first gameobject as a template (or like prefab) and set a parent to it. So you can remove GameObject tableGO = new GameObject ("Table"); and use prefab to create gameobject or remove the second line and set tableGO.transform.parent to whatever you need.
Related
I have a prefab object called Beam, which contains several things but one is an object that when an instance of it is collided with and triggered, should destroy itself.
Currently, I have the script that generates all of the instances on a variable called Beams. Shown here:
When that runs, it creates clones within it. Seen here:
You will also see in the last image, the Beam prefab that contains the Cookie in it. That cookie is where I have a script that says, if I hit it, destroy. That code looks like this:
...
public class Collectibles : MonoBehaviour
{
GameManager game;
// Start is called before the first frame update
void Start()
{
game = FindObjectOfType<GameManager> ();
}
...
void OnTriggerEnter2D(Collider2D other) {
if(other.tag == "Player"){
string coinType = "Cookie";
game.AddCollectible(coinType);
Destroy(gameObject);
}
}
}
Currently, when I run into a cookie, it runs Destroy(gameObject) and destroys ALL instances of the cookie (one per each Clone).
This code lives on Cookie, not on Beams. Is that correct? Should I have the code somewhere else? I also tried Destroy(this) but that doesn't do what I thought it would do (just the instance).
Is it possible that from where I was calling Destroy, the script doesn't have access to the instances, or am I missing something? Thank you in advance!
If I understand your question, you want that when the "player" collides with an instance of "beam" only destroy the instance of Cookie (or the gameobject that contains the script), in this case it would do so with the tag:
public GameObject[] arrayofcookie;
public int destroyedinstances=1;
//this int will tell how many instances you want to be destroyed (from the last instantiated to the first)
//for this example the last instance will be deleted
public void destroyCookie()
{
arrayofcookie= GameObject.FindGameObjectsWithTag("Cookie");
for (int i = 0; i < destroyedinstances; i++)
{
Destroy(arrayofcookie[i].gameObject);
}
}
You call this method in the cookie script, in the collider or if you prefer with an invoke method after N seconds.
I do not think I have understood your question very well, but in these problems I prefer to use the label and it also depends on the nature of your game
Using Unity, I'm working on a game where all Gameobjects with a certain tag vanish/reappear fairly regularly (every 10 seconds on average). I use GameObject.FindGameObjectsWithTag() to create a Gameobject[] through which I enumerate every time that the objects need to be made visible/invisible. I cannot call it once, on Start, as new Gameobjects are created while playing. I thought that it would be worse to access and change the Gameobject[] every time something got created/destroyed. Is there a better way to handle this. I know how bad of an impact on performance the GameObject.Find methods make...
Yes, there is a better way to do this. Have a script with List variable that can store GameObjects. Making it a singleton is better but not necessary.
This singleton script should be attached to an empty GameObject:
public class GameObjectManager : MonoBehaviour
{
//Holds instance of GameObjectManager
public static GameObjectManager instance = null;
public List<GameObject> allObjects = new List<GameObject>();
void Awake()
{
//If this script does not exit already, use this current instance
if (instance == null)
instance = this;
//If this script already exit, DESTROY this current instance
else if (instance != this)
Destroy(gameObject);
}
}
Now, write a script that registers itself to the List in the GameObjectManager script. It should register itself in the Awake function and un-register itself in the OnDestroy function.
This script must be attached to each prefab you want to add to the List. Simply do that from the Editor:
public class GameObjectAutoAdd : MonoBehaviour
{
void Awake()
{
//Add this GameObject to List when it is created
GameObjectManager.instance.allObjects.Add(gameObject);
}
void OnDestroy()
{
//Remove this GameObject from the List when it is about to be destroyed
GameObjectManager.instance.allObjects.Remove(gameObject);
}
}
If the GameObjects are not prefabs but GameObjects created through code, simply attach the GameObjectAutoAdd script to them once they are created:
GameObject obj = new GameObject("Player");
obj.AddComponent<GameObjectAutoAdd>();
You can now access your GameObjects in the List with GameObjectManager.instance.allObjects[n]; where n is the index number and you don't have to use any of the Find functions to find the GameObject anymore.
It's true that calling to GameObject.Find consumes a lot of recourses.
The point should be to save the result and use it always but I understand that you can't do that.
Other option is to have all this gameobjects as a child of one gameobject let's call it handlerGameobject. It will have a script that doesn't use GameObeject.Find it could getChild or transform.Find that uses less resources.
Hope it helps!
I have a scene that contains a nested prefab.
The nested prefab contains multiple barrel prefabs (image below)
These barrel prefabs have components: a transform, animation, mesh filter and a mesh renderer
The problem is that i can't find the mesh filter & mesh renderer by code. Everything works fine in the Unity player! But when i build for pc i get a nullreference on `gobj.getComponent, but only on the objects that have the "barrel" tag, (oil works fine and has the same components):
private void EvaluateAll() //gets called from awake
{
EvaluateDissolve();
EvaluateFish();
EvaluateFloor();
EvaluateFogCol();
EvaluateFogDens();
EvaluatePlants();
EvaluateRocks();
}
private void EvaluateDissolve()
{
foreach (GameObject gobj in GameObject.FindGameObjectsWithTag("oil"))
{
gobj.GetComponent<Renderer>().sharedMaterial.SetFloat("_SliceAmount",m_dissolveFactor + 0.2f);
}
foreach (GameObject gobj in GameObject.FindGameObjectsWithTag("barrel"))
{
gobj.GetComponent<Renderer().sharedMaterial.SetFloat("_SliceAmount",m_dissolveFactor);
}
}
I tried logging all the components of my barrel objects, it logs only the animation and transform components, for some reason the mesh renderer and mesh filter are not logged (this code is called from the awake function):
foreach (GameObject gobj in GameObject.FindGameObjectsWithTag("barrel"))
{
Debug.Log("is object active? " + gobj.activeInHierarchy.ToString());
Debug.Log("barrel scene: " + gobj.scene.name.ToString());
Debug.Log("asking components of barrel, is barrel null?" + (gobj == null).ToString());
Component[] components = gobj.GetComponents(typeof(Component));
foreach (Component comp in components)
{
Debug.Log(comp.GetType().ToString());
}
gobj.GetComponent<Renderer>().sharedMaterial.SetFloat("_SliceAmount",m_dissolveFactor);
}
GetComponent will never return null unless the component is not attached to that Object or the component is destroyed.
Your GameObject.FindGameObjectsWithTag("oil") or GameObject.FindGameObjectsWithTag("barrel") is finding a GameObject with the "oil" or "barrel" tag respectively.
After this, GetComponent<Renderer() is used in a loop to access each Renderer returned by FindGameObjectsWithTag. According to your, it returns null sometimes and the the loop that returns null points to a GameObject called "Fish1".
The problem is that you have mistakenly changed the tag of Fish1 to either "oil" or "barrel". FindGameObjectsWithTag will then pick it up and try to check for its Renderer which is null.
Change the "Fish1" tag to Untagged so that GameObject.FindGameObjectsWithTag("oil") and GameObject.FindGameObjectsWithTag("barrel") won't find it.
Now, if you really meant to have "Fish1" to have "oil" or "barrel" tag then you must make sure that a MeshRender is attached to it.
Note:
There is a search bar in the Editor, use that to search for "Fish1" GameObjects in the scene and change the tag.
Also, make sure that you are not instantiating any GameObject named ""Fish1" with "oil" or "barrel" tag. This is very important. It's not magic. GameObject.FindGameObjectsWithTag is returning it because it exist in the scene.
I have in the Hierarchy two ThirdPersonControllers.
And i create new c# script file then dragged the script to the first ThirdPersonController.
It should clone more 10 ThirdPersonControllers of the first ThirdPersonController.
This is the script:
using UnityEngine;
using System.Collections;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
void Awake()
{
gos = new GameObject[10];
for(int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos[i] = clone;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
In the Inspector of the ThirdPersonController in the script area in the Prefab i selected the ThirdPersonController.
But when running the game the Unity the whole program is frozed i had to use Task Manager to shut it down.
I know its the script the problem since i tried it without the script and it was fine.
What i want to do is instead 2 ThirdPersonControllers to have when running the game 10 or 20 ThiredPersonControllers each one in another position by using the c# script.
Screenshot of my Hierarchy:
From what I'm seeing looks like the prefab you are initializing has the "Multiple_objects" Script, so this means every new instance is going to execute that script and create 10 more and so on.
Try putting "Multiple_objects" Script on another GameObject thats not going to be dynamically initialized. (ex: An empty GameObject, Main Camera, Directional Light).
I made a prefab gameObject with a rigidbody component attached. Whenever I change the mass of the original prefab from the inspector, all the instances present in the scene get affected (the field is not overridden). But when I try to do the same thing using a script, the mass of the instances remain unchanged (only the main prefab is affected and every time I enter play mode, it retains its previous value!). The script is attached to another gameObject and looks like this:
using UnityEngine;
using System.Collections;
public class CubeScript : MonoBehaviour {
public GameObject largeCube; // dragged the original prefab in the inspector
private Rigidbody rb;
// Use this for initialization
void Start ()
{
rb = (Rigidbody) largeCube.GetComponent ("Rigidbody");
rb.mass = 44; // this is not changing the instances, rather only the main prefab. Note that the mass is not overridden
}
}
I don't understand as a beginner. Please explain this to me.
I belieive that "Apply" button is responsible for applying all values to the instances of prefab at the inspector level. From the script, you need to do it manually (You don't have any button or method called "Apply").
The best (and most efficient in my opinion) is to create tag for prefab and the usage of
GameObject[] myPrefabInstances = GameObject.FindGameObjectsWithTag("yourTagName").
And then:
foreach (var go in myPrefabInstances)
{
var rb = (Rigidbody) go.GetComponent ("Rigidbody");
rb.mass = 44;
}