New PlayerModule migration guide for PlayerScripts UseInputActionSystem

Introduction

This here is a guide on how to migrate the old PlayerModule to the new one. This is only relevant to you if you have scripts in your game that depend on the PlayerModule, e.g. :GetControls as mentioned here:


I tried to look for code bases to cover most cases that used PlayerModule in some way.

But there isn’t really a lot that has to be changed. And the things that appeared to be difficult to achieve with the old one, are more easier now with the new one. e.g. rebinding keys is very easy now.

How this Guide is structured

I’ll title each section and then explain what changed for each. All assume that you plan or already have PlayerScriptsUseInputActionSystem enabled.

To enable it use the Properties Window and set it to Enabled.

You might also find things faster with CTRL + F3, for the search function. On Mac Cmd + F.

Before starting - Best practises

Note that:

So, basically if you did or do rely on these, I recommend the following.

  • If you have :WaitForChild("PlayerModule") all over the place, consider unifying that into a “Utility Module” and place it inside a function that standardises where to find the PlayerModule. If you do that, you’ll have to change less in your scripts, if Roblox decides to move the PlayerModule location again, or similar.
    Follow, this concept for other things under the PlayerModule as well, and you should have an easier time.
Example
local PlayerModuleUtils = {}

function PlayerModuleUtils.GetPlayerModule()
  return game.StarterPlayer:WaitForChild("PlayerModule")
end

function PlayerModuleUtils.GetControlsTable()
  return require(PlayerModuleUtils.GetPlayerModule():WaitForChild("ControlModule"))
end

function PlayerModuleUtils.GetInputContextsFolder()
  local folder = PlayerModuleUtils.GetPlayerModule():WaitForChild("InputContexts")
  -- Note: For server authority, it needs to be retrieved from under the Player
  return folder
end

return PlayerModuleUtils


Migration Guide

Important things

  • The location for PlayerModule changed. The full path is now, game.StarterPlayer.PlayerModule.
    • This means there’s no self.controls nor self.camera anymore.

Currently, the PlayerModule says:

PlayerModule - This module file contains the public API for PlayerModule.

but at the bottom area of the Module they write this

– PlayerModule currently has no public API, but in future we can instantiate the internal class and expose methods here.

The new PlayerModule, does not return anything by default anymore. But you can expect there to be public API at some point.


  • When Server Authority is enabled InputContexts is cloned under each Player.
  • ControlModule is still a “singleton” and you can still require it and access some of the things that used to exist in the old one.

Overwriting PlayerModule - in case you want to

(or if you want your game to keep the old version)

In case you want to overwrite the default PlayerModule anyways (or if you KNOW that you want to keep an older version of the PlayerModule), here is what you have to do:

  • Enable game.Workspace.PlayerScriptsUseInputActionSystem
  • Have your PlayerModule ready!
    • e.g. Start a Play test then stop it to clone the new PlayerModule or have an override ready.
  • Uncheck game.StarterPlayer.CreateDefaultPlayerModule, so it is set to false.
  • Place your PlayerModule at…
    • If overwriting an older version that had it placed under game.StarterPlayer.StarterPlayerScripts then place it at the same place again.
    • If overwriting the new one, place it under game.StarterPlayer

Quick overview of the new PlayerModule internals

  • data.connectionUtil._connections has connections such as PLAYERMODULE_RENDERSTEPPED_CAMERA and PLAYERMODULE_RENDERSTEPPED_INPUT
  • playerData[<Player Instance here>]
  • .moveVector, .actions and other stuff


Migrating PlayerModule:GetControls()

:GetControls() is gone from the new one. But there are a few alternatives.

The bad way?

You can do require(game.StarterPlayer.PlayerModule.ControlModule). Since the way it currently is set-up you will get a “singleton”.

Otherwise, there’s not another way to get a similar object.

The good way?

There isn’t one that gives you these same objects, but you can get InputContexts if you check the next section.

Migrating controls:GetMoveVector()

The bad way?

It would just be

require(game.StarterPlayer.PlayerModule.ControlModule).playerData.moveVector)

But note that it is a Vector2 now, and NOT Vector3. It will also automatically update the moveVector based on controls.

The numbers might also be in a different position, so keep in mind.

The good way :thinking:

You’d use :GetState() on the right InputAction Instances in PlayerModule.InputContexts

Unlike .moveVector from the Module itself, you have to determine which of the controls are currently enabled. For example VehicleContext only appears to be enabled if you’re on a VehicleSeat.

Control Type / Platform Alternative to :GetMoveVector Description
All! MoveAction:GetState() Works even if ClickToMove is enabled! :partying_face:

Everything appears to write to MoveAction in the end. This means Mobile Input and Controller Input, will have all the vectors available in MoveAction.

:light_bulb: This gives an insight on how all these instances are used. You can bridge input to a unified location on where things get written to!

You’d use :GetState() for the things inside VehicleContext as well. I think the first is “Steer” and then “Throttle”.



Inverting Controls

To invert controls in the new PlayerModule, just locate CharacterContext and VehicleContext, because all you have to do, is to set Scale to a negative value.

Unfortunately, this doesn’t apply to Thumbstick from Mobile. MoveAction:Fire bypasses the Scale, since the scale is from the InputBindings which are child instances.

Though here is something that I tried to invert the mobile controls as well…

Script
_G.INVERT = true

local moveAction = game.StarterPlayer.PlayerModule.InputContexts.CharacterContext.MoveAction

local queueChainInvert

local function isStillInverting()
	return _G.INVERT
end

local function invert(value)
	moveAction:Fire(value * -1)

	if (isStillInverting()) then
		queueChainInvert()
	end
end

function queueChainInvert() 	
	moveAction.StateChanged:Once(function(value) 	
		invert(value)
	end)

	-- If you let go of the thumbstick the character won't stop though.
end

queueChainInvert()

However, that one won’t work for Server Authority, even though I did account for the InputContexts being moved under the Player when Server Authority is enabled.

Disabling Controls and Camera

Just disable all of the InputContext Instances.

:double_exclamation_mark: Note that if your character would sit on a VehicleSeat at that time, then VehicleContext will re-enable itself.

Otherwise if :Enable() and :Disable is your preferred way, it should still be the same.

require(game.StarterPlayer.PlayerModule.ControlModule):Disable()
require(game.StarterPlayer.PlayerModule.ControlModule):Enable()

Altering Keybinds

You can directly just alter the InputBindings to change key binds. There’s no need to create a StringValue and check for BoundKeys and MouseLockController anymore, for instance.



Migrating .moveFunction

You should still be able to do it through

require(game.StarterPlayer.PlayerModule.ControlModule).moveFunction

for now

But you might as well be able to achieve certain effects, without altering the functions at all just by changing Scale in the InputBinding.

Bind names

local CONNECTIONS = {
	BIND_TO_SIMULATION = "BIND_TO_SIMULATION", -- switch to bind_to_simulation when possible
	RENDERSTEPPED_INPUT = "PLAYERMODULE_RENDERSTEPPED_INPUT",
	RENDERSTEPPED_CAMERA = "PLAYERMODULE_RENDERSTEPPED_CAMERA",
	ONLOCALPLAYER = "ONLOCALPLAYER",
}

Previous ones were

  • ControlScriptRenderstep
  • cameraRenderUpdate


CursorImage

Nothing changed about "CursorImage".



Other things

The most used things I found, were just :GetControls, :GetMoveVector, :Enable and :Disable.

Some things remained the same, but are just at a different location.

I’ve also seen .moveFunction being used and wonder whether there will be a way to add modifiers to InputAction themselves, as it would be interesting.

3 Likes