Hi everyone, I have two methods for using a pressure plate to activate effects on another object. The first script is a component on the pressure plate, and waits for an allowed trigger object to touch it in order to call TriggerEffect(). The idea is that TriggerEffect() can be overridden in order to produce new effects, however the method TriggerEffect() in the second script below is not overriding the base method. Unity says that the pressure plate is indeed being triggered, but the override method is not being called. Why is this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PressureTrigger : MonoBehaviour
{
// VARIABLES
public GameObject target; // target object for pressure plate effect
public GameObject trigger; // trigger object for pressure plate effect
// used for all pressure plate interactions
public virtual void TriggerEffect()
{
// This method is meant to be overriden when coding different interactions
}
// detect if object is on pressure plate
private void OnTriggerEnter(Collider other)
{
if (other.GetType() != typeof(SphereCollider))
{
// trigger effect when collding object matches parameters
if (other.name == trigger.name)
{
Debug.Log("Triggered");
TriggerEffect();
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerMove : PressureTrigger
{
// VARIABLES
public Vector3 wishPosition; // desired position
public float speed; // speed to reach desired position
// overrides TriggerEffect() in PressureTrigger
public override void TriggerEffect()
{
Debug.Log("Called");
target.transform.position = wishPosition;
}
}
I should clarify that the second script, which overrides the TriggerEffect() method, is attached to the object I want the pressure plate to influence, though that shouldnât really change anything
I suspect itâs because reflection is being used to fire the OnTriggerEnter method (as it is for all âmagic methodsâ in Unity, like Awake, Start, etc), and the method is defined on the base type but not the derived type. I would still expect the override for TriggerEffect to be called honestly, so this is a bit surprising- I may have known about this limitation at some point, and just forgot about it since I use MonoBehaviours for very little anymore.
If you want to force it to work, you can probably set OnTriggerEnter itself to protected virtual, and override it in the derived type just calling base.OnTriggerEnter() in the function body so youâre not repeating yourself. Then reflection will call the OnTriggerEnter method in the derived type, the objectâs class would be set properly, and the override should be called for TriggerEffect. Copying the private OnTriggerEnter method into the derived type would also work- those reflection calls are essentially like having automatic overrides, so if it exists in the derived type, the base type method wonât get called anyways, but then you have a copy of the function logic in two places, which is annoying.
Alternatively, you may be able to set TriggerEffect to abstract instead of virtual, and force it to register that it doesnât exist on the base type (I give that a decent chance of working, but itâs sort of hack-ish- it may also cause an error). Then PressureTrigger would need to be abstract as well though, and you wouldnât be able to use it as a Component, so itâs only an option if thatâs not an issue.
Have you put a Debug.Log statement in the base class to make sure that the base version of TriggerEffect is really being called? Without knowing more, Iâd have to assume the OnTriggerEnter just isnât firing, or the other.name == trigger.name is false, or something else. The override behavior youâre describing should work fine.
Why the hell Unity changes how c# works? This stuff should be written in the documentation, Iâve been spending hours trying to understand why the overridden method wasnât being called by the LateUpdate function.
I donât have a solution for this and I really need the overriding behaviour for my code to work. Does anyone know any workaround? Setting the base class abstract is not an option for me.
Iâm using Unity 2019.3.2f1
Iâm not sure why youâre having any problems with this. I just took a few minutes to test this out, and the override behavior works fine. Here are the scripts, simplified down:
public class PressureTrigger : MonoBehaviour
{
public virtual void TriggerEffect()
{
Debug.Log("This is the base class");
}
// detect if object is on pressure plate
private void OnTriggerEnter(Collider other)
{
TriggerEffect();
}
}
public class TriggerMove : PressureTrigger
{
public override void TriggerEffect()
{
Debug.Log("This is the sub-class");
}
}
Attaching the TriggerMove component to a trigger collider, and then walking into the trigger, the only logging I get is âThis is the sub-classâ. So, for me this works fine.
If you try out this code in a brand new project, does it work properly? Are you maybe using IL2CPP, or some other .NET platform in this project?
Hi dgoyette, thanks for the reply.
I already tried the code you proposed and it actually works fine.
I just found the problem, which is a particular thing of C# that I wasnât aware of.
In my code I wrote:
var object = GetComponent<FatherClass>();
object.Hello();
As I used âvarâ C# didnât cast the object to the ChildClass, so the Hello() will print the parent name.
This is easily fixable replacing âvarâ with âFatherClassâ.
First, using âvarâ has no effect on the behavior of an object. âvarâ is purely a bit of syntactic sugar. There is no difference between var object = GetComponent<FatherClass>(); and FatherClass object = GetComponent<FatherClass>();. Just mouse over the âvarâ in Visual Studio, and it will show you what class itâs using.
Second, even if you cast an object to its base class, as youâre doing here, that wonât change its runtime behavior. Consider these two methods:
public class BaseClass : MonoBehaviour
{
public virtual void TheVirtualMethod()
{
Debug.Log("This is the base class");
}
public void Hello()
{
TheVirtualMethod();
}
}
public class SubClass : BaseClass
{
public override void TheVirtualMethod()
{
Debug.Log("This is the child class");
}
}
Now, I added a âSubClassâ component to my game object, and ran the following method:
public void ABC()
{
var thing = this.gameObject.GetComponent<BaseClass>();
thing.Hello();
}
Running that code prints âThis is the child classâ. The same is true if, instead, I use BaseClass thing = this.gameObject.GetComponent<BaseClass>();
So, the behavior youâre describing just doesnât make sense to me, unless youâve simplified your example, and youâre actually doing something more complicated.
Hello dgoyette,
I didnât simplify my example, my implementation was very minimal.
Changing var into BaseClass fixed it immediately.
Donât know whatâs happening under the hood, but it looks like var is not casting the object.
Iâm happy to be proven wrong, but to my knowledge, if a sub-class overrides a base classâs method, thereâs no way to call the base classâs implementation of that object if you have an instance of the sub-class. It doesnât matter whether you cast the object as the base class; the sub-class implementation will still be called.
Note: Obviously the sub-class method itself can call the base implementation from within the method, but to someone just having an instance of the object, they can only call the sub-classâs implementation.
Anyway, Iâd be interested in seeing the code the way youâve fixed it, because the way you describe it, itâs doing something impossible. Glad itâs working, but I suspect something else is going on. Or I have something to learn.