Getting a reference to Rigidbody - c#

I am trying to understand why I see different coders get a reference to a Rigidbody in two different ways.
One way I have seen is:
public class PlayerController : MonoBehaviour {
Rigidboody rb;
//then in the start or awake function do:
void Awake() {
rb = Getcomponet<Rigidbody>();
}
}
But then I have seen other people doing:
public class PlayerController : MonoBehaviour {
[SerializeField] Rigidboody rb;
void Start() {
}
Then they would drag the Rigidbody component in to the SerializedField in the inspector.
I know both ways work, but what is the difference between them?

As a general rule, use Getcomponet<T>(); when you have a hard dependency on the component T (for example, by using the attribute [RequireComponent(typeof(T))] in you Script) or when your script can fairly assume that a suitable reference will be there in your game object, without need to drag an instance in the Editor manually.
Use a public property when the component your script depends on is much more specific/useful to this Script than to the rest of the scripts of its GameObject, or when your GameObject might have multiple instances of that component and you will specify which one by dragging in the Editor.

There is no noticable difference in performance. Use the first method if the RigidBody component is on the same GameObject as the script, since it will make it way more diverse. Use the second method if the RigidBody component is on a different GameObject (for some reason). This will negate the need for a Find operation.

Related

Awake Method not starting

Im trying to use the Awake method without attaching it to an object, but it never initializes the class. It was supposed to initialize the class so that it could run an OnEnable method that added a subscriber to an event, but it did nothing. The only thing that works is attaching the script to a game object, but I don't want to do that, I tried changing the acmes modifier from private to protected virtual and a bunch more...
This is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovementAnimationParameterControl : MonoBehaviour
{
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
private void OnEnable()
{
EventHandler.MovementEvent += SetAnimationParameters;
Debug.Log("Enable. Im running, its not me");
}
private void OnDisable()
{
EventHandler.MovementEvent -= SetAnimationParameters;
Debug.Log("Im running its not me");
}
private void SetAnimationParameters(float xInput, float yInput, bool isWalking)
{
animator.SetFloat(Settings.xInput, xInput);
animator.SetFloat(Settings.yInput, yInput);
animator.SetBool(Settings.isWalking, isWalking);
}
}
It was supposed to initialize the class so that it could run an OnEnable method that added a subscriber to an event, but it did nothing. The only thing that works is attaching the script to a game object
When you create a class that inherits from MonoBehaviour (Aka a 'Unity Script'. You're creating a script that allows the use of certain functions at runtime, such as OnEnable and OnAwake among others.
It might not seem too intuitive, but MonoBehaviour actual inherits indirectly from GameObject. The reason for this is because GameObject is the "target" of all of these methods like Update() for example.
Without an actual GameObject for your MonoBehavior to access it's properties at runtime, your script wont do anything.
The best example would be from your code.
Take the OnAwake() method for example
private void Awake()
{
animator = GetComponent<Animator>();
}
What this method does is set the field animator to the reference of Animator. That reference is found using the inherited method called GetComponent. Get component is an inherited method from the MonoBehaviour class, which like a russian nesting doll, is inherited from GameObject.
So what you're really doing is saying "hey GameObject that this script is attached to, please give me your Animator component, if you have one."
This is where we see the issue at hand, that without an actual GameObject to access none of the methods like OnAwake() or even the method calls contained within will work, since they all(or most) come from GameObject not actually from MonoBehavior.
There is also another major obstacle to overcome specifically with the events like OnEnable(), OnAwake(), Start() etc.. This issue comes from how Unity processes scripts during runtime. Only scripts with a actual GameObject is ever scheduled to have it's components scripts to run.
Think of it like a big pile of paper work on some body's desk. This big pile of paperwork consists of all of the scripts on all gameobjects in the scene. Unity has to pull a sheet of paperwork(a script) from the pile, run it's contents, and put it in the done pile on the opposite side of the desk. With this analogy, a script that doesn't have any attached GameObject, would be a sheet a paper with no name on it. Unity will look at this paper and say "This doesn't even have a name on it, I'm not even going to look at it" and then proceeds to throw it in the trash because it's not a script that any GameObject needs to run - and that's all Unity cares about. This is because it would be inefficient to look at any scripts that no GameObject needs.

How to: Get instance of gameObject

Looking to get an instance of this game object so I can successfully use .enabled to hide it in a scene.
PlayPortalLoginButton loginButton = gameObject.GetComponent<PlayPortalLoginButton>();
Fairly new to C# and I believe I am close to achieving my goal with the line above. What needs changed? Want to understand how to correctly do this.
Here is one way you could find a component on a GameObject in the scene, where "PortalLoginButton" is the name of the GameObject as seen in the editor:
var loginButton = GameObject.Find("PortalLoginButton");
loginButton.enabled = false;
However, GameObject.Find("...") searches the name of every GameObject in the scene, and this is not usually the best way to reference a GameObject since it is not very efficient. So make sure not to use GameObject.Find("...") or similar function calls in the Update() function because it will execute every frame and slow your game down. If the GameObject is not instantiated while the game is running, it is usually better to make a global reference to any GameObject or Component that you use in your script and then drag-and-drop the GameObject with the Component you are looking for into the field in the editor. Alternatively, you can use GameObject.Find("...") in the Start() or Awake() functions to store a reference to the GameObject that you are looking for, so that the search only happens once at the start of your game.
Here is an example of how to store the reference in global field (it will show up in the editor and you can drag-and-drop the GameObject into it). The differences between using a public field vs a private field are explained in the comments (you can decide on using public or private):
// By default, private fields are not viewable in the editor,
// but the [SerializeField] attribute, placed before
// the field declaration, allows them to be visible.
[SerializeField]
private GameObject loginButtonPrivateReference;
// If you need to access loginButton from another script,
// you can make it a public field.
// Public fields are viewable in the editor by default.
public GameObject loginButtonPublicReference;
Here is an example of how you can use GameObject.Find("...") in the Awake() function:
private GameObject loginButton;
private void Awake() {
loginButton = GameObject.Find("PortalLoginButton");
}
If you need to search for GameObjects in your scene by type or by tag name, see the GameObject documentation here for more information. Searching by type is less efficient and searching by tag is more efficient than searching by name because type searches check each component on each GameObject, and tag searches search only an organized GameObject subset.
GameObject button;
void Start() {
button = GameObject.Find ("yourButtom");
}
void SomeEvent() {
button.SetActive(false);
}
I think you have to help to you
There are several ways to get access to a gameObject in a script depending on exactly which gameObject you are trying to get.
If you are trying to access a GameObject of a behavior in the behavior script then simply using gameObject should suffice since that is the gameObject that behavior is attached to.
Accessing a script that is attached to another GameObject is where it can be tricky. For scenes such as a MainMenu, there is nothing wrong with giving it a public reference as an example:
public PlayPortalLoginButton loginButton;
void setLoginState(bool activate) {
loginButton.enabled = activate;
}
With this example you would just drag and drop the gameObject with the PlayPortalLoginButton script on your manager script, the script that would control whether it is enabled or not.
If you need a more modular approach to it for example in a gameplay scene where you are populating objects at run time the approach you can take is:
PlayPortalLoginButton loginButton;
void setLoginState(bool activate) {
if(loginButton == null)
{
loginButton = FindObjectOfType<PlayPortalLoginButton>();
}
// Requires the correct version of .net otherwise you can reuse the check above...
loginButton?.enabled = activate;
}
In the above script if you needed to gameObject of loginButton, now you can access it through loginButton.gameObject.
There are some more examples you could use and other approaches however I feel the above should suffice if for a menu.
I will also mention I am avoiding example of GameObject.Find and GameObject.FindGameObjectsWithTag as those are more prone to error. They will only work if your name or tag are correctly marked, if you rename them for any reason then your scripts will fail to find the Object, and this can cause an issue with troubleshooting.
Using FindObjectOfType, you are less likely to have an error, and if you remove that script type these scripts will populate an error indicating the script no longer exists. When refactoring, if you right click on the behaviors name and use the rename option it will automatically update references to the script throughout your code.

What is better: link to a GameObject or to a Class in Unity?

Example: GameObject A has a script attached to it called MakeItRain. Inside that script is a public void Drizzle();
GameObject B also has a script and wants to tell MakeItRain to do Drizzle();
Inside the script of GameObject B, I can do this:
public GameObject makeitrain;
and then I have to use GetComponent to reach Drizzle(); in my code.
In the inspector, I drop GameObject A into the slot of makeitrain and I'm done.
However, I could also do this in the script of GameObject B:
public MakeItRain makeitrain;
and then just call makeitrain.Drizzle(); in my code of GameObject B's script, without GetComponent.
In both cases, in the Inspector, I have to drag and drop GameObject A into the slot of GameObject B.
Is there a difference or reason why I should definitely not do the last option? I understand that the first method gives me more flexibility because I could call other components of GameObject A as well and not just the script's stuff. Just wondering if there is any other rationale for not doing the second method.
The answer depends if you need to call any function or use variable from the MakeItRain script.
If you don't need to to call any function in the MakeItRain script or access any variables from it then it is better to use GameObject as the reference. Also, if what you need to do is activate, de-active, rotate the GameObject then use the GameObject as reference.
On the other hand, if you need to be able to call a function such as Drizzle or access a variable from the MakeItRain script from multiple places, then you need to use the MakeItRain reference. At this time, it doesn't make sense to use the GameObject reference since by using it, it's required to use GetComponent every-time you need to call a function or access a variable from the MakeItRain script attached to it.
Finally, when using the MakeItRain script to reference your object, you can directly and easily access the GameObject it is attached to without using the makeitrain.gameObject. This doesn't require the use of the GetComponent function.
Just wondering if there is any other rationale for not doing the
second method.
Performance issue due to the required use of the GetComponent function is the reason. Using it once in the Start or Awake function and initializing your MakeItRain variable is better.
For example, this is better:
public MakeItRain makeitrain;
void Start()
{
makeitrain = GetComponent<MakeItRain>();
}
void Update()
{
makeitrain.Drizzle();
}
than this:
public GameObject makeitrain;
void Update()
{
makeitrain.GetComponent<MakeItRain>().Drizzle();
}
And should be used to avoid having to search for the component on the native side every frame.
Using MakeItRain and explicitly defining the type is better than using GameObject.
As #hacksalot commented, using MakeItRain offers strong typing. One of the benefits of this is related to your comment:
In both cases, in the Inspector, I have to drag and drop GameObject A
into the slot of GameObject B.
If you explicitly set the public variable type to MakeItRain rather than GameObject, it is not possible to drag and drop a GameObject A into the slot of GameObject B unless GameObject A is has a script of the correct type. This gives you a compile-time/editor-time check that you are linking to the correct GameObject in the Unity Editor inspector.
Also, while not necessarily so, using GameObject references often encourages messier code, whether because of unnecessary chaining of methods together (e.g. GetComponent) just because the type wasn't specified, or because it adds a bit of friction to writing & using helper methods. Consider even in a simple example which one reads better:
makeitrain.Drizzle()
makeitrain.GetComponent<MakeItRain>().Drizzle()
I understand that the first method gives me more flexibility because I
could call other components of GameObject A as well and not just the
script's stuff.
Note that you still have the flexibility to access GameObject, it's just a bit more verbose (which is one downside of this approach):
public MakeItRain makeitrain;
void Start()
{
makeitrain.gameObject.SetActive(false)
}
However, you'll likely be using helper methods anyway (for anything more than basic calls), or even wrapper methods (which are inconvenient to write but sometimes helpful for readability).
In most cases, the benefits of linking to the class rather than the GameObject outweigh the downsides.
If u don't want to use GetComponent(), you can simply use SendMeassage(), like this
public Gameobject makeItRain;
void Start(){
makeitrain.SendMeassage("Drizzle");
}
Another way to link a script is that use FindObjectOfType(), which do not need to drag and drop GameObject into the slot, here is the sample
void Start(){
MakeItRain makeitrain = FindObjectOfType("MakeItRain");
}
Also you can use Gameobject.Find() to link a GameObject instead of dragging into slot, but I don't recommand this way, it cost a lot performance since you need to find every single GameObject in scene.

How to prevent "public" in Unity?

Being a student and having finished my school year I studied the development of Unity in C # to learn how to use the engine but also and above all to have fun.
One of the advantages of Unity is that you can pass a game object parameter directly in drag/drop, but for this to be possible it is necessary that the said variable is in public, which goes against what I have learned in class (the attributes must be private as often as possible).
My question was, how to make sure to have the same result with private attributes, i.e., recovered the game object manually without using the drag/drop system?
Thanks in advance and sorry for my English level.
It is not necessary to mark the variable as public in order to make it appear in the Editor. Just just put the SerializeField attribute on top of the private variable and it should show up in the Editor.
[SerializeField]
private GameObject cam1;
You can also do the opposite which is make public variable not show in the Editor. This can be done with the HideInInspector attribute.
[HideInInspector]
public GameObject cam1;
But there is another method in Unity to get a GameObject, by avoiding
drag/drop ?
Yes. Many ways.
If the GameObject already exist in the scene then You can with one of the GameObject.FindXXX functions such as GameObject.Find, GameObject.FindGameObjectWithTag
For example,
[HideInInspector]
public GameObject cam1;
// Use this for initialization
void Start()
{
cam1 = GameObject.Find("Name of GameObject");
}
If the GameObject is just a prefab then put the prefab in a folder named Resources then use Resources.Load to load it. See this for more thorough examples.
Finally if you just want to get the component of that GameObject, just like your cam1 variable which is not a GameObject but a component, First, find the GameObject like I did above then use the GetComponent function to get the component from it.
private Camera cam1;
// Use this for initialization
void Start()
{
GameObject obj = GameObject.Find("Main Camera");
cam1 = obj.GetComponent<Camera>();
}

Unity create a permanent access to an gameobject

in many of my scripts I have to deal with the playerObject. I don't want to search for it in each script.
I thought about creating an static class to have permanent access to the player.
This is how I do it
public class Globals
{
public static GameObject playerObject = GameObject.FindGameObjectWithTag(StringCollection.PLAYER); // The playerObject
public static Rigidbody playerRigid = GameObject.FindGameObjectWithTag(StringCollection.PLAYER).GetComponent<Rigidbody>();
} // The rigidbody of the player
So I want to store all the variables that I need many times. When loading a new scene, it says the rigidbody tries to access an object, that got destroyed previously.
How can I fix it?
I thought about creating an object inheriting from Monobehaviour that gets never destroyed but then I would have to create a reference to it in all my scripts..
Anything that inherits from Unity's UnityEngine.Object are destroyed even when made static.
If you want it to stay, you have to call DontDestroyOnLoad on it. In this case call:
DontDestroyOnLoad(Globals.playerObject);
This should make the playerObject and every component that is attached to it to stay through the next scene.

Categories

Resources