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 thePlayerModule. If you do that, you’ll have to change less in your scripts, if Roblox decides to move thePlayerModulelocation 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
PlayerModulechanged. The full path is now,game.StarterPlayer.PlayerModule.- This means there’s no
self.controlsnorself.cameraanymore.
- This means there’s no
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
InputContextsis cloned under each Player. ControlModuleis 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
PlayerModuleready!- e.g. Start a Play test then stop it to clone the new
PlayerModuleor have an override ready.
- e.g. Start a Play test then stop it to clone the new
- Uncheck
game.StarterPlayer.CreateDefaultPlayerModule, so it is set tofalse. - Place your
PlayerModuleat…- If overwriting an older version that had it placed under
game.StarterPlayer.StarterPlayerScriptsthen place it at the same place again. - If overwriting the new one, place it under
game.StarterPlayer
- If overwriting an older version that had it placed under
Quick overview of the new PlayerModule internals
data.connectionUtil._connectionshas connections such asPLAYERMODULE_RENDERSTEPPED_CAMERAandPLAYERMODULE_RENDERSTEPPED_INPUTplayerData[<Player Instance here>].moveVector,.actionsand 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 
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! |
Everything appears to write to MoveAction in the end. This means Mobile Input and Controller Input, will have all the vectors available in MoveAction.
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.
Note that if your character would sit on a
VehicleSeatat that time, thenVehicleContextwill 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
ControlScriptRenderstepcameraRenderUpdate
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.

