กรอบการเคลื่อนไหวอิสระ


11

ฉันได้อ่านการเคลื่อนไหวอีกสองเธรดที่นี่: การเคลื่อนไหวตามเวลา Vs การเคลื่อนไหวตามอัตราเฟรมหรือไม่ และ เมื่อใดที่ฉันควรใช้ขั้นตอนเวลาที่แน่นอนหรือแปรผัน

แต่ฉันคิดว่าฉันขาดความเข้าใจพื้นฐานของการเคลื่อนไหวแบบอิสระของเฟรมเพราะฉันไม่เข้าใจว่าหัวข้อเหล่านี้กำลังพูดถึงอะไร

ฉันติดตามบทเรียน SDL ของ lazyfoo และเข้าสู่บทเรียนอิสระของเฟรม http://lazyfoo.net/SDL_tutorials/lesson32/index.php

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

นั่นถูกต้องใช่ไหม? บทเรียนบอกว่า:

"ความเร็วเป็นพิกเซลต่อวินาที * เวลานับตั้งแต่เฟรมสุดท้ายเป็นวินาทีดังนั้นหากโปรแกรมทำงานที่ 200 เฟรมต่อวินาที: 200 pps * 1/200 วินาที = 1 พิกเซล"

แต่ ... ฉันคิดว่าเราคูณ 200 pps ด้วย 1/1000 ของวินาที ธุรกิจนี้มีเฟรมต่อวินาทีอะไร

ฉันขอขอบคุณถ้ามีคนให้คำอธิบายรายละเอียดเพิ่มเติมเล็กน้อยกับฉันเกี่ยวกับการเคลื่อนไหวของเฟรมอิสระ

ขอขอบคุณ.

ส่วนที่เพิ่มเข้าไป:

SDL_Rect posRect;
posRect.x = 0;
posRect.y = 0;

float y, yVel;
y = 0;
yVel = 0;

Uint32 startTicks = SDL_GetTicks();

bool quit = false;
SDL_Event gEvent;

while ( quit == false )
{
    while ( SDL_PollEvent( &gEvent ) )
    {
        if ( gEvent.type == SDL_QUIT )
            quit = true;
    }

    if ( y <= 580 )
    {
        yVel += DOT_VEL;
        y += (yVel * (SDL_GetTicks() - startTicks)/1000.f);
        posRect.y = (int)y;
    }

    startTicks = SDL_GetTicks();
    SDL_BlitSurface( bg, NULL, screen, NULL );
    SDL_BlitSurface( dot, NULL, screen, &posRect );
    SDL_Flip( screen );
}

นั่นคือรหัสที่ย้ายจุดลงบนหน้าจอ ฉันคิดว่าฉันมีทุกอย่างถูกต้องแล้ว มันเคลื่อนลงมาที่หน้าจอ แต่มีเรื่องแปลก ๆ เกิดขึ้นที่ฉันไม่สามารถอธิบายได้ จุดควรจะอยู่ที่ y = 580 เมื่อมันมาถึงมากกว่าค่า y อย่างไรก็ตามทุกครั้งที่ฉันเรียกใช้โปรแกรมจุดจะจบลงในตำแหน่งที่แตกต่างกันซึ่งหมายถึงนิดหน่อยถึงมากกว่า 580 ดังนั้นจุดนั้นจึงอยู่ครึ่งทางหรือมากกว่าครึ่งทางจากหน้าจอ (จุดคือ 20 พิกเซลหน้าจอ ขนาด 800x600) หากฉันทำอะไรเช่นคลิกค้างที่แถบชื่อเรื่องของโปรแกรมแล้วปล่อยจุดจะหายไปจากหน้าจอ ทำไมมันถึงแตกต่างกันในแต่ละครั้ง? สำหรับปัญหาของแถบชื่อเรื่องฉันคิดว่าเป็นเพราะเมื่อฉันยึดแถบหัวเรื่องไว้ตัวจับเวลายังคงทำงานอยู่และเวลาที่ผ่านไปจะใหญ่ขึ้น ทำให้เกิดการเคลื่อนที่ของจุดในเฟรมถัดไป นั่นถูกต้องใช่ไหม?


การเพิ่มของคุณเป็นคำถามอื่น คุณควรทำให้เป็นคำถามที่สองแทนที่จะเพิ่มลงในคำถามที่มีอยู่ สามารถตอบได้อย่างง่ายดายแม้ว่า: เพียงแค่คำนวณการเคลื่อนไหว y เช่น yMovement = (yVel * (SDL_GetTicks() - startTicks)/1000.f);จากนั้นทำ:if(y + yMovement <= 580){ y += yMovement; } else { y = 580; }
bummzack

คำตอบ:


10

หมายเหตุ: เศษส่วนทั้งหมดหมายถึงลอย

การเคลื่อนไหวอิสระของเฟรมทำงานโดยอาศัยการเคลื่อนที่ของเวลา คุณได้รับจำนวนเวลาที่ใช้ตั้งแต่เฟรมสุดท้าย (ดังนั้นหากมี 60 เฟรมในหนึ่งวินาทีแต่ละเฟรมจะใช้เวลา 1.0 / 60.0 วินาทีหากเฟรมทั้งหมดใช้เวลาเท่ากัน) และดูว่าการเคลื่อนไหวนั้นมากแค่ไหน แปลเป็น

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

มันทำงานโดยการหาจำนวนการเคลื่อนไหวที่คุณควรทำต่อเฟรมโดยใช้จำนวนเวลาที่ผ่านไปและความเร็วที่กำหนดไว้กับหน่วยของเวลา (วินาทีหรือมิลลิวินาทีจะดีกว่า) ดังนั้นการคำนวณของคุณอาจมีลักษณะดังนี้:movementPerSecond * (timeElapsedInMilliseconds / 1000.0)

ฉันหวังว่าจะมีเหตุผลสำหรับคุณ

ฉันต้องการย้ายผู้ชาย 200 พิกเซลไปทางขวาทุกวินาที หากเฟรมปัจจุบันทำงาน 50 มิลลิวินาทีหลังจากเฟรมก่อนหน้าฉันควรเลื่อนเศษส่วนของความเร็วที่ระบุก่อนหน้านี้ (ซึ่งคือ 200 พิกเซล) ฉันควรย้ายเขาไป 50/1000 ระยะทางเพราะเวลาผ่านไปเพียง 1 / 20th (50/1000 = 1/20) ดังนั้นมันจะสมเหตุสมผลที่จะย้ายเขาเพียง 10 พิกเซล (หากเกิดขึ้น 19 เฟรมมากกว่า 50 มิลลิวินาทีจากกันแล้วจำนวนการเคลื่อนไหวทั้งหมดในวินาทีนั้นจะเท่ากับ 200 พิกเซลซึ่งเป็นจำนวนที่เราต้องการ)

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

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

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


ในตัวอย่างที่คุณให้ 50 มิลลิวินาทีเป็นเวลาสำหรับแต่ละเฟรมใช่ไหม? และนั่นคำนวณโดย 1,000 / FPS ดังนั้นการเคลื่อนไหวที่คุณต้องทำให้แต่ละเฟรมเป็นพิกเซลต่อวินาที * 50/1000
ShrimpCrackers

หืมฉันรู้ว่ามิลลิวินาทีในแต่ละช่วงเวลาอาจจะแปรเปลี่ยนใช่มั้ย บางอย่างเช่น getTicks () - startTicks จะแตกต่างกันและไม่คงที่เสมอ
ShrimpCrackers

@Omnion: หากคุณระบุระยะทางเป็น "พิกเซลต่อวินาที" คุณไม่สามารถใช้มิลลิวินาทีได้ ... ควรเป็น 1.0 / 60.0 ไม่ใช่ 1,000/60 ซึ่งจะส่งผลให้บางสิ่งแตกต่างอย่างสิ้นเชิง
bummzack

@ShrimpCrackers ใช่เวลาที่ผ่านไปจะเปลี่ยนไป ลองจินตนาการถึงพีซีรุ่นเก่าที่ไม่สามารถแสดงผลได้ 60 เฟรมต่อวินาที คุณยังต้องการให้เกมรันที่ความเร็วเดียวกัน (แต่ไม่ใช่ fps ที่เหมือนกัน) บนเครื่องดังกล่าว
bummzack

ดังนั้นในการสอน lazyfoo 1,000 ค่าเฉลี่ยใน deltaticks / 1000.f คืออะไร FPS? 1,000 มิลลิวินาทีหรือไม่ ตอนนี้ฉันสับสนเล็กน้อย ดูเหมือนว่า FPS มีความจำเป็นในการกำหนดเวลาที่ต้องใช้สำหรับแต่ละเฟรม แต่จริง ๆ แล้วมันไม่ได้คำนวณการเคลื่อนไหว
ShrimpCrackers

7

ในกรอบการเปลี่ยนแปลงรหัสของคุณสำหรับ (ตัวอย่าง) การย้ายเอนทิตีจะมีลักษณะเช่นนี้:

x = x + speedPerFrame

ถ้าคุณต้องการที่จะเป็นอิสระจากเฟรมมันอาจมีลักษณะเช่นนี้:

x = x + speedPerSecond * secondsElapsedSinceLastFrame

ขอบคุณที่ทำให้รู้สึก ฉันมีคำถามอื่นข้างต้น
ShrimpCrackers

1

เกี่ยวกับคำถามเพิ่มเติม

คะแนนของคุณจะหยุดที่ตำแหน่งที่แตกต่างกันในแต่ละครั้งเพราะคุณไม่ได้ตรวจสอบขอบเขต (y> 580) เมื่อคุณย้าย คุณหยุดอัปเดตเพิ่มเติมเมื่อผ่านมา 580

ในเฟรมสุดท้ายก่อนที่คุณจะข้าม 580 คุณอาจเริ่มจาก 579 หรือคุณอาจอยู่ที่ 570 หรือคุณอาจจะอยู่ที่ 100 คุณอาจก้าวไปข้างหน้า 1 พิกเซลหรือ 1,000 ขึ้นอยู่กับระยะเวลาที่เฟรมสุดท้ายใช้ในการดำเนินการ

เพียงแค่เปลี่ยนเงื่อนไข IF ของคุณเป็นอย่างนี้แล้วคุณก็น่าจะสบายดี

if ( y <= 580 )
{
    yVel += DOT_VEL;
    y += (yVel * (SDL_GetTicks() - startTicks)/1000.f);
    if( y > 580 )
    {
        y = 580;
    }
    posRect.y = (int)y;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.