Tworzenie ruchu w dowolnym środowisku w ramach doświadczenia pomaga mu natychmiastowo poczuć się bardziej immersyjne i realistyczne dla naszego świata, czy to z ambient tree movement, reactive doors from player interaction, lub nawet animations, i analiza Twoich unik
Tworzenie burzy
Burza przeszła przez wiele cykli, zanim osiadłem, co jest żywe w Tajemnicy Duvall Drive. Wcześniej myśleliśmy o burzy jako o gigantycznym słupie obsydianu i w późniejszych cyklach rozważaliśmy ją jako gigantyczny portal do przestrzeni korruptnej. Po eksperymentowaniu z wieloma różnymi burzami z unikalnym wyglądem i uczuciem do nich, osiad
- Burza powinna dać graczom poczucie impaktu tego wydarzenia na świat , w tym drzewa płonące i śmieci latające.
- Obracający się wir w chmurze sama w sobie powinien dać graczom wzгляd na centralny portal bez ujawniania wszystkiego . To zachęci graczy do śledzenia bliżej, aby zobaczyć, co się dzieje.
- Bardziej rygorystyczne światło pozwoliłoby nam skupić się na kompozycji domu , która jest zarówno głównym bohaterem, jak i gdzie większość gry.
Aby uczynić burzę czującą dynamicznie, agresywnie i zawsze zmieniającą się w swoim środowisko, użyliśmy następujący systemy i funkcje:
- TweenService > - Dla ruchu chmurkowego.
- Zmiany w oświetleniu - Dla stworzenia chmury do chmury promienia.
- promienie - Dla "światła wolumetrycznego" i kopców gromowych.
- Emiter cząsteczek - Dla odpadów latających do portalu i latających wokół ze względu na wiatr.
- Animacje > - Dla drzew, które płynęły w wietrze.
Dodawanie chmur z teksturami
Podczas gdy dynamiczne chmury są wielką dla normalnych, wysokich chmurach realistycznych chmur, potrzebowali
Ponieważ każda chmurkowa siatka musi być masowa, aby w pełni otaczać dom i przekazywać, jak ogromna jest burza, wiedzieliśmy, że musimy pokryć tkaninę, którą chcemy użyć na pojedynczych chmurkowych siatkach, aby była ona ciężko powtarzalna na całej powierzchni siatki. Testowaliśmy materiały, które stworzyliśmy dla chmury na tych prostych częściach, a następnie stosow
W przeciwieństwie do emisji cząsteczek lub promieni, sieci pozwoliły nam bounce światło z każdej sieci, co było ważne, gdy chcieliśmy zaimplementować Cloud-to-Cloud lightning. Modelowaliśmy również w tzw. kręcącym się, aby promieniowanie bounce z niej wydawało się, że ma głębi! To było szczególnie ważne w sytuacjach, w których wymagania wydajności naszego interfe
Obracające się chmurkowe wątki
Po tym, jak byliśmy zadowoleni z ogólnego wyglądu chmur, musieliśmy go poruszać! Mieliśmy ogólne kształty każdej warstwy chmury w miejsce, ale wymagało to trochę przetestowania, aby upewnić się, że efekt kręcenia się dobrze wygląda w praktyce. Początkowo próbowaliśmy u
Chcieliśmy łatwego do użycia metody rotacji instancji, które były zbyt daleko, aby być interaktywnym, takich jak chmury, lub zbyt
Jak w wielu przypadkach w demonstracji użyliśmy tagu LocalSpaceRotation, abyśmy mogli zarządzać zainfekowanymi instancjami w Studio za pomocą dodatek plug-indo tagowania instancji. Wykorzystaliśmy tylko jeden LocalScript, który zarządzał wszystkimi zainfekowanymi instancjami używając wtyczki
local function Init()
for _, obj in CollectionService:GetTagged("LocalSpaceRotation") do
if obj:IsDescendantOf(workspace) then
SetupObj(obj)
end
end
end
CollectionService:GetInstanceAddedSignal("LocalSpaceRotation"):Connect(function(obj)
objInfoQueue[obj] = true
end)
CollectionService:GetInstanceRemovedSignal("LocalSpaceRotation"):Connect(function(obj)
if objInfo[obj] then
objInfo[obj] = nil
if objInfoQueue[obj] then
objInfoQueue[obj] = nil
end
end
end)
Init()
objInfo
Zakręтиliśmy instancje w funkcji Update połączonej z biciem serca
local parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- główna część może nie być jeszcze transmitowanacontinue -- czekaj na to, aby część główna się powtórzyłaendparentTransform = parentObj.PrimaryPart.CFrameelseparentTransform = parentObj.CFrameendcurObjInfo.curAngle += dT * curObjInfo.timeToAnglelocal rotatedLocalCFrame = curObjInfo.origLocalCFrame * CFrame.Angles( curObjInfo.axisMask.X * curObjInfo.curAngle, curObjInfo.axisMask.Y * curObjInfo.curAngle, curObjInfo.axisMask.Z * curObjInfo.curAngle )if obj:IsA("Model") thenobj.PrimaryPart.CFrame = parentTransform * rotatedLocalCFrameelseobj.CFrame = parentTransform * rotatedLocalCFrameend
Sprawdziliśmy ważny Model.PrimaryPart aby zostać ustawiony do przetwarzania strumieni. Jeśli aktualizacja została wezwana na naszym obiekcie podczas gdy Model.PrimaryPart (który może wskazać na dziecięce sieci) nie była jeszc
Projektowanie trafień błyskawicznych
Ponieważ Studio nie oferuje gotowego generatora promieniującego, a system częściek miał pewne ograniczenia, które nie działałyby dla bohaterskich trafień promieniujących, musieliśmy być kreatywni z rozwiązaniem dla bohaterskich trafień promieniujących. Zdecydowaliśmy się na dwa główne systemy do tworzenia promieniującego: tekst
Łączenie Laserów
Zwykle używamy narzędzia sequencer lub timeline, aby zachęcić czas trafienia promienia świetlnego, tak jak ten efekt, ale ponieważ Studio jeszcze tego nie oferuje, zdecydowaliśmy się na napisanie skryptów, które będą kontrolować czas trafienia promienia świetlnego. Skrypty tego efektu są dość proste, ale osiągają następujące ważne cele:
- Elementy trafień z porażenia, takie jak ich tekstury, jasność i opóźnienia, są losowo z każdym trafieniem.
- Zmiany w postaciach audio i postaciach postaci są zsynchronizowane z ruchami postaci.
- Gracze, którzy są albo w środku, albo w zepsutym obszarze, nie będą w stanie ich zobaczyć lub usłyszeć.
Mamy stronę serwera Script, która kalkuluje różne parametry i czasy, wysyłając je do wszystkich klientów i czekając na losowy czas:
local function LightningUpdate()
while true do
task.wait(rand:NextNumber(3.0, 10.0))
local info = CreateFXData()
lightningEvent:FireAllClients(info)
end
end
Wewnątrz CreateFXData wypełniamy strukturę informacji, aby wszyscy klienci otrzymali te same parametry.
Na stronie klienta ( LightningVFXClient ) sprawdzamy, czy ten klient powinien uruchomić FX:
local function LightningFunc(info)
…
-- nie FX w pomieszczeniach
if inVolumesCheckerFunc:Invoke() then
return
end
-- nie FX, gdy nie jesteś w świecie "normalnej"
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end
…
Dodatkowo wykonujemy sequencję, aby ustawić tekstury, pozycje i jasność, wykonać tzw. nastolatki i użyć task.wait(number). Parametry losowe pochodzą z struktury informacji, którą otrzymaliśmy od serwera, a niektóre liczby są fixed.
beam.Texture = textures[info.textIdx]beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)-- Usuńbeam.Brightness = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- dźwiękif audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.wait(info.waitTillFlashes)-- and so on
Aby sprawdzić, czy gracz jest w domu, używamy pomocy inVolumesCheckerFunc, która przechodzi przez wcześniej umieszczone głośniki określające obszary wewnętrzne, i sprawdza, czy pozycja gracza jest w jednej z nich (PointInABox). Moglibyśmy użyć wykryw
Aby sprawdzić, czy gracz jest w obszarach uszkodzonych, wzywamy pomocnik gameStateInfoFunc funkcję, która sprawdza aktualny stan gry. Aby odtwarzywać losowy dźwięk z katalogu, użyliśmy również pomocnika PlayOneShot. Dla błyskawicznych bóltów samych w Photoshopie stworzyliśmy poci
Korzystanie z systemów emisji cząsteczek
Hero przeciwprądowe uderzenia bohatera są wspierane przez system cząsteczek, który sugeruje dystyltny błyskawice poprzez stworzenie wrażenia warstwy chmur w tle, która przyciąga światło z dala od trafień, lub oświetlenie chmur w chmurach. Uzyskano ten efekt poprzez bardzo prosty system cząsteczek, który migotka chmur na boku głównego chmurnego chmury. System emituje
Tworzenie drzew wiatrem
Po tym, jak mieliśmy chmury i błyskawice działające tak, jak chcieliśmy, musieliśmy dodać do tego dwie inne główne komponenty burzy: wiatr i deszcz! Te elementy przedstawiały kilka wyzwań, w tym potrzebę działania w ramach aktualnych ograniczeń naszego systemu fizyki i efektów specjalnych. Na przyk
Wiedzieliśmy, że naprawdę sprzedać efekt wiatru i deszczu, potrzebowaliśmy drzew samych w sobie, aby się poruszać. Aby to zrobić w silniku, istnieje kilka sposobów, w tym przesuwanie części używając 插件, które są publicznie dostępne, używanie Class.Tween
Zaczęliśmy od skórzenia kilku drzew z Endorse Model Pack - Forest Assets . Ponieważ te drzewa już istnieją, a nasze doświadczenie miało miejsce w północno-zachodnym Pacyfiku, to pozwoliło nam trochę czasu na wcześniejsze tworzenie każdego modelu drzewa.
Po wybraniu naszych drzew wiedzieliśmy, że musimy je skórzyć. Skinning a mesh to akt dodawania łączeń (lub kości) do sieci w innym programie modelowania 3D, takim jak Blender lub Maya, a następnie aplikowanie wpływu na
Wiedzieliśmy, że chcemy zaoszczędzić czas i ponownie użyć tej samej animacja, więc zbudowaliśmy nasz pierwszy ryg z drzewem i upewniliśmy się, że wspólne nazwy są generyczne, ponieważ chcieliśmy użyć tych sam
Gdy stworzyliśmy nasze stawki/ kości, było czas na stworzenie animacji testowej, aby poruszać się po wszystkich stawkach i kościach w Studio, aby zobaczyć, czy porusza się tak, jak chcieliśmy. Aby to zrobić, musieliśmy zaimportowa
Po tym, jak byliśmy zadowoleni z wyników na tym drzewie, było czas na testowanie tej samej animacji na innym drzewie! Wiedzieliśmy już, że będzie to ta sama animacja między różnymi rygami dla każdego wpisywaćdrzew, więc po prostu upewniliśmy się, że nasza animacja wyglądała jakby była wystarczająco ogólna, aby działała między dużym Redwood i gęstym Beechwoodem!
Aby to zrobić, zdejmujemy drzewo z tego Forest Pack i budujemy podobny model, używając tej samej dokładnej nazwy dla połączeń. To tak, aby animacja, którą wcześniej zaimportowaliśmy, mogła być również zastosowana do tego drzewa! Ponieważ wszystkie animacje opierają się na obracających się łącznikach, nie miało znaczenia, jak duży, mały, wysoki lub szeroki jest drzewo!
Po tym, jak zaimportujemy drzewo Beechwood, możemy go zaimportować i zastosować dokładnie tę samą animacja. To oznaczało, że iterowanie i edytowanie tylko było konieczne do wykonania na jednym pliku, a także zapisano na wydajności z mniejszą liczbą animacji podczas uruchomania doświadczenia.
Gdy mieliśmy wszystkie rodzaje drzew, które chcieliśmy animować, stworzyliśmy każde w pakietach, abyśmy mogli nadal edytować i aktualizować, gdy grają kilka animacji wokół głównej obszarze doświadczenia. Gdy wiedzieliśmy, że mają koszty wydajności, używaliśmy ich oszczędnie wokół domu, gdzie ef
Tworzenie Debris Burzy
Chcieliśmy, aby deszcz wyglądał ciężki, a dla mgły i śmieci, aby waliły przez drzewa. Aby to zrobić, ustawiliśmy kilka niewidocznych części, które działają jako objętości cząsteczek z dzieckiem emittery cząsteczek z dzieckiem natychmiastowo poni
Deszczowe cząsteczki wykorzystały nową właściwość emitera cząsteczek ParticleEmitter.Squash, która pozwala ci uczynić cząsteczk
Dla mgły, mgły i liści płynących przez, było o wiele prostsze dodać pojedynczą większą część objętości pokrywającą mniejsze obszary, ponieważ nie potrzebowaliśmy tonę cząsteczek, które działały jednocześnie. Zaczęliśmy ustawiając wolumen i uzyskaliśmy częstotliwość cząsteczek, gdzie chcieliśmy ich.
Następnie stworzyliśmy nasze pustąjące liście i tekstury wiatru, a ustawiliśmy cząsteczki do wszystkiego, aby się kręciły/przesuwały w różnych szybkościach i zaczynały się w różnych szybkościach. To oznaczało, że większe cząsteczki mgły będą interakcjonować bardziej naturalnie i nie będą wyglądać tak bardzo jak powtarzająca się tekstura, szczególnie biorąc pod uwagę ich rozmiar.
Rezultatem była kilka wielkich działań między drzewami się poruszającymi, okłamaniem okna i promieniem, aby stworzyć efekt burzy otaczającej centralne oko burzy.
Ustawienie Oka Burzy
Pęknięty oczy kamienia z świecącym rdzeniem przeznaczony jest dla graczy, którzy chcą mieć wskazówkę, że w domu, w którym mogą dalej eksplorować, coś nieprawidłowego i złego. Ponieważ nasza scena jest ciemna i oczy są daleko w nie
Dystans od gracza oznaczał również, że możemy całkowicie polegać na normalnej mapie dla szczegółów powierzchni oka, więc siatka jest tylko płatną kulą! Rzeźbiliśmy szczegóły w wysokiej płatnej siatce, a następnie zapiekał je na znacznie niższym płatnym kuli, abyśmy mogli uzyskać wszystkie te piękne szczegóły bez kosztu wielkiej wydajności.
Aby dodać supernaturalnemu oczu naturalny wygląd, aby podkreślić jego obecność, zdecydowaliśmy się stworzyć świecącą, neonową magmę, która przeniknie przez jej p
Kolejnym wyzwaniem, który pojawił się podczas tworzenia oka, była imponowanie streamingu połączone z odległością oka od gracza. Ze
Udało nam się dodać ruchu do oka i jego pierścieni dzięki tej samej skryptowi, który używaliśmy do obrócenia sieci chmur. Dla ostatecznego podejścia z
Tworzenie rozszerzającego się zapasu
Jedną z najbardziej zabawnych rzeczy do produkcji były skorumpowane przestrzenie, w których mogliśmy podvertować oczekiwania graczy na rzeczywistość, poprzez dosłowne zmienianie jej wokół nich. Na przykład w puzzle ojca chcieliśmy imitować moment podobny do koszmaru, w którym niezależnie od tego, jak szybko biegniesz, pokój wydaje się być
Ustawiliśmy to za pomocą prostego ruchu ścian i mądrych ustawień naszych pokoi, które pojawiły się na obu stronach magazynka. W normalnym stanie pokoju, korytarz był prostym korytarzem, ale w przestrzeni korupcyjnej był naprawdę dłuższy z kilku skrzydeł i fałszywej ściany!
Fałszywa ściana była grupą modelową, którą przesuwaliśmy z powrotem, gdy gracze wchodzili w moment, w którym grałoby się przez nią prąd, co było przezroczystą częścią wcześniej w panterii, którą będą przechodzić. Ten prąd był również używany w skrypcie podobnym do tych używanych na wszystkich naszych drzwiach,
Ponieważ TweenService jest tak ogólnym systemem, wszystkie nasze modele danych ściany musiały zawierać te same komponenty. Na przykład poniższy obraz jest przykładem ogólnego skryptu drzwi, który wzywa dźwięk zdefiniowany przez „wartość” poniżej modelu „Grow_Wall”.
To samo script, z kilkoma modyfikacjami w następnym przykładzie kodu, również wywołał audio dla przenoszenia magazynu. To dodało dużo do ruchu!
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local model = script.Parent
local sound = model.Sound.Value
local trigger = model.Trigger
local left = model.TargetL_Closed
local right = model.TargetR_Closed
local tweenInfo = TweenInfo.new(
model.Speed.Value, --Czas/Prędkość Rozkładania Drzwi
Enum.EasingStyle.Quart, --Styl łagodzenia
Enum.EasingDirection.InOut, --Łatwiejsze kierunki
0, --Powtórz liczbę
false, --Odwróć prawdziwy
0 --Opóźnij
)
local DoorState = {
["Closed"] = 1,
["Opening"] = 2,
["Open"] = 3,
["Closing"] = 4,
}
local doorState = DoorState.Closed
local playersNear = {}
local tweenL = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Open.CFrame})
local tweenR = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Open.CFrame})
local tweenLClose = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Closed.CFrame})
local tweenRClose = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Closed.CFrame})
local function StartOpening()
doorState = DoorState.Opening
sound:Play()
tweenL:Play()
tweenR:Play()
end
local function StartClosing()
doorState = DoorState.Closing
--model ["Drzwi"]:Graj()
tweenLClose:Play()
tweenRClose:Play()
end
local function tweenOpenCompleted(playbackState)
if next(playersNear) == nil then
StartClosing()
else
doorState = DoorState.Open
end
end
local function tweenCloseCompleted(playbackState)
if next(playersNear) ~= nil then
StartOpening()
else
doorState = DoorState.Closed
end
end
tweenL.Completed:Connect(tweenOpenCompleted)
tweenLClose.Completed:Connect(tweenCloseCompleted)
local function touched(otherPart)
if otherPart.Name == "HumanoidRootPart" then
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
--print(" dotknij")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end
Gdy mieliśmy fałszywą ścianę poruszającą się w tle pokoju, potrzebowaliśmy reszty treści, aby się z nią poruszać. Aby to zrobić, potrzebowaliśmy wszystkich luźnych przedmiotów na pantrze do spawania z nią. Aby to zrobić, używając ograniczeń spawalniczych, byli
Tworzenie Uszkodzonego Domu Drzew
Studio to fantastyczny silnik oparty na fizycznie, który możesz użyć do tworzenia wszystkiego od tańczącego bramkę do obracającej się platforma. Z naszą demokracją chcieliśmy użyć fizyki, aby stworzyć poczucie realizmu w zacofanym zestawie środowisk. Używając zaledwie kilku ograniczeń , możesz stworzyć kilka zabawnych i wyzwań kursów przeszkód w swoich
Ograniczenia są grupą fizycznie opartych silników, które składają się z obiektów i ograniczają zachowania. Na przykład, możesz użyć ograniczenia różnicowania
Gdy gracze zjechali do głównej obszary puzzle, byli przywitani znanym widokiem na Roblox: kurs przeszkód. Ta szczególna kurs przeszkód składała się z kilku obrotowych platform i kręcących się ścian, wraz z "bezpiecznymi obszarami", które postępują historię. Skupimy się na obrotowych/kręcących się elementach.
Dlaczego używaliśmy ograniczeń tutaj? Ponieważ TweenService lub inne metody nie poruszałyby gracza, gdyby oni stali na nich. Bez ruchu obiektu, ktoś mógłby skakać na platformę i będzie ona kręcić się z ich pod. Zamiast tego
Aby to zrobić, musieliśmy najpierw użyć zasobów z naszego obecnego zestawu i dodać jakikolwiek nowy materiał dla efektu wizualnego. Stworzyliśmy kilka niekompletnych ścian i platform z otworami w nich, aby opowiedzieć historię dziadkowej budującej dom dla drzew. Ponieważ nie chcieliśmy tworzyć zbyt wiele unikalnych części baz
Wiedzieliśmy, że ponieważ używaliśmy ograniczeń, nie będziemy w stanie przywiązać tych siatek, ponieważ nie będą
Czasem było czas, aby ustawić zachowanie rzeczywistej siły odginającej się wraz z ramię, i dodać załączniki, które będą działać jako orientacja części i ograniczenia posiadać. Ustawiliśmy przyczepną łączkę na
Aby utrzymać platformy kręcące się w stałej szybkości, wtedy ustawiamy HingeConstraint.AngularVelocity, HingeConstraint.MotorMaxAcceleration i HingeConstraint.MotorMaxTorque wartości do wartości, które pozwoliłyby ruch i zapobiegały przerwom, jeśli gracz wskoczył na nią.
Teraz musieliśmy stworzyć obracające się ściany. Ściany musiały obracać się na ich oczywistym centrum, a wiedzieliśmy, że chcemy, aby były one w stanie poradzić sobie ze wszystkimi orientacjami w stosunku do reszty poziomu. Podobnie jak platformy, zbudowaliśmy je tak, aby wszystkie ściany były niezakotwiczone i spawione na Motor_Turn.
Użyliśmy obiektów Texture na wierzchołku SurfaceAppearance obiektów, aby dodać trochę wariacji do naszych podstaw
Gdy przetestowaliśmy kilka platform i obracających się ścian, zrobiliśmy kilka wariacji i grałyśmy z ich umieszczeniem, aby upewnić się, że kurs przeszkód był wyzwalający, mind-bending i również wyczyść, gdzie gracz musiał przechodzić, wykonywać! Wymagało trochę dostosowania do ich wartości i pozycji, aby uzyskać je dobrze. Mieliś
Jeśli nie jesteś pewien, do czego uderzają twoje fizyczne obiekty, możesz włączyć Dokładność kolizji z w widgetu Opcje wizualizacji w górnym rogu 3D.
Jak widać poniżej, w okolicy drzwi/okien są widoczne, ale mniejsze szczegóły, takie jak sub-paneling, nie są. To dlatego, że właściwość CollisionFidelity dla ścian ustawiona została na Skrzynia. Nie potrzebowaliśmy precyzji dla tych paneli, więc