Jump to content

[BUG] FOV command in camera script doesn't work


Recommended Posts

Hi! I've been messing around with a custom vehicle chase camera script I made for my own private server. It has a toggleable chase camera and can detect whether you're in a vehicle or not and disable/enable itself accordingly, and commands to adjust the behavior of the camera, like camera distance from the vehicle, camera height and camera FOV management, which currently is giving me the most gripe. For whatever reason i just can't manage to make the FOV aspect of the script work, as it for some reason only affects me when I exit the car, but when i enter it, the FOV resets even without enabling the custom camera. If you guys could take a look at my code it'd be greatly appreciated. I'd also like to mention that this code was created with the help of claudeAI, and I'm not an expert at scripting whatsoever, I just wanted to make something for my own use, and not trying to "sell it off" as my actual own hard work and effort. It just makes it easier when testing some race maps I created without having to use my mouse for moving the vanilla camera around.

-- client.lua - Fixed Camera with Working FOV

local chaseCamEnabled = false
local camX, camY, camZ
local camRotation = 0
local originalFOV = nil

-- Camera settings
local baseDist = 7.5
local height = 3
local smooth = 0.3
local turnSmooth = 0.05
local fov = 70

-- FOV update timer to ensure it stays applied
local fovTimer = nil

-- Function to force FOV application
function applyFOV()
    if chaseCamEnabled then
        setCameraFieldOfView("player", fov)
    end
end

-- Update camera every frame
function updateChaseCam()
    if not chaseCamEnabled then return end
    local veh = getPedOccupiedVehicle(localPlayer)
    if not veh then
        disableChaseCam()
        return
    end

    local x, y, z = getElementPosition(veh)
    local _, _, rz = getElementRotation(veh)

    -- Smoothly interpolate camera rotation instead of instant following
    local targetRotation = rz
    
    -- Handle rotation wrap-around (e.g., from 350° to 10°)
    local rotDiff = targetRotation - camRotation
    if rotDiff > 180 then
        rotDiff = rotDiff - 360
    elseif rotDiff < -180 then
        rotDiff = rotDiff + 360
    end
    
    -- Smooth the rotation
    camRotation = camRotation + rotDiff * turnSmooth
    
    -- Keep rotation in 0-360 range
    if camRotation >= 360 then
        camRotation = camRotation - 360
    elseif camRotation < 0 then
        camRotation = camRotation + 360
    end

    -- Use the smoothed rotation for camera position
    local rad = math.rad(camRotation)
    local targetX = x + math.sin(rad) * baseDist
    local targetY = y - math.cos(rad) * baseDist
    local targetZ = z + height

    -- Initialize camera position and rotation first time
    if not camX then
        camX, camY, camZ = targetX, targetY, targetZ
        camRotation = rz
    end

    -- Smooth position interpolation
    camX = camX + (targetX - camX) * smooth
    camY = camY + (targetY - camY) * smooth
    camZ = camZ + (targetZ - camZ) * smooth

    -- Apply camera matrix
    setCameraMatrix(camX, camY, camZ, x, y, z + 0.3)
end

-- Enable chase cam
function enableChaseCam()
    if chaseCamEnabled then return end
    local veh = getPedOccupiedVehicle(localPlayer)
    if not veh then 
        outputChatBox("You must be in a vehicle!", 255, 0, 0)
        return 
    end
    
    chaseCamEnabled = true
    camX, camY, camZ = nil, nil, nil
    
    -- Store original FOV
    originalFOV = getCameraFieldOfView("player")
    outputChatBox("Original FOV: " .. originalFOV, 255, 255, 0)
    
    -- Start the camera update loop
    addEventHandler("onClientRender", root, updateChaseCam)
    
    -- Create a timer to constantly apply FOV (this is the key fix)
    if fovTimer then
        killTimer(fovTimer)
    end
    fovTimer = setTimer(applyFOV, 50, 0) -- Apply FOV every 50ms
    
    -- Apply initial FOV
    setTimer(function()
        setCameraFieldOfView("player", fov)
        outputChatBox("Chase camera enabled (FOV: " .. fov .. ")", 0, 255, 0)
    end, 100, 1) -- Slight delay to ensure camera matrix is set first
end

-- Disable chase cam
function disableChaseCam()
    if not chaseCamEnabled then return end
    chaseCamEnabled = false
    camX, camY, camZ = nil, nil, nil
    camRotation = 0
    
    -- Stop the update loop
    removeEventHandler("onClientRender", root, updateChaseCam)
    
    -- Stop FOV timer
    if fovTimer then
        killTimer(fovTimer)
        fovTimer = nil
    end
    
    -- Restore normal camera
    setCameraTarget(localPlayer)
    
    -- Restore original FOV with a small delay
    if originalFOV then
        setTimer(function()
            setCameraFieldOfView("player", originalFOV)
            outputChatBox("FOV restored to: " .. originalFOV, 255, 255, 0)
        end, 100, 1)
    end
    
    outputChatBox("Chase camera disabled", 255, 0, 0)
end

-- Toggle chase cam with C
bindKey("c", "down", function()
    outputChatBox("C key pressed", 255, 255, 255)
    if chaseCamEnabled then
        disableChaseCam()
    else
        if isPedInVehicle(localPlayer) then
            enableChaseCam()
        else
            outputChatBox("You must be in a vehicle to use chase camera!", 255, 0, 0)
        end
    end
end)

-- Auto disable on exit vehicle
addEventHandler("onClientPlayerVehicleExit", localPlayer, function()
    if chaseCamEnabled then
        disableChaseCam()
    end
end)

-- FOV command with better handling
addCommandHandler("setfov", function(cmd, newFOV)
    local fovValue = tonumber(newFOV)
    if not fovValue then
        outputChatBox("Usage: /setfov [30-180]", 255, 255, 0)
        outputChatBox("Current FOV variable: " .. fov, 255, 255, 0)
        local currentFOV = getCameraFieldOfView("player")
        outputChatBox("Current camera FOV: " .. currentFOV, 255, 255, 0)
        return
    end
    
    if fovValue < 30 or fovValue > 180 then
        outputChatBox("FOV must be between 30 and 180", 255, 0, 0)
        return
    end
    
    fov = fovValue
    outputChatBox("FOV variable set to: " .. fov, 0, 255, 0)
    
    -- If chase cam is active, apply the new FOV
    if chaseCamEnabled then
        -- Apply multiple times to ensure it sticks
        setCameraFieldOfView("player", fov)
        setTimer(function()
            setCameraFieldOfView("player", fov)
            local actualFOV = getCameraFieldOfView("player")
            outputChatBox("New FOV applied: " .. actualFOV, 255, 255, 0)
        end, 100, 1)
    end
end)

-- Camera distance command
addCommandHandler("camdist", function(cmd, newDist)
    local distValue = tonumber(newDist)
    if not distValue then
        outputChatBox("Usage: /camdist [1-50]", 255, 255, 0)
        outputChatBox("Current distance: " .. baseDist, 255, 255, 0)
        return
    end
    
    if distValue < 1 or distValue > 50 then
        outputChatBox("Distance must be between 1 and 50", 255, 0, 0)
        return
    end
    
    baseDist = distValue
    outputChatBox("Camera distance set to: " .. baseDist, 0, 255, 0)
end)

-- Camera height command
addCommandHandler("camheight", function(cmd, newHeight)
    local heightValue = tonumber(newHeight)
    if not heightValue then
        outputChatBox("Usage: /camheight [-10 to 20]", 255, 255, 0)
        outputChatBox("Current height: " .. height, 255, 255, 0)
        return
    end
    
    if heightValue < -10 or heightValue > 20 then
        outputChatBox("Height must be between -10 and 20", 255, 0, 0)
        return
    end
    
    height = heightValue
    outputChatBox("Camera height set to: " .. height, 0, 255, 0)
end)

-- Test commands
addCommandHandler("testcam", function()
    outputChatBox("=== Camera Debug Info ===", 255, 255, 0)
    outputChatBox("Chase cam enabled: " .. tostring(chaseCamEnabled), 255, 255, 0)
    outputChatBox("In vehicle: " .. tostring(isPedInVehicle(localPlayer)), 255, 255, 0)
    outputChatBox("FOV variable: " .. fov, 255, 255, 0)
    outputChatBox("Camera FOV: " .. getCameraFieldOfView("player"), 255, 255, 0)
    outputChatBox("Distance: " .. baseDist .. ", Height: " .. height, 255, 255, 0)
    
    if camX then
        outputChatBox("Camera pos: " .. string.format("%.2f, %.2f, %.2f", camX, camY, camZ), 255, 255, 0)
    end
    
    if fovTimer then
        outputChatBox("FOV timer active: YES", 0, 255, 0)
    else
        outputChatBox("FOV timer active: NO", 255, 0, 0)
    end
end)

-- Camera smoothness commands
addCommandHandler("camsmooth", function(cmd, type, value)
    local val = tonumber(value)
    if not type or not val then
        outputChatBox("Usage: /camsmooth [pos/turn] [value]", 255, 255, 0)
        outputChatBox("pos = position smoothing (0.1-1.0)", 255, 255, 0)
        outputChatBox("turn = turn smoothing (0.01-0.5)", 255, 255, 0)
        outputChatBox("Current: pos=" .. smooth .. ", turn=" .. turnSmooth, 255, 255, 0)
        return
    end
    
    if type == "pos" or type == "position" then
        smooth = math.max(0.1, math.min(1.0, val))
        outputChatBox("Position smoothing set to: " .. smooth, 0, 255, 0)
    elseif type == "turn" or type == "rotation" then
        turnSmooth = math.max(0.01, math.min(0.5, val))
        outputChatBox("Turn smoothing set to: " .. turnSmooth, 0, 255, 0)
        outputChatBox("(Lower = smoother turns, Higher = more responsive)", 255, 255, 0)
    else
        outputChatBox("Unknown type. Use 'pos' or 'turn'", 255, 0, 0)
    end
end)

-- Preset smoothing commands
addCommandHandler("smoothcinematic", function()
    smooth = 0.1
    turnSmooth = 0.03
    outputChatBox("Cinematic smoothing applied", 0, 255, 0)
    outputChatBox("Position: " .. smooth .. ", Turn: " .. turnSmooth, 255, 255, 0)
end)

addCommandHandler("smoothnormal", function()
    smooth = 0.3
    turnSmooth = 0.08
    outputChatBox("Normal smoothing applied", 0, 255, 0)
    outputChatBox("Position: " .. smooth .. ", Turn: " .. turnSmooth, 255, 255, 0)
end)

addCommandHandler("smoothresponsive", function()
    smooth = 1.0
    turnSmooth = 0.2
    outputChatBox("Responsive smoothing applied", 0, 255, 0)
    outputChatBox("Position: " .. smooth .. ", Turn: " .. turnSmooth, 255, 255, 0)
end)

-- FOV presets
addCommandHandler("fovwide", function()
    fov = 90
    if chaseCamEnabled then
        setCameraFieldOfView("player", fov)
        setTimer(function() setCameraFieldOfView("player", fov) end, 100, 1)
    end
    outputChatBox("Wide FOV set: " .. fov, 0, 255, 0)
end)

addCommandHandler("fovnormal", function()
    fov = 70
    if chaseCamEnabled then
        setCameraFieldOfView("player", fov)
        setTimer(function() setCameraFieldOfView("player", fov) end, 100, 1)
    end
    outputChatBox("Normal FOV set: " .. fov, 0, 255, 0)
end)

addCommandHandler("fovnarrow", function()
    fov = 50
    if chaseCamEnabled then
        setCameraFieldOfView("player", fov)
        setTimer(function() setCameraFieldOfView("player", fov) end, 100, 1)
    end
    outputChatBox("Narrow FOV set: " .. fov, 0, 255, 0)
end)

-- Resource start notification
addEventHandler("onClientResourceStart", resourceRoot, function()
    outputChatBox("=== Enhanced Smooth Chase Camera ===", 0, 255, 0)
    outputChatBox("Controls: C = Toggle camera", 255, 255, 0)
    outputChatBox("Camera: /camdist [1-50], /camheight [-10 to 20]", 255, 255, 0)
    outputChatBox("FOV: /setfov [30-180], /fovwide, /fovnormal, /fovnarrow", 255, 255, 0)
    outputChatBox("Smoothing: /camsmooth [pos/turn] [value]", 255, 255, 0)
    outputChatBox("Presets: /smoothcinematic, /smoothnormal, /smoothresponsive", 255, 255, 0)
    outputChatBox("Debug: /testcam", 255, 255, 0)
    outputDebugString("[Enhanced Chase Camera] Resource loaded")
end)

-- Resource stop notification
addEventHandler("onClientResourceStop", resourceRoot, function()
    if chaseCamEnabled then
        disableChaseCam()
    end
    outputChatBox("Enhanced Chase Camera unloaded", 255, 100, 100)
end)

 

Link to comment

The core issue is that MTA automatically resets the FOV when entering a vehicle. In your code, the custom FOV is only applied when the chase camera is enabled, so it gets reset upon vehicle entry. To fix this, apply the FOV continuously or bind it to the

onClientPlayerVehicleEnter

event to ensure it stays consistent while driving

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...