I am very new to c# and Unity and I have a question. I am not able to understand how OnCollisionEnter works fundamentally.
Like, if I have a terrain and a sphere hovering above it which has the rigid body component and I write:-
void OnCollisionEnter(Collision other)
{
Debug.Log(other.gameObject.name);
}
and then run the code we on the console screen would get "Sphere" and not "Terrain". Why is Sphere being passed to "other" and not Terrain?
Secondly, I believe, OnCollisionEnter is a part of the MonoBehavior class and our class is already inheriting it from MonoBehavior.
Then, why do we have to define OnCollisionEnter() again?
These questions are really eating my head up. I will be very grateful if anyone could help me out.
Why is Sphere being passed to "other" and not Terrain?
what is passed in other depends on which object you attached that component to ... if you attach it to the Terrain then in other you should get Sphere and viceversa. So you see the name other actually kind of makes sense, right?
Then, why do we have to define OnCollisionEnter() again?
in MonoBehaviour you can find OnCollisionEnter among al the others like Awake, Update, Start etc under "Messages", not under "private/public/whatever Methods".
In very short: Messages are internally only called by the Unity process if present in a component and not called at all if not present.
I think they use something similar to Component.SendMessage in the background but not sure.
Calls the method named methodName on every MonoBehaviour in this game object.
How it works: "OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider" - https://docs.unity3d.com/ScriptReference/Collider.OnCollisionEnter.html
OnCollisionEnter is among other functions (update, fixed update, start, awake, etc.), if defined within a body of MonoBehavior derived class, called by the Engine itself, i.e. these are engine event functions.
Why is triggered on the Sphere: Because you have your method in the class attached to the sphere object, see above how the OnCollisionEnter works.
Related
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.
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.
I am following the Space Shooter Unity tutorial, and I got to the point where the ship can shoot bolts. (Works fine.) I thought it might be interesting to have bolts affected by the speed of the ship, which I thought should be easy enough. The problem is that a prefab cannot have a GameObject as a parameter (makes sense, in case the object is destroyed). So instead I created a new function in the script Mover used in the prefab object bolt:
public void AddSpeed() {
GetComponent<Rigidbody>().velocity = new Vector3(0.0f, 0.0f, 0.0f);
}
This function stops the bolt if successfully called, for example when I include it in Mover.Start(). Then, in PlayerController, I added a call to this function right after the bolt is instantiated:
GameObject clone = Instantiate(shot, shot_spawn.position, shot_spawn.rotation) as GameObject;
clone.GetComponent<Mover>().AddSpeed();
This code compiles and executes without complaint, but it is quite clear that AddSpeed is not working (since bolts move normally). Because I don't get any error feedback I am unsure about what is going on and how to fix it, so I would really thank any help.
After I learned about Debug.Log (further in the same tutorial) I was able to figure it out on my own.
The problem is that Start() function is NOT executed inmediately after instantiation, so my function AddSpeed (which is called correctly) was working as intended, but later Start() would undo any changes.
My solution was to pass the player's speed to the script Mover and add this speed later in Start().
How can I call Update() or Start() or any other MonoBehaviour method directly?
I disabled most of my MonoBehaviour auto Update() calls by setting enabled = false; on each of them, because I need to time Update() at a very specific time for those scripts.
I am thinking about doing it like this:
List<MonoBehaviour> disabledScripts;
foreach (MonoBehaviour m in disabledScripts)
m.Update();
But i'm not sure if it is the correct way to do it since not all MonoBehaviour in disabledScripts actually implement Update().
Start is called automatically on object start and Update is called automatically on each frame by the engine.
If you want to control the order in which the scripts's Start() or Update() method is called take a look at this http://docs.unity3d.com/Manual/class-ScriptExecution.html
If you need to execute certain code in a controlled timing or need on certain conditions, better create an specific method for each object and create a controller to call this method on each object at the desired time.
For example you can leave Update() method empty for every object and create a method called MyUpdate (with parameters if you wish) and call it when needed from the controller based on your game's workflow.
Unity's base flow must be kept unaltered, because it can lead to strange behaviors. I can't think about a thing that can't be done with Unity's natural flow.
Edit:
Ok, I understand that you don't want to create a complex functionality just to try something.
As you say, disabling the behaviour forces the engine to avoid calling Update on each frame for the given MonoBehaviour, but I don't think you can call Update as you propose because in MonoBehaviour, Update has no method modifiers (see method without access modifier)
Anyway, I would create a "MyMonoBehaviour" extending MonoBehaviour with a MyUpdate() function that you will override on each object extending MyMonoBehaviour. You can call the MyUpdate function doing something like this (very similar to the code you proposed):
List<MyMonoBehaviour> disabledScripts;
foreach (MyMonoBehaviour m in disabledScripts)
m.MyUpdate();
Alright I figured it out:
script.Update();
compiles an error, the correct way to do it is:
script.SendMessage("Update");
I also found out that setting enabled = false; also disables some other methods such as Start(), so I think I'll just extend MonoBehaviour and move all Update() code to MyUpdate() as antonio said.
I have an object with a mesh collider and a prefab with sphere collider. I want the instance of the prefab to be destroyed if the two collide.
I wrote the following in a script:
private void OnCollisionEnter(Collision c)
{
if (c == target)
Destroy(transform.gameObject);
print("something"); // Doesn't get printed
}
But it is not working. I have tried toggling isTrigger on both the objects.
I had the same problem of OnCollisionEnter not being called and found this question.
For me, the problem was that I was making a 2D game so the answer is to use the OnCollisionEnter2D function instead.
Have a look at this table
If you want your OnCollisionEnter to be called make sure:
(a) Both objects have a collider attached.
(b) None of the objects is a trigger collider (this will issue OnTrigger function & not OnCollisionEnter)
(c) One of the objects (doesn't matter which of them) is a rigid, non kinematic & non static object (the second don't have to be a rigid body).
(d) Due to computational difficulties MeshCollider might have hard times colliding with other mesh collider, use them with caution.
(e) Make sure both the objects are in the same layer (or at least that they collide in scene settings).
(f) If you are working in 2d - OnCollisionEnter2D will be called, rename your function.
Make sure one of them has a non-kinematic rigidbody attached. Taken from the Unity docs:
When a collision between two Colliders occurs and if at least one of them has a Rigidbody attached, three collision messages are sent out to the objects attached to them. These events can be handled in scripting, and allow you to create unique behaviors with or without making use of the built-in NVIDIA PhysX engine.
From here: Unity3D MeshCollider
I had a similar problem. The box collider wasn't as big as the collision object. Setting the x and z values to 2 units fixed the problem!
Have you tried using the OnTriggerEnter() method and setting a collider on the object to a trigger?
If it doesn't need to tell what object its colliding with you could do a simple
void OnTriggerEnter(){
Destroy(transform.gameObject);
}
Edit:
Also I have done OnCollision like this
private string hitobject;
void OnCollisionEnter(UnityEngine.Collision hit)
{
hitobject = hit.gameObject.tag;
if(hitobject == "Plane")
{
isgrounded = true;
}
}
None of the objects are triggers and they don't need rigid bodies to work.