Here’s my use case:
I want to make an input manager for a local multiplayer game in DOTS. The goal is that for each local multiplayer player, we’ll have a “Player” entity with a “PlayerInputs” component on it containing all of the input info (JumpWasPressed, MoveVector, CrouchIsHeld, etc…) but only for the input devices that were assigned to that specific player
Here’s my problem:
If I try to query the inputs on Update, for example with MyInputActions.MyActions.Jump.ReadValue(), I can’t get which device id it’s coming from, so this doesn’t work for local multiplayer.
Here’s what I’d want:
I can see two ways this could work:
1- Make the input events be queriable per-device. So instead of calling MyInputActions.MyActions.Jump.ReadValue(), I would be calling MyInputActions.devices[0].MyActions.Jump.ReadValue()
2- On Update(), I’d like to be able to call MyInputActions.MyActions.Jump.GetAllEvents(), which returns me a list of all the “InputAction.CallbackContext” for the Jump input that happened this frame. Basically the same thing the interface approach does, but in the form of a list. I think that would be the ideal solution to my problem.
I actually don’t know if these things are already possible so I’m hoping someone can tell me
@Rene-Damm how would you recommend passing InputActionTrace’s data to a job? Or just handling input in DOTS in general?
Also, I’m starting to see a problem with the InputActionTrace approach: in my dots input component, I want to store a “JumpReleased” bool value, which I update on every action trace. The problem is that I don’t get any action trace when no input is happening, so I never get an opportunity to set “JumpReleased” back to false the frame after the button was released. The last trace we always get from the Jump action is it being released
With all this, I still feel like being able to poll actions on Update per device would be ideal and would solve all my problems
Shooting from the hip, something like this: (just sketched out; haven’t even tried to compile, let alone run)
// Somewhere else (e.g. lobby screen), handle player setup.
void JoinPlayer(InputDevice device)
{
// Sets up a new user and pairs the given device.
// InputUser.all is all current players.
var user = InputUser.PerformPairingWithDevice(device);
// Give custom actions to user.
// Use generated C# class here.
var actions = new MyGameActions();
user.AssociateActionsWithUser(actions);
// Optionally, pick control scheme. If several are supported, probably
// want to find a suitable one for the given device.
var controlScheme = InputControlScheme.FindControlSchemeForDevice(device, actions.controlSchemes);
user.ActivateControlScheme(controlScheme.Value);
}
// In the component system.
struct MyGameInputs
{
public int playerIndex;
public bool interact;
public float2 move;
public float2 look;
};
[ReadOnly] NativeList<MyGameInputs> inputs;
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
// Read out input for each player.
var users = InputUser.all;
var userCount = users.Count;
var inputs = new NativeList<MyGameInputs>(userCount, Allocator.TempJob);
for (var i = 0; i < userCount; ++i)
{
var actions = (MyGameActions) users[i].actions;
inputs.Add(new MyGameInputs
{
playerIndex = i, // Or alternatively use InputUser.id
interact = actions.Gameplay.Interact.triggered,
move = actions.Gameplay.Move.ReadValue<Vector2>(),
look = actions.Gameplay.Look.ReadValue<Vector2>(),
});
}
// Set up job and feed "inputs" into it...
}
Of course, this assumes it’s okay for you to just go frame-to-frame (or, well, update-to-update) with your input granularity. If you do want to go event-to-event instead, you’re back to keeping lists of inputs. In that case, I’d probably still go and simply produce one MyGameInputs for each (meaningful) event and would probably employ InputActionTrace in combination with keeping one persistent MyGameInputs state for each player around.
Indeed. In its current form, InputActionTrace is more suitable to event-style processing than “state-style” processing.