Zurück zum Blog
Tutorial5 Min. Lesezeit

FiveM-Kamerasystem: Eigene Kameras, Überwachung und Filmszenen

Lerne FiveM-Kameraskripte: Freecam, Überwachung für die Polizei, Filmszenen und eigene Kameraoberflächen mit Code und passenden Ressourcen.

Agency Scripts

Gründer und leitender Entwickler bei Agency Scripts

Warum individuelle Kamerasysteme wichtig sind

Die Standardkamera von GTA V eignet sich gut für das allgemeine Gameplay, aber Roleplay-Server und benutzerdefinierte Spielmodi benötigen oft spezielle Kamerafunktionen. Bei der Charaktererstellung sind Orbitalkameras erforderlich, mit denen Spieler ihren Charakter drehen können. Zwischensequenzen benötigen geskriptete Kamerafahrten mit flüssigen Übergängen. CCTV-Systeme erfordern fest installierte Überwachungskameras. Objektvorschauen benötigen Kamerafahrten. Ein flexibles Kamerasystem, das all diese Anwendungsfälle abdeckt, wird die Präsentationsqualität deines Servers deutlich verbessern. Dieses Tutorial behandelt die FiveM-Kamera-API ausführlich und enthält praxisnahe Beispiele, die du sofort verwenden kannst.

Kamera-Grundlagen

FiveM-Kameras verwenden die nativen CreateCam und CreateCamWithParams Funktionen. Eine Kamera ist eine Entity mit Position, Rotation und Sichtfeld. Du kannst mehrere Kameras erstellen und zwischen ihnen wechseln oder fließend von einer Kameraposition zur nächsten überblenden. Wichtig ist, dass die normale Gameplay-Kamera deaktiviert wird, wenn über eine Skriptkamera gerendert wird. Daher musst du den Wechsel zurück sorgfältig handhaben.

-- client/camera_core.lua
local CameraSystem = {
    activeCam = nil,
    isActive = false,
}

function CameraSystem.Create(coords, rot, fov)
    local cam = CreateCamWithParams(
        'DEFAULT_SCRIPTED_CAMERA',
        coords.x, coords.y, coords.z,
        rot.x, rot.y, rot.z,
        fov or 60.0,
        false, 0
    )
    return cam
end

function CameraSystem.Activate(cam, transitionTime)
    transitionTime = transitionTime or 1000

    SetCamActive(cam, true)
    RenderScriptCams(true, true, transitionTime, true, false)

    CameraSystem.activeCam = cam
    CameraSystem.isActive = true
end

function CameraSystem.Deactivate(transitionTime)
    transitionTime = transitionTime or 1000

    RenderScriptCams(false, true, transitionTime, true, false)

    if CameraSystem.activeCam then
        SetCamActive(CameraSystem.activeCam, false)
        DestroyCam(CameraSystem.activeCam, false)
        CameraSystem.activeCam = nil
    end

    CameraSystem.isActive = false
end

-- Clean up on resource stop
AddEventHandler('onResourceStop', function(resourceName)
    if GetCurrentResourceName() ~= resourceName then return end
    if CameraSystem.isActive then
        CameraSystem.Deactivate(0)
    end
end)

Orbitalkamera für die Charaktererstellung

Eine Orbitalkamera kreist um einen Mittelpunkt und ermöglicht es dem Spieler, die Ansicht durch Ziehen mit der Maus zu drehen. Sie ist die Standardkamera für die Charaktererstellung, Kleidungsgeschäfte und Friseursalons. Die Kamera hält einen festen Abstand zum Ziel und wandelt Mausbewegungen in eine Drehung um den Mittelpunkt um.

-- client/orbit_camera.lua
local OrbitCam = {
    active = false,
    cam = nil,
    target = nil,
    distance = 2.0,
    angleH = 0.0,
    angleV = 20.0,
    minV = -30.0,
    maxV = 60.0,
    sensitivity = 0.3,
    fov = 45.0,
}

function OrbitCam.Start(targetEntity, distance, height)
    OrbitCam.target = targetEntity
    OrbitCam.distance = distance or 2.0
    OrbitCam.angleH = GetEntityHeading(targetEntity) + 180.0
    OrbitCam.angleV = 20.0

    local targetCoords = GetEntityCoords(targetEntity)
    local camPos = OrbitCam.CalculatePosition(targetCoords)

    OrbitCam.cam = CreateCamWithParams(
        'DEFAULT_SCRIPTED_CAMERA',
        camPos.x, camPos.y, camPos.z,
        0.0, 0.0, 0.0,
        OrbitCam.fov, false, 0
    )

    PointCamAtCoord(OrbitCam.cam, targetCoords.x, targetCoords.y, targetCoords.z + 0.5)
    SetCamActive(OrbitCam.cam, true)
    RenderScriptCams(true, true, 800, true, false)

    OrbitCam.active = true
    OrbitCam.UpdateLoop()
end

function OrbitCam.CalculatePosition(center)
    local hRad = math.rad(OrbitCam.angleH)
    local vRad = math.rad(OrbitCam.angleV)

    local x = center.x + OrbitCam.distance * math.cos(vRad) * math.sin(hRad)
    local y = center.y + OrbitCam.distance * math.cos(vRad) * math.cos(hRad)
    local z = center.z + 0.5 + OrbitCam.distance * math.sin(vRad)

    return vector3(x, y, z)
end

function OrbitCam.UpdateLoop()
    CreateThread(function()
        while OrbitCam.active do
            DisableAllControlActions(0)
            EnableControlAction(0, 1, true)   -- Mouse X
            EnableControlAction(0, 2, true)   -- Mouse Y
            EnableControlAction(0, 241, true)  -- Scroll Up
            EnableControlAction(0, 242, true)  -- Scroll Down

            -- Mouse rotation
            local mouseX = GetDisabledControlNormal(0, 1) * OrbitCam.sensitivity * 8.0
            local mouseY = GetDisabledControlNormal(0, 2) * OrbitCam.sensitivity * 8.0

            OrbitCam.angleH = OrbitCam.angleH - mouseX
            OrbitCam.angleV = math.max(OrbitCam.minV,
                math.min(OrbitCam.maxV, OrbitCam.angleV + mouseY))

            -- Scroll zoom
            if IsDisabledControlPressed(0, 241) then
                OrbitCam.distance = math.max(0.5, OrbitCam.distance - 0.1)
            elseif IsDisabledControlPressed(0, 242) then
                OrbitCam.distance = math.min(5.0, OrbitCam.distance + 0.1)
            end

            local targetCoords = GetEntityCoords(OrbitCam.target)
            local camPos = OrbitCam.CalculatePosition(targetCoords)

            SetCamCoord(OrbitCam.cam, camPos.x, camPos.y, camPos.z)
            PointCamAtCoord(OrbitCam.cam, targetCoords.x, targetCoords.y, targetCoords.z + 0.5)

            Wait(0)
        end
    end)
end

function OrbitCam.Stop()
    OrbitCam.active = false
    RenderScriptCams(false, true, 800, true, false)

    if OrbitCam.cam then
        SetCamActive(OrbitCam.cam, false)
        DestroyCam(OrbitCam.cam, false)
        OrbitCam.cam = nil
    end
end

Kamerainterpolation und Übergänge

Fließende Kamerabewegungen zwischen zwei Positionen sorgen für filmische Effekte in Zwischensequenzen, Objekttouren und Ladebildschirmen. FiveM stellt SetCamActiveWithInterp bereit, das die Interpolation nativ ausführt. Du kannst aber auch benutzerdefinierte Easing-Funktionen erstellen, um den Verlauf des Übergangs genauer zu steuern.

-- client/camera_transition.lua
local function TransitionCamera(fromPos, fromRot, toPos, toRot, duration, fov)
    fov = fov or 50.0

    local camFrom = CreateCamWithParams('DEFAULT_SCRIPTED_CAMERA',
        fromPos.x, fromPos.y, fromPos.z,
        fromRot.x, fromRot.y, fromRot.z,
        fov, false, 0)

    local camTo = CreateCamWithParams('DEFAULT_SCRIPTED_CAMERA',
        toPos.x, toPos.y, toPos.z,
        toRot.x, toRot.y, toRot.z,
        fov, false, 0)

    SetCamActive(camFrom, true)
    RenderScriptCams(true, false, 0, true, false)

    -- Interpolate from first camera to second
    SetCamActiveWithInterp(camTo, camFrom, duration, 1, 1)

    Wait(duration)

    -- Clean up the first camera
    DestroyCam(camFrom, false)

    return camTo
end

-- Usage: Flythrough preview of a property
local function PropertyPreview(waypoints, duration)
    local perSegment = duration / (#waypoints - 1)
    local currentCam = nil

    for i = 1, #waypoints - 1 do
        local wp = waypoints[i]
        local nextWp = waypoints[i + 1]

        currentCam = TransitionCamera(
            wp.pos, wp.rot,
            nextWp.pos, nextWp.rot,
            perSegment, wp.fov or 60.0
        )
    end

    -- Return to gameplay camera
    Wait(500)
    RenderScriptCams(false, true, 1000, true, false)
    if currentCam then DestroyCam(currentCam, false) end
end

CCTV-Überwachungskamerasystem

Ein CCTV-System verwendet fest installierte Kameras an verschiedenen Orten auf der Karte, zwischen denen Spieler wechseln können. Jede Kamera hat eine vordefinierte Position und Rotation. Das System wendet einen Post-Processing-Effekt an, der Aufnahmen von Überwachungskameras simuliert. Diese Funktion wird häufig in Polizeistationen und Geschäftsräumen eingesetzt.

-- client/cctv.lua
local CCTV = {
    cameras = {},
    currentIndex = 0,
    activeCam = nil,
    active = false,
}

function CCTV.AddCamera(name, coords, rot, fov)
    table.insert(CCTV.cameras, {
        name = name,
        coords = coords,
        rot = rot,
        fov = fov or 60.0,
    })
end

function CCTV.Start(startIndex)
    CCTV.currentIndex = startIndex or 1
    CCTV.active = true
    CCTV.SwitchTo(CCTV.currentIndex)

    CreateThread(function()
        while CCTV.active do
            -- Apply security camera filter
            SetTimecycleModifier('CAMERA_secuirity')
            SetTimecycleModifierStrength(1.0)

            DisableAllControlActions(0)
            EnableControlAction(0, 174, true) -- Left Arrow
            EnableControlAction(0, 175, true) -- Right Arrow
            EnableControlAction(0, 202, true) -- Escape

            -- Cycle cameras
            if IsDisabledControlJustPressed(0, 175) then
                local next = CCTV.currentIndex + 1
                if next > #CCTV.cameras then next = 1 end
                CCTV.SwitchTo(next)
            elseif IsDisabledControlJustPressed(0, 174) then
                local prev = CCTV.currentIndex - 1
                if prev < 1 then prev = #CCTV.cameras end
                CCTV.SwitchTo(prev)
            elseif IsDisabledControlJustPressed(0, 202) then
                CCTV.Stop()
            end

            Wait(0)
        end
    end)
end

function CCTV.SwitchTo(index)
    local data = CCTV.cameras[index]
    if not data then return end

    local newCam = CreateCamWithParams('DEFAULT_SCRIPTED_CAMERA',
        data.coords.x, data.coords.y, data.coords.z,
        data.rot.x, data.rot.y, data.rot.z,
        data.fov, false, 0)

    if CCTV.activeCam then
        SetCamActiveWithInterp(newCam, CCTV.activeCam, 500, 1, 1)
        Wait(500)
        DestroyCam(CCTV.activeCam, false)
    else
        SetCamActive(newCam, true)
        RenderScriptCams(true, true, 500, true, false)
    end

    CCTV.activeCam = newCam
    CCTV.currentIndex = index
end

function CCTV.Stop()
    CCTV.active = false
    ClearTimecycleModifier()
    RenderScriptCams(false, true, 800, true, false)

    if CCTV.activeCam then
        DestroyCam(CCTV.activeCam, false)
        CCTV.activeCam = nil
    end
end

Bewährte Methoden für Kamerasysteme

  • Zerstöre Kameras immer, wenn du sie nicht mehr benötigst. Nicht zerstörte Kameras führen zu Speicherlecks. Verfolge jedes Kamera-Handle und bereinige es in onResourceStop.
  • Deaktiviere Steuerungseingaben während Kamerasequenzen. Spieler sollten sich nicht bewegen, schießen oder interagieren können, solange eine Skriptkamera aktiv ist, sofern dies nicht ausdrücklich erlaubt ist.
  • Verwende passende FOV-Werte. Beim normalen Gameplay liegt das Sichtfeld bei 50-60 FOV. Für einen Teleobjektiv-Look werden in filmischen Einstellungen 30-40 FOV verwendet. Weitwinkelaufnahmen zur Einführung nutzen 70-90 FOV.
  • Blende während Kamerasequenzen das HUD aus. Verwende DisplayHud(false) und DisplayRadar(false) , um Gameplay-Elemente während filmischer Kamerafahrten auszublenden.
  • Teste Übergänge bei unterschiedlichen Bildraten. Kamerainterpolation kann bei 30fps anders aussehen als bei 144fps. Teste auf leistungsschwacher Hardware, um ein flüssiges Verhalten sicherzustellen.

Bereit loszulegen?

Hol dir die Scripts in unserem Shop, oder komm auf den Discord für Support, Updates und einen Blick auf das, was kommt.