Adding Player Controls - Client vs Server
Adding Player Controls - Client vs Server
Prerequisites | Have completed Arcade Action - Creating a Custom Character lesson. |
Lesson Time | 10 - 15 minutes |
Optional Handouts | N/A |
Learning Objectives |
|
Typically, it’s not considered a good thing when ships just fall from the sky. Since this game uses a custom character, the way that character moves needs to be created. This specific ship will move whenever the player presses a keyboard button and always point at their mouse.
Set Up Controls
To get the ship moving, you’ll use a VectorForce object to act as the engine of your ship. VectorForce objects push whatever they are attached to in a straight line. If you attach one to the HumanoidRootPart, it can be scripted to push the ship towards the player’s mouse.
Adding VectorForce and Attachment
- Under the HumanoidRootPart, add a VectorForce object and an Attachment.

- Set the VectorForce’s Attachment0 to the attachment created last step.
If you test right now, the ship won’t move because the controls need to be added through scripting.
Create the PlayerShipHandler
By default, players can only control the normal Humanoid avatar. Everything else is controlled by Roblox’s servers. Who controls an object, whether it’s the server or the player’s computer is referred to as network ownership. Players won’t be able to pilot the ship themselves until the server is told to give network ownership to the player, which the script below will do for you.
- Inside of ServerScriptService, add a Script.
- Rename the script PlayerShipHandler.
- Copy and paste the code below into the script.
--This script handles ship behavior on the server-side of the game -- Called when the character is added local function onCharacterAdded(character) local rootPart = character:WaitForChild("HumanoidRootPart") -- Wait until rootPart is part of the workspace while not rootPart:IsDescendantOf(workspace) do wait() end -- Gives control of the ship to the player rootPart:SetNetworkOwner(game.Players:GetPlayerFromCharacter(character)) end -- Called when a player is added to the game local function onPlayerAdded(player) player.CharacterAdded:Connect(onCharacterAdded) end -- Connect onPlayerAdded() to the PlayerAdded event. game.Players.PlayerAdded:Connect(onPlayerAdded)
Why Use IsDescendantOf()?
Roblox’s networking API can’t change the owner of a part unless it is in the workspace. Performing the while not
loop will stop the server from setting the network owner until the player is fully loaded into the game.
Roblox’s networking API can’t change the owner of a part unless it is in the workspace. Performing the while not
loop will stop the server from setting the network owner until the player is fully loaded into the game.
Creating Player Controls
Before we start coding the controls, now is a good time to start talking about client vs server. There are two places a script can run from. One place is the Roblox server and the other is the client, a player’s computer or device.
Script objects run on Roblox servers which helps make them more secure. Important game play features that you don’t want hacked, like awarding points, should happen on the server. The downside is that there can be lag while waiting for the server, and too many scripts running on the server will make the lag worse.
Things that need to run lag-free, like player controls, should run on the player’s computer using a LocalScript. In general, if the code is something that only the player should see, code it in a LocalScript. If all players should see it, write the code in a Script.
Set Up the ControlScript
This LocalScript will allow players to control the ship by holding down a button and aiming the ship with their mouse.
- In StarterPlayer > StarterPlayerScripts add a LocalScript named ControlScript.


- In ControlScript, add the following variables:
--This script determines what functions to call when a player presses a button when playing. -- Roblox Services local ContextActionService = game:GetService("ContextActionService") local Players = game:GetService("Players") -- Variables for the player, camera, and player’s mouse local player = game.Players.LocalPlayer local camera = workspace.CurrentCamera local mouse = player:GetMouse()
- At the bottom of the script, add the variables for ship speed and controls.
-- Configuration variables -- Sets the player's speed: The larger the negative number, the faster the ship will go. local PLAYER_SPEED = -45000 local FORWARD_KEY = Enum.KeyCode.W -- Creates a force vector that uses player's speed local forwardForceVector = Vector3.new(0,0,PLAYER_SPEED)
Using Different Controls
To change player controls, change the FORWARD_KEY
variable. Type Enum.Keycode.
to see a set of possible keys.
To change player controls, change the FORWARD_KEY
variable. Type Enum.Keycode.
to see a set of possible keys.
Make Players Move and Aim
The forward key act likes a gas pedal. While the player is pressing it, the ship’s vector force will go faster and faster. Releasing the key will stop the acceleration, but not the ship’s forward motion, just like a car.
- The function below handles the ship movement. On the next line in ControlScript, copy and paste:
-- Movement function that is called when the player presses the move forward button local function onMove(actionName, inputState) if inputState == Enum.UserInputState.Begin then -- If the player presses down the button, move forward player.Character.HumanoidRootPart.VectorForce.Force = forwardForceVector elseif inputState == Enum.UserInputState.End then -- If the player releases the button, stop moving player.Character.HumanoidRootPart.VectorForce.Force = Vector3.new(0,0,0) end end
- To make the player’s ship point towards the mouse, copy and paste:
-- Function that is called when the player moves their mouse or finger local function onAim() -- Make sure the character exists if player.Character then -- Finds the position of the mouse in the world and rotates the character model to face it local rootPart = player.Character:FindFirstChild("HumanoidRootPart") local mouseLocation = Vector3.new(mouse.Hit.X, rootPart.Position.Y, mouse.Hit.Z) rootPart.CFrame = CFrame.new(rootPart.Position, mouseLocation) end end
- Type the code below to connect the functions to ContextActionService:
-- Set up control bindings ContextActionService:BindAction("Aim", onAim, false, Enum.UserInputType.MouseMovement) ContextActionService:BindAction("Move", onMove, false, FORWARD_KEY)
- Test your code, press W to move the ship towards your mouse. The ship is alive!
Ship Moving Too Slow or Too Fast
Your ship may move differently depending on the material you picked. If the material of your ship is something other than “Granite”, you will want to experiment with the value of PLAYER_SPEED
. Granite parts are very heavy compared to other materials.
Your ship may move differently depending on the material you picked. If the material of your ship is something other than “Granite”, you will want to experiment with the value of PLAYER_SPEED
. Granite parts are very heavy compared to other materials.
Finished Player Controls Script »
--This script determines what functions to call when a player presses a button when playing.
-- Roblox Services
local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")
-- Variables for the player, camera, and player’s mouse
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local mouse = player:GetMouse()
-- Configuration variables
-- Sets the player's speed: -200000 will go fast, -20000 will go slow
local PLAYER_SPEED = -45000
local FORWARD_KEY = Enum.KeyCode.W
-- Creates a force vector that uses player's speed
local forwardForceVector = Vector3.new(0,0,PLAYER_SPEED)
-- Movement function that is called when the player presses the move forward button
local function onMove(actionName, inputState)
if inputState == Enum.UserInputState.Begin then
-- If the player presses down the button, move forward
player.Character.HumanoidRootPart.VectorForce.Force = forwardForceVector
elseif inputState == Enum.UserInputState.End then
-- If the player releases the button, stop moving
player.Character.HumanoidRootPart.VectorForce.Force = Vector3.new(0,0,0)
end
end
-- Function that is called when the player moves their mouse or finger
local function onAim()
-- Make sure the character exists
if player.Character then
-- Finds the position of the mouse in the world and rotates the character model to face it
local rootPart = player.Character:FindFirstChild("HumanoidRootPart")
local mouseLocation = Vector3.new(mouse.Hit.X, rootPart.Position.Y, mouse.Hit.Z)
rootPart.CFrame = CFrame.new(rootPart.Position, mouseLocation)
end
end
-- Set up control bindings
ContextActionService:BindAction("Aim", onAim, false, Enum.UserInputType.MouseMovement)
ContextActionService:BindAction("Move", onMove, false, FORWARD_KEY)
--This script determines what functions to call when a player presses a button when playing. -- Roblox Services local ContextActionService = game:GetService("ContextActionService") local Players = game:GetService("Players") -- Variables for the player, camera, and player’s mouse local player = Players.LocalPlayer local camera = workspace.CurrentCamera local mouse = player:GetMouse() -- Configuration variables -- Sets the player's speed: -200000 will go fast, -20000 will go slow local PLAYER_SPEED = -45000 local FORWARD_KEY = Enum.KeyCode.W -- Creates a force vector that uses player's speed local forwardForceVector = Vector3.new(0,0,PLAYER_SPEED) -- Movement function that is called when the player presses the move forward button local function onMove(actionName, inputState) if inputState == Enum.UserInputState.Begin then -- If the player presses down the button, move forward player.Character.HumanoidRootPart.VectorForce.Force = forwardForceVector elseif inputState == Enum.UserInputState.End then -- If the player releases the button, stop moving player.Character.HumanoidRootPart.VectorForce.Force = Vector3.new(0,0,0) end end -- Function that is called when the player moves their mouse or finger local function onAim() -- Make sure the character exists if player.Character then -- Finds the position of the mouse in the world and rotates the character model to face it local rootPart = player.Character:FindFirstChild("HumanoidRootPart") local mouseLocation = Vector3.new(mouse.Hit.X, rootPart.Position.Y, mouse.Hit.Z) rootPart.CFrame = CFrame.new(rootPart.Position, mouseLocation) end end -- Set up control bindings ContextActionService:BindAction("Aim", onAim, false, Enum.UserInputType.MouseMovement) ContextActionService:BindAction("Move", onMove, false, FORWARD_KEY)
These documents are licensed by Roblox Corporation under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Roblox, Powering Imagination, and Robux are trademarks of Roblox Corporation, registered in the United States and other countries.
Previous Creating A Custom Character Next Creating a Top-Down Camera