I am trying to develop a small 2D Unity platformer to learn to work with Unity's interface. I have been having an issue trying to make a gameobject track clones of an enemy and follow it. The function is to make it so when you hover on the enemy clone, it shows the enemy's health. But when I hover on the enemy, it tracks the position of the original enemy, not the clone. Both gameobjects have the same name. What I want is the GameObject HoverDataDisplay (as shown in the screenshot) to track its sibling, Enemy.
The current code that I have for the tracking script is as shown:
private GameObject Enemy;
void Start() {
Enemy = GameObject.Find ("Enemy");
}
void Update(){
transform.position = new Vector3 (Enemy.transform.position.x - 0.57f, Enemy.transform.position.y + 1.5f, Enemy.transform.position.z);
}
But the GameObject (HoverDataDisplay) Only follows the original enemy.
Thanks for your help!
In Unity, you can use the forward slash "/" to find a Child of an Object. Since the name of the parents are different, you can easily find them like this:
GameObject.Find("EnemyObject/Enemy");
And the second HoverDataDisplay:
GameObject.Find("EnemyObject(Clone)/Enemy");
It's not really a good idea to let your GameObject use the default "Clone" name. You should rename it after instantiating it so that it will be easy to find. Only do this if you need to find the Object after instantiating it.
This script is attached to the HoverDataDisplay GameObject
You can actually get it's parent Object which is either EnemyObject or EnemyObject(Clone) with transform.parent then use the FindChild to find the enemy Object.
//First, Find the Parent Object which is either EnemyObject or EnemyObject(Clone)
Transform parent = transform.parent;
//Now, Find it's Enemy Object
GameObject enemy = parent.FindChild("Enemy").gameObject;
I recommend you use this method instead of the first one I mentioned. The first one is mentioned so that you will know it can be done.
EDIT:
Transform.FindChild is now deprecated. You can use Transform.Find to do that same exact thing.
You can get a reference to the parent then search through the parents children
// get a reference to the rb of the parent
parentRigidbody = gameObject.GetComponentInParent<Rigidbody>();
// a reference to the camera in a sibling object
playerCam = rigRigidbody.gameObject.GetComponentInChildren<Camera>();
Related
(For 2D Project)
I created a gameObject and made it a prefab.
Now when the game starts, the prefab is used to instantiate gameObjects and they all should check if they collide with one another.
I tried other unity's collision methods but it didn't work.
They either kept colliding with themselves (their own rigidbody) or it didn't work at all.
I'm new to unity and learning things. I searched every where but didn't get my question solved. I'll appreciate any help, Thank you!
Prefab is loaded and Instantiated as such..
GameObject tile = Instantiate(Resources.Load("Prefabs/Tile") as GameObject);
Its a basic gameObject having SpriteRenderer 2D.
I used Box Collider 2D and Rigidbody 2D components on that prefab -
Inspector
A simple script which has OnTriggerEnter2D(Collider2D other) function to check if it collides..
using UnityEngine;
public class TileCollider : MonoBehaviour {
public Rigidbody2D triggerBody;
void OnTriggerEnter2D(Collider2D other) {
if (triggerBody == null)
return;
if (other.attachedRigidbody == triggerBody) {
Debug.Log("Collision!");
}
}
}
I tried it without any if statements - It triggers collision for the Rigidbody2D of the gameObject (itself)
I passed the Prefab itself to check the collision for - Script in Inspector.
This is where were things get bad. It looks for the rigidbody of its own gameObject but I wanted it to search for other cloned gameObjects from same prefab.
The first thing to look at to solve the problem is to check if your prefabs have tags attached to them.
Assuming you have the TileCollider script attached on your prefab which it looks like it is in the inspector, you are checking if the other.attachedRigidbody is its own rigid body (triggerBody). And an object cannot collide with itself. You should probably check if its own rigid body is not the other.attachedRigidbody. If its not its own rigidbody then you have collision with another GameObject!
I hope I understood your question correctly, Thanks!
There looks to be quite a few errors with your code. First, you've got a triggerBody but it doesn't look like it's assigned. Hopefully you're assigning this manually as part of the prefab arrangement, but you could guarantee this by doing that hookup in Start and throwing an error if it fails to get the Rigidbody, like:
public Rigidbody2D triggerBody;
void Start()
{
if(triggerBody == null) // Would happen if it's not set in the prefab
{
triggerBody = gameObject.GetComponent<RigidBody2D>();
}
if(triggerBody == null) // Would happen if there is no Rigidbody2D attached at all!
{
Debug.LogErrorFormat("Failed to find a Rigidbody2D to use with {0}!", gameObject.name);
}
}
Another issue is that you're asking for collisions, but your code is looking for triggers. Here's a link to an article, but the short version is that if you're checking OnTriggerEnter then at least one of the colliders involved needs to have the IsTrigger option ticked:
Another issue is that you're bailing on the operation if the triggerBody is null. As I mentioned at the start, you're not explicitly setting the triggerBody in your code, so if it's also not set in the prefab then you'd abort here even if the collider options were set correctly.
Finally, and probably most importantly, is this snippet doesn't make sense:
if (other.attachedRigidbody == triggerBody) {
Debug.Log("Collision!");
}
What you're saying here is that you want there to be a collision if the other rigidbody is the same as the local rigidbody. This would ensure the behavior you described in your post,
They either kept colliding with themselves (their own rigidbody) or it didn't work at all.
You're only calling it a collision if they're colliding with themselves! The way to check if it's NOT self-colliding is to make sure the other.attachedRigidbody is NOT equal to the local triggerBody!
What you would want instead would be:
if (other.attachedRigidbody != triggerBody) {
Debug.Log("Collision!");
}
Game Screen
Teleportation Code
Child Objects And Their Original Location
I'm new to Unity, and so after taking just some regular online courses for Unity2D I wanted to mess around with adding in different features, the first of which I decided to do was something like in Portal, where a projectile spawns two connected portals you can teleport between. However, I've run into an issue. When I'm teleporting my character, sometimes, usually when I'm teleporting too quickly but can happen at any time, the child objects to the Player game object tend to shift, and I don't understand why. **I'd like to:
Know why the offset between the parent and child objects are changing through teleportation.
Know how to fix this issue, preferably in code I can easily understand as a beginner to Unity. Also preferably in a way that doesn't involve me constantly appending the child objects to the parent object through transform position with the added offset, though if it's the simplest solution I'm not against trying it.**
Something worth noting is that the offset change is different as well, I have a Child Object called Feet which detect the Ground for jumping, which seems to remain at the location of the previous portal when it first breaks. However, another child object called Gun which is where the projectiles spawn from seem to only move down a little bit, meaning there's inconsistency in how they are offset when they break. It might be because the Feet has a collider, but I'm unsure, don't know enough, and only felt it was worth mentioning.
[SerializeField] GameObject otherPortal;
Portal otherPortalComponent;
BoxCollider2D boxCollider2D;
bool firstEntered = true;
// Start is called before the first frame update
void Start()
{
boxCollider2D = GetComponent<BoxCollider2D>();
otherPortalComponent = otherPortal.GetComponent<Portal>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (boxCollider2D.IsTouchingLayers(LayerMask.GetMask("Player")) && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
private void Teleport(GameObject obj)
{
obj.transform.position = otherPortal.transform.GetChild(0).transform.position;
}
public void SetFirstEnteredFalse()
{
this.firstEntered = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
this.firstEntered = true;
}
To simplify the question, the position of the child objects relative to the parent changes when I instantly change the parents position sometimes, why does this happen and how do I fix the issue without simply using transform.position in an Update method to constantly append the child to the parent, if possible.
I would look at your OnTriggerEnter2D method's if statement for a solution.
private void OnTriggerEnter2D(Collider2D collision)
{
if (boxCollider2D.IsTouchingLayers(LayerMask.GetMask("Player")) && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
Because you are using OnTriggerEnter this method will only be called with your feet since your player's main collider isn't a Trigger. This means that your collision variable that you call your Teleport method on is actually your feet object, not your player's body object. So you are changing the offset of your feet from your player in your Teleport method.
I would try changing your collision method to OnCollisionEnter2D(Collision2D)
which would pick up your player's base collider when it enters and not the feet collider.
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.GetComponent<Player>() && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
Since the Player script is on the same object as your base player collider you can do a simple GetComponent<Player>() call to check if its the player object. Though you could still use the Layer to check if you want.
Sorry, I know this is super basic! Finished my first Unity course and am working on my first game and just want a hockey puck to spawn at the position of the PlayerPuckSpawn GameObject in the hierarchy after the opponent scores a goal. Currently just writing a method for when the goal is scored.
public void EnemyGoalScored()
{
StartCoroutine(EnemyScored());
Destroy(gameObject);
Instantiate(gameObject, PlayerPuckSpawn.transform.position);
}
This is in the script associated with the puck, so the gameObject refers to the puck. Just can't remember how to write the script part that tells the puck to spawn at the position of the PlayerPuckSpawn, which is a GameObject in the hierarchy. I know it's super basic, but I've been searching online and through my class notes and can't find it. Super thanks to anyone who can help! :)
One variant of the Instantiate method has a location and rotation parameter. to use the world coordinates of the calling item, do:
Instantiate(prefab_to_inst,transform.position,transform.rotation);
If you do not want to copy the rotation, can also use default rot:
Instantiate(prefab_to_inst,transform.position,Quaternion.identity);
To copy the position of any item, prepend the reference before transform to use that item's location.
If you have GameObject puckspawn; defined and filled,
Instantiate(prefab_to_inst,puckspawn.transform.position,puckspawn.transform.rotation);
The function I'm currently working on instantiates a GameObject (using a prefab). I store this GameObject in a local variable
GameObject tmpObject;
Works flawlessly. Next I try to assign this GameObject to my GameObject representation of my Vive controller, which looks like this:
tmpObject = tmpController.gameObject.AddComponent<GameObject>() as GameObject;
The error that I get is that UnityEngine.GameObject cannot be converted to UnityEngine.Component.
Am I missing a simple / basic there? I tried adding a SphereCollider as per Unity's official guidline and it did work, so why can't I add GameObject? Is there a workaround to adding GameObjects to another GameObject? Any help is greatly appreciated!
You can't add GameObject to a GameObject. That doesn't even make sense to begin with.You can only attach components. I think that you want to make a GameObject a child of another GameObject... You can use the SetParent function for that. See below for examples how to create a GameObject and of what you can do:
Create a new GameOBject named "BallObject"
GameObject a = new GameObject("BallObject");
Create a new GameOBject named "BrickObject"
GameObject b = new GameObject("BrickObject");
Make "BrickObject" Object parent of "BallObject" Object
a.transform.SetParent(b.transform);
Make the "BallObject" Object to be by itself. "BrickObject" Object is not it's parent anymore
a.transform.parent = null;
Add script/component to your "BrickObject" Object
b.AddComponent<Rigidbody>();
I am currently working on a foxes & rabbits simulation, and I am completely stuck on "breeding".
The way I have built the simulation, three scripts are used; “TheGame”, “FoxScript” and “RabbitScript. Since the foxes and rabbits are essentially the same, we can reduce these three to two scripts; “RabbitScript” and “TheGame”. The RabbitScript is attached to the respective prefab; the “rabbitPrefab”, whereas TheGame is attached to an empty GameObject.
TheGame instantiates a number of RabbitPrefabs, which then move, age and breed. Since the build is supposed to collect and present data at a later stage, the rabbits are included in a list as well as being counted. This list is found in the main script, and when the rabbits breed, the offspring needs to be included in this list as well as adding to the counter.
I have tried instantiating a primitive with this method, and it works.
The Breed function in the script attached to the rabbits:
void Breed(){
float p = Random.Range (0.0f, 1.0f);
if (p < probability2breed) {
position = gameObject.transform.position;
TheGame.BreedRabbit(position);
}
}
And the BreedRabbit method in TheGame script:
public static void BreedRabbit(Vector3 position) {
GameObject rabbit = Instantiate(RabbitPrefab) as GameObject;
rabbit.transform.position = new Vector3(position);
Rigidbody gameObjectsRigidBody = rabbit.AddComponent<Rigidbody>();
rabbit.GetComponent<Rigidbody>().useGravity = false;
rabbit.name = "Rabbit#:" + rabbitCount;
rabbit.tag = "rabbittag";
rabbits.Add(rabbit);
rabbitCount++;
}
NOTES: (I figure a lot of this code seems pointless, so to answer any questions about that beforehand: I use collider to handle interactions between the agents involved, and to my understanding this calls for a rigidbody. With rigidbody they started falling, even without mass, so I had to turn of gravity. The tags are to my understanding needed for collision handlig as well.I could probably skip the count and just count the list, but this shouldn't matter now)
It keeps asking for an object reference , and I just can't figure out how this can be solved?
THe error message: "an object reference is required for the nonstatic field method or property"
I'd assume the object reference error occurs on this line?:
GameObject rabbit = Instantiate(RabbitPrefab) as GameObject;
If this is the case, it may be because the prefab hasn't been set, i.e, the script doesn't know what RabbitPrefab is.
You could set a variable in the script, and then drag your prefab onto the corresponding slot into the inspector:
public GameObject theRabbitPrefab;
GameObject rabbit = Instantiate(theRabbitPrefab) as GameObject;
If this is not the case, can you edit your question to where you are getting the error? Surely the error states which line of code the error is being generated from? :)
Edit: From Diego, if this is the case, you can add the rigidbody and configure it in your prefab, and you won't need to do it in the code for every new rabbit!