if (!this) { return; } makes sense in C#? - c#

I'm coding in Unity and I've found that sometimes a method can be called by an Invoke function even if the game object has been destroyed.
That's why I've added this check, and it seems to work correctly, but it's quite weird.
Let's say I have an object that I want to self destroy in one second.
I add an Invoke at the Awake to self destroy it.
But also, I want to destroy the game object instantly if the game is over. The object is subscribed to a Gameover event.
void Awake ()
{
Invoke ("_destroy", 1);
GameMachine.Gameover += _destroy;
}
The _destroy method is like this:
void _destroy()
{
if (!this) {
return;
}
GameMachine.Gameover -= _destroy;
Destroy (this.gameObject);
}
I've added that if (!this) check because I've found that the Invoke could reach the _destroy method, even if the object was already destroyed by the game over event.
This makes any sense, is it possible to happen?
Thanks

As far as I can tell, It will work, BUT, it's not a great idea to do so.
Unity's MonoBehaviour overrides the bool operator. Therefore, when you check
!this
you ARE checking whether the Object (and thereby the script) exists.
As a side note, if you want to unsubscribe events, use the OnDisable function instead. Code example
void OnDisable () {
GameMachine.Gameover -= _destroy;
}

Related

Method not being called after invoke delay (unity)

I'm trying to create a apply a fairly simple buff to my character for a short duration, but it seems that whatever method I try to use it does not want to apply.
I've tried creating a manual timer, making use of coroutine and now making use of invoke with a delay, but in each case it applies the increase in speed on collision, but then never does anything after the delay (also doesn't debug log after delay), see the screenshot below for the code.
Image of code
Any help would be greatly appreciated! Thanks
You destroy the gameobject allong with the script, that's why it never get's executed.
I'm not sure why you destroy the gameobject but you can remove the Destroy(this.gameObject);
If the Destory() method is necessary, you can use it inside your TimerBuff() method like this.
public void TimerBuff()
{
Debug.Log("timerstop");
PlayerMovement.runSpeed = 5f;
Destroy(gameObject);
}

Collision triggers without direct attachment to object

This is a very amateur question and I'm not entirely sure how to title it. Essentially I want to use the OnCollisionEnter2D method for multiple (while separate) game objects in the same script, rather than a script for each separate game object.
I've tried the following with no success:
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.name == "Ballone")
{
charge += 1;
}
}
I believe this isn't functioning properly because it's attached to a script for my event system, but I'm not sure how to change the syntax.
OnCollisionEnter2D is an event handler.
During FixedUpdate when Unity performs collision calculations, it notifies colliders about whether they have collided and passes them the relevant Collision2D object. If that collider is attached to an object with a script component that has a OnCollisionEnter2D method, then the Collision2D object is passed into that method and the collision is handled.
In that context, it doesn't make sense for an unrelated object to handle OnCollisionEnter2D events that belong to other objects. However, you can capture the events on each object and then pass those events (or data from those events) to another more "general purpose" method in another script if you want.
E.g
Create a class and method to handle your collisions then attach it to a GameObject in the scene such as an empty GameObject named CollisionManager
class CollisionManagerScr : MonoBehaviour {
public void HandleCollision(GameObject collidingObject, Collision2D col) {
Debug.Log("I've managed this collision with " + collidingObject.name);
Debug.Log(col) // Some info about the Collision2D object
}
}
Then, attach a script to the objects that you want to check for collision on:
public class MyCollisionScript : MonoBehaviour {
private CollisionManagerScr colManager;
void Start() {
colManager = GameObject.Find("CollisionManager").GetComponent<CollisionManagerScr>();
}
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.name.equals("Ballone")) {
if (colManager != null) {
colManager.HandleCollision(this, col); // Passes in the object that detected the collision
colManager.HandleCollision(col.gameObject, col) // Passes in the Ballone object
}
}
}
}
Now, whenever OnCollisionEnter2D is triggered for those objects, it calls HandleCollision. HandleCollision can then do whatever you want it to do. There undoubtedly is a use case for something like this but it smells a lot like unnecessary abstraction to me.
Please bear in mind that I wrote this on the fly so there could be problems/typos etc. Also, this is just one potential approach. You could also implement an approach that is effectively the same using EventHandlers etc. but that is just adding an additional event handling layer to the existing event handling layer.

What is "Messages" in Unity's MonoBehaviour class? (Start,Update,Awake...etc)

What is the "Messages" of this page? (Start,Update,Awake...etc)
Is is a something like virtual method or event?
Or "Messages" is one of the C# syntax?
https://docs.unity3d.com/ScriptReference/MonoBehaviour.html
The Unity engine basically calls these methods on MonoBehaviours if they are defined, depending on Engine events.
For example:
Awake is called when the script instance is being loaded.
Start is called in the first frame when the script is being enabled, before every Update method and after Awake
Update is being called in every frame
There are numerous messages as you can see it int the DOCs, and they are being called depending on engine events.
You can not call these events they are being handled by the engine!
Most commons are:
Update
Start
Awake
OnDestroy
OnEnable
But please note that using these methods(messages) while they are empty have a small overhead because the engine will call them, even if they are empty.
Another advanced thing is that some of these messages can be Coroutines. And you can give them some advanced functionality.
IEnumerator Start()
{
Debug.Log("First frame i'm being enabled! yeee");
// After 2 seconds i'm gonna blink
yield return new WaitForSeconds(2.0f);
Debug.Log("I'm going to blink");
Blink();
}
'Message' here is a synonymous for Function/Method, which are just Automatically called functions by unity engine, for any script inheriting from MonoBehaviour and attached to an ACTIVE gameobject in your scene.
Consider an animal script
public class Animal : MonoBehaviour
{
void Awake()
{
Debug.Log("Code here in awake is executed by unity the first time this object is activated, and never again in the lifetime of this object.");
}
void Start()
{
Debug.Log("Start is similar to awake but is executed after 'Awake' is executed on all game objects.");
}
void OnEnable()
{
Debug.Log("Code executed EVERYTIME your object is activated, including the first time you enter playmode, provided this object is active.");
}
void OnDisable()
{
Debug.Log("Code executed EVERYTIME your object is deactivated, does not include the first time you enter playmode if the object was disabled before playing.");
}
}
And so on, every Message/Function/Method has its use case and time, you'll get the hang of it when you start using them, they are the core of the engine.

How to wait some seconds before doing something in OnMouseUp Event

I'm writing a game with some animations, and use those animations when the user clicks on a button. I would like to show the user the animation, and not "just" call a new level with Application.loadLevel. I thought I could use the Time.DeltaTime in the onMouseUp method and add it to a predefined 0f value, then check if it is bigger than (for example) 1f, but it just won't work as the onMouseUp method adds just "it's own time" as the delta time.
My script looks like this now:
public class ClickScriptAnim : MonoBehaviour {
public Sprite pressedBtn;
public Sprite btn;
public GameObject target;
public string message;
public Transform mesh;
private bool inAnim = true;
private Animator animator;
private float inGameTime = 0f;
// Use this for initialization
void Start () {
animator = mesh.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
}
void OnMouseDown() {
animator.SetBool("callAnim", true);
}
void OnMouseUp() {
animator.SetBool("callAnim", false);
animator.SetBool("callGoAway", true);
float animTime = Time.deltaTime;
Debug.Log(inGameTime.ToString());
// I would like to put here something to wait some seconds
target.SendMessage(message, SendMessageOptions.RequireReceiver);
}
}
}
Im not entirely sure what your trying to do by using Time.deltaTime in onMouseUp. This is just the time in seconds since the last frame was rendered, and should act the same no matter where you try to access it. Normally it is used in a function that is called every frame, not one-off events like onMouseUp.
Despite not being certain what you are trying to achieve, it sounds like you should be using Invoke:
http://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
Just put the code you wish to be delayed into a separate function, and then invoke that function with a delay in onMouseUp.
EDIT: To backup what some others have said here I would not use Thread.Sleep() in this instance.
You want to do this (and all waiting functions that do not appear to make the game "freeze") by blocking the Update loop by using a Coroutine.
Here is a sample of what you are probably looking for.
void OnMouseUp()
{
animator.SetBool("callAnim", false);
animator.SetBool("callGoAway", true);
//Removed the assignement of Time.deltaTime as it did nothing for you...
StartCoroutine(DelayedCoroutine());
}
IEnumerator DoSomethingAfterDelay()
{
yield return new WaitForSeconds(1f); // The parameter is the number of seconds to wait
target.SendMessage(message, SendMessageOptions.RequireReceiver);
}
Based on your example it is difficult to determine exactly what you want to accomplish but the above example is the "correct" way to do something after a delay in Unity 3D. If you wanted to delay your animation, simply place the calling code in the Coroutine as I did the SendMessage invocation.
The coroutine is launched on it's own special game loop that is somewhat concurrent to your game's Update loop. These are very useful for many different things and offer a type of "threading" (albeit not real threading).
NOTE:
Do NOT use Thread.Sleep() in Unity, it will literally freeze the game loop and could cause a crash if done at a bad time. Unity games run on a single thread that handles all of the lifecycle events (Awake(), Start(), Update(), etc...). Calling Thread.Sleep() will stop the execution of these events until it returns and is most likely NOT what you're looking for as it will appear that the game has frozen and cause a bad user experience.

Unity 3D's component-based design model and race conditions

I'm having a bit of trouble with Unity 3D's Component-based design model.
Here's an example that demonstrates my problem:
class MyComponent : MonoBehaviour
{
MyType entity;
void Start()
{
entity = (MyType)FindObjectsOfType(typeof(MyType)).First();
}
void MyMethod()
{
var x = entity.SomeProperty; // <= NullReference exception
}
}
// ....
var clone = (GameObject)Instantiate(original);
clone.GetComponent<MyComponent>().MyMethod();
Sometimes, not always though, MyMethod executes before Start so what I end up doing is move all the initializations I usually have in Start to MyMethod which is quite an ugly workaround:
void Start() { }
void MyMethod()
{
entity = (MyType)FindObjectsOfType(typeof(MyType)).First();
var x = entity.SomeProperty; // <= now it's fine.
}
My question is, what is the correct way of working with this pattern (without a constructor)?
That probably happens when you call MyMethod from the Awake of another Component, because Awake is called before the game starts. The only solution I see is to make sure that you don't call methods on other components (in this case MyMethod) in the Awake() event, but rather in the Start() event.
The Unity documentation says "Awake is called before the game starts" but this is not precise. A component's Awake method is called when in a scene the gameobject becomes active which carries the component.
Some gameobjects will be active by default when a scene is loaded, but other gameobjects might be activated later. Then there is the possibility of dynamically adding components via AddComponent.
This aspect should be kept in mind when relying on dependencies between Awake and Start methods. Another example for a race condition is to enable a component from the Update method. While enabling a component implies that its Start method will be called, this will not happen in the running update cycle, so that other components cannot rely on the first component's ".enabled" property to determine wheter it has been started or not.

Categories