Roblox · Cloth Simulation

FlagController

v2.0 — dokumentacja / PL

Parametry

Wszystkie parametry przekazujesz do konstruktora jako trzeci argument (paramsOverride), albo ustawiasz w dowolnym momencie przez :SetParams({}). Poniżej każdy parametr z opisem co wizualnie zmienia.

⏱ Timing globalny
GlobalSpeed domyślnie: 1.25
Mnożnik czasu dla całej symulacji. Zwiększenie przyspiesza wszystko — wiatr, gusts, flutter, root sway. Zmniejszenie do 0.5–0.8 daje efekt ciężkiej, ceremonialnej flagi. Wartości powyżej 2.0 wyglądają chaotycznie.
zakres praktyczny: 0.4 — 2.5
🌬 Wiatr bazowy
WindStrength domyślnie: 3.2
Amplituda bazowego wiatru — jak daleko kości odchylają się w osi Y (boki). To najważniejszy parametr: wartość 1.0–2.0 to słaby wietrzyk, 4.0–6.0 to silny wiatr.
zakres praktyczny: 0.5 — 7.0
WindSpeed domyślnie: 1.4
Prędkość fali wiatru biegnącej przez flagę. Niskie wartości (0.6–1.0) tworzą długie, leniwe fale. Wysokie (2.5+) — szybkie, nerwowe ruchy. Używany głównie w Cheap Mode.
zakres praktyczny: 0.4 — 3.5
WindChaos domyślnie: 0.3
Modulacja amplitudy wiatru — losowe wzmocnienia i osłabienia siły bazowej. Wartość 0.0 to idealnie równy wiatr (mechaniczny). Wartość 0.5+ to wyraźna zmienność.
zakres praktyczny: 0.0 — 0.8
〰 Turbulencja
TurbulenceStrength domyślnie: 0.12
Nakłada drobne, szybkie zaburzenia na ruch tkaniny — efekt chropowatości powietrza. Powinien być wyraźnie mniejszy niż WindStrength. Zbyt wysoka wartość rozbija płynność fal.
zakres praktyczny: 0.0 — 0.4
TurbulenceSpeed domyślnie: 1.1
Jak szybko zmienia się turbulencja w czasie. Wartości 0.5–1.0 to powolne, ledwo widoczne zaburzenia. 2.0+ to wyraźnie drgająca tkanina.
zakres praktyczny: 0.3 — 2.5
💨 Podmuchy (Gusts)
GustStrength domyślnie: 1.6
Siła podmuchów. W v2 gusts to traveling wave — fala biegnąca od korzenia do wolnej krawędzi. GustStrength kontroluje amplitudę tej fali.
zakres praktyczny: 0.5 — 4.0
GustSpeed domyślnie: 0.8
Prędkość biegu fali podmuchów przez flagę. Wartość 0.4 — powolny, ciężki podmuch. Wartość 1.5+ — szybkie podrywy.
zakres praktyczny: 0.2 — 2.0
GustFrequency domyślnie: 0.05
Jak często pojawiają się podmuchy (próg noise). Wartość 0.02 to rzadkie, dramatyczne podrywy. 0.1+ — prawie ciągły podmuch.
zakres praktyczny: 0.01 — 0.15
🪶 Flutter (trzepotanie)
FlutterStrength domyślnie: 0.9
Amplituda trzepotania wolnej krawędzi. W v2 flutter to fala poprzeczna biegnąca od wolnej krawędzi do korzenia — dokładnie jak w naturze. Amplituda zanika przy korzeniu.
zakres praktyczny: 0.3 — 2.5
FlutterSpeed domyślnie: 8.0
Częstotliwość drgań trzepotania. 5.0 to wolne, widoczne fale. 12+ to szybkie drgania jak przy silnym wietrze.
zakres praktyczny: 3.0 — 15.0
FlutterChance domyślnie: 0.25
Prawdopodobieństwo aktywacji fluttera w danym obszarze tkaniny. 0.0 wyłącza flutter całkowicie. 0.6+ — tkanina prawie zawsze trzepocze na krawędzi.
zakres praktyczny: 0.0 — 0.7
⬇ Opadanie (Sag)
SagAmount domyślnie: math.rad(11)
Kąt opadania końcówki flagi. To statyczny efekt grawitacji — końcówka opada o ten kąt w osi X (pochylenie w dół). Wyrażony w radianach; math.rad(11) to około 11 stopni.
wskazówka Jeśli chcesz więcej opadania końcówki, zwiększ tę wartość np. do math.rad(18).
zakres praktyczny: math.rad(3) — math.rad(25)
SagCurve domyślnie: 1.15
Kształt krzywej opadania. Wartość 1.0 to opadanie liniowe (równomierne). Wartość 1.5+ — środek flagi prawie poziomy, tylko końcówka mocno opada (efekt ciężkiej tkaniny).
zakres praktyczny: 0.8 — 2.5
📌 Korzeń (Root hinge)
RootSway domyślnie: math.rad(2.2)
Amplituda kołysania kości zakotwiczonych przy drzewcu. Subtelny ruch "zawiasu" nadaje wrażenie że flaga jest przyczepiona do żywej konstrukcji. Zbyt wysoka wartość wygląda absurdalnie.
zakres praktyczny: math.rad(0) — math.rad(6)
RootSpeed domyślnie: 0.55
Prędkość kołysania korzenia. Powinien być niski — korzeń porusza się powoli, ciężko.
zakres praktyczny: 0.1 — 1.2
RootNoise domyślnie: math.rad(1.2)
Losowy szum na ruchu korzenia. Zapobiega idealnie mechanicznemu, sinusoidalnemu kołysaniu.
zakres praktyczny: math.rad(0) — math.rad(4)
🧵 Fizyka tkaniny
Decay domyślnie: 0.62
Zanikanie energii wzdłuż łańcucha kości. Wartość 0.62 oznacza że każda kolejna kość ma 62% energii poprzedniej. Niskie wartości (0.4) — ruch tylko na końcówce. Wysokie (0.85+) — cała flaga porusza się równomiernie (sztywna tkanina).
zakres praktyczny: 0.35 — 0.90
Inertia domyślnie: 0.18
Najważniejszy parametr v2. Jak szybko kość "dogania" swoją docelową pozycję. Wartość 0.05 — bardzo leniwa, masywna tkanina (np. jedwabny banner). Wartość 0.5+ — prawie brak inercji, flaga reaguje natychmiast.
zakres praktyczny: 0.03 — 0.6
Damping domyślnie: 0.72
Tłumienie prędkości inercji. Niska wartość (0.4) — tkanina zatrzymuje się gwałtownie. Wysoka (0.92) — kość "przelatuje" przez docelową pozycję i oscyluje (sprężysty efekt). Razem z Inertia definiuje charakter materiału.
zakres praktyczny: 0.4 — 0.95
SmoothFactor domyślnie: 15.0
Wygładzanie przestrzenne wzdłuż łańcucha kości. Zapobiega kanciastym przejściom między sąsiednimi kośćmi. Wartość 1.0 wyłącza wygładzanie. 30+ — bardzo miękkie, "gumowe" przejścia.
zakres praktyczny: 1.0 — 40.0
SmoothExposition domyślnie: 1.1
Mnożnik końcowy wszystkich kątów przy aplikowaniu transformów. Pozwala globalnie skalować intensywność ruchów bez zmiany pozostałych parametrów. Wartości poniżej 1.0 tłumią ruch, powyżej 1.3 dramatycznie go wzmacniają.
zakres praktyczny: 0.6 — 1.5

API — jak używać

Konstruktor

local fc = FlagController.new(
    flagModel,       -- Model Instance z Roblox
    userId,          -- number, UserId gracza
    paramsOverride   -- table (opcjonalne)
)

Aktualizacja co klatkę

-- w RunService.Heartbeat lub podobnym:
RunService.Heartbeat:Connect(function(dt)
    fc:Wave(dt)
end)

Zmiana parametrów w locie

-- np. na silny wiatr podczas burzy:
fc:SetParams({
    WindStrength  = 5.5,
    GustStrength  = 3.0,
    GustFrequency = 0.1,
    Inertia       = 0.08,
})

-- powrót do normy:
fc:SetParams({
    WindStrength  = 3.2,
    GustStrength  = 1.6,
    GustFrequency = 0.05,
    Inertia       = 0.18,
})
Uwaga SetParams ignoruje nieznane klucze. Jeśli wpiszesz Windstrength (mała litera) zamiast WindStrength, nic się nie zmieni i nie dostaniesz błędu.

Cheap Mode

Tryb oszczędny — uproszczona symulacja dla flag w tle, odległych lub gdy liczba flag na scenie jest duża. Brak noise, inercji i gustów. Tylko sinusoidalna fala + sag.

fc.Cheap = true   -- włącz
fc.Cheap = false  -- wyłącz (domyślnie)

Możesz przełączać dynamicznie — np. gdy gracz jest dalej niż 50 studs:

RunService.Heartbeat:Connect(function(dt)
    local dist = (flagPos - playerPos).Magnitude
    fc.Cheap = dist > 50
    fc:Wave(dt)
end)
Info Cheap Mode używa tylko WindSpeed, WindStrength, RootSway, RootSpeed, SagAmount, SagCurve i Decay. Pozostałe parametry są ignorowane.

Jak to działa — pipeline symulacji

FlagController to symulator szkieletowy oparty na kościach Roblox (Bone). Każda klatka wykonuje cztery przejścia:

Pass Co robi
Pass 1 Oblicza target offsets dla każdej kości — suma wiatru, turbulencji, gustów, fluttera i sagu.
Pass 2 Wygładzanie przestrzenne wzdłuż łańcucha (SmoothFactor). Usuwa kanciaste przejścia.
Pass 3 Inercja — każda kość "dogania" target z prędkością (velocity spring). Nadaje masę tkaninie.
Pass 4 Aplikuje finalne kąty do bone.Transform.

Nazewnictwo kości

Kontroler rozpoznaje kości według schematu nazwy:

Bone.{kolumna}.{głębokość}

-- przykłady:
Bone.0.0   -- kolumna 0, głębokość 0 (korzeń)
Bone.0.1   -- kolumna 0, głębokość 1
Bone.1.0   -- kolumna 1, głębokość 0 (korzeń)
Bone.1.3   -- kolumna 1, głębokość 3 (końcówka)

Kolumna to pasek pionowy flagi (od góry do dołu). Głębokość to pozycja wzdłuż flagi od drzewca do wolnej krawędzi. Kość o głębokości 0 to zawsze korzeń — ma inny ruch (root hinge).

Wskazówka Więcej kości = płynniejsza symulacja, ale wyższy koszt CPU. Siatka 4×4 do 6×8 to dobry kompromis dla flag graczy.

Przygotowanie modelu w Blenderze

  1. Utwórz siatkę flagi
    Dodaj płaszczyznę (Mesh → Plane), przeskaluj do proporcji flagi (np. 2:1 lub 3:2). Podziel ją przez Right Click → Subdivide — tyle razy żeby uzyskać kolumny i rzędy pasujące do twojej siatki kości. Dla siatki 5×4 potrzebujesz 4 podziałów poziomych i 3 pionowych.
  2. Dodaj Armaturę
    Add → Armature → Single Bone. Ustaw kość korzenia przy lewej krawędzi flagi (przy drzewcu). Następnie w Edit Mode duplikuj i układaj kości zgodnie ze schematem kolumna/głębokość. Każda kość powinna być rodzicem następnej w łańcuchu (głębokość 0 → 1 → 2…).
  3. Nazwij kości według schematu
    W panelu Bone Properties zmień nazwy na format Bone.{kolumna}.{głębokość}. Roblox i FlagController identyfikują kości wyłącznie po nazwie.
    Ważne Numery zaczynają się od 0, nie od 1. Pierwsza kość to Bone.0.0.
  4. Przypisz Vertex Groups
    Zaznacz siatkę, przejdź do Object Data Properties → Vertex Groups. Utwórz grupę dla każdej kości o tej samej nazwie. Przypisz wierzchołki do grup z wagą (weight painting) — kość wpływa na obszar bezpośrednio przy niej.
  5. Dodaj Armature Modifier
    Na siatce dodaj Modifiers → Armature, wskaż swoją armaturę. Sprawdź czy flaga deformuje się poprawnie przesuwając kości ręcznie w Pose Mode.
  6. Eksport do FBX
    File → Export → FBX. Ustawienia:
    • Apply Transform: włączone
    • Armature: Add Leaf Bones wyłączone
    • Mesh: Apply Modifiers włączone

Konfiguracja w Roblox Studio

  1. Importuj FBX
    Home → Import 3D → wybierz plik FBX. Studio powinno rozpoznać siatkę i kości i zaimportować je jako MeshPart z Bone instancjami w środku.
  2. Sprawdź hierarchię
    W Explorer rozwiń model. Powinna być struktura:
    Model
      └─ Plane (MeshPart)
           ├─ Front (SpecialMesh / Decal)
           ├─ Back  (SpecialMesh / Decal)
           ├─ Bone.0.0
           │    └─ Bone.0.1
           │         └─ Bone.0.2 …
           └─ Bone.1.0 …
  3. Sprawdź nazwy kości
    Upewnij się że kości mają nazwy w formacie Bone.X.Y. Jeśli Blender dodał prefiks (np. Armature_Bone.0.0) — zmień ręcznie lub użyj skryptu BatchRename.
  4. Skonfiguruj FlagController
    Umieść skrypt (LocalScript lub Script) i wywołaj konstruktor:
    local FlagController = require(
        game.ReplicatedStorage.Flags.Flag.FlagController
    )
    local RunService = game:GetService("RunService")
    
    local fc = FlagController.new(
        script.Parent,
        12345678,  -- userId
        { WindStrength = 3.2 }
    )
    
    RunService.Heartbeat:Connect(function(dt)
        fc:Wave(dt)
    end)
  5. Tekstury awatara
    Konstruktor automatycznie pobiera tekstury przez Players:GetUserThumbnailAsync. Wymaga że Plane.Front i Plane.Back to obiekty z właściwością .Texture (np. Decal lub SpecialMesh).
Wiele flag jednocześnie Każda flaga to osobna instancja FlagController. Dla 10+ flag rozważ włączenie Cheap = true dla flag poza kamerą lub odległych od gracza.