Unity OnDisable - can I virtual override the built in unity methods? - c#

Is it possible to use virtual void OnDisable() ? Its a built in Unity method, just like Start() or OnEnable(). I couldnt find any resources online where its explained if we can use virtual on these built in methods.
public abstract class ShipComponent : MonoBehaviour
{
[HideInInspector] public ShipControl shipControl;
public virtual void Init(ShipControl control)
{
this.shipControl = control;
StartCommonCoroutines();
}
public virtual void IsPlayer()
{
SetListeners();
StartPlayerOnlyCoroutines();
}
public virtual void OnEnable()
{
StartCommonCoroutines();
if (!shipControl.shipWrapper.ship.IsPlayer) return;
SetListeners();
StartPlayerOnlyCoroutines();
}
public virtual void OnDisable()
{
RemoveListeners();
StopAllCoroutines();
}
public abstract void SetListeners();
public abstract void RemoveListeners();
public abstract void StartCommonCoroutines();
public abstract void StartPlayerOnlyCoroutines();
public abstract void StopPlayerOnlyCoroutines();
}
I want to Inherit from ShipComponent in subclasses and whenever a subclass that inherits from ShipComponent calls OnDisable() (Unity built in version) then I want it to call the baseClass' ShipComponent.OnDisable() as initialized above.
I realize OnDisable() is a built in method, so should I instead be doing:
public override void OnDisable()
{
base.OnDisable();
RemoveListeners();
StopAllCoroutines();
}
Because we are overidding the original built in Unity method this way?
Thanks.

Unity does a little bit of reflection magic on user scripts, and it creates internal list of MonoBehaviours that have OnEnable/OnDisable methods - it calls them even if they are private, and you don't need to explicitly implement any interface for this to work.
This also means that it does not care if the method is virtual or not, it will call a correct override if you override it in your inheriting classes.
Do not mark it as override in your base class as there is no base implementation in MonoBehaviour itself. I would assume this decision was made for performance reasons - if all objects had those methods, even if they were empty, they would have to be called. If there are no methods, its a few calls less.

Related

Why use a constructor over protected variables?

I'm building some classes within unity to define the mechanics individually, and transition between each for easier and cleaner code.
What I wanna know, is when should I be using a constructor to pass variables around, and when to use protected variables. What are the pros and cons of each, and what should I know about them? Also what should I lean towards, like what's practical?
Previously I'd pass these variables into the PlayerState constructor, then in my classes that extend from my PlayerState would follow suit. But if they're protected variables I don't need to pass them into the constructor to access them, and I was wondering what should I do?
using UnityEngine;
The new way I'm doing it:
public class PlayerState
{
//protected Core Core;
protected Player player;
protected PlayerStateMachine StateMachine;
protected PlayerData playerData;
private string currentAnimation;
protected float StartTime; // Start time gets set everytime we're in a state, that way we have a reference for how long we've been in any state (good for mechanics)
protected bool isAnimationFinished;
protected bool isExitingState; // Very useful, if you run through if/else conditionals in a superState, the substate may still run, and both end up calling a change state. This will stop this from happening
public PlayerState(string currentAnimation)
{
this.currentAnimation = currentAnimation;
}
public virtual void Enter() { }
public virtual void Exit() { }
public virtual void LogicUpdate() { }
public virtual void PhysicsUpdate() { }
public virtual void DoChecks() { }
}
The Old Way:
public class PlayerState
{
protected Core Core;
protected Player player; // protected means private but shared between components that inherit the class
protected PlayerStateMachine StateMachine;
protected PlayerData playerData;
private string currentAnimation;
protected float StartTime; // Start time gets set everytime we're in a state, that way we have a reference for how long we've been in any state (good for mechanics)
protected bool isAnimationFinished;
protected bool isExitingState; // Very useful, if you run through if/else conditionals in a superState, the substate may still run, and both end up calling a change state. This will stop this from happening
public PlayerState(Player player, PlayerStateMachine stateMachine, PlayerData playerData, string currentAnimation)
{
this.player = player;
this.StateMachine = stateMachine;
this.playerData = playerData;
this.currentAnimation = currentAnimation;
Core = player.Core;
}
// So now we code out the functions for each state
// Every state must have an enter and exit function, as well as an update and fixedUpdate function
// We're naming the update function as "LogicUpdate", and fixedUpate as "PhysicsUpdate"
public virtual void Enter() // virtual means this function may be overriden from classes that inherit this class
{
DoChecks();
player.Anim.SetBool(currentAnimation, true);
StartTime = Time.time;
isAnimationFinished = false;
isExitingState = false;
//Debug.Log("Current Animation: " + currentAnimation);
}
public virtual void Exit()
{
player.Anim.SetBool(currentAnimation, false);
isExitingState = true;
}
The way I applied it to other classes the old way:
public class PlayerGroundedState : PlayerState
{
protected Vector2 input;
protected bool jumpInput;
private bool grabInput;
private bool dashInput;
private bool isTouchingGround;
private bool isTouchingWall;
protected bool willCollideWithCeiling;
public PlayerGroundedState(Player player, PlayerStateMachine stateMachine, PlayerData playerData, string currentAnimation) : base(player, stateMachine, playerData, currentAnimation)
{
}
public override void Enter()
{
base.Enter();
player.JumpState.ResetAmountOfJumpsLeft();
player.DashState.ResetCanDash();
}
public override void Exit()
{
base.Exit();
}
I don't have to pass player into the constructor of PlayerState and PlayerGroundedState to access it if it's protected. What should I do though, which method is the proper way to handle the variables, also which is better for my cpu?
This is just a question related to OOP. Unity is not needed to be considered.
A constructor let you create an object instance and initialize the members of the object at the same time. If there are some immutable members (i.e. they will never be changed after construction), you may need to initialize them in constructors, and you may add the keyword readonly to the members. If you don't need to initialize any member with passing parameter(s) when the instance is created, there is no need to have a custom constructor (unless you want to hide the default constructor).
The access modifier protected makes the member accessible only in code in the same class, or in a class that is derived from that class. If you need to access the member in other places, you still need do it via public/internal methods such as setters and getters, or make it public/internal.
In your case, I think a constructor is needed to initialize the members such as player when a PlayerState instance is created.

Requiring Child Classes to call base.Foo() in overrides

I'm working on a Unity project which uses a good amount of inheritance. I have an abstract base class whose methods I want its children to always call (e.g. Awake).
I haven't worked with attributes much - is there a way to add an attribute to my ABC's Awake method which causes the child-class to log an error if they override Awake() without calling base.Awake() in its implementation?
Thanks!
You could something like this:
public class A
{
public void Awake()
{
Console.WriteLine("Everybody does X on awake");
AwakeExtra();
}
public virtual void AwakeExtra() => Console.WriteLine("'A' also does A on awake");
}
public class B : A
{
public override void AwakeExtra() => Console.WriteLine("'B' also does B on awake");
}

(Unity) How to prevent MonoBehavior Awake() function overriding in children

Is it possible to disable the option to override MonoBehavior functions like Start(), Awake(), Update(), etc... in children class?
The reason behind this is, that when multiple people work on Unity project, someone unaware of this problem could disable important initialization that is defined in parent which could cause unwanted behavior that is hard to debug.
It seems to me that this goes against principles of OOP since you can mess a lot of things on other places. You wont even see a warning in Visual Studio, when trying to do this.
public class A: MonoBehavior
{
void Awake()
{
// Do some stuff
}
}
public class B: A
{
void Awake()
{
// This actually overrides stuff that Awake() does in parent class
}
}
Solution is simple and adheres to the principles of OOP.
Any class you think could have children in the future, define in it stubs
protected virtual void Awake() {}
protected virtual void Start() {}
protected virtual void OnEnable() {}
...list goes on.
Now if someone to use any of the callbacks in a child without 'overriding' the IDE will certainly notify them that there is a base implmentation of the callback.
I think sealing the Awake() method from your class A should be what you are looking for:
public class A: MonoBehavior
{
public sealed override void Awake()
{
// Do some stuff
}
}
Now when you try to override this method in your class B it will not only give an IDE warning but it will not compile.

Don't use Interfaces in Unity?

in Unity I make use of interfaces. I set a logic for components which are totally different to each other.
Examples:
A car, a dog and a aircraft would implement IMovable. I can call Move() from each component but these components execute different code.
Same for ISavable, each component, that has to save data to the database could save the stuff when looping through all savables.
The problem:
Some people in forums say that interfaces are bad for Unity.
When destroying a gameobject and call its interface method this still gets executed.
No error would come up because Destroy() does not destroy objects. Unity as a C++ driven Engine would setup a C# wrapper for the objects. These objects just get a flag destroyed which is a bool.
Destroyed gameobjects will not get destroyed immediately, they will be destroyed later on at the end of the frame.
Until this end of the frame is not reached the method can still get called from the destroyed object.
The best way would be using abstract classes only and never use interfaces because of the bad behaviour coming up when destroying objects.
I tested this with a small example, I created the following scripts:
public interface IIntfacable
{
void DoSomething();
void DestroyComponent();
}
public class bar : MonoBehaviour
{
private IIntfacable i;
private void Start()
{
i = FindObjectOfType<foo>().GetComponent<IIntfacable>();
}
private void Update()
{
i.DoSomething();
i.DestroyComponent();
i.DoSomething();
}
}
public class foo : MonoBehaviour, IIntfacable
{
public void DoSomething()
{
Debug.Log("=> DoSomething");
}
public void DestroyComponent()
{
Debug.Log("=> DestroyComponent");
Destroy(gameObject);
}
}
When executing this code I get the following result
Workaround:
I could create an abstract base class and choose between
public abstract void Foo();
and
public virtual void Bar()
{
return;
}
but this might lead to overengineering. Because all Scripts would need this base class whether they need this method or not.
Conclusion:
Should I prevent using interfaces?
I am confident in saying there is no harm in using interfaces.
The underlying fear is about keeping track of unmanaged references, a problem which will still be there weather you are using interfaces, abstract classes or whatever. You simply have to make sure that your game code will not try to access any objects which have been Destroy()ed.
Basically, I just construct a collection of objects that I know are not destroyed in my scene, and remove them after destruction.
With risk of answering an xy-problem, If you are scared to miss out on your reference count anyway or there is something in particular which wont allow creating such a list, there is not really any magic wand here, but there are a few precedent patterns in the .net framework with the IDisposable interface/pattern that may lead the way.
Many implementations of these patterns checks a flag in a few public-facing methods of the object. IDisposable.Dispose() would set the flag to true and throw an ObjectDisposedException on some public method if this is set to true, analog to MissingReferenceException in this case. Some patterns will then expose the flag IsDisposed, so that other objects that use the implementation can check instead of doing a try-catch on any access to the object. Your analog could be IsDestroyed, and you should set it in the override of OnDestroy.
You could change your method update like this (well it's not really a use case, why would you try to use it after destroying it, but to show my point):
private void Update()
{
i.DoSomething();
i.DestroyComponent();
if (!i.IsDestroyed) {
// This will not be called
i.DoSomething();
}
}
and implementation could be
public interface IIntfacable : IDestroyable
{
void DoSomething();
}
public interface IDestroyable
{
void DestroyComponent();
bool IsDestroyed { get; }
}
public class foo : MonoBehaviour, IIntfacable
{
bool IsDestroyed { get; private set; }
public void DoSomething()
{
Debug.Log("=> DoSomething");
}
public void DestroyComponent()
{
Debug.Log("=> DestroyComponent");
Destroy(gameObject);
}
public override OnDestroy() {
base.OnDestroy();
IsDestroyed = true;
}
}

Order of execution in Abstract Class

I came across a posting where it is said that MustBeCalled() method will get called if we have the Abstract class do the calling in this manner.
public abstract class AbstractClass
{
public void PerformThisFunction()
{
MustBeCalled();
AbstractMethod();
}
public void MustBeCalled()
{
//this must be called when AbstractMethod is invoked
}
//could also be public if desired
protected abstract void AbstractMethod();
}
public class ImplementClass : AbstractClass
{
protected override void AbstractMethod()
{
//when called, base.MustBeCalled() must be called.
//how can i enforce this?
}
}
But how does MustBeCalled() method get called?
In what order things are called here?
If you call PerformFunction() first, then everything will execute in the intended order, where that order is specified in the order of the lines of code in PerformFunction(). If you call AbstractMethod() directly, there's no guarantee that MustBeCalled() will ever be called. However, I notice that you have AbstractMethod() marked as protected, which means that outside consumers of your class will not be able to call it directly. They'll have to use PerformFunction() -- this is good, as there is now only one public way to invoke your internal methods, and that way guarantees the order that you need.
In truth, there is a level at which you can only guarantee that things happen by choosing to write code to make them happen. You can't, for example, guarantee that code is going to implement a game of Tetris except by actually writing that code and choosing to implement it in such a way that it produces Tetris behavior. The type system and the public/protected/private modifiers can help some by preventing some misuse (as your internals are not accessible and thus cannot be invoked by consumers of your module), but they can only go so far. This is such a case.
You cannot enforce how an implementation to call a method when invoked. The implementation could do its own thing entirely, or do nothing.
public class ImplementClass : AbstractClass
{
protected override void AbstractMethod()
{
// this is a perfectly valid implementation
}
}
A better implementation could be.
public abstract class AbstractClass
{
public void PerformThisFunction()
{
MustBeCalled();
AbstractMethod();
}
private void MustBeCalled()
{
}
protected virtual void AbstractMethod()
{
MustBeCalled();
}
}
This way refactoring tools will at least create the desired boilerplate code:
public class ImplementClass : AbstractClass
{
protected override void AbstractMethod()
{
base.AbstractMethod();
}
}
However, the person overriding AbstractMethod still needs to call base.AbstractMethod, this is not enforced by the compiler at all.

Categories

Resources