I’ve written a basic script that allows the player to slow time while they are aiming a weapon. Everything works fine until I perform the aiming action. The character performs it but time doesn’t change, and an error then appears in the console.
It uses the Opsive UFPS asset for the character controller. I’ve posted this same issue on their forums but they say it’s an Input System issue and not they’re asset.
Here’s the code for it for the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerUpgrades : MonoBehaviour
{
// Bullet Time
[Header("Bullet Time")]
public bool bulletTimeActive;
public float bulletTimeSpeed;
public void OnFire2(InputAction.CallbackContext context)
{
if (bulletTimeActive)
{
if (context.started)
{
Time.timeScale = bulletTimeSpeed;
}
else if (context.canceled)
{
Time.timeScale = 1f;
}
}
}
}
I changed the Behavior on the Player Input component to Invoke Unity Events. From there I just added the OnFire2 method to Fire2’s callback. No more errors and the method works correctly.
I know this is the solution in this case but I can’t understand why the designers at Unity would route the code around like this… it’s just more things that can break.
Yeah, I had a project last year that didn’t have this problem. And now I will seemingly have to use this workaround as well. Quite weird and annoying.
Btw a workaround is to also do InputAction.ActionMap.Action.started += function
works with callback context aswell.
For anyone still searching for a solution close to what the send event should perform, I use this method in my project. I share it there.
I created a base class for behaviours that needs to use PlayerInput events. So your script needs to inherit this class in order to work and the Player Input components needs to have its behavior sets to “Invoke C Sharp Events”.
using UnityEngine;
using UnityEngine.InputSystem;
// Base class for dispatching event of the PlayerInput component.
[RequireComponent(typeof(PlayerInput))]
public abstract class PlayerInputDispatcher : MonoBehaviour {
protected PlayerInput _playerInput;
public PlayerInput playerInput { get => _playerInput; }
private void OnEnable() {
_playerInput = GetComponent<PlayerInput>();
_playerInput.onActionTriggered += BroadcastAction;
}
private void BroadcastAction(InputAction.CallbackContext context) {
if (!_playerInput.isActiveAndEnabled) return;
SendMessage($"On{context.action.name}", context, SendMessageOptions.RequireReceiver);
}
}
As an example, here is how I use it on a player component (very bare but still).
// Base *example* class for a Character
public class Character : PlayerInputDispatcher, GameInput.ICharacterActions {
public void OnCancelLaunch(InputAction.CallbackContext context) {
Debug.Log("OnCancelLaunch");
}
public void OnInvertCharacterBonuses(InputAction.CallbackContext context) {
Debug.Log("OnInvertCharacterBonuses");
}
public void OnMove(InputAction.CallbackContext context) {
Debug.Log("OnMove");
}
public void OnPrepareLaunch(InputAction.CallbackContext context) {
Debug.Log("OnPrepareLaunch");
}
public void OnUseCharacterBonus1(InputAction.CallbackContext context) {
Debug.Log("OnUseCharacterBonus1");
}
public void OnUseCharacterBonus2(InputAction.CallbackContext context) {
Debug.Log("OnUseCharacterBonus2");
}
}
Another thing you can note is that with the new input system you can take advantage of the class/interfaces generation, for my project it generates this particular interface GameInput.ICharacterActions. By adding it, I am sure I handle all the methods corresponding to each input properly.
This interface is name that way because of those :
GameInput is the name of my generated class for the input actions.
Character is the name of the Action Maps I use for the input of my characters.
Note that those parameters depends on your input actions configuration.
PS : Unity may have a better way to do this, but this is a working solution so far.