Як програмувати гри на python з допомогою бібліотеки pygame
Дана стаття призначена для тих, хто вже знайомий з мовою програмування Python, і дозволяє докладніше вивчити бібліотеку Pygame для цієї мови. Як наочний приклад тут буде покроково продемонстрований процес програмування простої гри, в якій гравець повинен ухилятися від стрибаючих кульок.
кроки
Частина 1 з 8:
установка Pygame1. скачайте Pygame. Перейдіть по посиланню і знайдіть підходящу для вашої платформи версію бібліотеки: http: // pygame.org / download.shtml .
2. запустіть інсталятор.
3. Переконайтеся в тому, що установка завершилася успішно. Відкрийте термінал Python. Введіть «import pygame». Якщо повідомлень про помилки не з`явиться, то Pygame був успішно встановлений.
import pygame
Частина 2 з 8:
Створення головного вікна гри1. Створіть новий файл.
2. Імпорт Pygame. Pygame - це бібліотека, яка реалізує доступ до графічних функцій мови програмування Python. Якщо ви хочете дізнатися докладніше про те, як працюють ці функції, інформація доступна на сайті Pygame. https: // pygame.org / docs /
import pygamefrom pygame.locals import *
3. Задайте розміри вікна гри. Створіть глобальну змінну для розмірів вікна, щоб посилатися на неї з різних частин гри. Для зручності ці параметри краще вказувати саме на початку файлу, щоб при необхідності їх було простіше змінити в подальшому. Для більш складних проектів буде краще винести подібні дані в окремий файл.
resolution = (400,300)
4. Визначте кілька кольорів. Кольори в Pygame визначаються в форматі RGBA в межах від 0 до 255. Значення альфа (A) можна опустити, але інші кольори (червоний, зелений і синій) обов`язкові для заповнення.
white = (255,255,255) black = (0,0,0) red = (255,0,0)
5. Ініціалізуйте головний екран. Використовуйте змінну resolution, певну раніше.
screen = pygame.display.set_mode (resolution)
6. Створіть цикл гри. Задайте повторення певних дій в кожному кадрі гри. Створіть цикл, який буде послідовно виконувати всі ці дії.
while True:
7. Визначте колір заливки екрану.
screen.fill (white)
8. Відкрийте вікно. Якщо на даному етапі запустити програму, то екран забарвиться білим кольором, а потім програма аварійно завершиться. Причина збою буде полягати в тому, що операційна система посилає події програмі, а програма їх просто не обробляє. Коли програма накопичує занадто багато необроблених подій, виникає збій.
while True:...pygame.display.flip ()
9. Реалізуйте обробку подій. Отримайте список всіх подій, які виникли між отрисовкой кадрів. У нашому прикладі вас повинна хвилювати обробка тільки однієї події виходу (quit). Воно виникає, коли користувач закриває вікно гри. Обробка подій дозволить запобігти збій програми від появи занадто великої кількості подій.
while True:...for event in pygame.event.get (): if event.type == QUIT: pygame.quit ()
10. Випробуйте програму! Зараз код програми повинен виглядати так:
import pygamefrom pygame.locals import * resolution = (400,300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) while True: screen.fill (white) pygame.display.flip () for event in pygame.event.get (): if event.type == QUIT: pygame.quit ()
Частина 3 з 8:
Створення ігрових об`єктів1. Створіть новий клас об`єктів і його конструктор. Задайте всі властивості об`єкта. Також визначте значення за замовчуванням для кожного з властивостей.
class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball"
2. Визначте отрисовку об`єкта. Використовуйте властивості об`єкта, задані в конструкторі, щоб зобразити кулю як коло на поверхні, переданої параметром в функцію. Поверхнею буде екран, який ви створили раніше, визначивши його розміри.
def draw (self, surface): pygame.draw.circle (surface, black, (self.x, self.y), self.radius)
3. Створіть екземпляр класу і додайте в цикл гри завдання малювати куля при кожній ітерації циклу.
ball = Ball () while True:...ball.draw (screen)
4. Примусьте об`єкт рухатися. Створіть функцію, обновляющую положення об`єкта. Викликайте цю функцію в кожній ітерації циклу.
class Ball:...def update (self): self.x + = self.dxself.y + = self.dy
5. Обмежте частоту кадрів. Куля буде рухатися дуже швидко, тому що цикл гри повторюється сотні разів в секунду. Використовуйте таймер Pygame, щоб обмежити частоту зміни кадрів до 60 fps.
clock = pygame.time.Clock () while True:...clock.tick (60)
class Ball:...def update (self):...if (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1
import pygamefrom pygame.locals import * resolution = (400,300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball" def draw (self, surface): pygame.draw.circle (surface, black, (self.x, self.y), self.radius) def update (self): self.x + = self.dxself.y + = self.dyif (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1ball = Ball () clock = pygame.time.Clock () while True: screen.fill (white) ball.draw (screen) ball.update () pygame.display.flip () clock.tick (60) for event in pygame.event.get (): if event.type == QUIT: pygame.quit ()
Частина 4 з 8:
структурування гри1. Використовуйте класи для структурування. Надалі гра стане складнішим. Використовуйте техніки об`єктно-орієнтованого програмування для структурування коду.
2. Перетворіть цикл гри в клас. Оскільки на даний момент гра вже включає в себе дані, в тому числі ігрові об`єкти і функції, має сенс замінити цикл гри на клас.
class game ():
3. додайте конструктор. З його допомогою ви отрісуете кілька об`єктів, які використовуються в грі, створите екран, таймер і ініціалізіруете Pygame. Pygame необхідно ініціалізувати для того, щоб в подальшому використовувати такі можливості, як текст або звук.
class game (): def __init __ (self): pygame.init () self.screen = pygame.display.set_mode (resolution) self.clock = pygame.time.Clock ()
4. Задайте обробку подій в функції.
class game ():...def handleEvents (self): for event in pygame.event.get (): if event.type == QUIT: pygame.quit ()
5. Зробіть цикл гри функцією. Викликайте функцію обробки подій в кожній ітерації циклу.
class game ():...def run (self): while True: self.handleEvents () self.screen.fill (white) self.clock.tick (60) pygame.display.flip ()
6. Задайте обробку безлічі об`єктів гри. На даному етапі код гри викликає оновлення і перемальовування окремих об`єктів в кожному кадрі. З таким підходом код гри вийде досить громіздким і безладним, особливо якщо в грі буде багато об`єктів. Розумніше спочатку додавати об`єкти в масив, а потім оновлювати і перемальовувати всі об`єкти в масиві на кожній ітерації циклу. Тепер в гру легко можна внести новий об`єкт і визначити для нього іншу стартову позицію.
class game (): def __init __ (self):...self.gameObjects = [] self.gameObjects.append (Ball ()) self.gameObjects.append (Ball (100))...def run (self): while True: self.handleEvents () for gameObj in self.gameObjects: gameObj.update () self.screen.fill (white) for gameObj in self.gameObjects: gameObj.draw (self.screen) self.clock.tick (60) pygame.display.flip ()
7. Випробуйте програму! Зараз код програми повинен виглядати так:
import pygamefrom pygame.locals import * resolution = (400,300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball" def draw (self, surface): pygame.draw.circle (surface, black, (self.x, self.y), self.radius) def update (self): self.x + = self.dxself.y + = self.dyif (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1class game (): def __init __ (self): pygame.init () self.screen = pygame.display.set_mode (resolution) self.clock = pygame.time.Clock () self.gameObjects = [] self.gameObjects.append (Ball ()) self.gameObjects.append (Ball (100)) def handleEvents (self): for event in pygame.event.get (): if event.type == QUIT: pygame.quit () def run (self): while True: self.handleEvents () for gameObj in self.gameObjects: gameObj.update () self.screen.fill (white) for gameObj in self.gameObjects: gameObj.draw (self.screen) self.clock.tick (60) pygame.display.flip () game ().run ()
Частина 5 з 8:
Додавання об`єкта «Гравець»1. Створіть клас гравця і його конструктор. Вам необхідно створити ще одне коло, керований рухом миші. Ініціалізуйте його параметри в конструкторі. Єдиним важливим значенням буде радіус.
class Player: def __init __ (self, rad = 20): self.x = 0self.y = 0self.radius = rad
2. Визначте отрисовку об`єкта гравця. Він малюється аналогічно тому, як малювалися інші ігрові об`єкти.
class Player:...def draw (self, surface): pygame.draw.circle (surface, red, (self.x, self.y), self.radius)
3. Додайте управління мишею для об`єкта гравця. У кожному кадрі гри необхідно перевіряти стан покажчика миші і співвідносити положення об`єкта гравця з цією точкою.
class Player:...def update (self): cord = pygame.mouse.get_pos () self.x = cord [0] self.y = cord [1]
4. Додайте об`єкт гравця в масив gameObjects. Створіть новий екземпляр об`єкта і додайте його в список gameObjects.
class game (): def __init __ (self):...self.gameObjects.append (Player ())
5. Випробуйте програму! Зараз код програми повинен виглядати так:
import pygamefrom pygame.locals import * resolution = (400,300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball" def draw (self, surface): pygame.draw.circle (surface, black, (self.x, self.y), self.radius) def update (self): self.x + = self.dxself.y + = self.dyif (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1class Player: def __init __ (self, rad = 20): self.x = 0self.y = 0self.radius = radself.type = "player" def draw (self, surface): pygame.draw.circle (surface, red, (self.x, self.y), self.radius) def update (self): cord = pygame.mouse.get_pos () self.x = cord [0] self.y = cord [1] class game (): def __init __ (self): pygame.init () self.screen = pygame.display.set_mode (resolution) self.clock = pygame.time.Clock () self.gameObjects = [] self.gameObjects.append (Player ()) self.gameObjects.append (Ball ()) self.gameObjects.append (Ball (100)) def handleEvents (self): for event in pygame.event.get (): if event.type == QUIT: pygame.quit () def run (self): while True: self.handleEvents () for gameObj in self.gameObjects: gameObj.update () self.screen.fill (white) for gameObj in self.gameObjects: gameObj.draw (self.screen) self.clock.tick (60) pygame.display.flip () game ().run ()
Частина 6 з 8:
Створення взаємодії об`єктів1. Змініть функції поновлення положення. Щоб реалізувати взаємодію об`єктів, необхідно забезпечити їм доступ до властивостей один одного. Додайте новий параметр у функцію Update, щоб передати дані списку gameObjects. Параметр необхідно додати до функцій класів гравця і куль. Якщо у вас визначено багато класів, успадкування допоможе зберегти список параметрів однаковим.
class Ball:...def update (self, gameObjects):...class Player:...def update (self, gameObjects):
2. Введіть перевірку фактів зіткнення гравця з кулями. Переглядайте всі об`єкти і визначайте серед них ті, які відносяться до куль. Потім, використовуючи радіуси об`єктів і формулу визначення відстані, перевіряйте, зіткнулися чи об`єкти між собою. Перевіряти зіткнення між колами нескладно. Це головна причина того, що в якості прикладу в даній грі не використовуються фігури іншої форми.
class Player:...def update (self, gameObjects):...for gameObj in gameObjects: if gameObj.type == "ball": if (gameObj.x - self.x) ** 2 + (gameObj.y - self.y) ** 2 <= (gameObj.radius + self.radius)**2:
3. Задайте кінець гри при зіткненні гравця з кулею. На даному етапі просто задайте вихід з гри.
if (gameObj.x - self.x) ** 2 + (gameObj.y - self.y) ** 2 <= (gameObj.radius + self.radius)**2:pygame.quit()
4. Випробуйте програму! Зараз код програми повинен виглядати так:
import pygamefrom pygame.locals import * resolution = (400, 300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball" def draw (self, surface): pygame.draw.circle (surface, black, (self.x, self.y), self.radius) def update (self, gameObjects): self.x + = self.dxself.y + = self.dyif (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1class Player: def __init __ (self, rad = 20): self.x = 0self.y = 0self.radius = radself.type = "player" def draw (self, surface): pygame.draw.circle (surface, red, (self.x, self.y), self.radius) def update (self, gameObjects): cord = pygame.mouse.get_pos () self.x = cord [0] self.y = cord [1] for gameObj in gameObjects: if gameObj.type == "ball": if (gameObj.x - self.x) ** 2 + (gameObj.y - self.y) ** 2 <= (gameObj.radius + self.radius)**2:pygame.quit()class game():def __init__(self):pygame.init()self.screen = pygame.display.set_mode(resolution)self.clock = pygame.time.Clock()self.gameObjects = []self.gameObjects.append(Player())self.gameObjects.append(Ball())self.gameObjects.append(Ball(100))def handleEvents(self):for event in pygame.event.get():if event.type == QUIT:pygame.quit()def run(self):while True:self.handleEvents()for gameObj in self.gameObjects:gameObj.update(self.gameObjects)self.screen.fill(white)for gameObj in self.gameObjects:gameObj.draw(self.screen)self.clock.tick(60)pygame.display.flip()game().run()
Частина 7 з 8:
Додавання ігрового контролера для створення об`єктів1. Створіть клас ігрового контролера. Ігрові контролери відповідають за хід гри. Вони не збігаються з класом самої гри, який відповідає за отрисовку і оновлення всіх ігрових об`єктів. Контролер буде періодично додавати новий шар на екран і ускладнювати гру. Додайте конструктор і Ініціалізуйте деякі стартові значення. Інтервал буде означати час, через яке буде додаватися новий шар.
class GameController: def __init __ (self, interval = 5): self.inter = intervalself.next = pygame.time.get_ticks () + (2 * 1000) self.type = "game controller"
2. Додайте функцію поновлення. Вона буде перевіряти, скільки часу пройшло з моменту додавання попереднього кулі або від початку гри. Якщо час перевищить заданий інтервал, то буде скинутий таймер і доданий новий шар.
class GameController:...def update (self, gameObjects): if self.next < pygame.time.get_ticks():self.next = pygame.time.get_ticks() + (self.inter * 1000)gameObjects.append(Ball())
3. Встановіть для куль випадкові швидкості. Щоб гра поводилася по-різному, необхідно дозволити їй використовувати випадкові числа для параметра швидкості. Тепер швидкість куль стане визначатися числом з плаваючою коми, а не цілим числом.
class GameController:...def update (self, gameObjects): if self.next < pygame.time.get_ticks():self.next = pygame.time.get_ticks() + (self.inter * 1000)gameObjects.append(Ball(xVel=random()*2, yVel=random()*2))
4. Виправте функцію відтворення. Функція draw не працює з числами з плаваючою комою (float). Тому необхідно висловити позицію кулі в цілих числах, перш ніж його малювати.
class Ball:...def draw (self, surface): pygame.draw.circle (surface, black, (int (self.x), int (self.y)), self.radius)
5. Визначте метод відтворення для ігрового контролера. Оскільки це теж об`єкт гри, в головному ігровому циклі буде спроба намалювати його. Тому для контролера необхідно визначити функцію відтворення, яка нічого не робить, щоб уникнути збою гри.
class GameController:...def draw (self, screen): pass
6. Додайте ігровий контролер в gameObjects і видаліть 2 кулі. Гра повинна додавати новий шар кожні 5 секунд.
class game (): def __init __ (self):...self.gameObjects = [] self.gameObjects.append (GameController ()) self.gameObjects.append (Player ())
7. Випробуйте програму! Зараз код програми повинен виглядати так:
import pygamefrom random import randomfrom pygame.locals import * resolution = (400,300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball" def draw (self, surface): pygame.draw.circle (surface, black, (int (self.x), int (self.y)), self.radius) def update (self, gameObjects): self.x + = self.dxself.y + = self.dyif (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1class Player: def __init __ (self, rad = 20): self.x = 0self.y = 0self.radius = radself.type = "player" def draw (self, surface): pygame.draw.circle (surface, red, (self.x, self.y), self.radius) def update (self, gameObjects): cord = pygame.mouse.get_pos () self.x = cord [0] self.y = cord [1] for gameObj in gameObjects: if gameObj.type == "ball": if (gameObj.x - self.x) ** 2 + (gameObj.y - self.y) ** 2 <= (gameObj.radius + self.radius)**2:pygame.quit()class GameController:def __init__(self, interval = 5):self.inter = intervalself.next = pygame.time.get_ticks() + (2 * 1000)self.type = "game controller"def update(self, gameObjects):if self.next < pygame.time.get_ticks():self.next = pygame.time.get_ticks() + (self.inter * 1000)gameObjects.append(Ball(xVel=random()*2, yVel=random()*2))def draw(self, screen):passclass game():def __init__(self):pygame.init()self.screen = pygame.display.set_mode(resolution)self.clock = pygame.time.Clock()self.gameObjects = []self.gameObjects.append(GameController())self.gameObjects.append(Player())def handleEvents(self):for event in pygame.event.get():if event.type == QUIT:pygame.quit()def run(self):while True:self.handleEvents()for gameObj in self.gameObjects:gameObj.update(self.gameObjects)self.screen.fill(white)for gameObj in self.gameObjects:gameObj.draw(self.screen)self.clock.tick(60)pygame.display.flip()game().run()
Частина 8 з 8:
Додавання рахунку і завершення гри1. Додайте рахунок в клас ігрового контролера. Створіть об`єкт класу Font і змінну score. Об`єкт Font повинен отрісовиваться в кожному кадрі для відображення поточного рахунку і його збільшення в кожному кадрі при оновленні.
class GameController: def __init __ (self, interval = 5):...self.score = 0self.scoreText = pygame.font.Font (None, 12) def update (self, gameObjects):...self.score + = 1def draw (self, screen): screen.blit (self.scoreText.render (str (self.score), True, black), (5,5))
2. Змініть процес завершення гри. Приберіть простий вихід з гри при зіткненні гравця з кулею. Замість цього створіть змінну в класі гравця, яка буде перевірятися грою. Коли змінна gameOver стане справжньою, необхідно зупинити оновлення об`єктів. Всі об`єкти замруть на місці, тому гравець зможе зрозуміти, що сталося, і побачить свій рахунок. Об`єкти і раніше будуть відображатися на екрані, але просто перестануть оновлюватися.
class Player: def __init __ (self, rad = 20):...self.gameOver = Falsedef update (self, gameObjects):...for gameObj in gameObjects: if gameObj.type == "ball": if (gameObj.x - self.x) ** 2 + (gameObj.y - self.y) ** 2 <= (gameObj.radius + self.radius)**2:self.gameOver = Trueclass game():def __init__(self):...self.gameOver = Falsedef run(self):while True:self.handleEvents()if not self.gameOver:for gameObj in self.gameObjects:gameObj.update(self.gameObjects)if gameObj.type == "player":self.gameOver = gameObj.gameOver
3. Випробуйте програму! Підсумковий код програми повинен виглядати так:
import pygamefrom random import randomfrom pygame.locals import * resolution = (400,300) white = (255,255,255) black = (0,0,0) red = (255,0,0) screen = pygame.display.set_mode (resolution) class Ball: def __init __ (self, xPos = resolution [0] / 2, yPos = resolution [1] / 2, xVel = 1, yVel = 1, rad = 15): self.x = xPosself.y = yPosself.dx = xVelself.dy = yVelself.radius = radself.type = "ball" def draw (self, surface): pygame.draw.circle (surface, black, (int (self.x), int (self.y)), self.radius) def update (self, gameObjects): self.x + = self.dxself.y + = self.dyif (self.x <= 0 or self.x >= Resolution [0]): self.dx * = -1if (self.y <= 0 or self.y >= Resolution [1]): self.dy * = -1class Player: def __init __ (self, rad = 20): self.x = 0self.y = 0self.radius = radself.type = "player" self.gameOver = Falsedef draw (self, surface): pygame.draw.circle (surface, red, (self.x, self.y), self.radius) def update (self, gameObjects): cord = pygame.mouse.get_pos () self.x = cord [0] self.y = cord [1] for gameObj in gameObjects: if gameObj.type == "ball": if (gameObj.x - self.x) ** 2 + (gameObj.y - self.y) ** 2 <= (gameObj.radius + self.radius)**2:self.gameOver = Trueclass GameController:def __init__(self, interval = 5):self.inter = intervalself.next = pygame.time.get_ticks() + (2 * 1000)self.type = "game controller"self.score = 0self.scoreText = pygame.font.Font(None, 12)def update(self, gameObjects):if self.next < pygame.time.get_ticks():self.next = pygame.time.get_ticks() + (self.inter * 1000)gameObjects.append(Ball(xVel=random()*2, yVel=random()*2))self.score += 1def draw(self, screen):screen.blit(self.scoreText.render(str(self.score), True, black), (5,5))class game():def __init__(self):pygame.init()self.screen = pygame.display.set_mode(resolution)self.clock = pygame.time.Clock()self.gameObjects = []self.gameObjects.append(GameController())self.gameObjects.append(Player())self.gameOver = Falsedef handleEvents(self):for event in pygame.event.get():if event.type == QUIT:pygame.quit()def run(self):while True:self.handleEvents()if not self.gameOver:for gameObj in self.gameObjects:gameObj.update(self.gameObjects)if gameObj.type == "player":self.gameOver = gameObj.gameOverself.screen.fill(white)for gameObj in self.gameObjects:gameObj.draw(self.screen)self.clock.tick(60)pygame.display.flip()game().run()