سلاسة الحركة عند أي معدل إطارات
جزء من قسم الصوت وإحساس اللعب في رحلة Pygame على Coddy. الدرس 19 من 23.
x += (target_x - x) * 0.1 يقطع عُشر المسافة في كل إطار. عند 60 إطارًا في الثانية، يعني ذلك 60 خطوة في الثانية؛ أما الحاسوب البطيء الذي لا ينجز سوى 30 إطارًا، فينفّذ 30 خطوة، ويستغرق وصول الدائرة ضعف الوقت. اضغط على F لترى ذلك: تعمل اللعبة بمعدل 30 إطارًا في الثانية، وتتحرك الأشياء كلها بسلاسة أبطأ.
الحل هو قياس الوقت. تعيد clock.tick() عدد المللي ثانية منذ الإطار السابق. وعند القسمة على 1000، نحصل على dt، أي مدة الإطار بالثواني: نحو 0.017 عند 60 إطارًا في الثانية، و0.033 عند 30.
dt = 1 / 60 # قبل الحلقة
...
step = min(1, 6 * dt)
x += (target_x - x) * step
...
dt = clock.tick(fps) / 1000يقطع الإطار الأطول خطوة أكبر، لذا تقطع الدائرة المسافة نفسها في الوقت نفسه مهما كان معدل الإطارات. يحدد الرقم 6 السرعة: فعند 60 إطارًا في الثانية، تكون 6 * dt نحو 0.1، أي العُشر نفسه كما في السابق. وتمنع min(1, ...) الإطار الطويل جدًا من تجاوز الهدف بخطوة.
وينطبق الأمر نفسه على الحركة العادية: player_x += 240 * dt تعني 240 بكسلًا في الثانية مهما كان معدل الإطارات. تثبّت معظم الألعاب الصغيرة معدلها باستخدام tick(60) ولا تحتاج إلى ذلك مطلقًا؛ استخدم dt عندما يكون المعدل قابلًا للتغيير.
التحدي
متوسطيبدّل F معدل اللعبة بين 60 و30 إطارًا في الثانية. اجعل التدرّج يستغرق الوقت نفسه في الحالتين:
- أنشئ
dt = 1 / 60قبل الحلقة. - في نهاية الحلقة، عيّن
dt = clock.tick(fps) / 1000. - حرّك باستخدام
step = min(1, 6 * dt)بدلًا من 0.1:x += (target_x - x) * step، وطبّق الأمر نفسه علىy.
جرّب بنفسك
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Any Frame Rate")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 28)
x, y = 240, 180
target_x, target_y = 240, 180
fps = 60
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
target_x, target_y = event.pos
elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:
fps = 30 if fps == 60 else 60
x += (target_x - x) * 0.1
y += (target_y - y) * 0.1
screen.fill((30, 30, 46))
pygame.draw.circle(screen, (80, 200, 220), (x, y), 12)
label = font.render(f"{fps} FPS (F to change)", True, (240, 240, 240))
screen.blit(label, (10, 10))
pygame.display.flip()
clock.tick(fps)
pygame.quit()
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس الصوت وإحساس اللعب
تدرّب بنفسك: Playground لـ Pygame