2
\$\begingroup\$

I have a class BlinkingBehaviour which inherits from MonoBehaviour. Game objects attached to scripts containing classes that inherit from BlinkingBehaviour should, as the name suggests, blink.

BlinkingBehaviour is a small class so I will share the entirety of its code here:

public class BlinkingBehaviour : MonoBehaviour
{
    private Renderer renderer;

    public bool BlinkingEnabled;
    public Color BlinkingColour = new Color(1.0f, 127f / 255f, 39f / 255f);

    protected float CurrentColourScale;
    protected float ColourScaleIncrementPerUpdate = 0.005f; 

    protected const float MaxColourScale = 2f;

    protected void Start()
    {
        this.renderer = this.gameObject.GetComponent<Renderer>();
    }

    protected void Update()
    {
        if (this.BlinkingEnabled)
        {
            SetBlinkingColour();
        }
        else
        {
            SetColour();
        }
    }

    protected void SetColour()
    {
        if (this.CurrentColourScale <= 1f)
        {
            this.renderer.material.SetColor("_EmissionColor", this.BlinkingColour * this.CurrentColourScale);
        }
        else
        {
            this.renderer.material.SetColor("_EmissionColor", this.BlinkingColour * (MaxColourScale - this.CurrentColourScale));
        }
    }

    protected virtual void SetBlinkingColour()
    {
        SetColour();
        this.CurrentColourScale += ColourScaleIncrementPerUpdate;

        if (this.CurrentColourScale <= MaxColourScale)
        {
            return;
        }

        ResetBlinkingColourScale();
    }

    protected void ResetBlinkingColourScale()
    {
        this.CurrentColourScale = 0f;
    }
}

I have a class Lamp which inherits from BlinkingBehaviour. I attached Lamp.cs to an object in my scene. In my inspector, I checked the BlinkingEnabled option, expecting to see the Lamp game object blinking after I pressed "Play".

However, the object did not blink.

Here is my Lamp class:

public class Lamp : BlinkingBehaviour
{
    private void Start()
    {
        base.Start();
    }

    private void Update()
    {
        base.Update();
    }
}

After setting various breakpoints, I could see that the Start() method was hit in both Lamp and BlinkingBehaviour. I could also see that this.gameObject.GetComponent<Renderer>(); returned a valid component.

On the other hand, my breakpoint in the Update method in Lamp was never hit.

What can I do to fix this issue?

UPDATE:

I deleted the Start() and Update() methods from my derived class. I discovered something new: The Update() method in BlinkingBehaviour is never called, even though Start() is.

\$\endgroup\$
2
  • \$\begingroup\$ @Bálint by default, Update is private, and it can be protected or public, in this case it doesn't really matter since the superclass' callback is called anyway if there's no overriding Update method in the inheriting class. \$\endgroup\$
    – Galandil
    Commented Mar 23, 2018 at 15:23
  • \$\begingroup\$ @Bálint Why not? Unity calls MonoBehaviour messages through reflection, so they don't need to be public/etc. for it to access them. \$\endgroup\$
    – DMGregory
    Commented Mar 23, 2018 at 16:01

2 Answers 2

3
\$\begingroup\$

The Update in the derived class should be an override of a protected virtual Update in the base class.

Imagine when Unity reflects the non-public members on your behavior -

BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic;
MethodInfo theMethod = typeof(Lamp).GetMethod("Update", flags);
theMethod.Invoke(lampInstance);

Which Update method gets called? By having private and a protected methods that aren't associated as an override, both are available so one will be chosen depending on the underlying implementation - possibly throwing an exception.

The proper way to implement this pattern would be:

public class BlinkingBehaviour : MonoBehaviour
{
    protected virtual void Update()
    {
        // ...
    }
}
public class Lamp : BlinkingBehaviour
{
    protected override void Update()
    {
        base.Update();
        // ...
    }
}
\$\endgroup\$
0
\$\begingroup\$

I quickly tried your code by copy/pasting it on a test scene, and it's working perfectly fine.

First of all, you can delete both Start and Update methods from Lamp: since it inherits from BlinkingBehaviour, both Start and Update methods will be called anyway from the superclass if there're no overriding methods in the inheriting class.

The fact that Update from BlinkingBehaviour is never called, tells that there's something wrong elsewhere.

Are you sure that the Lamp script component is active?

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for your reply! Yes, the script component is attached to an active object in the scene. \$\endgroup\$
    – user112729
    Commented Mar 23, 2018 at 15:30
  • \$\begingroup\$ I don't mean the game object, but the script component itself. Just to be sure, create a new scene, create a simple cube and attach the Lamp script and see if it works (it should). \$\endgroup\$
    – Galandil
    Commented Mar 23, 2018 at 15:31
  • \$\begingroup\$ I have just double-checked -- yes, the script component is active. However, I will use your approach and test it in a new scene as well. \$\endgroup\$
    – user112729
    Commented Mar 23, 2018 at 15:36

You must log in to answer this question.