วิธีการคำนวณพื้นที่การมองเห็นในเกมแบบสองมิติได้อย่างรวดเร็ว


24

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

ฉันพยายามทำด้วยอัลกอริทึมของ Bresenhamแต่มันช้า นอกจากนี้ยังทำให้ฉันมีข้อผิดพลาด:

----XXX-        ----X**-     ----XXX-
-@------        -@------     -@------
----XXX-        ----X**-     ----XXX-
(raw version)   (Besenham)   (correct, since tunnel walls are 
                              still visible at distance)

(@ is the player, X is obstacle, * is invisible, - is visible)

ฉันแน่ใจว่าสิ่งนี้สามารถทำได้ - หลังจากทั้งหมดเรามี NetHack, Zangband และพวกเขาทั้งหมดจัดการกับปัญหานี้อย่างใด :)

คุณสามารถแนะนำอัลกอริทึมสำหรับสิ่งนี้ได้อย่างไร


สำหรับความต้องการของฉันฉันจะกำหนดให้มองเห็นได้เช่นนี้: ไทล์สามารถมองเห็นได้เมื่ออย่างน้อยส่วนหนึ่ง (เช่นมุม) ของไทล์สามารถเชื่อมต่อกับศูนย์กลางของไทล์เพลเยอร์ด้วยเส้นตรงที่ไม่ตัดสิ่งกีดขวางใด ๆ


1
อ๊ะผิดพลาดของฉัน, NetHack ไม่ได้ล้อเล่นกับสายของสายตา :)
Rogach

คุณสามารถหาแนวคิดที่เก่ากว่านี้ได้ที่fadden.com/tech/fast-los.htmlแม้ว่าจะย้อนกลับไปเมื่อสมัยที่ซีพียูค่อนข้างช้าและการคำนวณจุดลอยตัวเป็นสิ่งที่หลีกเลี่ยงได้ดีที่สุด
ทำให้อับอาย

คำตอบ:


10

คำจำกัดความที่มองเห็นได้ของคุณมีดังต่อไปนี้:

ไทล์สามารถมองเห็นได้เมื่ออย่างน้อยส่วนหนึ่ง (เช่นมุม) ของไทล์สามารถเชื่อมต่อกับกึ่งกลางของเพลเยอร์ด้วยเส้นตรงที่ไม่ตัดสิ่งกีดขวางใด ๆ

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

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

นี่คือภาพแสดงตัวอย่างรังสี 3 ตัวอย่าง แผ่นสีที่เข้มกว่านั้นคือ "ผลลัพธ์" ของรังสีแต่ละชนิดคือตำแหน่งที่เกิดการชน คุณจะต้องทำซ้ำสิ่งนี้รอบ ๆ วงกลม:

ป้อนคำอธิบายรูปภาพที่นี่

ปรับแต่งระยะทางสูงสุดและจำนวนรังสีเพื่อประสิทธิภาพ น้อยเกินไปและคุณจะพลาดกระเบื้องมากเกินไปและประสิทธิภาพการทำงานของคุณจะประสบ ยิ่งไปกว่านั้นรังสีที่ต้องเดินทางไกลยิ่งมี "ข้อผิดพลาด" มากเท่านั้นและคุณจะต้องแม่นยำยิ่งขึ้น

แก้ไข

ตรวจสอบบทช่วยสอนต่อไปนี้เกี่ยวกับ raycasting โดยเฉพาะอย่างยิ่งขั้นตอนที่ 3 และขั้นตอนที่ 4 เพื่อช่วยให้คุณใช้จุดตัดของอัลกอริทึม:

http://www.permadi.com/tutorial/raycast/rayc7.html


ฉันควร "เดิน" ไปตามแต่ละรังสีด้วยระยะทางคงที่ (พูด, 0.3 คะแนน) หรือฉันต้องใช้บางอย่างเช่นอัลกอริทึมของ Besenham ในแต่ละรังสี?
Rogach

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

1
อัลกอรึทึมนั้นดี แต่มันจะต้องใช้รังสีจำนวนมากในการทำงานกับอุโมงค์ที่มีความยาว 1 ไทล์
HolyBlackCat

@HolyBlackCat - มันจะเป็นอย่างนั้นก็ต่อเมื่อคุณส่งรังสีออกมาที่มุมแม้ในทุกทิศทาง แต่คุณสามารถหลีกเลี่ยงการส่งรังสีเหล่านี้ส่วนใหญ่และโยนพวกเขาที่บรรทัดสิ้นสุดในฉากของคุณ นี่คือคำอธิบายที่ดี: redblobgames.com/articles/visibility
Rogach

8

ฉันอยากโยนรังสีเงาแทนที่จะเป็นแนวสายตา

สมมติว่านี่เป็นพื้นที่ดูของคุณ (พื้นที่ที่มองเห็นได้)

######################
#####.............####
###................###
##..................##
#....................#
#....................#
#..........@.........#
#....................#
#....................#
##..................##
###................###
#####.............####
######################

ไม่สามารถมองเห็น # blocks ในขณะที่ มองเห็นได้

ลองใส่อุปสรรค X:

######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXXX...........#
##..................##
###....X...........###
#####.............####
######################

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

######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXX*...........#
##......##..........##
###....*#..........###
#####.###.........####
######################

ในตัวอย่างด้านบนคุณสามารถเห็นเงาที่ถูกหล่อทางด้านขวาสุดของผนังด้านล่างและวิธีที่เงานี้ลบสิ่งกีดขวางที่ซ่อนอยู่ออกจากรายการของสิ่งกีดขวางที่คุณต้องตรวจสอบ (X ต้องตรวจสอบ; * ถูกตรวจสอบ)

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

คุณอาจใช้อัลกอริทึม "การรบทางเรือ" เพื่อตรวจสอบบล็อกของ X ในคราวเดียว (โดยทั่วไปมองหา adiacent X ที่อยู่ในทิศทางที่ทำให้กรวยเงากว้างขึ้น)

[แก้ไข]

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

พิกัดของรังสีสามารถคำนวณได้โดยใช้การแบ่งช่องว่างอย่างง่าย ๆ รอบแผ่นสิ่งกีดขวาง:

ตัวอย่างการแบ่งพื้นที่

แต่ละพื้นที่สี่เหลี่ยมประกอบด้วยตัวเลือกเกี่ยวกับมุมของกระเบื้องที่ควรเป็นขอบรูปกรวยเงา

เหตุผลนี้สามารถผลักดันต่อไปเพื่อเชื่อมต่อแผ่นกระเบื้องหลายแผ่นที่อยู่ติดกัน

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

เลือกอุปสรรคที่ใกล้ที่สุด

ถ้าแผ่นสีเหลืองเป็นสิ่งกีดขวางที่แผ่นนั้นกลายเป็นแผ่นสีแดงใหม่

ตอนนี้ให้พิจารณาขอบกรวยด้านบน:

กระเบื้องของผู้สมัคร

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

ไทล์สีเขียวเป็นตัวเลือกเฉพาะเมื่อผู้สังเกตการณ์อยู่เหนือเส้นสีส้มที่ตามมา:

ตรวจสอบขยาย

สิ่งเดียวกันหมายถึงรังสีอื่นและตำแหน่งอื่นของผู้สังเกตการณ์เกี่ยวกับสิ่งกีดขวางสีแดง

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


วิธีการที่น่าสนใจและอาจเป็นความคิดที่ดีกว่าเพราะธรรมชาติที่หักเห หลังจากอ่านสิ่งนี้ฉันก็อาจใช้มันด้วยวิธีนี้เช่นกัน
David Gouveia

ฉันสามารถล่วงรู้ปัญหาในสถานการณ์เช่นนี้ ผู้เล่นในสีเหลืองอุปสรรคในสีน้ำเงินและสีม่วง ผู้เล่นควรจะเห็นสิ่งกีดขวางสีม่วง (ดังแสดงสีเขียว) แต่รังสีเงาสีแดงที่ขวางผ่านสิ่งกีดขวางสีน้ำเงินก็ปฏิเสธกระเบื้องสีม่วง แต่ฉันเดาว่าเวอร์ชั่นของสายตามีแนวโน้มที่จะมีปัญหาใหญ่กว่านี้
David Gouveia

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

@DavidGouveia - ปัญหาที่ใหญ่กว่าคืออะไร?
Rogach

@DavidGouveia - ฉันลองใช้วิธี "เงา" แบบเงาแล้วและมันก็ไม่มีประสิทธิภาพมาก สำหรับความแม่นยำของรังสีทัศนวิสัย - ประมาณ 5500 รังสีนั้นเพียงพอที่จะเห็นแผ่นผนัง 20 แผ่นในแต่ละทิศทางหากคุณยืนอยู่ใกล้ ๆ และระยะทางที่มองเห็นกระเบื้องเพียงแผ่นเดียวจะมองเห็นได้มากกว่า และสำหรับกระเบื้องบางอันที่หายไปในระยะที่ไกลกว่านี้ - ไม่ใช่ทุกคนที่มีสายตาที่ดีใช่มั้ย
Rogach

8

ปัญหาที่คุณพยายามแก้ไขบางครั้งเรียกว่า Field of View, FOV โดยย่อ ดังที่คุณพูดถึง roguelikes เป็นตัวอย่างคุณควรตรวจสอบสิ่งที่วิกิ RogueBasin พูดเกี่ยวกับเรื่องนี้ (มีลิงค์ไปสู่การใช้งาน): http://www.roguebasin.com/index.php?title=Field_of_Vision

มีอัลกอริทึมที่แตกต่างกันสองสามข้อที่มีข้อดีและข้อเสียแตกต่างกันไป - การเปรียบเทียบที่สะดวกมากมีให้ที่ RogueBasin: http://www.roguebasin.com/index.php?title=Comparative_study_of_field_of_view_algorithms_for_2D_grid_based_worlds


สรุปดีและสมบูรณ์จริงๆ!
Rogach

เว็บไซต์นั้นเป็นแหล่งข้อมูลที่ดีขอบคุณสำหรับการแบ่งปันลิงก์นั้น นอกจากนี้ยังมีคำอธิบายที่เข้าใจได้อย่างน่าอัศจรรย์ว่า A * pathfinding ทำงานอย่างไร :-)
uliwitness

เชื่อมโยงคำตอบตอนนี้ไปที่หน้าแรกของเว็บไซต์ - roguebasin.com/index.php?title=Category:FOVดูเหมือนจะเป็นการจับคู่ที่เหมาะสม
ทำให้โกรธ


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