التحريك
أهداف التعلّم
بعد هذا القسم
- ستعرف كيفية إنشاء تحريك (animation) بواسطة pygame
- ستتمكّن من استخدام ساعة لضبط سرعة برنامجك
- ستتمكّن من استخدام الدوال المثلثية الأساسية في تحريكاتك
كثير من الألعاب فيها شخصيات متحرّكة، ولذلك فإن إنشاء التحريكات خطوة تالية منطقية. يمكننا خلق وهم الحركة برسم الصورة نفسها في مواقع مختلفة على الشاشة وتوقيت التغييرات بصورة مناسبة.
إنشاء تحريك
تُنشئ الشيفرة التالية تحريكًا ينتقل فيه روبوت من اليسار إلى اليمين داخل نافذة pygame:
import pygame
pygame.init()
window = pygame.display.set_mode((640, 480))
robot = pygame.image.load("robot.png")
x = 0
y = 0
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
window.fill((0, 0, 0))
window.blit(robot, (x, y))
pygame.display.flip()
x += 1
clock.tick(60)
عند تنفيذ ذلك ينبغي أن تبدو النتيجة بهذا الشكل:

لنتأمّل الأوامر المستخدمة عن قرب. إذا أردنا تتبّع حركة الصورة على الشاشة، فعلينا معرفة موقعها، ولهذا السبب لدينا متغيّران لإحداثيات الزاوية العليا اليسرى للصورة:
x = 0
y = 0
ولدينا أيضًا ساعة، نستخدمها للتأكّد من أن سرعة التحريك مناسبة تمامًا:
clock = pygame.time.Clock()
ترسم الحلقة الرئيسية الصورة في موقعها الحالي مع كل تكرار:
window.fill((0, 0, 0))
window.blit(robot, (x, y))
pygame.display.flip()
أولًا تملأ الطريقة fill النافذة بالأسود كما في السابق. يُمرَّر اللون كصف (tuple) يحتوي على قيم RGB للون. وفي هذه الحالة يكون الوسيط (0, 0, 0)، أي أن المكوّنات الثلاثة جميعها — الأحمر والأخضر والأزرق — قيمتها 0. ويمكن أن تقع قيمة كل مكوّن بين 0 و255. فلو مرّرنا (255, 255, 255) وسيطًا لحصلنا على نافذة بيضاء، ولو مرّرنا (255, 0, 0) لحصلنا على نافذة حمراء. تشكّل رموز ألوان RGB العمود الفقري للتلوين الرقمي، وهناك أدوات كثيرة على الإنترنت للتعامل معها، مثل RGB Color Codes Chart.
بعد امتلاء النافذة باللون تُرسم الصورة في الموقع المُعطى عبر الطريقة blit. ثم يُحدَّث محتوى النافذة عبر الدالة pygame.display.flip.
وأخيرًا تزداد القيمة المخزّنة في x، وهو ما يجعل الصورة تنتقل بكسلًا واحدًا إلى اليمين مع كل تكرار:
x += 1
وتُستدعى طريقة الساعة tick في النهاية:
clock.tick(60)
تتولّى الطريقة tick سرعة التحريك. يفرض الوسيط 60 أن تُنفَّذ الحلقة 60 مرّة في الثانية، وهو ما يعني أن الصورة تنتقل 60 بكسل إلى اليمين في كل ثانية. وهذا يطابق تقريبًا قيمة FPS أي الإطارات في الثانية المستخدمة في الألعاب.
من حيث المبدأ تضمن الطريقة tick أن يجري التحريك بالسرعة نفسها على كل حاسوب. فلو لم يكن هناك توقيت كهذا، لاعتمدت سرعة التحريك على سرعة الحاسوب.
الارتداد عن الجدار
كان التحريك السابق ممتازًا في ما عدا أمرًا واحدًا: عندما بلغ الروبوت جدارًا واصل الخروج بعيدًا عن مجال الرؤية. لنجعل الروبوت يرتدّ عن الجدار.
import pygame
pygame.init()
window = pygame.display.set_mode((640, 480))
robot = pygame.image.load("robot.png")
x = 0
y = 0
velocity = 1
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
window.fill((0, 0, 0))
window.blit(robot, (x, y))
pygame.display.flip()
x += velocity
if velocity > 0 and x+robot.get_width() >= 640:
velocity = -velocity
if velocity < 0 and x <= 0:
velocity = -velocity
clock.tick(60)
ينبغي أن يبدو تشغيل الشيفرة أعلاه بهذا الشكل:

هناك متغيّر جديد باسم velocity يحدّد اتجاه الحركة. فإذا كانت قيمته أعلى من صفر فالحركة إلى اليمين، وإذا كانت أدنى من صفر فالحركة إلى اليسار. وبصورة أدقّ في هذه الحالة: إذا كانت القيمة 1 يتحرّك الروبوت إلى اليمين، وإذا كانت -1 يتحرّك الروبوت إلى اليسار.
تجعل الأسطر التالية الروبوت يرتدّ عن الجدران الجانبية:
if velocity > 0 and x+robot.get_width() >= 640:
velocity = -velocity
if velocity < 0 and x <= 0:
velocity = -velocity
فإذا كانت السرعة أعلى من صفر بحيث يتحرّك الروبوت إلى اليمين، وبلغت الحافة اليمنى للصورة الحافة اليمنى للنافذة أو تجاوزتها، انعكس الاتجاه وبدأ الروبوت يتحرّك إلى اليسار. وبالمثل، إذا كانت السرعة أدنى من صفر بحيث يتحرّك الروبوت إلى اليسار، وبلغت الحافة اليسرى للصورة الحافة اليسرى للنافذة، انعكس الاتجاه مرة أخرى وبدأ الروبوت يتحرّك إلى اليمين من جديد.
وبهذا ينتقل الروبوت في مسار من الحافة اليسرى للنافذة إلى الحافة اليمنى، ثم عائدًا إلى اليسار، ثم إلى اليمين مرة أخرى، ويتكرّر ذلك إلى ما لا نهاية.
الدوران
لننشئ تحريكًا آخر. هذه المرّة ينبغي أن يدور الروبوت في دائرة حول مركز النافذة:
import pygame
import math
pygame.init()
window = pygame.display.set_mode((640, 480))
robot = pygame.image.load("robot.png")
angle = 0
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
x = 320+math.cos(angle)*100-robot.get_width()/2
y = 240+math.sin(angle)*100-robot.get_height()/2
window.fill((0, 0, 0))
window.blit(robot, (x, y))
pygame.display.flip()
angle += 0.01
clock.tick(60)
ينبغي أن يبدو تشغيل الشيفرة أعلاه بهذا الشكل:

يتحقّق الدوران في دائرة دقيقة نسبيًا بمساعدة بعض الدوال المثلثية الأساسية. يحتوي المتغيّر angle على زاوية موقع الروبوت بالنسبة إلى مركز النافذة والخط الأفقي المارّ به. وتُستخدم الدالتان الجيب (sine) وجيب التمام (cosine) من مكتبة math في بايثون لحساب إحداثيات موقع الروبوت:
x = 320+math.cos(angle)*100-robot.get_width()/2
y = 240+math.sin(angle)*100-robot.get_height()/2
يدور الروبوت حول دائرة نصف قطرها 100 حول مركز النافذة. والوتر في هذه الحالة هو نصف قطر الدائرة. وتُعطي دالة جيب التمام طول الضلع المجاور في مثلث قائم الزاوية بالنسبة إلى الوتر، أي أنها تُعطينا الإحداثي x للموقع. وتُعطي دالة الجيب طول الضلع المقابل، أي الإحداثي y. ثم يُعدَّل الموقع بحسب حجم الصورة، بحيث يقع مركز الدائرة في مركز النافذة.
مع كل تكرار يزداد مقدار angle بمقدار 0.01:llä. وبما أننا نستخدم الراديان، فإن الدائرة الكاملة تساوي 2π، أي نحو 6.28. ويستغرق الروبوت نحو 628 تكرارًا لإكمال دورة كاملة، وبمعدل 60 تكرارًا في الثانية يستغرق ذلك ما يزيد قليلًا على 10 ثوانٍ.
تمرين برمجي: حركة رأسية
أنشئ تحريكًا يتحرّك فيه الروبوت صعودًا وهبوطًا في حلقة لا نهائية. ينبغي أن تبدو النتيجة النهائية بهذا الشكل:

تمرين برمجي: الدوران حول المحيط
أنشئ تحريكًا يطوف فيه الروبوت بمحيط النافذة. ينبغي أن تبدو النتيجة النهائية بهذا الشكل:

تمرين برمجي: روبوتان
أنشئ تحريكًا يتحرّك فيه روبوتان ذهابًا وإيابًا إلى اليسار وإلى اليمين. ينبغي أن يتحرّك الروبوت الأسفل بضعف سرعة الروبوت الأعلى. ينبغي أن تبدو النتيجة النهائية بهذا الشكل:

تمرين برمجي: روبوتات في دائرة
أنشئ تحريكًا تدور فيه عشرة روبوتات في دائرة. ينبغي أن تبدو النتيجة النهائية بهذا الشكل:

تمرين برمجي: الكرة المرتدة
أنشئ تحريكًا ترتدّ فيه كرة عن حوافّ النافذة. ينبغي أن تبدو النتيجة النهائية بهذا الشكل:

يحتوي قالب التمرين على الصورة ball.png.
تمرين برمجي: غزو الروبوتات
أنشئ تحريكًا تسقط فيه الروبوتات من السماء عشوائيًا. وعندما يبلغ روبوت الأرض، يبدأ بالتحرّك إلى اليسار أو إلى اليمين، ثم يختفي أخيرًا عن الشاشة. ينبغي أن تبدو النتيجة النهائية بهذا الشكل:
