Jump to content
Fivem-DEV.cz

teb

Members
  • Content Count

    10
  • Joined

  • Last visited

  • Days Won

    7

teb last won the day on January 6

teb had the most liked content!

Community Reputation

17 Farmář reputace

1 Follower

About teb

  • Rank
    Nováček

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Ty 2 klienti, to je dost osemetny - fivem to oficialne nepodporuje a alespon jednou mesicne dostanu 14 denni ban - Musis mit vypnuty steam, jinak to nebude fungovat - server musi podporovat variabilni identifikatory - na produkci mame Steam, ale muzu si to lokalne nastavit, aby to bralo jmeno. Jakykoli jiny identifikator, krom jmena, by byl sdileny obema klienty - Sem tam to proste nefunguje
  2. Ahoj, pár týdnu zpět jsem dělal stream primárně pro programátory LSRP, kde jsem jim chtěl předat základní znalosti na téma ID entity a komunikace client-server-client. Jeden z nich to šikovně natočil, tak jsem se rozhodl to zveřejnit na LSRP youtube pro všechny. Velká část streamu je dedikovaná porozumění entitám - entity ID a entity network ID, pak také PlayerID a PlayerServerId. Část pak komunikaci client-server-client.
  3. Zajimavy napad, pouzit text2speech. Z textu vubec jasne, ze link na github je "na cem jsi stavel" a "Voice GPS" download link je link ke stazeni - ja instinktivne sel na github link a tam hledal, jak je resena cestina. Upravil bych text tak, aby bylo jasnejsi, jak se dostat k tvoji praci. -- Prosel jsem kod a vsiml jsem si jedne zbytecne pomale veci local blacklistedNums = {5, 0, 8, 2, 9} if table.contains(blacklistedNums, dir) then return end -- (...) function table.contains(table, element) for _, value in pairs(table) do if value == element then return true end end return false end Tohle je zbytecne slozite reseni a zaroven 10x pomalejsi, nez nasledujici local blacklistedNums = {[5] = true, [0] = true, [8] = true, [2] = true, [9] = true} if blacklistedNums[dir] then return end jako bonus nepotrebujes zadnou funkci navic :) just works
  4. teb

    esx_rpchat xPlayer error

    Chces job z promenne xPlayer kterou nikde nedefinujes. Nad radek xPlayer.job... budes muset dat local xPlayer = ESX.GetPlayerFromId(source) Pokud v danym souboru jeste nemas definovany ESX, pak nekde budes jeste muset placnout ESX = nil Citizen.CreateThread(function() while ESX == nil do TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) Citizen.Wait(0) end end)
  5. weed.webm V tomto tutoriálu bych vám chtěl ukázat, jak na LSRP.cz navrhuji scripty tak, aby snadno fungovaly ve více hráčích, stav byl konzistentní napříč hráči a zároveň tento způsob programování naprosto znemožňuje, aby hacker mohl jakkoli benefitovat. Projdeme si, jak bych řešil script pro pěstování marihuany, připadně čehokoli jiného na podobný způsob. Výsledek neposkytuje nijak zábavný gameplay a smysl tohoto scriptu je pouze, aby jste se naučili, jak problémy podobného typu řešit správně, nikoli aby jste to použili na svém serveru. Je pak na vás, jak to dochutíte a uděláte to zábavné pro hráče. Nebudu vše vysvětlovat detailně, tuprtoriál je určen pro ogramátory, ne "fivem developery." Pokud ale něco nebude jasné, klidně se ptejte. Na LSRP.cz tak budeme řešit právě pěstování trávy, řešíme tak synchronizaci krabic, které hráči nosí jako součást jobu a některé mechaniky používáme pro jízdu na a vození nosítek a wheelchairu, nošení hráčů a NPC přes rameno, atd.. Hlavní koncept je, že server si udržuje stav, spravuje změnu stavu a distribuuje stav hráčům. To znamená, že když hráč chce zasadit květinu, pošle na server pouze event "chci zasadit květinu." Server pak zjistí, jestli může zasadit květinu (má v inventáři semínka? - neřeším v tutoriálu), kde se hráč nachází, vypočítá pozici kytky, nastaví počáteční stav kytky a pošle všem hráčům nový stav. Mohl by také kontrolovat, jestli na dané pozici či blízko už není jiná kytka (mimo scope tutoriálu) Dále server hlídá, kdy kytka vyroste a jde sebrat, řeší když hráč chce sebrat kytku (je kytka vyrostlá? je hráč dostatečně blízko?) a o těchto změnách stavu pak informuje hráče. Hacker při použítí tohoto stylu programování nemá žádnou výhodu. Nemůže zasadit kytku když nemá semínka, nemůže sebrat kytku když není vyrostlá a nemůže ukrást cizí kytky, aniž by u nich stál. Samozřejmě hacker může pomocí noclipu/fly hacku zasadit kytku mimo mapu, ve vzduchu, pod zemí atd., ale to už není problém tohoto scriptu, to by měl anticheat system. Na straně klienta pak pouze sledujeme stav, pokud je hráč blížko kytce tak vytvoříme OFFLINE objekt (je dobré omezit počet networked objektů), pokud se hráč vzdáli tak objekt smažeme, pokud se změní stage tak objekt smažeme a vytvoříme nový. Pokud je hráč blízko kytce co má stage který umožňuje kytku sklidit, zobrazíme hint a reagujeme na tlačítko. To je zhruba vše, co je potřeba. Detailněji to můžete vidět v následujícím anotovaném kódu. OneSync Server kód -- Zde si budeme udržovat kompletní přehled o trávě WeedState = {} -- vždy zvyšující se číslo, aby každá kytka měla unikátní ID WeedIndex = 1 -- jak dlouho trvá než kytka vyroste -- dvojnásobek pak trvá než kytka uhyne a zmizí TIME_TO_GROW = 10 * 1000 -- užitečný console příkaz pro vypsání stavu scriptu -- hodí se při debugování, když se ptáte "je chyba v syncu?" "za jak dlouho vyroste?" "kolik je kytek?" RegisterCommand('showweed', function(source) if source == 0 then tprint(WeedState) end end) -- příkaz pro zasazení trávy -- úmyslně zjednodušený, nic se nekontroluje, všichni můžou sázet všude -- ideálně by se melo zkontrolvoat jestli hráč má semínku a to pak sebrat z inventáře RegisterCommand('weed', function(source) local Source = source local ped = GetPlayerPed(Source) -- využijeme onesyncu, abychom bezpečně vypočítali, kam kytku zasadit -- díky tomu předejteme možnosti, že by si hacker mohl kytky sázet na místa, kde se nenachází local coords = GetEntityCoords(ped) local heading = GetEntityHeading(ped) -- kytku dáme před hráče. +90 proto, že EntityHeading směřuje o 90 stupnů doprava -- když přičteme 90 stupňů, bude heading směřovat před hráče local weedOffset = vector3( math.cos(math.rad(heading + 90.0)), math.sin(math.rad(heading + 90.0)), -1.0 ) AddPlant(coords + weedOffset) end, false) -- Jednoduchá funkce která přidá novou kytku do WeedState a synchronizuje stav -- nechceme nechávat tuto logiku zbytečne v command handleru -- kód se pak líp čte function AddPlant(pos) local idx = WeedIndex WeedIndex = WeedIndex + 1 WeedState[idx] = { pos = pos, stage = 0, plantedAt = GetGameTimer(), } SyncPlants(-1) end -- jen wrapper okolo eventu, nic zvláštního function SyncPlants(target) TriggerClientEvent('state_weed:sync', target, WeedState) end -- nesmíme zapomenout poslat stav komukoli, kdo se napojí na server -- když bychom to neudělali, tak hráči uvidí jen kytky, které byly zasazené během doby, co jsou online -- (nebo, s tím, jak je tento script napsaný, tak by dostali celý stav jakmile by někdo zasadil kytku) RegisterNetEvent('playerSpawned') AddEventHandler('playerSpawned', function() SyncPlants(source) end) -- tento thread slouží k tomu, že periodicky kontrolujeme, -- jestli nějaká kytka nevyrostla a pokud ano, posuneme jí stage -- případně ji smažeme, pokud zahynula Citizen.CreateThread(function() while true do Wait(1000) -- užitečný způsob, jak zjistit, jestli se tento tick něco změnilo -- pokud ano, tak synchronizovat stav -- pokud ne, tak zbytečně neposíláme data local anythingChanged = false for id, plant in pairs(WeedState) do if plant.stage == 0 and (GetGameTimer() - plant.plantedAt) > TIME_TO_GROW then plant.stage = 1 anythingChanged = true elseif plant.stage == 1 and (GetGameTimer() - plant.plantedAt) > (TIME_TO_GROW * 2) then WeedState[id] = nil anythingChanged = true end end if anythingChanged then SyncPlants(-1) end end end) -- když hráč chce sebrat kytku, pošle event na server -- jelikož nechceme, aby hackeři mohli jen tak sbírat kytky, -- ještě na serveru ověříme, jestli je tento event legit RegisterNetEvent('state_weed:pickup') AddEventHandler('state_weed:pickup', function(id) local Source = source local ped = GetPlayerPed(Source) local coords = GetEntityCoords(ped) local plant = WeedState[id] -- zkontrolujeme, že hráč sbírá kytku co existuje if plant then -- zkontrolujeme, že hráč je dostatečně blížko kytce if #(plant.pos - coords) < 3.0 then -- zkontrolujeme, že kytka je sbíratelná (stage 1) if plant.stage == 1 then WeedState[id] = nil -- tady bychom ještě hráče měli odměnit za sebrání kytky TriggerClientEvent('state_weed:info', Source, '~g~Tráva byla úspesne sklizena') SyncPlants(-1) else TriggerClientEvent('state_weed:info', Source, '~g~Tráva jeste nevyrostla') end else TriggerClientEvent('state_weed:info', Source, 'Tráva je moc daleko') end else TriggerClientEvent('state_weed:info', Source, 'Trávu se nepodarilo sklidit') end end) -- DEBUG FN -- jen debug funkce co vypíše obsah LUA tabulky function tprint (tbl, indent) if not indent then indent = 0 end if type(tbl) == 'table' then for k, v in pairs(tbl) do formatting = string.rep(" ", indent) .. k .. ": " if type(v) == "table" then print(formatting) tprint(v, indent+1) elseif type(v) == 'boolean' then print(formatting .. tostring(v)) else print(formatting .. v) end end else print(tbl) end end Client kód -- klientský stav scriptu WeedState = {} -- jednoduchý způsob, jak zjistit, jestli objekt/auto/ped je NETWORKED, je použít constantu -- pak se nedívám na `false, true, false)` a nemusím se ptát "je networked?" -- neboť to vidím - `NOT_NETWORKED, true, false)` - samozřejmě když popletu poředí argumentu, tak mi tohle nijak nepomůže NOT_NETWORKED = false -- na jakou vzdálenost chceme vytvořit objekt? -- pokud hráč bude dál, než tato vzdálenost, tak objekt vůbec nebude -- existovat ve hře. Já často používám 100 - 150, ale na testování 5 RENDER_DISTANCE = 5.0 -- Pokud už objekt existuje, tak ho despawneme, až když bude o toleranci dál, než v render distance -- Díky tomu pokud budu stát na hranici render distance tak mi objekt nebude přeblikávat pokaždé, když se přiblížím a oddálím -- Dobrá tolerance je třeba 20 jednotek RENDER_DISTANCE_TOLERANCE = 2.0 -- Jaké objekty použijeme, pro jakou stage STAGE_OBJECTS = { [0] = 'prop_weed_02', [1] = 'prop_weed_01', } RegisterNetEvent('state_weed:sync') AddEventHandler('state_weed:sync', function(w) local receivedIds = {} local ped = PlayerPedId() local coords = GetEntityCoords(ped) for id, plant in pairs(w) do -- receivedIds tabulku použijeme pro to, -- abychom poznali, jaké ID kytek už nejsou na serveru -- tzn. pokud v lokálním WeedState bude některé ID, co není v -- receivedIds, pak už server smazal danou kytku a klient musí taky receivedIds[id] = true -- pokud jde o novou kytku, tak ji jen přidáme do stavu a vytvoříme objekt if not WeedState[id] then WeedState[id] = plant ProcessPlantObject(coords, WeedState[id]) else -- pokud již na clientu kytka existuje -- musíme myslet na to, že klient už jí mohl vytvořit entitu -- a ten musíme zachovat. Nesmíme na něj ztratit referenci. -- zároveň zkontrolujeme, jestli se nezměnila stage a pokud ano, refreshneme objekt local oldPlantStage = WeedState[id].stage local entity = WeedState[id].entity WeedState[id] = plant WeedState[id].entity = entity if oldPlantStage ~= WeedState[id].stage then ProcessPlantObject(coords, WeedState[id]) end end end -- nakonec, jak jsem psal na začátku funkce -- smažeme kytky, které už nejsou evidované na serveru for id, plant in pairs(WeedState) do if not receivedIds[id] then DeleteWeedObject(plant) WeedState[id] = nil end end end) -- jen funkce na zobrazení notifikace. Ideálně použijte to, co používáte napříč projektem RegisterNetEvent('state_weed:info') AddEventHandler('state_weed:info', function(text) SetNotificationTextEntry('STRING') AddTextComponentSubstringPlayerName(text) DrawNotification(true, true) end) -- samostatný thread checkuje, jestli je hráč blízko nějaké kytce -- a pokud ano a kytka je vyrostlá, zobrazíme hint a reagujeme na tlačítko Citizen.CreateThread(function() while true do Wait(0) local ped = PlayerPedId() local coords = GetEntityCoords(ped) for id, plant in pairs(WeedState) do if plant.stage == 1 and #(coords - plant.pos) < 1.5 then RenderPickupText(plant.pos) if IsControlJustPressed(0, 38) then TriggerServerEvent('state_weed:pickup', id) end end end end end) -- samostatný thread checkuje stav entit -- pokud se hráč přiblíží, vytvoříme entitu -- pokud se vzdáli, smažeme entitu -- pokud nesedí model entity, refreshneme entitu Citizen.CreateThread(function() while true do Wait(1000) local ped = PlayerPedId() local coords = GetEntityCoords(ped) for _, plant in pairs(WeedState) do ProcessPlantObject(coords, plant) end end end) -- funkce která dělá to, co je popsané nad předchozím theadem ^^ function ProcessPlantObject(playerCoords, plant) if not plant.entity and #(plant.pos - playerCoords) < RENDER_DISTANCE then plant.entity = CreateWeedObject(plant.pos, plant.stage) elseif plant.entity and #(plant.pos - playerCoords) > (RENDER_DISTANCE + RENDER_DISTANCE_TOLERANCE) then DeleteWeedObject(plant) elseif plant.entity and GetEntityModel(plant.entity) ~= GetHashKey(STAGE_OBJECTS[plant.stage]) then DeleteWeedObject(plant) plant.entity = CreateWeedObject(plant.pos, plant.stage) end end -- Wrapper funkce na vytvoření objektu -- Ideálně by to chtělo do `while not HasModelLoaded(hash) do` -- přidat backpressure, že pokud se do např. 500 framu (nebo určitého času) nenačte model, tak -- to přestane čekat a vyfailuje -- to je ale mimo scope tohoto tutoriálu function CreateWeedObject(pos, stage) local hash = GetHashKey(STAGE_OBJECTS[stage]) if IsModelInCdimage(hash) then -- IsModelInCdimage checkuje, jestli model je ve hře. Na vzdory názvu nemá nic dočinení s CDčkama :) RequestModel(hash) while not HasModelLoaded(hash) do Wait(0) end local entity = CreateObject(hash, pos.x, pos.y, pos.z, NOT_NETWORKED, false, false) if entity == 0 then print("ERROR: Could not create object", hash) return nil end FreezeEntityPosition(entity, true) SetEntityAsMissionEntity(entity, true, true) return entity else print("ERROR: Model hash not in game ", hash) end end -- jednoduchy wrapper na smazani entity function DeleteWeedObject(plant) if plant.entity then DeleteObject(plant.entity) plant.entity = nil end end -- wrapper na 3d text function RenderPickupText(pos) local visible, x, y = GetScreenCoordFromWorldCoord(pos.x, pos.y, pos.z) SetTextFont(0) SetTextScale(0.0, 0.5) SetTextColour(255, 255, 255, 255) SetTextOutline() SetTextCentre(true) SetTextEntry("STRING") AddTextComponentString("~y~[E]~s~ Sklidit") DrawText(x, y) end
  6. teb

    CK Gang

    vzdy na tom gifu ani nic neni :D :D co to je za klauna
  7. Cetli jste, co vubec chce? Chce v intertalu mazat vsechny auta, ktery nejsou zamknuty. serverside: while true do Wait(10000) for _, entity in pairs(GetAllVehicles()) do local lockStatus = GetVehicleDoorLockStatus(entity) if lockStatus ~= 2 and lockStatus ~= 3 then DeleteEntity(entity) end end end Kazdopadne nenapsal jsi, k cemu to chces a nejspis to, co pises neni to, co chces.
  8. Vypada to zajimave, jen by me zajimalo, proc ti bugovala nativka na zjisteni posledniho impactu - presne tak je to totiz v R* scriptu pro shooting range. Na jakou cca vzdalenost to bugovalo? R* to ma cca nejak takhle int func_806(struct<2> Param0, var uParam1, var uParam2, var uParam3, var uParam4, var uParam5, var uParam6, var uParam7, var uParam8, var uParam9, var uParam10, var uParam11, var uParam12, var uParam13) { // Param0.f_1 je pointer na target // uParam13 je pointer na vystup fce GET_PED_LAST_WEAPON_IMPACT_COORD if (WEAPON::HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(Param0.f_1, 0, 2)) { ENTITY::CLEAR_ENTITY_LAST_DAMAGE_ENTITY(Param0.f_1); if (WEAPON::GET_PED_LAST_WEAPON_IMPACT_COORD(PLAYER::PLAYER_PED_ID(), uParam13)) { return 1; } else { return 0; } } return 0; } timhle detekuji hit+pozici, a pak zvlastnim zpusobem zjistuji, jak daleko od stredu jsi se strefil - spawnou si golfovy micek, attachnou ho na target (nejspis offsetnuty o vzdalenost hitu) a pak podle vzdalenosti mezi golf mickem a stredem cile davaji body.
  9. Smysl tohoto návodu je nasměrovat vás správným směrem co se porozumění flagů týče, nikoli je kompletně vysvětlit a objasnit. Některé nativky používají tzv. flagy, tenhle návod je pokud o to vysvetlit, jak s nimi pracovat. Flag není nic jiného, než jednoduchá reprezentace složité konfigurace pomoci nastavení bitu v integeru. Ukažme si, jak pracovat s flagy v nativce TaskPlayAnim. Z dokumentace: enum eAnimationFlags { ANIM_FLAG_NORMAL = 0, ANIM_FLAG_REPEAT = 1, ANIM_FLAG_STOP_LAST_FRAME = 2, ANIM_FLAG_UPPERBODY = 16, ANIM_FLAG_ENABLE_PLAYER_CONTROL = 32, ANIM_FLAG_CANCELABLE = 120, -- tohle je divna flaga, nebot zabira bity 4-7 }; Flagy jsou reprezentovány integerem, ale reálne se pracuje s bity daného integeru. Každý integer v desítkové soustavě (kterou používáme), lze reprezentovat v binární soustavě. Tedy: 0 => 0000 1 => 0001 2 => 0010 3 => 0011 => tedy součet 0001 a 0010 4 => 0100 5 => 0101 6 => 0110 7 => 0111 => tedy součet 0001 a 0010 a 0100 8 => 1000 Pro větší dvojkové binární soustavy si vyhledejte relevaltní článek binární soustavy. Čistě s těmito znalostmi bychom měli začít vidět, jak pouzívat jednotlivé flagy animace. Pokud například chceme animaci, aby se opakovala, umožnila pohyb hráče a platila jen na vrchní část těla, pak použijeme // 1 - repeat // 16 - upper body // 32 - controllable 1 + 16 + 32 (nebo dohromady 49, ale je lepší to nechat rozdělené pro budoucí porozumění flagů) Pro animaci na celé tělo, která se má zastavit na konci animace, použijeme flag 2 (neboť celé tělo je by default)
  10. teb

    ShapeTest

    ShapeTest (raycast) slouží k detekci "něčeho ve světě." se na určitém místě něco nachází (auto/ped) jestli se něco nachází mezi bodem A a bodem B Může být užitečné pro Spawnování aut (checknutí jestli je parkovací místo zabrané) Radar (vyhledání auta před/za autem hráče) Systém židlí (podívám se, jestli v ruzých směrech kolem peda je židle - rychlejší než GetClosestObjectOfType Užitečné jen když chcete vypsat help text. Pokud máte sednutí jen na tlačítko, pak 1 zavolání GetClosestObjectOfType dostačuje Zjištění, jestli mezi bodem A a bodem B je přímka, která není nijak blokovaná (ekvivalent HasEntityClearLosToEntity, netestoval jsem, co je rychlejší) Mnou testované ShapeTest funkce v GTA StartShapeTestRay StartShapeTestBox StartShapeTestCapsule Pro získání výsledku pak funkce GetShapeTestResult GetShapeTestResultIncludingMaterial Zbytek jsem nezkoušel a zatím jsem pro ně neměl potřebu. Příklad jednoduchého shapetestu, který najde auto před našim autem a zobrazí NumberPlate (R.Z.) Citizen.CreateThread(function() while true do Wait(0) local ped = PlayerPedId() local veh = GetVehiclePedIsIn(ped, false) if veh > 0 then local vehCoord = GetEntityCoords(veh) local fwdVector = GetEntityForwardVector(veh) local rayEnd = vehCoord + fwdVector * 8 -- debug line -- pro debug nevypisuju individualne x,y,z, nechavam CFX -- aby to spravne interpretoval. Neni doporuceno pro -- produkcni kod, nebot se to muze kdykoli zmenit -- a podpora muze zmizet DrawLine(vehCoord, rayEnd, 0, 0, 255, 255) -- nadefinujeme shapetest local ray = StartShapeTestRay( vehCoord.x, vehCoord.y, vehCoord.z, -- začátek shapetestu rayEnd.x, rayEnd.y, rayEnd.z, -- konec shapetestu 2, -- flag => auta veh, -- entita, kterou chceme ignorovat => naše auto 4 -- neznámý argument ) -- získáme výsledek raycastu local _, hit, endCoords, _, entityHit = GetShapeTestResult(ray) if hit > 0 and entityHit > 0 then -- nemusime zjišťovat, jeslti entityHit je auto -- protože jsme ray omezili flagem local license = GetVehicleNumberPlateText(entityHit) Draw3DText(endCoords.x, endCoords.y, endCoords.z, license) end end end end) function Draw3DText(x, y, z, text) local onScreen,_x,_y=World3dToScreen2d(x,y,z) if onScreen then SetTextScale(0.6, 0.6) SetTextFont(0) SetTextColour(255, 255, 255, 255) SetTextDropshadow(0, 0, 0, 0, 255) SetTextDropShadow() SetTextOutline() SetTextEntry("STRING") SetTextCentre(1) AddTextComponentString(text) DrawText(_x,_y) end end Příklad složitějšího shapetestu pro zjištění, jestli je parkovací místo zabrané. Citizen.CreateThread(function() -- checknuti 1 pozice trva na mojem PC neco málo přes 0.01ms local centers = { vector3(228.35, -786.49, 29.69), vector3(229.26, -783.89, 29.7), vector3(230.3, -781.41, 29.7), vector3(231.22, -778.95, 29.71), vector3(233.05, -773.91, 29.73), vector3(234.09, -771.28, 29.75) } local heading = 68.5 -- směr parkovacího boxu -- velikost parkovacího boxu local width = 2.3 local length = 4.5 local height = 2.0 while true do Wait(0) for _, center in pairs(centers) do local occluded = IsParkingSpotOccluded(center, heading, width, length, height) ShapeBoxDraw(center, heading, width, length, height, occluded) end end end) function IsParkingSpotOccluded(center, heading, width, length, height) -- 2 -> auta -- 4 -> peds -- 16 -> objekty -- 256 -> rostliny local flags = 2 + 4 + 16 + 256 local ray = StartShapeTestBox( -- vytvoříme ShapeTest center.x, center.y, center.z, -- center boxu width, length, height, -- velikost boxu 0.0, 0.0, heading, 2, -- https://en.wikipedia.org/wiki/Euler_angles, `2` - pořadí rotací flags, -- flagy, co raycast chyta - https://runtime.fivem.net/doc/natives/?_0x377906D8A31E5586 0, -- entita, kterou má raycast ignorovat (např. PlayerPedId() pokud chceme ignorovat sveho peda) 4 -- neznamy parametr ) -- 1. vystup je neznámá proměnná -- hit - 0 pokud nic nebylo nalezeno, 1 pokud bylo -- endCoords - kde raycast něco trefil -- surfaceNormal - https://cs.wikipedia.org/wiki/Norm%C3%A1la -- entityHit - jakou entitu ray trefil local _, hit, endCoords, surfaceNormal, entityHit = GetShapeTestResult(ray) -- zjistíme výsledek ShapeTestu -- pokud vás zajímá i materiál, pak funkce https://runtime.fivem.net/doc/natives/?_0x65287525D951F6BE return hit > 0 end -- Funkce na debug StartShapeTestBox, není vysvětleno jak funguje, neboť není předmětem návodu -- ale je to jen jednoduchá trigonometrie -- vykreslení debug boxu trvá ~0.01ms function ShapeBoxDraw(center, heading, width, length, height, occluded) local diagonal = math.sqrt((width/2)^2 + (length/2)^2) local fullDiagonal = math.sqrt(width^2 + length^2) local boxHeight = vector3(0.0, 0.0, height) local newAngle = math.deg(math.asin(length/fullDiagonal)) local topRight = GetAngledPosition(center, diagonal, heading + newAngle, 1) local bottomRight = GetAngledPosition(center, diagonal, heading - newAngle, 1) local bottomLeft = GetAngledPosition(center, diagonal, heading + newAngle, -1) local topLeft = GetAngledPosition(center, diagonal, heading - newAngle, -1) local off = vector3(0.0, 0.0, 5.0) local boxColor = {0, 255, 0} if occluded then boxColor = {255, 0, 0} end DrawLine(topRight-off, topRight+off, 0, 255,0,255) DrawLine(bottomRight-off, bottomRight+off, 255, 0 ,0,255) DrawLine(bottomLeft-off, bottomLeft+off, 0, 0,255,255) DrawLine(topLeft-off, topLeft+off, 255, 255,255,255) DrawPoly(topRight, topLeft, topLeft + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(topRight, topLeft + boxHeight, topRight + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(bottomLeft, bottomRight, bottomRight + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(bottomLeft, bottomRight + boxHeight, bottomLeft + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(topLeft, bottomLeft, bottomLeft + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(topLeft, bottomLeft + boxHeight, topLeft + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(bottomRight, topRight, topRight + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(bottomRight, topRight + boxHeight, bottomRight + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(bottomRight + boxHeight, topRight + boxHeight, topLeft + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) DrawPoly(topLeft + boxHeight, bottomLeft + boxHeight, bottomRight + boxHeight, boxColor[1], boxColor[2], boxColor[3], 100) end function GetAngledPosition(center, dist, angle, mod) local angRad = math.rad(angle) return center + mod * dist * vector3( math.cos(angRad), math.sin(angRad), 0.0 ) end

Our partners

rcore.cz
K4mb1
SLTH
×
×
  • Create New...