ฉันจะป้องกันไม่ให้ตัวละครของ platformer ของฉันจากการตัดบนกระเบื้องบุผนัง?


9

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

การชนกันของกระเบื้องมีการอธิบายค่อนข้างตรงตามที่อธิบายไว้ในคำตอบนี้ (ด้วย SAT แบบง่ายสำหรับสี่เหลี่ยมและวงกลม) และทุกอย่างทำงานได้ดี

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

รูปภาพของการจับตัวละคร

ในภาพหน้าจอนี้ผู้เล่นเคลื่อนไปทางขวาและตกลงมา ดังนั้นหลังจากการเคลื่อนไหวจะมีการตรวจสอบการชน - และก่อนอื่นมันจะเปิดออกตัวละครของผู้เล่นจะชนกับไทล์ที่ 3 จากพื้นและผลักขึ้นไป ประการที่สองเขาพบว่าชนกับกระเบื้องข้างเขาและผลักไปด้านข้าง - ผลลัพธ์สุดท้ายคือตัวละครของผู้เล่นคิดว่าเขาอยู่บนพื้นและไม่ล้มและ 'จับ' บนไทล์ตราบเท่าที่เขาวิ่งเข้าไป .

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

ฉันจะแก้ไขปัญหานี้อย่างไรเพื่อให้ผู้เล่นสามารถตกไปตามกำแพงได้อย่างที่ควร


คล้ายกับคำถามนี้? gamedev.stackexchange.com/questions/14486/…
MichaelHouse

@ Byte56 คำถามไม่เหมือนกัน แต่คำตอบอาจช่วยได้
doppelgreener

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

คำตอบ:


8

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

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

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

ฉันแนะนำให้คุณอ่านบทความด้านล่าง พวกเขากำลังแนะนำง่ายๆเกี่ยวกับการทำฟิสิกส์พื้นฐานและการตรวจจับการชนในแพลตฟอร์มที่ทันสมัย ​​(เช่น Cave Story) หากคุณอยากสัมผัสกับมาริโอวัยชรามากขึ้นมีเคล็ดลับอีกสองสามข้อที่ต้องกังวล แต่ส่วนใหญ่เกี่ยวข้องกับการกระโดดและแอนิเมชั่นแทนที่จะปะทะกัน

http://www.metanetsoftware.com/technique/tutorialA.html http://www.metanetsoftware.com/technique/tutorialB.html


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

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

5

นี่คือคำสั่งฉันจะทำสิ่งต่าง ๆ ....

1) บันทึก X, Y ของอักขระปัจจุบัน

2) เลื่อนทิศทาง X

3) ตรวจสอบทุกมุมของตัวละครโดยรับข้อมูลไทล์และตรวจสอบว่าเป็นของแข็งหรือไม่โดยทำดังต่อไปนี้: (X และ Y เป็นตำแหน่งตัวอักษร)

  • สำหรับซ้ายบน: ชั้น (X / tileWidth), ชั้น (Y / tileHeight);
  • สำหรับด้านบนขวา: ชั้น ((X + อักขระกว้าง) / tileWidth), ชั้น (Y / tileHeight);
  • สำหรับด้านล่างซ้าย: ชั้น (X + / tileWidth), ชั้น ((Y + characterHeight) / tileHeight);
  • สำหรับด้านล่างขวา: floor ((X + characterWidth) / tileWidth), floor ((Y + characterHeight) / tileHeight);

... เพื่อรับข้อมูลไทล์ที่ถูกต้องจากข้อมูลแผนที่

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

  • หากคุณย้ายไปทางซ้าย: X = floor (X / tileWidth) * tileWidth;
  • หากคุณย้ายไปทางขวา: X = ((floor ((X + characterWidth) / tileWidth) + 1) * tileWidth) - characterWidth;

5) ทำซ้ำ 2-4 โดยใช้ Y

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

int characterBlockHeight = 3;

// check all tiles and intermediate tiles down the character body
for (int y = 0; y < characterBlockHeight - 1; y++) {
    tile = getTile(floor(characterX / tileWidth), floor((characterY + (y * tileHeight)) / tileHeight));
    ... check tile OK....
}

// finally check bottom
tile = getTile(floor(characterX / tileWidth), floor((characterY + characterHeight) / tileHeight);
... check tile OK....

หากตัวละครมีความกว้างมากกว่า 1 บล็อกให้ทำเช่นเดียวกัน แต่แทนที่ characterY, characterHeight, tileHeight และอื่น ๆ ด้วย characterX, characterWidth และอื่น ๆ


2

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


1

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

ในตัวอย่างของคุณพื้นที่ของการชนกันของแกน x จะมากกว่าแกน y ดังนั้นมันจะถูกจัดการก่อน จากนั้นก่อนที่จะพยายามจัดการกับการชนกันของแกน y มันจะตรวจสอบและดูว่าไม่มีการชนกันอีกต่อไปหลังจากที่ถูกย้ายบนแกน x :)


1

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


1

ปัญหาที่คล้ายกันมากถูกปกคลุมในการกวดวิชานี้: รู้เบื้องต้นเกี่ยวกับการพัฒนาเกม ส่วนที่เกี่ยวข้องของวิดีโออยู่ที่1:44อธิบายด้วยแผนภาพและตัวอย่างโค้ด

ประกาศ14-tilemap.pyแสดงปัญหาการตัด แต่ได้รับการแก้ไขใน15-blocker-sides.py

ฉันไม่สามารถอธิบายได้อย่างชัดเจนว่าทำไมตัวอย่างบทช่วยสอนสุดท้ายไม่คลิปขณะที่โค้ดของคุณทำ แต่ฉันคิดว่ามันเกี่ยวข้องกับ:

  • การตอบสนองการชนกันแบบมีเงื่อนไขขึ้นอยู่กับประเภทของกระเบื้อง (ซึ่งขอบใช้งานจริง)
  • ผู้เล่นมักจะ 'ล้ม' แม้ว่าดูเหมือนว่าผู้เล่นจะวางอยู่บนแผ่นกระเบื้องผู้เล่นกำลังล้มลงบนแผ่นกระเบื้องซ้ำ ๆ จากนั้นก็ถูกผลักกลับไปด้านบน ( self.restingสมาชิกในรหัสนั้นใช้เพื่อตรวจสอบว่าผู้เล่นสามารถกระโดดได้หรือไม่อย่างไรก็ตามฉันไม่สามารถให้ผู้เล่นกระโดดข้ามกำแพงได้สองเท่าในทางทฤษฎีอาจเป็นไปได้)

0

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

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


0

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

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