geekdoc-python-zh/docs/askpython/pygame-graphical-hi-lo-game.md

19 KiB
Raw Permalink Blame History

PyGame 教程:Python 中的图形高低游戏

原文:https://www.askpython.com/python/examples/pygame-graphical-hi-lo-game

PyGame 是一个用 Python 编写的 2D 游戏开发库。它包含程序员从头开始创建简单或复杂游戏所需的特定函数和类。

在本教程中,我们将使用 PyGame 库创建自己的高低游戏。Hi-Lo 是一种非常简单的赌场游戏,玩家必须猜测一副牌中的下一张牌是高于还是低于当前的牌。

建议:如果这是你第一次创建游戏,试试用 Python 的命令行实现 hi-lo 游戏。这更容易,并且会让你对游戏机制有更好的理解。但是,如果您已经精通 Python请继续往下读

牌的排名系统从最低排名的 Ace 开始,到最高排名的 king 结束。


Python 中的 GUI 高低游戏

https://www.askpython.com/wp-content/uploads/2020/07/hi_lo_pygame.mp4


导入 PyGame

在使用任何 pygame 的模块之前,我们需要导入库。

import pygame

所有的 PyGame 函数都可以使用pygame后跟'.'和函数名来访问。


声明游戏常数

每个游戏设计都需要一些常量,用来指定游戏的关键特性。

# Margins
MARGIN_LEFT = 230
MARGIN_TOP = 150

# WINDOW SIZE
WIDTH = 800
HEIGHT = 600

# COLORS
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (110, 110, 110)
GREEN = (0, 255, 0)
LIGHT_GREEN = (0, 120, 0)
RED = (255, 0, 0)
LIGHT_RED = (120, 0, 0)

对于程序员来说,常量的类型和值是不同的。预先定义这样的常数是一个好习惯,这样在值改变的情况下,我们不必到处去修正它们。


初始化游戏模块

为了使用 PyGame 模块,我们首先需要通过以下方式初始化它们:

# Initializing PyGame
pygame.init()

每个游戏都在一个特定的游戏窗口中进行,这个窗口可以根据程序员的需要而改变。这个游戏窗口需要尺寸参数。

# WINDOW SIZE
WIDTH = 800
HEIGHT = 600

# Setting up the screen and background
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(GRAY)

使用内置的set_mode()函数,我们定义窗口大小。在使用 PyGame 时要记住的一件事是size 参数是作为一个由两个值组成的元组传递的:宽度和高度。

设置好窗口后,我们使用fill()命令设置背景颜色。


设置标题和图标

我们的游戏需要一个标题和图标来代表自己。

# Setting up caption
pygame.display.set_caption("Hi-Lo Game")

# Loading image for the icon
icon = pygame.image.load('icon.jpeg')

# Setting the game icon
pygame.display.set_icon(icon)

set_caption()函数将一个字符串作为参数,并将其作为标题。为了设置图标,我们首先需要使用接受图像文件名称的load()函数加载图像。

set_icon()功能将图像设置为游戏图标。

**注意:**如果图像文件不在与 python 游戏文件相同的目录中,那么我们需要添加相对路径以及图像的名称。


定义游戏字体

在屏幕上呈现文本之前,我们需要定义某些字体。

# Types of fonts to be used
small_font = pygame.font.Font(None, 32)
large_font = pygame.font.Font(None, 50)

Font()函数有两个参数:字体类型(默认字体为None)和字体大小。


设置游戏按钮的文本

我们的游戏有两个按钮:高和低。为按钮放置文本需要多个步骤:

  1. 为文本呈现字体
  2. 获取文本的矩形覆盖
  3. 将矩形放在屏幕上
# Hign and Low Game Buttons
high_button = large_font.render("HIGH", True, WHITE)

# Gets_rectangular covering of text
high_button_rect = high_button.get_rect()

# Places the text
high_button_rect.center = (280, 400)

low_button = large_font.render("LOW", True, WHITE)
low_button_rect = low_button.get_rect()
low_button_rect.center = (520, 400)

render()函数接受以下参数:

  • 文本-“高”
  • 对文本的平滑边缘应用抗锯齿?–正确
  • 文本颜色-白色

get_rect()函数返回所提供文本的矩形覆盖。

下一行指定矩形封面的中心位置,从而放置文本。


定义我们的纸牌

为了定义我们的一副牌,我们首先要定义一张单独的牌。我们将借助 Python 类来完成这项任务。

# Card class definition
class Card:
	def __init__(self, suit_type, value):
		self.suit_type = suit_type
		self.value = value

任何牌都有两个特征:花色类型和面值。继续讨论卡的定义,我们使用三种定义数据结构:

# The type of suit
suits = ["Spades", "Hearts", "Clubs", "Diamonds"]

# The type of card
cards = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]

# The card value
cards_values = {"A": 1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":11, "Q":12, "K":13}

这些卡的存储是在 Python 对象列表中完成的。

# The deck of cards - List of Objects
deck = []

# Loop for every type of suit
for suit in suits:

	# Loop for every type of card in a suit
	for card in cards:

		# Adding the card to the deck
		deck.append(Card(suit, card))

设置卡片图像

与图标图像类似,我们首先需要加载卡片图像,然后将它们呈现在游戏界面上。为此,我们需要一副牌中每张牌的图像。令人欣慰的是,它可以很容易地从互联网上获得。

这是卡片的样子:

Hi Lo Pygame Cards

The images for playing cards

正如我们所看到的,命名约定是必要的,因为当 Python 脚本从卡片组中取出卡片时,卡片将被加载。命名惯例很简单:牌值后跟花色的第一个字母。

# Load the card image
prev_card = pygame.image.load(r'./cards/card_cover.png')

# Scale the loaded image 
prev_card = pygame.transform.scale(prev_card , (100,160))

# Choose the starting card from the deck
current_card = random.choice(deck)

# Keep choosing until it is not the highest or lowest
while current_card.value == "A" or current_card.value == "K":
	current_card = random.choice(deck)

# Load the card image	
cur_card =  pygame.image.load(r'./cards/' + current_card.value + current_card.suit_type[0] + '.png')

# Scale the loaded card image
cur_card = pygame.transform.scale(cur_card , (100,160))

# Remove the card from the deck
deck.remove(current_card)

# Loading the card image
next_card =  pygame.image.load(r'./cards/card_cover.png')

# Scaling the loaded image
next_card = pygame.transform.scale(next_card , (100,160))

放置原始图像文件可能会覆盖整个屏幕,因此,我们需要根据屏幕的宽度和高度来缩放图像。在 PyGame 中,这是通过scale()函数完成的,该函数接受一个输入图像和目标大小进行变换。

根据 Hi-Lo 游戏的规则,起始牌不能是最高或最低的牌,即分别是 a 或 k。我们运行一个循环直到从一副牌中选出的牌不是他们中的任何一个。

在卡片被选取后,我们需要加载特定的卡片图像以显示在屏幕上。这是由load()函数完成的,该函数接受图像名称后面的相对路径。


声明游戏变量

有几个游戏变量是游戏所必需的:

# Number of chances left
chances = 3

# The current score
score = 0

# User's choice initialized
choice = -1

# Used to stop game functioning, if True
over = False

上述变量中的主要焦点是用于停止游戏运行的over变量,例如,按钮点击。


游戏循环

游戏循环是代码中永远运行的部分,负责维护游戏窗口、组件和游戏逻辑。

# The GAME LOOP
while True:

	# Tracking the mouse movements
	mouse = pygame.mouse.get_pos()

游戏循环的第一个议程是跟踪鼠标的移动。这对于识别鼠标点击的位置和其他事情来说很方便。

get_pos()函数以(X 轴Y 轴)的形式返回鼠标在屏幕上的位置的 Python 元组。


处理 PyGame 事件

PyGame 开发中最重要的部分是处理游戏窗口中发生的事件。

PyGame 将发生的每个事件注册到一个事件对象列表中。我们将遍历每个事件对象来处理它。

# Loop events occuring inside the game window 
for event in pygame.event.get():

	# Qutting event
	if event.type == pygame.QUIT:
		pygame.quit()
		quit()

	# Left-mouse clicked event	
	if not over and event.type == pygame.MOUSEBUTTONDOWN:

		# Clicked on the High Button 
		if 220 <= mouse[0] <= 220+125 and 370 <= mouse[1] <= 370+60: 
			choice = 1

		# Clicked on the Low Button	
		if 460 <= mouse[0] <= 460+120 and 370 <= mouse[1] <= 370+60:
			choice = 0

我们检查事件的类型并执行所需的任务。必须指出的是,在退出 python 代码之前,我们退出了 PyGame 模块。


游戏逻辑

游戏逻辑包括:

  • 将当前卡片放入前一张卡片的位置。
  • 从这副牌中选择一张新牌。
  • 从这副牌中取出所选的牌。
  • 检查新卡是高了还是低了。
  • 如果牌越低,那么剩下的机会就越少。
  • 如果牌更高,则增加分数。
  • 重置玩家选择
# If a valid choice, the game logic	
if choice != -1:	

	# Change current card to previous
	previous_card = current_card
	prev_card = pygame.image.load(r'./cards/' + previous_card.value + previous_card.suit_type[0] + '.png')
	prev_card = pygame.transform.scale(prev_card , (100,160))

	# Set up the current card
	current_card = random.choice(deck)
	deck.remove(current_card)

	cur_card =  pygame.image.load(r'./cards/' + current_card.value + current_card.suit_type[0] + '.png')
	cur_card = pygame.transform.scale(cur_card , (100,160))

	# Check the result, that is, High or Low
	if cards_values[current_card.value] > cards_values[previous_card.value]:
		result = 1
	elif cards_values[current_card.value] < cards_values[previous_card.value]:
		result = 0
	else:
		result = -1	 	

	# Manage the game variables
	if result == -1:
		continue
	elif result == choice:
		score = score + 1
	else:
		chances = chances - 1		

	# End the game if chances are finished
	if chances == 0:
		over = True	

	# Reset the choice
	choice = -1	


按钮动画

使用跟踪的鼠标移动,每当鼠标悬停在按钮上时,我们可以创建一个按钮动画。

# Manage the button hovering animation
if 220 <= mouse[0] <= 220+125 and 370 <= mouse[1] <= 370+60: 
	pygame.draw.rect(screen,LIGHT_GREEN,[220,370,125,60])  
else: 
	pygame.draw.rect(screen,GREEN,[220,370,125,60]) 

if 460 <= mouse[0] <= 460+120 and 370 <= mouse[1] <= 370+60: 
	pygame.draw.rect(screen,LIGHT_RED,[460,370,120,60]) 
else: 
	pygame.draw.rect(screen,RED,[460,370,120,60]) 

在这段代码中,我们首先检查鼠标位置是否在按钮内部。如果是,那么我们在屏幕上用比原来的按钮颜色更浅的颜色画一个矩形,否则就是原来的按钮颜色。

这里的pygame.draw.rect()函数带三个参数,显示面(我们的游戏窗口),矩形的颜色,盒子的尺寸【起始 x 坐标,起始 y 坐标,宽度,高度】。


显示记分板

我们需要显示一个记分牌,上面有当前的分数和剩余的机会数。

# Displaying scoreboard
pygame.draw.rect(screen, WHITE, [270, 40, 255, 90])
score_text = small_font.render("Score = "+str(score), True, BLACK)
score_text_rect = score_text.get_rect()
score_text_rect.center = (WIDTH//2, 70)

chances_text = small_font.render("Chances = "+str(chances), True, BLACK)
chances_text_rect = chances_text.get_rect()
chances_text_rect.center = (WIDTH//2, 100)	

我们使用与按钮文本相似的文本呈现方式。


设置整个显示

在所有的显示组件初始化之后,我们可以使用blit()函数将它们放置在我们的游戏窗口上。

# Setting up all the buttons, images and texts on the screen
screen.blit(high_button, high_button_rect)
screen.blit(low_button, low_button_rect)
screen.blit(score_text, score_text_rect)
screen.blit(chances_text, chances_text_rect)
screen.blit(prev_card, (MARGIN_LEFT,MARGIN_TOP))
screen.blit(cur_card, (MARGIN_LEFT+120, MARGIN_TOP))
screen.blit(next_card, (MARGIN_LEFT+240, MARGIN_TOP))	

blit()函数接收游戏对象,如图像或文本,以及其放置的位置。


管理最终游戏

在游戏逻辑中,当机会结束时,over变量被改为True。这里显示了它的效果。

# If the game is finished, display the final score
if over == True:
	pygame.draw.rect(screen, WHITE, [270, 40, 255, 90])
	score_text = small_font.render("Final Score = "+str(score), True, BLACK)
	score_text_rect = score_text.get_rect()
	score_text_rect.center = (WIDTH//2, 85)
	screen.blit(score_text, score_text_rect)

游戏结束后,我们在记分牌上显示最终得分。


更新游戏显示

最后要做的事情是在游戏循环结束时更新游戏显示。

# Update the display after each game loop
pygame.display.update()


完整的代码

import pygame
import random

# Card class definition
class Card:
	def __init__(self, suit_type, value):
		self.suit_type = suit_type
		self.value = value

if __name__ == '__main__':

	# Margins
	MARGIN_LEFT = 230
	MARGIN_TOP = 150

	# WINDOW SIZE
	WIDTH = 800
	HEIGHT = 600

	# COLORS
	BLACK = (0, 0, 0)
	WHITE = (255, 255, 255)
	GRAY = (110, 110, 110)
	GREEN = (0, 255, 0)
	LIGHT_GREEN = (0, 120, 0)
	RED = (255, 0, 0)
	LIGHT_RED = (120, 0, 0)

	# The type of suit
	suits = ["Spades", "Hearts", "Clubs", "Diamonds"]

	# The type of card
	cards = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]

	# The card value
	cards_values = {"A": 1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":11, "Q":12, "K":13}

	# The deck of cards - List of Objects
	deck = []

	# Loop for every type of suit
	for suit in suits:

		# Loop for every type of card in a suit
		for card in cards:

			# Adding the card to the deck
			deck.append(Card(suit, card))

	# Initializing PyGame
	pygame.init()

	# Setting up the screen and background
	screen = pygame.display.set_mode((WIDTH, HEIGHT))
	screen.fill(GRAY)

	# Setting up caption
	pygame.display.set_caption("Hi-Lo Game")

	# Loading image for the icon
	icon = pygame.image.load('icon.jpeg')

	# Setting the game icon
	pygame.display.set_icon(icon)

	# Types of fonts to be used
	small_font = pygame.font.Font(None, 32)
	large_font = pygame.font.Font(None, 50)

	# Hign and Low Game Buttons
	high_button = large_font.render("HIGH", True, WHITE)

	# Gets_rectangular covering of text
	high_button_rect = high_button.get_rect()

	# Places the text
	high_button_rect.center = (280, 400)

	low_button = large_font.render("LOW", True, WHITE)
	low_button_rect = low_button.get_rect()
	low_button_rect.center = (520, 400)

	# Load the card image
	prev_card = pygame.image.load(r'./cards/card_cover.png')

	# Scale the loaded image 
	prev_card = pygame.transform.scale(prev_card , (100,160))

	# Choose the starting card from the deck
	current_card = random.choice(deck)

	# Keep choosing until it is not the highest or lowest
	while current_card.value == "A" or current_card.value == "K":
		current_card = random.choice(deck)

	# Load the card image	
	cur_card =  pygame.image.load(r'./cards/' + current_card.value + current_card.suit_type[0] + '.png')

	# Scale the loaded card image
	cur_card = pygame.transform.scale(cur_card , (100,160))

	# Remove the card from the deck
	deck.remove(current_card)

	# Loading the card image
	next_card =  pygame.image.load(r'./cards/card_cover.png')

	# Scaling the loaded image
	next_card = pygame.transform.scale(next_card , (100,160))

	# Number of chances left
	chances = 3

	# The current score
	score = 0

	# User's choice initialized
	choice = -1

	# Used to stop game functioning, if True
	over = False

	# The GAME LOOP
	while True:

		# Tracking the mouse movements
		mouse = pygame.mouse.get_pos()

		# Loop events occuring inside the game window 
		for event in pygame.event.get():

			# Qutting event
			if event.type == pygame.QUIT:
				pygame.quit()
				quit()

			# Left-mouse clicked event	
			if not over and event.type == pygame.MOUSEBUTTONDOWN:

				# Clicked on the High Button 
				if 220 <= mouse[0] <= 220+125 and 370 <= mouse[1] <= 370+60: 
					choice = 1

				# Clicked on the Low Button	
				if 460 <= mouse[0] <= 460+120 and 370 <= mouse[1] <= 370+60:
					choice = 0

				# Finish the game if the deck is finished
				if len(deck) == 1:
					over = True	

				# If a valid choice, the game logic	
				if choice != -1:	

					# Change current card to previous
					previous_card = current_card
					prev_card = pygame.image.load(r'./cards/' + previous_card.value + previous_card.suit_type[0] + '.png')
					prev_card = pygame.transform.scale(prev_card , (100,160))

					# Set up the current card
					current_card = random.choice(deck)
					deck.remove(current_card)

					cur_card =  pygame.image.load(r'./cards/' + current_card.value + current_card.suit_type[0] + '.png')
					cur_card = pygame.transform.scale(cur_card , (100,160))

					# Check the result, that is, High or Low
					if cards_values[current_card.value] > cards_values[previous_card.value]:
						result = 1
					elif cards_values[current_card.value] < cards_values[previous_card.value]:
						result = 0
					else:
						result = -1	 	

					# Manage the game variables
					if result == -1:
						continue
					elif result == choice:
						score = score + 1
					else:
						chances = chances - 1		

					# End the game if chances are finished
					if chances == 0:
						over = True	

					# Reset the choice
					choice = -1	

		# Manage the button hovering animation
		if 220 <= mouse[0] <= 220+125 and 370 <= mouse[1] <= 370+60: 
			pygame.draw.rect(screen,LIGHT_GREEN,[220,370,125,60])  
		else: 
			pygame.draw.rect(screen,GREEN,[220,370,125,60]) 

		if 460 <= mouse[0] <= 460+120 and 370 <= mouse[1] <= 370+60: 
			pygame.draw.rect(screen,LIGHT_RED,[460,370,120,60]) 
		else: 
			pygame.draw.rect(screen,RED,[460,370,120,60]) 

		# Displaying scoreboard
		pygame.draw.rect(screen, WHITE, [270, 40, 255, 90])
		score_text = small_font.render("Score = "+str(score), True, BLACK)
		score_text_rect = score_text.get_rect()
		score_text_rect.center = (WIDTH//2, 70)

		chances_text = small_font.render("Chances = "+str(chances), True, BLACK)
		chances_text_rect = chances_text.get_rect()
		chances_text_rect.center = (WIDTH//2, 100)	

		# Setting up all the buttons, images and texts on the screen
		screen.blit(high_button, high_button_rect)
		screen.blit(low_button, low_button_rect)
		screen.blit(score_text, score_text_rect)
		screen.blit(chances_text, chances_text_rect)
		screen.blit(prev_card, (MARGIN_LEFT,MARGIN_TOP))
		screen.blit(cur_card, (MARGIN_LEFT+120, MARGIN_TOP))
		screen.blit(next_card, (MARGIN_LEFT+240, MARGIN_TOP))	

		# If the game is finished, display the final score
		if over == True:
			pygame.draw.rect(screen, WHITE, [270, 40, 255, 90])
			score_text = small_font.render("Final Score = "+str(score), True, BLACK)
			score_text_rect = score_text.get_rect()
			score_text_rect.center = (WIDTH//2, 85)
			screen.blit(score_text, score_text_rect)

		# Update the display after each game loop
		pygame.display.update()


结论

使用 PyGame 创建我们自己的 Hi-Lo 游戏似乎是一项简单的任务。我们希望这个教程能成为读者未来 PyGame 测试和冒险的基础。

感谢您的阅读。如有疑问或建议,欢迎在下面发表评论。