There’s two sides to this. You can either go into fancy physics or go with BodyGyro and BodyVelocity. I’m afraid there’s not much of an in between that doesn’t involve aerodynamics and physics.
If you DO want to use BodyGyros and BodyVelocities. You can have the BodyVelocity on both wings and when you turn the plane, turn the BodyGyro and have the Wing thats the highest go forward and the other wing have No force or go Backwards. And the goal velocity should be the Planes lookvector
--// Services
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()
--// Variables
local Plane = workspace:WaitForChild("Plane")
local Throttle = 0
local ThrottleIncrease = false
local ThrottleDecrease = false
local SteerActive = false
local SteerCFrame = workspace.CurrentCamera.CFrame
local inAir = false
--// Mechanisms
Plane.Root.BodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
Plane.Root.BodyGyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if not gameProcessed then
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.W then
ThrottleIncrease = true
end
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.S then
ThrottleDecrease = true
end
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.E then
SteerActive = true
end
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.T then
if Plane.Settings.LandingGear.Value then
ReplicatedStorage.PlaneSystem.LandingGear:FireServer(false)
else
ReplicatedStorage.PlaneSystem.LandingGear:FireServer(true)
end
end
end
end
end)
UserInputService.InputEnded:Connect(function(input, gameProcessed)
if not gameProcessed then
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.W then
ThrottleIncrease = false
end
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.S then
ThrottleDecrease = false
end
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.E then
SteerActive = false
end
end
end
end)
while wait() do
if ThrottleIncrease then
if Throttle <= (Plane.Settings.MaxThrottle.Value - 1) then
Throttle = Throttle + 1
end
end
if ThrottleDecrease then
if Throttle >= 1 then
Throttle = Throttle - 1
end
end
if SteerActive then
SteerCFrame = workspace.CurrentCamera.CFrame
Plane.Mechanism.Steers.Left.Part.CylindricalConstraint.LowerAngle = (SteerCFrame.LookVector.X * 100)
Plane.Mechanism.Steers.Left.Part.CylindricalConstraint.UpperAngle = (SteerCFrame.LookVector.X * 100)
Plane.Mechanism.Steers.Right.Part.CylindricalConstraint.LowerAngle = (SteerCFrame.LookVector.X * 100)
Plane.Mechanism.Steers.Right.Part.CylindricalConstraint.UpperAngle = (SteerCFrame.LookVector.X * 100)
else
Plane.Mechanism.Steers.Left.Part.CylindricalConstraint.LowerAngle = 0
Plane.Mechanism.Steers.Left.Part.CylindricalConstraint.UpperAngle = 0
Plane.Mechanism.Steers.Right.Part.CylindricalConstraint.LowerAngle = 0
Plane.Mechanism.Steers.Right.Part.CylindricalConstraint.UpperAngle = 0
end
if Plane.Root.Velocity.magnitude >= 80 then
inAir = true
else
inAir = false
end
if inAir then
Plane.Root.BodyGyro.CFrame = CFrame.new(SteerCFrame.Position, SteerCFrame.Position + SteerCFrame.LookVector * 20)
else
Plane.Root.BodyGyro.CFrame = CFrame.new(SteerCFrame.Position, SteerCFrame.Position + Vector3.new(SteerCFrame.LookVector.X, 0, SteerCFrame.LookVector.Z) * 20)
end
Plane.Root.BodyVelocity.Velocity = SteerCFrame.LookVector * Throttle
Plane.Mechanism.Elevator.Union.CylindricalConstraint.InclinationAngle = -(SteerCFrame.LookVector.Y * 100)
Plane.Mechanism.Tail.Union.CylindricalConstraint.InclinationAngle = (SteerCFrame.LookVector.X * 50)
Plane.Mechanism.Propeller.Spinner.CylindricalConstraint.AngularVelocity = math.clamp(Throttle, 0, 125)
-- Steers
Plane.Mechanism.Steers.Left.Part.CylindricalConstraint.InclinationAngle = -(SteerCFrame.LookVector.Y * 100)
Plane.Mechanism.Steers.Right.Part.CylindricalConstraint.InclinationAngle = -(SteerCFrame.LookVector.Y * 100)
if Plane.PlaneServer.Pilot.Value == nil then
script:Destroy()
end
ReplicatedStorage.PlaneSystem.LocalData:FireServer({
["Throttle"] = Throttle
})
end
--[[
print(math.floor(Plane.Root.Velocity.magnitude))
if input.UserInputType == Enum.UserInputType.MouseButton1 then
end
]]--
Here is a quick view of the basics, rest of the code mostly just moves the constraints.
while wait() do
SteerCFrame = workspace.CurrentCamera.CFrame
if ThrottleIncrease then
if Throttle <= (Plane.Settings.MaxThrottle.Value - 1) then
Throttle = Throttle + 1
end
end
if ThrottleDecrease then
if Throttle >= 1 then
Throttle = Throttle - 1
end
end
Plane.Root.BodyGyro.CFrame = CFrame.new(SteerCFrame.Position, SteerCFrame.Position + SteerCFrame.LookVector * 20)
Plane.Root.BodyVelocity.Velocity = SteerCFrame.LookVector * Throttle
end
A classic (and relatively abrupt) way of solving this is having the velocity cut out when below a certain throttle. What I’d suggest is start scaling the maxforce of the velocity down once you reach this throttle threshold, to smooth it out.