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

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

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

建议:如果这是您第一次创建游戏,请尝试在 Python 中使用命令行实现 hi-lo 游戏它更容易,并且会让您更好地了解游戏机制。但是,如果您已经熟悉 Python,请继续阅读!

卡牌的排名系统从 Ace(排名最低的卡牌)开始,到 King(排名最高的卡牌)结束。


Python 中的 GUI 高低游戏

PyGame:高低游戏

导入 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 时要记住的一件事是,大小参数作为两个值的元组传入:宽度和高度。

设置窗口后,我们使用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))

设置卡片图像

与图标图像类似,我们首先需要加载卡牌图像,然后再将其渲染到游戏表面上。为此,我们需要为这副牌中的每张牌提供一个图像。值得庆幸的是,它可以很容易地从互联网上获得。

这些卡片是这样的:

扑克牌的图像

正如我们所看到的,命名约定是必要的,因为当 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()获取输入图像和目标大小进行转换的函数来完成的。

根据高低游戏规则,起始牌不能是最高牌或最低牌,即 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()函数返回鼠标在屏幕上的位置的 Python 元组(X 轴,Y 轴)。


处理 PyGame 事件

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

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

# 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 试验和冒险的基础。

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