การแยกโลจิคัล / อัพเดตจากโค้ดการแสดงผล / การวาดในเธรดเดี่ยวโดยใช้โหมดสลีป


9

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

รหัสหลอกปัจจุบันของฉันเป็นดังนี้

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

ปัญหาคือรหัสการวาดภาพเป็นอุปสรรคต่อประสิทธิภาพของอัตราการอัปเดต () และมันใช้ cpu 100% เพราะถ้าการนอนหลับถูกโยนทิ้งมันจะสลัดฟังก์ชันการวาดภาพ / ลอจิกออก

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


1
คุณไม่จำเป็นต้องสิ้นเปลืองพลังงาน CPU 100% เพียงแค่รอให้สลีป (0) ที่ส่วนท้ายของลูปถ้า ticksElapsed () <100 ระบบปฏิบัติการจะกลับไปที่เธรดทันทีหากไม่มีเธรดอื่นที่ ต้องการเรียกใช้ แต่ไม่ต้องสิ้นเปลืองพลังงาน CPU 100% อีกต่อไป
Maik Semder

อย่างไรก็ตามทางออกที่ดีที่สุดสำหรับการตั้งค่า 1 เธรดคือการใช้ vsync หากคุณไม่สามารถ vsync แล้วโทร sleep (0) ในลูปจนกว่าคุณจะถึงอัตราเฟรมเป้าหมายแล้วอัปเดตและวาด
Maik Semder

คำตอบ:


3

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

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

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

โดยทั่วไปคุณไปจาก:

void UpdatePlayer()
 player.x += 10;

ถึง

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;

ดังนั้นเครื่องยนต์ของฉันจะใช้งาน 100% เสมอและไม่มีอะไรที่ฉันสามารถทำได้จริง ๆ
Oskenso Kashi

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

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

@ Maik Semder: คุณมีวิธีแก้ปัญหาการนอนหลับ (x) ไม่ถูกต้องหรือไม่? หลังจากผ่านช่วงเวลาสลีปแล้วเธรดก็พร้อมที่จะทำงาน แต่เธรดที่พร้อมจะไม่รับประกันว่าจะรันทันที ขึ้นอยู่กับตัวกำหนดตารางเวลา เมื่อคุณใช้สองเธรดมีวิธีแก้ไขปัญหาอื่น ๆ เพื่อดูบทความที่ยอดเยี่ยมนี้: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.

1
@Roy sleep (0) เป็นทางออก มันจะส่งคืนทันทีหากไม่มีเธรดอื่นที่ต้องการรัน ( Sleep WinAPI ) และให้โอกาสเธรดอื่นรัน หากเธรดอื่นไม่ให้โอกาสในการรันเธรดหลักคุณก็มีปัญหาเกลียว แต่การบล็อกทุกอย่างอื่นโดยไม่เรียก sleep ในตอนแรกทำให้แย่ลงและแทบจะไม่ได้ทางออกเลย กุญแจสำคัญคือการโทรหลับ (0) และทดสอบเวลาที่ผ่านไปจนกว่าคุณจะถึงอัตราเฟรมคงที่เป้าหมายของคุณดังนั้นคุณไม่ต้องเสีย CPU 100% เพียงแค่รอ
Maik Semder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.