// Serwus Studio · Unity Asset Store · v2.0

Crowd Cheer System — Dokumentacja

Lekki, GPU-owy system tłumu dla Unity URP. Tysiące animowanych kibiców na stadionie, arenie czy koncercie — bez riggowanych meshy, bez Animatorów, bez kosztu CPU. Ta strona to pełna dokumentacja online: Quick Start, atlas, komponenty, API skryptowe, parametry shadera, wydajność i FAQ.

Unity2020.3 LTS+ PipelineURP 10+ ShaderSerwusStudio/CrowdCheer NamespaceSerwusStudio PlatformyAndroid · iOS · Quest 1/2/3 · PC · WebGL

01Quick Start

Dwie ścieżki: sam shader (30 sekund, zero skryptów) albo pełny system ze sterowaniem globalnym i triggerami. Zacznij od pierwszej, przejdź na drugą kiedy potrzebujesz fali i skoków.

A. Sam shader — minimum

  1. Wrzuć na scenę Quada albo dołączony model plate_crowd.fbx.
  2. Stwórz materiał i ustaw shader SerwusStudio/CrowdCheer.
  3. Podepnij swój atlas w slot Crowd Texture Atlas.
  4. Ustaw Columns in Atlas = liczba wariantów postaci (poziomo) i Anim Rows in Atlas = liczba klatek animacji (pionowo).
  5. Pokręć Tiling X / Y — tyle postaci zmieści się na płaszczyźnie.

Gotowe. Postacie animują się same, z losowym wariantem i losowym przesunięciem fazy. Żadnych skryptów.

B. Pełny system — zalecane

  1. Rozstaw prefaby plate_crowd tam, gdzie ma być publiczność (trybuny, sektory, widownia).
  2. Dodaj CrowdController do każdej płaszczyzny i ustaw per-plane: atlas, kolumny, wiersze, tiling, offset.
  3. Stwórz pusty GameObject i dodaj CrowdManager. Kliknij Auto Collect All — zbierze wszystkie kontrolery ze sceny.
  4. Steruj całym tłumem z Managera: gęstość, prędkość animacji, fade, kolor, losowość, jakość.
  5. Opcjonalnie dodaj CrowdWaveTrigger — fala i skok z klawisza, przycisku UI albo collidera. Bez pisania kodu.
Wymagania

Unity 2020.3 LTS lub nowszy, Universal Render Pipeline (URP 10+). Shader jest pisany ręcznie w HLSL — Shader Graph nie jest potrzebny. Wszystkie komponenty mają [ExecuteAlways], więc tłum widać w edytorze bez wchodzenia w Play.

02Jak to działa

Tłum renderuje się w całości na GPU. Nie ma GameObjectu na postać, nie ma Animatorów, nie ma skinowanych meshy. Jedna płaska płaszczyzna wyświetla kilkadziesiąt animowanych postaci przez tilowanie atlasu tekstur — jeden draw call niezależnie od tego, ile postaci widać.

Ścieżka fragmentu

  1. Clip bounds — jeśli włączone, fragmenty poza prostopadłościanem world-space odpadają natychmiast.
  2. Distance fade — odległość do kamery liczona pierwsza; poza Fade End fragment jest odrzucany, zanim cokolwiek zostanie policzone. To jest oszczędność fillrate, nie tylko efekt wizualny.
  3. Tiling — UV mnożone przez Tiling X/Y, część całkowita to tileID (numer kafla = numer postaci), część ułamkowa to lokalne UV wewnątrz kafla.
  4. Plane seed — ziarno losowości liczone z pivota obiektu (unity_ObjectToWorld). Stałe dla całego mesha, różne dla każdej płaszczyzny na scenie — dwie identyczne płyty obok siebie pokażą inny tłum.
  5. Person IDplaneSeed + tileID + RandomSeed. Z tego jednego hasha wychodzi: czy kafel jest zajęty (gęstość), który wariant postaci, czy odbita w poziomie, jakie przesunięcie fazy animacji, jaki odcień tintu.
  6. Animacjaframe = floor(_Time.y * AnimSpeed + timeOffset), modulo liczba wierszy. Każda postać ma własny timeOffset, więc nikt nie klaszcze w rytm sąsiada.
  7. Fala / La Ola — pozycja world-space rzutowana na kierunek fali; jeśli fragment mieści się w paśmie Wave Width, wiersz animacji jest podmieniany na Wave Pose. Przy Wave Speed = 0 cały blok jest pomijany — zero kosztu.
  8. Sampling i maska — UV atlasu z paddingiem, miękka maska krawędzi (Edge Smoothness), alpha cutout.
Determinizm

Cała losowość jest deterministyczna — ten sam hash daje ten sam wynik w każdej klatce i na każdej maszynie. Tłum nie miga, puste miejsca nie skaczą, a scena wygląda tak samo w edytorze i w buildzie. Żeby przetasować układ, zmień Random Seed.

Passy

Shader ma dwa passy: ForwardLit (kolor, oświetlenie z głównego światła kierunkowego + ambient SH) oraz DepthOnly. Oba dzielą tę samą funkcję CrowdTile(), więc głębia zawsze zgadza się z tym, co widać. Kolejka AlphaTest z ZWrite On — bez sortowania przezroczystości.

03Atlas tekstur

Atlas to równa siatka. Kolumny to warianty postaci (kolor koszulki, sylwetka), wiersze to klatki animacji odtwarzane po kolei z góry na dół.

Atlas 4 × 4 → 4 warianty postaci × 4 klatki animacji. Z włączonym Random Flip X wygląda jak 8 wariantów, bez dodatkowej pamięci.

Wymagania

  • Wszystkie komórki tej samej wielkości, siatka bez przerw.
  • Tło w pełni przezroczyste (alpha = 0) — cutout tego wymaga.
  • Kilka pikseli przezroczystego marginesu wokół każdej postaci — inaczej sąsiednie komórki będą się wzajemnie podbierać przy filtrowaniu.
  • PNG lub TGA z kanałem alpha, rozdzielczość potęga dwójki (512², 1024², 2048²).
  • Na mobile / VR: Crunch compression w ustawieniach importu, 512² lub 1024² w zupełności wystarcza.
  • W materiale ustaw Columns in Atlas i Anim Rows in Atlas dokładnie tak, jak wygląda siatka. Niezgodność = pocięte postacie.
Wskazówka

Pola Tiling/Offset Unity są dla tej tekstury ukryte ([NoScaleOffset]) — świadomie. Używaj sekcji UV Tiling w materiale albo pól na CrowdController; dają precyzyjniejszą kontrolę i działają razem z per-plane MaterialPropertyBlock.

04Komponenty

Trzy komponenty runtime i dwa custom inspektory. Kluczowa zasada: CrowdManager jest opcjonalny. CrowdController działa samodzielnie — Manager tylko przejmuje nad nim sterowanie globalne.

CrowdController

Komponent per-płaszczyzna. [ExecuteAlways], [DisallowMultipleComponent], wymaga Renderer na tym samym obiekcie. Wszystkie wartości idą do shadera przez MaterialPropertyBlock — współdzielony materiał nigdy nie jest modyfikowany, więc nie powstają instancje materiału i GPU instancing zostaje zachowany.

  • Per-plane: atlas, liczba kolumn i wierszy, Tiling X/Y, Offset X/Y, clip bounds.
  • Globalne: gęstość, prędkość animacji, fade, kolor, losowość, jakość, parametry fali — sterowane z Managera, edytowalne lokalnie gdy Managera nie ma.
  • Brak Update i LateUpdate. Wartości lecą do GPU tylko przy zmianie. Zerowy koszt CPU na klatkę.

CrowdManager

Komponent na poziomie sceny, zwykle jeden. Jeden suwak zmienia gęstość na wszystkich płaszczyznach naraz. Trzyma listę kontrolerów (Auto Collect zbiera je automatycznie na Awake) i przy każdej zmianie robi jeden batchowany SetGlobalParams() na kontroler — nie jedno wywołanie na właściwość.

  • Globalne: Density, Anim Speed, Fade Start/End, Tint Color, Tint Variation, Random Seed, Flip, Edge Smoothness, Alpha Clip.
  • Fala: kierunek, prędkość, szerokość pasa, wiersz pozy dopingu, czas trwania (0 = w nieskończoność).
  • Skok: wysokość, czas trwania, propagacja falowa i opóźnienie na jednostkę świata.

CrowdWaveTrigger

Odpala falę i skok bez pisania kodu. Wymaga referencji do CrowdManagera. Trzy tryby, można łączyć:

  1. KlawiszTrigger On Key Press + wybrany klawisz (domyślnie Space).
  2. Przycisk UI — w onClick przeciągnij obiekt i wybierz TriggerWave() lub TriggerJump().
  3. Strefa collidera — Collider z Is Trigger, opcjonalny filtr po tagu (domyślnie Player).

Do tego Cooldown (minimalna przerwa między falami — blokuje spam) oraz dwa UnityEventy: On Wave Triggered i On Wave Blocked. Podepnij pod nie dźwięk tłumu, wibrację pada albo cokolwiek innego.

Custom inspektory

Oba komponenty mają własne inspektory z foldoutami. CrowdControllerEditor wykrywa obecność Managera na scenie: sekcje globalne stają się read-only (żeby nie edytować w dwóch miejscach naraz), a per-plane zostają zawsze aktywne. Ostrzega o braku Renderera, braku materiału i o złym shaderze. CrowdManagerEditor daje licznik kontrolerów, przyciski Auto Collect All / Apply Globals Now oraz Trigger Wave / Jump i Stop w trybie Play. Waliduje też, czy Fade Start jest mniejszy od Fade End.

05Scripting API

Namespace SerwusStudio. Wszystko, co ustawiasz w inspektorze, ma odpowiednik w kodzie — settery same wysyłają wartości do GPU, nie trzeba nic „odświeżać".

CrowdManager — właściwości

SkładowaTypOpis
DensityLevelfloat 0–1Gęstość tłumu na wszystkich płaszczyznach.
AnimSpeedfloat 0.1–10Prędkość animacji w klatkach na sekundę.
FadeStartfloat 0–200Odległość początku zanikania.
FadeEndfloat 0–500Odległość pełnego zaniku (dalej fragmenty są odrzucane).
TintColorColorGlobalny kolor bazowy tłumu.
TintVariationfloat 0–1Wariacja ciepły/zimny per postać.
RandomSeedfloat 0–100Ziarno losowości — zmiana przetasowuje cały tłum.
FlipEnabledboolLosowe odbicie lustrzane ~50% postaci.
Smoothnessfloat 0–0.3Miękkość krawędzi kafla.
AlphaClipfloat 0–1Próg alpha cutout.
JumpHeightfloat 0.05–5Wysokość skoku w jednostkach świata.
JumpDurationfloat 0.1–3Czas trwania skoku jednej płaszczyzny.
ControllersIReadOnlyListLista zarejestrowanych kontrolerów (tylko odczyt).
IsWaveActiveboolCzy fala aktualnie leci (tylko odczyt).

CrowdManager — metody

MetodaOpis
AutoCollect()Znajduje wszystkie CrowdControllery na scenie i wysyła im globalne ustawienia.
RegisterController(c)Dopisuje kontroler do listy i od razu przekazuje mu globalne — dla płaszczyzn spawnowanych w runtime.
UnregisterController(c)Usuwa kontroler z listy (wywołaj przed zniszczeniem obiektu).
ApplyGlobals()Wypycha wszystkie globalne ustawienia do każdego kontrolera.
TriggerWave()Fala z ustawieniami z inspektora. Bezpieczne dla onClick przycisku UI.
TriggerWave(angle, speed, width, waveRow, duration)Fala z własnymi parametrami. duration = 0 → leci w nieskończoność.
TriggerWaveFrom(worldOrigin)Fala wychodząca OD punktu w świecie — np. od bramki po golu.
TriggerWaveToward(worldTarget)Fala zbiegająca DO punktu — np. w stronę sceny.
StopWave()Natychmiast zatrzymuje falę na wszystkich płaszczyznach.
TriggerJump()Skok wszystkich płaszczyzn. Z włączoną propagacją falową każda skacze z opóźnieniem wg pozycji. Tylko w Play.
TriggerJumpFrom(worldOrigin)Skok promieniowy od punktu — bliższe płaszczyzny skaczą pierwsze. Tylko w Play.
StopJump()Zatrzymuje skoki i przywraca pozycje.

CrowdController — metody

MetodaOpis
Apply()Wysyła wszystkie wartości do MaterialPropertyBlock. Wywoływane automatycznie przy każdej zmianie.
SetGlobalParams(…)Ustawia 10 globalnych parametrów naraz i robi jedno Apply(). Używane przez Managera.
SetWaveParams(angle, speed, width, row)Ustawia parametry fali naraz i robi jedno Apply().
TriggerWave(duration = 5f)Jednorazowa fala na tej płaszczyźnie. Jeśli Wave Speed = 0, przyjmuje 2. Tylko w Play.
StopWave()Zatrzymuje falę, zeruje Wave Speed.
TriggerJump(height, duration, delay = 0f)Podskok po krzywej sinusoidalnej: y = sin(t · π) · height. Opóźnienie służy propagacji. Tylko w Play.
StopJump()Zatrzymuje skok i przywraca pozycję wyjściową.
SetClipBounds(min, max)Włącza i ustawia prostopadłościan przycinający w world space.
SetClipBoundsFromRenderer()Ustawia bounds z aktualnych granic Renderera.
ClearClipBounds()Wyłącza przycinanie.

CrowdWaveTrigger — metody

MetodaOpis
TriggerWave()Fala z uwzględnieniem cooldownu. Jeśli włączone Also Trigger Jump — odpala też skok.
TriggerJump()Sam skok, bez fali. Też z cooldownem.
TriggerWaveFromThisPosition()Fala wychodząca z pozycji tego obiektu.
StopWave()Zatrzymuje falę na Managerze.

Przykłady

using SerwusStudio;

public class MatchEvents : MonoBehaviour
{
    public CrowdManager crowd;
    public Transform homeGoal;

    // gol — tłum wariuje: szybsza animacja, fala od bramki, skok
    public void OnGoalScored()
    {
        crowd.AnimSpeed = 8f;
        crowd.TriggerWaveFrom(homeGoal.position);
        crowd.TriggerJump();
        Invoke(nameof(CalmDown), 6f);
    }

    void CalmDown() => crowd.AnimSpeed = 2f;

    // sektory drużyn — dwa Managery albo dwa zestawy materiałów
    public void SetHomeColors()
    {
        crowd.TintColor     = new Color(1f, 0.82f, 0.82f);
        crowd.TintVariation = 0.25f;
    }

    // stadion pustoszeje po meczu
    IEnumerator EmptyStadium(float seconds)
    {
        float t = 0f;
        while (t < seconds)
        {
            t += Time.deltaTime;
            crowd.DensityLevel = Mathf.Lerp(1f, 0f, t / seconds);
            yield return null;
        }
    }

    // profil jakości pod VR / Quest
    public void ApplyVRProfile()
    {
        crowd.FadeStart = 30f;
        crowd.FadeEnd   = 60f;
    }
}

Płaszczyzny tworzone w runtime rejestruj ręcznie — dostaną wtedy komplet globalnych ustawień:

var plane = Instantiate(crowdPrefab, position, rotation);
crowd.RegisterController(plane.GetComponent<CrowdController>());

// przy usuwaniu
crowd.UnregisterController(controller);
Destroy(controller.gameObject);
Bez skryptów

Jeśli wolisz sterować bezpośrednio materiałem, wszystkie właściwości są dostępne przez Material.SetFloat / SetColor — nazwy w tabeli w sekcji 06. Pamiętaj tylko, że modyfikacja współdzielonego materiału tworzy jego instancję; komponenty robią to poprawnie przez MaterialPropertyBlock.

06Parametry shadera

Kompletna lista właściwości shadera SerwusStudio/CrowdCheer — nazwa w inspektorze, nazwa właściwości do kodu, zakres i wartość domyślna.

WłaściwośćW inspektorzeZakres / domyślnaOpis
_MainTexCrowd Texture AtlasTexture2DAtlas sprite'ów: kolumny = warianty, wiersze = klatki. Tiling/Offset Unity ukryte — użyj sekcji UV Tiling.
_AnimSpeedAnimation Speed (FPS)0.1–10 · 2Tempo przewijania klatek. 1–2 spokojny tłum, 3–5 doping, 6–10 szybkie klaskanie.
_AnimRowCountAnim Rows in Atlas1–8 int · 4Liczba wierszy klatek w atlasie. Musi zgadzać się z teksturą.
_ColumnsCountColumns in Atlas1–16 int · 4Liczba kolumn wariantów postaci. Musi zgadzać się z teksturą.
_TilingXTiling X0.1–50 · 1Ile postaci mieści się na szerokość płaszczyzny.
_TilingYTiling Y0.1–50 · 1Ile rzędów postaci na wysokość płaszczyzny.
_OffsetXOffset X-1–1 · 0Przesunięcie UV w poziomie — dostrojenie do geometrii.
_OffsetYOffset Y-1–1 · 0Przesunięcie w pionie — ustawienie „linii stóp" na krawędzi siedzeń.
_RandomSeedRandom Seed0–100 · 0Globalne przetasowanie wariantów bez ruszania geometrii.
_FlipEnabledRandom Flip Xtoggle · onLustrzane odbicie ~50% postaci. Podwaja różnorodność za darmo. Wyłącz przy numerach na koszulkach.
_DensityLevelDensity Level0–1 · 1Procent zajętych miejsc. 1 = komplet, 0.3 = rzadka publika, 0 = pusto. Deterministyczne — nie miga.
_FadeStartFade Start0–200 · 50Odległość, od której tłum zaczyna zanikać.
_FadeEndFade End0–500 · 100Odległość pełnego zaniku. Dalej fragmenty odrzucane — realna oszczędność fillrate.
_TintColorBase TintColor · whiteGlobalny mnożnik koloru. Barwy klubowe, nocne chłodne światło, zachód słońca.
_TintVariationTint Variation0–1 · 0Ciepły/zimny odcień per postać. Już 0.1–0.2 wyraźnie ożywia tłum przy małej liczbie kolumn.
_WaveSpeedWave Speed0–10 · 0Prędkość fali. 0 = wyłączona (blok pomijany w shaderze). 1–2 dostojna, 3–5 naturalna, 6–10 szybka.
_WaveRowWave Pose0–7 int · 2Wiersz atlasu z pozą dopingu (ręce w górze). Indeksowanie od 0.
_WaveWidthWave Width0.5–30 · 5Szerokość pasa fali w jednostkach świata — ile osób doping robi jednocześnie.
_WaveAngleWave Direction0–360 · 0Kierunek propagacji w stopniach. 0° = +X, 90° = +Z, 180° = −X, 270° = −Z.
_SmoothnessEdge Smoothness0–0.3 · 0.05Miękkie wygaszenie krawędzi kafla. Widzisz czarne linie między postaciami? Podnieś do 0.05–0.1.
_AlphaClipAlpha Clip Threshold0–1 · 0.3Próg odcięcia alpha. Twarde krawędzie atlasu: 0.3–0.5. Miękkie, antyaliasowane: 0.1–0.2.
_ClipBoundsEnabledEnable Clip Boundstoggle · offPrzycinanie do prostopadłościanu world-space. Domyślnie wyłączone — zero kosztu, gdy nieużywane.
_ClipBoundsMin / MaxClip Bounds Min / Max XYZVector3Narożniki bryły przycinającej w przestrzeni świata.

07Clip Bounds

Płaskie płaszczyzny tłumu na zakrzywionych trybunach lubią się przecinać — róg jednej płyty wystaje zza drugiej albo przebija barierkę. Clip Bounds rozwiązuje to bez cięcia geometrii: fragmenty poza zadanym prostopadłościanem world-space są po prostu odrzucane.

  • Włącz Enable Clip Bounds na CrowdController i ustaw narożniki Min/Max.
  • Zaznaczony obiekt rysuje pomarańczowy gizmo bryły — widać dokładnie, co zostanie przycięte.
  • SetClipBoundsFromRenderer() ustawia bryłę na aktualne granice Renderera — dobry punkt startowy do ręcznego zawężenia.
  • Test jest pierwszą rzeczą w shaderze, więc przycięte fragmenty nie kosztują nic więcej.
// przytnij tłum do bryły sektora
controller.SetClipBounds(sector.bounds.min, sector.bounds.max);

// wyjściowo: dopasuj do własnych granic mesha, potem zawęź ręcznie
controller.SetClipBoundsFromRenderer();

// wyłącz
controller.ClearClipBounds();

08Wydajność i VR

System jest lekki z założenia, ale wąskim gardłem zawsze będzie fillrate — liczba pikseli ekranu pokrytych płaszczyznami tłumu. Wszystko poniżej sprowadza się do tego, żeby ich było mniej.

Co jest darmowe z definicji

  • Zero kosztu CPU na klatkę — żaden komponent nie ma Update ani LateUpdate. Dane lecą do GPU tylko przy zmianie.
  • MaterialPropertyBlock — brak duplikacji materiałów, GPU instancing zachowany.
  • Batchowane Apply() — zmiana globalna to jedno wywołanie na kontroler, nie jedno na właściwość.
  • Fala kosztuje zero, gdy śpi — przy Wave Speed = 0 shader nie wchodzi w blok fali.
  • Alpha cutout, nie transparency — kolejka AlphaTest z ZWrite On, bez sortowania przezroczystości.

VR / Quest / mobile

  • Distance Fade agresywnie. Na Queście Fade Start 30, Fade End 60. Na mobile 40 / 80. Odległe trybuny nic nie wnoszą, a kosztują fillrate.
  • Mniejszy tiling na dalekich płytach. Płaszczyzna 40 metrów od kamery nie potrzebuje 10×10 postaci — 3×3 wygląda tak samo.
  • Atlas 512² lub 1024² z Crunch compression. Postaci ogląda się z dystansu, nie widać różnicy.
  • Włącz GPU Instancing w materiale, jeśli używasz wielu płaszczyzn z tym samym materiałem.
  • Single Pass Instanced działa. Shader ma pełne wsparcie stereo: UNITY_VERTEX_INPUT_INSTANCE_ID, UNITY_VERTEX_OUTPUT_STEREO, UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX. Testowane na Quest 2 i Quest 3.

PC i konsole

Można pozwolić sobie na atlas 2048², Fade End 200+ i gęsty tiling. Oświetlenie to główne światło kierunkowe plus ambient SH — realistyczna reakcja na scenę przy minimalnym koszcie.

Budżet

Kolejność strojenia przy spadkach FPS: najpierw Fade End, potem tiling na dalekich płytach, dopiero na końcu rozdzielczość atlasu. Pierwsze dwa dają największy zysk, bo bezpośrednio zmniejszają liczbę renderowanych pikseli.

09FAQ

Między postaciami widać czarne linie.
Podnieś Edge Smoothness do 0.05–0.1. Sprawdź też, czy atlas ma przezroczysty margines wokół każdej komórki — bez niego filtrowanie tekstury podbiera piksele z sąsiedniej komórki.
Wszystkie postacie wyglądają tak samo.
Columns in Atlas musi odpowiadać rzeczywistej liczbie kolumn w teksturze. Sprawdź też, czy Random Flip X jest włączone i podnieś Tint Variation do 0.2.
Fala się nie pojawia.
Ustaw Wave Speed powyżej 0 (np. 3) i sprawdź, czy Wave Pose wskazuje istniejący wiersz atlasu (indeksowanie od 0). Z Managera: TriggerWave() tworzy korutynę, więc działa tylko w trybie Play.
Postacie są przesunięte albo wiszą w powietrzu.
Skoryguj Offset Y, żeby ustawić linię stóp na krawędzi siedzeń. Sprawdź też, czy UV mesha idą od 0 do 1.
Suwaki na CrowdControllerze są wyszarzone.
To celowe — na scenie jest CrowdManager i to on trzyma ustawienia globalne. Edytuj je na Managerze; sekcje per-plane (atlas, tiling) pozostają aktywne. Usuń Managera, a Controller wróci do trybu samodzielnego.
Dwie sąsiednie płaszczyzny pokazują identyczny układ tłumu.
Ziarno liczone jest z pivota obiektu — jeśli płaszczyzny mają tę samą pozycję pivota (np. są dziećmi tego samego obiektu bez przesunięcia), dostaną ten sam układ. Przesuń pivoty albo zmień Random Seed na jednej z nich.
Czy zadziała z Shader Graph / HDRP / Built-in?
Shader jest pisany ręcznie w HLSL pod URP — dla wydajności i pełnej kontroli. Shader Graph nie jest potrzebny, wszystko ustawia się w inspektorze materiału. HDRP i Built-in nie są wspierane.
Czy mogę użyć własnego atlasu?
Tak, to podstawowy scenariusz użycia. Zrób siatkę, gdzie kolumny to warianty postaci, a wiersze to klatki animacji, i ustaw liczby kolumn/wierszy w materiale. Wymagania w sekcji 03.
Jak zrobić różne sektory — gospodarze i goście?
Osobne instancje materiału z różnym Base Tint i Random Seed. Przy sterowaniu z kodu użyj dwóch CrowdManagerów, każdy z własną listą kontrolerów.
Ile postaci da się wyrenderować?
Testowane na tysiącach widocznych postaci na Quest 2 przy stabilnym framerate. Limitem nie jest liczba postaci, tylko fillrate — czyli ile pikseli ekranu zajmują płaszczyzny. Steruje tym Distance Fade.

10Changelog

2.0.02026-06-27

Major update — Crowd Cheer to teraz pełny system skryptowy, nie tylko shader. Nowe: CrowdManager / CrowdController / CrowdWaveTrigger, runtime API (TriggerWave, TriggerJump…), efekt skoku/bounce z propagacją falową, world-space clip bounds, dwie sceny demo (Koncert, Stadion), custom inspektory z podglądem, rozszerzona dokumentacja EN + PL. Zmiana: seeding losowości oparty o pivot mesha (stabilniejszy). Usunięto: parametr „Randomize Scale".

1.0.02026-03

Pierwsze wydanie. Atlasowy shader tłumu na GPU dla URP (bez riggowanych meshy), sterowanie materiałem (gęstość, prędkość, UV tiling, losowość, distance fade, tint, edge smoothness, alpha clip), wbudowane parametry fali La Ola, scena demo, prefaby, przykładowe materiały i tekstury, dokumentacja EN + PL.