PyQt5 - 蛇游戏
在本文中,我们将了解如何使用 PyQt5 设计简单的蛇游戏。
Snake是视频游戏概念的通用名称,玩家在其中操纵一条越来越长的线,而线本身就是主要障碍。这个概念起源于 1976 年的街机游戏 Blockade,实现 Snake 的便利性导致了许多平台的数百个版本(其中一些在标题中带有蛇或蠕虫一词)。
Implementation steps :
1. Create a main window add status bar to it, to show the score and create an object of board class and add it as central widget
2. Create a class named board which inherits the QFrame
3. Inside the board class create a timer object which calls the timer method after certain amount of time
4. Inside the timer method call other action of the snake game like movement, food eaten and if snake committed suicide
5. Create a key press event method that check if arrow keys are pressed and change the direction of the snake according to it.
6. Create a paint event method that draws snake and the food
7. Create move method to move the snake according to the direction
8. Create food eaten method that checks the snake current position and position if food is eaten remove the current food increment the snake length and drop a new food at random location.
9. Create check suicide method that checks if snakehead position is similar to the body position or not, if matches stop the timer and show the message
下面是实现
# importing libraries
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import random
import sys
# creating game window
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
# creating a board object
self.board = Board(self)
# creating a status bar to show result
self.statusbar = self.statusBar()
# adding border to the status bar
self.statusbar.setStyleSheet("border : 2px solid black;")
# calling showMessage method when signal received by board
self.board.msg2statusbar[str].connect(self.statusbar.showMessage)
# adding board as a central widget
self.setCentralWidget(self.board)
# setting title to the window
self.setWindowTitle('Snake game')
# setting geometry to the window
self.setGeometry(100, 100, 600, 400)
# starting the board object
self.board.start()
# showing the main window
self.show()
# creating a board class
# that inherits QFrame
class Board(QFrame):
# creating signal object
msg2statusbar = pyqtSignal(str)
# speed of the snake
# timer countdown time
SPEED = 80
# block width and height
WIDTHINBLOCKS = 60
HEIGHTINBLOCKS = 40
# constructor
def __init__(self, parent):
super(Board, self).__init__(parent)
# creating a timer
self.timer = QBasicTimer()
# snake
self.snake = [[5, 10], [5, 11]]
# current head x head
self.current_x_head = self.snake[0][0]
# current y head
self.current_y_head = self.snake[0][1]
# food list
self.food = []
# growing is false
self.grow_snake = False
# board list
self.board = []
# direction
self.direction = 1
# called drop food method
self.drop_food()
# setting focus
self.setFocusPolicy(Qt.StrongFocus)
# square width method
def square_width(self):
return self.contentsRect().width() / Board.WIDTHINBLOCKS
# square height
def square_height(self):
return self.contentsRect().height() / Board.HEIGHTINBLOCKS
# start method
def start(self):
# msg for status bar
# score = current len - 2
self.msg2statusbar.emit(str(len(self.snake) - 2))
# starting timer
self.timer.start(Board.SPEED, self)
# paint event
def paintEvent(self, event):
# creating painter object
painter = QPainter(self)
# getting rectangle
rect = self.contentsRect()
# board top
boardtop = rect.bottom() - Board.HEIGHTINBLOCKS * self.square_height()
# drawing snake
for pos in self.snake:
self.draw_square(painter, rect.left() + pos[0] * self.square_width(),
boardtop + pos[1] * self.square_height())
# drawing food
for pos in self.food:
self.draw_square(painter, rect.left() + pos[0] * self.square_width(),
boardtop + pos[1] * self.square_height())
# drawing square
def draw_square(self, painter, x, y):
# color
color = QColor(0x228B22)
# painting rectangle
painter.fillRect(x + 1, y + 1, self.square_width() - 2,
self.square_height() - 2, color)
# key press event
def keyPressEvent(self, event):
# getting key pressed
key = event.key()
# if left key pressed
if key == Qt.Key_Left:
# if direction is not right
if self.direction != 2:
# set direction to left
self.direction = 1
# if right key is pressed
elif key == Qt.Key_Right:
# if direction is not left
if self.direction != 1:
# set direction to right
self.direction = 2
# if down key is pressed
elif key == Qt.Key_Down:
# if direction is not up
if self.direction != 4:
# set direction to down
self.direction = 3
# if up key is pressed
elif key == Qt.Key_Up:
# if direction is not down
if self.direction != 3:
# set direction to up
self.direction = 4
# method to move the snake
def move_snake(self):
# if direction is left change its position
if self.direction == 1:
self.current_x_head, self.current_y_head = self.current_x_head - 1, self.current_y_head
# if it goes beyond left wall
if self.current_x_head < 0:
self.current_x_head = Board.WIDTHINBLOCKS - 1
# if direction is right change its position
if self.direction == 2:
self.current_x_head, self.current_y_head = self.current_x_head + 1, self.current_y_head
# if it goes beyond right wall
if self.current_x_head == Board.WIDTHINBLOCKS:
self.current_x_head = 0
# if direction is down change its position
if self.direction == 3:
self.current_x_head, self.current_y_head = self.current_x_head, self.current_y_head + 1
# if it goes beyond down wall
if self.current_y_head == Board.HEIGHTINBLOCKS:
self.current_y_head = 0
# if direction is up change its position
if self.direction == 4:
self.current_x_head, self.current_y_head = self.current_x_head, self.current_y_head - 1
# if it goes beyond up wall
if self.current_y_head < 0:
self.current_y_head = Board.HEIGHTINBLOCKS
# changing head position
head = [self.current_x_head, self.current_y_head]
# inset head in snake list
self.snake.insert(0, head)
# if snake grow is False
if not self.grow_snake:
# pop the last element
self.snake.pop()
else:
# show msg in status bar
self.msg2statusbar.emit(str(len(self.snake)-2))
# make grow_snake to false
self.grow_snake = False
# time event method
def timerEvent(self, event):
# checking timer id
if event.timerId() == self.timer.timerId():
# call move snake method
self.move_snake()
# call food collision method
self.is_food_collision()
# call is suicide method
self.is_suicide()
# update the window
self.update()
# method to check if snake collides itself
def is_suicide(self):
# traversing the snake
for i in range(1, len(self.snake)):
# if collision found
if self.snake[i] == self.snake[0]:
# show game ended msg in status bar
self.msg2statusbar.emit(str("Game Ended"))
# making background color black
self.setStyleSheet("background-color : black;")
# stopping the timer
self.timer.stop()
# updating the window
self.update()
# method to check if the food cis collied
def is_food_collision(self):
# traversing the position of the food
for pos in self.food:
# if food position is similar of snake position
if pos == self.snake[0]:
# remove the food
self.food.remove(pos)
# call drop food method
self.drop_food()
# grow the snake
self.grow_snake = True
# method to drop food on screen
def drop_food(self):
# creating random co-ordinates
x = random.randint(3, 58)
y = random.randint(3, 38)
# traversing if snake position is not equal to the
# food position so that food do not drop on snake
for pos in self.snake:
# if position matches
if pos == [x, y]:
# call drop food method again
self.drop_food()
# append food location
self.food.append([x, y])
# main method
if __name__ == '__main__':
app = QApplication([])
window = Window()
sys.exit(app.exec_())
输出 :