How to add stabilizer to Ferrarico tank system

i cant upload a video bc it too large, so ill explain how this turret System work(Desc + Code review) :

  • Desc :
    we have a TurretRing and CannonHinge
    Both have 1 thing called “BTWeld” which is used to control rotation of turret(weld)
    TurretRing - control y Axis of rotation
    CannonHinge - control x Axis of rotation
    also you might asking what are “BTWeld” welded to(into make this clear) :
    BTWeld(TurretRing) : “TurretBase” which inside Chassi.Structure (body of tank)
    BTWeld(CannonHinge) : “CannonRef” which inside Chassi.Turret.Structure(turret head)
    reminder : (turret head != cannon)
    Turret Script are running on Client sided, which player have SetNetworkOnwer to the tank(Other client can see tank turret moving while server doesnt)

the problem :
while i aiming inside tank, and then turn tank either to left or right, the aim it just doesnt lock where it aim(Only if i aiming)

  • LocalScript(Turret) :
local Vehicle = script.Parent.Vehicle.Value
local DriverSeat = Vehicle.Chassi.Structure.DriverSeat.Seat

local UserInputService = game:GetService("UserInputService")
local AimPart = Vehicle.Chassi.Turret.Cannon.GunnerScope
local ViewPart = Vehicle.Chassi.Structure.DriverScope
local HorBase = Vehicle.Chassi.Turret.Structure.TurretRing
local VertBase = Vehicle.Chassi.Turret.Cannon.CannonHinge
local VertRef = Vehicle.Chassi.Turret.Structure.CannonRef
local SmokeLauncherH = Vehicle.Chassi.Structure.EngineBay.MainEngine.ESS
local SmokeLauncherT = Vehicle.Chassi.Turret.Structure.SmokeLauncher
--local FlareLauncher = Vehicle.Chassi.Turret.Structure.FlareLauncher
local event = game.ReplicatedStorage.TankGunsFolder:WaitForChild("TurretRotateEvent")
local currentAngle = VertBase.Orientation.X-VertRef.Orientation.X
local RunService = game:GetService("RunService")
local Horn = Vehicle.Chassi.Structure.EngineBay.MainEngine.Horn
local Indicator = script.Parent.DriverFrame.TankIndicator
local GunnerSeat = Vehicle.Chassi.Turret.Structure.GunnerSeat.Seat

local Player = game.Players.LocalPlayer
local mouse = Player:GetMouse()
local Camera = workspace.CurrentCamera
Camera.CameraSubject = Player.Character.Humanoid
local Aiming = false
local DriverView = false
local SensLimit = 2.3
local motorTurn = false
Indicator.Turret.Rotation = (Vehicle.Chassi.Structure.TurretBase.Orientation.Y-HorBase.Orientation.Y)

mouse.TargetFilter = Vehicle

local IsTurretAiming = script.Parent.IsTurretAiming
local IsCommTurretAiming = script.Parent.IsCommTurretAiming

-- Config
local ZoomLevel = 0
local tankModule = require(Vehicle.ModdedScript.Config)

-- Mobile Support
local function IsMobile()
	return UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled
end

local MobiButtons = script.Parent.MobileButtons
local Mobile = false

-- Variables to track touch state
local currentTouch = nil
local previousTouchPosition = nil

-- Settings (adjust these as needed)
local SensLimit = 50  -- Max movement speed per frame
local BaseSensitivity = 0.07  -- How responsive the turret is
local currentsen = BaseSensitivity

local DefaultFOV = Camera.FieldOfView


local function GetBestSen(BaseSen, defaultFOV, currentFOV)
	return BaseSen * (currentFOV / defaultFOV)
end

-- Crosshair
local CrosshairAim = script.Parent.Periscope.ImageLabel

local function EnterCamera()
	if IsCommTurretAiming.Value then return end
	Aiming = true
	DriverView = false
	Camera.CameraType = "Scriptable"
	Camera.CFrame = AimPart.CFrame
	UserInputService.MouseDeltaSensitivity = 0.03
	UserInputService.MouseIconEnabled = false
	UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
	Camera.FieldOfView = 7
	script.Parent.Periscope.Visible = true
	-- Addon
	DriverSeat["Tank Cannon Rotating"].Playing = true
	MobiButtons.ZoomButton.Visible = true
	IsTurretAiming.Value = true
end

local function EnterDriverView()
	Aiming = false
	DriverView = true
	Camera.CameraType = "Scriptable"
	Camera.CFrame = ViewPart.CFrame
	UserInputService.MouseIconEnabled = false
	Camera.FieldOfView = 70
	-- Addon
	DriverSeat["Tank Cannon Rotating"].Playing = false
	MobiButtons.ZoomButton.Visible = false
	IsTurretAiming.Value = true
	currentsen = BaseSensitivity
end

local function ExitCamera()
	Aiming = false
	DriverView = false
	motorTurn = false
	Camera.CameraType = "Custom"
	Camera.FieldOfView = 70
	Camera.CameraSubject = Player.Character.Humanoid
	UserInputService.MouseDeltaSensitivity = 1
	UserInputService.MouseBehavior = Enum.MouseBehavior.Default
	UserInputService.MouseIconEnabled = true
	script.Parent.Periscope.Visible = false
	-- Addon
	DriverSeat["Tank Cannon Rotating"].Playing = false
	MobiButtons.ZoomButton.Visible = false
	IsTurretAiming.Value = false
	currentsen = BaseSensitivity
end

if IsMobile() then 
	MobiButtons.Visible = true
	Mobile = true
end

if Mobile then
	MobiButtons.AimButton.MouseButton1Click:Connect(function()
		if Aiming == false then 
			ExitCamera()
			EnterCamera()
		elseif Aiming == true then 
			ExitCamera()
			ZoomLevel = 0
		end
	end)
	
	MobiButtons.DriverViewButton.MouseButton1Click:Connect(function()
		if DriverView == false then 
			ExitCamera()
			EnterDriverView()
		elseif DriverView == true then 
			ExitCamera()
		end
	end)
	
	MobiButtons.ZoomButton.MouseButton1Click:Connect(function()
		if not Aiming then return end
		if ZoomLevel < tankModule.MaxZoomLevel then
			ZoomLevel += 1
			currentsen = GetBestSen(BaseSensitivity, DefaultFOV, Camera.FieldOfView)
		else
			ZoomLevel = 0
			currentsen = BaseSensitivity
		end
	end)
	
	MobiButtons.ReleaseSmoke.MouseButton1Click:Connect(function()
		if SmokeLauncherH.Explosion.Enabled then return end
		SmokeLauncherH.Explosion.Enabled = true
		SmokeLauncherH.Smoke:Play()
		task.wait(20)
		SmokeLauncherH.Smoke:Stop()
		SmokeLauncherH.Explosion.Enabled = false
	end)
	
	MobiButtons.LaunchSmoke.MouseButton1Click:Connect(function()
		if SmokeLauncherT.Grenade.Transparency == 1 then return end
		SmokeLauncherT.RemoteEvent:FireServer(SmokeLauncherT.Launcher.Attachment.WorldPosition, SmokeLauncherT.Launcher.Attachment.WorldOrientation)
	end)
	
	MobiButtons.HeadLight.MouseButton1Click:Connect(function()
		Vehicle.Chassi.Structure.HeadLights.RemoteEvent:FireServer()
	end)
	
	MobiButtons.IntLight.MouseButton1Click:Connect(function()
		Vehicle.Chassi.Structure.InteriorLights.RemoteEvent:FireServer()
	end)
	
	MobiButtons.Horn.InputBegan:Connect(function()
		Horn.RemoteEvent:FireServer("Start")
	end)
	
	MobiButtons.Horn.InputEnded:Connect(function()
		Horn.RemoteEvent:FireServer("Stop")
	end)
else
	UserInputService.InputBegan:connect(function(inputObject, gameProcessedEvent)
		if inputObject.KeyCode == Enum.KeyCode.F and not gameProcessedEvent then
			if Aiming == false then 
				ExitCamera()
				EnterCamera()
			elseif Aiming == true then 
				ExitCamera()
				ZoomLevel = 0
			end
		elseif inputObject.KeyCode == Enum.KeyCode.V and not gameProcessedEvent then
			if DriverView == false then 
				ExitCamera()
				EnterDriverView()
			elseif DriverView == true then 
				ExitCamera()
			end
		elseif inputObject.KeyCode == Enum.KeyCode.Z and Aiming == true and not gameProcessedEvent then
			if ZoomLevel < tankModule.MaxZoomLevel then
				ZoomLevel += 1
				
			else
				ZoomLevel = 0
			end
		elseif inputObject.KeyCode == Enum.KeyCode.C and not gameProcessedEvent then
			if SmokeLauncherH.Explosion.Enabled then return end
			SmokeLauncherH.Explosion.Enabled = true
			SmokeLauncherH.Smoke:Play()
			task.wait(20)
			SmokeLauncherH.Smoke:Stop()
			SmokeLauncherH.Explosion.Enabled = false
		elseif inputObject.KeyCode == Enum.KeyCode.J and not gameProcessedEvent then
			if SmokeLauncherT.Grenade.Transparency == 1 then return end
			SmokeLauncherT.RemoteEvent:FireServer(SmokeLauncherT.Launcher.Attachment.WorldPosition, SmokeLauncherT.Launcher.Attachment.WorldOrientation)
	--[[elseif inputObject.KeyCode == Enum.KeyCode.J and not gameProcessedEvent then
		if FlareLauncher.Grenade.Transparency == 0 then 
			FlareLauncher.Grenade.Transparency = 1
			FlareLauncher.RemoteEvent:FireServer(FlareLauncher.Launcher.Attachment.WorldPosition,FlareLauncher.Launcher.Attachment.WorldOrientation)
		end
	]]
		elseif inputObject.KeyCode == Enum.KeyCode.L and not gameProcessedEvent then
			Vehicle.Chassi.Structure.HeadLights.RemoteEvent:FireServer()
		elseif inputObject.KeyCode == Enum.KeyCode.K and not gameProcessedEvent then
			Vehicle.Chassi.Structure.InteriorLights.RemoteEvent:FireServer()
		elseif inputObject.KeyCode == Enum.KeyCode.H and not gameProcessedEvent then
			Horn.RemoteEvent:FireServer("Start")
		end
	end)
	
	UserInputService.InputEnded:connect(function(inputObject, gameProcessedEvent)
		if inputObject.KeyCode == Enum.KeyCode.H and not gameProcessedEvent then
			Horn.RemoteEvent:FireServer("Stop")
		end
	end)
end


local function MoveTurretPC()
	if Aiming then
		Camera.CameraType = "Scriptable"
		Camera.FieldOfView = tankModule.ZoomLevelTable[ZoomLevel]
		UserInputService.MouseDeltaSensitivity = 0.03
		currentAngle = VertBase.Orientation.X-VertRef.Orientation.X
		if motorTurn == true then
			local position = Vector2.new(mouse.X, mouse.Y)
			local size = Vector2.new(mouse.ViewSizeX, mouse.ViewSizeY)
			local normalizedPosition = position / size
			if normalizedPosition.X < 0.45 then
				HorBase.BTWeld.C0 = HorBase.BTWeld.C0 * CFrame.Angles(math.rad((1-normalizedPosition.X)/2.8),0,0)
			elseif normalizedPosition.X > 0.55 then
				HorBase.BTWeld.C0 = HorBase.BTWeld.C0 * CFrame.Angles(math.rad(-normalizedPosition.X/2.8),0,0)
			end
		elseif motorTurn == false then
			local delta = UserInputService:GetMouseDelta()
			local deltaX = delta.X
			local deltaY = delta.Y
			if delta.X > SensLimit then
				deltaX = SensLimit
			elseif delta.X < -SensLimit then
				deltaX = -SensLimit
			end
			if delta.Y > SensLimit then
				deltaY = SensLimit
			elseif delta.Y < -SensLimit then
				deltaY = -SensLimit
			end
			if currentAngle > 0 then
				if currentAngle-deltaY > 13 then
					deltaY = 0
				end
			elseif currentAngle < 0 then
				if currentAngle-deltaY < -5 then
					deltaY = 0
				end
			end
			--if not HorBase.BTWeld then return end
			HorBase.BTWeld.C0 = HorBase.BTWeld.C0 * CFrame.Angles(math.rad(-deltaX),0,0) -- This line is where rotation applied
			VertBase.BTWeld.C0 = VertBase.BTWeld.C0 * CFrame.Angles(math.rad(deltaY/2),0,0) -- This line is where rotation applied
		end
		event:FireServer(HorBase,VertBase,HorBase.BTWeld.C0,VertBase.BTWeld.C0) -- Optional(you can comment this out)
		Camera.CFrame = AimPart.CFrame
		Indicator.Turret.Rotation = (Vehicle.Chassi.Structure.TurretBase.Orientation.Y-HorBase.Orientation.Y)
		--print((AimPart.Position-mouse.hit.p).Magnitude)
	elseif DriverView then
		Camera.CameraType = "Scriptable"
		Camera.CFrame = ViewPart.CFrame
	else
		Camera.CameraType = "Custom"
		Camera.CameraSubject = Player.Character.Humanoid
	end
end

-- [ Mobile Support Parts ] --

-- Set up touch event listeners
if Mobile then
	UserInputService.TouchStarted:Connect(function(touch)
		if not Aiming then return end
		currentTouch = touch
		previousTouchPosition = touch.Position
	end)

	UserInputService.TouchEnded:Connect(function(touch)
		if not Aiming then return end
		currentTouch = nil
		previousTouchPosition = nil
	end)

	UserInputService.TouchMoved:Connect(function(touch)
		if not Aiming then return end
		if currentTouch ~= touch then return end
		currentTouch = touch
	end)
end

-- allat Mobile 
local function MoveTurretMobile()
	if Aiming then
		Camera.CameraType = "Scriptable"
		Camera.FieldOfView = tankModule.ZoomLevelTable[ZoomLevel]
		currentAngle = VertBase.Orientation.X - VertRef.Orientation.X

		if currentTouch then
			local currentPosition = currentTouch.Position
			if previousTouchPosition then
				-- Calculate movement delta :P
				local delta = (currentPosition - previousTouchPosition) * currentsen
				local deltaX = math.clamp(delta.X, -SensLimit, SensLimit)
				local deltaY = math.clamp(delta.Y, -SensLimit, SensLimit)

				-- Limit vertical movement based on current angle yo turret
				if currentAngle > 0 and currentAngle - deltaY > 13 then
					deltaY = 0
				elseif currentAngle < 0 and currentAngle - deltaY < -5 then
					deltaY = 0
				end

				-- Move the turret
				HorBase.BTWeld.C0 = HorBase.BTWeld.C0 * CFrame.Angles(math.rad(-deltaX), 0, 0)
				VertBase.BTWeld.C0 = VertBase.BTWeld.C0 * CFrame.Angles(math.rad(deltaY), 0, 0)
			end
			previousTouchPosition = currentPosition
		else
			previousTouchPosition = nil
		end
		event:FireServer(HorBase, VertBase, HorBase.BTWeld.C0, VertBase.BTWeld.C0)
		Camera.CFrame = AimPart.CFrame
		Indicator.Turret.Rotation = (Vehicle.Chassi.Structure.TurretBase.Orientation.Y - HorBase.Orientation.Y)
	elseif DriverView then
		Camera.CameraType = "Scriptable"
		Camera.CFrame = ViewPart.CFrame
	else
		Camera.CameraType = "Custom"
		Camera.CameraSubject = Player.Character.Humanoid
	end
end

if Mobile then
	RunService.RenderStepped:Connect(MoveTurretMobile)
else
	RunService.RenderStepped:Connect(MoveTurretPC)
end

DriverSeat.Changed:connect(function(property)
	if property ~= 'Occupant' then return end
	local occupant = DriverSeat.Occupant
	if not occupant then
		ExitCamera()
		Horn.RemoteEvent:FireServer("Stop")
		script.Parent.GuiRemover.RemoveEvent:FireServer()
	end
end)

mouse.Button2Down:Connect(function()
	if Aiming == true then
		motorTurn = true
		UserInputService.MouseBehavior = Enum.MouseBehavior.Default
		UserInputService.MouseIconEnabled = true
	end
end)

mouse.Button2Up:Connect(function()
	if Aiming == true then
		motorTurn = false
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
		UserInputService.MouseIconEnabled = false
	end
end)

--Made by Ferrarico
-- Modded by Mawin_CK

im very thankful if you helped me out with this

1 Like