Simplest way to iterate over all GameObject childrens from a script at runtime - unity3d

In my Unity3D project I got a complex GameObject of a truck that his hierarchy looks like this.
+ Truck
+FrontPivotPoint
+LeftWheel
Tire
Rim
Hindge
+RightWheel
Tire
Rim
Hindge
+CenterPivotPoint
+Body
Arm
Screw
Pin
Basically what's happening is I got a lot of complex parenting and I want to go trough every child and add to him a RigidBody.
I think it should be something nested but I don't have something in mind.
Any help would be appreciated!

Unity3d allows you to easily automate every routine. You may consider to add custom menu item.
public class MakeRigidBodies : EditorWindow
{
[MenuItem ("Make rigid bodies %&r")]
private static void Execute()
{
var selectedObject = UnityEditor.Selection.activeObject;
if( selectedObject && selectedObject is GameObject )
{
// for all children of selectedObject
{
// add rigid body
}
}
}
}

From what I can see, the structure of your game objects is like a tree data structure. Truck as its parent / root node, and the others as its child. So, you must traverse all the objects / nodes in your structure with tree traversing algorithm, the best that I know is Depth-First-Search (DFS) algorithm.
DFS work like nested loop, the main algorithm are:
Start from the root node
Find its node children
Visit the child
Back to step 2 until all children is visited
This algorithm can be implemented in Unity3d since GameObject stores its child information in transform properties (see http://answers.unity3d.com/questions/416730/get-the-child-gameobject-of-a-parent-and-not-the-t.html). And last, we can add RigidBody to the GameObject with the AddComponent() method (see http://answers.unity3d.com/questions/19466/how-do-i-add-a-rigidbody-in-script.html).
Here is my script to answer your question:
using UnityEngine;
using System.Collections;
public class AddRigidBody : MonoBehaviour {
private void addRigidBody(GameObject gameObject)
{
Rigidbody rigidBody = gameObject.AddComponent<Rigidbody>();
foreach(Transform t in gameObject.transform)
{
addRigidBody(t.gameObject);
}
}
void Start () {
addRigidBody (this.gameObject);
}
}
This script attached to the parent / root GameObject. The AddRigidBody() method is called when this script start (as in Start() method), and this method will traverse all child and add a RigidBody through it.

If I'm not mistaken, and bear with me since it's been a while since I've worked with Unity3D, this could fix it:
* Drag your model onto the stage
* Your entire model should now be selected; parent and its underlying children
* Select the option to add a rigidbody and it should add it to all selected objects.
* Make a prefab of the model and you should be done.
Once again, don't trust me on this. It's been a while and since I don't have access to Unity3D right now, I am unable to check this method myself.
If it's the code approach you want, try this (C#, methods may vary for Boo or Java):
Getting a child: http://forum.unity3d.com/threads/get-child-c.83512/ (see second reply)
Adding a rigidbody: http://answers.unity3d.com/questions/19466/how-do-i-add-a-rigidbody-in-script.html
I hope this helps.

Related

Destroy specific child object of prefab object in c# / Unity

I search a lot but couldn't find any answer. My problem: I instantiate several Gameobjects of a Prefab, each consists of two child objects: an obstacle and a scorezone. I want to destroy the scorezone (only of that specific instance), when the player touches the obstacle. So in Runtime my hierarchy looks like this:
-ObjectPrefab(clone)
---Scorezone
---Obstacle
-ObjectPrefab(Clone)
---Scorezone
---Obstacle
...
So I need to find ONE child gameobject (not the Transform) to then destroy it. I tried several codes but none worked. Here is the code with the three alternatives I tried, but none of them worked.:
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.tag == "obstacle") {
Alternative 1:
Destroy(GameObject.FindWithTag("scorezone"));
Alternative 1 comes really close, but it destroys all scorezones of all instantiated objects.
Alternative 2:
Destroy(collision.gameObject.transform.parent.gameObject.FindWithTag("scorezone"));
Alternative 2 would sound logic to me but I get the error message "Transform does not contain a definition for "FindWithTag".
Alternative 3:
foreach (Transform child in collision.gameObject.transform)
{
if (child.tag == "scorezone")
{
Destroy(child.gameObject);
}
}
}
Alternative 3 does not give an error but actually does nothing at all when the player hits the obstacle.
I really appreciate some help, thanks in advance!
Store the reference!
If it is really a "prefab" so meaning you have it as an asset and instantiate it on runtime the best way (in my eyes and performance wise) would actually be to use a serialized field:
// Put this component on your obstacle
public class ObjectController : MonoBehaviour
{
// Drag the according object here via the Inspector in the prefab
// => when the prefab is Instantiated this field is already referenced
[SerializeField] private GameObject _scoreZone;
// Read-only access to the reference
public GameObject ScoreZone => _scoreZone;
}
This field reference is stored together with your prefab asset. So every instance of the prefab already has its own child references set up.
and then do
private void OnCollisionEnter2D(Collision2D collision)
{
// Rather use CompareTag which is slightly more efficient
// and adds some security (== silently fails if typo, CompareTag throws an exception instead)
if (collision.gameObject.CompareTag("obstacle"))
{
var controller = collision.gameObject.GetComponent<ObjectController>();
if(controller)
{
Destroy(controller.ScoreZone);
}
}
}
By Index
Alternatively but error prone you could simply use the index. You already know that there are only 2 childs, the first one being the Scorezone with index 0 so you can also do
if (collision.gameObject.CompareTag("obstacle"))
{
var scoreZone = collision.transform.parent.GetChild(0);
Destroy(scoreZone);
}
By Name
Using the name you could also use Find - I would not recommend this! It is error prone and slow
if (collision.gameObject.CompareTag("obstacle"))
{
var scoreZone = collision.transform.parent.Find("ScoreZone");
Destroy(scoreZone);
}

unity get child data

I am trying to create a procedural created dungeon in Unity.
I have it creating the dungeon, and have a script attached to each prefab that goes to make up the walls, floor etc. Each one of these is attached to a single parent, to make it easy to delete and rebuild the entire dungeon. The script contains details:
public class WallPrefab
{
public bool use;
private int roomId;
public WallTypes type; //enum = torch, switch, item etc
public WallDirections direction; //direction wall is facing
public GameObject myPrefab; //nested
}
The prefab also contains an empty game object used as a mount point for torches, switches etc.
myPrefab
prefab //prefab for the actual 3d object
wallmount //empty game object to store location and rotation details
....
My questions:
1. How do I get the wall type?
int childs = transform.childCount;
for (var i = childs - 1; i >= 0; i--)
{
c = transform.GetChild(i).gameObject;
Debug.Log("child:" + c.name);
//WallTypes g = c.GetComponentInChildren<WallPrefab>().type;
WallTypes g = c.transform.GetComponentInChildren<WallSettings>().type;
(Edited to insert working solution to this part of my problem!)
This code does not work, because Unity complains that:
ArgumentException: GetComponent requires that the requested component
'WallPrefab' derives from MonoBehaviour or Component or is an
interface.
How do I get the transform of the mount point?
I know how to get the location and rotation, once I get the mount point, but how do I actually get the mount point?
Question 1 seems pretty self explainatory.
Question 2 looks a bit more interessting.
If i understood you correctly, you have a script, that "creates" torches on specified mount points. Assuming those mount points are empty game objects and you dont want to add torches directly into the prefab (would be nice if we got a little bit more detail here on how exactly your dungeon+mount point is generated), i would add a script to the prefab, add a GameObject instance variable and use the inspector to drag the mount point onto the script. then i would use "gameobjectVariable.transform.position" to get the position.
something like:
public class prefabClassWithMountPoints{
GameObject mountpoint; //drag mount point from inspector
GameObject torchGameObject; //drag torch prefab here
public void createTorch(){
GameObject torchObject = Instantiate(torchGameObject, mountpoint.transform.position, Quaternion.identity);
//...
}
//rest of code
}
this is assuming the mount points are not randomly generated WITHIN the prefab.

How to create game-wide object in Unity3d without singleton?

Is there a way in Unity3d framework to register an object that would be accessible to all entities in all scenes in the game without resorting to singleton pattern (like a game state object)?
You could take the following approach:
In the scene where the GameObject is created, in a MonoBehavior script attached to the GameObject:
Name the GameObject with "name = ..." in Awake() (or in the Editor)
Example: myObject.name = "FindMeInEveryScene";
Ref: https://docs.unity3d.com/ScriptReference/Object-name.html
Call "DontDestroyOnLoad" on the GameObject in Awake()
Example: DontDestroyOnLoad( myObject );
Ref: https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
In the same scene and in subsequent scenes, in MonoBehavior scripts attached to other GameObjects.
Find the object with GameObject.Find(...) in Start()
Example: globalGameObject = GameObject.Find( "FindMeInEveryScene" );
Ref: https://docs.unity3d.com/ScriptReference/GameObject.Find.html
This will allow you to access the GameObject named "FindMeInEveryScene" without a singleton.
To pull it all together...
GlobalGameObject.cs
// Attached to the GameObject that needs to be accessd by any other
// GameObjects and needs to survive between loading scenes
public class GlobalGameObject : MonoBehaviour
{
static bool gameObjectInitialized = false;
void Awake()
{
if ( !gameObjectInitialized )
{
gameObject.name = "FindMeInEveryScene";
DontDestroyOnLoad( gameObject );
gameObjectInitialized = true;
}
}
}
UsingGameObject.cs
// Attached to GameObjects that need access to the GlobalGameObject
public class UsingGameObject : MonoBehaviour
{
private GameObject globalGameObject = null;
void Start()
{
globalGameObject = GameObject.Find( "FindMeInEveryScene" );
}
}
You could possibly do it using events but it isn't really the optimal way to use events. I'm intrigued to know why a singleton pattern doesn't work for you as this is pretty much their main purpose.
If you aren't going to do this with a static or a singleton, I don't see any point in you using any kind of game state object in your system design. It would do the same thing, but slower (avoid GameObject.Find at all costs). It would lack the guaaranteed uniqueness of the singleton and the easy reference provided by both. I don't understand what tradeoffs you mean but I'll assume they're sound to try and answer your question.
Doing this without a central game state object is going to require a more decentralized solution. Tasks that would be performed by a static game manager are now controlled by the individual objects themselves and notified by other objects.
https://unity3d.com/learn/tutorials/topics/scripting/delegates
The beauty of this method is that objects don't need to know about eachother at all. They just need to listen for events and notify the system that an event has occured. I really can't think of another alternative. Hope this helps. Best of Luck!

Scriptable Object as a custom version of sprite

I have made a scriptable object which has a one property a public sprite. I want to have one version of sprite added everywhere I want.
With the possibility that if I ever want to change it I will just pin another sprite to scriptable object and in every place where it is pinned the sprite will change.
Is there any way to walk thought this problem or any other idea to make one object where I will store one Sprite and pin it to multiple objects.
Finally is this concept possible in Unity?
I have already thought about:
Deriving prom Sprite Renderer but the class is sealed, so i cannot make my own version of it this way
Creating my own custom version of Sprite Render cause if an any update come I would have to make yet again another version.
I think you can use prefabs to achieve the same thing as you need:
You can create prefab by dragging and object to your assets.(Your prefab will be just an object with transform and e.g. Sprite Renderer)
After creating prefab you can copy it as many times as you want.
Than when you decide to change the sprite just simply go to your prefab and edit sprite there. Now every instance of your prefab will have sprite changed.
It is possible with C# events. First, make your ScriptableObject call event whenever sprite is set. Allow Sprite to be set only using property (or method), so that you could track the change, like this:
public sealed class SpriteObject : ScriptableObject
{
public event Action SpriteChanged;
[SerializeField]
private Sprite _sprite;
public Sprite Sprite
{
get { return _sprite; }
set
{
if(_sprite == value)
return;
_sprite = value;
SpriteChanged?.Invoke();
}
}
}
Then, you need script, that will react to changed and assign the changed sprite to SpriteRenderer. So, something like this:
NOTE: name is purely for example. Do NOT name your classes like this!
[RequireComponent(typeof(SpriteRenderer))]
public class ScriptThatUsedSpriteObject : MonoBehaviour
{
public SpriteObject spriteObject;
private SpriteRenderer spriteRenderer;
/// Called once the script is created.
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
if(spriteObject != null)
{
spriteRenderer.sprite = spriteObject.Sprite;
spriteObject.SpriteChanged += HandleSpriteChanged;
}
}
/// Called whenever sprite is changed inside SpriteObject.
private void HandleSpriteChanged()
{
spriteRenderer.sprite = spriteObject.Sprite;
}
}
Now, if some script changes sprite in SpriteObject, every ScriptThatUsedSpriteObject will automatically update their sprites.
public class PublicSprite : MonoBehaviour {
public Sprite publicSprite;
#region Singelton
public static PublicSprite instance;
private void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one instance found");
return;
}
instance = this;
}
}
get the sprite like this:
PublicSprite.instance.publicSprite;
as Mentioned here, you probably want to use a Singelton. It is best to avoid the use of them only if its necessary as its a very bad programming practice. (you can learn more on why to avoid them here: What is so bad about singletons?
However, I can't think of another way of doing what you seek for.

Unity 2D: Simple Cutscene animation system/manager

I am struggeling to come up with a smart way to deal with cutscenes in my 2D game. I think I have decided to Create/Control the cutscenes using the Animator, and moving the actual GameObjects around.
What I am struggeling with is:
This approach means each GameObject needs it's own Animator. So how do I then trigger the correct Cutscene? I am going to use game stages to control the game and its progress, so for example:
if(gameStage == 34 && entering scene castle)
{
playCorrectCutscene();
}
How and where do I keep references to all my cutscenes? Say I have 22 NPCs in a scene, and 15 of those have their own Animator since they have a cutscene, how do I play the Animation from NPC_11?
I guess what I am looking for is some sort of "Cutscene Manager". The best possible solution would be to have a List of every cutscene (animation) with an ID. Then I could just use the ID to play the correct animation. But since each Animation is stored in the actual GameObject I have no idea if that is possible.
Hope Im making sense.
Once you create complete prefab with 1 Animator per prefab, then you can use bellow method to create instances on the fly:
Let say you have CutsceneManager script like this:
class CutsceneManager : MonoBehaviour {
public GameObject InstantiateResource(string folder, string name)
{
GameObject resource = Instantiate<GameObject>(Resources.Load<GameObject>(folder + "/" + name));
if (resource == null)
{
Debug.LogErrorFormat("Cannot find resource {0} in {1}", name, folder);
return null;
}
resource.name = name;
return resource;
}
}
The easiest way to use it is to attach it to Empty object. Then you can create public variable for manager in other scripts, where you would need to show cutscene, like this:
class SomeOtherScript : MonoBehaviour {
public CutsceneManager CutsceneManager;
}
This will let you drag&drop CutsceneManager empty into each script where you need it. This way you have 1 instance of cutscenemanager for all classes.
Now, in place where you would need to play custscene, you instantiate:
class SomeOtherScript : MonoBehaviour {
public CutsceneManager CutsceneManager;
public void PlayMyCutscene() {
GameObject cutscene = CutsceneManager.InstantiateResource("Cutscenes", "SomeOtherCutscene");
cutscene.setActive(true); // Or whetever other method to fire it off
}
}
On folder structure you would need to have: Assets\Resources\Cutscenes
Cutscenes would be called: SomeOtherCutscene.prefab
Notice there is no need to include .prefab when you are instantiating one - Unity framework will "know" and add it for you.