I am making a game centered around flying planes. I want controls nearly identical to a game called War Thunder. but it is proving to be difficult. In War Thunder, there is a circle in the 3D space (I am using a placeholder part with a billboard gui of a circle) and it orbits around the plane using the mouse movement (mouse delta). The problem I am having is that when the plane rolls, the camera tilts with the plane. This means that when moving the mouse up on the screen, the circle should move upwards relative to the plane and sideways relative to the world. This isn’t happening though. I attempted to write code that resolved this issue by rotating the circle and using the camera as a reference but this leads to a weird behavior of the path of the circle spiraling and getting stuck in weird places. I have been stuck on this for HOURS so if anybody could help I would be eternally grateful
game.ReplicatedStorage.Events.SpawnRequest:FireServer("Test", "Test")
--Services
local run_service = game:GetService("RunService")
local user_input_service = game:GetService("UserInputService")
local replicated_storage = game:GetService("ReplicatedStorage")
--Main variables
local player = game.Players.LocalPlayer
local plane = nil
local camera = workspace.CurrentCamera
local events = replicated_storage:WaitForChild("Events")
local spawn_event = events:WaitForChild("SpawnRequest")
local update_event = events:WaitForChild("PlaneUpdate")
--Rig parts
local mouse_rig = Instance.new("Part")
local camera_rig = Instance.new("Part")
--Temporary variables
local damp = 3
local speed = 15
--Input axes
local roll_input = 0
local pitch_input = 0
local yaw_input = 0
--Create visuals
local circle = Instance.new("Attachment")
local crosshair = Instance.new("Attachment")
local billboard = Instance.new("BillboardGui")
billboard.Size = UDim2.new(0,80,0,80)
billboard.AlwaysOnTop = true
local billboard2 = Instance.new("BillboardGui")
billboard2.Size = UDim2.new(0,40,0,40)
billboard2.AlwaysOnTop = true
local image = Instance.new("ImageLabel")
image.Image = "rbxassetid://11738792917"
image.Size = UDim2.new(1,0,1,0)
image.BackgroundTransparency = 1
image.Parent = billboard
local image = Instance.new("ImageLabel")
image.Image = "rbxassetid://316279304"
image.Size = UDim2.new(1,0,1,0)
image.BackgroundTransparency = 1
image.Parent = billboard2
billboard.Parent = circle
billboard2.Parent = crosshair
circle.Parent = workspace.Terrain
crosshair.Parent = workspace.Terrain
run_service.RenderStepped:Connect(function(delta_time)
if plane == nil then
camera.CameraType = Enum.CameraType.Follow
billboard.Enabled = false
billboard2.Enabled = false
else
camera.CameraType = Enum.CameraType.Scriptable
--Variables
local root = plane.PrimaryPart or plane:GetPropertyChangedSignal("PrimaryPart"):Wait()
--Enable visuals
billboard.Enabled = true
billboard2.Enabled = true
--Mouse input
user_input_service.MouseBehavior = Enum.MouseBehavior.LockCenter
local mouse_x = math.clamp(user_input_service:GetMouseDelta().X,-10,10)
local mouse_y = math.clamp(user_input_service:GetMouseDelta().Y,-8,8)
--Circle
circle.WorldPosition = mouse_rig.Position + mouse_rig.CFrame.LookVector * 500
crosshair.WorldPosition = root.Position + root.CFrame.LookVector * 500
--Roll
roll_input = 0
if user_input_service:IsKeyDown(Enum.KeyCode.D) then
roll_input-=1
elseif user_input_service:IsKeyDown(Enum.KeyCode.A) then
roll_input+=1
end
--Pitch
pitch_input = 0
if user_input_service:IsKeyDown(Enum.KeyCode.S) then
pitch_input-=1
elseif user_input_service:IsKeyDown(Enum.KeyCode.W) then
pitch_input+=1
end
--Yaw
yaw_input = 0
if user_input_service:IsKeyDown(Enum.KeyCode.E) then
yaw_input-=1
elseif user_input_service:IsKeyDown(Enum.KeyCode.Q) then
yaw_input+=1
end
--Mouse rig
if user_input_service:IsKeyDown(Enum.KeyCode.C) then
mouse_rig.CFrame *= CFrame.fromOrientation(pitch_input,yaw_input,0)
else
mouse_rig.Position = Vector3.new(0,15,0)
print(mouse_y)
mouse_rig.CFrame *= CFrame.fromAxisAngle(camera.CFrame:ToWorldSpace().UpVector, -mouse_x/100)
end
--Camera rig
camera_rig.Position = camera_rig.Position:Lerp(root.Position, 5 * delta_time)
if user_input_service:IsKeyDown(Enum.KeyCode.C) then
camera_rig.CFrame *= CFrame.fromOrientation(math.rad(-mouse_y/100*15),math.rad(-mouse_x/100*15),0)
else
camera_rig.CFrame = camera_rig.CFrame:Lerp(CFrame.lookAt(root.Position, circle.Position, root.CFrame.UpVector), 1.5 * delta_time)
end
--Camera movement
camera.CFrame = camera_rig.CFrame * CFrame.new(0,20,40)
--Plane movement
plane:PivotTo(root.CFrame:ToWorldSpace(CFrame.new(0,0,-speed * delta_time)))
plane:PivotTo(plane.PrimaryPart.CFrame:Lerp(CFrame.lookAt(root.Position, mouse_rig.Position + mouse_rig.CFrame.LookVector * 500, root.CFrame.UpVector) * CFrame.fromOrientation(0,0,math.rad(roll_input*50)), delta_time * damp))
--Send to server
update_event:FireServer(root.CFrame)
end
end)
spawn_event.OnClientEvent:Connect(function(plane_input)
local clone = nil
if plane_input ~= nil then
clone = plane_input:Clone()
clone.Parent = workspace.Planes
for i,v in pairs(workspace.Planes:GetChildren()) do
if v:GetAttribute("user_id") == player.UserId then
v:Destroy()
end
end
end
if plane ~= nil then
plane:Destroy()
end
plane = clone
end)