This question already has answers here:
How to find child of a GameObject or the script attached to child GameObject via script
(4 answers)
Closed 4 years ago.
I want to access the animator component of my player character. The character is spawned under the GameObject Character position, which it self is the child of Game Manager.
The character prefabs have various names, so I cannot find them through exact name. So its easier to just get the only child of Character position.
Game Manager
Character position
Player Prefab
Ive searched online and tried GetChild by index and GetComponentInChildren. None of them work. Below is the script I wrote for this:
private Animator archerAnimator;
private float startSpeed;
GameObject charPos;
GameObject archer_;
// Use this for initialization
void Start () {
charPos = GameObject.Find("Game manager/Character position");
Debug.Log(charPos);
archer_ = charPos.transform.GetChild(0).gameObject;
archerAnimator = charPos.GetComponentInChildren<Animator>();
Debug.Log(archerAnimator);
}
charPos is found, but for archer_ I get the error, Transform child out of bounds. The player archer is not there but is spawned at run time when the scene starts, is this the reason it cannot find it so quickly?
Some guidance would be appreciated.
Thank you
I think you're scanning for the player too early. You should reverse your discovery logic. Instead of scanning for the player and its Animator, you should put a script on the player itself that runs after it is created and reports itself to the game manager or whatever object needs access to it, something like this:
void Start() { GetComponentInParent<GameManager>().OnPlayerSpawned(this); }
I'll also mention that some script finding an object by name and accessing its components is a generally bad idea. Here's a design guideline to always keep in mind: You should traverse Unity's object hierarchy as infrequently as possible, and even if you do, you should only traverse objects that don't have other scripts attached. In this case, you should also put the logic to control the Animator inside your Player script. Then, you wouldn't need to get a reference to the Animator in the first place.
Related
I am working on a game and came to a point where I had nothing in my mind. want to take reference of an object by collider frequently. I don't know how to make so please help.
Thanks in advance
Take Object A with script As and script 2As and collider Ac
Object B with script Bs and collider Ac
Their might be 3 different things you are looking to achieve here:
Reference Script Bs from script As:
First you need to reference the Game Object that Bs is on (B) from script As
At the very top of script As you would add:
public GameObject objectB;
Then later in your code when you want to reference the script on objectB you can do:
int value = objectB.GetComponent<Bs>().score;
Where score is a variable in Bs
Reference Script 2As from As:
In this case we already are on the same gameobject so we can simply do
int value = gameObject.GetComponent<2As>().Score;
Where Score is a integer in the Script 2As
The reason we can just say gameObject.GetComponent here instead of having to reference the other object is because gameObject with a lower case g refers the object that the script is sitting on. So effectively we just say "get the other script on this gameobject"
Lastly (I think you are trying to do this maybe?)
Take a collider and get the GameObject on this collider then get the script on that GameObject:
public Collider coll;
int val = coll.GameObject.GetComponent<script>().value;|
I hope that helps, I didn't really understand your question so if this doesn't answer your question let me know :)
Also join my discord if you have any other questions: discord.io/gamedev
We really need more info about what you're trying to accomplish. Are you trying to know what an object collided with recently? trying to raycast? Depending on what you want to do with it, you might need to reference different components on the gameobject. etc.
But here's an example:
//Component on a gameobject with a collider attached.
public class RefByCollision : MonoBehaviour
{
[SerializeField] //makes it show in inspector
private GameObject lastCollisionGO;
//Called everytime this gameobject collides with another that has a collider (non trigger)
private void OnCollisionEnter(Collision other)
{
lastCollisionGO = other.gameObject; //replaces reference with the newly collided object.
}
}
This component on a gameobject with a collider attached will replace the reference stored in lastCollisionGO everytime the object collides with another object.
Note: If you try to query the value of lastCollisionGO before it collides with something, it will be empty and give you a null reference exception.
In my scene I am instantiating at runtime a prefab, which has a dynamic Rigidbody2D and a BoxCollider2D;
It also has a script with an OnTriggerEnter2D method, that runs the following function:
/* ... */
public Rigidbody2D rigidbody;
private void StopMovement()
{
rigidbody = GetComponent<Rigidbody2D>();
rigidbody.velocity = Vector2.zero;
Debug.Log(rigidbody.velocity);
}
Basically I want the object to stop when he collides with the trigger. The problem is it doesn't stop, and even if the console message says (0.0, 0.0) the rigidbody's inspector looks like this:
And as you can see, its velocity isn't actually zero.
I tried adding
rigidbody.constraints = RigidbodyConstraints2D.FreezeAll;
but all it does is freezing the position on the prefab, not on the actual gameObject instance. And I don't get how that's possible, also considering that the Rigidbody field on the script is occupied by Ball(clone), which is the name of the gameObject that's in the scene.
I also tried using GetComponent in Awake/Start instead of there, but the result was the same.
I've already looked up a bunch of other posts, both here and on UnityAnswers, but none of them could help me. What is happening exactly?
You are accessing the prefab and not the actual gameObjecy and so the rigidbody. When you instantiate the ball use a
'GameObject ballClone = Instantiate(ballpref, pos, quaternion);'
In this way you can access to ballClone.getcomponent() and it will work fine
Turns out that what caused that strange behaviour was related to a custom Event System, that's why it has taken so long to figure out.
Basically I was assigning the prefab as the target object in the Event Listener's inspector, thinking that its effect would be automatically applied to all the instances of that prefab, but in the end it doesn't work like that.
If anyone has the same issue, I solved it by putting the Event Listener component onto the vary target object, so basically it references itself and causes the expected behaviour.
Thanks for everyone's time, this can be closed now.
The Unity Manual says the Resources.Load returns the requested asset as an Object.I wonder why could't I use the returned Objectdirectly.For example,I have a Text prefab and I want to add it's instance to the Hierarchy,but the Code below won't work
Text prefab;
private void Start()
{
prefab = Resources.Load<Text>("Prefabs/Text");
GameObject canvas = GameObject.Find("Canvas");
prefab.transform.SetParent(canvas.transform);
}
I must Instantiate the return of the Resources.Load first like below
Text prefab;
private void Start()
{
prefab = Resources.Load<Text>("Prefabs/Text");
GameObject canvas = GameObject.Find("Canvas");
Text text = Instantiate(prefab);
text.transform.SetParent(canvas.transform);
}
I don't know what's the difference between the Instantiate result and Resources.Load result,and what the Instantiate do ,so that it's return can be added to Hierarchy.
Forgive my poor English!
To use the Method Instantiate(GameObject) you would write a new component, create a new variable of type GameObject, attach the component to an GameObject, and fill the variable in the inspector.
To use the Method Instantiate(Resource.Load("object path")) you just need the name/path of the Prefab.
this is extremely useful if you have a huge amount of generated parts in your game (so there are no gameobjects placed in the editor), if you'd want to avoid Resource.Load you'd need some "data-holder-gameobject" placed in an nearly empty scene. edited to make my point a bit clearer
it is aswell helpfull if you have large number of different Prefabs and your method knows the name of the object it wants to build, or you just simply dont want to drag and drop all those prefabs into the inspector window
Resource.Load, loads data from your drive. it's possible that your game is played from a Hard drive, which would mean to load the prefabs the hard drive needs to rotate, position the read-head, and so on.
Instantiate is slow itself even without the need of Resource.Load Instantiate is not that fast. if it happens that you need it very often ( multiple times per second) you should consider some kind of object-pool 1
In my fps level (Unity), targets spawn at a random position. I want to make sure targets can't spawn behind objects or inside objects.
To make sure they don't spawn behind an object, I've made a raycast going from the player to the target. If it's obstructed I recalculate the spawn point. This works fine, but, since the targets are spheres the raycast won't be obstructed when a target is 50% inside an object, for example the floor. I don't want that, obviously.
To determine whether or not the target is in the bounds of another object, I tried using OnCollisionEnter and OnCollisionExit. While this works when simply moving a target inside another object, it seems to be unreliable when one script's Update cycle is recalculating the spawn position while the target's Update cycle is keeping track of the Collision.
So I looked for a different approach. Here's what I came up with (from the Unity docs):
m_Collider2 = spawnpoints[i].GetComponent<Collider>();
m_Collider = world.GetComponentInChildren<Collider>();
if (m_Collider.bounds.Intersects(m_Collider2.bounds))
{
Debug.Log("Bounds intersecting");
}
The Game Object world is the parent in which I put all the objects of my gaming world.
The problem is that he only takes into account the collider of the first object. I basically want to use one big collider, which is composed by all the level objects.
Is this possible? Or does anyone know a different approach on how I can achieve this?
You should use the GetComponentsInChildren method instead of GetComponentInChildren, so that you can get from it an array of colliders on which you can execute a foreach to check if the bounds are intersecting.
I.E.:
m_Collider2 = spawnpoints [i].GetComponent<Collider>();
m_Collider = world.GetComponentsInChildren<Collider>();
foreach(Collider objCollider in m_Collider) {
if (objCollider.bounds.Intersects(m_Collider2.bounds))
{
Debug.Log("Bounds intersecting");
break;
}
}
But, this way of doing things is very heavy for the CPU, since GetComponent methods are really slow, so their use should be limited inside Awake and Start methods if possible.
Another approach to the problem would be to create a List<Collider> at the start, and add to it the starting children of your World game object. If another one is instantiated, just Add it to your list, if it's destroyed, just Remove it.
Then, just before instantiation, you can check the bounds by looping inside the List with a foreach, the check will be a lot more faster.
==================================
EDIT:
Ok, here's the deal. First of all, add these lines to your World game object script (I guess you called the class World):
using UnityEngine;
using System.Collections.Generic; //Namespace needed to use the List type
public class World : MonoBehaviour {
//The list which will hold references to the children game objects colliders
public List<Collider> childrenColliders;
private void Start() {
//Code used to populate the list at start
childrenColliders = new List<Collider>(GetComponentsInChildren<Collider>());
}
Now, since in the script which spawns a new object has already a world variable which holds a reference to the World class:
foreach(Collider coll in world.childrenColliders) {
if (coll.bounds.Intersects(m_Collider2.bounds))
{
Debug.Log("Bounds intersecting");
break;
}
}
And, of course, as I said before remember to add a newly spawned game object's collider to the list with:
void AddNewGameObject() {
// spawnPoint is the transform.position Vector3 you'll use for the new game object
var newGameObject = Instantiate(yourObjectPrefab, spawnPoint, Quaternion.identity, world.transform);
world.childrenColliders.Add(newGameObject.GetComponent<Collider>());
}
That's pretty much it. ;)
What is the best way to detect if I passed obstacles (succeed to not collide with them) in an endless runner game?
What I would do is to place an empty GameObject (no 3d/3d model attached to them) in the parts of the path which are clear (no obstacles). And to those empty GameObjects I would add a collider which is triggered.
Then I will add a script to those empty GameObjects which will have this piece of code:
void OnTriggerEnter(Collider other) {
//Do something, like for example increase a counter of points
// or show a success message...
}
So when the player goes through these empty GameObjects, it will trigger some actions that represents success.