Hey, I have a sort of a complicated question. I have this movement system for an upcoming marble game. Currently it works perfectly with WASD. Here is the code for context of the upcoming paragraph:
Marble Movement Code
local uis = game:GetService("UserInputService")
local cas = game:GetService("ContextActionService")
local gui = game:GetService("StarterGui")
local rs = game:GetService("RunService")
local forwards, left, right, back = false, false, false, false
local Thumbstick = require(script.PlayerModule.ControlModule.TouchThumbstick)
local thumbframe = Thumbstick.new()
thumbframe:Create(game.Players.LocalPlayer.PlayerGui:WaitForChild("GameUI").TouchFrame.Zone)
cas:UnbindAction("moveForwardAction")
local plr = game.Players.LocalPlayer
local forwardsens = 2.4
local rightsens = 2.4
local leftsens = -2.4
local backsens = -2.4
local MouseInput = {
Enum.UserInputType.MouseButton1,
Enum.UserInputType.MouseButton2,
Enum.UserInputType.MouseButton3,
Enum.UserInputType.MouseMovement,
Enum.UserInputType.MouseWheel
}
local function CheckForMouseInput(InputType)
for _,mouse in pairs(MouseInput) do
if InputType == mouse then
return true
end
end
return false
end
local function CharacterAdded(chr)
local cam = workspace.CurrentCamera
local multiplier = Vector3.new(0,0,0)
local hum = chr:WaitForChild("HumanoidRootPart")
local steadypart = Instance.new("Part")
steadypart.Parent = chr
steadypart.CFrame = hum.CFrame
steadypart.Massless = true
steadypart.CanCollide = false
steadypart.Size = Vector3.new(1,1,1)
local function moveForwards(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
forwards = true
end
if inputState == Enum.UserInputState.End then
forwards = false
end
end
local function moveLeft(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
left = true
end
if inputState == Enum.UserInputState.End then
left = false
end
end
local function moveRight(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
right = true
end
if inputState == Enum.UserInputState.End then
right = false
end
end
local function moveBack(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
back = true
end
if inputState == Enum.UserInputState.End then
back = false
end
end
local d = true
hum.Touched:Connect(function(obj)
if obj.Name == "BoostPart" and d then
d = false
local p = obj.Power
multiplier *= p and p.Value or 5
spawn(function()
wait(2)
d = true
end)
end
end)
local function removePitch(cf)
local pitch, yaw, roll = cf:toEulerAnglesYXZ()
return CFrame.fromEulerAnglesYXZ(0, yaw, roll) + cf.p
end
game.ReplicatedStorage.Events.Manipulate.Event:Connect(function(direction)
multiplier += Vector3.new(direction.X, 0, direction.Y) * forwardsens
end)
rs.RenderStepped:Connect(function(dt)
local angle = cam.CFrame - cam.CFrame.Position
local c = (angle + steadypart.CFrame.Position)
steadypart.CFrame = removePitch(CFrame.fromMatrix(hum.Position, c.XVector, c.YVector, c.ZVector))
if forwards then
multiplier += steadypart.CFrame.LookVector * forwardsens
end
if back then
multiplier += steadypart.CFrame.LookVector * backsens
end
if left then
multiplier += steadypart.CFrame.RightVector * leftsens
end
if right then
multiplier += steadypart.CFrame.RightVector * rightsens
end
-- Movement is a BodyVelocity with a P of 100 and a MaxForce of (6000, 3000, 6000)
multiplier *= 0.91
hum.Movement.Velocity = multiplier * 2.23
hum.FakeDrag.Force = -hum.Movement.Velocity
end)
cas:BindAction("moveForwardAction", moveForwards, false, Enum.PlayerActions.CharacterForward)
cas:BindAction("moveBackwardAction", moveBack, false, Enum.PlayerActions.CharacterBackward)
cas:BindAction("moveLeftAction", moveLeft, false, Enum.PlayerActions.CharacterLeft)
cas:BindAction("moveRightAction", moveRight, false, Enum.PlayerActions.CharacterRight)
end
-- do initial check
if uis:GetLastInputType() == Enum.UserInputType.Touch then
thumbframe:Enable(true)
end
uis.LastInputTypeChanged:Connect(function(InputType)
if InputType == Enum.UserInputType.Keyboard or CheckForMouseInput(InputType) then -- If on computer.
thumbframe:Enable(false)
end
if InputType == Enum.UserInputType.Touch then -- Might be on computer, but enabled touch controls.
thumbframe:Enable(true)
end
end)
uis.TouchTap:Connect(function()
thumbframe:Enable(true)
end)
if plr.Character then
CharacterAdded(plr.Character)
end
plr.CharacterAdded:Connect(CharacterAdded)
That works fine! The issue is when it comes to mobile controls. I’m using Roblox’s TouchThumbstick module (PlayerModule → ControlModule → TouchThumbstick) to create a thumbstick, which again works fine. The issue is that my code for detecting which way the marble should roll only works from certain camera angles. From certain camera angles, the ball rolls the opposite way the stick was moved. Here are the important parts of that script (it is very long so I couldn’t post the whole thing):
TouchThumbstick Code
local function Rotate(vector, angle)
return Vector2.new(
vector.X * math.cos(angle) + vector.Y * math.sin(angle),
-vector.X * math.sin(angle) + vector.Y * math.cos(angle)
)
end
local super = 0
-- when thumbstick is moved
if inputObject == self.moveTouchObject then
centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
super = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
local look = workspace.CurrentCamera.CFrame.LookVector
local angle = math.atan2(look.Z, look.X) + math.pi / 2
local old = (inputObject.Position - inputObject.Delta)
super = Rotate(super.Unit, angle)
end
-- when thumbstick movement has stopped
if inputObject == self.moveTouchObject then
self:OnInputEnded()
super = 0
end
end)
game:GetService("RunService").Heartbeat:Connect(function(dt)
if super ~= 0 then
game.ReplicatedStorage.Events.Manipulate:Fire(super)
end
end)
I think the reason why it only works with certain camera angles is because in my original script, I could change what the LookVector is being multiplied by ( multiplier += steadypart.CFrame.LookVector * 2.4
). With this, it’s always being multiplied by a positive number despite the camera angle.
multiplier += Vector3.new(direction.X, 0, direction.Y) * forwardsens
What can I do to fix this? If you need any more context don’t hesitate to ask. Thanks!
P.S, I made a small image to help visualize my problem in simpler, less wordy form: