การคาดการณ์แบ่งการตรวจจับการชนกัน


10

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

นี่คือสิ่งที่ทำงานก่อนการคาดการณ์:

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

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

หลังจากที่ฉันใช้การคาดการณ์ของฉัน

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

วิธีแก้ไขพฤติกรรมนี้

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

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

ฉันพบคำถามที่คล้ายกันสองสามข้อที่นี่ แต่คำตอบไม่ได้ช่วยฉัน

นี่คือรหัสการคาดการณ์ของฉัน:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

การใช้การประมาณค่า

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

แก้ไข

ดังที่ฉันได้กล่าวไว้ข้างต้นฉันได้ลองทำสำเนาพิกัดของเทพดาโดยเฉพาะเพื่อวาดด้วย .... นี่เป็นปัญหาของตัวเอง

ประการแรกไม่ว่าการคัดลอกเมื่อสไปรต์เคลื่อนที่ไปทางไหนมันจะลื่นไหลสุด ๆ เมื่อมันหยุดมันจะโยกไปทางซ้าย / ขวาเล็กน้อย นี่เป็นพฤติกรรมปกติและเราสามารถ 'ปิด' เมื่อผีสางหยุดหรือไม่

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

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


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

สวัสดี @StevenStadnicki หลายขอบคุณสำหรับความคิดเห็นของคุณมีความหลากหลายของตัวอย่างที่แสดงค่าการแก้ไขที่ถูกส่งเข้ามาในโหมดแสดงภาพโปรดดูที่: mysecretroom.com/www/programming-and-software/...ซึ่งอยู่ห่างจากที่ผมดัดแปลงของฉัน รหัส. ความเข้าใจที่ จำกัด ของฉันคือสิ่งนี้จะสอดแทรกตำแหน่งระหว่างการอัปเดตครั้งสุดท้ายและครั้งต่อไปตามจำนวนเวลาที่ดำเนินการตั้งแต่การอัปเดตครั้งล่าสุดฉันยอมรับว่ามันเป็นฝันร้ายสักหน่อย! ฉันจะขอบคุณถ้าคุณสามารถเสนอและเป็นทางเลือกที่จะง่ายต่อการทำงานของฉัน - ไชโย :-)
BungleBonce

6
ฉันจะบอกว่า 'เพียงเพราะคุณสามารถหารหัสสำหรับบางสิ่งบางอย่างไม่ได้ทำให้เป็นแนวปฏิบัติที่ดีที่สุด' :-) ในกรณีนี้ฉันสงสัยว่าหน้าเว็บที่คุณเชื่อมโยงไปนั้นจะใช้ค่าการแก้ไขเพื่อหาตำแหน่งที่จะแสดงวัตถุของมัน แต่จริง ๆ แล้วมันไม่ได้อัพเดทตำแหน่งของวัตถุด้วย คุณสามารถทำได้เช่นกันเพียงแค่มีตำแหน่งเฉพาะการวาดที่คำนวณทุกเฟรม แต่ให้แยกตำแหน่งนั้นออกจากตำแหน่ง 'กายภาพ' จริงของวัตถุ
Steven Stadnicki

สวัสดี @StevenStadnicki ตามที่ระบุไว้ในคำถามของฉัน (ย่อหน้าที่เริ่มต้น "ฉันได้ลองทำสำเนา") ฉันได้พยายามใช้ตำแหน่ง 'ดึงเท่านั้น' :-) คุณสามารถเสนอวิธีการแก้ไขที่ฉัน ไม่จำเป็นต้องทำการปรับตำแหน่งของสไปรต์ภายในรูทีนการเรนเดอร์? ขอบคุณ!
BungleBonce

ดูรหัสของคุณดูเหมือนว่าคุณกำลังคาดการณ์แทนการแก้ไข
Durza007

คำตอบ:


1

ฉันยังไม่สามารถแสดงความคิดเห็นได้ดังนั้นฉันจะโพสต์สิ่งนี้เป็นคำตอบ

ถ้าฉันเข้าใจปัญหาอย่างถูกต้องมันจะเป็นดังนี้:

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

ฉันสามารถคิดถึงวิธีแก้ปัญหา 3 ข้อที่เป็นไปได้ ฉันจะแสดงรายการเหล่านี้ตามลำดับที่ต้องการมากที่สุดไปยัง IMHO น้อยที่สุด

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

รหัสตัวอย่างสำหรับ # 2

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

ฉันคิดว่า # 2 อาจเป็นวิธีที่เร็วและง่ายที่สุดในการเริ่มต้นใช้งานแม้ว่า # 1 ดูเหมือนจะเป็นวิธีที่แม่นยำกว่า ทั้งนี้ขึ้นอยู่กับวิธีที่คุณจัดการกับโซลูชันเวลาเดลต้าของคุณ # 1 อาจแตกในลักษณะเดียวกันโดยเดลต้าขนาดใหญ่ซึ่งในกรณีนี้คุณอาจต้องใช้ทั้ง # 1 และ # 2 ร่วมกัน

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

ที่ถูกกล่าวว่าปัญหาเดียวคือวัตถุที่เคลื่อนไหวหลังจากการปะทะกัน หากมีการชนกันวัตถุควรหยุดเคลื่อนที่ในทิศทางนั้น ดังนั้นหากมีการชนกันให้ตั้งค่าความเร็วเป็น 0 ซึ่งควรหยุดฟังก์ชั่นการเรนเดอร์ไม่ให้ย้ายวัตถุใด ๆ เพิ่มเติม


สวัสดี @Aholio ฉันลองตัวเลือก 2 แล้วมันใช้งานได้ แต่มีข้อผิดพลาดเล็กน้อยบางทีฉันทำผิดฉันจะกลับมาดูอีกครั้ง อย่างไรก็ตามฉันมีความสนใจในตัวเลือก 1 ถึงแม้ว่าฉันจะไม่สามารถหาข้อมูลเกี่ยวกับวิธีการทำสิ่งนี้ได้ ฉันจะคาดการณ์ว่าจะพูดในรูทีนตรรกะของฉันได้อย่างไร BTW เดลต้าของฉันได้รับการแก้ไขดังนั้นจึงเป็น 1/60 หรือ 0.01667 (นี่คือตัวเลขที่ฉันใช้เพื่อรวมถึงแม้ว่าระยะเวลาในการทำซ้ำแต่ละครั้งในเกมของฉันจะแตกต่างกันอย่างชัดเจนขึ้นอยู่กับสิ่งที่เกิดขึ้น แต่ไม่ควรใช้เวลามากกว่า 0.01667 ) ดังนั้นความช่วยเหลือใด ๆ ที่จะขอบคุณมาก
BungleBonce

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

Hmmmmm ในปัจจุบันฟังก์ชั่นอัพเดทของฉันทำทุกอย่าง การเคลื่อนไหว (โดยการเคลื่อนไหวฉันหมายถึงคำนวณตำแหน่งใหม่ของสไปรต์ - ซึ่งคำนวณจากเวลาเดลต้าของฉัน) สิ่งเดียวที่ฉันทำในฟังก์ชั่นการเรนเดอร์ของฉัน (นอกเหนือจากการวาดภาพจริง) คือการคาดการณ์ตำแหน่ง 'ใหม่' แต่แน่นอนว่านี่ใช้เวลาเพราะต้องคำนึงถึงเวลาตั้งแต่การอัพเดทลอจิกครั้งสุดท้าย นั่นคือความเข้าใจของฉันต่อไป :-) คุณจะทำอย่างไร ฉันไม่เข้าใจวิธีใช้การคาดการณ์ภายในการอัพเดตเชิงตรรกะ ขอบคุณ!
BungleBonce

อัปเดตคำตอบของฉัน หวังว่าจะช่วยให้
Aholio

ขอบคุณดูใน Q ดั้งเดิมของฉัน "เมื่อมันหยุดมันสั่นไปซ้าย / ขวาเล็กน้อย" - ดังนั้นแม้ว่าฉันจะหยุดสไปรต์ของฉันการคาดการณ์ยังคงทำให้สไปรต์ 'เคลื่อนที่' (การส่าย) - เกิดขึ้นเพราะฉันไม่ได้ใช้ ตำแหน่งเก่าและปัจจุบันของสไปรต์เพื่อหาค่าคาดการณ์ของฉันดังนั้นแม้ว่าสไปรท์จะเป็นแบบคงที่ แต่ก็ยังคงมีผลกระทบที่คลาดเคลื่อนนี้ตามค่า 'การคาดการณ์' ซึ่งทำงานได้ในแต่ละเฟรมซ้ำ ฉันเคยลองพูดว่า 'ถ้า POS เก่าและปัจจุบันเหมือนกันอย่าคาดเดา' แต่นี่ก็ดูเหมือนจะไม่ทำงานฉันจะกลับไปดูอีกครั้ง!
BungleBonce

0

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

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

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

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

นี่คือรหัส psuedo สำคัญจากบทความ:

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

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

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