Home > Enterprise >  Is it not possible to use events of parent class?
Is it not possible to use events of parent class?

Time:07-02

I have separate classes for jumping and running. There could be other animatable movements, like climbing, flying, who knows what I'll come up with. Therefore it would make sense to make all these movement classes to inherit from a class called AnimatableMovement. Its only purpose is to setup the events.

Please consider:

public class AnimatableMovement : MonoBehaviour
{
    public event AnimationStateDelegate OnAnimationStateChange;
    public delegate void AnimationStateDelegate(string newState);
}

Then the walking:

public class Movement : AnimatableMovement
{
    public float speed = 5f;
    protected Rigidbody2D body;

    void Start()
    {
        body = transform.GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");

        if (horizontalInput != 0)
        {
            body.velocity = new Vector2(horizontalInput * speed, body.velocity.y);
            OnAnimationStateChange?.Invoke("walk");
        } else
        {
            OnAnimationStateChange?.Invoke("idle");
        }
    }
}

If the delegate is defined in the same class as the movement, everything works like a charm, but violates the DRY principle, because I have to copy/paste the same two lines to each movement type class. So I thought this would be a no-brainer solution, but done like this, separated into two classes, I get an error:

Assets\Scripts\Movement.cs(30,13): error CS0070: The event 'AnimatableMovement.OnAnimationStateChange' can only appear on the left hand side of = or -= (except when used from within the type 'AnimatableMovement')

CodePudding user response:

Try this approach:

public abstract class AnimatableMovement 
{
    public event EventHandler<string> AnimationStateChange;
    
    protected void RaiseAnimationStateChangeEvent( string newState )
    {
        this.AnimationStateChange?.Invoke( this, newState );
    }
}

And:

public class Movement : AnimatableMovement
{
    public float speed = 5f;
    protected Rigidbody2D body;

    void Start()
    {
        body = transform.GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");

        if (horizontalInput != 0)
        {
            body.velocity = new Vector2(horizontalInput * speed, body.velocity.y);
            RaiseAnimationStateChangeEvent("walk");
        } else
        {
            RaiseAnimationStateChangeEvent("idle");
        }
    }
}

CodePudding user response:

When invoking events, do it with a null coalescing operator. If you call OnAnimationStateChange.Invoke(...), you will get null exceptions if OnAnimationStateChange doesn't have any subscribers. Call all events like this instead: OnAnimationStateChange?.Invoke("idle")

With your current code, the event is going to be called each and every frame. Do you expect the animation state to change every frame?

If you want to invoke an inherited event, just set it up as a method in the base class that is only accessible to derived types. Just add this line to AnimatableMovement:

protected void InvokeOnAnimationStateChange(string newState) => OnAnimationStateChange?.Invoke(newState);

CodePudding user response:

That's how events work: the only class that's allowed to raise an event, is the class that defines that event.

Rename the event to drop the "On" prefix, then add a dedicated method to raise it - the "On" prefix is usually understood to identify a method that raises an event (or one that handles an event, depending on your naming convention - but the event itself shouldn't have any prefixing scheme):

protected void OnAnimationStateChanged(string state)
{
    AnimationStateChanged?.Invoke(state);
}

Inherited classes can then access and invoke this method to get the event raised.

  • Related