Helicopter Solution

https://create.roblox.com/store/asset/107651024770195/Apache-Helicopter-Simulator

tl;dr: we make a helicopter using math

--[[ The function for cyclic pitch. Components are 
collective pitch: the angle of attack of each airfoil in unison, 
the throttle float: vehicle seat part carries us hard here and in steerFloat as it's necessary to determine where the intent of the player is, 
and, finally, azimuthAngle: the init. point for a blade in its rotation around a unit circle.]]

local function cyclicPitch(collectivePitch, throttleFloat, steerFloat, azimuthAngle)
    return collectivePitch + (throttleFloat * math.sin(azimuthAngle)) + (steerFloat * math.cos(azimuthAngle))
end

end of tl;dr

So in lieu of the new aerodynamic solver, I decided to make my own fully articulated helicopter.
This means it has flapping, it has lead lag, and it has pitch. All three degrees of freedom necessary for stable, controllable helicopter flight. With the aero solver it’s no longer a cosmetic feature, but a physics defining element. So how does it work? It’s actually pretty basic. We check for a helicopter tagged with helicopter_chassis, look for the roles of the seats; there are two: pilot and co-pilot seats. Eventually the seats will be able to swap controls, but for now the unfinished goal is to have a pilot and a gunner. Once we satisfy the pilot condition, we can begin flight. We listen for either the controller or the keyboard. For example, to go up, you press “F”. Really basic stuff. The tricky part is the motor6Ds. They are CFramed to specifically to:

                    local leadLagAngle = -requestedThrottle * max_leadLag;
                    for _, bData in ipairs(mainBlades) do
                        local hubRelCF = physicsRoot.CFrame:ToObjectSpace(bData.HubPart.CFrame);
                        local bladeRestCF = hubRelCF * bData.OriginalC0 * bData.OriginalC1:Inverse();
                        local azimuth = math.atan2(bladeRestCF.LookVector.X, -bladeRestCF.LookVector.Z);

                        local cyclicPitch = ((-total_long * math.cos(azimuth + phase_offset)) + (-total_lat * math.sin(azimuth + phase_offset))) * max_cyclicPitch;
                        local totalFlap = (requestedThrottle * max_flapAngle) + ((-total_long * math.sin(azimuth) - total_lat * math.cos(azimuth)) * max_flapAngle);

                        bData.Motor.C0 = bData.OriginalC0 * CFrame.Angles(totalFlap, leadLagAngle, cyclicPitch);
                    end

To adjust the output of this you must adjust:

local max_cyclicPitch = math.rad(35);
local max_flapAngle = math.rad(1.725);
local max_leadLag = math.rad(1.5);
local phase_offset = math.pi / 2;
local mast_tiltTrim = math.rad(1.725);
2 Likes