Hướng dẫn rắn ăn mồi python

Trụ sở chính:

Văn phòng: Số 27-3RD, Sunrise D, The Manor Central Park, đường Nguyễn Xiển, phường Đại Kim, quận Hoàng Mai, TP. Hà Nội.

Liên hệ truyền thông: 0929.536.185

Email: [email protected]

Chịu trách nhiệm nội dung: Ông Trần Anh Tú

TEK4.VN giữ bản quyền nội dung trên website này. Cấm sao chép dưới mọi hình thức nếu không có sự chấp thuận bằng văn bản.

Python là một ngôn ngữ lập trình bậc cao với cú pháp dễ hiểu, dễ học, thân thiện với người mới bắt đầu. Vì vậy, hôm nay mình sẽ hướng dẫn các bạn lập trình game rắn săn mồi cực kỳ dễ hiểu với Python. 

Lần này mình có sử dụng thư viện pygame, một thư viện được thiết kế để hỗ trợ lập trình những game đơn giản.

Setup môi trường

Để cài đặt pygame, các bạn truy cập vào Command Promt và nhập lệnh :

pip install pygame

Sau khi đã cài đặt xong, ta tiến hành khai báo những thư viện cần dùng:

import pygame
import sys
import random

Khai báo biến

Ta sẽ bắt đầu bằng cách khai báo một số biến cần dùng:

SCREEN_WIDTH = 480
SCREEN_HEIGHT = 480

GRIDSIZE = 20
GRID_WIDTH = SCREEN_WIDTH / GRIDSIZE
GRID_HEIGHT = SCREEN_HEIGHT / GRIDSIZE

UP    = (0, -1)
DOWN  = (0, 1)
LEFT  = (-1, 0)
RIGHT = (1, 0)

Tọa độ màn hình

SCREEN_WIDTH và SCREEN_HEIGHT là kích thước ô cửa sổ của game. GRIDSIZE là kích thước mỗi ô caro. UP, DOWN, LEFT, RIGHT dùng để lưu phương hướng của con rắn.

Công việc tiếp theo của ta là vẽ bàn caro lên cửa sổ. Việc đó có thể thực hiện qua hàm sau đây:

def drawGrid(surface):
    for y in range(0, int(GRID_HEIGHT)):
        for x in range(0, int(GRID_WIDTH)):
            if (x + y) % 2 == 0:
                r = pygame.Rect((x*GRIDSIZE, y*GRIDSIZE), (GRIDSIZE, GRIDSIZE))
                pygame.draw.rect(surface, (93, 216, 228), r)
            else:
                rr = pygame.Rect((x*GRIDSIZE, y*GRIDSIZE), (GRIDSIZE, GRIDSIZE))
                pygame.draw.rect(surface, (84, 194, 205), rr)

Trong đoạn code trên, cứ vị trí ô vuông nào có tổng tọa độ x và y thì là chẵn thì ta sẽ tô một màu, những ô còn lại ta tô màu khác. Vậy là ta đã có một bàn caro để con rắn chạy trên đó.

Giờ ta sẽ tạo ra con rắn của mình. 

class Snake(object):
    def __init__(self):
        self.length = 1
        self.positions = [((SCREEN_WIDTH / 2), (SCREEN_HEIGHT / 2))]
        self.direction = random.choice([UP, DOWN, LEFT, RIGHT])
        self.color = (17, 24, 47)

    def get_head_position(self):
        return self.positions[0]

    def turn(self, point):
        if self.length > 1 and (point[0] * -1, point[1] * -1) == self.direction:
            return
        else:
            self.direction = point

    def move(self):
        cur = self.get_head_position()
        x, y = self.direction
        new = (((cur[0] + (x*GRIDSIZE)) % SCREEN_WIDTH), (cur[1] + (y*GRIDSIZE)) % SCREEN_HEIGHT)
        if len(self.positions) > 2 and new in self.positions[2:]:
            self.reset()
        else:
            self.positions.insert(0, new)
            if len(self.positions) > self.length:
                self.positions.pop()            
            
    def reset(self):
        self.length = 1
        self.positions =  [((SCREEN_WIDTH / 2), (SCREEN_HEIGHT / 2))]
        self.direction = random.choice([UP, DOWN, LEFT, RIGHT])
    
    def draw(self, surface):
        for p in self.positions:
            r = pygame.Rect((p[0], p[1]), (GRIDSIZE, GRIDSIZE))
            pygame.draw.rect(surface, self.color, r)
            pygame.draw.rect(surface, (93, 216, 228), r, 1)
            
    def handle_keys(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    self.turn(UP)
                elif event.key == pygame.K_DOWN:
                    self.turn(DOWN)
                elif event.key == pygame.K_LEFT:
                    self.turn(LEFT)
                elif event.key == pygame.K_RIGHT:
                    self.turn(RIGHT)

Trong object trên, rắn của ta là một mảng lưu vị trí những phần thân của nó. Ta có thể thấy hàm turn để hỗ trợ việc chuyển hướng của con rắn, hàm draw để vẽ những phần thân của rắn, get_head_position để lấy vị trí đầu rắn, reset để bắt đầu lại trò chơi và handle_keys để xử lý thao tác của người chơi.

Hàm move điều khiển cách con rắn của chúng ta di chuyển. Hàm này hoạt động bằng cách tính toán vị trí tiếp theo của đầu rắn, nếu nó không trùng với vị trí của phần thân nào thì sẽ được thêm vào rắn, đồng thời bỏ đi phần thân cuối, còn nếu trùng thì trò chơi sẽ bắt đầu lại.

Tiếp đến ta sẽ tạo thức ăn cho rắn:

class Food(object):
    def __init__(self):
        self.position = (0, 0)
        self.color = (233, 163, 49)
        self.randomize_position()

    def randomize_position(self):
        self.position = (random.randint(0, GRID_WIDTH-1) * GRIDSIZE, random.randint(0, GRID_HEIGHT-1) * GRIDSIZE)

    def draw(self, surface):
        r = pygame.Rect((self.position[0], self.position[1]), (GRIDSIZE, GRIDSIZE))
        pygame.draw.rect(surface, self.color, r)
        pygame.draw.rect(surface, (93, 216, 228), r, 1)

Với object Food, ta có hàm randomize_position để những ô đồ ăn xuất hiện ngẫu nhiên trên bàn caro, hàm draw để vẽ ô thức ăn đó.

Vậy là ta đã có đủ thành phần để lắp ghép thành game rắn săn mồi hoàn chỉnh. Thứ tiếp theo ta làm sau đây là vòng lặp game, nơi những quy định của game được cấu hình.

def main():
    pygame.init()
    
    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), 0, 32)
    
    surface = pygame.Surface(screen.get_size())
    surface = surface.convert()
    drawGrid(surface)

    snake = Snake()
    food = Food()
    
    myfont = pygame.font.SysFont("monospace", 16)
    
    score = 0 
    while True:
        clock.tick(10)
        snake.handle_keys()
        drawGrid(surface)
        snake.move()
        if snake.get_head_position() == food.position:
            snake.length += 1
            score += 1
            food.randomize_position()
        snake.draw(surface)
        food.draw(surface) 
        screen.blit(surface, (0,0))
        text = myfont.render("Score {0}".format(score), 1, (0,0,0))
        screen.blit(text, (5, 10))
        pygame.display.update()

Theo dõi thời gian

Việc làm đầu tiên của ta là bao gồm những module của pygame, tạo một Clock để theo dõi thời gian và tạo một cửa sổ game.

pygame.init()
    
    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), 0, 32)
    
    surface = pygame.Surface(screen.get_size())
    surface = surface.convert()

Vẽ caro

drawGrid(surface)

Tạo rắn và thức ăn

snake = Snake()
food = Food()

Đây sẽ là vòng lặp chính của game

while True:
        clock.tick(10)
        snake.handle_keys()
        drawGrid(surface)
        snake.move()
        if snake.get_head_position() == food.position:
            snake.length += 1
            score += 1
            food.randomize_position()
        snake.draw(surface)
        food.draw(surface) 
        screen.blit(surface, (0,0))
        text = myfont.render("Score {0}".format(score), 1, (0,0,0))
        screen.blit(text, (5, 10))
        pygame.display.update()

Ta sẽ quy định game chạy với tốc độ 10 khung hình 1 giây.

clock.tick(10)

Nếu vị trí đầu rắn trùng với vị trí của thức ăn, ta tăng độ dài rắn lên một, đồng thời tạo ô thức ăn mới ngẫu nhiên

if snake.get_head_position() == food.position:
            snake.length += 1
            score += 1
            food.randomize_position()

Vẽ lại rắn và đồ ăn

snake.draw(surface)
food.draw(surface)

Tạo một ô nhỏ để hiển thị điểm số

screen.blit(surface, (0,0))
text = myfont.render("Score {0}".format(score), 1, (0,0,0))
screen.blit(text, (5, 10))
pygame.display.update()

Vậy là xong! Việc duy nhất ta cần làm là gọi lại hàm main của ta rồi chạy game thôi!

main()

Source code

Sản phẩm cuối cùng sẽ như thế này

Sau đây là toàn bộ code của trò chơi: Game snake với python

Có chỗ nào cần giải đáp hoặc bổ sung, các bạn để lại comment dưới bài viết nha. Chúc các bạn vận dụng được kiến thức và thực hiện thành công game này :))