Poglavlje 36: Audio, Physics i Gameplay Performance
Zasto ovo poglavlje postoji
Ako ste pratili prethodna poglavlja, verovatno ste stekli utisak da je performans u igrama pre svega pitanje renderinga. I nije cudno -- GPU, shaderi, draw call-ovi, LOD sistemi, occlusion culling -- sve to zauzima ogroman deo diskusije o optimizaciji. Ali evo jedne neprijatne istine koju svaki iskusan developer zna:
Rendering nije sve.
Mozete imati scenu koja renderuje 200 FPS na najjacoj grafickoj kartici, a da vam igra i dalje "stuca" -- jer vam physics simulacija guta CPU, jer imate 500 zvukova koji se istovremeno procesiraju, jer hiljade aktora tikuje svaki frejm bez potrebe, ili jer Garbage Collector odluci da napravi pauzu od 30 milisekundi bas u najgorem momentu.
Ovo poglavlje pokriva sve ono sto nije rendering, a moze vas unistiti performansno. Govoricemo o:
- Audio sistemu i koliko vas kosta reprodukcija zvuka, okluzija, reverb i proceduralni audio
- Physics sistemu -- od jednostavnih kolizija do ragdoll-a, cloth simulacije i Chaos Destruction-a
- Tick rate-ovima i kako neupravljani tick moze postati vasa najveca performansna nocna mora
- Blueprint vs C++ overhead-u -- gde je granica i kada morate preci na C++
- Actor lifecycle-u -- spawn, destroy, object pooling i Garbage Collection
- Async loading-u -- jer sinhrono ucitavanje asset-a je recept za hitch-eve
- Multithreading-u -- jer moderni UE5 koristi vise niti (thread-ova) i morate razumeti kako one medusobno saradjuju
Hajde da krenemo.
36.1 Audio Performance
Zasto je audio uopste performansno pitanje?
Vecina pocetnika razmislja o zvuku ovako: "Pustim WAV fajl, on svira, gotovo." Ali u praksi, audio engine u UE5 radi mnogo vise od toga:
- Svaki zvuk mora da se pozicionira u 3D prostoru (spatialization)
- Mora da se izracuna kako zidovi i prepreke blokiraju zvuk (occlusion)
- Mora da se primeni reverb na osnovu okruzenja
- Svi aktivni zvukovi moraju da se miksuju u finalni stereo/surround output
- Proceduralni zvuk (MetaSounds) generise audio u realnom vremenu
Sve ovo kosta CPU vreme, i to moze postati znacajno.
36.1.1 Sound Playback Cost -- Voice Count
Najosnovniji parametar audio performansa je voice count -- koliko zvukova istovremeno svira.
Svaki aktivan zvuk u UE5 predstavlja jedan voice (glas). Engine mora za svaki voice da:
- Dekodira audio podatke (ili cita iz memorije ako su vec dekodirani)
- Primeni pitch, volume i druge parametre
- Izracuna spatialization (gde se zvuk nalazi u 3D prostoru)
- Primeni efekte (reverb, EQ, itd.)
- Miksa ga u finalni output
Cena po voice-u nije ogromna, ali skalira linearno. Deset zvukova je jeftino. Sto zvukova je primetno. Trista zvukova moze biti problem.
Max Concurrent Sounds
UE5 ima podesavanje Max Concurrent Sounds (u Project Settings > Audio) koje ogranicava koliko zvukova moze istovremeno da svira. Default vrednost je tipicno 32.
Project Settings > Engine > Audio > Max Channels
Ovo znaci: bez obzira koliko SoundCue-ova ili MetaSounds instanci pokrenete, engine ce aktivno procesirati najvise toliko zvukova. Ostali se ili stavljaju u red cekanja (virtuelni zvukovi) ili se jednostavno ne pustaju.
Prakticni saveti:
| Platforma | Preporuceni Max Channels |
|---|---|
| PC (high-end) | 64-128 |
| PC (mid-range) | 32-64 |
| Konzole (PS5/Xbox Series X) | 48-96 |
| Mobilni uredjaji | 16-24 |
Virtuelni zvukovi (virtual voices) su kljucan koncept -- UE5 moze da "prati" mnogo vise zvukova nego sto zapravo procesira. Virtuelni zvuk pamti svoju poziciju, volume i stanje, ali ne prolazi kroz audio procesiranje. Kada postane dovoljno glasan ili blizak igracu, engine ga promovise u pravi voice.
// U C++-u, mozete podesiti Sound Concurrency za specifican zvuk
USoundConcurrency* Concurrency = NewObject<USoundConcurrency>();
Concurrency->MaxCount = 4; // Maksimalno 4 instance ovog zvuka
Concurrency->ResolutionRule = EMaxConcurrentResolutionRule::StopLowestPriority;
36.1.2 Audio Occlusion -- Raycast Cost
Audio occlusion je sistem koji simulira kako zvuk biva blokiran fizickim objektima. Zamislite da ste u jednoj sobi, a eksplozija se desava u susednoj -- zvuk treba da bude prigusen i filtriran jer prolazi kroz zid.
Da bi ovo izracunao, engine mora da baca zrake (raycast) izmedju izvora zvuka i slusaoca (kamere/igraca):
[Izvor zvuka] ----raycast----> [Igrac/Listener]
^
|
Da li nesto blokira put?
Problem je sto ovo mora da se radi za svaki aktivan zvuk i to svaki frejm (ili u redovnim intervalima). Ako imate 50 aktivnih zvukova sa occlusion-om, to je 50 raycast-ova po frejmu, minimum.
Dodatno, napredni occlusion sistemi mogu da bacaju vise zraka po izvoru (na primer, 5-10 zraka u razlicitim pravcima) da bi procenili koliko je zvuk delimicno okluziran.
Cena raycast-a zavisi od:
- Kompleksnosti scene (koliko collision geometry ima)
- Duzine zraka (dalji zvukovi = duzi raycast = vise provera)
- Tipa collision-a koji se koristi
Optimizacione strategije:
- Ogranicite occlusion samo na bitne zvukove -- nema smisla racunati okluziju za ambijentalni vetar
- Smanjite frekvenciju provere -- umesto svakog frejma, proveravajte svakih 3-5 frejmova
- Koristite Audio Volume-e umesto raycast-a gde je moguce -- rucno definisani volumeni su mnogo jeftiniji
- Ogranicite domet okluzije -- zvukovi dalje od odredjene distance ne trebaju okluziju
// Primer: Podesavanje occlusion intervala
UAudioComponent* AudioComp = /* ... */;
AudioComp->OcclusionCheckInterval = 0.2f; // Provera svakih 200ms umesto svakog frejma
36.1.3 Reverb i Spatial Audio Processing
Reverb (odjek) je efekat koji simulira kako se zvuk odbija od povrsina u zatvorenom prostoru. Mala soba ima kratak, oster reverb. Velika katedrala ima dug, difuzan reverb.
U UE5, reverb se tipicno primenjuje na dva nacina:
- Audio Volumes sa Reverb Effect-om -- najjeftiniji pristup, postavite volume u level i dodelite mu reverb preset
- Convolution Reverb -- koristi snimljeni impulse response (IR) realnog prostora. Mnogo realisticniji, ali i mnogo skuplji jer zahteva konvoluciju audio signala sa IR-om
Convolution reverb je posebno skup jer zahteva matematicku operaciju zvanu konvolucija koja je O(n*m) gde je n duzina audio buffer-a a m duzina impulse response-a. Za dugacke IR-ove (2-3 sekunde reverb-a), ovo moze biti znacajno.
Spatialization (prostorni zvuk) takodje kosta:
- Osnovni panning (stereo L/R) je jeftin
- HRTF (Head-Related Transfer Function) spatialization je skuplji -- simulira kako ljudska glava i usi filtriraju zvuk iz razlicitih pravaca
- Ambisonics rendering (za VR) je jos skuplji
Cena spatialization-a (od najjeftinijeg do najskupljeg):
1. Stereo panning -- trivijalno
2. Surround panning (5.1) -- jeftino
3. HRTF spatialization -- umereno
4. Ambisonics (VR) -- skupo
36.1.4 MetaSounds -- Proceduralni Audio
MetaSounds je UE5-ov sistem za proceduralni audio -- umesto da pustite unapred snimljen zvuk, vi generisete zvuk u realnom vremenu koristeci graf cvorova (node graph), slicno Blueprint-u.
MetaSounds je neverovatno mocan:
- Mozete generisati beskonacne varijacije zvuka bez ogromnih audio fajlova
- Mozete zvuk ucinti reaktivnim na gameplay (brzina vozila menja pitch i ton motora u realnom vremenu)
- Mozete kreirati adaptivnu muziku koja se neprimtno menja
Ali ta moc dolazi sa cenom: compute cost.
Svaki MetaSounds graf koji se izvrsava u realnom vremenu trosira CPU cikluse. Sto je graf kompleksniji (vise oscilatora, filtera, envelope-a, modulacija), to je veci trosak.
Kljucni faktori cene MetaSounds grafa:
| Element | Relativna cena |
|---|---|
| Jednostavan oscillator (sine, saw) | Niska |
| Filter (low-pass, high-pass) | Niska-umerena |
| Delay line | Umerena |
| Granular synthesis | Visoka |
| FFT-based processing | Vrlo visoka |
| Mnogo modulacija i feedback petlji | Visoka (akumulativno) |
Prakticni savet: Koristite MetaSounds za zvukove koji zaista trebaju proceduralnost (motor vozila, interaktivna muzika), a za obicne zvukove (koraci, pucnjevi) koristite klasicne SoundCue-ove ili obicne WAV fajlove.
// Pseudokod: MetaSounds graf za motor vozila
Input: RPM (float), Throttle (float), Load (float)
Oscillator1(Frequency = RPM * 0.5) --> Filter(Cutoff = Throttle * 2000)
Oscillator2(Frequency = RPM * 1.0) --> Filter(Cutoff = Load * 3000)
Noise() --> Filter(Cutoff = RPM * 0.3) --> Gain(Throttle * 0.1)
Mix(Osc1, Osc2, Noise) --> Output
36.1.5 Audio LOD -- Level of Detail za Zvuk
Bas kao sto koristimo vizuelni LOD (manje poligona za udaljene objekte), mozemo koristiti Audio LOD da smanjimo trosak zvuka za udaljene izvore.
Audio LOD strategije:
-
Distance-based attenuation -- zvuk se gasi sa rastojanjem. Kada padne ispod praga cujnosti, engine ga prebacuje u virtualni voice (ne procesira ga)
-
Smanjenje kvaliteta spatialization-a -- za daleke zvukove koristite jednostavan stereo panning umesto HRTF
-
Iskljucivanje occlusion-a za daleke zvukove -- ionako se jedva cuju
-
Smanjenje sample rate-a za daleke zvukove -- umesto 48kHz, koristite 22kHz ili 11kHz
-
Pojednostavljivanje MetaSounds grafova -- za daleke instance, koristite jednostavniju verziju grafa
// Primer: Audio LOD na osnovu distance
void AMyActor::UpdateAudioLOD(float DistanceToListener)
{
if (DistanceToListener > 5000.0f)
{
// Daleko - minimalan audio procesiranje
AudioComponent->SetIntParameter("AudioLOD", 2);
AudioComponent->bEnableOcclusion = false;
}
else if (DistanceToListener > 2000.0f)
{
// Srednje rastojanje
AudioComponent->SetIntParameter("AudioLOD", 1);
AudioComponent->bEnableOcclusion = true;
AudioComponent->OcclusionCheckInterval = 0.5f;
}
else
{
// Blizu - pun kvalitet
AudioComponent->SetIntParameter("AudioLOD", 0);
AudioComponent->bEnableOcclusion = true;
AudioComponent->OcclusionCheckInterval = 0.1f;
}
}
36.1.6 Audio Thread i Interakcija sa Game Thread-om
UE5 pokrece audio procesiranje na zasebnom thread-u (Audio Thread). To znaci da audio miksovanje i procesiranje ne blokiraju direktno Game Thread.
Ali interakcija izmedju ova dva thread-a i dalje postoji:
Game Thread Audio Thread
| |
|-- "Pusti zvuk eksplozije" -->|
|-- "Promeni volume" -------->|
|-- "Zaustavi zvuk" --------->|
| |-- dekodiranje
| |-- spatialization
| |-- miksovanje
| |-- output
|<-- "Zvuk zavrsen" ----------|
Problemi koji mogu nastati:
-
Previse komandi iz Game Thread-a -- ako svaki frejm saljete stotine audio komandi (play, stop, set parameter), to stvara overhead na Game Thread-u jer svaka komanda mora da se pakuje i salje
-
Audio Thread preopterecen -- ako Audio Thread ne moze da zavrsi procesiranje pre nego sto treba da isporuci sledeci audio buffer, doceice do audio glitch-eva (pucketanje, preskakanje)
-
Sinhronizacija -- neki audio dogadjaji moraju da budu sinhroni sa gameplay-em (na primer, zvuk koraka u ritam igri), sto zahteva pazljivu koordinaciju
Dijagnostika u UE5:
Koristite komandu stat Audio u konzoli da vidite:
- Broj aktivnih voices
- Broj virtuelnih voices
- Vreme procesiranja Audio Thread-a
- Broj aktivnih Sound Cue/MetaSounds instanci
// U konzoli igre
stat Audio
stat SoundCues
stat SoundWaves
Ako Audio Thread vreme prelazi vreme trajanja jednog audio buffer-a (tipicno 5-10ms za buffer od 256-512 semplova na 48kHz), imacete audio artefakte.
36.2 Physics Performance
Physics je cesto najskuplji non-rendering sistem u igri. Simulacija fizike zahteva kompleksne matematicke proracune koji se izvrsavaju svakog frejma, a troskovi mogu eksplodirati ako niste pazljivi.
UE5 koristi Chaos Physics engine (nasledjujuci PhysX u ranijim verzijama), koji je dizajniran za napredne simulacije ali takodje donosi nove performansne izazove.
36.2.1 Collision Detection Complexity
Kolizija (collision detection) je proces utvrdjivanja da li se dva objekta dodiruju ili presecaju. Ovo je fundamentalna operacija za fiziku, ali njena cena drasticno varira u zavisnosti od oblika kolizije koji koristite.
Simple Shapes -- Jeftino
Najjednostavniji i najjeftiniji oblici kolizije su primitivni geometrijski oblici:
| Oblik | Opis | Relativna cena |
|---|---|---|
| Sphere (lopta) | Najjeftiniji -- samo provera rastojanja izmedju centara | 1x (bazna linija) |
| Box (kutija, AABB/OBB) | Provera preklapanja po osama | 1-2x |
| Capsule (kapsula) | Cylinder sa zaobljenim krajevima, idealno za karaktere | 1-2x |
Provera kolizije izmedju dve sfere je bukvalno jedno oduzimanje vektora i jedno poredjenje:
// Kolizija dve sfere - neverovatno jeftino
bool SpheresCollide(FVector Center1, float Radius1, FVector Center2, float Radius2)
{
float DistSquared = FVector::DistSquared(Center1, Center2);
float RadiiSum = Radius1 + Radius2;
return DistSquared <= (RadiiSum * RadiiSum);
}
Ovo je razlog zasto se za broad phase collision detection (prva faza provere) uvek koriste jednostavni oblici.
Convex Hull -- Umerena Cena
Convex hull je najmanji konveksni oblik koji obuhvata mesh. Zamislite da obmotate gumenu foliju oko objekta -- to je convex hull.
- Tipicno ima 16-64 verteksa (UE5 ogranicava na 256)
- Kolizija se proverava koristeci GJK (Gilbert-Johnson-Keerthi) algoritam
- Mnogo skuplje od primitivnih oblika, ali i dalje razumno
Vazno: UE5 dozvoljava "compound collision" -- vise konveksnih oblika kombinovanih da aproksimiraju kompleksan oblik. Na primer, sto moze imati 4 convex hull-a (jedan za povrsinu, cetiri za noge).
Jedan Convex Hull: Compound (4 Convex Hulls):
_________ _________
/ \ |_________|
/ \ | | | |
| | | | | |
|_____________| |__| |__|
Jednostavniji, Precizniji,
ali manje precizan ali 4x vise provera
Triangle Mesh Collision -- Skupo
Triangle mesh collision (takodje zvana "complex collision") koristi stvarnu geometriju mesh-a za koliziju. Svaki trougao mesh-a postaje potencijalna koliziona povrsina.
Zasto je ovo skupo:
- Mesh sa 10.000 trouglova zahteva potencijalno 10.000 provera po kolizionom paru
- Cak i sa prostornim strukturama podataka (BVH -- Bounding Volume Hierarchy), cena je znacajno veca od konveksnih oblika
- Ne moze se koristiti za dinamicke (pokretne) objekte u simulaciji -- samo za staticnu geometriju (tereni, zidovi)
Cena kolizije
Triangle Mesh ████████████████████████████████ 100x+
Convex Hull ████████████ 10-20x
Capsule ██ 2x
Box █ 1-2x
Sphere █ 1x (bazna)
Zlatno pravilo: Koristite najjednostavniji kolizioni oblik koji daje prihvatljiv rezultat. Ne trebaju vam triangle mesh kolizije za metak -- sfera je sasvim dovoljna.
// U UE5, za Static Mesh Component mozete podesiti tip kolizije
UStaticMeshComponent* MeshComp = CreateDefaultSubobject<UStaticMeshComponent>("Mesh");
// DOBRO - koristite jednostavnu koliziju
MeshComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
MeshComp->BodyInstance.SetCollisionProfileName("BlockAll");
// Engine ce koristiti Simple Collision (auto-generisan box/convex) ako postoji
// LOSA PRAKSA za dinamicke objekte:
// MeshComp->bUseComplexAsSimpleCollision = true; // Triangle mesh za SVE - SKUPO!
36.2.2 Rigid Body Simulation Cost
Rigid body simulacija je proces izracunavanja kako se kruta tela krecu, rotiraju i medjusobno interaguju pod uticajem sila (gravitacija, impulsi, kontakti).
Cena simulacije zavisi od broja aktivnih tela. Kljucna rec je "aktivnih" -- UE5 (Chaos Physics) automatski stavlja tela u sleep stanje kada se dovoljno smire (prestanu da se krecu). Telo u sleep stanju ne kosta skoro nista.
Stanje tela:
ACTIVE (budno) -- simulira se svaki frejm -- KOSTA
SLEEPING (spava) -- ne simulira se dok ga neko ne probudi -- BESPLATNO
Problem nastaje kada imate mnogo aktivnih tela:
| Broj aktivnih tela | Tipicna cena (ms) | Situacija |
|---|---|---|
| 10-50 | < 0.5ms | Normalan gameplay |
| 50-200 | 0.5-2ms | Akciona scena |
| 200-500 | 2-5ms | Velika destrukcija |
| 500-1000 | 5-15ms | Problematicno |
| 1000+ | 15ms+ | Neodrzivo bez optimizacije |
Tipicni problemi:
-
Chain reaction -- jedan objekat udari u gomilu objekata, svi se probude, svi pocinju da interaguju. Broj aktivnih tela eksplodira.
-
Nikad ne zaspe -- objekti koji se stalno pomeraju (na primer, na pokretnoj traci ili u blizini vibrirajuceg objekta) nikad ne ulaze u sleep. Resenje: podesite
SleepThresholdna visu vrednost ili ih forsirajte u sleep. -
Jittering -- objekti koji bi trebalo da miruju ali se stalno blago pomeraju, drzeci se budnim. Ovo se desava kada su kontaktni solver-i nedovoljno precizni.
// Forsiranje sleep-a za fizicko telo
UPrimitiveComponent* PhysComp = /* ... */;
PhysComp->PutAllRigidBodiesToSleep();
// Podesavanje sleep threshold-a
FBodyInstance* BodyInst = PhysComp->GetBodyInstance();
if (BodyInst)
{
BodyInst->SleepFamily = ESleepFamily::Sensitive; // Lakse zaspi
}
36.2.3 Collision Pair Count -- N-Body Problem
Kada imate N tela u sceni, potencijalni broj kolizionih parova je N*(N-1)/2. Za 100 tela, to je 4,950 mogucih parova. Za 1000 tela, to je skoro pola miliona.
Ocigledno, proveravati svaki par bi bilo suludo. Zato physics engine koristi dvofazni pristup:
Broad Phase
Broad phase brzo elimise parove koji sigurno ne mogu da se sudare. Koristi jednostavne bounding volume-e (AABB -- Axis-Aligned Bounding Box) i prostorne strukture podataka.
Chaos Physics u UE5 koristi prostornu hash mapu ili BVH stablo za broad phase. Rezultat je lista potencijalnih kolizionih parova -- znatno manja od ukupnog broja mogucih parova.
1000 tela u sceni
|
v
Broad Phase: AABB provere, prostorno hashiranje
|
v
~200 potencijalnih parova (umesto ~500,000)
|
v
Narrow Phase: precizne kolizione provere
|
v
~30 stvarnih kolizija
Narrow Phase
Za parove koji su prosli broad phase, engine radi detaljnu koliziju koristeci stvarne kolizione oblike (sfere, convex hull-ove, itd.). Ovo je skuplji korak, ali se radi na mnogo manjem broju parova.
Kako smanjiti collision pair count:
-
Collision Channels -- ne moraju svi objekti da interaguju sa svim drugim objektima. Metak ne treba da kolizira sa drugim metcima. Koristite collision channel-e da iskljucite nepotrebne provere.
-
Collision Presets -- UE5 ima unapred definisane kolizione profile (BlockAll, OverlapAll, BlockAllDynamic, itd.)
-
Smanjite broj fizickih tela -- spojite male objekte u vece, koristite instanced static mesh gde je moguce
// Primer: Podesavanje collision channel-a da metak ignorise druge metke
void AProjectile::SetupCollision()
{
CollisionComponent->SetCollisionObjectType(ECC_GameTraceChannel1); // Custom "Projectile" channel
CollisionComponent->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Ignore); // Ignorisi druge projektile
CollisionComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block); // Blokiraj pawn-ove
CollisionComponent->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block); // Blokiraj staticnu geometriju
}
36.2.4 Physics Substeps
Standardno, physics simulacija se izvrsava jednom po frejmu. Ali ovo moze dovesti do problema:
- Brzi objekti mogu "proci kroz" tanke zidove (tunneling)
- Simulacija moze biti nestabilna pri niskim frame rate-ovima
- Kontakti mogu biti neprecizni
Physics substeps resavaju ovo tako sto izvrsavaju fiziku vise puta po frejmu, sa manjim vremenskim korakom.
Bez substeps-a (1 iteracija po frejmu na 60 FPS):
Delta time = 16.67ms per step
Sa 4 substeps-a po frejmu:
Delta time = 4.17ms per step
= 4x preciznija simulacija
= 4x skuplja simulacija
Kada koristiti substeps:
- Brzi projektili koji moraju precizno da detektuju kolizije
- Vozila sa kompleksnom fizikom (tockovi, suspenzija)
- Simulacije koje zahtevaju stabilnost (lancane reakcije, slagalice fizike)
Podesavanje u UE5:
Project Settings > Physics > Substepping
bSubstepping = true
MaxSubstepDeltaTime = 0.008333 // Maksimalni delta time jednog substep-a (120Hz)
MaxSubsteps = 4 // Maksimalan broj substepova po frejmu
Upozorenje: Substeps su skupi. Ako vec imate performansne probleme sa fizikom, dodavanje substeps-a ce ih pogorsati. Koristite ih samo kada su zaista potrebni, i samo za objekte koji ih zahtevaju.
36.2.5 Ragdoll Physics Cost
Ragdoll je sistem koji simulira ljudsko telo kao niz rigid body-ja povezanih zglobovima (constraints). Kada lik "umre" u igri, prelazi iz animiranog stanja u ragdoll stanje.
Zasto je ragdoll skup:
-
Tipican ragdoll ima 15-25 rigid body-ja (za svaki deo tela: glava, gornji torzo, donji torzo, nadlaktica x2, podlaktica x2, saka x2, butina x2, potkolenica x2, stopalo x2)
-
Svaki par susednih tela ima constraint (zglobnu vezu) koji solver mora da zadovolji
-
Ragdoll-ovi se cesto medjusobno sudaraju (self-collision) sto dodaje jos kolizionih parova
-
Ragdoll-ovi retko "zaspe" brzo jer se ljuljaju, klize, itd.
Prakticne optimizacije:
| Strategija | Usteda | Opis |
|---|---|---|
| Ogranici broj ragdoll-ova | Velika | Max 5-10 aktivnih istovremeno |
| Smanji broj tela po ragdoll-u | Umerena | 10 tela umesto 20 |
| Forsiraj sleep nakon N sekundi | Velika | Ragdoll zaspi 3-5 sekundi nakon aktivacije |
| Konvertuj u static mesh | Velika | Nakon zaustavljanja, zameni ragdoll staticnim mesh-om |
| Iskljuci self-collision | Umerena | Manje kolizionih parova |
| LOD ragdoll | Velika | Daleki ragdoll-ovi imaju manje tela ili se ne simuliraju |
// Primer: Ogranicavanje broja aktivnih ragdoll-ova
class ARagdollManager
{
TArray<AActor*> ActiveRagdolls;
int32 MaxActiveRagdolls = 8;
void ActivateRagdoll(AActor* Actor)
{
if (ActiveRagdolls.Num() >= MaxActiveRagdolls)
{
// Ugasi najstariji ragdoll
AActor* OldestRagdoll = ActiveRagdolls[0];
ConvertToStaticMesh(OldestRagdoll);
ActiveRagdolls.RemoveAt(0);
}
Actor->GetMesh()->SetSimulatePhysics(true);
ActiveRagdolls.Add(Actor);
}
};
36.2.6 Cloth Simulation (Chaos Cloth)
Cloth simulacija simulira ponasanje tkanine -- kako se ogrtac vijori na vetru, kako se odeca deformise pri kretanju.
UE5 koristi Chaos Cloth sistem koji simulira tkaninu kao mrezu cestica (particles) povezanih oprugama (springs).
Cena cloth simulacije zavisi od:
- Broja cestica -- vise cestica = detaljnija simulacija = skuplje
- Broja constraint-a (opruga) -- svaka veza izmedju cestica kosta
- Broja solver iteracija -- vise iteracija = stabilnija simulacija = skuplje
- Kolizije sa telom -- cloth mora da zna gde je telo da ne prodre kroz njega
Tipicna cena cloth simulacije po instanci:
Jednostavan (200 cestica, 2 iteracije) -- 0.1-0.3ms
Srednji (500 cestica, 4 iteracije) -- 0.3-0.8ms
Kompleksan (1000+ cestica, 6 iteracija) -- 0.8-2.0ms+
Optimizacije za cloth:
- Cloth LOD -- na vecim rastojanjima, koristite cloth sa manje cestica ili ga potpuno iskljucite
- Smanjite solver iteracije za daleke instance
- Ogranicite broj cloth instanci -- 3-5 istovremeno je razumno za vecinu platformi
- Koristite cloth za igraca i par vaznih NPC-ova, ostali neka imaju staticne mesh-ove
36.2.7 Chaos Destruction System
Chaos Destruction je UE5-ov sistem za razaranje objekata. Omogucava kreiranje objekata koji se lome, krse, drobe na realistican nacin.
Kako funkcionise:
- Mesh se unapred "lomi" na fragmente koristeci Fracture tool u editoru
- Fragmenti su povezani constraint-ima koji imaju "prag lomljenja" (break threshold)
- Kada sila prekoraci prag, fragmenti se odvajaju i postaju nezavisna fizicka tela
Zasto je ovo performansno skupo:
Zamislite zid od cigli koji se razori. Jedan static mesh postaje 200 nezavisnih fizickih tela, svako sa svojim kolizionim oblikom, svako interaguje sa svim ostalim. Broj kolizionih parova eksplodira.
Pre destrukcije: Nakon destrukcije:
[Jedan objekat] --> [200 fragmenata]
1 rigid body 200 rigid body-ja
0 collision pairs 200*199/2 = ~20,000 potencijalnih parova!
Chaos Destruction optimizacije:
| Tehnika | Opis |
|---|---|
| Hierarchical fracture | Umesto da se sve lomi odjednom, koristite nivoe -- prvo se lomi na velike komade, pa tek oni na manje |
| Cluster constraint | Grupisanje fragmenata koji se ponasaju kao jedno telo dok se ne razbiju |
| Max active clusters | Ogranicite koliko cluster-a moze biti aktivno |
| Remove on break | Fragmenti nestaju nakon nekoliko sekundi |
| Sleep threshold | Agresivniji sleep -- fragmenti brze zaspe |
| Distance-based destruction LOD | Daleki objekti se razore na manje fragmenata |
| Max simulated bodies | Globalno ogranicenje za Destruction tela |
// Primer: Podesavanje Geometry Collection za performans
UGeometryCollectionComponent* GCComp = /* ... */;
// Ogranicite broj aktivnih fragmenata
GCComp->SetMaxSimulatedClusters(50);
// Automatsko uklanjanje fragmenata nakon N sekundi
GCComp->bAutoRemove = true;
GCComp->RemoveOnMaxSleepTime = 5.0f; // Ukloni 5 sekundi nakon sto zaspe
36.2.8 Physics LOD -- Pojednostavljivanje Fizike za Daleke Objekte
Isto kao sto smanjujemo vizuelni kvalitet za daleke objekte (mesh LOD, texture streaming), mozemo i trebamo smanjiti kvalitet fizike.
Physics LOD strategije:
-
Disable simulation za daleke objekte -- objekti dalji od odredjene distance se ne simuliraju uopste
-
Pojednostavi collision shape -- blizak objekat koristi convex hull, dalek koristi box
-
Smanji update rate -- blizak objekat se simulira svakog frejma, dalek svaki 3. frejm
-
Iskljuci cloth i destruction na daljini
-
Koristite Significance Manager (objasnicemo kasnije u poglavlju) da automatski upravljate prioritetima
// Primer: Physics LOD sistem
void APhysicsActor::UpdatePhysicsLOD(float DistToCamera)
{
if (DistToCamera > 10000.0f)
{
// Potpuno iskljuci fiziku
MeshComponent->SetSimulatePhysics(false);
MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
else if (DistToCamera > 5000.0f)
{
// Pojednostavljena fizika
MeshComponent->SetSimulatePhysics(true);
MeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
// Koristimo samo query (raycast/overlap), ne punu fizicku simulaciju
}
else
{
// Puna fizika
MeshComponent->SetSimulatePhysics(true);
MeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
}
36.2.9 Async Physics -- Fizika na Zasebnom Thread-u
UE5 podrzava pokretanje fizicke simulacije na zasebnom thread-u, paralelno sa Game Thread-om. Ovo se zove Async Physics.
Bez Async Physics:
Game Thread: [Input][Game Logic][PHYSICS][Animation][...]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sve sekvencijalno, duze ukupno vreme frejma
Sa Async Physics:
Game Thread: [Input][Game Logic][Animation][...]
Physics Thread: [PHYSICS SIMULATION...............]
^
Paralelno -- ustedelo smo vreme na Game Thread-u!
Prednosti:
- Game Thread je oslobodjen od tezine fizicke simulacije
- Vece fizicke scene ne blokiraju gameplay logiku
- Mogucnost pokretanja fizike na fiksnom tick rate-u nezavisno od frame rate-a
Izazovi:
- Latencija -- rezultati fizike mogu biti 1 frejm "kasni" jer se citaju iz prethodnog frejma
- Sinhronizacija -- pristup fizickim podacima iz Game Thread-a zahteva pazljivu sinhronizaciju
- Debugging je tezi jer se stvari desavaju paralelno
Ukljucivanje Async Physics u UE5:
Project Settings > Physics > Async Scene
bEnableAsyncScene = true
Ili per-actor:
// Per-actor async physics
UPrimitiveComponent* Comp = /* ... */;
Comp->BodyInstance.bUseAsyncScene = true;
36.3 Tick Rates i Component Optimization
Zasto je Tick toliko vazan?
Tick je funkcija koja se poziva svakog frejma za svaki aktor i svaku komponentu koja ima tick ukljucen. Na 60 FPS, to znaci 60 poziva u sekundi. Na 120 FPS, 120 poziva.
Ovo zvuci bezazleno, ali hajde da izracunamo:
1000 aktora x Tick svakog frejma x 60 FPS = 60,000 Tick poziva u sekundi
Ako svaki Tick traje 0.01ms:
60,000 x 0.01ms = 0.6ms -- razumno
Ako svaki Tick traje 0.1ms (sto je sasvim moguce za kompleksniju logiku):
60,000 x 0.1ms = 6ms -- znacajno!
Ako svaki Tick traje 0.5ms:
60,000 x 0.5ms = 30ms -- KATASTROFA (samo tick trosi ceo budget za 30 FPS)
36.3.1 Actor Tick
Svaki AActor u UE5 ima Tick funkciju koja se moze pozvati svakog frejma. Default-no, novi aktori imaju tick ukljucen.
// U konstruktoru aktora
AMyActor::AMyActor()
{
// OVO JE DEFAULT -- tick je ukljucen!
PrimaryActorTick.bCanEverTick = true;
// Ovako iskljucujete tick:
PrimaryActorTick.bCanEverTick = false;
}
// Tick funkcija
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Kod koji se izvrsava svakog frejma
}
Problem: Vecina aktora u tipicnoj sceni ne treba da tikuje svakog frejma. Drvo ne treba da tikuje. Kamen ne treba da tikuje. Zid definitivno ne treba da tikuje. Ali ako ste zaboravili da iskljucite tick, oni svi trose CPU vreme.
Pravilo: Tick bi trebalo da bude iskljucen po default-u za sve aktore koji ne trebaju konstantno azuriranje. Ukljucite ga samo kada je zaista potreban.
36.3.2 Component Tick
Svaka komponenta (UActorComponent i njeni potomci) takodje moze da tikuje nezavisno od svog vlasnika (aktora).
Ovo znaci da jedan aktor sa 5 komponenti moze imati 6 tick poziva po frejmu (1 za aktora + 5 za komponente).
Tipicne komponente i da li trebaju tick:
| Komponenta | Treba li Tick? | Razlog |
|---|---|---|
UStaticMeshComponent |
NE | Mesh se ne menja |
USkeletalMeshComponent |
MOZDA | Samo ako ima aktivnu animaciju |
UCharacterMovementComponent |
DA | Mora da procesira kretanje |
UWidgetComponent |
MOZDA | Samo ako se UI menja |
UAudioComponent |
NE (interni) | Audio thread upravlja reprodukcijom |
| Custom logic komponenta | ZAVISI | Samo ako treba per-frame update |
// Iskljucivanje tick-a za komponentu
UMyComponent::UMyComponent()
{
PrimaryComponentTick.bCanEverTick = false;
// ILI, ako ponekad treba tick:
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false; // Pocni sa iskljucenim tick-om
}
// Dinamicko ukljucivanje/iskljucivanje
MyComponent->SetComponentTickEnabled(true); // Ukljuci
MyComponent->SetComponentTickEnabled(false); // Iskljuci
36.3.3 Smanjenje Tick Frequency
Cak i kada aktor ili komponenta mora da tikuje, ne mora to da radi svakog frejma. Mnogi sistemi savrseno rade sa azuriranjem svakih 3, 5, ili 10 frejmova.
Tick Interval
UE5 dozvoljava postavljanje minimalnog intervala izmedju tick-ova:
// Tick svakih 0.5 sekundi umesto svakog frejma
PrimaryActorTick.TickInterval = 0.5f;
// Tick 10 puta u sekundi
PrimaryActorTick.TickInterval = 0.1f;
Timer-Based umesto Tick-Based
Umesto koriscenja Tick() funkcije, koristite Timer-e za logiku koja ne mora da bude per-frame:
// Umesto Tick-a, koristite Timer
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// Pozovi CheckForEnemies svakih 0.5 sekundi
GetWorldTimerManager().SetTimer(
EnemyCheckTimer,
this,
&AMyActor::CheckForEnemies,
0.5f, // Interval
true // Ponavljaj
);
}
void AMyActor::CheckForEnemies()
{
// Ova logika se izvrsava svakih 0.5 sekundi
// umesto 60-120 puta u sekundi
}
Usteda je dramaticna:
Tick svakog frejma na 60 FPS: 60 poziva/sekundi
Timer svakih 0.5s: 2 poziva/sekundi
Usteda: 96.7%!
36.3.4 Disabling Tick za Daleke/Nevidljive Aktore
Aktori koji su daleko od igraca ili koji nisu vidljivi na ekranu cesto ne trebaju da tikuju uopste.
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Proveri da li je igrac dovoljno blizu
float DistToPlayer = GetDistanceTo(GetWorld()->GetFirstPlayerController()->GetPawn());
if (DistToPlayer > 5000.0f)
{
// Previse daleko -- iskljuci tick i cekaj
SetActorTickEnabled(false);
// Postavi timer da periodicno proverava distance
GetWorldTimerManager().SetTimer(
DistanceCheckTimer,
this,
&AMyActor::CheckDistanceToPlayer,
2.0f, // Proveri svakih 2 sekunde
true
);
return;
}
// Normalna logika
DoGameplayLogic(DeltaTime);
}
void AMyActor::CheckDistanceToPlayer()
{
float DistToPlayer = GetDistanceTo(GetWorld()->GetFirstPlayerController()->GetPawn());
if (DistToPlayer <= 5000.0f)
{
// Igrac je dovoljno blizu -- ukljuci tick ponovo
SetActorTickEnabled(true);
GetWorldTimerManager().ClearTimer(DistanceCheckTimer);
}
}
36.3.5 Significance Manager
UE5 ima ugradjeni sistem zvani Significance Manager (USignificanceManager) koji automatizuje upravljanje prioritetima za aktore na osnovu njihovog "znacaja" za igraca.
Kako radi:
-
Svaki aktor moze da registruje funkciju koja racuna njegov "significance" (znacaj) -- tipicno bazirano na rastojanju od kamere, vidljivosti, gameplay relevantnosti
-
Significance Manager sortira sve registrovane aktore po znacaju
-
Na osnovu znacaja, sistemi mogu da odluce sta da rade -- da li da tikuju, koliko cesto, da li da simuliraju fiziku, itd.
// Registrovanje aktora u Significance Manager
void AMyActor::BeginPlay()
{
Super::BeginPlay();
if (USignificanceManager* SM = FSignificanceManagerModule::Get(GetWorld()))
{
SM->RegisterObject(
this,
"MyActorTag",
// Significance funkcija
[](USignificanceManager::FManagedObjectInfo* ObjectInfo,
const FTransform& ViewPoint) -> float
{
AActor* Actor = Cast<AActor>(ObjectInfo->GetObject());
if (!Actor) return 0.0f;
float Distance = FVector::Dist(Actor->GetActorLocation(), ViewPoint.GetLocation());
// Veci znacaj za blize objekte (invertovano rastojanje)
return FMath::Max(0.0f, 1.0f - (Distance / 10000.0f));
},
// Post-significance callback
[](USignificanceManager::FManagedObjectInfo* ObjectInfo,
float OldSignificance, float NewSignificance, bool bFinal)
{
AActor* Actor = Cast<AActor>(ObjectInfo->GetObject());
if (!Actor) return;
if (NewSignificance > 0.7f)
{
// Visok znacaj - pun tick
Actor->SetActorTickInterval(0.0f);
}
else if (NewSignificance > 0.3f)
{
// Srednji znacaj - sporiji tick
Actor->SetActorTickInterval(0.2f);
}
else
{
// Nizak znacaj - iskljuci tick
Actor->SetActorTickEnabled(false);
}
}
);
}
}
void AMyActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (USignificanceManager* SM = FSignificanceManagerModule::Get(GetWorld()))
{
SM->UnregisterObject(this);
}
Super::EndPlay(EndPlayReason);
}
Significance Manager je posebno koristan u igrama sa otvorenim svetom gde imate hiljade aktora, od kojih su samo desetine zaista relevantni za igraca u svakom trenutku.
36.4 Blueprint vs C++ Overhead
Fundamentalna razlika
Blueprint Visual Scripting u UE5 je neverovatno mocan alat za prototipizaciju i razvoj gameplay-a. Ali postoji cena.
Blueprint-i se izvrsavaju kroz Virtual Machine (VM):
Kada napravite Blueprint, UE5 ga kompajlira u bytecode koji se izvrsava kroz Blueprint VM. Ovo je slicno tome kako se Java izvrsava kroz JVM, ili Python kroz svoj interpreter.
C++ se kompajlira direktno u masinski kod:
C++ kod se kompajlira u native masinske instrukcije koje CPU direktno izvrsava. Nema interpretacije, nema VM overhead-a.
Blueprint izvrsavanje:
Blueprint Node --> Bytecode --> VM interpretacija --> Rezultat
^
|
Overhead ovde!
C++ izvrsavanje:
C++ Source --> Kompajler --> Masinski kod --> CPU --> Rezultat
^
|
Direktno, bez overhead-a
36.4.1 Kolika je razlika u performansu?
Razlika zavisi od tipa operacije:
| Operacija | Blueprint | C++ | Razlika |
|---|---|---|---|
| Jednostavno racunanje (a + b) | ~0.001ms | ~0.00001ms | ~100x |
| Poziv funkcije | ~0.005ms | ~0.0001ms | ~50x |
| For petlja (1000 iteracija) | ~0.5ms | ~0.005ms | ~100x |
| Array operacije (Sort 1000 elemenata) | ~2ms | ~0.02ms | ~100x |
| Spawn jednog aktora | ~1ms | ~0.8ms | ~1.25x |
| Pristup property-ju | ~0.003ms | ~trivijalno | ~30x |
Kljucna opservacija: Razlika je ogromna za ciste racunske operacije, ali se smanjuje za operacije koje dominira engine internalni rad (spawn, load, rendering pozivi). Spawn aktora kosta skoro isto iz Blueprint-a i C++-a jer vecinu vremena trose iste interne engine funkcije.
36.4.2 Kada Blueprint Overhead Zaista Boli
Blueprint overhead postaje problem u tri situacije:
1. Mnogo instanci
Ako imate jednog NPC-a sa Blueprint tick-om, overhead je zanemarljiv. Ali ako imate 500 NPC-ova sa Blueprint tick-om, svaki frejm se poziva 500 Blueprint tick funkcija, svaka sa VM overhead-om.
1 NPC Blueprint Tick: 0.01ms overhead -- zanemarljivo
500 NPC Blueprint Tick: 5ms overhead -- znacajno!
500 NPC C++ Tick: 0.05ms overhead -- zanemarljivo
2. Kompleksna logika u Tick-u
Ako vas Blueprint tick ima 3 cvora, overhead je minimalan. Ali ako ima 200 cvorova sa petljama, grananjima, array operacijama -- overhead se akumulira.
Blueprint Tick sa 3 cvora: ~0.01ms per instance
Blueprint Tick sa 200 cvorova: ~0.5ms per instance
500 instanci x 0.5ms = 250ms per frame -- KATASTROFA
3. Tight Loops (Petlje sa mnogo iteracija)
For-each petlja u Blueprint-u je posebno skupa jer svaka iteracija ima VM overhead:
// C++ -- jedna for petlja, trivijalna cena
for (int i = 0; i < 10000; i++)
{
Sum += Array[i];
}
// Vreme: ~0.01ms
// Blueprint -- svaka iteracija prolazi kroz VM
// ForEachLoop na Array od 10000 elemenata
// Vreme: ~5ms (500x sporije!)
36.4.3 Nativization -- Konvertovanje Blueprint-a u C++
UE4 je imao funkcionalnost zvanu Blueprint Nativization koja je mogla da konvertuje Blueprint logiku u C++ kod tokom cook procesa. Ovo je znacajno smanjivalo Blueprint overhead.
U UE5, nativization je depreciran (uklonjen iz standardnog workflow-a). Razlog: Unreal-ov tim je fokusiran na poboljsanje Blueprint VM performansa i na koriscenje drugih pristupa.
Alternativni pristupi u UE5:
- Rucna konverzija -- prepisite kriticne delove iz Blueprint-a u C++
- Hybrid pristup -- koristite C++ za baznu klasu sa optimizovanom logikom, Blueprint za podklase sa specificnom konfiguracijom
- Blueprint Function Libraries u C++ -- napisite performansno kriticne funkcije u C++, pozovite ih iz Blueprint-a
// Primer: C++ bazna klasa sa optimizovanom logikom
UCLASS()
class AOptimizedNPC : public ACharacter
{
GENERATED_BODY()
public:
// Tick u C++ -- brz
virtual void Tick(float DeltaTime) override
{
Super::Tick(DeltaTime);
// Skupa logika u C++ -- brza
UpdateAI(DeltaTime);
UpdatePathfinding(DeltaTime);
UpdateCombat(DeltaTime);
}
protected:
// Blueprint moze da override-uje specificno ponasanje
UFUNCTION(BlueprintImplementableEvent, Category = "NPC")
void OnStateChanged(ENPCState NewState);
// Skupa interna logika ostaje u C++
void UpdateAI(float DeltaTime);
void UpdatePathfinding(float DeltaTime);
void UpdateCombat(float DeltaTime);
};
36.4.4 Pravilo: Prototip u BP, Optimizuj u C++
Ovo je industrijski standard i valja ga ponoviti:
WORKFLOW:
1. PROTOTIPIZACIJA (Blueprint)
- Brzo iterirajte na gameplay ideji
- Testirajte mehanike
- Ne brinite o performansu
2. VALIDACIJA
- Da li mehanika radi kako treba?
- Da li je zabavna?
- Ako NE -- bacite i pocnite iznova (jeftino u BP!)
3. PROFILISANJE
- Da li Blueprint overhead uzrokuje probleme?
- stat game, stat Blueprint -- proverite
- Ako NE -- ostavite u Blueprint-u, ne optimizujte nepotrebno!
4. OPTIMIZACIJA (C++)
- Samo za delove koji su profilisanjem identifikovani kao problem
- Prepisite hot path (najcesci/najskuplji put izvrsavanja) u C++
- Ostavite retke/jednostavne operacije u Blueprint-u
Ne prepisujte sve u C++ "za svaki slucaj"! To je prematura optimizacija koja trosira vreme, komplikuje rad dizajnerima, i cesto donosi zanemarljivo poboljsanje. Optimizujte samo ono sto profiler pokaze kao problem.
36.5 Actor Lifecycle -- Spawn, Destroy i Object Pooling
36.5.1 Spawning Cost
Kreiranje (spawning) novog aktora u UE5 nije besplatno. Engine mora da:
- Alocira memoriju za aktor i sve njegove komponente
- Konstruise aktor (pozove konstruktor)
- Inicijalizuje sve komponente
- Registruje aktor u World-u
- Registruje sve komponente za tick, rendering, fiziku, itd.
- Pozove BeginPlay za aktor i sve komponente
Za jednostavan aktor (StaticMeshComponent + BoxCollision), spawn kosta mozda 0.1-0.5ms. Ali za kompleksan aktor (SkeletalMesh + AnimBlueprint + AIController + NavigationComponent + mnogo custom komponenti), spawn moze da kosta 2-10ms.
Tipicna cena spawn-a:
Prazan aktor: ~0.05ms
Aktor sa StaticMesh + Collision: ~0.1-0.5ms
Character sa AI: ~2-5ms
Kompleksan Vehicle: ~5-10ms
Ako spawn-ujete 20 projektila odjednom (shotgun blast), to moze biti 2-10ms samo na spawn-ovanje.
36.5.2 Destroying Cost
Unistavanje aktora takodje nije besplatno:
- Poziva se EndPlay i BeginDestroy
- Sve komponente se deregistruju
- Fizicka tela se uklanjaju iz scene
- Aktor se uklanja iz World-a
- Memorija se oznacava za Garbage Collection (ali se ne oslobadja odmah)
Sam destroy je tipicno brzi od spawn-a, ali prave probleme pravi Garbage Collection koji se desava kasnije.
36.5.3 Garbage Collection Pauses
UE5 koristi Garbage Collector (GC) za upravljanje memorijom UObject-a (sto ukljucuje aktore, komponente, asset-e, itd.).
Kako GC radi:
- GC povremeno skenira sve UObject-e da pronadje one koji vise nemaju reference (niko ih ne koristi)
- Objekti bez referenci se oznacavaju za brisanje
- GC brise te objekte i oslobadja memoriju
Problem: GC skeniranje je stop-the-world operacija -- Game Thread se pauzira dok GC ne zavrsi. Ova pauza moze biti od 1ms do 30ms+ u zavisnosti od broja objekata.
GC Pauza
|<------>|
Game Thread: [Frame][Frame][GC STOP][Frame][Frame]
^^^^^^^^
Igrac vidi "hitch" ovde!
Faktori koji uticu na GC pauze:
| Faktor | Uticaj |
|---|---|
| Ukupan broj UObject-a u memoriji | Vise objekata = duze skeniranje |
| Broj objekata za brisanje | Vise brisanja = duze |
| Frekvencija spawn/destroy ciklusa | Cesca destrukcija = cesce GC pauze |
| Kompleksnost referentnog grafa | Dublje reference = sporije skeniranje |
Kako minimizovati GC pauze:
- Koristite Object Pooling (vidite ispod)
- Smanjite frekvenciju spawn/destroy operacija
- Koristite incremental GC -- UE5 podrzava inkrementalni GC koji raspodjeljuje rad kroz vise frejmova
- Rucno kontrolisite GC timing -- pozovite GC u momentima kada igrac nece primetiti pauzu (loading screen, cutscena)
- Smanjite ukupan broj UObject-a -- koristite struct-ove (USTRUCT) umesto UObject-a gde je moguce, jer struct-ovi ne prolaze kroz GC
// Rucno pozivanje GC u pogodnom momentu
void AMyGameMode::OnLevelTransitionStart()
{
// Prikazujemo loading screen, idealan moment za GC
GEngine->ForceGarbageCollection(true);
}
// Podesavanje GC parametara
// U DefaultEngine.ini:
// [/Script/Engine.GarbageCollectionSettings]
// gc.TimeBetweenPurgingPendingKillObjects=30 // Koliko cesto se vrsi GC (sekunde)
// gc.MaxObjectsNotConsideredByGC=1 // Objekti ispod ovog broja se ne proveravaju
36.5.4 Object Pooling
Object pooling je tehnika gde umesto da spawn-ujete i destroy-ujete objekte, vi ih reciklirate.
Ideja je prosta:
- Na pocetku igre (ili nivoa), spawn-ujete odredjeni broj objekata unapred
- Kada vam treba objekat, umesto spawn-a, uzimate jedan iz "pool-a" i aktivirate ga
- Kada vam objekat vise ne treba, umesto destroy-a, deaktivirate ga i vracate u pool
BEZ POOLING-A:
Treba metak? --> Spawn() --> [Koristite] --> Destroy() --> GC brise
Treba metak? --> Spawn() --> [Koristite] --> Destroy() --> GC brise
Treba metak? --> Spawn() --> [Koristite] --> Destroy() --> GC brise
(Svaki ciklus kosta spawn + destroy + GC overhead)
SA POOLING-OM:
Inicijalizacija: Spawn 50 metaka, deaktiviraj ih
Treba metak? --> Pool.Get() --> Aktiviraj --> [Koristite] --> Deaktiviraj --> Pool.Return()
Treba metak? --> Pool.Get() --> Aktiviraj --> [Koristite] --> Deaktiviraj --> Pool.Return()
(Nema spawn/destroy/GC overhead-a tokom gameplay-a!)
Implementacija Object Pool-a u UE5:
UCLASS()
class AObjectPool : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
TSubclassOf<AActor> PooledActorClass;
UPROPERTY(EditAnywhere)
int32 PoolSize = 50;
private:
TArray<AActor*> AvailableActors;
TArray<AActor*> ActiveActors;
public:
void InitializePool()
{
for (int32 i = 0; i < PoolSize; i++)
{
AActor* NewActor = GetWorld()->SpawnActor<AActor>(PooledActorClass);
NewActor->SetActorHiddenInGame(true);
NewActor->SetActorEnableCollision(false);
NewActor->SetActorTickEnabled(false);
AvailableActors.Add(NewActor);
}
}
AActor* GetFromPool(FVector Location, FRotator Rotation)
{
if (AvailableActors.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("Object Pool prazan! Razmatrajte povecanje PoolSize."));
return nullptr; // Ili spawn novog kao fallback
}
AActor* Actor = AvailableActors.Pop();
Actor->SetActorLocation(Location);
Actor->SetActorRotation(Rotation);
Actor->SetActorHiddenInGame(false);
Actor->SetActorEnableCollision(true);
Actor->SetActorTickEnabled(true);
// Pozovite custom "OnActivated" ako postoji
if (IPoolable* Poolable = Cast<IPoolable>(Actor))
{
Poolable->OnActivatedFromPool();
}
ActiveActors.Add(Actor);
return Actor;
}
void ReturnToPool(AActor* Actor)
{
Actor->SetActorHiddenInGame(true);
Actor->SetActorEnableCollision(false);
Actor->SetActorTickEnabled(false);
// Pozovite custom "OnDeactivated" ako postoji
if (IPoolable* Poolable = Cast<IPoolable>(Actor))
{
Poolable->OnDeactivatedToPool();
}
ActiveActors.Remove(Actor);
AvailableActors.Add(Actor);
}
};
// Interface za poolable aktore
UINTERFACE(MinimalAPI)
class UPoolable : public UInterface
{
GENERATED_BODY()
};
class IPoolable
{
GENERATED_BODY()
public:
virtual void OnActivatedFromPool() = 0;
virtual void OnDeactivatedToPool() = 0;
};
Sta se tipicno pool-uje:
| Objekat | Razlog za pooling |
|---|---|
| Projektili (meci, rakete) | Spawn/destroy stotine u sekundi |
| Particle efekti | Cest spawn, kratak zivot |
| Sound actors | Mnogo zvukova koji se kratkotrajno pustaju |
| Pickup items | Spawn/collect ciklus |
| Decals (rupe od metaka, mrlje) | Mnogo malih objekata |
| UI elementi (floating damage numbers) | Cest spawn, kratak zivot |
| NPC-ovi (u nekim slucajevima) | Recikliranje za open world igre |
36.6 Async Loading -- Ucitavanje Asset-a Bez Zastoja
36.6.1 Problem sa Sinhronim Ucitavanjem
Kada UE5 mora da ucita asset (mesh, teksturu, zvuk, Blueprint) sa diska, default ponasanje je sinhrono ucitavanje -- Game Thread ceka dok se asset ne ucita.
Sinhrono ucitavanje:
Game Thread: [Frame][Frame][LOADING...CEKAM...][Frame][Frame]
^^^^^^^^^^^^^^^^^^
HITCH! Igra se zamrznula!
Tipicni slucajevi kada se ovo desava:
- Spawn aktora koji referencira mesh koji nije u memoriji
- Ulazak u novu oblast gde su potrebni novi asset-i
- Pristup asset-u koji je soft-referenciran ali nije ucitan
Za male asset-e (par KB), ovo je neprimtno. Ali za veliki mesh (50MB) ili veliku teksturu (256MB), igra moze da se "zamrzne" na stotine milisekundi -- sto je apsolutno neprihvatljivo.
36.6.2 Async Loading
Resenje je asinhrono ucitavanje -- asset se ucitava u pozadini dok igra nastavlja da radi normalno.
Asinhrono ucitavanje:
Game Thread: [Frame][Frame][Frame][Frame][Frame][Asset spreman!]
Loading Thread: [LOADING..........................]
^ ^
Pocelo ucitavanje Ucitavanje zavrseno
(ne blokira igru!) (callback)
Kako koristiti async loading u UE5:
// Asinhrono ucitavanje pojedinacnog asset-a
void AMyActor::LoadWeaponAsync()
{
// Soft reference na mesh -- NE ucitava automatski
TSoftObjectPtr<UStaticMesh> WeaponMeshRef =
TSoftObjectPtr<UStaticMesh>(FSoftObjectPath("/Game/Weapons/Rifle.Rifle"));
// Pokretanje async load-a
FStreamableManager& Streamable = UAssetManager::GetStreamableManager();
Streamable.RequestAsyncLoad(
WeaponMeshRef.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &AMyActor::OnWeaponLoaded)
);
}
void AMyActor::OnWeaponLoaded()
{
// Asset je ucitan -- sada ga mozemo koristiti
UStaticMesh* WeaponMesh = WeaponMeshRef.Get();
if (WeaponMesh)
{
MeshComponent->SetStaticMesh(WeaponMesh);
}
}
Async loading vise asset-a odjednom:
void AMyActor::LoadMultipleAssetsAsync()
{
TArray<FSoftObjectPath> AssetsToLoad;
AssetsToLoad.Add(FSoftObjectPath("/Game/Meshes/Building01.Building01"));
AssetsToLoad.Add(FSoftObjectPath("/Game/Textures/Building01_Diffuse.Building01_Diffuse"));
AssetsToLoad.Add(FSoftObjectPath("/Game/Sounds/Ambient_Wind.Ambient_Wind"));
FStreamableManager& Streamable = UAssetManager::GetStreamableManager();
Streamable.RequestAsyncLoad(
AssetsToLoad,
FStreamableDelegate::CreateUObject(this, &AMyActor::OnAllAssetsLoaded)
);
}
void AMyActor::OnAllAssetsLoaded()
{
// SVI asset-i su ucitani -- sada ih mozemo koristiti
UE_LOG(LogTemp, Log, TEXT("Svi asset-i su ucitani!"));
}
36.6.3 Soft References vs Hard References
Razlika izmedju soft i hard referenci je kljucna za razumevanje loading-a u UE5.
Hard References
Hard reference (cvrsta referenca) znaci da se referencirani asset automatski ucitava kada se ucita vlasnik.
// Hard reference -- WeaponMesh se ucitava AUTOMATSKI kada se ucita ovaj aktor
UPROPERTY(EditAnywhere)
UStaticMesh* WeaponMesh;
// Hard reference na klasu -- Blueprint klasa se ucitava AUTOMATSKI
UPROPERTY(EditAnywhere)
TSubclassOf<AProjectile> ProjectileClass;
Problem sa hard referencama: Ako Actor A ima hard referencu na Mesh B, koji ima hard referencu na Material C, koji ima hard referencu na Texture D... ucitavanje Actor A povlaci ucitavanje svega u lancu. Ovo se zove reference chain i moze dovesti do toga da ucitavanje jednog malog Blueprint-a povuce ucitavanje stotine megabajta asset-a.
Hard Reference Chain (problem):
AMyActor
|-- WeaponMesh (30MB)
| |-- Material (referencira 5 tekstura, 200MB)
|
|-- ProjectileClass
| |-- ProjectileMesh (5MB)
| |-- ExplosionEffect (50MB)
| | |-- ExplosionSound (10MB)
|
Total: 295MB ucitano samo zato sto ste ucitali AMyActor!
Soft References
Soft reference (meka referenca) je samo putanja do asset-a. Asset se ne ucitava automatski.
// Soft reference -- NE ucitava automatski
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UStaticMesh> WeaponMeshSoft;
// Soft reference na klasu
UPROPERTY(EditAnywhere)
TSoftClassPtr<AProjectile> ProjectileClassSoft;
Sa soft referencama, vi kontrolisete kada se asset ucitava:
void AMyActor::NeedWeapon()
{
if (WeaponMeshSoft.IsValid())
{
// Vec je ucitan -- koristite ga odmah
UStaticMesh* Mesh = WeaponMeshSoft.Get();
MeshComponent->SetStaticMesh(Mesh);
}
else
{
// Nije ucitan -- pokrenite async load
LoadWeaponAsync();
}
}
Pravilo:
| Situacija | Koristite |
|---|---|
| Asset koji UVEK treba (mesh igraca) | Hard reference |
| Asset koji PONEKAD treba (posebno oruzje) | Soft reference + async load |
| Asset koji treba KASNIJE (sledeci nivo) | Soft reference + preload |
| Asset koji treba samo u EDITORU | Soft reference |
Vise o streaming sistemu i kako on koristi soft reference mozete naci u Poglavlju 32 (World Partition i Streaming).
36.7 Multithreading Awareness
36.7.1 Zasto je multithreading vazan za performans
Moderni procesori imaju 8, 12, 16 ili cak vise jezgara. Ako vas program koristi samo jedno jezgro, gubite 87-94% raspolozive racunarske snage procesora.
UE5 je dizajniran da koristi vise thread-ova (niti izvrsavanja) paralelno. Razumevanje kako ovi thread-ovi rade i kako interaguju je kljucno za razumevanje performansa.
36.7.2 Glavni Thread-ovi u UE5
UE5 ima tri "velika" thread-a koji su uvek aktivni:
1. Game Thread
Game Thread je "glavni" thread. On izvrsava:
- Gameplay logiku (Tick funkcije)
- Blueprint izvrsavanje
- AI logiku
- Input procesiranje
- Animation update (odredjivanje koje animacije da se puste)
- Audio komande (ali ne procesiranje)
- Kretanje karaktera (Character Movement)
Ovo je tipicno najoptereceniji thread jer na njemu radi najvise razlicitih sistema.
2. Render Thread
Render Thread priprema rendering komande:
- Prolazi kroz vidljive objekte
- Priprema draw call-ove
- Upravlja materijalima i shader-ima
- Salje komande RHI thread-u
Render Thread radi 1 frejm iza Game Thread-a -- dok Game Thread priprema frejm N+1, Render Thread renderuje frejm N.
3. RHI Thread (Rendering Hardware Interface)
RHI Thread komunicira direktno sa grafickim API-jem (DirectX 12, Vulkan):
- Salje draw call-ove GPU-ju
- Upravlja GPU resursima (buffer-i, teksture)
- Sinhronizuje sa GPU-jem
Vizuelizacija pipeline-a (3 frejma):
Vreme ---->
Game Thread: [Frame 3 Logic][Frame 4 Logic][Frame 5 Logic]
Render Thread: [Frame 2 Prep ][Frame 3 Prep ][Frame 4 Prep ]
RHI Thread: [Frame 1 Submit][Frame 2 Submit][Frame 3 Submit]
GPU: [Frame 0 Render][Frame 1 Render][Frame 2 Render]
36.7.3 Physics Thread
Kao sto smo pomenuli u sekciji o Async Physics (36.2.9), fizicka simulacija moze da radi na zasebnom thread-u.
Game Thread: [Logic][Logic][Logic]
Physics Thread: [Sim ][Sim ][Sim ] <-- paralelno sa Game Thread-om
Ovo je posebno korisno za igre sa intenzivnom fizikom (destruction, mnogo ragdoll-ova, simulacija vozila).
36.7.4 Audio Thread
Audio procesiranje se odvija na zasebnom thread-u (Audio Render Thread):
Game Thread: [Logic + Audio Commands][...]
Audio Thread: [Decode + Mix + Spatialize][Output to Device]
Audio Thread ima strog vremenski rok -- mora da isporuci audio buffer pre nego sto zvucna kartica zatrazi sledeci. Ako ne stigne, cujete audio glitch (pucketanje, preskakanje).
36.7.5 Kada Thread-ovi "Takmice" za CPU Vreme
Svaki thread zahteva CPU jezgro za izvrsavanje. Na procesoru sa 8 jezgara, mozete imati 8 thread-ova koji rade istovremeno bez problema. Ali ako imate vise aktivnih thread-ova nego jezgara, operativni sistem mora da vrsi context switching -- prebacivanje izmedju thread-ova, sto ima svoj overhead.
Tipicanscenario problema:
8-core CPU:
Game Thread: [jezgro 1]
Render Thread: [jezgro 2]
RHI Thread: [jezgro 3]
Physics Thread: [jezgro 4]
Audio Thread: [jezgro 5]
Task Graph (3): [jezgro 6, 7, 8]
= Sva jezgra zauzeta, ali radi OK
Problem nastaje kada:
- Game Thread treba 15ms ali ima budget od 16.67ms (60 FPS)
- Physics Thread takodje treba 15ms
- Oba se takmic za CPU jezgra
- Context switching dodaje overhead
- Rezultat: frame time prelazi 16.67ms -> pad ispod 60 FPS
Dijagnostika:
Koristite UE5 Profiler ili stat Threading da vidite koliko svaki thread trosira vremena:
// U konzoli igre
stat Threading
stat Game // Game Thread vreme
stat GPU // Render/GPU vreme
Kljucno pitanje pri profilisanju:
- Ako je Game Thread najsporiji -> optimizujte gameplay logiku, tick-ove, Blueprint-e
- Ako je Render Thread najsporiji -> optimizujte draw call-ove, materijale (pogledajte prethodna poglavlja)
- Ako je GPU najsporiji -> optimizujte shadere, rezoluciju, geometriju
- Ako je Physics Thread najsporiji -> optimizujte fiziku (ovaj deo poglavlja)
Vas frame rate je ogranicen NAJSPORIJIM thread-om! Nema koristi od optimizacije GPU-a ako je Game Thread bottleneck.
36.7.6 UE5 Task Graph
Pored "velikih" thread-ova, UE5 koristi Task Graph sistem za paralelizaciju manjih poslova.
Task Graph je sistem gde:
- Razliciti sistemi kreiraju task-ove (male jedinice posla)
- Task-ovi definisu zavisnosti (task B ne moze da pocne dok task A ne zavrsi)
- Task Graph scheduler automatski rasporedjuje task-ove na dostupna CPU jezgra
Task Graph primer:
[Task A: Update AI za NPC grupe 1-100]
|
+---------+---------+
| |
[Task B: Pathfinding [Task C: Pathfinding
za NPC 1-50] za NPC 51-100]
| |
+---------+---------+
|
[Task D: Finalize AI decisions]
Prednosti Task Graph-a:
- Automatska paralelizacija -- ne morate rucno upravljati thread-ovima
- Work stealing -- ako jedno jezgro zavrsi svoje task-ove, ono "krade" task-ove od drugih
- Definisane zavisnosti -- garantovano korektan redosled izvrsavanja
- Skalabilnost -- automatski koristi onoliko jezgara koliko je dostupno
Sistemi koji koriste Task Graph:
- Animation evaluation (svaki lik se evaluira kao zaseban task)
- Mesh draw command preparation
- Cloth simulacija
- Navigation mesh update-ovi
- Async loading operacije
// Primer koriscenja Task Graph-a za custom paralelizaciju
void AMyManager::ProcessEntitiesParallel()
{
const int32 NumEntities = Entities.Num();
const int32 BatchSize = 64; // Svaki task procesira 64 entiteta
// Kreiraj task-ove
FGraphEventArray CompletionEvents;
for (int32 i = 0; i < NumEntities; i += BatchSize)
{
int32 StartIndex = i;
int32 EndIndex = FMath::Min(i + BatchSize, NumEntities);
CompletionEvents.Add(FFunctionGraphTask::CreateAndDispatchWhenReady(
[this, StartIndex, EndIndex]()
{
for (int32 j = StartIndex; j < EndIndex; j++)
{
// Procesiranje entiteta -- ovo se izvrsava paralelno!
Entities[j]->UpdateLogic();
}
},
TStatId(),
nullptr, // Nema zavisnosti
ENamedThreads::AnyThread
));
}
// Sacekaj da svi task-ovi zavrse
FTaskGraphInterface::Get().WaitUntilTasksComplete(CompletionEvents);
}
Napomena o thread safety: Kada koristite Task Graph (ili bilo koji vid paralelizma), morate paziti na race condition-e -- situacije gde dva thread-a istovremeno pristupaju istim podacima. UE5 nudi razne mehanizme za zastitu:
FCriticalSection(mutex)FRWLock(read-write lock)TAtomic(atomicke operacije)TQueue(thread-safe red)
Ali idealno, dizajnirajte task-ove tako da svaki radi na svojim podacima bez deljenja, cime se izbegava potreba za sinhronizacijom.
36.8 Prakticni Primeri i Scenarija
Scenario 1: "Igra stuca ali GPU je na 30%"
Simptomi: Frame rate je nizak ili nestabilan. GPU utilizacija je niska.
Dijagnoza: Problem je na CPU strani -- Game Thread ili Physics Thread.
Koraci:
stat Game-- proverite Game Thread vremestat Physics-- proverite Physics vremestat SceneRendering-- proverite Render Thread vreme- Ako je Game Thread problem:
stat Tick-- koliko tick-ova se izvrsava?stat Blueprint-- da li Blueprint-i trose previse?- Koristite Unreal Insights za detaljniji profiling
Scenario 2: "Kada ima mnogo neprijatelja, igra uspori"
Verovatni uzroci:
- Previse Actor Tick-ova (svaki NPC tikuje)
- Blueprint AI logika je preskupa za mnogo instanci
- Navigation/Pathfinding preopterecen
- Previse skeletal mesh animacija
Resenja:
- Significance Manager -- daleki NPC-ovi tikuju redje
- Prebacite AI hot path u C++
- Ogranicite istovremene pathfinding zahteve
- Animation LOD -- daleki NPC-ovi koriste jednostavnije animacije ili se ne animiraju
Scenario 3: "Audio pucka i preskace"
Uzrok: Audio Thread ne stize da procesira audio buffer na vreme.
Resenja:
- Smanjite Max Channels (manje istovremenih zvukova)
- Iskljucite skupe audio efekte (convolution reverb)
- Smanjite audio occlusion frekvenciju
- Pojednostavite MetaSounds grafove
- Proverite da li Audio Thread nema dovoljno CPU vremena (previse drugih thread-ova)
Scenario 4: "Igra se zamrzne na pola sekunde kada udjem u novu oblast"
Uzrok: Sinhrono ucitavanje asset-a.
Resenja:
- Prebacite na async loading
- Koristite soft reference umesto hard reference
- Preload-ujte asset-e pre nego sto igrac stigne u oblast
- Koristite Level Streaming (Poglavlje 32) za streaming celih poddlevoa
Scenario 5: "Destrukcija zidova ubija frame rate"
Uzrok: Chaos Destruction generise previse fizickih tela.
Resenja:
- Smanjite broj fragmenata (krupnije lomljenje)
- Koristite hijerarhijsko lomljenje
- Fragmenti nestaju nakon par sekundi
- Ogranicite maksimalan broj aktivnih destruction tela
- Daleki objekti se lome na jos manje fragmenata
36.9 Checklist za Non-Rendering Performance
Pre nego sto zavrsite optimizaciju, prodjite kroz ovaj checklist:
Audio Checklist
- Max Channels je podeseno na razumnu vrednost za ciljanu platformu
- Audio occlusion je ukljucena samo za bitne zvukove
- Occlusion check interval nije svaki frejm
- Convolution reverb se koristi stedljivo
- MetaSounds grafovi su optimizovani (nema nepotrebne kompleksnosti)
- Audio LOD je implementiran za daleke zvukove
-
stat Audiopokazuje razumne brojeve
Physics Checklist
- Svi objekti koriste najjednostavniji moguci collision shape
- Triangle mesh collision se koristi samo za staticnu geometriju
- Collision channel-i su pravilno podeseni (ne proveravaju se nepotrebni parovi)
- Ragdoll-ovi se ogranicavaju i forsirano uspavljuju
- Cloth simulacija ima LOD
- Destruction sistema ima ogranicenje na broj fragmenata
- Physics LOD je implementiran za daleke objekte
-
stat Physicspokazuje razumna vremena
Tick Checklist
-
bCanEverTick = falseje default za sve aktore koji ne trebaju tick - Komponente koje ne trebaju tick imaju tick iskljucen
- Tick interval je povecen gde je moguce
- Timer-i se koriste umesto Tick-a za periodicnu logiku
- Daleki/nevidljivi aktori ne tikuju
- Significance Manager je implementiran za velike svetove
Blueprint vs C++ Checklist
- Hot path logika (cest poziv, mnogo instanci) je u C++
- Blueprint tight loops su eliminisane ili prebacene u C++
- Hybrid pristup (C++ baza, BP podklase) je koriscen
-
stat Blueprintne pokazuje alarmantan overhead
Actor Lifecycle Checklist
- Object pooling je implementiran za cesto spawn-ovane objekte
- Spawn/destroy ciklus je minimizovan
- GC pauze su proverene i u prihvatljivom opsegu
- Hard reference chain-ovi su provereni (Size Map u editoru)
Loading Checklist
- Kriticki asset-i koriste hard reference (uvek dostupni)
- Opcioni asset-i koriste soft reference + async load
- Nema sinhronog ucitavanja tokom gameplay-a
- Asset preloading je implementiran za predvidljive situacije
Multithreading Checklist
- Poznato je koji thread je bottleneck (Game, Render, GPU, Physics)
- Optimizacija je fokusirana na bottleneck thread
- Async physics je razmatran za fizicki intenzivne igre
- Task Graph se koristi za paralelizovane sisteme
36.10 Tabela Kljucnih Pojmova
| Pojam (EN) | Opis (SR) |
|---|---|
| Voice Count | Broj istovremeno aktivnih zvukova u audio engine-u |
| Audio Occlusion | Simulacija blokiranja zvuka fizickim objektima (koristeci raycast) |
| MetaSounds | UE5 sistem za proceduralno generisanje zvuka u realnom vremenu |
| Audio LOD | Smanjenje kvaliteta audio procesiranja za daleke zvucne izvore |
| Collision Shape | Geometrijski oblik koji se koristi za detekciju kolizije (sphere, box, capsule, convex hull, triangle mesh) |
| Broad Phase | Prva faza detekcije kolizije -- brza eliminacija parova koji se sigurno ne dodiruju |
| Narrow Phase | Druga faza detekcije kolizije -- precizna provera za parove koji su prosli broad phase |
| Physics Substeps | Vissestruko izvrsavanje fizicke simulacije po frejmu za vecu preciznost |
| Ragdoll | Simulacija tela kao niza rigid body-ja povezanih zglobovima |
| Chaos Cloth | UE5 sistem za simulaciju tkanine kao mreze cestica i opruga |
| Chaos Destruction | UE5 sistem za razaranje objekata lomljenje mesh-ova na fragmente |
| Physics LOD | Pojednostavljivanje ili iskljucivanje fizicke simulacije za daleke objekte |
| Async Physics | Pokretanje fizicke simulacije na zasebnom thread-u, paralelno sa Game Thread-om |
| Actor Tick | Funkcija koja se poziva svakog frejma za aktora -- moze biti znacajan performansni trosak |
| Component Tick | Tick funkcija na nivou komponente, nezavisna od tick-a aktora |
| Tick Interval | Minimalno vreme izmedju dva tick poziva -- sluzi za smanjenje frekvencije tick-a |
| Significance Manager | UE5 sistem koji automatski upravlja prioritetom aktora na osnovu njihovog znacaja za igraca |
| Blueprint VM | Virtualna masina koja interpretira Blueprint bytecode -- sporija od native C++ koda |
| Nativization | Proces konverzije Blueprint koda u C++ tokom cook procesa (depreciran u UE5) |
| Object Pooling | Tehnika recikliranja objekata umesto spawn/destroy ciklusa, eliminise GC overhead |
| Garbage Collection (GC) | Automatski sistem za upravljanje memorijom UObject-a -- moze uzrokovati pauze |
| Hard Reference | Direktna referenca na asset koja uzrokuje automatsko ucitavanje tog asset-a |
| Soft Reference | Referenca kao putanja (string) -- asset se ne ucitava automatski, vec na zahtev |
| Async Loading | Ucitavanje asset-a u pozadini bez blokiranja Game Thread-a |
| Game Thread | Glavni thread UE5 koji izvrsava gameplay logiku, Blueprint-e, AI, input |
| Render Thread | Thread koji priprema rendering komande za GPU |
| RHI Thread | Thread koji komunicira sa grafickim API-jem (DX12, Vulkan) |
| Task Graph | UE5 sistem za paralelno izvrsavanje manjih poslova na dostupnim CPU jezgrima |
| Context Switching | Prebacivanje CPU-a izmedju thread-ova -- ima overhead i usporava izvrsavanje |
| Race Condition | Bug gde dva thread-a istovremeno pristupaju istim podacima sa nepredvidljivim rezultatom |
36.11 Korisni Linkovi i Dalje Citanje
Zvanicna Unreal Engine Dokumentacija
- Audio System Overview: docs.unrealengine.com/audio-overview
- MetaSounds Reference: docs.unrealengine.com/metasounds
- Physics Overview (Chaos): docs.unrealengine.com/physics-overview
- Collision Reference: docs.unrealengine.com/collision-overview
- Chaos Destruction: docs.unrealengine.com/chaos-destruction
- Blueprint Performance: docs.unrealengine.com/blueprint-performance
- Asset Management and Streaming: docs.unrealengine.com/asset-management
- Threading Overview: docs.unrealengine.com/threading-overview
- Significance Manager: docs.unrealengine.com/significance-manager
Unreal Engine Blog i Video Resursi
- Unreal Engine Performance Guide -- sveobuhvatan vodic za optimizaciju
- GDC Talks o UE5 performansu -- pretrazite "Unreal Engine optimization GDC" na YouTube-u
- Unreal Fest prezentacije -- cesto pokrivaju napredne optimizacione tehnike
Povezana Poglavlja u Ovoj Knjizi
- Poglavlje 32: World Partition i Streaming -- detaljno o streaming sistemu, level streaming-u i soft/hard referencama u kontekstu velikih svetova
- Poglavlje 49: CPU Optimization -- napredne tehnike CPU optimizacije, ukljucujuci detaljniji rad sa Task Graph-om, SIMD instrukcijama i memory layout optimizacijom
Rezime Poglavlja
Performans igre nije samo pitanje renderinga. U ovom poglavlju smo pokrili sve glavne non-rendering performansne faktore:
Audio moze da iznenadi svojom cenom -- posebno kada imate mnogo istovremenih zvukova sa okluzijom, reverb-om i proceduralnim generisanjem kroz MetaSounds. Audio LOD i pazljivo upravljanje voice count-om su kljucni.
Fizika je cesto najskuplji non-rendering sistem. Izbor kolizionih oblika (uvek najjednostavniji moguci!), upravljanje brojem aktivnih tela, ogranicavanje ragdoll-ova i destruction fragmenata -- sve su to kriticne optimizacije.
Tick sistem moze da postane nocna mora ako se ne upravlja pazljivo. Iskljucite tick za sve sto ne mora da tikuje, smanjite frekvenciju gde je moguce, koristite Timer-e umesto Tick-a za periodicnu logiku, i implementirajte Significance Manager za velike svetove.
Blueprint vs C++ -- ne prepisujte sve u C++, ali prepoznajte hot path-ove (mnogo instanci, kompleksna logika, svaki frejm) i prebacite ih u C++ kada profiler pokaze da je potrebno.
Actor lifecycle -- object pooling je obavezan za objekte koji se cesto kreiraju i unistqvaju. GC pauze mogu da budu razarajuce za gameplay, i jedini nacin da ih kontrolisete je da smanjite pritisak na GC.
Async loading -- nikada ne ucitavajte velike asset-e sinhrono tokom gameplay-a. Koristite soft reference i async loading za sve sto nije apsolutno neophodno od samog pocetka.
Multithreading -- razumejte koji thread-ovi postoje, koji je bottleneck, i optimizujte taj thread. Nema svrhe optimizovati rendering ako je Game Thread bottleneck.
Svi ovi sistemi medusobno uticu jedni na druge. Fizika trosira Game Thread vreme, sto ostavlja manje vremena za Blueprint tick-ove, sto moze da natera Render Thread da ceka... Razumevanje celokupne slike je kljuc za pravi performans.
U sledecem poglavlju cemo nastaviti sa daljim aspektima optimizacije. A za detaljniju CPU optimizaciju, pogledajte Poglavlje 49.