AI Core
Dokumentacja APILib: klient

Kamera i czas

ScriptCamera, TickLoop, AreaFollower i ServerClock - kamery skryptowe, zadania co klatkę działające tylko wtedy, gdy są potrzebne, zgłaszanie obszaru gracza do serwera i zegar serwera na kliencie.

Kamera skryptowa dla salonów i scen, zadanie co klatkę, które włączasz tylko wtedy, gdy jest potrzebne, kliencka połowa strumieniowania obszarów serwera i zegar serwera do odliczań. Praca okresowa w ogóle to every(ms, fn) (zobacz Lib: shared i fivem); nigdy setInterval ani gołe setTick.

ScriptCamera

new ScriptCamera

Kamera skryptowa: widok salonu sklepu, edytor, scena, lornetka. start ją tworzy i pokazuje, pozostałe metody ją przesuwają i zmieniają, stop płynnie wraca do kamery gry i ją niszczy. Kamera, która nadal działa, gdy twój zasób się zatrzymuje, jest od razu oddawana, więc gracz nigdy w niej nie utknie.

Strona: Klient · Czeka: nie

Sygnatura
new ScriptCamera(type?: string)

Brak odpowiednika w Lua: zasoby Lua używają CreateCam, RenderScriptCams i DestroyCam.

Parametry

Prop

Type

CameraView

Gdzie patrzy kamera, używane przez start, move i glideTo:

Prop

Type

Składowe

Prop

Type

Przykład

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

Tworzy kamerę (gdy jeszcze nie istnieje), stawia ją w punkcie i robi z niej widok, płynnie przechodząc z kamery gry.

Strona: Klient · Czeka: nie

Sygnatura
camera.start(at: Vec3, view?: CameraView, easeMs?: number): void

Parametry

Prop

Type

Zwracana wartość

void. Zobacz przykład klasy.

camera.move

Przesuwa kamerę, od razu albo płynnie (interpoluje gra). Nic nie robi, gdy jest zatrzymana.

Strona: Klient · Czeka: nie

Sygnatura
camera.move(at: Vec3, view?: CameraView, ms?: number): void

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

camera.move({ x: -42.0, y: -1099.0, z: 28.5 }, { pointAt: CAR }, 600);

camera.glideTo

Przechodzi do nowego ujęcia: druga kamera w nowym miejscu przejmuje od tej, a stara jest niszczona po przejściu. Drgania i rozmycie przechodzą dalej. Uruchamia kamerę, gdy jest zatrzymana (albo gdy ms wynosi 0).

Strona: Klient · Czeka: nie

Sygnatura
camera.glideTo(at: Vec3, view: CameraView, ms: number): void

Parametry

Prop

Type

Zwracana wartość

void. Zobacz przykład klasy.

camera.attach

Sprawia, że kamera podąża za encją z przesunięciem we własnym układzie encji.

Strona: Klient · Czeka: nie

Sygnatura
camera.attach(entity: number, offset: Vec3, relative?: boolean): void

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

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

Sprawia, że kamera stale patrzy na punkt.

Strona: Klient · Czeka: nie

Sygnatura
camera.pointAt(target: Vec3): void

Parametry

Prop

Type

Zwracana wartość

void.

camera.setFov

Zmienia pole widzenia (przybliżenie).

Strona: Klient · Czeka: nie

Sygnatura
camera.setFov(fov: number): void

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

// Lornetka: przybliżanie kółkiem myszy.
let fov = 40;
export function zoom(step: number): void {
  fov = Math.min(70, Math.max(5, fov + step));
  camera.setFov(fov);
}

camera.shake

Dodaje drgania kamery z gry. Amplituda 0 je zatrzymuje.

Strona: Klient · Czeka: nie

Sygnatura
camera.shake(type: string, amplitude: number): void

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

camera.shake("HAND_SHAKE", 0.3);

camera.blur

Głębia ostrości: ostro między dwiema odległościami, rozmyte poza nimi. Podaj null, żeby jej nie było. Wymaga natywki co klatkę, która działa tylko wtedy, gdy ustawione jest rozmycie.

Strona: Klient · Czeka: nie

Sygnatura
camera.blur(dof: CameraBlur | null): void

Parametry

Prop

Type

Zwracana wartość

void.

Przykład

camera.blur({ near: 2, far: 8, strength: 0.8 });

camera.stop

Oddaje widok kamerze gry (płynnie przez easeMs) i niszczy kamerę. Drgania i rozmycie też się kończą. Nic nie robi, gdy jest zatrzymana.

Strona: Klient · Czeka: nie

Sygnatura
camera.stop(easeMs?: number): void

Parametry

Prop

Type

Zwracana wartość

void. Zobacz przykład klasy.

TickLoop

new TickLoop

Zadanie co klatkę (every(0, fn) w jedynym harmonogramie twojego zasobu), które jest wyłączone, dopóki go nie włączysz. Używaj go do wszystkiego, co musi działać co klatkę tylko czasami: rysowanie markera w pobliżu, podgląd stawiania, blokada strzelania przy celowaniu w przyjaciela.

Strona: Klient · Czeka: nie

Sygnatura
new TickLoop(onFrame: () => void, name?: string)

Brak odpowiednika w Lua: aiLib.every(0, fn, { active = false, name = name }) i job:set(active).

Parametry

Prop

Type

Składowe

Prop

Type

Przykład

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),
});

Każde zadanie twojego zasobu działa z jednego setTick, który istnieje tylko wtedy, gdy jakieś zadanie jest włączone. Utwórz pętlę raz na górze pliku i ją przełączaj, zamiast tworzyć nową przy każdym starcie.

AreaFollower

new AreaFollower

Kliencka połowa serwerowego AreaStream (Strumieniowanie obszarów): mówi serwerowi, w której komórce siatki stoi gracz, za każdym razem, gdy się zmieni (sprawdzane co 2 sekundy po uruchomieniu), więc serwer wysyła tylko obiekty świata (zrzuty, rośliny, postawione propy) wokół gracza. Utwórz jeden na górze pliku.

Strona: Klient · Czeka: nie

Sygnatura
new AreaFollower(event: string, cellSize?: number)

Brak odpowiednika w Lua.

Parametry

Prop

Type

Składowe

Prop

Type

Przykład

resource/client/index.ts
import { AreaFollower, onServerEvent } from "@aicore-fivem/lib/client";

const follower = new AreaFollower("myplants:cell");
follower.start();

const plants = new Map<number, unknown>();

// Strumień wysyła (reset, puts, removes): jedna migawka z reset true, potem tylko zmiany.
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);
  },
);

Serwer sprawdza zgłoszoną komórkę względem prawdziwej pozycji gracza (followReported przyjmuje tylko komórki w odległości jednej komórki od peda), więc oszust nie może poprosić o odległy obszar.

ServerClock

new ServerClock

Zegar serwera na kliencie. Zegary klienta i serwera mogą się różnić, więc odliczanie do czasu serwera (cooldown napadu, koniec aukcji) trzeba najpierw zamienić na czas lokalny. Wywołuj sync z Date.now() serwera, gdy tylko przyjdzie migawka albo wiadomość startowa.

Strona: Klient (z @aicore-fivem/lib/shared) · Czeka: nie

Sygnatura
new ServerClock()

Brak odpowiednika w Lua.

Składowe

Prop

Type

Przykład

resource/client/index.ts
import { every, onServerEvent } from "@aicore-fivem/lib/client";
import { ServerClock } from "@aicore-fivem/lib/shared";

const clock = new ServerClock();
let endsAt = 0; // czas lokalny

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 });
});

Serwer wysyła czas końca raz; klient odlicza na własnym zegarze. Nigdy nie wysyłaj pozostałego czasu co sekundę.

Na tej stronie