การสร้างการเคลื่อนไหวในทุกสิ่งแวดล้อมภายในประสบการณ์ช่วยให้รู้สึกมีความ immersive และสมจริงกับโลกของเราได้ทันที ไม่ว่าจะเป็นการเคลื่อนไหวของต้นไม้ การตอบสนองจากความสัมพันธ์ของผู้เล่นในการเปิดประตู หรือแม้แต่กล่องที่เคลื่อนที่เมื่อมีการชนกัน Studio มีวิธีการเฉพาะเจาะจงมากมายในการสร้างการเคลื่อนไหวเพื่อช่วยให้โลกมีชีวิตชีวามากขึ้น รวมถึงระบบฟิสิกส์ TweenService และการเคลื่อนไหว โดยการวิเคราะห์ความต้องการที่เฉพาะเจาะจงของประสบการณ์ของคุณจะช่วยให้คุณตัดสินใจได้ว่าควรใช้วิธีใด ในส่วนนี้ เราจะแสดงให้เห็นว่าเราตัดสินใจว่าประเภทการเคลื่อนไหวแบบใดที่เราต้องการสร้างใน Studio และเครื่องมือใดที่เราใช้ในการบรรลุเป้าหมายที่แตกต่างกันเหล่านี้

สร้างพายุ
พายุผ่านหลายช่วงก่อนที่เราจะตัดสินใจว่าจะทำอย่างไรใน The Mystery of Duvall Drive ในช่วงแรก เราคิดเกี่ยวกับพายุในรูปแบบของเสาหินสีดำขนาดใหญ่ และในช่วงการพัฒนาครั้งต่อมาเราคิดว่ามันจะเป็นพอร์ทัลขนาดใหญ่ไปยังพื้นที่ที่เสื่อมโทรม หลังจากทดลองกับพายุที่มีลักษณะและความรู้สึกที่แตกต่างกันหลายแบบ เราจึงตัดสินใจเลือกพายุที่มี "ตา" ที่อยู่กลางขนาดเล็ก เพราะว่า:
- พายุควรสร้างความรู้สึกถึง ผลกระทบของเหตุการณ์นี้ต่อโลก รวมถึงต้นไม้ที่ปลิวและซากปรักหักพังที่ลอยอยู่รอบๆ
- วงเวียนหมุนของเมฆควรให้ผู้เล่นเห็นพอร์ทัลกลาง โดยไม่เปิดเผยทั้งหมด ซึ่งจะกระตุ้นให้ผู้เล่นต้องตรวจสอบใกล้ๆว่าเกิดอะไรขึ้น
- จุดของแสงที่แน่นหนาจะช่วยให้เรา มุ่งเน้นที่องค์ประกอบของบ้าน ซึ่งเป็นตัวละครหลักและสถานที่ส่วนใหญ่อยู่ในมัน




เพื่อทำให้พู้รู้สึกถึงการพัฒนา มีความรุนแรงและเปลี่ยนแปลงอยู่ในสิ่งแวดล้อม เราใช้ระบบและฟีเจอร์ดังต่อไปนี้:
- TweenService - สำหรับการเคลื่อนไหวของเมฆ
- การเปลี่ยนแสง - สำหรับการสร้างฟ้าผ่าจากเมฆไปถึงเมฆ
- Beams - สำหรับ "การสร้างแสงแบบวอลลูเมตริก" และสายฟ้า
- Particulate Emitters - สำหรับซากปรักหักพังที่ลอยไปยังพอร์ทัลและบินไปรอบๆเนื่องจากลมพัดแรง
- Animations - สำหรับต้นไม้ที่ปลิวไปตามลม
เพิ่มเมฆด้วยพื้นผิว
ในขณะที่ เมฆแบบไดนามิก เหมาะสำหรับเมฆที่มีความจริงสูงในระดับสูง เราต้องการสิ่งที่ให้ความรู้สึกมีพลังและที่เราสามารถควบคุมและกำหนดค่าได้มากขึ้น เพื่อทำเช่นนี้ เราได้ใช้วัตถุ surface appearance ที่มีความโปร่งแสงบางส่วนต่อชุดของเมฆที่ซ้อนกันอย่างหนักและชั้นเพื่อสร้างภาพลวงตาของการคลุมเมฆ ทำไมเราถึงซ้อนพวกมันและชั้นเป็นจำนวนมาก? เพราะว่าเมื่อแต่ละเมฆมีการเคลื่อนไหวที่แตกต่างกัน จะทำให้มันตัดกันและสร้างรูปแบบเมฆที่เข้าไปและออกจากกันได้ กระบวนการนี้ทำให้เมฆดูมีชีวิตชีวามากขึ้นและดูเป็นธรรมชาติมากขึ้นถึงแม้จะเป็นเพียงดิสก์ที่หมุน นอกจากนี้ยังมีความสำคัญที่เมฆจะต้องมี ความโปร่งแสงบางส่วน เพราะเราต้องการให้ผู้เล่นสามารถมองเห็นสิ่งที่สว่างอยู่ตรงกลางก่อนที่จะมาถึงบ้าน!


เนื่องจากแต่ละเมฆต้องมีขนาดใหญ่พอที่จะล้อมรอบบ้านและสื่อให้ทราบว่าพายุมีความใหญ่โตเพียงใด เราจึงรู้ว่าต้องทำการจัดเซรามิคที่เราต้องการใช้บนเมฆแต่ละขนาดที่เป็นผลทำให้มันมีการซ้ำที่หนักในตลอดพื้นผิวของเมฆ เราทดลองใช้วัสดุที่เราสร้างเป็นเมฆบนส่วนเหล่านั้น จากนั้นนำไปใช้กับวงเวียน!

ต่างจากอิมิเตอร์พาร์ติเคิลหรือคาน เมฆทำให้เราสามารถสะท้อนแสงออกจากแต่ละเมฆ ซึ่งสำคัญสำหรับเมื่อเราต้องการทำฟ้าผ่าระหว่างเมฆ ในขณะเดียวกัน เราก็ทำการโมเดลด้วยการหมุนเพื่อให้แสงที่สะท้อนออกจากมันดูเหมือนมีความลึก! นี่เป็นสิ่งสำคัญโดยเฉพาะในสถานการณ์ที่ความต้องการในการทำงานของประสบการณ์ดึงคุณภาพของวัตถุพื้นผิวของเราไปลง

หมุนเมฆ
หลังจากที่เราพอใจในลักษณะที่ปรากฏโดยรวมของเมฆ เราต้องทำให้มันเคลื่อนที่! เรามีรูปร่างทั่วไปของแต่ละชั้นเมฆ แต่ก็ใช้เวลาหน่อยสำหรับการทดลองและความผิดพลาดเพื่อให้แน่ใจว่าเอฟเฟกต์การหมุนดูดีในทางปฏิบัติ ในตอนแรกเราลองใช้ constraints เพื่อเพิ่มความเร็วที่ทำให้มันเคลื่อนไหวได้ ทางเลือกนี้มีความยากมากกว่าที่เราต้องการในภายหลัง และผู้เล่นจะไม่เคยมีการตอบสนองต่อมันเลยทำให้ไม่จำเป็นต้องมีความถูกต้องในการเคลื่อนไหวเท่าไหร่
เราต้องการวิธีที่ใช้งานง่ายเพื่อหมุนตัวที่อยู่ระหว่างที่อยู่ห่างหรือเล็กหรือสวยงามเกินไปที่จะมีความสำคัญกับเกมเพลย์/ฟิสิกส์ เช่น เฟอร์นิเจอร์ภายในที่มีขนาดเล็กเช่น โคมไฟขนาดเล็ก เราจึงตัดสินใจใช้ LocalScript เพื่อลดปริมาณข้อมูลที่ส่งผ่านระหว่างเครื่องลูกและเครื่องแม่ ช่วยให้การเคลื่อนไหวมีความราบรื่นมากขึ้น โดยให้แต่ละโมเดลเมฆมีอัตราการหมุนและการหน่วงที่แตกต่างกัน ในการทำให้มันทั่วไปมากขึ้น เราก็ทำให้สามารถระบุแกนหมุนได้ มันสามารถใช้ 3 คุณสมบัติ แต่สำหรับกรณีของเราเราจะใช้ 3 ค่า: Axis, Delay, และ Speed.

เหมือนกับกรณีหลายๆ กรณีในเดโม เราใช้แท็ก LocalSpaceRotation เพื่อให้เราสามารถจัดการกับอินสแตนซ์ที่ได้รับผลกระทบใน Studio โดยใช้ปลั๊กอินแท็กอินสแตนซ์ เราได้ใช้เพียง LocalScript เดียวที่จัดการกับทุกอินสแตนซ์ที่ถูกแท็กโดยใช้ CollectionService เพื่อที่ว่าเราจะไม่มีกระบวนการดูแลรักษาสคริปต์จำนวนมากในระหว่างการพัฒนา
ในเดโมของเรา ส่วนของโลกจะถูกคัดลอกจาก ServerStorage ไปยังพื้นที่ทำงานตามความจำเป็น ดังนั้นเราจึงต้องจัดการกับกรณีที่มีการสร้างและทำลายวัตถุที่ถูกแท็ก โดยใช้ LocalScripts เรายังต้องคำนึงถึงการสตรีมซึ่งเมฆและค่าอีฟต้องถูกสตรีมออกและเข้ามา เราได้จัดการวัตถุที่ถูกวางไว้ในฟังก์ชัน Init() และเชื่อมต่อกับ CollectionService.GetInstanceAddedSignal และ CollectionService.GetInstanceRemovedSignal สำหรับวัตถุที่ถูกแท็กเพื่อจัดการกับวัตถุที่สร้าง/ทำลายใหม่ ฟังก์ชัน SetupObj เดิมถูกใช้เพื่อเริ่มต้นวัตถุใหม่ใน Init() และใน CollectionService.GetInstanceAddedSignal.
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 คือแผนที่ที่มีข้อมูลสำหรับวัตถุที่เกี่ยวข้องทั้งหมด เช่น ความเร็วในการหมุนและแกนของพวกมัน โปรดทราบว่าเราจะไม่เรียก SetupObj จาก CollectionService.GetInstanceAddedSignal ในทันที แต่เราจะเพิ่มวัตถุไปยัง objInfoQueue ด้วยการสตรีมและการทำซ้ำวัตถุในเซิร์ฟเวอร์ เมื่อเราเรียก CollectionService.GetInstanceAddedSignal เราอาจยังไม่มีค่า Axis, Delay, และ Speed ดังนั้นเราจึงเพิ่มวัตถุเข้าในคิว และเรียกใช้ SetupObj ในเฟรมต่อไปจากฟังก์ชัน Update จนกว่าค่าจะมาถึงและเราสามารถอ่านเข้าไปใน "ข้อมูล" ของวัตถุได้
เราหมุนอินสแตนซ์ในฟังก์ชัน Update ที่เชื่อมต่อถึง heartbeat เราได้พาราเมาท์ของผู้ปกครอง (parentTransform) สะสมมุมหมุนใหม่ (curObjInfo.curAngle) ตามความเร็วการหมุนของวัตถุนี้ คำนวณการแปลงท้องถิ่น (rotatedLocalCFrame) และในที่สุดตั้งค่าเป็น CFrame โปรดทราบว่าทั้งผู้ปกครองและวัตถุก็สามารถเป็น Model หรือ MeshPart ดังนั้นเราจึงต้องตรวจสอบ IsA("Model") และใช้ทั้ง PrimaryPart.CFrame หรือ CFrame.
local parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- primary part might might not be streamed in yetcontinue -- wait for primary part to replicateendparentTransform = 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
เราได้ตรวจสอบให้แน่ใจว่าสำหรับ Model.PrimaryPart ถูกตั้งค่าเพื่อจัดการกับการสตรีม หากมีการ Update ในวัตถุของเราขณะที่ Model.PrimaryPart (ที่สามารถชี้ไปยังเมฆที่เป็นบุตร) ยังไม่ถูกสตรีม เราจะข้ามการ update นั้นไปเลย ระบบปัจจุบันเป็นรุ่นที่สองของการหมุนวัตถุ โดยระบบก่อนหน้านี้ทำงานแตกต่างกันค่ายังไงขอให้เราเก็บข้อมูลตรงนี้ เราแปลงมันในสคริปต์ของเราเป็น "12 * obj.Speed.Value".
ออกแบบฟ้าผ่า
เนื่องจาก Studio ไม่ได้เสนอเครื่องสร้างฟ้าผ่าสำเร็จรูป และระบบพาร์ติเคิลมีข้อจำกัดบางประการที่ไม่เหมาะสำหรับการสร้างฟ้าผ่าที่มีความสำคัญ, เราจำเป็นต้องใช้ความคิดสร้างสรรค์ในการหาทางออกสำหรับฟ้าผ่าที่มีความสำคัญเหล่านี้ เราตัดสินใจใช้ระบบหลักสองอย่างสำหรับการสร้างฟ้าผ่า: คานมีพื้นผิวสำหรับฟ้าผ่าที่มีความสำคัญที่มาจากตามกลางพายุและจอมศิลป์ที่แสดงออกซึ่งตรงตามเสียงและการประมวลผลภาพตลอดเวลา และเอฟเฟกต์พาร์ติเคิลง่ายๆ สำหรับฟ้าผ่าระหว่างเมฆ
คานมีพื้นผิว
เรามักจะใช้ตัวจัดลำดับเวลา หรือเครื่องมือเวลาที่ขับเคลื่อนเวลาของการฟ้าผ่าแบบนี้ แต่เนื่องจาก Studio ยังไม่เสนอฟังก์ชันนี้ เราจึงตัดสินใจเขียนสคริปต์ที่ควบคุมเวลาการฟ้าผ่า การสคริปต์เอฟเฟกต์นี้มีความเรียบง่าย แต่ก็ทำให้บรรลุเป้าหมายสำคัญเหล่านี้:
- องค์ประกอบของฟ้าผ่า เช่น พื้นผิว สว่าง และการหน่วงเวลา จะถูกสร้างแบบสุ่มในแต่ละครั้งที่ฟ้าผ่า
- เสียงและการเปลี่ยนแปลงโพสต์ FX จะซิงค์กับเอฟเฟกต์ฟ้าผ่า
- ผู้เล่นที่อยู่ภายในหรือในพื้นที่ที่เสื่อมจะไม่สามารถมองเห็นหรือได้ยินพวกมันได้
เรามี Script ฝั่งเซิร์ฟเวอร์ที่คำนวณพารามิเตอร์และเวลาที่แตกต่างกัน ส่งไปยังผู้เล่นทั้งหมด และรอเวลาสุ่มจำนวนหนึ่ง:
local function LightningUpdate()
while true do
task.wait(rand:NextNumber(3.0, 10.0))
local info = CreateFXData()
lightningEvent:FireAllClients(info)
end
end
ภายใน CreateFXData เรากรอกโครงสร้าง info เพื่อให้ผู้เล่นทั้งหมดได้รับพารามิเตอร์เดียวกัน
ในด้านลูกค้า (LightningVFXClient), เราเช็คว่าลูกค้านี้ต้องใช้ FX หรือไม่:
local function LightningFunc(info)
…
-- no FX when indoors
if inVolumesCheckerFunc:Invoke() then
return
end
-- no FX when not in the "normal" world
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end
…
นอกจากนี้เรายังเรียกใช้ลำดับเพื่อตั้งค่าพื้นผิว ตำแหน่ง และความสว่าง ใช้เวลาเลื่อนไหล และใช้ task.wait(number). พารามิเตอร์แบบสุ่มจะใช้จากโครงสร้าง info ที่เราได้รับจากเซิร์ฟเวอร์ และบางค่าจะมีการตั้งค่าคงที่
beam.Texture = textures[info.textIdx]beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)-- Wipebeam.Brightness = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- audioif audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.wait(info.waitTillFlashes)-- and so on
ในการตรวจสอบว่าผู้เล่นอยู่ภายในหรือไม่ เราใช้ฟังก์ชันช่วย inVolumesCheckerFunc ที่ตรวจสอบปริมาณที่วางไว้ล่วงหน้าสำหรับพื้นที่ภายใน และเช็คว่าตำแหน่งของผู้เล่นอยู่ภายในหรือไม่ (PointInABox) เราสามารถใช้การตรวจจับโดยใช้การแตะ แต่พบว่าตอนที่ผู้เล่นนั่งอยู่ภายในปริมาณ ทางนั้นพวกเขาจะไม่ได้ "สัมผัส" กับพื้นที่นั้นอีกต่อไป การทดสอบจุดในกล่องไม่กี่กล่องเป็นวิธีที่ง่ายกว่าและเราทำเมื่อตำแหน่งของผู้เล่นเคลื่อนไหวห่างจากตำแหน่งที่ทดสอบก่อนหน้านี้มากพอ
ในการตรวจสอบว่าผู้เล่นอยู่ในพื้นที่ที่เสื่อมเราจะเรียกใช้ฟังก์ชันช่วย gameStateInfoFunc ที่ตรวจสอบสถานะเกมปัจจุบัน สำหรับการเล่นเสียงสุ่มจากโฟลเดอร์ เราก็ใช้ฟังก์ชันช่วย PlayOneShot สำหรับฟ้าผ่าเอง การสร้างฟ้าผ่าเหล่านี้ก็ทำได้ง่ายมากใน Photoshop เราเพียงแค่สร้างเส้นโค้ง แล้วเพิ่มเลเยอร์เอฟเฟกต์ "Outer Glow".


ใช้ระบบพาร์ติเคิลอิมิเตอร์
ฟ้าผ่าที่มีความสำคัญนั้นได้รับการสนับสนุนจากระบบพาร์ติเคิลที่เสนอให้สอดคล้องกับการฟ้าผ่าห่างโดยสร้างให้เกิดภาพลวงตาของชั้นเมฆในพื้นหลังที่จับแสงจากฟ้าผ่าที่ห่างไกลหรือฟ้าผ่าระหว่างเมฆ เราสามารถบรรลุเอฟเฟกต์นี้ผ่านระบบพาร์ติเคิลที่ง่ายมากซึ่งปล่อยพาร์ติเคิลเมฆเป็นระยะๆกับการตั้งค่าความโปร่งแสงแบบสุ่ม:


ทำให้ต้นไม้ปลิวไปตามลม
หลังจากที่เรามีเมฆและฟ้าผ่าทำงานได้ตามที่เราต้องการแล้ว เราจำเป็นต้องเพิ่มอีกสององค์ประกอบหลักของพายุ: ลมและฝน! องค์ประกอบเหล่านี้นำเสนอความท้าทายหลายอย่าง รวมถึงต้องทำงานภายใต้ข้อจำกัดที่มีอยู่ในฟิสิกส์และระบบเอฟเฟกต์พิเศษของ Studio ตัวอย่างเช่น การทำให้ต้นไม้เคลื่อนที่ตามลมจริงๆเป็นไปไม่ได้ในเครื่องยนต์ในปัจจุบัน ดังนั้นเราจึงใช้เอฟเฟกต์พาร์ติเคิล particle emitter และ การเคลื่อนไหวที่กำหนดเอง สำหรับต้นไม้
เรารู้ว่าเพื่อลดความรู้สึกของลมและฝน เราจำเป็นต้องทำให้ต้นไม้เคลื่อนที่ด้วย ตัวเลือกในการทำเช่นนี้มีหลายวิธีในเครื่องยนต์ รวมถึงการเคลื่อนที่ด้วย ปลั๊กอิน ที่มีให้โดยสาธารณะ การใช้ TweenService หรือการเคลื่อนไหวโมเดลโดยตรง สำหรับจุดประสงค์ของเรา การเคลื่อนไหวทำให้เราสามารถควบคุม motion ที่เราอยากให้ต้นไม้ของเราได้ และยังช่วยให้เราสามารถใช้การเคลื่อนไหวเดียวกันได้ซึ่งเราสามารถแชร์ระหว่างต้นไม้ทั้งหมดในประสบการณ์
เราเริ่มต้นโดยการสร้างต้นไม้อย่างมากจาก Endorse Model Pack - Forest Assets. เนื่องจากต้นไม้เหล่านี้มีอยู่แล้ว แล้วประสบการณ์ของเราจัดขึ้นในเขตชายฝั่งตะวันตก จึงช่วยให้เราประหยัดเวลาในช่วงต้นจากการสร้างโมเดลต้นไม้แต่ละต้น

หลังจากเราเลือกต้นไม้ เรารู้ว่าต้องสร้างโครงกระดูกให้พวกเขา Skinning a mesh คือการเพิ่มข้อต่อ (หรือกระดูก) ลงบนเมชในแอปพลิเคชั่นการสร้างโมเดล 3 มิติอื่นๆ เช่น Blender หรือ Maya และการใช้แรงกระทำกับข้อต่อ/กระดูกเหล่านั้นเพื่อให้เคลื่อนที่ เมื่อนี้มักจะใช้ใน ตัวละครมนุษย์ แต่กับ ตัวละครที่กำหนดเอง คุณสามารถสร้างเกือบทุกอย่าง
เรารู้ว่าต้องการประหยัดเวลาและใช้การเคลื่อนไหวเดียวกัน ดังนั้นเราจึงสร้างโครงสร้างต้นไม้แรกของเราและมั่นใจว่าพวกมันมีชื่อข้อต่อทั่วไปเพราะเราอยากใช้ชื่อเหล่านี้ในโครงสร้างของต้นไม้อื่นๆ นอกจากนี้เรายังรู้ว่าจำเป็นต้องมีข้อต่อ/กระดูกหลัก ข้อต่อ/กระดูกรอง และข้อต่อ/กระดูกย่อยสำหรับลำต้นที่จะงอขึ้นกับลม กิ่งจะโยกและใบจะดูเหมือนว่าระวังตัว เมื่อมีการสร้างกระบวนการนี้ เราจำเป็นต้องสร้าง การเคลื่อนไหวรอง ซึ่งคือแนวคิดการเคลื่อนไหวที่นำไปสู่การดึงดูดส่วนอื่นๆของวัตถุให้ตอบสนองต่อการกระทำและดูเหมือนจะจับเวลาที่เคลื่อนที่

เมื่อเราสร้างข้อต่อ/กระดูกได้แล้ว ก็ถึงเวลาสร้างการเคลื่อนไหวทดสอบเพื่อให้เคลื่อนไหวไปตามข้อต่อและกระดูกทั้งหมดใน Studio เพื่อตรวจสอบว่าเคลื่อนไหวในแบบที่เราต้องการได้หรือไม่ ในการทำเช่นนี้ เราต้อง นำเข้าต้นไม้ไปยัง Studio ผ่านการตั้งค่า Custom Rig ใน Importer แล้วเคลื่อนไหว/แอนิเมชั่นเมชนั้นโดยใช้ Animation Editor. เราจัดตั้งวัสดุและพื้นผิวหลังจากการทดสอบเหล่านี้แต่คุณสามารถดูผลลัพธ์ได้ด้านล่าง

หลังจากที่เราพอใจกับผลลัพธ์ในต้นไม้นั้น ก็ถึงเวลาทดสอบการเคลื่อนไหวเดียวกันในต้นไม้อีกต้น! เรารู้แล้วว่ามันจะต้องเป็นเคลื่อนไหวนั้นของแต่ละโครงสร้างต้นไม้ ดังนั้นเราจึงเพียงแค่ต้องมั่นใจว่าการเคลื่อนไหวของเราดูเพียงพอที่จะทำงานในต้นไม้ขนาดสูงกับต้นไม้ขนาดเตี้ย!

ในการทำเช่นนี้ เราใช้ต้นไม้ Beechwood จากแพ็คป่าและสร้างโครงสร้างที่คล้ายกัน โดยใช้ชื่อที่แน่นอนสำหรับข้อต่อ นี่หมายความว่าการเคลื่อนไหวที่เรานำเข้าไปจะสามารถนำไปใช้กับต้นไม้นี้ได้เช่นกัน เนื่องจากการเคลื่อนไหวทั้งหมดขึ้นอยู่กับการหมุนของข้อต่อ ขนาดของต้นไม้จะไม่สำคัญไม่ว่าจะเล็กใหญ่ หรือต้นไม้ขนาดสูงหรือต้นไม้ขนาดหนา!

หลังจากที่เรา ทำงานและสวม ต้นไม้ Beechwood แล้ว เราก็จะนำมันเข้ามาและให้การเคลื่อนไหวที่เหมือนกันกับต้นไม้ได้ ซึ่งหมายความว่าการแก้ไขและการปรับแต่งจะต้องดำเนินการในไฟล์เดียว นอกจากนี้ยังช่วยลดประสิทธิภาพพลังงานเมื่อจำนวนการเคลื่อนไหวถูกลดให้มีจำนวนที่น้อยลงเมื่อมีการทำให้ประสบการณ์ดูราบเรียบ

เมื่อเรามีต้นไม้ทั้งหมดที่ต้องการเคลื่อนไหว เราทำให้แต่ละคนเป็น แพ็กเกจ เพื่อเราจะสามารถแก้ไขและอัปเดตกฎเล่นการเคลื่อนไหวโดยเฉพาะรอบๆพื้นที่หลักของประสบการณ์ เนื่องจากเราทราบว่าตนมีค่าใช้จ่ายในการทำงาน จึงค่อยๆใช้พวกคุณที่รอบๆบ้านที่เอฟเฟกต์มีค่ามากที่สุด! ในอนาคตเมื่อสิ่งนี้ทำให้ดำเนินงานลดราคามากขึ้น คุณจะสามารถเพิ่มเติมการสร้างที่ของการมีไปรอบ

สร้างซากพายุ
เราต้องการให้ฝนดูเหมือนหนัก และให้หมอกและซากปรักหักพังเคลื่อนที่ไปตามต้นไม้ เพื่อทำเช่นนี้ เราได้ตั้งค่าบางส่วนเป็นโครงสร้างที่ไม่มองเห็นที่ทำหน้าที่เป็นปริมาณพาร์ติเคิลพร้อมพาร์ติเคิลอิมิเตอร์ลูกหลาน particle emitters ทันทีใต้ก้อนเมฆใหญ่ เนื่องจากข้อจำกัดของจำนวนพาร์ติเคิลใน Studio เราจึงไม่สามารถใช้พาร์ติเคิลอิมิเตอร์เดียวสำหรับพื้นที่ทั้งหมดได้ จึงต้องเพิ่มหลายๆ อันที่มีขนาดเท่ากันในรูปแบบตารางทั่วพื้นที่เล่นเพราะเมื่อต้นไม้เหล่านี้อยู่ในพวกเขาจะทำให้ผู้เล่นไม่สามารถมองเห็นไกลมากนัก.

ส่วนพาร์ติเคิลฝนใช้ประโยชน์จากคุณสมบัติใหม่ของอิมิเตอร์พาร์ติเคิล ParticleEmitter.Squash ที่ช่วยให้คุณทำให้พาร์ติเคิลยาวขึ้น หรือเตี้ยลง โดยเฉพาะสำหรับฝนนั้น ช่วยทำให้เราไม่ต้องใช้พื้นผิวฝนขนาดใหญ่เพียงขยายภาพวาดที่มีอยู่ขึ้นเพียงตรงนี้ โดยให้คุณรู้ว่าถ้าคุณต้องการเพิ่มค่า ParticleEmitter.Squash คุณอาจจำเป็นต้องเพิ่มค่า ParticleEmitter.Size ใหญ่ขึ้นเพื่อให้มันไม่ดูเล็กเกินไป! โดยรวมแล้ว เพียงแค่ลองค่าเหล่านี้เพื่อหาฝนให้หนาพอ แต่ไม่ยืดพวกมันจนมีสิ่งบกพร่องที่มองเห็นของประสบการณ์!


สำหรับหมอก หมอก และใบไม้ที่ปลิวไหว การทำให้มีพาร์ติเคิลที่เคลื่อนที่อยู่ตลอดเวลาเป็นเรื่องง่ายกว่าเพราะเราไม่ได้ต้องการพาร์ติเคิลจำนวนนับไม่ถ้วนมีการทำงานในเวลาเดียวกัน เราเริ่มต้นโดยการตั้งค่าพื้นที่หนึ่งพื้นที่และได้รับความถี่ของพาร์ติเคิลที่เราต้องการ.


หลังจากนั้น เราทำพื้นผิวที่เคลื่อนที่ได้และลมใบไม้ ตั้งค่าพาร์ติเคิลให้หมุน/เคลื่อนที่ในอัตราแตกต่างกันและเริ่มต้นด้วยความเร็วที่แตกต่างกัน นี่หมายความว่าพาร์ติเคิลหมอกใหญ่กว่าจะมีปฏิสัมพันธ์ที่เป็นธรรมชาติมากขึ้นและไม่ดูเป็นเหมือนพื้นผิวที่ซ้ำซาก โดยเฉพาะเมื่อพวกมันมีขนาด



ผลก็คือมีการเคลื่อนไหวที่มีความยิ่งใหญ่ระหว่างต้นไม้ที่เคลื่อนที่ ฟ้าผ่าที่พัดไปและฟ้าผ่าที่ช่วยสร้างเอฟเฟกต์พายุรอบตา
ตั้งค่าตาแห่งพายุ
ตาหลักที่แตกหักที่มีแกนสีสว่างนั้นมีวัตถุประสงค์เพื่อมอบความรู้สึกแรกให้กับผู้เล่นว่ามีสิ่งที่ชั่วร้ายและลึกลับเกิดขึ้นที่บ้านที่พวกเขาควรค้นหาเพิ่มเติม เนื่องจากฉากของเราเป็นสถานที่มืดมิดและตาอยู่สูงขึ้นในท้องฟ้า จึงเป็นสิ่งสำคัญในการสร้างรูปร่างที่แสดงให้เห็นถึงใบหินที่แตกหัก แต่ไม่จำเป็นต้องสร้างรายละเอียดพื้นผิวที่แสดงให้เห็นถึงใบหินที่ดูสมจริงเนื่องจากผู้เล่นจะไม่สามารถมองเห็นสิ่งเหล่านั้นได้ การเข้าใจว่าสิ่งใดที่เป็นจริงสำหรับผู้เล่นที่จะเห็นภายในแสงของฉากนั้นก่อนที่จะทำงานมากกว่าข้อความรายละเอียดที่ไม่จำเป็นสามารถช่วยคุณลดทรัพยากรในกระบวนการพัฒนาได้พอสมควร.


ระยะห่างจากผู้เล่นก็หมายความว่าเราสามารถพึ่งพาภาพปกติสำหรับรายละเอียดของพื้นผิวของตา รวมถึงตาที่ควรทำให้เมชเป็นเพียงสเฟียร์ที่เรียบ พวกเราแกะสลักรายละเอียดลงไปที่เมชพอลีสิกของที่ทำการปั้นไว้ในเมชขนาดเล็กที่ต่ำกว่าเพื่อรับรายละเอียดที่สวยงามโดยไม่มีค่าต้นทุนการทำงานที่สูงมาก



เพื่อเพิ่มความรู้สึกเหนือธรรมชาติให้กับตาและเน้นการปรากฏตัวของมัน เราจึงตัดสินใจสร้างมักมากมายที่มีแสงสีสว่าง ซึ่งจะซึมซาบผ่านรอยแตกของมัน ขณะที่ไม่มีช่องสำหรับการให้แสงในการปรากฏ เราเอาชนะอุปสรรคนี้โดยการสร้างตาจากสเฟียร์ 2 ลูก: หนึ่งลูกสำหรับผิวที่หินด้านนอกและลูกที่สองเล็กกว่าเล็กน้อยสำหรับมักมากที่ส่องแสง ใน Substance Painterเราได้สร้างพื้นผิวสีพื้นฐานสำหรับโลกที่มีความโปร่งแสงในพื้นที่ที่เราต้องการให้เห็นเมื่อมีรอยแตก กับ Blender เรายังมีการ "vertex painting" ลูกในส่วนด้านในเพื่อให้มีความหลากสารในการให้สีไว้ในขอบที่ดูสวยงาม.

ความท้าทายอีกอย่างที่เราพบขณะสร้างตาคือการใช้ การสตรีม คู่กับระยะห่างจากผู้เล่น ในการตั้งฐานความสำคัญของโครงสร้างนี้ เราหวังให้สามารถมองเห็นได้ในสถานที่ซึ่งแม้ระยะห่าง แต่โดยไม่มีการหลอกลวงเมช ผู้เล่นจะไม่สามารถเห็นตาได้เลย เว้นแต่พวกเขาอยู่ในโซลาริอัม เรายังสามารถบังคับให้ตาเปิดเผยในฉากได้ในกรณีที่เราเพิ่มเรขาคณิตที่มีน้ำหนักให้กับตานั้นและวงแหวน เมื่อลักษณะพวกเขารวมอยู่ใต้มวลของพื้นดิน จุดนี้จะทำให้เครื่องยนต์เชื่อว่าจุดนี้ใกล้กับผู้เล่นตั้งแต่ความไกลและจะมีการถ่ายสตรีมข้ามไปตลอดเวลา นี้ควรถูกทำอย่างระมัดระวังเพราะว่าการบังคับให้มีวัตถุขนาดใหญ่หลายๆชิ้นเพื่อให้มีการสตรีมในขนาดนี้ อาจลดผลกระทบของการเปิดการสตรีมและทำให้คุณภาพการทำงานตกต่ำให้ลดลง.
เราสามารถเพิ่มการเคลื่อนไหวไปยังตามันและวงแหวนด้วยสคริปต์เดียวกันที่เรามีเพื่อ หมุนเมฆ. สำหรับการสัมผัสสุดท้าย เราตัดสินใจที่จะใส่เบาะแสเกี่ยวกับการมีโลกอีกอย่างหนึ่งซึ่งอยู่หลังเมฆ แต่เราต้องมีวิธีการสร้างสรรค์ในการหลีกเลี่ยงการทำให้มีเรขาคณิตมากขึ้นในฉากและการจัดการกับอุปสรรคที่เกี่ยวกับการเปิดการสตรีมอีกครั้ง เราสร้างฉากที่มีลักษณะการมองที่ลึกได้อิงจากขนาดและระยะห่างของวัตถุและกำหนดภาพให้กับสถานการณ์ที่เราสร้างแล้วใช้ภาพนี้เป็น decal บนชิ้นส่วนที่อยู่ตรงหลังตามกลางพายุ เราใช้วิธีการเดียวกันในการหมุนชิ้นส่วนนี้กับวิธีที่เราใช้สำหรับตาและวงแหวน.

สร้างห้องเก็บอาหารที่ขยายได้
หนึ่งในสิ่งที่สนุกที่สุดในการผลิตคือพื้นที่ที่เสื่อมเสีย ที่เราสามารถบิดเบือนความคาดหวังของผู้เล่นต่อความจริงได้โดยการเปลี่ยนแปลงมันรอบๆตัวพวกเขา ตัวอย่างเช่น ในปริศนาของบิด เราต้องการจำลองช่วงเวลาที่คล้ายกับฝันร้าย ที่ไม่ว่าคุณจะวิ่งเร็วแค่ไหน ห้องก็ดูเหมือนว่าจะยาวขึ้นเรื่อยๆ เราตัดสินใจที่จะทำห้องเก็บอาหารที่ขยายได้ที่หลบหนีจากผู้เล่นในขณะที่พวกเขาค้นหาส่วนผสมเพื่อทำให้ห้องกลับเข้าสู่สภาพปกติ.
เราตั้งค่านี้ด้วยการเคลื่อนไหวจากผนังและการวางแผนที่ชาญฉลาดของห้องที่ปรากฏอยู่ทั้งสองด้านของห้องเก็บอาหาร ในสภาพปกติของห้อง ห้องเก็บอาหารมีลักษณะเหมือนทางเดิน แต่ในพื้นที่ที่เสื่อมมันจะมีลักษณะยาวขึ้นมากพร้อมกับปีกหลายช่องและกำแพงปลอม!


ผนังปลอมเป็นกลุ่มของโมเดลที่เราจะเคลื่อนย้ายกลับเมื่อผู้เล่นเข้ามาในพื้นที่กระตุ้น ซึ่งเป็นชิ้นส่วนที่มองไม่เห็นเมื่ออยู่ในห้องเก็บอาหาร กระตุ้นนั้นยังถูกใช้ในสคริปต์คล้ายกับที่ใช้ในประตูทั้งหมดของเรา ซึ่งจะเรียกใช้ TweenService เพื่อเคลื่อนที่จากจุดประสงค์หนึ่งไปยังอีกจุดหนึ่ง เราใช้ปริมาณส่วนเพื่อบอกกับการดำเนินการเคลื่อนไหวว่าจุดเริ่มต้นและจุดสิ้นสุดของผนังคือที่ไหน


เนื่องจาก TweenService เป็นระบบทั่วไป ข้อมูลโมเดลของผนังของเราที่ใช้ต้องมีสมาชิกเดียวกัน ในสคริปต์ "Door_Script" ทั่วไปที่เล่นเสียงที่กำหนดโดย "value" ที่อยู่ด้านล่างของโมเดล "Grow_Wall" สคริปต์เดียวกันนี้ด้วยการปรับแต่งในตัวอย่างโค้ดด้านล่าง ก็ยังได้เปิดเสียงสำหรับการเคลื่อนที่ของห้องเก็บอาหาร นี่ทำให้การเคลื่อนที่มีสีสันมาก!
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, --เวลา/ความเร็วของการเคลื่อนไหวประตู
Enum.EasingStyle.Quart, --สไตล์การให้น้ำหนัก
Enum.EasingDirection.InOut, --ทิศทางการให้น้ำหนัก
0, --จำนวนการทำซ้ำ
false, --ย้อนกลับได้จริง
0 --หน่วง
)
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["Door"]:Play()
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("touch")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end
หลังจากที่เรามีผนังปลอมขยับไปที่ด้านหลังของห้อง เราจำเป็นที่ต้องเคลื่อนที่สิ่งอื่นๆในห้องเช่นกัน เพื่อทำเช่นนี้ เราต้องการให้สิ่งของที่อยู่ในห้องเก็บอาหารทั้งหมดถูกเชื่อมติดอยู่กับผนังในขณะที่มันเคลื่อนที่ ใช้ Weld Constraints เราได้เชื่อมโยงวัตถุทั้งหมดไปยังผนังห้องเก็บอาหาร เพื่อให้มันเคลื่อนไปพร้อมๆกันในรูปแบบเดียว การทำในลักษณะนี้ทำให้เรามีตัวเลือกในการทำให้วัตถุเหล่านี้หลุดออกเพื่อให้ผู้เล่นสามารถชนหรือโยนมันไปได้!
สร้างต้นไม้ที่มีผวกชั่วร้าย
Studio เป็นเครื่องยนต์ที่ยอดเยี่ยมที่มีพื้นฐานจากฟิสิกส์ที่คุณสามารถใช้สร้างทุกอย่างตั้งแต่ประตูหมุนไปจนถึงแพลตฟอร์มหมุน ในเดโมนี้ เราต้องการใช้ฟิสิกส์เพื่อสร้างความรู้สึกที่สมจริงในสภาพแวดล้อมที่ไม่สมจริง ในการใช้เพียงไม่กี่ constraints คุณสามารถสร้างอุปสรรคที่สนุกและท้าทายในประสบการณ์ของคุณเอง!

Constraints เป็นกลุ่มของมอเตอร์ที่มีพื้นฐานจากฟิสิกส์ที่ช่วยให้ออปเจ็กต์สามารถจัดเรียงและจำกัดพฤติกรรมได้ ตัวอย่างเช่นคุณสามารถใช้ rod constraint เพื่อเชื่อมต่อนออปเจ็กต์ในระยะห่างที่แน่นอนกัน หรือใช้ rope constraint เพื่อมีโคมไฟที่แขวนจากปลายทางสาย โดยในปริศนาของบุตรที่ผู้เล่นถูกย้ายไปที่สถานะที่เสื่อมของการศึกษา เราต้องการพลิกโลกจริงๆในด้านข้าง ทำเช่นนี้จะทำให้ผู้เล่นตรวจสอบปัจจุบันและวัฒนธรรมในกฎรอบข้าง โดยยังคงมีการใช้ระบบฟิสิกส์อย่างที่ตั้งใจไว้!

เมื่อผู้เล่นได้ลงมาที่บริเวณหลักของปริศนา พวกเขาก็พบว่าผู้เล่นได้เห็นภาพที่คุ้นเคยใน Roblox: ในอุปสรรค ที่ค่อนข้างมีอยู่ของเรา อุปสรรคนี้จัดก็มีชิ้นแพลตฟอร์มหมุนและผนังหมุน พร้อมกับ "พื้นที่ปลอดภัย" ที่พัฒนาขึ้นตามเรื่องราว เราจะเน้นที่ส่วนหมุน/หมุน

ทำไมเราถึงใช้ constraints ที่นี่? เพราะ TweenService หรือวิธีการอื่นๆจะไม่ได้ทำให้ผู้เล่นเคลื่อนที่ขณะที่พวกเขายืนอยู่บนมัน โดยไม่มีการเคลื่อนไหวของวัตถุที่เคลื่อนผู้เล่นโดยมีความเสี่ยงว่าผู้เล่นจะกระโดดขึ้นแพลตฟอร์มแล้วมันจะหมุนหลุดไปจากพวกเขา แทนที่ เราต้องการให้ผู้เล่นเดินเข้าไปในแพลตฟอร์มที่หมุนและกระโดดลงแพลตฟอร์มถัดไป ด้วยวิธีนี้ ผู้เล่นจะรู้สึกว่าเหมือนกับยืนอยู่กับที่ขณะที่พวกเขาถือการตัดสินใจในวิธีที่ต้องผ่านทาง

ในการทำเช่นนี้ เราจำเป็นต้องใช้สินทรัพย์จากชุดปัจจุบันและเพิ่มเนื้อหาใหม่ในการสร้างภาพ เอฟเฟกต์ เราทำหลายผนังและแพลตฟอร์มที่ไม่เสร็จสมบูรณ์ที่มีรูให้บอกเรื่องของปู่ย่าตายายในการสร้างบ้านต้นไม้ เพราะเราจะไม่สร้างพันจำเพาะมากมาย เราจึงทำออกเป็น 4 ชิ้นพื้นฐานและชิ้นราวโดยแยกกัน สоставให้เราสามารถผสมและจับคู่ชิ้นพื้นฐานได้นานๆ

เราทราบว่าเมื่อเรากำลังใช้ constraints เราจะไม่สามารถยึดเหล่าเมฆไว้เพราะมันจะไม่เคลื่อนตัวแม้กับการมี constraints/motor ขับเคลื่อนพวกมัน Constraints ต้องเป็นลูกหลานของอย่างใดอย่างหนึ่งที่ได้ทิ้งไว้ เพื่อให้แพลตฟอร์มตรงข้ามไม่เพียงแค่ติ้งลงไปจากโลก เราแก้ไขในสิ่งที่เราชื่อว่า Motor_Anchor ที่มี hinge constraint เพื่อตัวการเคลื่อนตัวของแพลตฟอร์ม เราจึงต้องการให้สองเมซเคลื่อนที่เป็นหนึ่ง ดังนั้นเราจึงสร้างส่วนที่เรียกว่า Motor_Turn และทำการเชื่อมโยงแพลตฟอร์มสองตัวกับมัน วิธีนี้อุปสรรคจึงจะทำงานได้ โดยใช้หน่วยเดียว แทนที่จะทำงานกับหลายๆชิ้น ด้านปลายของเงื่อนไข

ตอนนี้ถึงเวลาในการตั้งค่าพฤติกรรมจริงของ hinge constraint และเพิ่มการยึดติดซึ่งทำหน้าที่เป็นการจัดเรียงที่กำหนดของชิ้นส่วนและ constraints ด้วยกัน เราวางการยึดติดที่หมุนอยู่บน Motor_Turn ซึ่งมีชิ้นส่วนที่อยู่บนกับกันที่เชื่อมอยู่ด้วยกัน และการยึดติดอีกชิ้นหนึ่งที่ทำหน้าที่คลุมดูแลด้านการยึดภรรยาบน Motor_Anchor โดยติดอยู่ด้านข้างของ hinge constraint สำหรับนี่ต้องการให้มันหมุนได้ด้วยตัวเอง แทนที่จะถูกใช้โดยผู้เล่น (เช่น บานพับประตู) เราตั้งค่าคุณสมบัติ HingeConstraint.ActuatorType เป็น Motor, พิจารณาว่าตนคือการทำให้ปรับเปลี่ยนได้
เพื่อทำให้แพลตฟอร์มหมุนอยู่ตลอด เราจึงตั้งค่าคุณสมบัติ HingeConstraint.AngularVelocity, HingeConstraint.MotorMaxAcceleration, และ HingeConstraint.MotorMaxTorque ให้มีค่าเพียงพอสำหรับการเคลื่อนที่และป้องกันไม่ให้มีการขัดจังหวะถ้าผู้เล่นเจริญขึ้นในนั้น.

ตอนนี้เราจำเป็นต้องสร้างผนังที่หมุน ผนังต้องหมุนอยู่บนศูนย์กลางที่เห็นและเราทราบว่าเราต้องการให้มันจัดการกับการจัดตำแหน่งใดๆสัมพันธ์กับส่วนที่เหลือของระดับเดียวกัน เช่นเดียวกับแพลตฟอร์ม เราจึงสร้างผนังให้เป็นแบบที่ไม่ถูกต้อนรบเลยและเชื่อมโยงไว้ที่ Motor_Turn

เราเองก็ใช้ Texture บน top ของ SurfaceAppearance เพื่อทำให้วัสดุพื้นฐานมีความหลากหลายมากขึ้น Textures คล้ายกับ Decals ทำให้คุณสามารถใส่ภาพลงบนพื้นผิวของเมชซึ่งมีประโยชน์ถ้าคุณต้องการใส่สิ่งสกปรกลงบนผนังอิฐ หรือทำให้ไม้ดูเก่าแม้ว่าจะใช้วัสดุไม้พื้นฐานเดียวกัน Texture ทำงานได้เล็กน้อยต่างจาก Decal ซึ่งจะมีความสามารถในการทำให้ภาพขยายและเลื่อนไปตามทางที่คุณต้องการ ซึ่งเหมาะสุดถ้าคุณต้องการให้การปรับพื้นผิวของคุณมีแผนอื่น

เมื่อเราทดสอบแพลตฟอร์มและผนังหมุนที่แตกต่างกัน เราได้สร้างหลากหลายแบบและทดลองใช้งานของพวกเขาเพื่อให้แน่ใจว่าพวกเขาจะท้าทายและเปลี่ยนแปลงในการทำให้ชัดเจนที่ผู้ใช้ต้องไป! มันใช้เวลาทำการปรับแต่งเพื่อปรับความดันและตำแหน่งเพื่อให้มันทำงานได้อย่างราบรื่น มีหลายจุดที่แพลตฟอร์มและผนังปะทะกันหรือการทำงานอยู่ใกล้ๆ แต่ด้วยการเปลี่ยนรูปร่างที่ปราศจากเอฟเฟกต์ เราสามารถไปสู่ปรัชญาการจัดการที่เรามีในเดโม!
หากคุณไม่แน่ใจว่าวัตถุทางกายภาพของคุณกำลังชนกัน สามารถเปิดใช้งาน Collision fidelity จากฟังก์ชัน Visualization Options widgets ที่มุมขวาบนของ 3D viewport



ดังที่คุณเห็นด้านล่าง ช่องทางบานประตู/หน้าต่างสามารถมองเห็นได้ แต่รายละเอียดเล็กๆน้อยๆเช่นกังหันย่อยไม่สามารถมองเห็นได้ นี่เป็นเพราะว่าคุณสมบัติ CollisionFidelity ของผนังนั้นตั้งค่าเป็น Box เราไม่ต้องการความแม่นยำสำหรับงานวางมุมนี้ ดังนั้นเพื่อเป็นการประหยัดพลังงาน เราจึงใช้รายละเอียดนั้นมากพอสำหรับผู้เล่นที่จะกระโดดลงไปที่แพลตฟอร์ม ไฟล์ของพื้นที่ผนังและผนังหมุนและสร้างรายละเอียดเพิ่มเติมบางอย่าง เช่นกล่องและโคมไฟ จากนั้นมันจึงพร้อมที่จะเล่น!
