Yes, youâll have to implement your own version of touch controls for this to work.
Random rambly background info
If you need touch controls and want them to be as close to the default controls as possible, you might want to actually use the default controls. Problem is, you need more control than the default system lets you have.
You don't really need to read all this, but I typed it already and I'm not going to delete it now >:C
Luckily, you can copy the default control scripts when the game is running and just insert them to StarterPlayerScripts, like so:
![image](//devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/4X/a/d/1/ad11648a7f539aa5949f9bec27b86de12b263e1f.png)
Now you can modify it to your heartâs content. Buuuuuut thereâs several thousand lines of code, so actually knowing it well enough to modify it to your needs is an enormous task.
Thankfully the default controls are super modular and self- contained. Sure it has all sorts of logic to calculate how the player should move with all sorts of control schemes, but it actually just outputs it in a single place. The PlayerModule
is just loaded/required by the PlayerScriptsLoader
, which has this single line of code:
local PlayerModule = require(script.Parent:WaitForChild("PlayerModule"))
The PlayerModule
basically just loads two other modules, CameraModule
and ControlModule
. ControlModule
deals with player controls, and has a property called moveFunction
which is just set to LocalPlayer.Move. If we change the PlayerScriptsLoader
to this:
local PlayerModule = require(script.Parent:WaitForChild("PlayerModule"))
PlayerModule.controls.moveFunction = function(...)
print(...)
end
All of the controls still work, and touch control widgets appear when emulating a mobile device in Studio. Instead of moving the character though, it just prints something like "ThanksRoBama 0, 0, 0 false"
. Looking at the wiki page I linked for Player.Move
, it expects a walkDirection
Vector3 and a relativeToCamera
bool.
So thatâs how you can hook into the default controls and override them ![:smiley: :smiley:](https://doy2mn9upadnk.cloudfront.net/images/emoji/twitter/smiley.png?v=12)
Usage example
To make it super convenient to hook into the default controls, you can delete the PlayerModule
and instead just have the script loader and a custom control script (shouldnât be called "ControlScript"
though, because we donât want to overwrite the default controls). Like so:
![image](//devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/4X/b/3/8/b38eea95f4acb9b6d112084ba1ad8762e519c54c.png)
Change the script loader to hook the controls into a BindableEvent, like so:
local PlayerMoveEvent = Instance.new("BindableEvent")
PlayerMoveEvent.Name = "PlayerMove"
PlayerMoveEvent.Parent = game.Players.LocalPlayer.PlayerScripts
local PlayerModule = require(script.Parent:WaitForChild("PlayerModule"))
PlayerModule.controls.moveFunction = function(player, direction, relativeToCamera)
PlayerMoveEvent:Fire(direction)
end
And here's an example custom control script that limits movement to the world X and Z axis:
local player = game.Players.LocalPlayer
local playerMoveEvent = player.PlayerScripts:WaitForChild("PlayerMove")
function absV3(v3)
return Vector3.new(
math.abs(v3.X),
math.abs(v3.Y),
math.abs(v3.Z)
)
end
function greatestAxis(v3)
local v3 = absV3(v3)
local max = math.max(v3.X, v3.Y, v3.Z)
if max == v3.X then
return Enum.Axis.X
elseif max == v3.Y then
return Enum.Axis.Y
else
return Enum.Axis.Z
end
end
function onPlayerMove(dir)
local moveAxis = greatestAxis(dir)
player:Move(Vector3.FromAxis(moveAxis) * dir, false)
end
playerMoveEvent.Event:Connect(onPlayerMove)
The moveFunction
seems to get called every RenderStepped
, so instead of calling getMoveDirection()
inside a function bound to RenderStepped
, you can just bind a function to the BindableEvent and it will get called just as often. The single argument that gets passed along is the desired walk direction, so getMoveDirection()
is obsolute with this system.