Currenty I’m trying to make a 2D Character Animation System, where if you press the W A S or D keys, the character will begin moving in the according direction. Whenever I press one of these keys, the character has a chance of violently vibrating. I’m not sure if this is Roblox Studio failing to update the character position in time or a misuse of something on my part.
Video Example:
MovementScript:This is a client script!
--Variables
local UserInputService = game:GetService("UserInputService")
local Character = game.Workspace:WaitForChild("2DCharacter")
local ForwardWalking = false
local LeftWalking = false
local RightWalking = false
local BackwardWalking = false
local function WkeyPressed()
while BackwardWalking do
wait(.00000000001)
Character.Position = Vector3.new(Character.Position.X - 1,Character.Position.Y, Character.Position.Z)
end
end
local function AkeyPressed()
while LeftWalking do
wait(.00000000001)
Character.Position = Vector3.new(Character.Position.X ,Character.Position.Y, Character.Position.Z - 1)
end
end
local function SkeyPressed()
while ForwardWalking do
wait(.00000000001)
Character.Position = Vector3.new(Character.Position.X + 1,Character.Position.Y, Character.Position.Z)
end
end
local function DkeyPressed()
while RightWalking do
wait(.00000000001)
Character.Position = Vector3.new(Character.Position.X ,Character.Position.Y, Character.Position.Z + 1)
end
end
local function Idle()
while not BackwardWalking or LeftWalking or RightWalking or ForwardWalking do
wait()
--Put something here
end
end
--Input & Output
UserInputService.InputBegan:Connect(function(input, gameProcessedEvent) --Input
if input.KeyCode == Enum.KeyCode.W then
BackwardWalking = true
WkeyPressed()
elseif
input.KeyCode == Enum.KeyCode.A then
LeftWalking = true
AkeyPressed()
elseif
input.KeyCode == Enum.KeyCode.S then
ForwardWalking = true
SkeyPressed()
elseif
input.KeyCode == Enum.KeyCode.D then
RightWalking = true
DkeyPressed()
end
end)
UserInputService.InputEnded:Connect(function(input, gameProcessedEvent) -- Output
if input.KeyCode == Enum.KeyCode.W then
BackwardWalking = false
Idle()
elseif
input.KeyCode == Enum.KeyCode.A then
LeftWalking = false
Idle()
elseif
input.KeyCode == Enum.KeyCode.S then
ForwardWalking = false
Idle()
elseif
input.KeyCode == Enum.KeyCode.D then
RightWalking = false
Idle()
end
end)
CameraScript:This is a client script!
--Variables
local Character = game.Workspace:WaitForChild("2DCharacter")
local CameraPart2 = Character.CameraAttachment
local Camera = game.Workspace.CurrentCamera
local FocusPart2 = Character.CameraAttachment
local Player = game:GetService("Players").LocalPlayer
--Whenever the Customize button is clicked it fires this function
while true do
wait()
--Sets cameras cframe and focus
Camera.CameraType = "Scriptable"
Camera.CameraSubject = CameraPart2
Camera.Focus = FocusPart2.WorldCFrame
Camera.CFrame = FocusPart2.WorldCFrame
end
This. Your user input processing code should also have zerowait() calls of any flavor and zerowhile do loops. Input event handlers start their own Lua threads so having yielding calls or loops in them creates the chaos you’re currently experiencing. Just set and clear your directional boolean flags on the input began and ended events, and then separately bind to RenderStep with input priority and process the flags to generate that frame’s move direction based on the bools.
As I can’t test this it may not be the best way of doing it(there are a few more) but here is an example I of how it could be setup with renderstepped and changing your previous code a bit no loops
--Variables
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService('RunService') -- added this for render function below
local Character = game.Workspace:WaitForChild("2DCharacter")
local CameraPart2 = Character.CameraAttachment
local Camera = game.Workspace.CurrentCamera
local FocusPart2 = Character.CameraAttachment
local Player = game:GetService("Players").LocalPlayer
Camera.CameraType = "Scriptable" -- sets the camera up just once renderstepped below will set cframes
Camera.CameraSubject = CameraPart2
local ForwardBackwardWalking = 0 -- changed so they can be set to either 1 or 0 or -1
local LeftRightWalking = 0
--[[ -- might not need this if you do then it will need changed
local function Idle() --
while not BackwardWalking or LeftWalking or RightWalking or ForwardWalking do
wait()
--Put something here
end
end
--]]
--Input & Output
UserInputService.InputBegan:Connect(function(input, gameProcessedEvent) --Input
if input.KeyCode == Enum.KeyCode.W then
ForwardBackwardWalking = -1
elseif input.KeyCode == Enum.KeyCode.S then
ForwardBackwardWalking = 1
elseif input.KeyCode == Enum.KeyCode.A then
LeftRightWalking = -1
elseif input.KeyCode == Enum.KeyCode.D then
LeftRightWalking = 1
end
end)
UserInputService.InputEnded:Connect(function(input, gameProcessedEvent) -- Output
if input.KeyCode == Enum.KeyCode.W then
-- this will set to 0 only if its curretly set to this keys value else it will just leave it alone since the other key maybe active
ForwardBackwardWalking = ForwardBackwardWalking == -1 and 0 or ForwardBackwardWalking
elseif input.KeyCode == Enum.KeyCode.S then
ForwardBackwardWalking = ForwardBackwardWalking == 1 and 0 or ForwardBackwardWalking
elseif input.KeyCode == Enum.KeyCode.A then
LeftRightWalking = LeftRightWalking == -1 and 0 or LeftRightWalking
elseif input.KeyCode == Enum.KeyCode.D then
LeftRightWalking = LeftRightWalking == 1 and 0 or LeftRightWalking
end
end)
RunService.RenderStepped:Connect(function()
Character.Position = Vector3.new(Character.Position.X + ForwardBackwardWalking,Character.Position.Y, Character.Position.Z + LeftRightWalking)
--Sets cameras cframe and focus
Camera.Focus = FocusPart2.WorldCFrame
Camera.CFrame = FocusPart2.WorldCFrame
end)
Writing controls like this with loops and waits in each button function is going to cause you infinite pain, since you cannot directly control the order in which each function resumes. This could lead to the situation you’re seeing. Instead, it is typical to have each button function only control a variable, kind of like the ones you already have, and then doing the actual updates to the position in a single function which is looped (or even better bound to Stepped / Heartbeat) and has a single specific location where it waits. This makes the timing situation much more predictable.