หน้านี้อธิบายปัญหาประสิทธิภาพทั่วไปและแนวทางปฏิบัติที่ดีที่สุดในการบรรเทา
การคำนวณสคริปต์
การดำเนินการที่มีค่าใช้จ่ายสูงในโค้ด Luau ใช้เวลานานในการประมวลผลและอาจส่งผลกระทบต่ออัตราเฟรม เว้นแต่จะถูกดำเนินการแบบขนาน โค้ด Luau จะทำงานแบบซิงโครนัสและบล็อกเธรดหลักจนกว่าจะพบฟังก์ชันที่ทำให้เธรดหยุดชั่วคราว
ปัญหาทั่วไป
- การดำเนินการที่เข้มข้นบนโครงสร้างตาราง - การดำเนินการที่ซับซ้อน เช่น การจัดรูปแบบ การถอดรูปแบบ และการทำสำเนาลึก มีค่าใช้จ่ายด้านประสิทธิภาพสูง โดยเฉพาะอย่างยิ่งในโครงสร้างตารางขนาดใหญ่ สิ่งนี้เป็นจริงโดยเฉพาะหากการดำเนินการเหล่านี้เป็นแบบวนซ้ำหรือเกี่ยวข้องกับการวนซ้ำผ่านโครงสร้างข้อมูลขนาดใหญ่
- เหตุการณ์ที่มีความถี่สูง - การผูกการดำเนินการที่มีค่าใช้จ่ายสูงกับเหตุการณ์ที่อิงเฟรมของ RunService โดยไม่จำกัดความถี่หมายความว่าการดำเนินการเหล่านี้จะถูกทำซ้ำในทุกเฟรม ซึ่งมักส่งผลให้เวลาในการคำนวณเพิ่มขึ้นโดยไม่จำเป็น เหตุการณ์เหล่านี้รวมถึง:
การบรรเทา
- เรียกใช้โค้ดในเหตุการณ์ RunService อย่างประหยัด โดยจำกัดการใช้งานในกรณีที่การเรียกใช้งานที่มีความถี่สูงเป็นสิ่งจำเป็น (เช่น การอัปเดตกล้อง) คุณสามารถดำเนินการโค้ดส่วนใหญ่ในเหตุการณ์อื่น ๆ หรือในลูปที่มีความถี่น้อยกว่า
- แบ่งงานขนาดใหญ่หรือมีค่าใช้จ่ายสูงโดยใช้ task.wait() เพื่อกระจายงานไปยังหลายเฟรม
- ระบุและปรับแต่งการดำเนินการที่มีค่าใช้จ่ายสูงโดยไม่จำเป็นและใช้ มัลติเทรดดิ้ง สำหรับงานที่มีค่าใช้จ่ายสูงในการคำนวณที่ไม่จำเป็นต้องเข้าถึงโมเดลข้อมูล
- สคริปต์บางตัวที่ทำงานบนเซิร์ฟเวอร์สามารถได้รับประโยชน์จาก การสร้างโค้ดเนทีฟ ซึ่งเป็นธงง่าย ๆ ที่คอมไพล์สคริปต์เป็นโค้ดเครื่องแทนที่จะเป็นไบต์โค้ด
- หากเซิร์ฟเวอร์ของคุณยังคงใช้ CPU หมดหลังจากที่คุณปรับแต่งแล้ว คุณสามารถสำรอง CPU เพิ่มเติมได้ด้วย Extended Services for Compute
ขอบเขต MicroProfiler
| ขอบเขต | การคำนวณที่เกี่ยวข้อง |
| RunService.PreRender | โค้ดที่ดำเนินการในเหตุการณ์ PreRender |
| RunService.PreSimulation | โค้ดที่ดำเนินการในเหตุการณ์ Stepped |
| RunService.PostSimulation | โค้ดที่ดำเนินการในเหตุการณ์ Heartbeat |
| RunService.Heartbeat | โค้ดที่ดำเนินการในเหตุการณ์ Heartbeat |
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการดีบักสคริปต์โดยใช้ MicroProfiler โปรดดูที่ไลบรารี debug ซึ่งรวมถึงฟังก์ชันสำหรับการติดแท็กโค้ดเฉพาะและเพิ่มความเฉพาะเจาะจง เช่น debug.profilebegin และ debug.profileend วิธีการ API ของ Roblox หลายวิธีที่เรียกโดยสคริปต์ยังมีแท็ก MicroProfiler ที่เกี่ยวข้องซึ่งสามารถให้สัญญาณที่มีประโยชน์
การใช้หน่วยความจำของสคริปต์
การรั่วไหลของหน่วยความจำสามารถเกิดขึ้นเมื่อคุณเขียนสคริปต์ที่ใช้หน่วยความจำที่ตัวเก็บขยะไม่สามารถปล่อยได้อย่างเหมาะสมเมื่อไม่ใช้งานอีกต่อไป การรั่วไหลมีอยู่โดยเฉพาะในเซิร์ฟเวอร์ เนื่องจากสามารถออนไลน์ได้อย่างต่อเนื่องเป็นเวลาหลายวัน ในขณะที่เซสชันของลูกค้าจะสั้นกว่ามาก
ค่าหน่วยความจำต่อไปนี้ใน Developer Console อาจบ่งชี้ถึงปัญหาที่ต้องการการตรวจสอบเพิ่มเติม:
- LuaHeap - การใช้จ่ายที่สูงหรือเพิ่มขึ้นบ่งชี้ถึงการรั่วไหลของหน่วยความจำ
- InstanceCount - จำนวนอินสแตนซ์ที่เพิ่มขึ้นอย่างต่อเนื่องบ่งชี้ว่าการอ้างอิงไปยังบางอินสแตนซ์ในโค้ดของคุณไม่ได้ถูกเก็บขยะ
- PlaceScriptMemory - ให้การแบ่งปันการใช้หน่วยความจำตามสคริปต์
ปัญหาทั่วไป
การเชื่อมต่อที่ยังคงเชื่อมต่ออยู่ - เอนจินไม่เคยเก็บขยะเหตุการณ์ที่เชื่อมต่อกับอินสแตนซ์และค่าที่อ้างอิงภายในฟังก์ชันที่เชื่อมต่อ ดังนั้นการเชื่อมต่อที่ใช้งานอยู่ของเหตุการณ์และโค้ดภายในอินสแตนซ์ที่เชื่อมต่อ ฟังก์ชันที่เชื่อมต่อ และค่าที่อ้างอิงจะอยู่นอกขอบเขตสำหรับตัวเก็บขยะหน่วยความจำ แม้หลังจากที่เหตุการณ์ถูกเรียก
แม้ว่าเหตุการณ์จะถูกตัดการเชื่อมต่อเมื่ออินสแตนซ์ที่พวกเขาสังกัดถูกทำลาย แต่ข้อผิดพลาดทั่วไปคือการสมมติว่าสิ่งนี้ใช้กับวัตถุ Player หลังจากที่ผู้ใช้ออกจากเกม เอนจินจะไม่ทำลายวัตถุ Player และโมเดลตัวละครที่เป็นตัวแทนของพวกเขาโดยอัตโนมัติ ดังนั้นการเชื่อมต่อกับวัตถุ Player และอินสแตนซ์ภายใต้โมเดลตัวละคร เช่น CharacterAdded ยังคงใช้หน่วยความจำหากคุณไม่ตัดการเชื่อมต่อในสคริปต์ของคุณ สิ่งนี้อาจส่งผลให้เกิดการรั่วไหลของหน่วยความจำที่สำคัญมากในเซิร์ฟเวอร์เมื่อผู้ใช้หลายร้อยคนเข้าร่วมและออกจากเกม
ตาราง - การแทรกวัตถุในตารางแต่ไม่ลบเมื่อไม่จำเป็นอีกต่อไปจะทำให้การใช้หน่วยความจำไม่จำเป็น โดยเฉพาะอย่างยิ่งสำหรับตารางที่ติดตามข้อมูลผู้ใช้เมื่อพวกเขาเข้าร่วม ตัวอย่างเช่น โค้ดตัวอย่างต่อไปนี้สร้างตารางที่เพิ่มข้อมูลผู้ใช้ทุกครั้งที่ผู้ใช้เข้าร่วม:
Examplelocal playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- ข้อมูลบางอย่างend)หากคุณไม่ลบรายการเหล่านี้เมื่อไม่จำเป็นอีกต่อไป ตารางจะยังคงเติบโตในขนาดและใช้หน่วยความจำมากขึ้นเมื่อผู้ใช้เข้าร่วมเซสชันมากขึ้น โค้ดใด ๆ ที่วนซ้ำผ่านตารางนี้ก็จะมีค่าใช้จ่ายในการคำนวณที่สูงขึ้นเมื่อขนาดของตารางเพิ่มขึ้น
การบรรเทา
เพื่อทำความสะอาดค่าที่ใช้ทั้งหมดเพื่อป้องกันการรั่วไหลของหน่วยความจำ:
ตัดการเชื่อมต่อการเชื่อมต่อทั้งหมด - ตรวจสอบโค้ดของคุณและตรวจสอบให้แน่ใจว่าการเชื่อมต่อแต่ละรายการถูกทำความสะอาดผ่านหนึ่งในเส้นทางต่อไปนี้:
- ตัดการเชื่อมต่อด้วยตนเองโดยใช้ฟังก์ชัน Disconnect()
- ทำลายอินสแตนซ์ที่เหตุการณ์เป็นของด้วยฟังก์ชัน Destroy()
- ทำลายวัตถุสคริปต์ที่การเชื่อมต่อย้อนกลับไปถึง
ลบวัตถุผู้เล่นและตัวละครหลังจากออก - เปิดใช้งาน Workspace.PlayerCharacterDestroyBehavior เพื่อทำลายวัตถุผู้เล่นและโมเดลตัวละครโดยอัตโนมัติหลังจากที่ผู้ใช้ออก หากคุณต้องการ คุณสามารถทำความสะอาดด้วยตนเอง:
Example player and character cleanuplocal Players = game:GetService("Players")Players.PlayerAdded:Connect(function(player)player.CharacterRemoving:Connect(function(character)task.defer(character.Destroy, character)end)end)Players.PlayerRemoving:Connect(function(player)task.defer(player.Destroy, player)end)
การคำนวณฟิสิกส์
การจำลองฟิสิกส์ที่มากเกินไปอาจเป็นสาเหตุหลักของการเพิ่มขึ้นของเวลาในการคำนวณต่อเฟรมทั้งในเซิร์ฟเวอร์และลูกค้า
ปัญหาทั่วไป
ความถี่ของการก้าวฟิสิกส์ที่มากเกินไป - โดยค่าเริ่มต้น พฤติกรรมการก้าวอยู่ใน โหมดปรับตัว ซึ่งฟิสิกส์จะก้าวที่ 60 Hz, 120 Hz หรือ 240 Hz ขึ้นอยู่กับความซับซ้อนของกลไกฟิสิกส์
โหมดคงที่ที่มีความแม่นยำของฟิสิกส์ที่ดีขึ้นยังมีให้ ซึ่งบังคับให้การประกอบฟิสิกส์ทั้งหมดก้าวที่ 240 Hz (สี่ครั้งต่อเฟรม) สิ่งนี้ส่งผลให้มีการคำนวณมากขึ้นอย่างมีนัยสำคัญในแต่ละเฟรม
จำนวนวัตถุที่ซับซ้อนที่จำลองมากเกินไป - ยิ่งมีการจำลองการประกอบ 3D มากเท่าไหร่ การคำนวณฟิสิกส์ก็จะใช้เวลานานขึ้นในแต่ละเฟรมบ่อยครั้ง เกมจะมีวัตถุที่ถูกจำลองซึ่งไม่จำเป็นต้องจำลองหรือจะมีกลไกที่มีข้อจำกัดและข้อต่อมากกว่าที่จำเป็น
การตรวจจับการชนที่แม่นยำเกินไป - ส่วนเมชมีคุณสมบัติ CollisionFidelity สำหรับการตรวจจับการชนซึ่งมีโหมดที่หลากหลายพร้อมระดับผลกระทบด้านประสิทธิภาพที่แตกต่างกัน โหมดการตรวจจับการชนที่แม่นยำสำหรับส่วนเมชมีค่าใช้จ่ายด้านประสิทธิภาพที่แพงที่สุดและใช้เวลานานกว่าในการคำนวณ
การบรรเทา
ยึดส่วนที่ไม่ต้องการการจำลอง - ยึดส่วนทั้งหมดที่ไม่จำเป็นต้องขับเคลื่อนด้วยฟิสิกส์ เช่น NPC แบบสถิต
ใช้การก้าวฟิสิกส์แบบปรับตัว - การก้าวแบบปรับตัวจะปรับอัตราการคำนวณฟิสิกส์สำหรับกลไกฟิสิกส์โดยอัตโนมัติ ทำให้การอัปเดตฟิสิกส์สามารถทำได้ในบางกรณีน้อยลง
ลดความซับซ้อนของกลไก
- เมื่อเป็นไปได้ ให้ลดจำนวนข้อจำกัดหรือข้อต่อฟิสิกส์ในประกอบ
- ลดการชนกันภายในกลไก เช่น โดยการใช้ขีดจำกัดหรือข้อจำกัดการไม่ชนกันกับแขนขาของรากดอลล์เพื่อป้องกันไม่ให้ชนกัน
ลดการใช้ความแม่นยำในการตรวจจับการชนสำหรับเมช
สำหรับวัตถุขนาดเล็กหรือไม่สามารถโต้ตอบได้ซึ่งผู้ใช้จะสังเกตเห็นความแตกต่างได้ยาก ให้ใช้ความแม่นยำแบบกล่อง
สำหรับวัตถุขนาดเล็กถึงขนาดกลาง ให้ใช้ความแม่นยำแบบกล่องหรือฮัลล์ ขึ้นอยู่กับรูปร่าง
สำหรับวัตถุขนาดใหญ่และซับซ้อนมาก ให้สร้างการชนแบบกำหนดเองโดยใช้ส่วนที่มองไม่เห็นเมื่อเป็นไปได้
สำหรับวัตถุที่ไม่ต้องการการชน ให้ปิดการชนและใช้ความแม่นยำแบบกล่องหรือฮัลล์ เนื่องจากเรขาคณิตการชนยังคงถูกเก็บไว้ในหน่วยความจำ
คุณสามารถเรนเดอร์เรขาคณิตการชนเพื่อวัตถุประสงค์ในการดีบักใน Studio โดยการเปิดใช้งาน Collision fidelity จาก Visualization Options วิดเจ็ตในมุมขวาบนของมุมมอง 3D
นอกจากนี้ คุณสามารถใช้ตัวกรอง CollisionFidelity=PreciseConvexDecomposition กับ Explorer ซึ่งจะแสดงจำนวนส่วนเมชทั้งหมดที่มีความแม่นยำและช่วยให้คุณเลือกได้ง่าย
สำหรับการเดินผ่านอย่างละเอียดเกี่ยวกับวิธีการเลือกตัวเลือกความแม่นยำในการตรวจจับการชนที่สมดุลระหว่างความแม่นยำและความต้องการด้านประสิทธิภาพของคุณ โปรดดูที่ ตั้งค่าพารามิเตอร์ฟิสิกส์และการเรนเดอร์
ขอบเขต MicroProfiler
| ขอบเขต | การคำนวณที่เกี่ยวข้อง |
| physicsStepped | การคำนวณฟิสิกส์โดยรวม |
| worldStep | การก้าวฟิสิกส์แบบไม่ต่อเนื่องที่ดำเนินการในแต่ละเฟรม |
การใช้หน่วยความจำฟิสิกส์
การเคลื่อนไหวของฟิสิกส์และการตรวจจับการชนใช้หน่วยความจำ ส่วนเมชมีคุณสมบัติ CollisionFidelity ที่กำหนดวิธีการที่ใช้ในการประเมินขอบเขตการชนของเมช
ปัญหาทั่วไป
โหมดการตรวจจับการชนที่เริ่มต้นและแม่นยำใช้หน่วยความจำมากกว่าสองโหมดอื่น ๆ ที่มีรูปร่างการชนที่มีความแม่นยำต่ำกว่า
หากคุณเห็นระดับการใช้หน่วยความจำสูงภายใต้ PhysicsParts คุณอาจต้องสำรวจการลด ความแม่นยำในการตรวจจับการชน ของวัตถุในเกมของคุณ
วิธีการบรรเทา
เพื่อลดหน่วยความจำที่ใช้สำหรับความแม่นยำในการตรวจจับการชน:
- สำหรับส่วนที่ไม่ต้องการการชน ให้ปิดการชนโดยการตั้งค่า BasePart.CanCollide, BasePart.CanTouch และ BasePart.CanQuery เป็น false
- ลดความแม่นยำของการชนโดยใช้การตั้งค่า CollisionFidelity Box มีค่าใช้จ่ายหน่วยความจำต่ำสุด และ Default และ Precise จะมีค่าใช้จ่ายสูงกว่าโดยทั่วไป
- โดยทั่วไปปลอดภัยที่จะตั้งค่าความแม่นยำในการชนของส่วนที่ยึดไว้ขนาดเล็กเป็น Box
- สำหรับเมชขนาดใหญ่ที่ซับซ้อนมาก คุณอาจต้องสร้างเมชการชนของคุณเองจากวัตถุขนาดเล็กที่มีความแม่นยำในการชนแบบกล่อง
มนุษย์
Humanoid เป็นคลาสที่ให้ฟังก์ชันการทำงานที่หลากหลายแก่ตัวละครผู้เล่นและตัวละครที่ไม่ใช่ผู้เล่น (NPC) แม้ว่าจะมีพลัง แต่ Humanoid จะมีค่าใช้จ่ายในการคำนวณที่สำคัญ
ปัญหาทั่วไป
- การเปิดใช้งาน HumanoidStateTypes ทั้งหมดใน NPC - มีค่าใช้จ่ายด้านประสิทธิภาพในการเปิดใช้งาน HumanoidStateTypes บางประเภท ปิดการใช้งานที่ไม่จำเป็นสำหรับ NPC ของคุณ ตัวอย่างเช่น เว้นแต่ NPC ของคุณจะปีนบันไดได้ ก็ปลอดภัยที่จะปิดสถานะ Climbing
- การสร้าง แก้ไข และสร้างโมเดลใหม่ด้วย Humanoids หรือ MeshParts ที่มีการสกินบ่อยครั้ง
- สิ่งนี้อาจมีค่าใช้จ่ายสูงสำหรับเอนจินในการประมวลผล โดยเฉพาะอย่างยิ่งหากโมเดลเหล่านี้ใช้ เสื้อผ้าหลายชั้น สิ่งนี้ยังอาจเป็นปัญหาโดยเฉพาะในเกมที่อวตารเกิดใหม่บ่อยครั้ง
- ใน MicroProfiler แท็ก updateInvalidatedFastClusters ที่ใช้เวลานาน (มากกว่า 4 มิลลิวินาที) มักจะเป็นสัญญาณว่าการสร้าง/แก้ไขอวตารกำลังทำให้เกิดการยกเลิกที่มากเกินไป
- การใช้ Humanoids ในกรณีที่ไม่จำเป็น - NPC แบบสถิตที่ไม่เคลื่อนที่โดยทั่วไปไม่จำเป็นต้องใช้คลาส Humanoid
- การเล่นอนิเมชั่นบน NPC จำนวนมากจากเซิร์ฟเวอร์ - อนิเมชั่น NPC ที่ทำงานบนเซิร์ฟเวอร์จำเป็นต้องจำลองบนเซิร์ฟเวอร์และทำซ้ำไปยังลูกค้า สิ่งนี้อาจเป็นภาระที่ไม่จำเป็น
- การเปลี่ยนขนาดและสเกลที่ไม่จำเป็น - การเปลี่ยนขนาด/สเกลทำให้ FastCluster ต้องสร้างใหม่ พยายามลดสิ่งนี้ในระหว่างการเล่นเกมหากคุณเห็นปัญหาด้านประสิทธิภาพที่เกี่ยวข้องกับ FastCluster นอกจากนี้ การเปลี่ยนแปลงคุณสมบัติอื่น ๆ อาจทำให้ FastCluster ต้องสร้างใหม่ ดังนั้นโดยทั่วไปให้ลดการเปลี่ยนแปลงเหล่านี้ให้น้อยที่สุด
การบรรเทา
- เล่นอนิเมชั่น NPC บนลูกค้า - ในเกมที่มี NPC จำนวนมาก ให้พิจารณาสร้าง Animator บนลูกค้าและเรียกใช้อนิเมชั่นในท้องถิ่น สิ่งนี้ช่วยลดภาระบนเซิร์ฟเวอร์และความจำเป็นในการทำซ้ำที่ไม่จำเป็น นอกจากนี้ยังทำให้การปรับแต่งเพิ่มเติมเป็นไปได้ (เช่น การเล่นอนิเมชั่นเฉพาะสำหรับ NPC ที่อยู่ใกล้ตัวละคร)
- ใช้ทางเลือกที่เป็นมิตรกับประสิทธิภาพแทน Humanoids - โมเดล NPC ไม่จำเป็นต้องมีวัตถุ humanoid
- สำหรับ NPC แบบสถิต ให้ใช้ AnimationController ที่เรียบง่าย เนื่องจากพวกเขาไม่จำเป็นต้องเคลื่อนที่ แต่เพียงแค่ต้องเล่นอนิเมชั่น
- สำหรับ NPC ที่เคลื่อนที่ ให้พิจารณานำเสนอการควบคุมการเคลื่อนที่ของคุณเองและใช้ AnimationController สำหรับอนิเมชั่น ขึ้นอยู่กับความซับซ้อนของ NPC ของคุณ
- ปิดการใช้งานสถานะ humanoid ที่ไม่จำเป็น - ใช้ Humanoid:SetStateEnabled() เพื่อเปิดใช้งานเฉพาะสถานะที่จำเป็นสำหรับแต่ละ humanoid
- จัดกลุ่มโมเดล NPC ที่เกิดใหม่บ่อย - แทนที่จะทำลาย NPC โดยสิ้นเชิง ให้ส่ง NPC ไปยังกลุ่ม NPC ที่ไม่ใช้งาน ด้วยวิธีนี้ เมื่อจำเป็นต้องเกิด NPC ใหม่ คุณสามารถเปิดใช้งาน NPC จากกลุ่มได้ง่าย ๆ กระบวนการนี้เรียกว่าการจัดกลุ่ม ซึ่งช่วยลดจำนวนครั้งที่ต้องสร้างตัวละครใหม่
- สร้าง NPC เฉพาะเมื่อผู้ใช้อยู่ใกล้ - อย่าสร้าง NPC เมื่อผู้ใช้ไม่ได้อยู่ในระยะ และตัดการเชื่อมต่อเมื่อผู้ใช้ออกจากระยะ
- หลีกเลี่ยงการเปลี่ยนแปลงในลำดับชั้นของอวตารหลังจากที่มันถูกสร้างขึ้น - การปรับเปลี่ยนบางอย่างในลำดับชั้นของอวตารมีผลกระทบด้านประสิทธิภาพที่สำคัญ มีการปรับแต่งบางอย่างที่มีให้:
- สำหรับอนิเมชั่นเชิงพาณิชย์ที่กำหนดเอง อย่าอัปเดตคุณสมบัติ JointInstance.C0 และ JointInstance.C1 แทนที่จะอัปเดตคุณสมบัติ Motor6D.Transform
- หากคุณต้องการแนบวัตถุ BasePart ใด ๆ กับอวตาร ให้ทำเช่นนั้นนอกลำดับชั้นของโมเดลอวตาร
ขอบเขต MicroProfiler
| ขอบเขต | การคำนวณที่เกี่ยวข้อง |
| stepHumanoid | การควบคุมและฟิสิกส์ของ Humanoid |
| stepAnimation | อนิเมชั่นของ Humanoid และ Animator |
| updateInvalidatedFastClusters | เกี่ยวข้องกับการสร้างหรือแก้ไขอวตาร |
การเรนเดอร์
ส่วนสำคัญของเวลาที่ลูกค้าใช้ในแต่ละเฟรมคือการเรนเดอร์ฉากในเฟรมปัจจุบัน เซิร์ฟเวอร์ไม่ทำการเรนเดอร์ ดังนั้นส่วนนี้จึงเฉพาะเจาะจงสำหรับลูกค้า
การเรียกวาด
การเรียกวาดคือชุดคำสั่งจากเอนจินไปยัง GPU เพื่อเรนเดอร์บางสิ่ง การเรียกวาดมีค่าใช้จ่ายสูง โดยทั่วไปแล้ว ยิ่งมีการเรียกวาดน้อยลงต่อเฟรม เวลาที่ใช้ในการเรนเดอร์เฟรมก็จะยิ่งน้อยลง
คุณสามารถดูว่ามีการเรียกวาดกี่ครั้งในขณะนี้โดยใช้รายการ Render Stats ⟩ Timing ใน Studio คุณสามารถดู Render Stats ในลูกค้าโดยการกด ShiftF2
ยิ่งมีวัตถุที่ต้องเรนเดอร์ในฉากของคุณในเฟรมที่กำหนดมากเท่าใด การเรียกวาดก็จะยิ่งมากขึ้นเท่านั้น อย่างไรก็ตาม เอนจิน Roblox ใช้กระบวนการที่เรียกว่า instancing เพื่อรวมเมชที่เหมือนกันที่มีลักษณะพื้นผิวเดียวกันเข้าด้วยกันในการเรียกวาดเดียว โดยเฉพาะอย่างยิ่ง เมชหลายตัวที่มี MeshContent เดียวกันจะถูกจัดการในการเรียกวาดเดียวเมื่อ:
- SurfaceAppearances เหมือนกันหากมีอยู่ มิฉะนั้นเมื่อ TextureContents เหมือนกัน
- วัสดุเหมือนกันเมื่อทั้ง SurfaceAppearance และ MeshPart.TextureID ไม่มีอยู่
ปัญหาอื่น ๆ ที่พบบ่อย
ความหนาแน่นของวัตถุมากเกินไป - หากมีวัตถุจำนวนมากที่มีความหนาแน่นสูง การเรนเดอร์พื้นที่นี้ของฉากจะต้องใช้การเรียกวาดมากขึ้น หากคุณพบว่าอัตราเฟรมของคุณลดลงเมื่อมองไปที่บางส่วนของแผนที่ นี่อาจเป็นสัญญาณที่ดีว่าความหนาแน่นของวัตถุในพื้นที่นี้สูงเกินไป
วัตถุเช่นสติกเกอร์ พื้นผิว และอนุภาคไม่สามารถจัดกลุ่มได้ดีและทำให้เกิดการเรียกวาดเพิ่มเติม ให้ความสนใจกับประเภทวัตถุเหล่านี้ในฉาก โดยเฉพาะอย่างยิ่ง การเปลี่ยนแปลงคุณสมบัติของ ParticleEmitters อาจมีผลกระทบอย่างมากต่อประสิทธิภาพ
โอกาสในการสร้างที่พลาดไป - บ่อยครั้งที่ฉากจะรวมเมชเดียวกันซ้ำหลายครั้ง แต่แต่ละสำเนาของเมชมี ID ของเมชหรือพื้นผิวที่แตกต่างกัน สิ่งนี้ป้องกันไม่ให้เกิดการสร้างและอาจนำไปสู่การเรียกวาดที่ไม่จำเป็น
สาเหตุทั่วไปของปัญหานี้คือเมื่อฉากทั้งหมดถูกนำเข้ามาในครั้งเดียว แทนที่จะนำเข้าสินทรัพย์แต่ละรายการไปยัง Roblox และจากนั้นทำซ้ำหลังจากนำเข้าเพื่อประกอบฉาก
แม้แต่สคริปต์ง่าย ๆ เช่นนี้ก็สามารถช่วยให้คุณระบุส่วนเมชที่มีชื่อเดียวกันซึ่งใช้ ID เมชที่แตกต่างกันได้:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endendผลลัพธ์ (โดยเปิดใช้งาน Stack Lines) อาจมีลักษณะดังนี้ บรรทัดที่ซ้ำกันบ่งชี้ถึงการใช้เมชเดียวกันซึ่งเป็นสิ่งที่ดี บรรทัดที่ไม่ซ้ำกันไม่ได้หมายความว่าไม่ดีเสมอไป แต่ขึ้นอยู่กับแผนการตั้งชื่อของคุณ อาจบ่งชี้ถึงเมชที่ซ้ำกันในเกมของคุณ:
LargeRock, rbxassetid://106420009602747 (x144) -- ดีLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- อาจเป็นการซ้ำกันทั้งหมดความซับซ้อนของวัตถุมากเกินไป - แม้ว่าจะไม่สำคัญเท่ากับจำนวนการเรียกวาด แต่จำนวนสามเหลี่ยมในฉากก็มีอิทธิพลต่อระยะเวลาที่ใช้ในการเรนเดอร์เฟรม ฉากที่มีเมชที่ซับซ้อนมากจำนวนมากเป็นปัญหาทั่วไป เช่นเดียวกับฉากที่มีคุณสมบัติ MeshPart.RenderFidelity ตั้งอยู่ที่ Precise บนเมชมากเกินไป
การสร้างเงาที่มากเกินไป - การจัดการเงาเป็นกระบวนการที่มีค่าใช้จ่ายสูง และแผนที่ที่มีจำนวนและความหนาแน่นของวัตถุที่สร้างเงา (หรือจำนวนและความหนาแน่นของชิ้นส่วนเล็ก ๆ ที่ได้รับผลกระทบจากเงา) อาจมีปัญหาด้านประสิทธิภาพ
การทับซ้อนของความโปร่งใสที่สูงเกินไป - การวางวัตถุที่มีความโปร่งใสบางส่วนใกล้กันบังคับให้เอนจินเรนเดอร์พิกเซลที่ทับซ้อนกันหลายครั้ง ซึ่งอาจทำให้ประสิทธิภาพลดลง สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการระบุและแก้ไขปัญหานี้ โปรดดูที่ ลบความโปร่งใสที่ซ้อนกัน
การเคลื่อนไหวของ MeshPart ที่มีการสกินมากเกินไป - MeshParts ที่มีการสกินซึ่งเป็นส่วนหนึ่งของโมเดลที่ไม่มี Humanoid จะถูกจัดกลุ่มโดยใช้ FastClusters ที่จัดระเบียบเชิงพื้นที่ เมื่อ MeshParts เหล่านี้เคลื่อนที่ พวกเขาจะต้องถูกเพิ่มและลบออกจากกลุ่มเชิงพื้นที่เหล่านี้อย่างต่อเนื่อง ทำให้ต้องสร้างกลุ่มใหม่และส่งผลกระทบต่อประสิทธิภาพ
- วิธีแก้ปัญหาที่มีประสิทธิภาพสูงคือการฝัง Humanoid ไว้ในโมเดล การมี Humanoid จะทำให้พฤติกรรมการจัดกลุ่มเชิงพื้นที่เริ่มต้นถูกแทนที่ โดยบังคับให้ใช้ FastCluster เดียวที่เป็นเอกภาพสำหรับโมเดลทั้งหมด ดังนั้นการอัปเดตตำแหน่งจึงไม่จำเป็นต้องสร้างกลุ่มใหม่ ซึ่งช่วยบรรเทาคอขวดด้านประสิทธิภาพ เทคนิคนี้ควรสงวนไว้สำหรับ MeshParts ที่คาดว่าจะเคลื่อนที่เท่านั้น เนื่องจากอาจทำให้เกิดการใช้หน่วยความจำมากขึ้นและทำให้ประโยชน์ของการปรับแต่งเชิงพื้นที่ลดลง เราขอแนะนำให้คุณทำการวิเคราะห์ประสิทธิภาพเกมของคุณหลังจากทำการเปลี่ยนแปลงประเภทนี้ ดู เคล็ดลับประสิทธิภาพ Humanoid สำหรับข้อมูลเพิ่มเติม
จำนวนชิ้นส่วนใน Model มากเกินไป - จำนวนชิ้นส่วนในโมเดลมากเกินไปอาจทำให้เกิดการสร้างใหม่บ่อยขึ้นเนื่องจากความเป็นไปได้ที่คุณสมบัติของชิ้นส่วนจะเปลี่ยนแปลงซึ่งนำไปสู่การสร้างใหม่ทั้งหมด ค้นหาสมดุลที่เหมาะสมของชิ้นส่วนในโมเดลเมื่อใช้ FastCluster
การบรรเทา
- การสร้างเมชที่เหมือนกันและลดจำนวนเมชที่ไม่ซ้ำกัน - หากคุณมั่นใจว่าเมชที่เหมือนกันทั้งหมดมี ID ของสินทรัพย์พื้นฐานเดียวกัน เอนจินสามารถรับรู้และเรนเดอร์พวกเขาในการเรียกวาดเดียว ตรวจสอบให้แน่ใจว่าได้อัปโหลดเมชแต่ละตัวในแผนที่เพียงครั้งเดียวและจากนั้นทำซ้ำใน Studio เพื่อใช้งานซ้ำ แทนที่จะนำเข้าแผนที่ขนาดใหญ่ทั้งหมดซึ่งอาจทำให้เมชที่เหมือนกันมี ID เนื้อหาที่แยกจากกันและถูกมองว่าเป็นสินทรัพย์ที่ไม่ซ้ำกันโดยเอนจิน Packages เป็นกลไกที่มีประโยชน์สำหรับการใช้งานซ้ำของวัตถุ
- การตัดการเชื่อมต่อ - การตัดการเชื่อมต่อหมายถึงกระบวนการกำจัดการเรียกวาดสำหรับวัตถุที่ไม่เกี่ยวข้องกับเฟรมที่เรนเดอร์สุดท้าย โดยค่าเริ่มต้น เอนจินจะข้ามการเรียกวาดสำหรับวัตถุที่อยู่นอกมุมมองของกล้อง (frustum culling) และชิ้นส่วน เมช และภูมิประเทศที่ถูกบังจากมุมมองโดยวัตถุอื่น (occlusion culling) ในบางสถานการณ์ เช่น สภาพแวดล้อมในร่ม คุณอาจสามารถใช้ระบบห้องหรือพอร์ทัลและตัดการเชื่อมต่อวัตถุเพื่อช่วยลดการเรียกวาดหรือภาระการคำนวณโดยรวม
- ลดระดับรายละเอียดสำหรับโมเดล - เปิดใช้งาน instance streaming และตั้งค่าคุณสมบัติ LevelOfDetail ของโมเดลโลกของคุณเป็น SLIM เพื่อเรนเดอร์ เมช SLIM ที่เบาและปรับให้เหมาะสม สำหรับโมเดลเมื่อระยะห่างจากกล้องเพิ่มขึ้น
- ลดระดับรายละเอียดสำหรับอวตาร - เปิดใช้งานการสตรีมอินสแตนซ์และตั้งค่า Workspace.EnableSLIMAvatars เพื่อเรนเดอร์อวตารแพลตฟอร์มเป็น การแสดงผล SLIM ที่เบาและปรับให้เหมาะสม พร้อมการสนับสนุนการเคลื่อนไหวเต็มรูปแบบเมื่อระยะห่างจากกล้องเพิ่มขึ้น
- ลดความแม่นยำในการเรนเดอร์ - ตั้งค่า MeshPart.RenderFidelity เป็น Automatic หรือ Performance สิ่งนี้ช่วยให้เมชสามารถกลับไปใช้ทางเลือกที่ซับซ้อนน้อยลง ซึ่งสามารถลดจำนวนโพลิกอนที่ต้องเรนเดอร์
- ปิดการสร้างเงาบนชิ้นส่วนและวัตถุแสงที่เหมาะสม - เอนจิน Roblox จะลดคุณภาพของเงาโดยอัตโนมัติเมื่อระดับคุณภาพกราฟิกของลูกค้าลดลง ในที่สุดจะปิดการสร้างเงาโดยสิ้นเชิงที่ระดับคุณภาพต่ำกว่า 4 อย่างไรก็ตาม คุณสามารถปิดการสร้างเงาในคุณสมบัติของวัตถุแสงและชิ้นส่วนเพื่อปรับปรุงประสิทธิภาพในขณะที่เปิดใช้งานเงาและเพิ่มโอกาสที่เงาจะยังคงเปิดอยู่ ตัวอย่างบางประการของการปรับแต่งที่คุณสามารถทำได้ทั้งในเวลาตัดต่อหรือแบบไดนามิกในระหว่างการทำงาน:
ใช้คุณสมบัติ BasePart.CastShadow เพื่อปิดการสร้างเงาบนชิ้นส่วนเล็ก ๆ ที่ไม่น่าจะมองเห็นเงาได้ กลยุทธ์นี้มีประสิทธิภาพโดยเฉพาะเมื่อใช้กับชิ้นส่วนที่อยู่ห่างไกลจากกล้องของผู้ใช้
ปิดเงาบนวัตถุที่เคลื่อนที่เมื่อเป็นไปได้
ปิด Light.Shadows บนอินสแตนซ์แสงที่วัตถุไม่จำเป็นต้องสร้างเงา
จำกัดระยะและมุมของอินสแตนซ์แสง
ใช้อินสแตนซ์แสงน้อยลง
พิจารณาปิดไฟที่อยู่นอกช่วงเฉพาะหรือเป็นรายห้องสำหรับสภาพแวดล้อมในร่ม
ขอบเขต MicroProfiler
| ขอบเขต | การคำนวณที่เกี่ยวข้อง |
| Prepare and Perform | การเรนเดอร์โดยรวม |
| Perform/Scene/computeLightingPerform | การอัปเดตกริดแสงและเงา |
| LightGridCPU | การอัปเดตกริดแสงแบบวอกเซล |
| ShadowMapSystem | การสร้างแผนที่เงา |
| Perform/Scene/UpdateView | การเตรียมการสำหรับการเรนเดอร์และการอัปเดตอนุภาค |
| Perform/Scene/RenderView | การเรนเดอร์และการประมวลผลหลังการเรนเดอร์ |
การเชื่อมต่อและการทำซ้ำ
การเชื่อมต่อและการทำซ้ำอธิบายกระบวนการที่ข้อมูลถูกส่งระหว่างเซิร์ฟเวอร์และลูกค้าที่เชื่อมต่อ ข้อมูลจะถูกส่งระหว่างลูกค้าและเซิร์ฟเวอร์ในทุกเฟรม แต่ข้อมูลจำนวนมากขึ้นต้องใช้เวลาในการคำนวณมากขึ้น
ปัญหาทั่วไป
การจราจรทางไกลมากเกินไป - การส่งข้อมูลจำนวนมากผ่านวัตถุ RemoteEvent หรือ RemoteFunction หรือการเรียกใช้งานบ่อยเกินไปอาจทำให้ใช้เวลา CPU จำนวนมากในการประมวลผลแพ็กเกจที่เข้ามาในแต่ละเฟรม ข้อผิดพลาดทั่วไป ได้แก่:
- การทำซ้ำข้อมูลในทุกเฟรมที่ไม่จำเป็นต้องทำซ้ำ
- การทำซ้ำข้อมูลตามการป้อนข้อมูลของผู้ใช้โดยไม่มีกลไกในการควบคุม
- การส่งข้อมูลมากกว่าที่จำเป็น ตัวอย่างเช่น การส่งสินค้าคงคลังทั้งหมดของผู้เล่นเมื่อพวกเขาซื้อรายการแทนที่จะเป็นรายละเอียดของรายการที่ซื้อ
การสร้างหรือการลบต้นไม้ของอินสแตนซ์ที่ซับซ้อน - เมื่อมีการเปลี่ยนแปลงในโมเดลข้อมูลบนเซิร์ฟเวอร์ จะมีการทำซ้ำไปยังลูกค้าที่เชื่อมต่อ สิ่งนี้หมายความว่าการสร้างและทำลายลำดับชั้นของอินสแตนซ์ขนาดใหญ่ เช่น แผนที่ในระหว่างการทำงานอาจใช้เครือข่ายมาก
สาเหตุทั่วไปที่นี่คือข้อมูลอนิเมชั่นที่ซับซ้อนที่บันทึกโดยปลั๊กอิน Animation Editor ในร่าง หากสิ่งเหล่านี้ไม่ได้ถูกลบออกก่อนที่เกมจะเผยแพร่และโมเดลที่มีอนิเมชั่นถูกทำซ้ำเป็นประจำ ข้อมูลจำนวนมากจะถูกทำซ้ำโดยไม่จำเป็น
TweenService ฝั่งเซิร์ฟเวอร์ - หาก TweenService ถูกใช้เพื่อทำให้วัตถุเคลื่อนที่ในฝั่งเซิร์ฟเวอร์ คุณสมบัติที่ถูกทำให้เคลื่อนที่จะถูกทำซ้ำไปยังลูกค้าในทุกเฟรม สิ่งนี้ไม่เพียงทำให้การเคลื่อนที่กระตุกเมื่อความหน่วงของลูกค้าเปลี่ยนแปลง แต่ยังทำให้เกิดการจราจรทางเครือข่ายที่ไม่จำเป็นมากมาย
การบรรเทา
คุณสามารถใช้กลยุทธ์ต่อไปนี้เพื่อลดการทำซ้ำที่ไม่จำเป็น:
- หลีกเลี่ยงการส่งข้อมูลจำนวนมากในครั้งเดียวผ่านเหตุการณ์ทางไกล แทนที่จะส่งข้อมูลที่จำเป็นในความถี่ที่ต่ำกว่า ตัวอย่างเช่น สำหรับสถานะของตัวละคร ให้ทำซ้ำเมื่อมีการเปลี่ยนแปลงแทนที่จะเป็นทุกเฟรม
- แบ่งต้นไม้ของอินสแตนซ์ที่ซับซ้อน เช่น แผนที่และโหลดเป็นชิ้น ๆ เพื่อกระจายงานในการทำซ้ำเหล่านี้ไปยังหลายเฟรม
- ทำความสะอาดข้อมูลเมตาอนิเมชั่น โดยเฉพาะอย่างยิ่งไดเรกทอรีอนิเมชั่นของร่างหลังจากการนำเข้า
- จำกัดการทำซ้ำอินสแตนซ์ที่ไม่จำเป็น โดยเฉพาะในกรณีที่เซิร์ฟเวอร์ไม่จำเป็นต้องมีความรู้เกี่ยวกับอินสแตนซ์ที่ถูกสร้างขึ้น สิ่งนี้รวมถึง:
- เอฟเฟกต์ภาพ เช่น การระเบิดหรือการระเบิดของเวทมนตร์ เซิร์ฟเวอร์จำเป็นต้องรู้เพียงตำแหน่งเพื่อกำหนดผลลัพธ์ ในขณะที่ลูกค้าสามารถสร้างภาพได้ในท้องถิ่น
- โมเดลมุมมองไอเท็มในมุมมองบุคคลที่หนึ่ง
- ทำให้วัตถุเคลื่อนที่บนลูกค้าแทนที่จะเป็นเซิร์ฟเวอร์
ขอบเขต MicroProfiler
| ขอบเขต | การคำนวณที่เกี่ยวข้อง |
| ProcessPackets | การประมวลผลสำหรับแพ็กเกจเครือข่ายที่เข้ามา เช่น การเรียกใช้งานเหตุการณ์และการเปลี่ยนแปลงคุณสมบัติ |
| Allocate Bandwidth and Run Senders | เหตุการณ์ที่ส่งออกที่เกี่ยวข้องกับเซิร์ฟเวอร์ |
การใช้หน่วยความจำของสินทรัพย์
กลไกที่มีผลกระทบสูงสุดที่ผู้สร้างสามารถใช้เพื่อปรับปรุงการใช้หน่วยความจำของลูกค้าคือการเปิดใช้งาน instance streaming
การสตรีมอินสแตนซ์
การสตรีมอินสแตนซ์จะโหลดส่วนของโมเดลข้อมูลที่ไม่จำเป็นโดยเลือก ซึ่งสามารถนำไปสู่เวลาการโหลดที่ลดลงอย่างมากและเพิ่มความสามารถของลูกค้าในการป้องกันการชนเมื่อเผชิญกับแรงกดดันจากหน่วยความจำ
หากคุณพบปัญหาหน่วยความจำและปิดการสตรีมอินสแตนซ์ ให้พิจารณาอัปเดตเกมของคุณเพื่อสนับสนุน โดยเฉพาะอย่างยิ่งหากโลก 3D ของคุณมีขนาดใหญ่ การสตรีมอินสแตนซ์จะขึ้นอยู่กับระยะทางในพื้นที่ 3D ดังนั้นโลกที่ใหญ่กว่าจะได้รับประโยชน์มากขึ้นจากมัน
หากการสตรีมอินสแตนซ์เปิดใช้งาน คุณสามารถเพิ่มความก้าวร้าวของมันได้ ตัวอย่างเช่น ให้พิจารณา:
- ลดการใช้ Enum.ModelStreamingMode.Persistent เท่าที่จะทำได้ คุณอาจต้องอัปเดตสคริปต์ของคุณหากคุณใช้เป็นมาตรการความเข้ากันได้
- ลด Workspace.StreamingMinRadius และ Workspace.StreamingTargetRadius
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกการสตรีมและประโยชน์ของพวกเขา โปรดดูที่ คุณสมบัติการสตรีม
ปัญหาอื่น ๆ ที่พบบ่อย
การทำซ้ำสินทรัพย์ - ข้อผิดพลาดทั่วไปคือการอัปโหลดสินทรัพย์เดียวกันหลายครั้งซึ่งส่งผลให้เกิด ID สินทรัพย์ที่แตกต่างกัน สิ่งนี้อาจทำให้เนื้อหาเดียวกันถูกโหลดเข้าสู่หน่วยความจำหลายครั้ง
ปริมาณสินทรัพย์มากเกินไป - แม้ว่าสินทรัพย์จะไม่เหมือนกัน แต่ก็มีกรณีที่พลาดโอกาสในการใช้งานสินทรัพย์เดียวกันซ้ำและประหยัดหน่วยความจำ
ไฟล์เสียง - ไฟล์เสียงอาจเป็นผู้มีส่วนร่วมที่น่าประหลาดใจต่อการใช้หน่วยความจำ โดยเฉพาะอย่างยิ่งหากคุณโหลดไฟล์ทั้งหมดเข้าสู่ลูกค้าในครั้งเดียวแทนที่จะโหลดเฉพาะสิ่งที่คุณต้องการสำหรับส่วนหนึ่งของเกม สำหรับกลยุทธ์ โปรดดูที่ เวลาโหลด
พื้นผิวความละเอียดสูง - การใช้หน่วยความจำกราฟิกสำหรับพื้นผิวไม่เกี่ยวข้องกับขนาดของพื้นผิวในดิสก์ จำนวนพิกเซลในพื้นผิวกำหนดการใช้หน่วยความจำ ตัวอย่างเช่น พื้นผิวขนาด 1024x1024 พิกเซลใช้หน่วยความจำกราฟิกมากกว่าพื้นผิวขนาด 512x512 ถึงสี่เท่า
รูปภาพที่อัปโหลดไปยัง Roblox จะถูกแปลงเป็นรูปแบบที่กำหนดไว้ ดังนั้นจึงไม่มีประโยชน์ในการใช้หน่วยความจำในการอัปโหลดรูปภาพในโมเดลสีที่เกี่ยวข้องกับไบต์ต่อพิกเซลที่น้อยลง นอกจากนี้ การบีบอัดรูปภาพก่อนการอัปโหลดหรือการลบช่องอัลฟาจากรูปภาพที่ไม่ต้องการอาจลดขนาดของรูปภาพในดิสก์ แต่ไม่ช่วยปรับปรุงการใช้หน่วยความจำ
ขณะที่เกมโหลด เอนจินจะเริ่มต้นด้วยพื้นผิวคุณภาพต่ำกว่าและจากนั้นจะเพิ่มคุณภาพตามหน่วยความจำของอุปกรณ์ที่มีอยู่ ระยะห่างจากกล้อง จำนวนพื้นที่หน้าจอที่พื้นผิวใช้ และปัจจัยอื่น ๆ แม้ว่าเช่นนั้น การปรับขนาดพื้นผิวของคุณอย่างมีกลยุทธ์สามารถปรับปรุงการใช้หน่วยความจำในเกมของคุณได้
การบรรเทา
อัปโหลดสินทรัพย์เพียงครั้งเดียว - ใช้ ID สินทรัพย์เดียวกันซ้ำในวัตถุและตรวจสอบให้แน่ใจว่าสินทรัพย์เดียวกัน โดยเฉพาะอย่างยิ่งเมชและภาพ ไม่ได้ถูกอัปโหลดแยกกันหลายครั้ง
ค้นหาและแก้ไขสินทรัพย์ที่ซ้ำกัน - มองหาส่วนเมชและพื้นผิวที่เหมือนกันซึ่งถูกอัปโหลดหลายครั้งด้วย ID ที่แตกต่างกัน
- แม้ว่าจะไม่มี API ในการตรวจจับความคล้ายคลึงของสินทรัพย์โดยอัตโนมัติ แต่คุณสามารถรวบรวม ID สินทรัพย์ภาพทั้งหมดในสถานที่ของคุณ (ไม่ว่าจะด้วยตนเองหรือด้วยสคริปต์) ดาวน์โหลดพวกเขาและเปรียบเทียบโดยใช้เครื่องมือเปรียบเทียบภายนอก
- สำหรับส่วนเมช กลยุทธ์ที่ดีที่สุดคือการนำ ID เมชที่ไม่ซ้ำกันและจัดระเบียบตามขนาดเพื่อระบุการซ้ำกันด้วยตนเอง
- แทนที่จะใช้พื้นผิวแยกกันสำหรับสีที่แตกต่างกัน ให้อัปโหลดพื้นผิวเดียวและใช้คุณสมบัติ SurfaceAppearance.Color เพื่อใช้สีต่าง ๆ กับมัน
นำเข้าสินทรัพย์ในแผนที่แยกกัน - แทนที่จะนำเข้าแผนที่ทั้งหมดในครั้งเดียว ให้นำเข้าและสร้างสินทรัพย์ในแผนที่แยกกันและสร้างใหม่ พื้นที่นำเข้าไม่ทำการลบซ้ำของเมช ดังนั้นหากคุณนำเข้าแผนที่ขนาดใหญ่ที่มีพื้นผิวแยกกันมากมาย แต่ละพื้นผิวเหล่านั้นจะถูกนำเข้าเป็นสินทรัพย์แยกกัน (แม้ว่าจะเป็นการซ้ำกัน) สิ่งนี้อาจนำไปสู่ปัญหาด้านประสิทธิภาพและหน่วยความจำในอนาคต เนื่องจากแต่ละเมชจะถูกมองว่าเป็นรายบุคคลและใช้หน่วยความจำและการเรียกวาด
จำกัดพิกเซลของภาพ ให้อยู่ในจำนวนที่จำเป็นเท่านั้น เว้นแต่ภาพจะใช้พื้นที่ทางกายภาพจำนวนมากบนหน้าจอ มักจะต้องการพิกเซลสูงสุด 512x512 พิกเซล ภาพเล็ก ๆ ส่วนใหญ่ควรมีขนาดเล็กกว่า 256x256 พิกเซล
ใช้แผ่นตัด เพื่อให้แน่ใจว่าการใช้พื้นผิวสูงสุดในแผนที่ 3D สำหรับขั้นตอนและตัวอย่างในการสร้างแผ่นตัด โปรดดูที่ สร้างแผ่นตัด
คุณอาจพิจารณาใช้สไปรท์ชีตเพื่อโหลดภาพ UI ขนาดเล็กหลายภาพเป็นภาพเดียว จากนั้นคุณสามารถใช้ ImageLabel.ImageRectOffset และ ImageLabel.ImageRectSize เพื่อแสดงส่วนต่าง ๆ ของแผ่น
เวลาโหลด
ก่อนที่ผู้เล่นจะเริ่มเล่น ลูกค้าต้องได้รับทุกอย่างใน ReplicatedFirst และ ReplicatedStorage เนื้อหาต้นฉบับของ Workspace และสินทรัพย์ใด ๆ ที่สคริปต์ของคุณโหลดล่วงหน้า ยิ่งมีเนื้อหามากเท่าใดที่ลูกค้าต้องดาวน์โหลดและประมวลผลในช่วงเวลานี้ ผู้เล่นก็จะรอนานขึ้น สำหรับการแบ่งปันแต่ละขั้นตอนในกระบวนการเข้าร่วม โปรดดูที่ ระบุปัญหาด้านประสิทธิภาพ
ปัญหาทั่วไป
การสตรีมอินสแตนซ์ปิดใช้งาน - โดยไม่มี การสตรีมอินสแตนซ์ ทั้งหมด Workspace จะต้องทำซ้ำไปยังลูกค้าก่อนที่ผู้เล่นจะสามารถเข้าร่วมได้ ดังนั้นเกมที่มีชิ้นส่วน โมเดล และสินทรัพย์จำนวนมากจึงมีเวลาโหลดที่ยาวนานขึ้นตามสัดส่วน
เนื้อหามากเกินไปใน ReplicatedStorage - ทุกอย่างใน ReplicatedStorage จะทำซ้ำไปยังลูกค้าทุกคนก่อนที่ผู้เล่นจะสามารถเห็นหรือโต้ตอบกับสิ่งใดได้ ไม่ว่าจะเป็นสิ่งที่ลูกค้าต้องการในทันทีหรือไม่ โมเดลฟรีและสินทรัพย์ที่สืบทอดสามารถเพิ่มขนาดได้อย่างเงียบ ๆ
เนื้อหา ReplicatedFirst ที่หนัก - ReplicatedFirst จะทำซ้ำก่อนสิ่งอื่นใดและมีอยู่เพื่อถือหน้าจอโหลดที่กำหนดเองเป็นหลัก สินทรัพย์หรือสคริปต์เพิ่มเติมแต่ละรายการในนั้นจะทำให้การเริ่มต้นของเฟสการทำซ้ำหลักล่าช้า
การใช้ PreloadAsync() มากเกินไป - เกมหลายเกมใช้หน้าจอโหลดที่กำหนดเองและใช้ ContentProvider:PreloadAsync() เพื่อขอสินทรัพย์เพื่อให้ภาพ เสียง และเมชถูกดาวน์โหลดในพื้นหลัง วิธีการนี้ช่วยให้แน่ใจว่าส่วนสำคัญของเกมของคุณถูกโหลดอย่างเต็มที่โดยไม่มีการปรากฏตัว แต่ข้อผิดพลาดทั่วไปคือการโหลดสินทรัพย์มากกว่าที่จำเป็นจริง ๆ
ตัวอย่างของแนวทางปฏิบัติที่ไม่ดีคือการโหลด Workspace ทั้งหมด แม้ว่าสิ่งนี้อาจป้องกันการปรากฏตัวของพื้นผิว แต่จะเพิ่มเวลาโหลดอย่างมีนัยสำคัญ
แนวทางปฏิบัติที่คล้ายกันคือการใช้ ContentProvider.RequestQueueSize เพื่อให้แน่ใจว่าสินทรัพย์ที่ร้องขอทั้งหมดได้เสร็จสิ้นการโหลด อย่างไรก็ตาม สิ่งนี้นำเสนอปัญหาเดียวกันของเวลาโหลดที่เพิ่มขึ้นอย่างมีนัยสำคัญ ในขณะที่ยังเป็นวิธีการที่ไม่น่าเชื่อถือเนื่องจากธรรมชาติที่เปลี่ยนแปลงของมัน
สคริปต์เริ่มต้นที่มีค่าใช้จ่ายสูง - การดำเนินการสคริปต์ที่หนักใน LocalScripts ที่ทำงานในระหว่างกระบวนการเข้าร่วม เช่น การโหลดโครงสร้างข้อมูลขนาดใหญ่แบบซิงโครนัส จะทำให้การเปลี่ยนจากการทำซ้ำไปสู่สถานะที่เล่นได้ล่าช้า
การบรรเทา
เปิดใช้งานการสตรีมอินสแตนซ์ - การสตรีมจะส่งเฉพาะส่วนของโลกของคุณที่อยู่ใกล้จุดเกิดของผู้เล่นในระหว่างการเข้าร่วมครั้งแรก จากนั้นจะสตรีมส่วนที่เหลืออย่างค่อยเป็นค่อยไปเมื่อผู้เล่นเคลื่อนที่ผ่านเกมของคุณ สำหรับเกมที่ยังไม่ใช้การสตรีม การสตรีมเป็นหนึ่งในวิธีที่มีประสิทธิภาพที่สุดในการลดเวลาโหลด การสตรีมจะเปิดใช้งานโดยค่าเริ่มต้นสำหรับสถานที่ใหม่ สำหรับขั้นตอนการตั้งค่า การอ้างอิงคุณสมบัติ และแนวทางการย้าย โปรดดูที่ การสตรีมอินสแตนซ์
ทำให้ ReplicatedStorage เบา - เก็บเฉพาะสิ่งที่ลูกค้าต้องการในขณะเข้าร่วม เช่น ModuleScripts ที่ใช้โดย LocalScripts ย้ายโมเดลขนาดใหญ่ เสียง และสินทรัพย์อื่น ๆ ที่ไม่จำเป็นต้องใช้ในทันทีไปยัง ServerStorage และย้ายไปยังลูกค้าเมื่อผู้เล่นต้องการเท่านั้น ตรวจสอบ ReplicatedStorage เป็นประจำสำหรับเนื้อหาที่คุณไม่ใช้แล้ว
ทำให้ ReplicatedFirst เบา - จำกัด ReplicatedFirst ให้มีเฉพาะสิ่งที่หน้าจอโหลดของคุณต้องการ เช่น องค์ประกอบ UI พื้นฐานแทนที่จะเป็นพื้นผิวขนาดใหญ่หรือโมเดล 3D และหลีกเลี่ยงการคำนวณที่หนักในสคริปต์ที่ทำงานจากมัน สำหรับการสร้างหน้าจอโหลดที่กำหนดเอง รวมถึงเมื่อเรียกใช้ ReplicatedFirst:RemoveDefaultLoadingScreen() โปรดดูที่ หน้าจอโหลด
โหลดสินทรัพย์ที่จำเป็นเท่านั้น - ใช้ ContentProvider:PreloadAsync() เฉพาะในสถานการณ์ที่จำเป็น ซึ่งรวมถึง:
- รูปภาพในหน้าจอโหลด
- รูปภาพที่สำคัญในเมนูเกมของคุณ เช่น พื้นหลังปุ่มและไอคอน
- สินทรัพย์ที่สำคัญในพื้นที่เริ่มต้นหรือการเกิด
หากคุณต้องโหลดสินทรัพย์จำนวนมาก เราขอแนะนำให้คุณจัดเตรียมปุ่ม ข้ามการโหลด
เลื่อนงานเริ่มต้นที่ไม่จำเป็น - โหลดเนื้อหาที่ผู้เล่นยังไม่ถึง เช่น แผง UI ที่พวกเขายังไม่ได้เปิด เฉพาะเมื่อจำเป็นครั้งแรก สำหรับงานการเริ่มต้นที่ต้องเกิดขึ้นในช่วงต้น ให้กระจายไปยังหลายเฟรมแทนที่จะทำทั้งหมดในครั้งเดียว สำหรับเทคนิค โปรดดูที่ การคำนวณสคริปต์