I’m making a vehicle turret which is supposed to aim in all directions. I use ball socket constraint for the turret linked with the vehicles primary part and align orientation to move the turret.
As you see, the turret only moves in z and x axes and not in the y axis.
Spawn script:
local vehicleModel = VehicleName and Storage.Vehicles:FindFirstChild(VehicleName)
if vehicleModel then
local vehicleInstance = vehicleModel:Clone()
local driverSeat = vehicleInstance:FindFirstChild("DriverSeat") or vehicleInstance:FindFirstChildOfClass("VehicleSeat")
if driverSeat then
vehicleInstance:SetPrimaryPartCFrame(CF)
vehicleInstance.Parent = workspace
vehicleInstance.PrimaryPart:SetNetworkOwner(nil)
local VehicleGunName = GetRndChild(Faction.Roles[Role].GunNames).Name
local VehicleGunStat = Storage.GunStats[VehicleGunName]:Clone()
local VehicleGunModel = Storage.GunModels[VehicleGunName]:Clone()
VehicleGunStat.Name = "GunStat"
VehicleGunStat.LoadedAmmo.Value = VehicleGunStat.MaxAmmo.Value
VehicleGunStat.Parent = vehicleInstance
VehicleGunModel.Name = "GunModel"
-- Find the turret part we set up in Step 1.
local turretPart = vehicleInstance:FindFirstChild("Turret", true) -- <<< FIX IS HERE
if not turretPart then
warn("Vehicle prefab is missing a 'Turret' part!", vehicleInstance.Name)
return
end
turretPart:SetNetworkOwner(nil)
for i, part in vehicleInstance:GetDescendants() do
if part:IsA("BasePart") or part:IsA("UnionOperation") then
part:SetNetworkOwner(nil)
end
end
-- Parent the gun model to the vehicle instance
VehicleGunModel.Parent = vehicleInstance
VehicleGunModel.PrimaryPart:SetNetworkOwner(nil)
-- Position the gun relative to the turret part that aims.
VehicleGunModel:SetPrimaryPartCFrame(turretPart.CFrame * CFrame.new(0, 4, 0))
-- Create a WeldConstraint to rigidly attach the gun to the turret.
local gunWeld = Instance.new("WeldConstraint")
gunWeld.Name = "GunWeld"
gunWeld.Part0 = turretPart
gunWeld.Part1 = VehicleGunModel.PrimaryPart
gunWeld.Parent = gunWeld.Part0 -- Parent it to the turret part
CharsId += 1
vehicleInstance.Settings.General.Id.Value = CharsId
vehicleInstance.Settings.General.ActionTime.Value = Config.ActionDuration.Value * math.random(5, 15) / 10
vehicleInstance.Settings.General.OriginPos.Value = CF.Position
vehicleInstance.Settings.General.Movement.Value = "Mobile"
vehicleInstance.Settings.General.WalkTo.Value = CF.Position
vehicleInstance.Settings.General.Team.Value = TeamName
vehicleInstance.Settings.Combat.ReloadTime.Value = Config.ReloadTime.Value * math.random(0, 10) / 10
vehicleInstance.Settings.Combat.SuppressTime.Value = 0
for _, part in ipairs(C:GetChildren()) do
if part:IsA("BasePart") then
part.Transparency = 1
end
end
for _, part in ipairs(C.Arms:GetDescendants()) do
if part:IsA("BasePart") then
part.Transparency = 0
end
end
else
warn("Vehicle '" .. VehicleName .. "' has no DriverSeat! Spawning NPC normally.")
C.Parent = workspace
end
else
if VehicleName then
warn("Vehicle '" .. VehicleName .. "' not found in Storage.Vehicles! Spawning NPC normally.")
end
C.Parent = workspace
end
Aiming script:
elseif IsVehicle(C) then
-- Find the components. We must now also find the new WeldConstraint.
local turretPart = C:FindFirstChild("Turret", true)
if not turretPart then return end -- Silently exit if no turret part exists
local alignOrientation = turretPart:FindFirstChild("AlignOrientation", true)
local turretAttachment = turretPart:FindFirstChild("TurretAttachment", true)
local turretLockWeld = turretPart:FindFirstChild("TurretLockWeld") -- Get the new locking weld
-- Ensure ALL components exist before proceeding.
if not (alignOrientation and turretAttachment and turretLockWeld) then
warn("Vehicle", C.Name, "is missing aiming components (AlignOrientation, TurretAttachment, or TurretLockWeld).")
return
end
-- Get the target position from the settings value
local lookAtVal = C.Settings.General.LookAt.Value
if typeof(lookAtVal) == "Instance" and lookAtVal:IsA("Vector3Value") then
lookAtVal = lookAtVal.Value
elseif typeof(lookAtVal) ~= "Vector3" then
print("return")
return -- Not a valid target
end
-- This is the core logic: check if we should be aiming or idle/locked.
local isAiming = (lookAtVal ~= Vector3.new() and (turretAttachment.WorldPosition - lookAtVal).Magnitude > 0.1)
if isAiming then
-- AIMING STATE: Disable the lock, enable the aimer.
print("aiming and facing the target. turret")
-- Only change properties if they are not already in the correct state.
if turretLockWeld.Enabled then
turretLockWeld.Enabled = false
end
if not alignOrientation.Enabled then
alignOrientation.Enabled = true
end
-- Calculate and set the target CFrame for the AlignOrientation
local targetCFrame = CFrame.lookAt(turretAttachment.WorldPosition, lookAtVal)
alignOrientation.CFrame = targetCFrame
else
-- IDLE/LOCKED STATE: Disable the aimer, enable the lock.
print("Idle, locked state")
if alignOrientation.Enabled then
alignOrientation.Enabled = false
end
if not turretLockWeld.Enabled then
-- Enabling this weld will snap the turret back to the default forward
-- position that was saved when you created it in the prefab.
turretLockWeld.Enabled = true
end
end
-- Optional Debugger to visualize the aim point (only shows when aiming)
if USE_DEBUG_VISUALIZER then
local debugPart = workspace:FindFirstChild("TurretDebugAimPart")
if isAiming then
if not debugPart then
debugPart = Instance.new("Part", workspace)
debugPart.Name = "TurretDebugAimPart"
debugPart.Size = Vector3.new(1, 1, 1)
debugPart.Color = Color3.new(1, 0, 0)
debugPart.Material = Enum.Material.Neon
debugPart.Anchored = true
debugPart.CanCollide = false
end
debugPart.Position = lookAtVal
debugPart.Parent = workspace
elseif debugPart then
-- If not aiming and the debug part exists, hide it.
debugPart.Parent = nil
end
end
end
I really really appreciate your help!