Làm game cơ bản với Pygame
Khi bắt đầu học lập trình, niềm yêu thích chơi game đã luôn thúc đẩy mình phải tự tay làm ra 1 game cho riêng mình, vì thế mình đã mày mò làm thử và thấy nó không khó như mình nghĩ. Vậy nên, hôm nay mình viết bài này để chia sẻ với các bạn những điều cơ bản khi làm game bằng Python mà mình đã học được.
1. Pygame
Trước hết chúng ta cần một công cụ hỗ trợ việc lập trình game trong Python, và đó chính là Pygame.
Để cài đặt Pygame, bạn chỉ cần chạy câu lệnh sau
pip install pygame
2. Chương trình Pygame cơ bản
Sau khi cài đặt pygame hãy thử tạo một chương trình pygame cơ bản với tên basicPygame.py
bao gồm các nội dung sau
-
Các thư viện cần thiết
import pygame import sys
- Khởi tạo đối tượng game và kích cỡ màn hình hiển thị
# Initialize pygame object pygame.init() # Set size of screen screenWidth = 500 screenHeight = 500 screen = pygame.display.set_mode((screenWidth, screenHeight))
- Vòng lặp chính của game
# Game loop while True: # Get event for event in pygame.event.get(): # Handle exit event if event.type == pygame.QUIT: pygame.quit() sys.exit() # Fill screen by gray screen.fill((150, 150, 150)) # Draw a blue circle at the center of the screen pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75) # Flip pygame.display.flip()
- Full source code.
# File basicPygame.py import pygame import sys # Initialize pygame object pygame.init() # Set size of screen screenWidth = 500 screenHeight = 500 screen = pygame.display.set_mode((screenWidth, screenHeight)) # Game loop while True: # Get event for event in pygame.event.get(): # Handle exit event if event.type == pygame.QUIT: pygame.quit() sys.exit() # Fill screen by gray screen.fill((150, 150, 150)) # Draw a blue circle at the center of the screen pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75) # Flip pygame.display.flip()
Chạy chương trình trên và bạn sẽ thấy một cửa sổ trông giống với hình ảnh dưới đây:
3. Vòng lặp của game
Như đã biết trong chương trình vật lý phổ thông, trong 0,1s sau khi tắt ánh sáng, mắt người vẫn còn cảm nhận về hình ảnh của vật, nhờ điều này mà người ta có thể tạo ra một hình ảnh chuyển động bằng cách trình chiếu liên tục các hình ảnh rời rạc. Đây cũng là lý thuyết khi tạo ra các chuyển động trong game, phim hoạt hình, ….
Để tạo ra một hệ thống các hình ảnh liên tục trong game, ta cần một vòng lặp để liên tục hiển thị các hình ảnh đó theo thứ tự, đó là vòng lặp game.
Tuy nhiên, các hình ảnh trong game ta không thể lưu sẵn để phát lần lượt như trong phim hoạt hình bởi các hình ảnh đó còn phụ thuộc vào người chơi. Vì thế trong vòng lặp game ta sẽ cập nhật trạng thái của tất cả vật thể của game và vẽ lại chúng.
Về cơ bản, quá trình thực hiện sẽ như sau
- Vẽ các vật thể
- Cập nhật trạng thái các vật thể
- Quay lại bước 1
Thử thực hành với một chương trình như sau để hiều rõ hơn về vòng lặp game
import pygame
import random
import sys
# Initialize pygame object
pygame.init()
# Set size of screen
screenWidth = 500
screenHeight = 500
screen = pygame.display.set_mode((screenWidth, screenHeight))
colors = ['#C7980A',
'#F4651F',
'#82D8A7',
'#CC3A05',
'#575E76',
'#156943',
'#0BD055',
'#ACD338']
color = random.choice(colors)
# coordinate x of circle
coordinateX = 250
# velocity of circle
velocity = 5
clock = pygame.time.Clock()
# Game loop
while True:
clock.tick(60)
# Get event
for event in pygame.event.get():
# Handle exit event
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Fill screen by gray
screen.fill((150, 150, 150))
# Update coordinate x of circle
if coordinateX <= 75 or coordinateX >= 425:
color = random.choice(colors)
velocity *= -1
coordinateX += velocity
# Draw circle with new coordinate x and color
pygame.draw.circle(screen, color, (coordinateX, 250), 75)
# Flip
pygame.display.flip()
Về cơ bản, tại mỗi vòng lặp của game ta cập nhật tọa độ và màu của hình tròn sau đó vẽ lại nó. Và đây là kết quả:
4. Sự kiện game
Tất nhiên, game không giống một chương trình giải một bài toán nào đó, khi mà ta chỉ việc chạy chương trình và xem kết quả. Game cần sự tương tác với người dùng trong suốt quá trình chạy.
Mỗi tín hiệu mà người dùng cung cấp (di chuyển con trỏ chuột, ấn một phím, …) được gọi là một sự kiện game, tại mỗi vòng lặp game ta cần xử lý tín hiệu này và phản hồi lại bằng cách vẽ lên màn hình để tạo nên sự tương tác giữa game và người chơi.
Với pygame bạn có thể dùng
pygame.event.get()
để lấy tất cả sự kiện của game.
Trong các chương trình ví dụ bên trên các bạn có thể thấy đoạn code sau
for event in pygame.event.get():
# Handle exit event
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
Đây là một sự kiện cơ bản của game dùng để thoát chương trình khi nhấn QUIT
.
Để hiểu rõ hơn về sự kiện game, ta sẽ sửa chương trình trong phần 3 sao cho hình tròn không còn tự chuyển động nữa mà sẽ chuyển động sang trái, phải khi người dùng ấn các phím A, D và đổi màu khi ấn SPACE.
import pygame
import random
import sys
# Initialize pygame object
pygame.init()
# Set size of screen
screenWidth = 500
screenHeight = 500
screen = pygame.display.set_mode((screenWidth, screenHeight))
colors = ['#C7980A',
'#F4651F',
'#82D8A7',
'#CC3A05',
'#575E76',
'#156943',
'#0BD055',
'#ACD338']
color = random.choice(colors)
moving = False
coordinateX = 250
velocity = 5
clock = pygame.time.Clock()
# Game loop
while True:
clock.tick(60)
# Get event
for event in pygame.event.get():
# Handle exit event
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
pygame.quit()
sys.exit()
# Change color event
if event.key == pygame.K_SPACE:
color = random.choice(colors)
# Move event
if event.key == pygame.K_a:
moving = True
velocity = -5
if event.key == pygame.K_d:
moving = True
velocity = 5
# Stop event
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
moving = False
if event.key == pygame.K_d:
moving = False
# Fill screen by gray
screen.fill((150, 150, 150))
# Update coordinate of circle
if moving:
coordinateX += velocity
if coordinateX <= 75:
coordinateX = 75
if coordinateX >= 425:
coordinateX = 425
# Draw a blue circle at the center of the screen
pygame.draw.circle(screen, color, (coordinateX, 250), 75)
# Flip
pygame.display.flip()
Kết quả khi chạy chương trình
Các sự kiện ta đã xử lý tại mỗi vòng lặp trong đoạn code trên
- Thoát chương trình
# Handle exit event if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False pygame.quit() sys.exit()
Ở đây ngoài việc thoát bằng cách nhấp phím
QUIT
trên màn hình, mình cung cấp thêm điều kiện thoát khi người dùng ấn phímESCAPE
trên bàn phím. - Đổi màu hình tròn
# Change color event if event.key == pygame.K_SPACE: color = random.choice(colors)
- Di chuyển hình tròn
# Move event if event.key == pygame.K_a: moving = True velocity = -5 if event.key == pygame.K_d: moving = True velocity = 5
- Dừng hình tròn
# Stop event if event.type == pygame.KEYUP: if event.key == pygame.K_a: moving = False if event.key == pygame.K_d: moving = False
Ở đây có nhiều thuộc tính hơi lạ của pygame
như KEYUP
, KEYDOWN
. Các bạn có thể
tham khảo tại pygame.key
để hiểu rõ về cách lấy sự kiện của pygame
.
5. Thử làm một game với pygame
Lý thuyết đã xong, bây giờ chúng ta sẽ thực hành với việc tạo 1 game với pygame
5.1. Thiết kế game
Trước khi viết một chương trình hay cụ thể là làm một game, bạn nên có một bước thiết kế cơ bản những thành phần của game. Trong phần này mình sẽ hướng dẫn các bạn làm một game với các chức năng cơ bản sau:
- Sinh ngẫu nhiên các hình tròn từ trên đỉnh màn hình di chuyển từ trên xuống
- Người chơi điều khiển một hình chữ nhật ở dưới cùng màn hình có thể di chuyển qua trái phải
- Ghi điểm mỗi khi hình chữ nhật chạm vào 1 hình tròn
Sau khi đã thiết kế, chúng ta có 3 việc chính phải làm
- Tạo class Circle mô tả các hình tròn
- Tạo class Rectangle mô tả hình chữ nhật
- Thiết kế sự tương tác giữa các đối tượng trên
5.2. Code
Với những thiết kế trên, ta bắt đầu xây dựng từng đối tượng và kết nối chúng lại để tạo thành 1 game như ý muốn.
Vì bài viết đã dài rồi, mà đoạn code còn nhiều nên mình sẽ chỉ show source code mà không giải thích chi tiết nữa (mình tin vào khả năng đọc hiểu code của mọi người). Các bạn có thể tham khảo thêm tại trang tài liệu của pygame.
import pygame
import random
import sys
class Circle(pygame.sprite.Sprite):
def __init__(self, x, y, radius, colors, gravity):
pygame.sprite.Sprite.__init__(self)
self.radius = radius
color = random.choice(colors)
self.gravity = gravity
self.surface = pygame.Surface([2*radius, 2*radius], pygame.SRCALPHA)
self.rect = self.surface.get_rect()
self.rect.x = x
self.rect.y = y
pygame.draw.circle(self.surface, color, (radius,radius), radius)
def fall(self):
self.rect.y += self.gravity
def draw(self, screen):
screen.blit(self.surface, self.rect)
class Rectangle(pygame.sprite.Sprite):
def __init__(self, x, y, w, h, color):
pygame.sprite.Sprite.__init__(self)
self.surface = pygame.Surface([w, h])
self.rect = self.surface.get_rect()
self.rect.x = x
self.rect.y = y
self.surface.fill(color)
self.velocity = 0
def move(self):
self.rect.x += self.velocity
def draw(self, screen):
screen.blit(self.surface, self.rect)
class Game:
def __init__(self):
# Initialize pygame object
pygame.init()
# Set size of screen
screenWidth = 500
screenHeight = 750
self.screen = pygame.display.set_mode((screenWidth, screenHeight))
self.circleColors = [
'#F94892',
'#FF7F3F',
'#FBDF07',
'#89CFFD',
'#1CD6CE',
'#FEDB39',
'#B4FF9F',
]
self.circleList = []
self.rectangle = Rectangle(250, 730, 150, 20, '#293462')
self.score = 0
def update(self):
w, h = self.screen.get_size()
# Move rectangle
self.rectangle.move()
if self.rectangle.rect.x <= 0:
self.rectangle.rect.x = 0
if self.rectangle.rect.x >= w - self.rectangle.rect.w:
self.rectangle.rect.x = w - self.rectangle.rect.w
# Move circle
for circle in self.circleList:
circle.fall()
outOfScreen = circle.rect.y - circle.radius >= h
# If circle out of screen, it disappear
if outOfScreen:
self.circleList.remove(circle)
isCollide = pygame.sprite.collide_rect(circle, self.rectangle)
if isCollide:
self.circleList.remove(circle)
self.score += 1
if len(self.circleList) <= 5:
x = random.randint(50, w - 50)
y = 0
gravity = random.randint(2, 7)
circle = Circle(x, y, 20, self.circleColors, gravity)
self.circleList.append(circle)
for circle in self.circleList:
circle.draw(self.screen)
self.rectangle.draw(self.screen)
font = pygame.font.Font('freesansbold.ttf', 32)
text = font.render(str(self.score), True, '#293462')
textRect = text.get_rect()
textRect.center = (w//2, 32)
self.screen.blit(text, textRect)
pygame.display.update()
def run(self):
clock = pygame.time.Clock()
# Game loop
while True:
clock.tick(60)
# Get event
for event in pygame.event.get():
# Handle exit event
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
# Move event
if event.key == pygame.K_a:
self.rectangle.velocity = -5
if event.key == pygame.K_d:
self.rectangle.velocity = 5
# Stop event
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d:
self.rectangle.velocity = 0
# Fill screen by gray
self.screen.fill('#6E85B7')
self.update()
def main():
""" Main function."""
game = Game()
game.run()
# Run function main.
if __name__ == "__main__":
main()
Và đây là demo cho game này
6. Lời kết
Những gì mình viết trên đây là những điều rất cơ bản khi làm game, còn rất nhiều điều thú vị khác mà bạn có thể tìm hiểu và mày mò trong quá trình làm game. Trong tương lai mình sẽ viết nhiều hơn về chủ đề này, mong mọi người sẽ thích.