如何控制角色在python / pygame中移动迷宫的速度?

时间:2017-01-10 23:23:39

标签: python pygame pacman

在过去的几天里,我一直在努力为学校项目重建Pacman游戏。到目前为止我只碰到了一个问题,就是如何用我到目前为止创建的代码来降低字符的速度。如果能找到解决问题的方法,我将不胜感激。

以下是重现我的问题的代码:

import pygame, sys
from pygame.locals import *
import random
import math
pygame.init()
FPS=30 
fpsClock=pygame.time.Clock()
screen=pygame.display.set_mode((608,800),0,32)

FILL=(0,162,232)
BLACK=(0,0,0)

pmsprite0=pygame.image.load('pmsprite0.png').convert()
pmsprite0.set_colorkey(FILL)

brick=pygame.image.load('brick.png').convert()
brick.set_colorkey(FILL)

def createMaze():  
    row1=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
    row2=[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1]
    row3=[1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,0,1]
    row4=[1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,0,1]
    row5=[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]
    row6=[1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,0,1]
    row7=[1,0,0,0,0,1,0,1,1,1,1,1,0,1,0,0,0,0,1]
    row8=[1,1,1,1,0,1,0,0,0,1,0,0,0,1,0,1,1,1,1]
    row9=[1,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1]
    row10=[1,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,1]
    row11=[1,1,1,1,0,1,0,1,0,0,0,1,0,1,0,1,1,1,1]
    row12=[1,1,1,1,0,1,0,1,0,0,0,1,0,1,0,1,1,1,1]
    row13=[0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0]
    row14=[1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1]
    row15=[1,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,1]
    row16=[1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1]
    row17=[1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1]
    row18=[1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1]
    row19=[1,0,0,0,0,1,1,1,0,1,0,1,1,1,0,0,0,0,1]
    row20=[1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1]
    row21=[1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,0,1]
    row22=[1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1]
    row23=[1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1]
    row24=[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]
    row25=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
    maze = [row1,row2,row3,row4,row5,row6,row7,row8,row9,row10,row11,row12,row13,row14,row15,row16,row17,row18,row19,row20,row21,row22,row23,row24,row25]
    return (maze)

def drawMaze(maze):
    for j in range(19):
        for i in range(25):
            x=j*32
            y=i*32
            if maze[i][j]==1:
                screen.blit(brick,(x,y))

def pacmanblit(x,y):
    screen.blit(pmsprite0,(x,y))

def movePacman(maze,row,column,direction,):
        if direction=='left'  and maze[row][column-1]==0:
            column=column-1
        if direction=='right' and maze[row][column+1]==0:
            column=column+1
        if direction=='up' and maze[row-1][column]==0:
            row=row-1
        if direction=='down' and maze[row+1][column]==0:
            row=row+1
        return row,column

row=19
column=9
maze=createMaze()
direction='none'
while True:
    screen.fill(BLACK)
    drawMaze(maze)
    pacmanblit(column*32,row*32)
    row,column=movePacman(maze,row,column,direction)

    pygame.display.update()
    for event in pygame.event.get():
        if event.type==QUIT:
            pygame.quit()
            sys.exit()
        keys=pygame.key.get_pressed()  
        if keys[K_LEFT] and maze[row][column-1]==0:
            direction='left'
        if keys[K_RIGHT] and maze[row][column+1]==0:
            direction='right'
        if keys[K_UP] and maze[row-1][column]==0:
            direction='up'
        if keys[K_DOWN] and maze[row+1][column]==0:
            direction='down'

    fpsClock.tick(FPS)

感谢。

1 个答案:

答案 0 :(得分:0)

我看到两种解决方案适用于您当前的代码:

  1. 由于您的游戏依赖于FPS而且没有动画,最简单的解决方案就是降低FPS。

  2. 另一种解决方案可能是创建一个计时器变量。

  3. 示例:

    time = 0
    while True:
        dt = clock.tick(FPS) / 1000  # 'dt' will be the amount of seconds since last loop.
        time += dt
    
        screen.fill(BLACK)
        ...
    
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
    
        # Don't have this code inside the event loop! You'll be checking the keys state
        # for every event in the loop, which is just unnecessary. You only need to do it once.
        if time > 1:  # Allows pacman to move once every second.
            time = 0  # Reset the timer.
            keys = pygame.key.get_pressed()  
            if keys[K_LEFT] and maze[row][column-1] == 0:
                direction = 'left'
            elif keys[K_RIGHT] and maze[row][column+1] == 0:  # Use 'elif'! Pacman can't move both left and right.
                direction = 'right'
            if keys[K_UP] and maze[row-1][column] == 0:
                direction = 'up'
            elif keys[K_DOWN] and maze[row+1][column] == 0:
                direction = 'down'
    
            ...
    

    稍后您可能希望改变游戏时间,这意味着您使用dt变量(每个循环所用的时间)计算移动速度,时间等,而不是按帧进行。我在下面有一个最小的例子,但它会要求你改变游戏的运作方式。

    导入并初始化

    每个模块都需要导入,pygame也不例外。虽然我们需要为pygame中所有导入的模块调用函数pygame.init()来正确初始化。如果我们忘记这一点,一些模块将无法运作。该函数还返回所有成功和失败初始化的元组(如果模块无法初始化,则不会引发错误)。

    import pygame
    successes, failures = pygame.init()
    print("{0} successes and {1} failures".format(successes, failures))
    

    创造必需品

    我们还需要创建一个显示器。 Pygame已经创建了一个(隐藏)显示,所以我们需要做的就是设置显示模式(在这个例子中我们只设置分辨率)。创建一个时钟以确保我们的程序以固定的速度更新也是一个好主意(否则它会根据计算机的速度以不同的速度运行)。

    screen = pygame.display.set_mode((720, 480))  # Notice the tuple! It's not 2 arguments.
    clock = pygame.time.Clock()
    FPS = 60  # This variable will define how many frames we update per second.
    

    为了稍后在我们的代码中可读性,我们将创建两个颜色常量,它们代表红色,绿色和蓝色(RGB)的元组。值从0(无光)到255(全光)。

    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    

    在pygame中,我们通常使用 Surface 来表示对象的外观,使用 Rect (矩形)来表示对象的位置。 Surface 就像一张包含颜色或图像的空白纸。如果您正在创建一个类,则应将属性命名为 image rect ,因为许多函数将查找并使用这些属性。这些类可以通过继承pygame.sprite.Sprite类而受益,原因是您可以阅读here

    rect = pygame.Rect((0, 0), (32, 32))  # First tuple is position, second is size.
    image = pygame.Surface((32, 32))  # The tuple represent size.
    image.fill(WHITE)  # We fill our surface with a nice white color (by default black).
    

    游戏循环

    现在我们为游戏循环设置了所有内容。这是一个循环,将在整个游戏中运行,我们处理事件并更新屏幕和对象的位置。

    首先,我们确保我们的循环在给定的 FPS 处执行。我们定义了 FPS 并在程序开始时创建了我们的时钟。以下代码将确保我们的程序有足够的时间让我们的循环重复我们定义 FPS 的数量。在这个例子中,每秒60次。

    clock.tick(FPS)
    

    然后我们将处理事件。事件基本上是用户操作,例如移动鼠标或按键。 Pygame会将所有这些事件注册到我们通过调用pygame.event.get()得到的队列中。我们可以对此进行迭代,并检查是否存在我们想要处理的事件。事件有一个 type 属性,我们可以检查pygame模块中的常量来确定它是什么类型的事件。

    for event in pygame.event.get():
        if event.type == pygame.QUIT:  # The user pressed the close button in the top corner of the window.
            quit()
            # Close the program. Other methods like 'raise SystemExit' or 'sys.exit()'.
            # Calling 'pygame.quit()' won't close the program! It will just uninitialize the modules.
    

    我们还可以检查if event.type == pygame.KEYDOWN以查看用户是否按下了按键。在这种情况下,事件有一个属性 key ,我们可以检查它是否代表它所代表的键。

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_w:
                rect.move_ip(0, -2)  # Changes the rect's position.
            elif event.key == pygame.K_s:
                rect.move_ip(0, 2)
            elif event.key == pygame.K_a:
                rect.move_ip(-2, 0)
            elif event.key == pygame.K_d:
                rect.move_ip(2, 0)
    

    现在我们需要显示我们的图像。首先,我们可能希望从之前的渲染中清除屏幕。我们通过用黑色填充整个屏幕来实现这一点(删除代码以了解我们为什么要清除它)。然后我们需要 blit 我们的图像到屏幕上。 Blitting实际上意味着将图像复制到另一个表面(在我们的例子中是屏幕)。最后,我们翻转更新屏幕。

    当我们进行blitting时,我们实际上并没有向用户显示任何内容。想象一下它是一面的计算机而另一面是用户。计算机在屏幕的一侧绘制( blits ),向用户翻转,然后重复。

    screen.fill(BLACK)
    screen.blit(image, rect)
    pygame.display.update()  # Or 'pygame.display.flip()'.
    

    现在我们有一个基本游戏!非常无聊,是的,但必需品就在那里!将此与您当前的Python知识相结合,您应该能够创建一些非常棒的东西。

    完整代码

    import pygame
    successes, failures = pygame.init()
    print("{0} successes and {1} failures".format(successes, failures))
    
    
    screen = pygame.display.set_mode((720, 480))
    clock = pygame.time.Clock()
    FPS = 60  # Frames per second.
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    # RED = (255, 0, 0), GREEN = (0, 255, 0), BLUE = (0, 0, 255).
    
    rect = pygame.Rect((0, 0), (32, 32))
    image = pygame.Surface((32, 32))
    image .fill(WHITE)  
    
    while True:
        clock.tick(FPS)
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_w:
                    rect.move_ip(0, -2)
                elif event.key == pygame.K_s:
                    rect.move_ip(0, 2)
                elif event.key == pygame.K_a:
                    rect.move_ip(-2, 0)
                elif event.key == pygame.K_d:
                    rect.move_ip(2, 0)
    
        screen.fill(BLACK)
        screen.blit(image, rect)
        pygame.display.update()  # Or pygame.display.flip()
    

    略微改进游戏机制

    请注意,程序会检查我们何时按下该键,而不是当我们按住该键时。为了解决这个问题,我们可以引入一个 velocity 变量。我们可以创建一个播放器类来保持它更有条理。为了避免与帧相关的移动(如果我们将FPS更改为30,对象将以一半的速度移动),我们通过将时间间隔传递给可移动对象来引入时间相关的移动。

    import pygame
    
    successes, failures = pygame.init()
    print("Initializing pygame: {0} successes and {1} failures.".format(successes, failures))
    
    screen = pygame.display.set_mode((720, 480))
    clock = pygame.time.Clock()
    FPS = 60
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    
    
    class Player(pygame.sprite.Sprite):
        def __init__(self):
            super().__init__()
            self.image = pygame.Surface((32, 32))
            self.image.fill(WHITE)
            self.rect = self.image.get_rect()  # Get rect of some size as 'image'.
            self.velocity = [0, 0]
    
        def update(self):
            self.rect.move_ip(*self.velocity)
    
    
    player = Player()
    running = True
    while running:
        dt = clock.tick(FPS) / 1000  # Returns milliseconds between each call to 'tick'. The convert time to seconds.
        screen.fill(BLACK)  # Fill the screen with background color.
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_w:
                    player.velocity[1] = -200 * dt  # 200 pixels per second
                elif event.key == pygame.K_s:
                    player.velocity[1] = 200 * dt
                elif event.key == pygame.K_a:
                    player.velocity[0] = -200 * dt
                elif event.key == pygame.K_d:
                    player.velocity[0] = 200 * dt
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_w or event.key == pygame.K_s:
                    player.velocity[1] = 0
                elif event.key == pygame.K_a or event.key == pygame.K_d:
                    player.velocity[0] = 0
    
        player.update()
    
        screen.blit(player.image, player.rect)
        pygame.display.update()  # Or pygame.display.flip()
    
    print("Exited the game loop. Game will quit...")
    quit()  # Not actually necessary since the script will exit anyway.
    

    关于此代码,仍有许多事情需要改进。我建议您阅读理查德·琼斯的pygame tutorial和此talk以获取更深入的内容。