Deneyim içindeki herhangi bir çevrede hareket yaratmak, deneyimin aniden dünyamıza daha fazla dalgın ve gerçekçi hissetmesine yardımcı olur, bunun ambient ağaç hareketinden, oyuncu etkileşiminden reaktif kapılara veya onlara çarptıklarında hareket eden kutulardan geliyor olsa bile.Studio, dünyaların daha canlı hissetmesine yardımcı olmak için bir fizik sistemi, TweenService ve animasyonlar dahil birçok benzersiz yönteme sahiptir ve deneyiminizin özel ihtiyaçlarını analiz etmek, hangisini kullanacağınıza karar vermenize yardımcı olabilir.Bu bölümde, Studio'da oluşturmak istediğimiz hareket türünü nasıl belirlediğimizi ve bu farklı hedefleri gerçekleştirmek için hangi araçları kullandığımızı göstereceğiz.

Fırtınu oluştur
Fırtına, Duvall Sürücüsünün Sırrı'nda neyin canlı olduğuna karar vermeden önce birçok itterasyondan geçti.Erken, fırtınayı dev bir obsidiyen sütunu olarak düşündük ve daha sonraki ittifaklarda bunun yozlaşmış uzaya bir dev portal olacağını düşündük.Onlara benzersiz bakış ve hislerle birçok farklı fırtına denedikten sonra, daha küçük bir merkezi "göz" ile bir fırtınaya karar verdik çünkü:
- Fırtına, oyunculara bu olayın dünya üzerindeki etkisinin hissini vermeli , ağaçların uçması ve parçaların uçması dahil.
- Bulutun kendi dönen vorteks oyunculara her şeyi açmadan merkezi portala bir göz atmalıdır .Bu, oyuncuların neler olduğunu görmek için daha yakından araştırmaya teşvik edecektir.
- Işığın daha sıkı noktası, ana karakter ve oyunun çoğu yer bulunduğu yer olan evin kompozisyonuna odaklanmamıza izin verecekti .




Fırtına hissetmesini dinamik, agresif ve çevresinde sürekli değişen yapmak için, aşağıdaki sistemleri ve özellikleri kullandık:
- TweenService - Bulut hareketi için.
- Işık değişiklikleri - Buluta buluta yıldırım oluşturmak için buluta.
- Işınlar - "Hacimsel aydınma" ve yıldırımlar için.
- Parçacık Yayıcıları - Portalın üzerine uçan ve rüzgarın esmesi nedeniyle etrafında dolaşan parçalar için
- Animasyonlar - Rüzgarın estiği ağaçlar için
Doku ile bulutlar ekleyin
Dinamik bulutlar normal, yüksek irtifalistik gerçek bulutlar için harika olsa da, dramatik hisseden ve daha ağır yönlendirebileceğimiz ve özelleştirebileceğimiz bir şeye ihtiyacımız vardı.Bunu yapmak için, bulut kapağını taklit etmek için yüzey görünümü nesnelerini yarı saydamlık ile bir dizi ağır yığılmış ve katmanlı bulut örüntüsüne uyguladık.Neden onları yığdık ve çok ağır bir şekilde katmanladık? Çünkü her bulut meshesi farklı hızlarda hareket ettiğinde, birbirlerinin içine ve dışına giren bulut formları oluştururlar.Bu süreç, bulutların sadece dönen diskler olmasına rağmen biraz daha dinamik ve doğal hissetmesini sağladı.Bulutların yarı saydam olması da önemliydi, çünkü oyuncuların evlere gelmeden önce merkezde parlak bir şey görebilmelerini istedik!


Her bulut örgüsünün evi tamamen çevirmek ve fırtınanın ne kadar büyük olduğunu iletmek için büyük olması gerektiğinden, bireysel bulut örgülerinde kullanmak istediğimiz dokuyu yerleştirmemiz gerektiğini biliyorduk, böylece mesanın yüzeyinde ağır tekrarlanacaktı.Bulut için yaptığımız malzemeleri bu basit parçalara test ettik, ardından vortekse uyguladık!

Parçacık yayıcıları veya ışınlardan farklı olarak, örüntüler, her bir örüntüden ışığı yansıtabilmemizi sağladı, ki bu, bulut-buluta yıldırım uygulamak istediğimizde önemliydi.Ayrıca çevirme sırasında aydınlatmanın üzerinden atlaması gibi bir şey tasarladık, böylece performans gereksinimleri deneyimin yüzey görünüm nesnelerinin kalite seviyelerini düşürdü. Bu özellikle deneyimin performans gereksinimlerinin yüzey görünüm nesnelerimizin kalite seviyelerini düşürdüğü durumlarda önemliydi.

Bulut örüntülerini döndür
Bulutların genel görünümünden memnun kaldıktan sonra, onu hareket ettirmemiz gerekiyordu! Her bulut tabakasının genel şekillerine sahiptik, ancak dönen etkinin pratikte iyi göründüğünden emin olmak için biraz deneme ve hata yapmak gerekiyordu.Başlangıçta, bulutları hareket ettirecek hızı tanıtmak için kısıtlamaları kullanmayı denedik.Bu, daha sonra tekrarlanmasını istediğimizden daha zordu ve oyuncu asla onunla etkileşime girmeyecekti, bu yüzden hareketinde o kadar doğru olması gerekmiyordu.
Bulutlar gibi fazla etkileşime geçilemeyen veya küçük lambalar gibi oyun/fizik için önemli olacak kadar küçük veya dekoratif olan instansları döndürmek için kullanımı kolay bir yöntem istedik.Müşteri-sunucu genişliğini azaltmak, daha pürüzsüz bir hareket sağlamak ve her bulut örgüsünün farklı bir dönme oranına ve gecikmeye sahip olmasına izin vermek için bir LocalScript kullanmaya karar verdik.Daha genel hale getirmek için, dönme eksenini de belirtmeyi mümkün kıldık.3 özellik kullanmak mümkündür, ancak durumumuz için 3 değer kullandık: Axis , Delay ve Speed .

Demodaki birçok durumda olduğu gibi, bir LocalSpaceRotation etiketi kullandık, böylece Studio'daki etkilenen örnekleri bir örnek etiketleme eklentikullanarak yönetebildik.Geliştirme sürecinin boyunca tüm etiketli örnekleri ele alan yalnızca bir kullandık, böylece geliştirme sürecinde bir sürü kodu korumak zorunda kalmadık.
Demomuzda, dünyanın bölümleri gerektiği gibi ServerStorage iş alanına klonlanır, böylece etiketli nesnelerin oluşturulduğu ve yok edildiği durumlarla ilgilenmemiz gerekiyordu.LocalScripts ile, ayrıca meshelerin ve onların çocuk değerlerinin içeri ve dışarı yayınlanabileceği yayın bilincine sahip olmalıyız.Başlangıçta yerleştirilen nesneleri Init() işlevine işledik ve etiketli nesneleri yeni oluşturulan/yok edilen nesnelerle ele almak için CollectionService.GetInstanceAddedSignal ve CollectionService.GetInstanceRemovedSignal bağladık.Aynı SetupObj fonksiyonu, yeni nesneleri Init() ve CollectionService.GetInstanceAddedSignal 'de başlatmak için kullanıldı.
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 tüm ilgili nesnelerin dönme hızı ve eksen gibi bilgileri olan bir haritadır.Aklınızda bulundurun, hemen SetupObj 'dan CollectionService.GetInstanceAddedSignal 'a çağrı yapmıyoruz, ancak objInfoQueue 'a bir nesne ekledik.sunucuyayınlanan ve klonlanan nesnelerle, 'ün çağrıldığında, henüz , ve değerlerimiz olmayabilir, bu yüzden nesneyi bir sıraya ekledik ve değerler orada olana kadar 'nın çağrısını yaptık ve değerleri her bir nesneye "bilgi" yapısına okuduk.
Kalp atışına bağlı Update fonksiyona bağlı instansları döndürdük.Ebeveyn dönüşümü (parentTransform) aldık, bu nesnenin dönme hızına dayalı olarak yeni bir dönme açısı biriktirdik ( curObjInfo.curAngle ), yerel dönüşü hesapladık ( rotatedLocalCFrame) ) ve sonunda onu CFrame 'a ayarladık.Not that both parent and the object can be a Model or MeshPart , so we had to check IsA("Model") and use either a PrimaryPart.CFrame or CFrame .
local parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- birincil parça henüz yayınlanmamış olabilircontinue -- birincil parçanın yeniden üretilmesini bekleyinendparentTransform = 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
Yayın işleme için ayarlanacak geçerli bir Model.PrimaryPart kontrol ettik.Eğer bir güncelleme çağrıldığında nesnemizde bir Model.PrimaryPart (bu bir çocuk meshesine işaret edebilir) henüz yayınlanmadıysa, güncellemeyi atlayacaktık.Mevcut sistem, nesne dönüşümünün ikinci bir yeniden yorumudur ve önceki sistem farklı çalıştı: değerler 12 kez farklıydı! Aynı verileri korumak için kodumuza dönüştürdük, örneğin "12 * obj.Speed.Value".
Tasarım yıldırımları tasarla
Stüdyo kutudan çıkan bir yıldırım jeneratörü sunmuyor ve parçacık sistemi kahraman yıldırımları için çalışmayacak bazı sınırlara sahipti, bu yüzden kahraman yıldırımları için bir çözüm bulmak zorundaydık.Yıldırımı oluşturmak için iki ana sisteme karar verdik: fırtına gözünden gelen kahraman yıldırım vuruşları için dokulu ışınlar, ses ve post-izleme efektleriyle uyum sağlayan ve ses ve post-izleme efektleriyle uyum sağlayan basit bir parçacık etkisi ile uzak bulut-bulut yıldırımı için basit bir parçacık etkisi.
Doku ışınları
Genellikle bir aydınlatma çarpması etkisinin zamanlamasını sürdürmek için bir sayıcı veya zaman çizelgesi aracı kullanırız, ancak Studio henüz bu işlevi sunmadığından, aydınlatma çarpması zamanlamasını kontrol edecek kodlar yazmaya karar verdik.Bu efektin kodlanması oldukça basittir, ancak şu önemli hedefleri gerçekleştirir:
- Onların dokuları, parlaklıkları ve gecikmeleri gibi yıldırım çarpmasının elemanları her saldırıda rastgeleleştirilir.
- Ses ve gönderi FX değişiklikleri grev FX ile uyumludur.
- İçeride veya yozlaşmış bölgede olan oyuncular onları göremez veya duymaz.
Çeşitli parametreleri ve zamanları hesaplayan bir sunucu tarafı Script var ve bunları tüm istemcilere gönderir ve rastgele bir süre bekler:
local function LightningUpdate()
while true do
task.wait(rand:NextNumber(3.0, 10.0))
local info = CreateFXData()
lightningEvent:FireAllClients(info)
end
end
İçinde CreateFXData , tüm müşteriler aynı parametrelere sahip olabilmeleri için bilgi yapısını dolduruyoruz.
Müşteri tarafında ( LightningVFXClient ), bu müşterinin FX'i çalıştırması gereip geremediğini kontrol ederiz:
local function LightningFunc(info)
…
-- İçeride FX yok
if inVolumesCheckerFunc:Invoke() then
return
end
-- ""normal" dünyada olmadığında FX yok
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end
…
Ayrıca, dokuları, pozisyonları ve parlaklığı ayarlamak için sırayı çalıştırıyoruz, gençleri kullanıyoruz ve task.wait(number) kullanıyoruz.Rastgele parametler, sunucudan aldığımız bilgi yapısından gelir ve bazı sayılar sabitlenir.
beam.Texture = textures[info.textIdx]beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)-- Silmebeam.Brightness = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- sestarihi: 2020-04-06if audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.wait(info.waitTillFlashes)-- and so on
Bir oyuncunun içerde olup olmadığını kontrol etmek için, önceden yerleştirilmiş hacimlerden yaklaşık iç bölgelere giden bir yardımcı inVolumesCheckerFunc işlevi kullanıyoruz ve oyuncunun konumunun herhangi birinde olup olmadığını kontrol ediyoruz (PointInABox).Dokunma tabanlı tespit kullanabilirdik, ancak bir oyuncu ses içinde bir koltuk aldığında, artık seslere "dokunmadığını" fark ettik.Birkaç kutuda bir noktayı test etmek daha basittir ve bunu yalnızca bir oyuncu önceden test edilmiş konumdan yeterince uzaklaştığında yaparız.
Bir oyuncunun yozlaşmış alanlarda olup olmadığını kontrol etmek için, mevcut oyun durumunu kontrol eden bir yardımcı gameStateInfoFunc fonksiyonu çağırıyoruz.Bir klasörden rastgele bir ses çalmak için, ayrıca bir yardımcı PlayOneShot fonksiyonu kullandık.Yıldırımların kendisi için, bunları Photoshop'ta oluşturmak süper kolaydı; kıvrımlı bir çizgi çizdik ve ardından "Dış Işık" katmanı efekti ekledik.


Parçacık yayıcı sistemlerini kullan
Kahraman yıldırımları, arka planında bulutların bir tabakasını yakarak uzaktaki vuruşlardan ışık alma izlenimini yaratarak uzak yıldırımları öneren bir parçacık sistemi tarafından desteklenir veya bulut-bulut ışıklandırması.Bu etkiyi, ana fırtına bulutunun kenarında bir bulut reklam panosu yanıp sönen çok basit bir parçacık sistemi aracılığıyla elde ettik.Sistem, rastgele bir saydamlık eğrisi ile periyodik olarak bulut parçacığı yayar:


Ağaçları rüzgarın estirdiğini yap
Bulutları ve yıldırımı istediğimiz şekilde çalıştırdıktan sonra, bir fırtınanın diğer iki önemli bileşenini eklememiz gerekiyordu: rüzgar ve yağmur! Bu elemanlar bazı zorluklar sunuyordu, Studio'nun fiziğimizin ve özel efekt sistemlerimizin mevcut sınırları içinde çalışması gerekiyordu.Örneğin, ağaçların gerçek rüzgar ile hareket etmesini sağlamak bugünün motorunda mümkün değildir, bu yüzden ağaçlar için parçacık yayıcı ve özel karakter animasyonları kullandık.
Rüzgar ve yağmurun etkisini gerçekten satmak istediğimizi biliyorduk, ağaçların kendilerinin hareket etmesine ihtiyacımız vardı.Motor içinde bunu yapabileceğiniz birkaç yol var, hareketli parçaları kullanarak plugins veya doğrudan modelleri animlayarak TweenService , halka açık olarak mevcut olanları kullanarak.Amaçlarımız için, animasyonlar bizim ağaçlarımızdan istediğimiz hareketi kontrol etme yeteneği verdi ve deneyim içindeki tüm ağaçlar arasında paylaşabileceğimiz tek bir animasyon kullanmamıza izin verdi.
Sonlandırma Model Paketi - Orman Varlıklarından birkaç ağaç kabuğu çıkararak başladık.Bu ağaçlar zaten var olduğundan ve deneyimimiz Pasifik Kuzeybatı'da gerçekleşti, her bir ağaç modelini oluşturmak zorunda kalmadan bize biraz erken bir süre kazandırdı.

Ağaçlarımızı seçtikten sonra, onları kabartmamız gerektiğini biliyorduk.Bir örsü kabartmak, örsü başka bir 3B modelleme uygulamasında eklemleri (veya kemikleri) bir örse eklemek ve ardından bu eklemler/kemiklere etki uygulayarak örsü hareket ettirmek, örneğin Blender veya Maya.Bu en çok insansoid karakterlerde kullanılır, ancak özel karakterlerle , hemen hemen her şeyi deriye döndürebilirsiniz.
Zaman kazanmak ve aynı animasyonu yeniden kullanmak istediğimizi biliyorduk, bu yüzden ilk ağacımızı kurduk ve ortak isimlerin genel olduğundan emin olduk, çünkü bu isimleri diğer ağaçlar için kullanmak istedik.Ayrıca, kütüklerin rüzgar ile eğilmesi, dalların sallanması ve yaprakların tepme gibi görünmesi için birincil, ikincil ve üçüncül eklem/kemiğe ihtiyacımız olduğunu biliyorduk.Bu süreç için, herhangi bir eylemin nesnenin diğer kısımlarını bu eyleme tepki vermesine ve başlangıç hareketine yetişmesini sağladığı bir animasyon konsepti olan bir ikincil hareket oluşturmalıydık.

Ortaklarımızı/kemiklerimizi oluşturduktan sonra, Studio'daki tüm eklemleri ve kemikleri hareket ettirmek için bir test animasyonu oluşturma zamanı geldi, böylece istediğimiz şekilde hareket ettiğini görebildik.Bunu yapmak için, ağacı Studio'ya ithal etmeliydik 3D İçe aktarıcıda Özel Rig ayarını kullanarak, ardından Animasyon Editörü kullanarak meshi hareket ettirmek/animasyon yapmak zorundaydık.Bu testlerden sonra malzemeleri ve dokuları ayarladık, ancak aşağıdaki sonucu görebilirsiniz.

Bu ağaçtaki sonuçlardan memnun kaldıktan sonra, aynı animasyonu farklı bir ağaçta test etme zamanı geldi! Zaten her ağaç yaziçin farklı kasalar arasında aynı animasyon olacağını biliyorduk, bu yüzden sadece yüksek bir Redwood ve sert bir Beechwood ağacı arasında çalışabilecek kadar genel görünen bir animasyon olduğundan emin olduk!

Bunu yapmak için, bu Orman Paketinden Beechwood ağacını aldık ve aynı isimleri kullanarak benzer bir ekipman inşa ettik.Bu yüzden daha önce ithal ettiğimiz animasyon da bu ağa uygulanabilirdi.Animasyonların tümü döner eklemler üzerine dayandığından, ağacın ne kadar büyük, küçük, uzun veya geniş olduğu önemli değildi!

Beechwood ağacını ayarladıktan ve cildi çıkardıktan sonra, onu ithal edip aynı animasyonu uygulayabiliriz.Bu, sadece bir dosyada döngü yapmak ve düzenlemek gerektiğini ve deneyimin çalıştırılması sırasında daha az animasyonla performans kaydedilmesini de sağladı.

Tüm animasyon türlerini istediğimizde sahip olduktan sonra, her birini paketlere dönüştürdük, böylece deneyimin ana bölgesindeki birkaç animasyonu düzenlemeye ve güncellemeye devam edebildik.Performans maliyetlerinin olduğunu bildiğimizden, etkinin en değerli olduğu evin etrafında dikkatli bir şekilde kullandık! Gelecekte bunun daha verimli hale gelmesiyle, giderek daha fazla ciltlanmış meshes örneği ekleyebileceksiniz!

Fırtına kalıntıları yapın
Yağmurun ağır görünmesini ve sis ve çöpün ağaçların içinden geçmesini istedik.Bunu yapmak için, büyük fırtına bulutlarının hemen altında çocuk parçacık yayıcıları olarak hareket edecek birkaç görünmez parça kurduk.Studio'daki parça sayısı sınırı nedeniyle, tüm uzay için bir parçacık verici kullanamadık.Bunun yerine, oynanabilir alan alanı üzerinde bir ızgara modelinde aynı boyuta sahip birkaçı ekledik, çünkü ağaçların varlığı oyuncuların çok uzağa göremeyeceğini ifade ediyor.

Yağmur parçacıkları, daha uzun veya daha kısa bir parçacık yapmanıza izin veren yeni bir parçacık yayıcı özelliğini kullandı ParticleEmitter.Squash .Yağmur için özellikle yararlıdır çünkü büyük bir yağmur dokusuna ihtiyacımız olmadığını ifade ediyordu, sadece oradaki olanı uzatın.Sadece bilin ki, ParticleEmitter.Squash değerini artırırsanız, genel ParticleEmitter.Size özelliğini de artırmak zorunda kalabilirsiniz, böylece çok ince olmaz! Genel olarak, deneyimin görünürlüğünü engellemek için yeterince fazla değer oynadık, ancak deneyimin görünürlüğünü engellemedi!


Sis, sis ve yaprakların geçmesi için, daha az alanı kapsayan tek bir daha büyük parça hacmi eklemek çok daha basitti, çünkü aynı anda bir sürü parçacığa ihtiyacımız yoktu.Bir ses ayarladık ve istedikleri parçacıkların frekansını aldık.


Bundan sonra, yapraklarımızın fırlatılmasını ve rüzgar dokularını yaptık ve parçacıkların tümünün farklı hızlarda dönmesine/hareket etmesine izin verdik ve farklı hızlarda başladılar.Bu, daha büyük sis parçacıklarının daha doğal bir şekilde etkileşime gireceği ve özellikle boyutları nedeniyle tekrarlayan bir dokuya çok benzemeyeceği anlamına geliyordu.



Sonuç, ağaçların hareket etmesi, pencere üflemesi ve fırtına merkezi gözünü çevreleyen fırtına etkisini yaratmak için yıldırım arasında bazı harika eylemlerdi.
Fırtına gözünü kurun
Parıldayan çekirdeği olan kırık taş gözü, oyunculara evde keşfetmeleri gereken gizemli ve gizemli bir şey olduğu ilk ipucunu vermek için tasarlanmıştır.Sahnemiz karanlık ve göz gökyüzünde yüksek olduğundan, inandırıcı bir kırık taş silueti oluşturmak önemliydi, ancak oyuncular bunu göremeyecekti, bu yüzden inandırıcı taş yüzey detayları oluşturmak önemli değildi.Oyuncuların sahnenizin aydınlatmasında görmesi için gerçekçi olan şeyleri bilmek, gereksiz detaylara bir sürü zaman harcadan önce geliştirme sürecinde birçok kaynağı kurtarabilir.


Oyuncudan uzaklık ayrıca gözün yüzey detayları için normal bir haritaya tümüyle güvenebileceğimizi de ifade ediyordu, böylece örs çok büyük bir performans maliyeti olmadan tüm o güzel detayları alabildik! Detayları yüksek bir pol meshesine heykelt ettik ve normal haritasını çok daha düşük bir pol küreye pişirdik, böylece tüm bu güzel detayları büyük bir performans maliyeti olmadan elde edebildik!



Göze doğaüstü bir duygu eklemek ve varlığını vurgulamak için, çatlaklarından geçecek parlak, neon bir magma yaratmaya karar verdik.Yüzey görünümü için yayıcı bir kanal olmadığından, bu engeli 2 küreden göz yaratarak aşıyoruz: biri kayalı dış yüzey için ve diğeri parlayan magma için biraz daha küçük.In Madde Boyacısı , iç çekirdeğin geçmesini istediğimiz alanlarda transparans ile dış küre için bir temel renk dokusu oluşturduk.In Blender , biz "vertex painted" iç küreyi ucuz ve kolay bir şekilde bazı renk değişiklikleri elde etmek için uyguladık.

Gözü oluştururken karşılaştığımız bir diğer zorluk, gözün oyuncudan uzaklığı ile yayın akışı birleştirmemiz tarafından dayatıldı.Bu yapının merkeziyetine göre, uzaklığına rağmen daima görünür olmasını istedik, ancak meshesine herhangi bir saldırı yapmadıysa, oyuncular solaryumda olmasalar bile gözü göremiyorlardı.Gözün sahnede sürekli varlığını göze ve yüzüğüne biraz geometri ekleyerek zorlayabildik.Bu geometri, arazinin yüzeyinin hemen altında oturur ve motoru kürenin oyuncuya daha yakın olduğunu düşünmeye ve daima yayınlamaya ikna etmek için yeterlidir.Ancak bu, çok fazla büyük nesneyi yayınlamaya zorlamak yerine oldukça seyrek yapılmalıdır, çünkü çok fazla sayıda büyük nesneyi yayınlamak, yayının yararlarını ortadan kaldırabilir ve oyun sözleşme imzalamaolumsuz etkileyebilir.
Göze ve yüzüklerine hareket ekleyebildik, çünkü bulut örüntülerini döndürmek için kullandığımız aynı senaryo sayesinde.Son dokunuş için, bulutların ötesinde başka bir dünyanın varlığına bir ipucu eklemek istedik, ancak sahneye daha fazla geometri eklemekten ve ayrıca yayın etkinleştirilmesiyle ortaya çıkan önceden bahsedilen engellerle uğraşmaktan kaçınmak için yaratıcı bir yaklaşım benimsemek zorundaydık.Nesnelerin mutlak büyüklüğü ve mesafesi nedeniyle çok fazla derinliğe sahip bir sahne yarattık, bu sahnenin görüntüsünü çizdik, ardından bu görüntüyü fırtına gözünün hemen arkasına yerleştirilen bir parçada bir çıkartma olarak kullandık.Bu parçayı göz ve yüzükleri için kullandığımız aynı yöntemi kullandık.

Genişleyen panoyu yap
Üretmek için en eğlenceli şeylerden biri, oyuncuların gerçeklik beklentilerini kelimeleri değiştirerek etrafında değiştirebildiğimiz yozlaşmış alanlardı.Örneğin, babanın bulmacasında, ne kadar hızlı koşarsanız da olsa, odanın gittikçe daha uzun hissettiği bir kabus benzeri bir anı taklit etmek istedik.Odayı normale döndürmek için malzeme ararken oyunculardan kaçacak geniş bir mutfak yapmaya karar verdik.
Duvarların basit bir hareketiyle ve mutfağın her iki tarafına görünecek akıllı bir oda düzeniyle bunu ayarladık.Odanın normal durumunda, mutfak basit bir koridordu, ancak yozlaşmış alanda aslında birkaç kanat ve sahte bir duvarla çok daha uzundu!


Sahte duvar, oyuncuların bir tetikleyici hacim girdiğinde geri döneceğimiz bir model grubuydu, ki bu daha önce mutfağın transparan bir kısmından geçeceklerdi.Bu tetikleyici, tüm kapılarımızda kullanılanlarla benzer bir senaryoda da kullanıldı ve TweenService bir hedeften diğerine taşınması için çağrıldı.Duvar için başlangıç ve bitiş pozisyonlarının olduğu yere yönlendirme işlemini söylemek için parça hacimlerini kullandık.


Çünkü TweenService genel bir sistem olduğundan, tüm duvar veri modelimiz aynı bileşenleri içermek zorundaydı.Örneğin, genel bir "Door_Script" senaryosu, "Grow_Wall" modelinin altında tanımlanan bir ses çalar.Aynı senaryo, aşağıdaki kod örneğinde bazı değişiklikler yapılarak, ayrıca mutfak hareketi için ses tetikledi.Bu, harekete çok şey ekledi!
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, --Kapının Orta Süresi/Hızı
Enum.EasingStyle.Quart, --Kolaylaştırma Stili
Enum.EasingDirection.InOut, --Kolaylaştırma Yönü
0, --Tekrar Sayısını Tekrarla
false, --Gerçek tersine çevir
0 --Gecikme
)
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["Kapı"]:Oyna()
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("dokunma")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end
Sahte duvarı odanın arkasına hareket ettirdikten sonra, içeriğin geri kalanının onunla hareket etmesi gerekiyordu.Bunu yapmak için, mutfakta bulunan tüm serbest eşyaların hareket ederken duvara kaynaklanması gerekiyordu.Kaynak Kısıtlarını kullanarak, tüm nesneleri tek bir nesne olarak hareket etmek için mutfak duvarına hızlıca kaynaklayabildik.Bunu yapmak, bu öğeleri kaynaklardan çıkarma seçeneğimiz olduğunu gösterdi, böylece oyuncular onlara çarparak ve etrafında dolaşarak çarpabilirdi!
Kirlenmiş ağaç evi yap
Stüdyo, her şeyi sallanan bir kapıdan dönen bir platforma kadar oluşturmak için kullanabileceğiniz muhteşem fiziksel tabanlı bir motor.Demomuzla, gerçekçilik duygusu yaratmak için fiziği bir başka gerçekçi olmayan ortamda kullanmak istedik.Sadece birkaç kısıtlama kullanarak, kendi deneyimleriniz içinde bazı eğlenceli ve zorlu engel parkurları oluşturabilirsiniz!

Kısıtlamalar bir grup fiziksel tabanlı motor, nesneleri uyumlaştırır ve davranışları kısıtlar.Örneğin, sabit bir mesafe birbirinden uzak tutmak için nesnelerle bağlantı kurmak için bir çubuk sınırı veya çizginin sonundan bir lamba asmak için bir ip sınırı kullanabilirsiniz.Oğlunun, oyuncuların çalışmanın yozlaşmış durumuna taşındığı bulmacası için, dünyayı kelimenin tam anlamıyla tersine çevirmek istedik.Böyle yapmak, oyuncuların gerçeklik beklentilerini ve kurallarını alt üst ederken, hala niyet edildiği gibi fizik sistemini kullanıyor!

Oyuncular bir kez bulmacanın ana bölgesine kadar çalıştıktan sonra, Roblox'ta tanıdık bir manzara ile karşılandılar: bir engel kursu.Bu özel engel kursu, hikayeyi ilerleten "güvenli alanlar" ile birlikte birkaç dönen platform ve dönen duvarlardan oluşuyordu.Dönen/çeviren elemanlara odaklanacağız.

Neden burada sınırlar kullandık? Çünkü TweenService veya diğer yöntemler oyuncuyu ayakta dururken hareket ettirmeyecekti.Oyuncuyu hareket ettirmeden, birisi bir platforma atlayabilir ve altından dönecektir.Bunun yerine, oyuncuların bir sonrakine atlamaya çalışırken dönen bir platformdan gezinmesini istedik.Bu yaklaşım nedeniyle, oyuncular kursa nasıl devam edeceklerine dair bir karar verirken durdukları yerde kendilerini hissettiler ve dönen bir yüzeyle hareket ettiklerinden emin olmak için özel bir şey yapmak zorunda değildik!

Bunu yapmak için, önce mevcut kitimizden varlıkları kullanmak ve görsel bir etki için herhangi bir yeni içerik eklemek gerekiyordu.Ağaç evi inşa eden büyükannenin hikayesini anlatmak için bazı tamamlanmamış duvarlar ve platformlar yaptık ve içlerinde delikler vardı.Eşsiz bir dizi platform oluşturmak istemediğimiz için, 4 farklı temel parça ve korkuluk parçaları ayrı ayrı yaptık.Bu, birçok çeşitlilik elde etmek için bireysel taban ve korkuluk parçalarını karıştırmamıza izin verdi.

Sınırları kullandığımız için bu örüntüleri sabitleyemeyeceğimizi biliyorduk, çünkü bir sınır/motor onları sürmekle bile hareket etmeyeceklerdi.Platformun sadece dünyadan düşmemesi için sabitlenen bir şeyin çocuğu olması gereken kısıt.Bunu, platformun genel hareketini sürmek için Motor_Anchor adlı bir parçasında bulunan eklem kısıtlaması ile çözdük.Bundan sonra, iki meshin bir olarak hareket etmesi gerekiyordu, bu yüzden adlandırdığımız bir parça olan Motor_Dönüş oluşturduk ve iki meshevi buna kaynakladık.Bu şekilde kısıt, çoklu parça ile çalışan çok sayıda menteşeye karşın tek bir parçada çalışabilir.

Şimdi, kilit sınırının gerçek davranışını ayarlama ve parçanın ve sınırın birlikte yönlendireceği ekleri eklemek için zamanı gelmişti.Yürüyüş parçalarına kaynaklanan Motor_Döndür'e döndürme ekipmanı yerleştirdik ve Motor_Kelepçe'nin kenarında yer alan motor davranışı için başka bir ekipman daha ekledik, kızak sınırının yanında.Bunun sahip olbaşına dönmesi gerektiğinden, oyuncuya etkilenmek yerine (bir kapı menteşesi gibi), HingeConstraint.ActuatorType 'yi Motor olarak ayarladık, sınırı kendi hareket eden bir motor gibi ele aldık.
Platformları sabit bir hızda döndürmek için, ardından bir oyuncu üzerine atladığında hareketi engelleyip kesintiye uğratmayı engelleyecek HingeConstraint.AngularVelocity , HingeConstraint.MotorMaxAcceleration ve HingeConstraint.MotorMaxTorque özelliklerini değerlere ayarladık.

Şimdi dönen duvarlar yapmak zorundaydık.Duvarlar görünür merkezlerinde döndürmek gerekiyordu ve onların seviyenin geri kalanına ilişkin herhangi bir yönü ele alabilmelerini istediğimizi biliyorduk.Platformlar gibi, bunları tüm duvarların sabit olmadığı ve Motor_Turn'a kaynaklanmış olduğu şekilde inşa ettik.

Temel materyallere biraz çeşitlilik eklemek için Texture nesnelerini SurfaceAppearance nesnelerinin üzerine kullandık.Dokular, resimlere benzer, bir mesh'in uçağına bir görüntü yerleştirmenize izin verir.Bu, bir tuğla duvara kir eklemek veya aynı temel ahşap malzeme kullanırken ahşabı yaşlı görünmesini sağlamak istiyorsanız yararlı olabilir.Texture nesneler, çizgileri ve görüntüleri istediğiniz şekilde yerleştirip ofset edebildiğiniz Decal nesnelerden biraz farklı bir davranışa sahiptir, ki bu, örtüşüm dokusunu ölçeklendirmek ve tekrar etmek istemediğinizde çok yararlıdır!

Birkaç platformu ve döner duvarları test ettikten sonra, bir engel parkuru zorlu, zihin açıcı ve ayrıca oyuncunun gitmesi gereken yer olduğundan emin olmak için birkaç değişiklik yaptık! Onları iyi çalıştırmak için değerleri ve konumlarını her ikisine de ayarlamak gerektiği için biraz ayar yaptık.Platformlar ve duvarlar birbirine veya çevreye vuruyordu birkaç noktada, ancak bazı hareketler ve sık sık testler yaparak, demodaki ayarlara inebildik!
Fiziksel nesnelerin neye vurduğundan emin değilseniz, 3D görüntü penceresinin sağ üst kısmındaki Görselleştirme Seçenekleri düğmesinden çarpışma sadakati açabilirsiniz.



Aşağıdaki gibi kapı/pencere delikleri görülebilir, ancak alt paneller gibi daha küçük detaylar görülemez.Bunun nedeni, duvarlar için CollisionFidelity özelliğinin Kutu olarak ayarlanmasıdır.Bu paneller için hassasiyete ihtiyacımız yoktu, bu yüzden performans maliyetinden tasarruf etmek için, oyuncuların atlayabileceği kadar detaylıydı.Platformlar ve dönen duvarlar yapıldıktan sonra, sadece kutular ve lambalar gibi ayrıntı varlıkları eklememiz gerekiyordu, ardından oynanmaya hazırdı!
