I would like to make it so that in a script I can find out if an object in my hierarchy has a child and if it doesn't, destroy it. I would also like to know how in a script, create the same gameobject as one in my hierarchy, i.e. duplicate an object in my hierarchy.
I couldn't find anything on the sites.
Deleting objects that do not have a child
You can create a script like this:
public GameObject gm;
if (gm.transform.childCount != 0)
{
// The object has a child.
// Do whatever you want here.
}
else
{
// The object do not have a child.
// Destroy the object.
Destroy(gm);
}
If you want, you can even create a method that does that!
public void DeleteObject(GameObject gm)
{
if (gm.transform.childCount == 0)
{
// The object do not have a child.
// Destroy the object.
Destroy(gm);
}
}
Duplicating Objects
About the "Duplicate Object", you can use this method:
// You can call this method whenever you like and pass in the object that you want to duplicate.
public void DuplicateObject(GameObject obj)
{
Instantiate(obj);
}
Related
I want to assign each item I create with a GameObject of some sort and then when I create a new object (called Item) it will have the assigned GameObject as a child.
For that I have a class of a scriptable object which holds a public GameObject called "gameObj" within it:
public abstract class ItemObject : ScriptableObject
{
public int id;
public GameObject gameObj;
}
Then, in another class I want to have something of this sort:
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
this.gameObject.transform.GetChild(0) = item.gameObj; //WRONG CODE, NEED HELP HERE
}
}
The purpose is to set the gameObj from the given ItemObject.item as the GameObject for the GroundItem.
The purpose is in the end to have lots of scriptable items of all sorts (like bread, sword, stone etc) and each one will have a GameObject assigned to it, and once I create a new GroundItem game object I will simply assign the scriptable object item and its child will have the game object itself (which includes all the visuals, special scripts etc).
For reference, in the following link the person is doing this from minute 4 to minute 6, but with a sprite instead of a game object.
Anyone knows how it should be written? Is it even possible?
You would probably mean
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
if(!item) return;
if(!item.gameObj) return;
// make according object a child of this object
item.gameObj.transform.parent = transform;
// make it the firs child
item.gameObj.transform.SetSiblingIndex(0);
}
}
However, in general ScriptableObjects are assets and GameObjects are instances in the scene hierarchy -> you can't simply reference scene objects in assets, doing so anyway might lead to unexpected behavior.
Is there a good reason why this has to be done in ISerializationCallbackReceiver?
I think what you actually rather want to achieve would be e.g.
public class GroundItem : MonoBehaviour
{
public ItemObject item;
#if UNITY_EDITOR
[HideInInspector]
[SerializeField]
private ItemObject _lastItem;
// This is called whenever something is changed in this objects Inspector
// like e.g. item ;)
private void OnValidate()
{
// Delay the call in order to not get warnings for SendMessage
UnityEditor.EditorApplication.delayCall += DelayedOnValidate;
}
private void DelayedOnValidate()
{
// remove the callback since we want to be sure it is always added only once
UnityEditor.EditorApplication.delayCall -= DelayedOnValidate;
// if item hasn't changed nothing to do
if (item == _lastItem) return;
// otherwise first destroy current child if any
if (_lastItem && transform.childCount > 0)
{
if (Application.isPlaying) Destroy(transform.GetChild(0).gameObject);
else DestroyImmediate(transform.GetChild(0).gameObject);
}
// is an item referenced and does it even have a gameObject ?
if (item && item.gameObj)
{
// instantiate the new one as child of this object
var obj = Instantiate(item.gameObj, transform);
// set as first child (if needed)
obj.transform.SetSiblingIndex(0);
if (!Application.isPlaying)
{
// if in edit mode mark this object as dirty so it needs to be saved
UnityEditor.EditorUtility.SetDirty(gameObject);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
_lastItem = item;
}
#endif
}
Which would now look like
When game starts, script will close the shop.(SetActive(false))
void Start()
{
_shopMenu = GameObject.Find("Shop");
_shopMenu.SetActive(false);
if (_shopSlotContainer == null || _shopItemSlotTemplate == null)
FindItemShopContainerAndItemTemplate();
}
And try to find two objects on scene
private void FindItemShopContainerAndItemTemplate()
{
_shopSlotContainer = transform.Find("itemShopContainer");
if (_shopSlotContainer == null)
Debug.LogError("Container doesn't found");
_shopItemSlotTemplate = transform.Find("shopItem_template");
if (_shopItemSlotTemplate == null)
Debug.LogError("Item template doesn't found");
}
Any other ways to find objects or fix this problem?
(Script attached to "Shop" object)
Transform.Find will only search the immediate children, it will not look further down in the hierarchy. It is also not very performant. You can assign the game objects you want to variables in your script
public class MyScript : MonoBehavior
{
[SerializeField]
private GameObject _itemShopContainer {get; set;}
}
After compile you should be able to see Item Shop Container property on MyScript game object in the editor and be able to drag the proper game object there.
In the code _itemShopContainer will now have your game object.
If you want to assign via code like you are doing switch to using a variant of Transform.GetComponentsInChildren<>(), rather than Transform.Find
I have a really simple question, where I can't understand what I am missing.
I am getting error:
NullReferenceException: Object reference not set to an instance of an object
test.Update () (at Assets/Scripts/test.cs:13)
So, I have one class "DissableInput", where there is a simple trigger that changes whenever it triggered by another object. It returns true/false, whenever it changes.
Another class has is named "test", which basically should print out when it's true/false depending on trigger input.
//Test Class
public bool touchedCollision;
// Update is called once per frame
void Update()
{
if (this.GetComponent<DissableInput>().touchedCollision)
{
Debug.Log("IN COLL");
}
if(!this.GetComponent<DissableInput>().touchedCollision)
{
Debug.Log("NOT IN COLL");
}
}
// DisableInput Class
public bool touchedCollision;
void OnTriggerEnter (Collider other)
{
Debug.Log("In Triger");
touchedCollision = true;
}
public void OnTriggerExit(Collider other)
{
Debug.Log("Out Triger");
touchedCollision = false;
}
I am expecting that true/false will go into the test class, but instead it gives NullReferenceException error.
This part this.GetComponent<DisableInput>().touchedCollision will try to get a component of the specified type, in this case your "DisableInput" class, in the same gameObject that its attached. If you wish to have the DisableInputClass script in another gameObject you need to reference in some other way.
Using a public variable would look like that
//TestClass
public DisableInput disableInput;
// Update is called once per frame
void Update()
{
if (disableInput.touchedCollision)
{
Debug.Log("IN COLL");
}
else //you don`t need to specify the condition again, you can do it with and else
{
Debug.Log("NOT IN COLL");
}
}
you can look how GetComponent works here
An alternative is also to use FindObjectOfType<DisableInput>()
//TestClass
private DisableInput disableInput;
void Awake(){
disableInput = FindObjectOfType<DisableInput>();
// you only need to get the
//class refence one time, no need for it to be in the update,
//it gains a lot of performance
}
// Update is called once per frame
void Update()
{
if (disableInput.touchedCollision)
{
Debug.Log("IN COLL");
}
else //you don`t need to specify the condition again, you can do it with and else
{
Debug.Log("NOT IN COLL");
}
}
More about FindObjectOfType here, but to use this you must understand that
it will return the first object that he finds that has DisableInput attached to it. If you have multiple gameObjects with DisableInput you can`t be sure which one it will get.
Your scripts are on different objects, therefor this.GetComponent won't work
You want to know when ObjA touches ObjB, so you have one script on ObjA that wants to know and a script on ObjB that is run when the two touch. However, this.GetComponent can only see scripts attached to the same object. So you can't run this code from ObjA (because it doesn't, and can't, know about ObjB!)
I'm going to make two small changes to your scripts:
//Test Class
public bool touchedCollision; //you already had this, but weren't using it
// Update is called once per frame
void Update()
{
if (touchedCollision)
{
Debug.Log("IN COLL");
}
if(!touchedCollision) //can be changed to just `else`
{
Debug.Log("NOT IN COLL");
}
}
// DisableInput Class
//removed touchedCollision from here
void OnTriggerEnter (Collider other)
{
Debug.Log("In Triger");
other.GetComponent<TestClass>().touchedCollision = true;
}
public void OnTriggerExit(Collider other)
{
Debug.Log("Out Triger");
other.GetComponent<TestClass>().touchedCollision = false;
}
This works because it is assumes that:
You have more than one DisableInput instance for each volume that you wish to disable input
You may have more than one TestClass (or whatever you're going to call it) instance (for example, multiplayer or AI controlled objects).
The DisableInput class finds the TestClass of the object that caused the physics event and changes its value.
Addendum:
You should check for nulls. This is the simplest way (glory be to C# 6!)
other.GetComponent<TestClass>()?.touchedCollision = ...
Using a null conditional operator.
I have this problem that I want to call an object from my first scene then call that object on my second scene . I tried doing this
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
and put it on the object I don't want to destroy then changed my scene on the
void Start(){
SceneManagement.LoadScene("Menu",LoadSceneMode.Single);
}
But it's not there on the heirarchy
Could someone help me out
EDIT:
Now when the next scene is loaded
The object I wanted is not there anymore. It is being destroyed
Create a persistent object
Create a preloader scene here you can place a splash screen or whatever you prefer but the important thing is loading things that should be persistent(maybe such as a network or gamemanager)
Create a script PersistentObject.cs and put the following code in it
private void Awake(){
DontDestroyOnLoad(this.gameObject);
}
Put this script on any object you initialize in the preloader
Access object from anywhere
If you want to access an object in another scene there are several ways but I will assume you do not have any specific reference to the object
So if we have a GameManager.cs and we created a Persistent cube in our preloader called Cube we can get a reference to the gameobject by saying GameObject cube = GameObject.FindGameobjectWithName("Cube");
Now you are able to do whatever you want by using cube
Write less, Do more with singletons
Creating a singleton will also be very useful as well
public static class Singleton<T>: MonoBehavior where T: MonoBehavior{
private static T instance;
//Notice the lower and upper case difference here
public static T Instance{
get{
if(instance == null){
instance = GameObject.FindGameObjectOfType<T>();
}
return instance;
}
}
}
You can then add this to your script make accessing properties easier and reduces the amount of code you have to write
public class Cube: Singleton<Cube>{
private string cubeName = "Jeff";
public void ChangeCubeName(string newName){
cubeName = newName;
}
}
To access this methods of this class you could now call the singleton from anywhere in your code
Example
public class GameManager: MonoBehavior{
private void Start(){
cube.Instance.ChangeCubeName("Joe");
}
}
If I have the original hierarchy for example:
Cube1
Cube2
And I did on Cube2:
transform.SetParent(null);
Now the Hierarchy is looks like:
Cube1
Cube2
But if I want now to move back Cube2 to his original place ?
Cube1
Cube2
And same for other objects if they were children of children or sub parents and I move a group of objects and made them all SetParent(null) and now I want them all to return them to they original place.
You find the "Cube1" GameObject which was the parent:
GameObject cube1 = GameObject.Find("Cube1");
You call the SetParent function again but with the "Cube1" Transform in the argument passed to it:
transform.SetParent(cube1.transform);
I used something like this for the tutorial of our game. Basically the purpose is to have a game object from the UILayer be transfer to the TutorialLayer.
In the TutorialHandler of our game I made a Dictionary<int, GameObject> originalParent
PutToLayer(GameObject targret) function transfers target to the TutorialLayer and I store the original parent to the dictionary, using the target's instance id as key originalParent.Add(target.GetInstanceId()), target.transform.parent)
Returning the trarget to the original parent can be done by getting it from the dictionary
Transform origParent = originalParent[target.GetInstanceId()];
target.SetParent(origParent);
This way, you can transfer game objects to different parents while keeping track of the original parent
I think a nicer solution wouldn't involve any hard-coded names or inspector assigning since those implementations are prone to breaking when you rename or shift objects around.
I'd create a new component that remembers the original parent and then simply leaves it or rejoins it when you tell it to.
public class RoamingChild : MonoBehaviour {
private Transform _parent;
public void Awake () {
_parent = transform.parent;
}
//Leave the parent and go off on its own!
public void Leave () {
transform.SetParent(null);
}
//Come back to the parent!
public void Rejoin () {
transform.SetParent(_parent);
}
}
It might also be useful for the Child to come back to the same position within the Parent. So Child2 can leave and then rejoin the parent as the second child.
You could add this functionality by also remembering the SiblingIndex.
public class RoamingSibling : MonoBehaviour {
private Transform _parent;
private int _siblingIndex;
public void Awake () {
_parent = transform.parent;
_siblingIndex = transform.GetSiblingIndex();
}
//Leave the parent and go off on its own!
public void Leave () {
transform.SetParent(null);
}
//Come back to the parent!
public void Rejoin () {
transform.SetParent(_parent);
transform.SetSiblingIndex(_siblingIndex);
}
}