2

I am trying to invoke the Actions in the derived class but nothing happens.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(PlayerInput))]
public abstract class CharacterInput : MonoBehaviour
{
    protected PlayerInput input;

    protected Action<InputAction.CallbackContext> OnMove;
    protected Action<InputAction.CallbackContext> OnAttack;
    protected Action<InputAction.CallbackContext> OnSpecial;
    protected Action<InputAction.CallbackContext> OnBlock;
    protected Action<InputAction.CallbackContext> OnGrab;
    protected Action<InputAction.CallbackContext> OnJump;

    // Start is called before the first frame update
    void Start()
    {
        input = GetComponent<PlayerInput>();
        InputActionMap fighterMap = input.actions.FindActionMap("Fighter");
        fighterMap.FindAction("Move").performed += OnMove;
        fighterMap.FindAction("Attack").performed += OnAttack;
        fighterMap.FindAction("Special").performed += OnSpecial;
        fighterMap.FindAction("Block").performed += OnBlock;
        fighterMap.FindAction("Grab").performed += OnGrab;
        fighterMap.FindAction("Jump").performed += OnJump;
    }

    // Update is called once per frame
    void Update()
    {

    }
}

Here is the derived class

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class ExampleInput : CharacterInput
{
    // Start is called before the first frame update
    void Start()
    {
        OnAttack = callbackContext => Debug.Log(callbackContext.performed);
    }
    // Update is called once per frame
    void Update()
    {

    }
}

"Attack" is bound to left click. At first I made CharacterInput not abstract and defined OnAttack to just print a line in the debug window, then when I added CharacterInput as a component instead of ExampleInput, it worked fine. Printed every time I left-clicked. But I want to be able to add children of CharacterInput as components to a game object, and have the OnWhatever actions defined there, but nothing happens.

  • I'd factorize a little bit the content of the `Start`method in the base class into a `protected InitializeActions` method (you can probably find a better name). in the `Start`from the derived class, just call `InitializeActions` as well when you need it, that would be after you set the aciton. Or you can try to make the `Start` from the base class `protected`, and call `base.Start()` in the Derived method, at the right time. (that's just a quick way to make things work, there is probably a better architecture) – Pac0 Apr 28 '20 at 19:20
  • What about the warning, _"Warning CS0108 'xxx' hides inherited member 'xxxx'. Use the new keyword if hiding was intended."_? You should mark the `Start` method in the base class as `virtual` and use `override` in the `ExampleInput` class. Don't forget to call `base.Start()` in `ExampleInput` class. – Jeroen van Langen Apr 28 '20 at 20:27

1 Answers1

1

If you want to be sure it works correctly, change your current method hiding to overriding. Add "virtual" keyword to the base Start() method.

virtual void Start()
{
    input = GetComponent<PlayerInput>();
    InputActionMap fighterMap = input.actions.FindActionMap("Fighter");
    fighterMap.FindAction("Move").performed += OnMove;
    fighterMap.FindAction("Attack").performed += OnAttack;
    fighterMap.FindAction("Special").performed += OnSpecial;
    fighterMap.FindAction("Block").performed += OnBlock;
    fighterMap.FindAction("Grab").performed += OnGrab;
    fighterMap.FindAction("Jump").performed += OnJump;
}

Then add override keyword to the derived method and call base.Start() at the end.

override void Start()
{
    OnAttack = callbackContext => Debug.Log(callbackContext.performed);
    base.Start();
}

In C#, non-abstract methods are not automatically overridden (like it works in Java) and you have to use virtual keyword. Otherwise, the derived method just hides the base method, but it is possible to accidentally call the base method when you don't mean it (for example - by casting or simply declaring it as a base class type - like in here: https://dotnetfiddle.net/FOeUL9). That's why it can cause various issues, so avoid it if you can.

There's some more info on what's the difference between the two: https://www.geeksforgeeks.org/difference-between-method-overriding-and-method-hiding-in-c-sharp/

In your case, your main problem was most probably, that the derived method didn't call its base method by base.Start(), so the statement fighterMap.FindAction("Attack").performed += OnAttack; was never called. The hiding could also be an issue.

Edit: My bad, the base.Start() should probably be at the end of the derived method, not at the beginning, since it's the base method that creates event subscriptions - without preceding OnAction event handler initialization it just subscribes null to the event.

A. Stef
  • 23
  • 1
  • 5