نظام الكاميرا في FiveM: كاميرات مخصصة، CCTV وسينمائية
اتقن برمجة كاميرا FiveM. كاميرا حرة، CCTV للشرطة، لقطات سينمائية وواجهات كاميرا مخصصة مع كود عملي وأفضل الموارد.
Agency Scripts
المؤسس والمطور الرئيسي في Agency Scripts
لماذا تهم أنظمة الكاميرا المخصصة
الكاميرا الافتراضية في GTA تعمل جيدًا للعب العام، لكن خوادم اللعب التمثيلي وأنماط اللعبة المخصصة غالبًا ما تحتاج إلى سلوك كاميرا متخصص. تتطلب شاشات إنشاء الشخصيات كاميرات دوران تسمح للاعبين بالتدوير حول شخصياتهم. تحتاج المشاهد السينمائية إلى مسارات كاميرا مبرمجة مع انتقالات سلسة. تحتاج أنظمة المراقبة CCTV إلى زوايا مراقبة ثابتة. تحتاج معاينات العقارات إلى كاميرات طيران. بناء نظام كاميرا مرن يتعامل مع كل هذه الاستخدامات سيحسن بشكل كبير جودة العرض في خادمك. يغطي هذا الدرس واجهة برمجة تطبيقات كاميرا FiveM بعمق مع أمثلة واقعية يمكنك استخدامها فورًا.
أساسيات الكاميرا
كاميرات FiveM تستخدم الأصلية CreateCam و CreateCamWithParams functions. الكاميرا هي كيان له موقع، دوران، ومجال رؤية. يمكنك إنشاء عدة كاميرات والتبديل بينها، أو التداخل بسلاسة من موقع كاميرا إلى آخر. المفهوم الأساسي هو أن العرض من خلال كاميرا سكريبت يعطل كاميرا اللعب العادية، لذا تحتاج إلى التعامل مع الانتقال بعناية للعودة.
-- 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)
كاميرا المدار لإنشاء الشخصية
كاميرا تدور حول نقطة مركزية، تتيح للاعب تدوير العرض بسحب الماوس. هذه هي الكاميرا القياسية لإنشاء الشخصيات، متاجر الملابس، وصالونات الحلاقة. تحافظ الكاميرا على مسافة ثابتة من الهدف وتحول حركة الماوس إلى دوران زاوي حول النقطة المركزية.
-- 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
التداخل والانتقالات في الكاميرا
الانتقالات السلسة للكاميرا بين موضعين تخلق تأثيرات سينمائية للمشاهد المقطوعة، جولات الممتلكات، وشاشات التحميل. FiveM يوفر SetCamActiveWithInterp الذي يتعامل مع الاستيفاء محليًا، لكن يمكنك أيضًا بناء دوال تيسير مخصصة لمزيد من التحكم في منحنى الانتقال.
-- 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
يستخدم نظام CCTV كاميرات ثابتة موضوعة حول الخريطة يمكن للاعبين التنقل بينها. كل كاميرا لها موقع ودوران محددان مسبقًا، ويطبق النظام تأثير معالجة لاحقة لمحاكاة مظهر لقطات الأمان. يُستخدم هذا عادة في مراكز الشرطة وداخل الأعمال.
-- 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
أفضل ممارسات نظام الكاميرا
- دمر دائمًا الكاميرات عند الانتهاء. الكاميرات غير المدمرة تسبب تسرب في الذاكرة. تتبع كل مقبض كاميرا وقم بتنظيفها في
onResourceStop. - تعطيل التحكمات أثناء تسلسلات الكاميرا. يجب ألا يتمكن اللاعبون من المشي، أو إطلاق النار، أو التفاعل أثناء تفعيل كاميرا سكربتية إلا إذا سُمح بذلك عمدًا.
- استخدم قيم FOV المناسبة. اللعب العادي يستخدم مجال رؤية 50-60. اللقطات السينمائية تستخدم مجال رؤية 30-40 لمظهر تليفوتوغرافي. اللقطات الافتتاحية الواسعة تستخدم مجال رؤية 70-90.
- إخفاء HUD أثناء تسلسلات الكاميرا. استخدم
DisplayHud(false)وDisplayRadar(false)لإزالة عناصر اللعب أثناء الكاميرات السينمائية. - اختبر الانتقالات عند معدلات إطارات مختلفة. يمكن أن يبدو التداخل في الكاميرا مختلفًا عند 30fps مقابل 144fps. اختبر على أجهزة منخفضة الأداء لضمان سلاسة السلوك.