ทำงานกับก้อนจำนวนมาก ปรับปรุงประสิทธิภาพ?


12

แก้ไข:เพื่อสรุปคำถามฉันมีโลกตาม voxel (สไตล์ Minecraft (ขอบคุณคอมมิวนิสต์ Duck)) ซึ่งเป็นทุกข์จากการทำงานที่ไม่ดี ฉันไม่ได้เป็นแหล่งกำเนิดบวก แต่ต้องการคำแนะนำที่เป็นไปได้เกี่ยวกับวิธีกำจัดมัน

ฉันกำลังทำงานในโครงการที่โลกประกอบด้วยลูกบาศก์จำนวนมาก (ฉันจะให้ตัวเลขแก่คุณ แต่เป็นโลกที่ผู้ใช้กำหนด) หนึ่งการทดสอบของฉันอยู่ประมาณบล็อก (48 x 32 x 48)

โดยทั่วไปบล็อกเหล่านี้จะไม่ทำอะไรเลยในตัวเอง พวกเขานั่งอยู่ตรงนั้น

พวกเขาเริ่มใช้เมื่อมันมาถึงการมีปฏิสัมพันธ์ของผู้เล่น

ฉันต้องตรวจสอบก้อนที่ผู้ใช้โต้ตอบกับเมาส์ (เลื่อนเมาส์คลิก ฯลฯ ) และตรวจจับการชนเมื่อผู้เล่นเคลื่อนที่

ตอนนี้ฉันมีความล่าช้าอย่างมากในตอนแรกวนไปทุกช่วงตึก

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

อย่างไรก็ตามฉันยังคงอยู่ที่ 2 เฟรมต่อวินาทีที่น่าหดหู่

ใครบ้างมีความคิดอื่น ๆ เกี่ยวกับวิธีที่ฉันสามารถลดความล่าช้านี้

Btw ฉันใช้ XNA (C #) และใช่มันเป็น 3d


คุณหมายถึง Minecraft เหมือนไหม? นั่นคือ voxel?
The Duck คอมมิวนิสต์

4
คุณดูเป็นแปดสิบ en.wikipedia.org/wiki/Octree
bummzack

1
คุณได้ลองทำโปรไฟล์เกมของคุณแล้วหรือยัง? อาจแสดงบางประเด็นสำคัญที่มีการใช้เวลามากที่สุด อาจไม่ใช่สิ่งที่คุณคิด
deceleratedcaviar

2
แทนที่จะวาดทั้ง 6 ใบหน้าของแต่ละคิวบ์คุณสามารถวาดรูปใบหน้าที่ไม่ได้ติดต่อกับอะไรเลย
เดวิดแอชมอร์

1
@David: ใช่หรือเขาสามารถหยุดการเรียกเลขหมายเดียวต่อคิวบ์ก่อนแล้วค่อยกังวลกับรูปหลายเหลี่ยมแต่ละรูปในภายหลัง
Olhovsky

คำตอบ:


20

ดูเหมือนว่าคุณกำลังต้องการเรียนรู้เกี่ยวกับต้นไม้!

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

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

ตอนนี้เพื่อหาโซนนี้เราต้องแมปพื้นที่ประสานงานของผู้เล่นของเราไปยังพื้นที่ประสานงานของแผนที่ลูกบาศก์ นั่นคือเราจำเป็นต้องแมปตำแหน่งจุดลอยตัวของผู้เล่นไปยังดัชนีแยกของอาร์เรย์หลายมิติของคิวบ์ (สัญกรณ์ตัวอย่างอาจจะเป็นworld[31][31][31]เช่นตรงกลางที่แน่นอนสำหรับอาร์เรย์ 64 มิติหลายมิติ 64 * 64 * 64)

เราสามารถคำนวณบล็อกโดยรอบโดยใช้การจัดทำดัชนีแบบแยกกันแบบนี้อาจสุ่มตัวอย่างเฉพาะลูกบาศก์ที่อยู่ใกล้เคียง แต่ก็ยังต้องการการคำนวณซ้ำอย่างต่อเนื่องและไม่อนุญาตสำหรับวัตถุใด ๆ ที่ไม่ต่อเนื่องในตำแหน่ง แผนที่).

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

คำถามคือ: เราจะใช้สิ่งนี้ได้อย่างไร

สำหรับ 64 * 64 * 64 ของโลกคิดว่ามันเสียลงถึง 8 * 8 * 8 โซน ซึ่งหมายความว่าในโลกของคุณคุณจะมี 8 โซนต่อแกน (X, Y, Z) แต่ละโซนเหล่านี้จะมี 8 คิวบ์สามารถเรียกคืนได้ง่ายโดยดัชนีใหม่ที่ง่ายนี้

หากคุณต้องการดำเนินการกับชุดของลูกบาศก์ใกล้เคียงแทนการวนซ้ำทุกคิวบ์ในโลกของคุณคุณสามารถวนซ้ำโซนเหล่านี้ทำลายจำนวนการทำซ้ำสูงสุดจากเดิม 64 * 64 * 64 (262144) เป็น เพียง 520 (8 * 8 * 8 + 8)

ตอนนี้ซูมออกจากโลกของโซนนี้และวางโซนออกเป็นขนาดใหญ่สุดโซน ; ประเด็นแต่ละซุปเปอร์โซนมี 2 * 2 * 2 ประจำโซน ในขณะที่โลกของคุณในปัจจุบันมี 512 (8 * 8 * 8) โซนเราสามารถแบ่ง 8 * 8 * 8 โซนออกเป็น 64 (4 * 4 * 4) ซุปเปอร์โซนโดยการหาร 8 โซน 2 โซนต่อซุปเปอร์โซน ใช้ตรรกะเดียวกันจากข้างต้นนี้จะทำลายซ้ำสูงสุด 512-8 เพื่อหาสิ่งที่ซุปเปอร์โซน ; และสูงสุด 64 เพื่อค้นหาเขตดำเนินการ(รวมสูงสุด 72)! คุณสามารถดูว่านี่ช่วยให้คุณประหยัดได้มากซ้ำแล้วซ้ำอีก (262144: 72)

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

แผนภาพด้านล่างจะช่วยให้คุณเห็นภาพแนวคิด (ภาพจากWikipedia: Octrees ): octree

Disclaimer:

ในการตั้งค่าที่เหมาะสมดังที่ voxel-world วางไว้ในอาร์เรย์หลายมิติที่มีขนาดคงที่คุณสามารถสอบถามตำแหน่งของผู้เล่นได้จากนั้นทำดัชนีบล็อกรอบ ๆ ด้วยค่าใช้จ่าย O (1)! (ดูคำอธิบาย Olhovskys) แต่สิ่งนี้จะยากขึ้นเมื่อคุณเริ่มพิจารณาว่าโลกของคุณมีขนาดคงที่ในเกม voxel; และคุณอาจต้องการโครงสร้างข้อมูลของคุณเพื่อให้สามารถโหลดซุปเปอร์โซนทั้งหมดจาก HDD ไปยังหน่วยความจำ ไม่เหมือนกับอาเรย์หลายมิติที่มีขนาดคงที่ต้นไม้สามารถให้สิ่งนี้ได้โดยไม่ต้องใช้เวลามากเกินไปในอัลกอริธึมผสม


ฉันจะบอกว่าฉันได้รับมัน แต่น่าเสียดายที่ฉันทำไม่ได้ กล่องการชนของบล็อกไม่ได้เคลื่อนไหวเพียงแค่กล่องการชนของผู้เล่นเท่านั้น และฉันมีวิธีตอนนี้ที่ (โดยไม่วนลูปผ่านบล็อคทั้งหมด) ส่งคืนบล็อกทั้งหมดที่อยู่ในรัศมี 5 บล็อกของผู้เล่น ขออภัยสำหรับความยุ่งยาก แต่ชี้แจงใด ๆ ? โดยวิธีการที่จะทำให้มันง่ายขึ้นคุณสามารถคิดว่าโลกคือ 64 x 64 x 64?
Joel

และฉันยังคงได้รับประมาณ 5fps :(
Joel

ฉันถามคำตอบของฉันอีกครั้งแจ้งให้เราทราบหากมันช่วยได้
deceleratedcaviar

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

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

13

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

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

มีหลายวิธีในการโปรไฟล์รหัสของคุณคุณสามารถม้วนระดับการวิเคราะห์ประสิทธิภาพของคุณเอง (ซึ่งอาจใช้ประโยชน์จากระดับนาฬิกาจับเวลา (MSDN) ) หรือคุณสามารถใช้ PIX เพื่อรับทราบแนวคิดทั่วไปว่า CPU / GPU ยุ่งแค่ไหน .

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


2
+1 เห็นด้วยทั้งหมด คุณไม่ควรมองเข้าไปในอัลกอริทึมคุณควรดูที่การทำโปรไฟล์โค้ดของคุณ ฉันเดาว่ามันไม่เกี่ยวอะไรกับความจริงที่ว่าคุณวนซ้ำผ่านบล็อก (มันไม่มาก) มันเป็นสิ่งที่คุณทำกับแต่ละบล็อค ท้ายที่สุดคุณจำเป็นต้องมีวิธีที่ดีกว่ากำลังดุร้าย แต่ถ้าคุณไม่สามารถจัดการการวนซ้ำอย่างง่ายบนก้อนขนาด 48x32x48 คุณต้องคิดใหม่ว่าคุณกำลังทำอะไรกับลูกบาศก์แต่ละก้อนไม่ใช่ว่าคุณกำลังวนซ้ำ
Tim Holt

4
@Tim: หากผู้เล่นของเขามีขนาดใหญ่พอที่จะใช้พื้นที่ 48x32x48 จากนั้นเขาไม่ควรวนซ้ำทุกที่ใกล้กับก้อนจำนวนมาก หากเขาทำซ้ำผ่าน 73,000 ลูกบาศก์ต่อเฟรมฉันสามารถบอกคุณได้โดยไม่ต้องทำโปรไฟล์ใด ๆ ว่ามันคุ้มค่าสำหรับเขาที่จะแก้ไขปัญหานั้นหากไม่มีเหตุผลอื่นนอกจากเรียนรู้วิธีหลีกเลี่ยงการทำซ้ำซ้ำนับหมื่นกว่า มีความจำเป็น นี่ไม่ใช่สิ่งที่ฉันจะเรียกการเพิ่มประสิทธิภาพขนาดเล็กหรือก่อนวัยอันควร
Olhovsky

ผู้เล่นของฉันมีขนาดน้อยกว่า 1 คิวบ์ แต่อาจมีขนาดใหญ่กว่าในบางช่วง (แต่ไม่มาก)
Joel

Randomman159: จากนั้นคุณเพียงแค่ทดสอบกับ 27 ลูกบาศก์ของเขาเพื่อหาการปะทะกัน ดูคำตอบของฉัน
Olhovsky

6

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

อย่างไรก็ตามหากผู้เล่นของคุณมีขนาดเล็กเมื่อเทียบกับขนาดของก้อนนั้นอาจเป็นวิธีที่เร็วที่สุดในการตรวจสอบการชนกับก้อนคือการค้นหาเชิงเส้นอย่างง่ายของพื้นที่รอบผู้เล่น

เนื่องจากผู้เล่นของคุณมีขนาดเล็กกว่า 1 ลูกบาศก์ดังนั้นคุณจะต้องทดสอบการชนกับ 27 ก้อนที่อยู่ใกล้เคียงเท่านั้น

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

ดังที่คนอื่น ๆ ชี้ให้เห็นคุณต้องทำการโปรไฟล์รหัสของคุณเพื่อดูว่าอะไรที่ทำให้คุณช้าลงจริง ๆ

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


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

โดยส่วนตัวแล้วฉันไม่ต้องการพึ่งพา broadphase ของเครื่องมือฟิสิกส์เพื่อทดสอบกับ 73,000 cubes สำหรับฉันเมื่อฉันสามารถเขียนโค้ดสองโหลหรือมากกว่านั้นเพื่อทดสอบการชนกันของฉันได้อย่างมีประสิทธิภาพ นอกจากนี้เขาอาจไม่มีเครื่องมือฟิสิกส์ที่จะเข้ามาในเวลานี้
Olhovsky

1

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

เห็นได้ชัดว่า Minecraft ทำอะไรบางอย่างที่คล้ายกันนี้ - ฉันตีก้อนที่ไม่โหลดอีกครั้งหนึ่งซึ่งทำให้ฉันได้เห็นโลก - ฉันสามารถมองผ่านพื้นดินแข็งทั้งหมดที่ปรากฏขึ้นเป็นพื้นที่เปิดโล่ง (ด้านไกลของ พวกเขาเป็นพื้นผิวสัมผัสและดังนั้นจึงแสดงผล)


-1

ฉันมีปัญหากับเครื่องยนต์ voxel ของฉัน

วิธีแก้ปัญหา: (ง่ายกว่า octrees มาก) แทนที่จะวนลูปผ่านบล็อกทั้งหมดเพียงแค่ใช้สมการเพื่อกำหนดตำแหน่งของบล็อกในอาร์เรย์บล็อก

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

ถ้าคุณต้องการดูว่ามีบล็อกอยู่หรือไม่:

Blocks[BlockIndex].Type > -1;

หรืออย่างไรก็ตามคุณพิจารณาว่ามีบล็อกอยู่หรือไม่


ปัญหาที่นี่มีความซับซ้อนมากขึ้นเนื่องจากคุณมีเพียง 2 พิกัดสำหรับการทดสอบเมาส์กับโลก 3 มิติ หากมุมมองจากบนลงล่างคุณสามารถค้นหาดัชนีจากตำแหน่งขวาของเมาส์ได้ 2 ใน 3 และจากนั้นวนไปตามความสูงโดยเริ่มจากด้านบน - ใกล้กับกล้องมากขึ้นและค้นหาการเกิดขึ้นครั้งแรกของบล็อก
Markus von Broady
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.