如何在 pygame 中制作咀嚼效果

How to make a chomping effect in pygame

这是我的代码:

    import pygame

pygame.init()
pygame.display.set_caption("Pac-Man")

# Sets the size of the screen via (WIDTH, HEIGHT)
SCREEN_WIDTH = 478
SCREEN_HEIGHT = 608
# Speed of Characters
SPEED = 1
# Frames per second, how fast the game runs
FPS = 50
# Colors (RED,GREEN,BLUE)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)

HIGH_SCORE = 0

# Sets the WIDTH and HEIGHT of the window
WINDOW = (SCREEN_WIDTH, SCREEN_HEIGHT)
# Displays the screen
SCREEN = pygame.display.set_mode(WINDOW)
CLOCK = pygame.time.Clock()

PacManStartSurface = pygame.transform.scale(pygame.image.load 
                                           ("PacManStart.png"), (24, 24))
PacManStartSurface.convert()
PacManStartRect = PacManStartSurface.get_rect(topleft = 
                                             (((SCREEN_WIDTH - 25) // 2),
                                             (SCREEN_HEIGHT + 144) // 2))

PacManSurface = pygame.transform.scale(pygame.image.load 
                                      ("PacManRight.png"), (24, 24))
PacManSurface.convert()
PacManRect = PacManStartSurface.get_rect(topleft = 
                                        (((SCREEN_WIDTH - 125) // 2),
                                        (SCREEN_HEIGHT + 144) // 2))

CurrentSurface = PacManStartSurface
CurrentRect = PacManStartRect 

BackgroundSurface = pygame.image.load("Background.png").convert()

class PacMan():
    def __init__(self):
        self.LIVES = 3
        
class PowerUp(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.scale(pygame.image.load("PowerUp.png")
                                            .convert(), (23, 23))
        self.mask = pygame.mask.from_surface(self.image)
        
class YellowGhost(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.scale(pygame.image.load
                                           ("YellowGhost.png")
                                           .convert(), (23, 23))
        self.rect = self.image.get_rect(topleft = (235, 347))
        self.mask = pygame.mask.from_surface(self.image)
        
class RedGhost(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.scale(pygame.image.load("RedGhost.png")
                                           .convert(), (23, 23))
        self.rect = self.image.get_rect(topleft = (235, 347))
        self.mask = pygame.mask.from_surface(self.image)
        
class BlueGhost(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.scale(pygame.image.load("BlueGhost.png")
                                           .convert(), (23, 23))
        self.rect = self.image.get_rect(topleft = (235, 347))
        self.mask = pygame.mask.from_surface(self.image)
        
class PinkGhost(pygame.sprite.Sprite):
    def __init__(self):        
        self.image = pygame.transform.scale(pygame.image.load("PinkGhost.png")
                                           .convert(), (23, 23))
        self.rect = self.image.get_rect(topleft = (235, 347))
        self.mask = pygame.mask.from_surface(self.image)
        
class Maze():
    def __init__(self):
        self.DOTS = []
        self.WALLS = []
        self.BLOCK_WIDTH = 25
        self.BLOCK_HEIGHT = 25
        self.MAZE_OFFSET_X = 0
        self.MAZE_OFFSET_Y = 50
        self.MARGIN = 3
        # 0 - Dots
        # 1 - Walls
        # 2 - Power Up
        # 3 - Empty Space
        # 4 - Ghosts
        self.MATRIX = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], \
                      [1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1], \
                      [1,2,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,2,1], \
                      [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], \
                      [1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,0,1], \
                      [1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1], \
                      [1,1,1,1,0,1,1,1,3,1,3,1,1,1,0,1,1,1,1], \
                      [3,3,3,1,0,1,3,3,3,3,3,3,3,1,0,1,3,3,3], \
                      [1,1,1,1,0,1,3,1,1,1,1,1,3,1,0,1,1,1,1], \
                      [0,0,0,0,0,3,3,1,4,4,4,1,3,3,0,0,0,0,0], \
                      [1,1,1,1,0,1,3,1,1,1,1,1,3,1,0,1,1,1,1], \
                      [3,3,3,1,0,1,3,3,3,3,3,3,3,1,0,1,3,3,3], \
                      [1,1,1,1,0,1,3,1,1,1,1,1,3,1,0,1,1,1,1], \
                      [1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1], \
                      [1,2,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,2,1], \
                      [1,0,0,1,0,0,0,0,0,3,0,0,0,0,0,1,0,0,1], \
                      [1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1], \
                      [1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1], \
                      [1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1], \
                      [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], \
                      [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
                      
        # BackgroundImage(X, Y, WIDTH, HEIGHT)
        self.MAZE_X = self.BLOCK_WIDTH * (len(self.MATRIX[0]) 
                      + self.MAZE_OFFSET_X)
        self.MAZE_Y = self.BLOCK_HEIGHT * (len(self.MATRIX)
                      + self.MAZE_OFFSET_Y)
        self.MAZE_WIDTH = self.BLOCK_WIDTH * len(self.MATRIX[0])
        self.MAZE_HEIGHT = self.BLOCK_HEIGHT * len(self.MATRIX) 

    def DrawMaze(self, MazeSurface):
        for ROW in range(len(self.MATRIX)):
            for COLUMN in range(len(self.MATRIX[0])):
                # Only saves the position of each dot
                if self.MATRIX[ROW][COLUMN] == 0:
                    self.DOTS.append([(self.BLOCK_WIDTH * COLUMN),
                                     (self.BLOCK_HEIGHT * ROW), 4, 4])
                if self.MATRIX[ROW][COLUMN] == 1:
                    self.WALLS.append(pygame.draw.rect(MazeSurface, WHITE,
                                     [((self.BLOCK_WIDTH) * COLUMN),
                                     ((self.BLOCK_HEIGHT) * ROW), 
                                     self.BLOCK_WIDTH, self.BLOCK_HEIGHT]))

class Main(Maze):
    def __init__(self):
        # Inherits Maze class
        Maze.__init__(self)
        self.DIRECTION = ""
        self.SCORE = 0
        
    def Movement(self):
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT] and not key[pygame.K_UP] \
                              and not key[pygame.K_DOWN]:
            self.DIRECTION = "LEFT"
        elif key[pygame.K_RIGHT] and not key[pygame.K_UP] \
                               and not key[pygame.K_DOWN]:
            self.DIRECTION = "RIGHT"
        elif key[pygame.K_UP] and not key[pygame.K_LEFT] \
                            and not key[pygame.K_RIGHT]:
            self.DIRECTION = "UP"
        elif key[pygame.K_DOWN] and not key[pygame.K_LEFT] \
                              and not key[pygame.K_RIGHT]:
            self.DIRECTION = "DOWN"
            
    def ContinueMovement(self):
        if self.DIRECTION == "LEFT":
            CurrentRect.x -= SPEED
            self.WallDetection(-1, 0, CurrentRect)
        if self.DIRECTION == "RIGHT":
            CurrentRect.x += SPEED
            self.WallDetection(1, 0, CurrentRect)
        if self.DIRECTION == "UP":
            CurrentRect.y -= SPEED
            self.WallDetection(0, -1, CurrentRect)
        if self.DIRECTION == "DOWN":
            CurrentRect.y += SPEED
            self.WallDetection(0, 1, CurrentRect)

    def ChangeImage(self):
        global CurrentSurface
        if self.DIRECTION == "LEFT":
            CurrentSurface = pygame.transform.rotate(PacManSurface, 180)
        if self.DIRECTION == "RIGHT":
            CurrentSurface = PacManSurface
        if self.DIRECTION == "UP":
            CurrentSurface = pygame.transform.rotate(PacManSurface, 90)
        if self.DIRECTION == "DOWN":
            CurrentSurface = pygame.transform.rotate(PacManSurface, 270)
        
    def Teleport(self):
        if CurrentRect.right < 0:
            CurrentRect.right = SCREEN_WIDTH + 20
        if CurrentRect.left > SCREEN_WIDTH:
            CurrentRect.right = 0
    
    def WallDetection(self, x, y, CurrentRect):
        CurrentRect.right += x
        for WALL in self.WALLS:
            COLLIDE = CurrentRect.colliderect(WALL)
            if COLLIDE:
                if x < 0: 
                    CurrentRect.left = WALL.right
                elif x > 0:
                    CurrentRect.right = WALL.left
                break
        
        CurrentRect.top += y
        for WALL in self.WALLS:
            COLLIDE = CurrentRect.colliderect(WALL)
            if COLLIDE:
                if y < 0:
                    CurrentRect.top = WALL.bottom
                if y > 0:
                    CurrentRect.bottom = WALL.top
                break
            
    def EatDots(self):
        for ROW in range(len(self.MATRIX)):
            for COLUMN in range(len(self.MATRIX[0])):
                for DOT in self.DOTS:
                    CHOMP = CurrentRect.colliderect(DOT)
                    if CHOMP:
                        self.DOTS.remove(DOT)
                        self.MATRIX[ROW][COLUMN] = 3
                        self.SCORE += 10
                        return str(self.SCORE)
                        
    def DrawDots(self):
        for POSITION in self.DOTS:
            X = POSITION[0] + 13
            Y = POSITION[1] + 13
            WIDTH = POSITION[2]
            HEIGHT = POSITION[3]
            pygame.draw.circle(MazeSurface, YELLOW, (X, Y), 
                               WIDTH // 2, HEIGHT // 2)
            
    def EatGhosts(self):
        pass
    
    def EatPowerUp(self):
        pass
                
    def ShowText(self):
        Font = pygame.font.Font("emulogic.ttf", 15)
        OneUpText = Font.render("1UP", True, WHITE)
        OneUpTextRect = OneUpText.get_rect(center = (70, 10))
        OneUpScoreText = Font.render("00", True, WHITE)
        UpdateScore = Main.EatDots(self)
        if int(self.SCORE) > 0:
            OneUpScoreText = Font.render(UpdateScore, True, WHITE)
        OneUpScoreRect = OneUpScoreText.get_rect(center =
                                                ((SCREEN_WIDTH - 290) 
                                                // 2, 26))
        HighScoreText = Font.render("High Score", True, WHITE)
        HighScoreTextRect = HighScoreText.get_rect(center = 
                                                  (SCREEN_WIDTH // 2, 10))
        HighScoreNumber = Font.render("00", True, WHITE)
        HighScoreNumberRect = HighScoreNumber.get_rect(center = 
                                                      ((SCREEN_WIDTH + 90) 
                                                      // 2, 26))
        
        SCREEN.blit(OneUpText, OneUpTextRect)
        SCREEN.blit(OneUpScoreText, OneUpScoreRect)
        SCREEN.blit(HighScoreText, HighScoreTextRect)
        SCREEN.blit(HighScoreNumber, HighScoreNumberRect)
        
    def PacManBite(self):
        SCREEN.blit(PacManStartSurface, PacManStartRect)
        pygame.display.update()
    
Player = Main()

BackgroundSurface = pygame.transform.scale(BackgroundSurface, 
                                          (Player.MAZE_WIDTH, 
                                           Player.MAZE_HEIGHT))
BackgroundRect = BackgroundSurface.get_rect()

MazeSurface = pygame.Surface((Player.MAZE_WIDTH, Player.MAZE_HEIGHT))
MazeRect = MazeSurface.get_rect(topleft = (Player.MAZE_OFFSET_X, 
                                           Player.MAZE_OFFSET_Y))
Player.DrawMaze(MazeSurface)

'''
pregame = True
while pregame:
    if key button pressed:
        pregame = False
    run = True
'''

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            Player.Movement()
            Player.ChangeImage()
            Player.Teleport()   
            
    Player.ContinueMovement()
    MazeSurface.blit(BackgroundSurface, BackgroundRect) 
    Player.DrawDots() 
    MazeSurface.blit(CurrentSurface, CurrentRect)
    Player.PacManBite()
    SCREEN.blit(MazeSurface, MazeRect)
    #SCREEN.fill(BLACK)
    Player.ShowText()
    pygame.display.update()
    CLOCK.tick(FPS)
    
pygame.quit()

所以我基本上有两个表面,主屏幕和 MazeSurface,我将迷宫、圆点、pacman 和背景 blit,然后将它们 blit 到主屏幕。所以出于某种原因,只有一个 pacman 图像被 blitted,这意味着我有 PacManStartSurface,它是闭着嘴的 PacMan,所以只有一个圆圈和 PacManSurface,它是 PacMan 面向右侧或我进入的任何方向,基本上我想重叠它们,以便 PacMan 图像重叠并产生咬住或咬住点的效果

因此您可以像这样更改背景图片的比例:

backgroundImage = pygame.transform.scale(backgroundImage, (width, height))

为了运动。你会加速,因为正在调用 Movement 函数和 ContinueMovement 函数并且它们都在移动你所以我让 Movement 函数不移动你。

    def Movement(self):
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT]:
            self.DIRECTION = "LEFT"
        elif key[pygame.K_RIGHT]:
            self.DIRECTION = "RIGHT"
        elif key[pygame.K_UP]:
            self.DIRECTION = "UP"
        elif key[pygame.K_DOWN]:
            self.DIRECTION = "DOWN"
        
    def ContinueMovement(self):
        if self.DIRECTION == "LEFT":
            CurrentRect.x -= SPEED
            self.WallDetection(-1, 0, CurrentRect)
        if self.DIRECTION == "RIGHT":
            CurrentRect.x += SPEED
            self.WallDetection(1, 0, CurrentRect)
        if self.DIRECTION == "UP":
            CurrentRect.y -= SPEED
            self.WallDetection(0, -1, CurrentRect)
        if self.DIRECTION == "DOWN":
            CurrentRect.y += SPEED
            self.WallDetection(0, 1, CurrentRect)

你可以在这里看到我用 elif 语句

替换了长 if 语句

我还用这个替换了 Main.WallDetection(self, 0, 1, CurrentRect)self.WallDetection(0, 1, CurrentRect) 因为它更清晰,更容易理解

您还应该考虑让 Movement 函数接受这样的事件:def Movement(self, event): 并使用事件来检查按钮按下,而不是像这样:if event.button == PYGAME.K_LEFT.

要绘制背景使其与迷宫的大小完全相同,您需要执行以下操作:

mazeHeight = (maze.BLOCK_HEIGHT + maze.MARGIN) * len(maze.MATRIX)
mazeWidth = (maze.BLOCK_WIDTH + maze.MARGIN) * len(maze.MATRIX[0])
backgroundImage = pygame.transform.scale(backgroundImage, (mazeWidth, mazeHeight))

首先你得到 mazeHeight,这是通过将行数乘以每个块的高度来完成的

然后你得到 mazeWidth 通过将其中一行中的列数(哪一行无关紧要)乘以每个块的宽度

最后,您将背景图像的大小设置为 mazeWidthmazeHeight

请注意,变量 maze 是 class Maze 的一个实例,它是这样构成的:maze = Maze()

Mazeclass应该是这样的:

class Maze:
    def __init__(self):
        self.DOTS = []
        self.WALLS = []
        self.BLOCK_SIZE = 22
        self.ROWS = 21
        self.COLUMNS = 20
        self.MARGIN = 3
        self.OFFSET_Y = 40
        # 0 - Dots
        # 1 - Walls
        # 2 - Power Up
        # 3 - Empty Space
        # 4 - Ghosts
        self.MATRIX = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], \
                      [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1], \
                      [1, 2, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 2, 1], \
                      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], \
                      [1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1], \
                      [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1], \
                      [1, 1, 1, 1, 0, 1, 1, 1, 3, 1, 3, 1, 1, 1, 0, 1, 1, 1, 1], \
                      [3, 3, 3, 1, 0, 1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 1, 3, 3, 3], \
                      [1, 1, 1, 1, 0, 1, 3, 1, 1, 1, 1, 1, 3, 1, 0, 1, 1, 1, 1], \
                      [0, 0, 0, 0, 0, 3, 3, 1, 4, 4, 4, 1, 3, 3, 0, 0, 0, 0, 0], \
                      [1, 1, 1, 1, 0, 1, 3, 1, 1, 1, 1, 1, 3, 1, 0, 1, 1, 1, 1], \
                      [3, 3, 3, 1, 0, 1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 1, 3, 3, 3], \
                      [1, 1, 1, 1, 0, 1, 3, 1, 1, 1, 1, 1, 3, 1, 0, 1, 1, 1, 1], \
                      [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1], \
                      [1, 2, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 2, 1], \
                      [1, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 1], \
                      [1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1], \
                      [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1], \
                      [1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1], \
                      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], \
                      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

    def DrawGrid(self):
        for ROW in range(self.ROWS):
            for COLUMN in range(self.COLUMNS - 1):
                if self.MATRIX[ROW][COLUMN] == 0:
                    self.DOTS.append(pygame.draw.circle(SCREEN, YELLOW,
                                                        (((self.MARGIN + self.BLOCK_SIZE) * COLUMN) + self.BLOCK_SIZE/2,
                                                         ((self.MARGIN + self.BLOCK_SIZE) * ROW) + self.OFFSET_Y + self.BLOCK_SIZE/2),
                                                        2))
                if self.MATRIX[ROW][COLUMN] == 1:
                    self.WALLS.append(pygame.draw.rect(SCREEN, WHITE,
                                                       (((self.MARGIN + self.BLOCK_SIZE) * COLUMN),
                                                        ((self.MARGIN + self.BLOCK_SIZE) * ROW) + self.OFFSET_Y,
                                                        self.BLOCK_SIZE, self.BLOCK_SIZE)))

我创建了一个 self.OFFSET_Y 变量,并添加了 self.BLOCK_SIZE/2 以使点位于块的中间

然后我像这样更改了 BackgroundSurface 的比例:

BackgroundSurface = pygame.transform.scale(BackgroundSurface, ((Player.BLOCK_SIZE + Player.MARGIN) * len(Player.MATRIX[0]), (Player.BLOCK_SIZE + Player.MARGIN) * len(Player.MATRIX)))

我将块大小与每个块之间的距离相加,然后将它们乘以块的数量以获得所有块的总大小

编辑:

所以我把 import time 放在脚本的顶部,因为我用它来制作 pacman bite

并且我在Mainclass中的__init__中添加了三个变量,像这样:

def __init__(self):
    # Inherits Maze class
    Maze.__init__(self)
    self.timeBetweenBites = 0.2
    self.lastBiteTime = time.time()
    self.mouthOpen = False
    self.DIRECTION = ""
    self.SCORE = 0

我添加了 timeBetweenBites,您可以更改它以使 Pacman 咬得更慢或更快

我添加了 lastBitTime 每次 Pacman 咬时都设置为当前时间(更改其外观)

然后我添加了mouthOpen来知道Pacman's嘴巴是张着还是闭着

我也像这样更改了 changeImage 函数:

def ChangeImage(self):
    global CurrentSurface
    if self.DIRECTION == "LEFT":
        CurrentSurface = pygame.transform.rotate(CurrentSurface, 180)
    if self.DIRECTION == "RIGHT":
        CurrentSurface = CurrentSurface
    if self.DIRECTION == "UP":
        CurrentSurface = pygame.transform.rotate(CurrentSurface, 90)
    if self.DIRECTION == "DOWN":
        CurrentSurface = pygame.transform.rotate(CurrentSurface, 270)

我所做的只是将 PacManSurface 替换为 CurrentSurface

我把 PacManBite 函数改成这样:

def PacManBite(self):
    global CurrentSurface

    if time.time() - self.lastBiteTime >= self.timeBetweenBites:
        self.lastBiteTime = time.time()

        if self.mouthOpen:
            CurrentSurface = PacManStartSurface
        else:
            CurrentSurface = PacManSurface

        self.mouthOpen = not self.mouthOpen

        if self.DIRECTION == "LEFT":
            CurrentSurface = pygame.transform.rotate(CurrentSurface, 180)
        if self.DIRECTION == "RIGHT":
            CurrentSurface = CurrentSurface
        if self.DIRECTION == "UP":
            CurrentSurface = pygame.transform.rotate(CurrentSurface, 90)
        if self.DIRECTION == "DOWN":
            CurrentSurface = pygame.transform.rotate(CurrentSurface, 270)

我在这里做的是检查当前时间减去我上次更改 Pacman 表面的时间是否大于或等于我想在更改 Pacman 表面之前花费的时间。如果那是真的。

我将上次更改 Pacman's Surface 的时间设置为当前时间,因为我现在将更改它

然后我检查吃豆人的嘴是否张开。如果是我关闭它,如果不是我打开它。然后我切换 Pacman 的 mouthOpen 变量,因为我刚刚切换了 Pacman 的表面

然后我将精灵的旋转设置为吃豆人的旋转,就这样。

我注意到您忘记将 pygame.sprite.Sprite.__init__(self) 放在 PinkGhost class 中并且脚本末尾的 pygame.quit 行几乎没有用

那应该有用,谢谢