การสร้างการเคลื่อนไหวในสภาพแวดล้อมใด ๆ ภายในประสบการณ์ช่วยให้รู้สึกเหมือนอยู่ในโลกมาก
สร้างพายุ
พายุได้ผ่านหลายฤดูกาลก่อนที่เราจะตัดสินใจว่าอะไรเป็นสีขาวใน The Mystery of Duvall Drive ตั้งแต่ต้น เราคิดว่าพายุเป็นปilar obsidian ขนาดยักษ์และในภายหลังเราคิดว่ามันเป็น
- พายุควรให้ผู้เล่นรู้สึกเหมือนการเกิด ผลกระทบของเหตุการณ์นี้ต่อโลก รวมทั้งต้นไม้ที่พัดและวัตถุรอบๆ
- ลวดลายเถาวัลย์ที่หมุนของเมฆด้วยตัวเองควรให้ผู้เล่นได้มองเห็นพอร์ทัลกลาง โดยไม่เปิดเผยทุกอย่าง นี่จะเป็นการเร่งให้ผู้เล่นสำรวจใกล้ชิดเพื่อดูสิ่งที่เกิดขึ้น
- จุดสว่างที่แน่นอนขึ้นจะช่วยให้เราโฟกัสที่ คอมโพองชันของบ้าน ซึ่งเป็นตัวละครหลักและสถานที่ส่วนใหญ่ของเกม
เพื่อให้รู้สึกว่าพายุมีความเคลื่อนไหวอย่างรวดเร็ว ก้าวร้าว และเปลี่ยนแปลงไปตามสภาพแวดล้อมของมัน เราใช้ระบบและคุณสมบัติต่อไปนี้:
- TweenService > สำหรับการเคลื่อนที่ในเมฆ
- การเปลี่ยนแปลงการจุดไฟ - สำหรับการสร้างเมฆเพื่อสร้างสายฟ้า
- ลำแสง volumetric และสายฟ้า สำหรับ "การจัดแสง" และสายฟ้า
- ผู้ปล่อยอนุภาค - สำหรับของเสียบินขึ้นไปยังพอร์ทัลและบินรอบ ๆ เนื่องจากลมพัด
- อนิเมชั่น สำหรับต้นไม้ที่พัดในลม
เพิ่มเมฆด้วยเท็กซ์จูรี่
ในขณะที่ เมฆพลุกระแทก เหมาะสำหรับเมฆขนาดให
เนื่องจากแต่ละตาข่ายคลาวด์จำเป็นต้องมีขนาดใหญ่เพื่อรอบ ๆ บ้านและสื่อสารว่าพายุนี้ใหญ่แค่ไหน เราจึงรู้ว่าเราต้องปูพื้นผิวของเมชให้กว้างขวางเพื่อให้มันทำซ้ำ
ในขณะที่ผู้ปล่อยอนุภาคหรือลำแสงไม่สามารถทำให้เราสามารถปัดเป่าแสงออกจากแต่ละเมชได้ ซึ่งเป็นสิ่งสำคัญเมื่อเราต้องการใช้งานในเมฆเมฆสายฟ้า เรายังจำลอ
เมชรูปกลมหมุน
หลังจากที่เราพอใจกับลักษณะทั่วไปของเมฆเราจำเป็นต้องทำให้มันเคลื่อนไหว! เรามีรูปร่างทั่วไปของแต่ละชั้นเมฆในที่ว่าง แต่
เราต้องการวิธีการใช้งานง่ายเพื่อหมุนตัวอินสแตนซ์ที่อยู
ปลั๊กอินLocalSpaceRotation เพื่อให้เราสามารถจัดการตัวอินสแตนซ์ที่ได้รับผลกระทบใน Studio โดยใช้ป้ายชื่อ LocalScript ที่จัด
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 เป็นแผนที่ที่มีข้อมูลสำหรับวัตถุทั้งหมดที่เกี่ยวข้อง เช่นความเร็วในการหม
เราหมุนตัวอินสแตนซ์ในคุณสมบัติ Update
local parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- อาจไม่ได้สตรีมส่วนหลักcontinue -- รอให้ส่วนหลักเคลียดendparentTransform = 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 ที่ถูกต้องเพื่อจะได้รับการจัดการการสตรีม หากมีการโทรกเรียกอัปเดตในวัตถุขอ
การออกแบบสายฟ้า
เนื่องจาก Studio ไม่เสนอให้ตัวเก็บฟ้าออกจากกล่อง และระบบอนุภาคมีข้อจำกัดบางอย่างที่จะไม่ทำงานสำหรับการโจมตีของฮีโร่ที่มาจากดวงตา
การแต่งแสง
เรามักใช้เครื่องมือตัดต่อหรือการนับเวลาเพื่อขับเวลาการโจมตีของไฟฟ้าเช่นนี้ แต่เนื่องจาก Studio ยังไม่เสนอคุณสมบัตินี้ เราจึงตัดสินใจที่จะเขียนสคริปที่จะควบคุมเวลาการโจมต
- ตัวองค์ประกอบของสายฟ้าตก, เช่น เนื้อผ้า, ความสว่าง, และความแลคของพวกเขา, จะถูกสุ่มด้วยทุกครั้งที่ตี
- การเปลี่ยนแปลงของเสียงและโพสต์จะอยู่ในขั้นตอนการเคลื่อนไหวของ strike 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 เรากรอกโครงสร้างข้อมูลเพื่อให้ลูกค้าทุกรายได้รับพารามิเตอร์เดียวกัน
ในด้านลูกค้า ( LightningVFXClient ) เราตรวจสอบว่าลูกค้านี้ควรใช้งาน FX หรือไม่
local function LightningFunc(info)
…
-- ไม่มี FX ในระหว่างอยู่ในบ้าน
if inVolumesCheckerFunc:Invoke() then
return
end
-- ไม่มี FX เมื่ออยู่ในโลก "ปกติ"
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end
…
นอกจากนี้เราจะดำเนินการตามลำดับเพื่อตั้งค่าข้อความเรืองแสง ตำแหน่ง และความสว่าง รุ่นวัยรุ่น และใช้ task.wait(number) เซิร์ฟเวอร์
beam.Texture = textures[info.textIdx]beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)-- ล้างbeam.Brightness = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- เสียงif audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.wait(info.waitTillFlashes)-- and so on
เพื่อตรวจสอบว่าผู้เล่นอยู่ในร่มเราใช้ตัวช่วย inVolumesCheckerFunc ฟังก์ชันซึ่งไปข้างล่างเส้นสายที่เป็นภายในพื้นที่ภาย
เพื่อตรวจสอบว่าผู้เล่นอยู่ในพื้นที่ที่เสียหายหรือไม่ เราจะเรียกใช้ฟังก์ชัน gameStateInfoFunc ซึ่งตรวจสอบสถานะเกมปัจจุบัน สำหรับการเล่
การใช้เครื่องปล่อยอนุภาค
สายฟ้าฮีโร่สนับสนุนโดยระบบอนุภาคที่แนะนำไฟฟ้าไกลโดยการสร้างความรู้สึกของชั้นเมฆในพื้นหลังที่จับแสงจากการโจมตีไกลหรือการอื่นๆ หรือคลาวด์-to-คลาวด
ทำให้ต้นไม้พัดในลม
หลังจากที่เรามีเมฆและสายฟ้าทำงานอย่างที่เราต้องการมันจะ, เราจำเป็นต้องเพิ่มสองส่วนประกอบหลักอื่น ๆ ของพายุ: ลมและฝน! ส
เรารู้ว่าจริงๆ แล้วเราขายผลกระทบของลมและฝน เราต้องการต้นไม้เองเพื่อเคลื่อนที่ มีวิธีการที่คุณสามารถทำสิ่งนี้ภายในเค
เราเริ่มต้นด้วยการเก็บเกี่ยวต้นไม้หลายต้นจาก แพ็ครุ่น Endorse - ทรัพยากรป่า เนื่องจากต้นไม้เหล่านี้มีอยู่แล้วและประสบการณ์ของเรามีขึ้นในภาคเหนือตะวันตกของประเทศไทย จึงทำให้เรา
หลังจากที่เราเลือกต้นไม้ของเราเรารู้ว่าเราต้องการสกินพวกเขา สกินผ้าหนังเป็นการเพิ่มข้อต่อ (หรือกระดูก
เรารู้ว่าเราต้องการประหยัดเวลาและใช้เดียวกันเดียวกันอนิเมชัน, ดังนั้นเราจึงสร้างริกของต้น
เมื่อเรามีสร้างข้อต่อ/กระดูกของเราเรียบร้อยแล้ว ก็ถึงเวลาสร้างแอนิเมชั่นทดสอบเพื่อเคลื่อนไหวไปทั่ว
หลังจากที่เราพอใจกับผลลัพธ์ในต้นไม้นั้น ก็ถึงเวลาทดสอบแอนิเมชันเดียวกันในต้นไม้อื่น! เราแล้วรู้ว่ามันจะเป็นแอนิเมชันเดียวกันระหว่างแต่ละประเภทของต้นไม้, ดังนั
เพื่อทำเช่นนี้เราใช้ต้นไม้ Beechwood จาก Forest Pack และสร้างริกของเรียบร้อยโดยใช้การตั้งชื่อที่ถูกต้องเดียวกันสำหรับข้อต่อ นี่คือเหตุผลที่ว่าทำไมอนิเมชั่นที่เรานำเข้ามา
หลังจากที่เรา รีแมปและสกินต้นไม้ Beechwood แอนิเมชันนี่หมายถึงการทำซ้ำและแก้ไขเฉพาะเวลาเท่านั้นที่จำเป็นต้องทำได้ในไฟล์เด
เมื่อเรามีทุกประเภทของต้นไม้ที่เราต้องการแอนิเมชันต้องออกมาเป็น แพ็กเกจ เพื่อให้เราสามารถเปิดเผยและปรับปรุงได้ในขณะที่เรากำลังเล
การสร้างเศษเหล็กพายุ
เราต้องการให้ฝนดูหนักและให้หมอกและขอบเศษพุ่งผ่านต้นไม้ เพื่อทำเช่นนี้เราจัดตั้งส่วนที่มองไม่เห็นบางส่วนให้เป็น
ฝนละอองใช้โครงสร้างผู้ปล่อยอนุภาคใหม่ ParticleEmitter.Squash ซึ่งช่วย
สำหรับหมอก หมอก และใบไม้ที่พัดผ่านไปยัง มันเป็นเรื่องง่ายมากที่จะเพิ่มปริมาณขนาดใหญ่เดียวกันที่ครอบคลุมพื้นที่น้อยลงเพราะเราไม่ต้องการให้มีการวิ่งของอนุภาคในขณะเดียวกัน เราเ
หลังจากนั้นเราทำให้ใบไม้พัดและวิธีการเรียกขาวของเราและตั้งค่าอนุภาคให้หมุน/เคลื่อนที่ทุกอันในอัตราที่แตกต่างกันและเริ่มต้นที่ความเร็วที่แตกต่างกัน นั่นหมายถึงว่าอนุภาคขนาดใ
ผลลัพธ์คือบางการกระทําที่ดีระหว่างต้นไม้เคลื่อนไหวหน้าต่างพังทลาย และสายฟ้าสร้างผลกระทบของพายุรอบตากลางของพายุ
การตั้งค่าสายตาของพายุ
ดวงตาหินที่แตกที่มีเนื้อหุ้มสีน้ำเงินอมชมพู มีไว้เพื่อให้ผู้เล่นได้รับเบาะแสแรกว่ามีสิ่งที่น่ากลัวและ
ระยะทางจากผู้เล่นยังหมายถึงว่าเราสามารถพึ่งพาแผนที่ปกติสำหรับรายละเอียดพื้นผิวของตาดังนั้นเนื้อผ้าจึงเป็นเพียงลูกบาศก์ที่เรียบง่าย! เราสร้างรายละเอียดเป็นระยะโพลี
เพื่อเพิ่มความรู้สึกเหนือธรรมชาติให้กับตาและเพื่อเน้นความประสงค์ของมั
ความท้าทายอื่นที่เราเผชิญในขณะที่สร้างตาคือการกำหนดให้ใ
เราสามารถเพิ่มการเคลื่อนไหวของตาและแหวนของมันได้ขอบคุณสคริปที่เรา
การสร้างคลังอาหารขนาดขยาย
หนึ่งในสิ่งที่สนุกที่สุดในการผลิตคือพื้นที่ที่เสียหายซึ่งเราสามารถเปลี่ยนความคาดหวังของผู้เล่นเกี่ยวกับความเป็นจริงโดยการเปลี่ยนแปลงมัน
เราตั้งสิ่งนี้ขึ้นด้วยการเคลื่อนไหวที่เรียบง่ายของผนัง และการจัดเรียงที่ฉลาดของห้องพักที่จะปรากฏขึ้นทั้งสองด้านของโรงประชุม ในสถานะปกติของห้องพัก โรงประชุมเป็นทางเด
ผนังปลอมเป็นกลุ่มของผู้เล่นที่เราจะย้ายกลับในขณะที่ผู้เล่นเข้าสู่ระดับเวลาที่ตั้งอยู่ในแผนกที่ ซึ่งเป็นส่วนโปร่งใสที่พวกเขาจะเดินผ
เนื่องจาก TweenService เป็นระบบระบบทั่วไป ดังนั้นวิธีการจัดการผนังของเราทั้งหมดจึงต้องมีส่วนประกอบเดียวกัน เช่น ภาพต่อไปนี้เป็นตัวอย่างของสคริปทั่วไปที่เรียกใช้โด
สคริปเดียวกันนั้น ด้วยการแก้ไขบางอย่างในตัวอย่างโค้ดต่อไปนี้ ยังเรียกใช้เสียงสำหรับการเคลื่อนย้ายของห้องเก็บของ ซึ่งเพิ่มมากขึ้นสำหรับการเคลื่อนที่!
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, --กลับ true
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
--โมเดล ["Door"]:เล่น()
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
--พิมพ์("触摸")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end
เมื่อเรามีผนังปลอมที่ย้ายไปที่ด้านหลังของห้อง เราต้องการเนื้อหาส่วนที่เหลือเพื่อย้ายไปกับมัน เพื่อทำเช่นนั้น เราต้องการให้
การสร้างบ้านไม้ที่เสียหาย
Studio เป็นเครื่องยนต์ทางกายภาพที่ยอดเยี่ยมที่คุณสามารถใช้เพื่อสร้างทุกอย่างตั้งแต่ประตูแขวนไปยังแพลตฟอร์มหมุนได้ ด้วยโดโมของเราเราต้องการใช้ฟิสิกส์เพื่อสร้
ข้อจํากัดทางกายภาพ เป็นกลุ่มของมอเตอร์ที่อยู่ภายใต้พลังลูกศรที่เช
เมื่อผู้เล่นทำงานลงไปในพื้นที่หลักของปริศนาพวกเขาได้รับการต้อนรับด้วยสายตาที่คุ้นเคยบน Roblox: สถานที่วิ่งขวางหน้าผาหลายชั้น พร้อมกับ "พื้นที่ปลอดภัย" ที่เริ่มเรื่อง
ทำไมเราใช้ข้อจํากัดที่นี่? เพราะ TweenService หรือวิธีการอื่น ๆ จะไม่ย้ายผู้เล่นขณะที่พวกเขายืนบนพวกเ
เพื่อทำสิ่งนี้ เราต้องใช้สินทรัพย์จากชุดปัจจุบันของเราและเพิ่มเนื้อหาใหม่สำหรับวิธีการแสดงผล เราสร้างผนังและพื้นที่พักผ่อนที่มีหลุมในนั
เรารู้ว่าเนื่องจากเราใช้ข้อจํากัดดังนั้นเราจึงไม่สา
ตอนนี้มันเป็นเวลาที่จะติดตั้งพฤติกรรมที่แท้จริงของ hinge ข้อจํากัดเอง, และเพิ่มอุปกรณ
เพื่อให้แนวฟ้ามุ่งหน้าไปยังความเร็วที่เท่าเทียมกัน เราจัดตั้ง HingeConstraint.AngularVelocity และ HingeConstraint.MotorMaxAcceleration และ HingeConstraint.MotorMaxTorque โปรพี่สุด
ตอนนี้เราต้องทำผนังหมุนได้ ระดับและเรารู้ว่าเราต้องการให้พวกเขาสามารถจัดการกับการจัดเรียงใด ๆ ที่เกี่ยวข้องกับส่วนที่เหลือขอ
เราใช้วัตถุ Texture เหนือวัตถุ SurfaceAppearance เพื่อเพิ่มควา
หลังจากทดสอบแพลตฟอร์มและผนังหมุนได้สองสามแบบเราทำการเปลี่ยนแปลงและเล่นกับตำแหน่งของพวกเขาเพื่อให้แน่ใจว่าเส้นทางอุปสรรคเป็นเรื
หากคุณไม่แน่ใจว่าวัตถุทางกายภาพของคุณกำลังตี คุณสามารถเปิดใช้งาน ความนิ่งการชนกัน จาก widget ตัวประกอบการวิดีโอ ในมุมมองด้านบนขวาใน 3D viewport
เนื่องจากคุณสามารถเห็นได้ด้านล่างหน้าต่าง/หน้าต่างมีขนาดเล็กลง รายละเอียดเล็ก ๆ น้อย ๆ เช่น sub-panelling จะไม่มองเห็