Камера и время
ScriptCamera, TickLoop, AreaFollower и ServerClock - скриптовые камеры, покадровые задачи, которые работают только когда нужны, сообщение серверу об области игрока и часы сервера на клиенте.
Скриптовая камера для витрин и сцен, покадровая задача, которую вы включаете только когда она нужна, клиентская половина потоковой передачи областей сервера и часы сервера для обратных отсчётов. Периодическая работа в целом это every(ms, fn) (см. Lib: общее и fivem); никогда не setInterval и не голый setTick.
ScriptCamera
new ScriptCamera
Скриптовая камера: вид витрины магазина, редактор, сцена, бинокль. start создаёт и показывает её, остальные методы двигают и меняют её, stop плавно возвращает к игровой камере и уничтожает её. Камера, которая ещё работает, когда ваш ресурс останавливается, сразу возвращается, поэтому игрок никогда в ней не застрянет.
Сторона: Клиент · Ждёт: нет
new ScriptCamera(type?: string)Lua-аналога нет: ресурсы на Lua используют CreateCam, RenderScriptCams и DestroyCam.
Параметры
Prop
Type
CameraView
Куда смотрит камера, используется в start, move и glideTo:
Prop
Type
Члены
Prop
Type
Пример
import { ScriptCamera } from "@aicore-fivem/lib/client";
const camera = new ScriptCamera();
const CAR = { x: -44.4, y: -1097.1, z: 26.4 };
export function openShowroom(): void {
camera.start({ x: -40.2, y: -1100.5, z: 27.6 }, { pointAt: CAR, fov: 50 }, 800);
}
export function frontView(): void {
camera.glideTo({ x: -47.5, y: -1093.8, z: 27.2 }, { pointAt: CAR, fov: 45 }, 1200);
}
export function closeShowroom(): void {
camera.stop(800);
}camera.start
Создаёт камеру (если её ещё нет), ставит её в точку и делает её видом, плавно переходя от игровой камеры.
Сторона: Клиент · Ждёт: нет
camera.start(at: Vec3, view?: CameraView, easeMs?: number): voidПараметры
Prop
Type
Возвращает
void. См. пример класса.
camera.move
Двигает камеру сразу или плавно (интерполирует игра). Ничего не делает, пока камера остановлена.
Сторона: Клиент · Ждёт: нет
camera.move(at: Vec3, view?: CameraView, ms?: number): voidПараметры
Prop
Type
Возвращает
void.
Пример
camera.move({ x: -42.0, y: -1099.0, z: 28.5 }, { pointAt: CAR }, 600);camera.glideTo
Переходит к новому кадру: вторая камера в новом месте принимает управление у этой, а старая уничтожается после перехода. Тряска и размытие переносятся. Запускает камеру, если она остановлена (или если ms равно 0).
Сторона: Клиент · Ждёт: нет
camera.glideTo(at: Vec3, view: CameraView, ms: number): voidПараметры
Prop
Type
Возвращает
void. См. пример класса.
camera.attach
Заставляет камеру следовать за сущностью со смещением в собственном пространстве сущности.
Сторона: Клиент · Ждёт: нет
camera.attach(entity: number, offset: Vec3, relative?: boolean): voidПараметры
Prop
Type
Возвращает
void.
Пример
import { cache, ScriptCamera } from "@aicore-fivem/lib/client";
const chase = new ScriptCamera();
if (cache.vehicle !== false) {
chase.start({ x: 0, y: 0, z: 0 });
chase.attach(cache.vehicle, { x: 0, y: -6, z: 2 });
}camera.pointAt
Заставляет камеру продолжать смотреть на точку.
Сторона: Клиент · Ждёт: нет
camera.pointAt(target: Vec3): voidПараметры
Prop
Type
Возвращает
void.
camera.setFov
Меняет поле зрения (зум).
Сторона: Клиент · Ждёт: нет
camera.setFov(fov: number): voidПараметры
Prop
Type
Возвращает
void.
Пример
// Бинокль: зум колёсиком мыши.
let fov = 40;
export function zoom(step: number): void {
fov = Math.min(70, Math.max(5, fov + step));
camera.setFov(fov);
}camera.shake
Добавляет игровую тряску камеры. Амплитуда 0 останавливает её.
Сторона: Клиент · Ждёт: нет
camera.shake(type: string, amplitude: number): voidПараметры
Prop
Type
Возвращает
void.
Пример
camera.shake("HAND_SHAKE", 0.3);camera.blur
Глубина резкости: резко между двумя расстояниями, размыто за их пределами. Передайте null, чтобы убрать. Нужен покадровый натив, который работает, только пока задано размытие.
Сторона: Клиент · Ждёт: нет
camera.blur(dof: CameraBlur | null): voidПараметры
Prop
Type
Возвращает
void.
Пример
camera.blur({ near: 2, far: 8, strength: 0.8 });camera.stop
Возвращает вид игровой камере (с плавным переходом за easeMs) и уничтожает камеру. Тряска и размытие тоже заканчиваются. Ничего не делает, пока камера остановлена.
Сторона: Клиент · Ждёт: нет
camera.stop(easeMs?: number): voidПараметры
Prop
Type
Возвращает
void. См. пример класса.
TickLoop
new TickLoop
Покадровая задача (every(0, fn) в едином планировщике вашего ресурса), которая выключена, пока вы её не включите. Используйте для всего, что должно работать каждый кадр лишь иногда: рисование маркера поблизости, предпросмотр размещения, отключённая стрельба при прицеливании в друга.
Сторона: Клиент · Ждёт: нет
new TickLoop(onFrame: () => void, name?: string)Lua-аналога нет: aiLib.every(0, fn, { active = false, name = name }) и job:set(active).
Параметры
Prop
Type
Члены
Prop
Type
Пример
import { TickLoop, Zones } from "@aicore-fivem/lib/client";
const SPOT = { x: 215.8, y: -810.1, z: 30.7 };
const light = new TickLoop(() => {
DrawLightWithRange(SPOT.x, SPOT.y, SPOT.z + 2, 61, 247, 158, 8, 4);
}, "garageLight");
const zones = new Zones();
zones.add("garage", {
shape: { type: "sphere", center: SPOT, radius: 25 },
onEnter: () => light.set(true),
onExit: () => light.set(false),
});Каждая задача вашего ресурса выполняется из одного setTick, который существует, только пока включена какая-либо задача. Создайте цикл один раз в начале файла и переключайте его, а не создавайте новый при каждом запуске.
AreaFollower
new AreaFollower
Клиентская половина серверного AreaStream (Потоковая передача областей): сообщает серверу, в какой ячейке сетки стоит игрок, каждый раз, когда она меняется (проверка каждые 2 секунды, пока запущен), поэтому сервер отправляет только объекты мира (выброшенные предметы, растения, поставленные пропы) вокруг игрока. Создайте один в начале файла.
Сторона: Клиент · Ждёт: нет
new AreaFollower(event: string, cellSize?: number)Lua-аналога нет.
Параметры
Prop
Type
Члены
Prop
Type
Пример
import { AreaFollower, onServerEvent } from "@aicore-fivem/lib/client";
const follower = new AreaFollower("myplants:cell");
follower.start();
const plants = new Map<number, unknown>();
// Поток отправляет (reset, puts, removes): один снимок с reset true, затем только изменения.
onServerEvent(
"myplants:area",
{
args: ([reset, puts, removes]) =>
typeof reset === "boolean" && Array.isArray(puts) && Array.isArray(removes)
? ([reset, puts, removes] as const)
: null,
},
(reset, puts, removes) => {
if (reset) plants.clear();
for (const plant of puts as Array<{ id?: unknown } | null>)
if (typeof plant?.id === "number") plants.set(plant.id, plant);
for (const id of removes) if (typeof id === "number") plants.delete(id);
},
);Сервер сверяет сообщённую ячейку с реальной позицией игрока (followReported принимает только ячейки в пределах одной ячейки от педа), поэтому читер не может запросить далёкую область.
ServerClock
new ServerClock
Часы сервера на клиенте. Часы клиента и сервера могут различаться, поэтому обратный отсчёт до серверного времени (кулдаун ограбления, конец аукциона) нужно сначала перевести в локальное время. Вызывайте sync с серверным Date.now() каждый раз, когда приходит снимок или сообщение о старте.
Сторона: Клиент (из @aicore-fivem/lib/shared) · Ждёт: нет
new ServerClock()Lua-аналога нет.
Члены
Prop
Type
Пример
import { every, onServerEvent } from "@aicore-fivem/lib/client";
import { ServerClock } from "@aicore-fivem/lib/shared";
const clock = new ServerClock();
let endsAt = 0; // локальное время
onServerEvent(
"myauction:state",
{ args: ([now, ends]) => (typeof now === "number" && typeof ends === "number" ? [now, ends] : null) },
(now, ends) => {
clock.sync(now);
endsAt = clock.toLocal(ends);
},
);
every(1000, () => {
const left = Math.max(0, Math.round((endsAt - Date.now()) / 1000));
SendNUIMessage({ action: "countdown", data: left });
});Сервер отправляет время окончания один раз; клиент ведёт обратный отсчёт по своим часам. Никогда не отправляйте оставшееся время каждую секунду.
Замеры и загрузка
raycast, groundZ, locationName, vehicleLabel, waypoint, pedHeadshot, onShot и загрузчики request - вопросы к игре о мире и загрузка игровых ресурсов.
Членство и данные игрока
hasJob, inGang, onDutyIn, allows, onMembershipChange, getPlayerData, onPlayerData и seedPlayerData - персонаж, деньги, работы, дежурство и банды локального игрока в том виде, в каком их присылает ai-core.