Hi guys i'm trying to drive in the my GameObject (The Player) in my Project, I want a script to navigate to a var in a script that is on another GameObject. It's not easy because there is Parents and children... Here is my tree:
>PlayerEntity
>Canvas
Gun_Name_Text
Gun_Ammo_Text
>Player
Sprite
I want a script attached to 'Gun_Name_Text' to fetch a var in a script attached to 'Player', so i didnt manage to do it with :
var ammo1 = GetComponentInParent<GameObject> ().GetComponent<weapon> ().weaponOn.Ammo1;
PS: I prefer not to use GameObject.Find()
Thanks in advance
As I said in the comments, the easiest way to do this would to be to just assign the variable in the inspector. However if you can't do this then you could simply use:
OtherScript otherScript = null;
void Start()
{
otherScript = transform.root.GetComponentInChildren<OtherScript>();
}
Note: This will set otherScript equal to the first instance of the OtherScript that it finds in the child objects though. You will have to use GetComponentsInChildren if you have more than one OtherScript object in the children.
For your specific example you could use:
var ammo1 = transform.root.GetComponentInChildren<weapon>().Ammo1;
If you are calling this often then it would be wise to cache a refrence to the weapon script though. You will also have to do this if you want to modify the Ammo1 variable that is a member of the weapon class as it is passed to the var ammo1 by value and not by reference.
There are cases where my game object does not know everything that interacts with it. An example would be a destructible object. If I wanted to know so what I'm hitting that was destructible I'd have all destructible objects inherit from a base type or interface and search on that type. Here is an example:
private void CheckForDestructables(Collider2D c)
{
this.Print("CheckForDestructables Called");
string attackName = GetCurrentAttackName();
AttackParams currentAttack = GetCurrentAttack(attackName);
D.assert(currentAttack != null);
this.Print("CheckForDestructables Called");
if (currentAttack.IsAttacking && c.gameObject.tag == "Destructable")
{
List<BaseDestructibleScript> s = c.GetComponents<BaseDestructibleScript>().ToList();
D.assert(s.Any(), "Could Not find child of type BaseDestructibleScript");
for (int i = 0; i < s.Count(); i++)
{
s[i].onCollision(Movement.gameObject);
}
}
}
Related
Usually you would instantiate a clone of a prefab in a script using
var obj = (GameObject)Instantiate(....);
and than change it's parent using
var obj.transform.SetParent(...);
However I'm writing on a little editor script for instantiating prefabs over the menu entries that should keep their link to the prefab. I do it in a static class like:
public static class EditorMenu
{
// This is a reference that I already have and is not null
privtae static Transform exampleParent;
[MenuItem("Example/Create Clone")]
private static void CreateClone()
{
var prefab = AssetDatabase.LoadAssetAtPath("Example/Path/myObject.prefab", typeof(GameObject));
var obj = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
Selection.activeGameObject = obj;
}
}
I had to use
(GameObject)PrefabUtility.InstantiatePrefab(prefab);
because Instantiate() doesn't keep the link to the prefab but creates a clone.
So the above code works great so far and the object is inserted to the scene as if I would drag and drop it.
Now I would like to change this newly instantiated objects parent to parent so I added the line
obj.transform.SetParent(exampleParent, true);
I also tried
obj.transform.parent = exampleParent;
But both throw an exception:
Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption.
UnityEngine.Transform:SetParent(Transform)
prefab and thereby obj are the topmost GameObject of the prefab so I thought I am setting the parent of the whole instantiated object not any transform inside of the prefab hierachy.
How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?
Update
A workarround I just tried was to actually use Instantiate and do
var obj = Object.Instantiate(prefab, cancel.transform);
// remove the added "(clone)" suffix
obj.name = prefab.name;
obj.transform.SetParent(cancel.transform);
however as mentioned before this doesn't completely keep the prefb functionality intact .. so it only allows me to Revert but not to Apply changes. As workaround it might be good enough however since in my case I use it as a shortcut for instantiating a prefab which users are not really supposed to change later...
Sorry I just found the issue:
I didn't add it since the error message was confusing/bad formulated.
I was using Resources.FindObjectsOfTypeAll for checking if the exampleParent of type SomeType was in the scene.
Following Unity's example which should exclude prefabs I used
// Validation for the Menu item
[MenuItem("Example/Create Clone", true]
private bool TargetAvailable()
{
foreach (var target in (SomeType[])Resources.FindObjectsOfTypeAll(typeof(SomeType))
{
if (target.hideFlags == HideFlags.NotEditable || target.hideFlags == HideFlags.HideAndDontSave)
continue;
if (!EditorUtility.IsPersistent(target.transform.root.gameObject))
continue;
exampleParent = target.transform;
return true;
}
exampleParent = null;
return false;
}
But this seems actually to be wrong and not working since it allways returned me the SomeType reference from the prefabs! (I already found it a bit strange that they do
!EditorUtility.IsPersistent(target.transform.root.gameObject))
continue;
I'm not sure if that ! maybe is a type in their example code?!
So the error which sounds like setting the parent was not allowed actually means and should say
setting the parent to a Transform which resides in a prefab is not allowed ...
than I would have found the actual issue in the first place.
So again as a workaround until I can figure out that FindObjectsOfTypeAll thing I switched to Object.FindObjectOfType instead assuming my target will always be active in the scene. And using SetParent works now together with PrefabUtitlity.InstantiatePrefab.
How can I change the parent of a GameObject instantiated via PrefabUtility.InstantiatePrefab?
This is right, but make sure cancel is also an instantiated object.
obj.transform.parent = cancel.transform;
During runtime, i first add some items to an array, then write their values to the log(works just fine) then i instantiate them(still works fine), then later i want to change the values of the array items, but for some reason now the array indexes returns null? Anyone who has an idea whats going wrong?
public class ListEquippedItems : MonoBehaviour
{
private static ListEquippedItems instance = null;
public static ListEquippedItems Instance
{
get
{
if (instance == null) instance = new ListEquippedItems();
return instance;
}
}
[SerializeField]
private GameObject[] currentlyEquippedItems = new GameObject[8];
void Awake()
{
// Putting fake items into the currently equipped to test that items can actually be transfered to it
for (int i = 0; i < currentlyEquippedItems.Length; i++)
{
//////////////////// In 3 below lines the array works just fine and returns proper values.
currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().SetItemType((EffectTypes)i);
currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().SetItemName("I am item: " + i);
currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().SetItemIcon(RewardAssetContainer.Instance.commonExample);
Debug.Log(currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemIcon().ToString());
Debug.Log(currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemType().ToString());
Debug.Log(currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemName());
}
}
public void SetEquippedItem(EffectTypes _itemType, Sprite _itemIcon, string _itemName)
{
for (int i = 0; i < currentlyEquippedItems.Length; i++)
{
Debug.Log(currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemType().ToString());
Debug.Log(currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemName());
Debug.Log(currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemIcon().ToString());
//////////////////// In 3 below lines the array returns null references, but why?
if (currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().GetItemType() == _itemType)
{
currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().SetItemName(_itemName);
currentlyEquippedItems[i].GetComponent<ListEquippedEntry>().SetItemIcon(_itemIcon);
break;
}
}
}
I would ask a clarification question, but I don't have the rep to comment, so I'll just take a stab at it...
You have ListEquippedItems coded similar to a singleton, but it's not technically a singleton. My guess is that the object that SetEquippedItem() is invoked on is not the same object that Awake() is invoked on.
Ways this might happen:
You create a ListEquippedItems game object in the hierarchy. This creates one instance.
You reference ListEquippedItems.Instance. This creates another instance.
You create a ListEquippedItems using the constructor. This would created another instance.
You have 2 (or more) threads reference ListEquippedItems.Instance at the same time. With just the right timing, both threads could see instance as null, and create a new instance.
How to verify if this is what is happening:
Add the following code snippet to Awake() and SetEquippedItem() methods:
if( this != instance )
{
Debug.Log( "<methodName> invoked against non-singleton instance." );
}
How to resolve the issue:
Only create the ListEquippedItems one way. If you want to keep it in the heirarchy, store the instance in the Awake() method instead of when the Instance field is referenced.
If you want a proper singleton, check out this wiki entry from the Unity wiki. It appears there's a stackoverflow discussion on the wiki entry here.
I have a gameObject 'gm' which has a script. That script has a reference to Transform parentFoo, and needs to access a non-static, public int 'x', which exists and is defined in a script attached as a component to multiple children 'bar', which are all children of parentFoo. I'm then trying to throw all these ints into a list, but the issue is with actually getting them. What's the simplest way to get this done?
The following code demonstrates what I'm trying to get done, but it doesn't work at all.
//defined in UnityEditor:
public Transform fooParent;
void blah () {
List<int> list = new List<int>();
for (int i = 0; i < fooParent.childCount; i++) {
Transform tempChild = Players.GetChild(i);
list.Add (tempChild.x);
}
}
Any theoretical discussion on why and how your answer works is more than welcome :D ("teach a man how to fish...")
EDIT: specifying title, copied text to stackoverflow; removed link from Unity Answers b.c. question was fully addressed here
New Code:
public Transform fooParent;
public void blah () {
List<int> list = new List<int>();
for (int i = 0; i < fooParent.childCount; i++) {
Transform tempChild = fooParent.GetChild(i);
list.Add (tempChild.gameObject.GetComponent<ScriptName>().x);
}
}
This is because .GetChild returns a transform so from there you must use .gameObject to get the GameObject that that transform is attached to. From there you can access the components attached to that gameobject (Scripts, Transforms, etc...) by using .GetComponent<>() where inside the <> brackets you input the name of the component (in this case the script) you want to access. Now in control of the script you can access its variables by using .variableName. Lastly the void blah should be public void blah. Unless of course you want it to be a private or static method.
I needed to find inactive objects in Unity3D using C#.
I have 64 objects, and whenever I click a button then it activates / inactivates objects for the corresponding button at runtime. How can I find inactive objects at this time?
Since Unity 2020
In the years since this question was asked, Unity put in the exact thing you need. At least, the exact thing I needed. Posting here for future peoples.
To find an object of a certain type whether it's on an active or inactive GameObject, you can use FindObjectsOfType<T>(true)
Objects attached to inactive GameObjects are only included if inactiveObjects is set to true.
Therefore, just use it like you regularly would, but also pass in true.
The following code requires System.Linq:
SpriteRenderer[] onlyActive = GameObject.FindObjectsOfType<SpriteRenderer>();
SpriteRenderer[] activeAndInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true);
// requires "using System.Linq;"
SpriteRenderer[] onlyInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true).Where(sr => !sr.gameObject.activeInHierarchy).ToArray();
The first array includes only SpriteRenderers on active GameObjects, the second includes both those on active and inactive GameObjects, and the third uses System.Linq to only include those on inactive GameObjects.
See this answers for Unity 2020 and higher.
Before Unity 2020
Well, using GameObject.Find(...) will never return any inactive objects. As the documentation states:
This function only returns active gameobjects.
Even if you could, you'd want to keep these costly calls to a minimum.
There are "tricks" to finding inactive GameObjects, such as using a Resources.FindObjectsOfTypeAll(Type type) call (though that should be used with extreme caution).
But your best bet is writing your own management code. This can be a simple class holding a list of objects that you might want to find and use at some point. You can put your object into it on first load. Or perhaps add/remove them on becoming active or inactive. Whatever your particular scenario needs.
If you have parent object (just empty object that plays role of a folder) you can find active and inactive objects like this:
this.playButton = MainMenuItems.transform.Find("PlayButton").gameObject;
MainMenuItems - is your parent object.
Please note that Find() is slow method, so consider using references to objects or organize Dictionary collections with gameobjects you need access very often
Good luck!
For newer Unity versions this answer provides probably a better solution!
First of all
In general any usage of Find or it's variants should be avoided.
Actually they are never really required but only a "hot-fix" used to cover an implementation "laziness".
Usually from the beginning storing and passing on required references is always the better approach.
Especially in your case you seem to have a fix amount of objects so you could probably already reference them all in a certain "manager" component and store them in a list or array (them you can get a reference by index) or even a Dictionary<string, GameObject> (then you can also get the according reference by name - you can find an example below).
Workarounds
There are alternative solutions (FindObjectsWithTag, FindObjectsOfType) but it will always be quite expensive (though most of the Find variants are expensive anyway).
You could e.g. also "manually" iterate through all objects in the scene using Scene.GetRootGameObjects
Returns all the root game objects in the Scene.
And then search through them until you find your object. This way you get also inactive GameObject.
public static GameObject Find(string search)
{
var scene = SceneManager.GetActiveScene();
var sceneRoots = scene.GetRootGameObjects();
GameObject result = null;
foreach(var root in sceneRoots)
{
if(root.name.Equals(search)) return root;
result = FindRecursive(root, search);
if(result) break;
}
return result;
}
private static GameObject FindRecursive(GameObject obj, string search)
{
GameObject result = null;
foreach(Transform child in obj.transform)
{
if(child.name.Equals(search)) return child.gameObject;
result = FindRecursive (child.gameObject, search);
if(result) break;
}
return result;
}
But ofcourse this should be strongly avoided and the usage of such deep searches reduced to a minimum!
What I would do
Another way - in my eyes the best approach here - could be to have a certain component attached to all your objects and actually store all the references once as said before in a dictionary like e.g.
public class FindAble : MonoBehaviour
{
private static readonly Dictionary<string, GameObject> _findAbles = new Dictionary<string, GameObject>();
public static GameObject Find(string search)
{
if(!_findAbles.ContainsKey(search)) return null;
return _findAbles[search];
}
private IEnumerator Start()
{
// Wait one frame
// This makes it possible to spawn this object and
// assign it a different name before it registers
// itself in the dictionary
yield return null;
if(_findAbles.ContainsKey(name))
{
Debug.LogError($"Another object with name /"{name}/" is already registered!", this);
yield break;
}
_findAbles.Add(name, gameObject);
}
private void OnDestroy ()
{
if(_findAbles.ContainsKey(name))
{
_findAbles.Remove(name);
}
// Optionally clean up and remove entries that are invalid
_findAbles = _findAbles.Where(kvp => kvp.Value).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
and then use it like
var obj = FindAble.Find("SomeName");
if(obj)
{
// ...
}
Also for this the component would need to be enabled at least once so Start is called.
Again an alternative would be to have instead a
public void Initialize(string newName)
{
if(_findAbles.ContainsKey(name))
{
Debug.LogError($"Another object with name /"{name}/" is already registered!", this);
return;
}
name = newName;
_findAbles.Add(name, gameObject);
}
which you could call also after e.g. spawning an inactive object.
You can use Predicates.
Just get the gameObjects and check them whith a Predicate as below:
public List<GameObject> FindInactiveGameObjects()
{
GameObject[] all = GameObject.FindObjectsOfType<GameObject> ();//Get all of them in the scene
List<GameObject> objs = new List<GameObject> ();
foreach(GameObject obj in all) //Create a list
{
objs.Add(obj);
}
Predicate inactiveFinder = new Predicate((GameObject go) => {return !go.activeInHierarchy;});//Create the Finder
List<GameObject> results = objs.FindAll (inactiveFinder);//And find inactive ones
return results;
}
and don't forget using System; using System.Collections.Generic;
You can do this at runtime by having your inactive gameobject under an active parent object as previously mentioned; slightly different from what was mentioned, this is an approach I've used for activating/deactivating menus that should be inactive by default:
canvas = GameObject.FindGameObjectWithTag("GameMenu").GetComponentInChildren<Canvas>().gameObject;
Now you can change its activeSelf to toggle it in a method/event listener of your choice:
canvas.SetActive(!canvas.activeSelf);
Even while it is inactive, you can still use the tag property of it and use it for a filter, if getting multiple components of the same type. I haven't tested this using GetComponentsInChildren, but you could probably use a 'Single' linq query, and get the object by tag name which would require creating a tag for every gameobject you want to do this to.
Although its not the correct answer, but this is what I did in my case.
1) Attach a script to (inactive) game objects and instead of setting then inactive keep it active.
2) Position them out of the scene somewhere.
3) Set a flag in the script which says inactive.
4) In Update() check for this inactive flag and skip function calls if false.
5) When needed the object, position it at the proper place and set the flag active.
It will be a bit of a performance issue but that's the only workaround I could think of so far.
I'm creating a game in C# with Unity3D and I need to call a script that I create on runtime, so I know the name of the script because I have an object named exactly like it.
I create the script on runtime and then I save it to my Resources folder, but then I change the scene and need to call that script and attach it to my Empty GameObject in the current scene, so I would like to use something like that:
Script myScript = Resources.Load(myObject.name) as Script;
But I know this can't be done like this, so I'm looking for a way I can do what I want, because I need to load that new script to add some functions on runtime.
Do you know any solution to my problem? Thanks a lot!!
That's actually easier than you think. You can add a script to a game object using the AddComponent method. So after your level has changed you can just get hold of your empty game object and do a:
var gameObject = ...
gameObject.AddComponent<Script>(); // where Script is the name of your Script's C# class
See http://docs.unity3d.com/Documentation/ScriptReference/GameObject.AddComponent.html
GameObject gameObj = new GameObject();
Type scriptType = Type.GetType(nameSpace.className);
if (scriptType == null)
{
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
var tempType = a.GetType(nameSpace.className);
if (tempType != null)
{
scriptType = tempType;
}
}
}
gameObj.AddComponent(scriptType);