1

我正在开发我的第一个 Python 游戏,它类似于 Bejeweled,但有一些曲折。游戏运行良好,但我正在尝试做出一个不起作用的改变。

现在游戏有一个叫做光标的“选择栏”。玩家在两个形状之间向左、向右、向上和向下移动此条。当他们想要交换形状位置时,只需按空格键,图像就会交换位置。虽然这种格式效果很好,但我希望光标能够在水平和垂直之间切换。现在,条形图只是水平的,这使得从上方或下方交换形状是不可能的。

这是完整的代码:

import pygame, random, time, sys
from pygame.locals import *
import itertools
import os

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

SHAPE_WIDTH = 64                # Width of each shape (pixels).
SHAPE_HEIGHT = 64               # Height of each shape (pixels).
PUZZLE_COLUMNS = 10              # Number of columns on the board.
PUZZLE_ROWS = 11                # Number of rows on the board.
MARGIN = 118                     # Margin around the board (pixels).
WINDOW_WIDTH = PUZZLE_COLUMNS * SHAPE_WIDTH + 2 * MARGIN + 485
WINDOW_HEIGHT = PUZZLE_ROWS * SHAPE_HEIGHT + 2 * MARGIN - 150
FONT_SIZE = 60
TEXT_OFFSET = MARGIN + 950

# Map from number of matches to points scored.
MINIMUM_MATCH = 4
EXTRA_LENGTH_POINTS = .1
RANDOM_POINTS = .3
DELAY_PENALTY_SECONDS = 1
DELAY_PENALTY_POINTS = 0

FPS = 30
EXPLOSION_SPEED = 15            # In frames per second.
SPIN_SPEED = 15
REFILL_SPEED = 10               # In cells per second.

VERTICAL = False

class Cell(object):
"""
A cell on the board, with properties:
 `image` -- a `Surface` object containing the sprite to draw here.
 `offset` -- vertical offset in pixels for drawing this cell.
"""
def __init__(self, image):
    self.offset = 0.0
    self.image = image

def tick(self, dt):
    self.offset = max(0.0, self.offset - dt * REFILL_SPEED)

class Board(object):
"""
A rectangular board of cells, with properties:
`w` -- width in cells.
`h` -- height in cells.
`size` -- total number of cells.
`board` -- list of cells.
`matches` -- list of matches, each being a list of exploding cells.
`refill` -- list of cells that are moving up to refill the board.
`score` -- score due to chain reactions.
"""
def __init__(self, width, height):
    self.explosion = [pygame.image.load('images/explosion{}.png'.format(i))
                      for i in range(1, 7)]
    self.spin = [pygame.image.load('images/powerframe{}.png'.format(i))
                  for i in range (1, 12)]
    self.image_color = {}
    self.shapes = []
    self.rareshapes = []

    colors = 'red blue yellow'
    letters = 'acgtu'

    for c in colors.split():
        im = pygame.image.load('images/{}.png'.format(c))
        self.shapes.append(im)
        self.image_color[im] = c
        for l in letters:
            im = pygame.image.load('rareimages/{}{}.png'.format(c, l))
            self.rareshapes.append(im)
            self.image_color[im] = l

    self.background = pygame.image.load("images/bg.png")
    self.blank = pygame.image.load("images/blank.png")
    self.x = pygame.image.load("images/x.png")
    self.w = width
    self.h = height
    self.size = width * height
    self.board = [Cell(self.blank) for _ in range(self.size)]
    self.matches = []
    self.refill = []
    self.score = 0.0
    self.spin_time = 15


def randomize(self):
    """
    Replace the entire board with fresh shapes.
    """
    rareshapecount = 0
    for i in range(self.size):
        if rareshapecount <= 8:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount == 9:
            self.board[i] = Cell (random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <=22:
            self.board[i] = Cell(random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount == 23:
            self.board[i] = Cell(random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 26:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount == 27:
            self.board[i] = Cell (random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 40:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount == 41:
            self.board[i] = Cell (random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 45:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount == 46:
            self.board[i] = Cell (random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 57:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 59:
            self.board[i] = Cell (random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 70:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount == 71:
            self.board[i] = Cell (random.choice(self.rareshapes))
            rareshapecount = rareshapecount + 1

        elif rareshapecount <= 200:
            self.board[i] = Cell (random.choice(self.shapes))
            rareshapecount = rareshapecount + 1



def pos(self, i, j):
    """
    Return the index of the cell at position (i, j).
    """
    assert(0 <= i < self.w)
    assert(0 <= j < self.h)
    return j * self.w + i

def busy(self):
    """
    Return `True` if the board is busy animating an explosion or a
    refill and so no further swaps should be permitted.
    """
    return self.refill or self.matches

def tick(self, dt):
    """
    Advance the board by `dt` seconds: move rising blocks (if
    any); otherwise animate explosions for the matches (if any);
    otherwise check for matches.
    """
    if self.refill:
        for c in self.refill:
            c.tick(dt)
        self.refill = [c for c in self.refill if c.offset > 0]
        if self.refill:
            return
    elif self.matches:
        self.explosion_time += dt
        f = int(self.explosion_time * EXPLOSION_SPEED)
        if f < len(self.explosion):
            self.update_matches(self.explosion[f])
            return
        self.update_matches(self.blank)
        self.refill = list(self.refill_columns())
    self.explosion_time = 0
    self.matches = self.find_matches()

def draw(self, display):
    """
    Draw the board on the pygame surface `display`.
    """
    display.blit(self.background, (0, 0))
    for i, c in enumerate(self.board):
        display.blit(c.image,
                     (MARGIN + SHAPE_WIDTH * (i % self.w),
                      MARGIN + SHAPE_HEIGHT * (i // self.w - c.offset) - 68))
    display.blit(self.x, (995, 735))
    display.blit(self.x, (1112, 735))
    display.blit(self.x, (1228, 735))

def swap(self, cursor):
    """
    Swap the two board cells covered by `cursor` and update the
    matches.
    """
    i = self.pos(*cursor)
    b = self.board
    b[i], b[i+1] = b[i+1], b[i]
    self.matches = self.find_matches()


def find_matches(self):
    """
    Search for matches (lines of cells with identical images) and
    return a list of them, each match being represented as a list
    of board positions.
    """
    def lines():
        for j in range(self.h):
            yield range(j * self.w, (j + 1) * self.w)
        for i in range(self.w):
            yield range(i, self.size, self.w)
    def key(i):
        return self.image_color.get(self.board[i].image)
    def matches():
        for line in lines():
            for _, group in itertools.groupby(line, key):
                match = list(group)
                if len(match) >= MINIMUM_MATCH:
                    yield match
                    self.score = self.score + 1
    return list(matches())

def update_matches(self, image):
    """
    Replace all the cells in any of the matches with `image`.
    """
    for match in self.matches:
        for position in match:
            self.board[position].image = image

def refill_columns(self):
    """
    Move cells downwards in columns to fill blank cells, and
    create new cells as necessary so that each column is full. Set
    appropriate offsets for the cells to animate into place.
    """
    for i in range(self.w):
        target = self.size - i - 1
        for pos in range(target, -1, -self.w):
            if self.board[pos].image != self.blank:
                c = self.board[target]
                c.image = self.board[pos].image
                c.offset = (target - pos) // self.w
                target -= self.w
                yield c
        offset = 1 + (target - pos) // self.w
        for pos in range(target, -1, -self.w):
            c = self.board[pos]
            c.image = random.choice(self.shapes)
            c.offset = offset
            yield c

class Game(object):
"""
The state of the game, with properties:
`clock` -- the pygame clock.
`display` -- the window to draw into.
`font` -- a font for drawing the score.
`board` -- the board of cells.
`cursor` -- the current position of the (left half of) the cursor.
`score` -- the player's score.
`last_swap_ticks` -- 
`swap_time` -- time since last swap (in seconds).
"""
def __init__(self):
    pygame.init()
    pygame.display.set_caption("Nucleotide")
    self.clock = pygame.time.Clock()
    self.display = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT),
                                           DOUBLEBUF)
    self.board = Board(PUZZLE_COLUMNS, PUZZLE_ROWS)
    self.font = pygame.font.Font(None, FONT_SIZE)

def start(self):
    """
    Start a new game with a random board.
    """
    self.board.randomize()
    self.cursor = [0, 0]
    self.cursor2 = [0, 0]
    self.score = 0.0
    self.swap_time = 10

def quit(self):
    """
    Quit the game and exit the program.
    """
    pygame.quit()
    sys.exit()

def play(self):
    """
    Play a game: repeatedly tick, draw and respond to input until
    the QUIT event is received.
    """
    self.start()
    while True:
        self.draw()
        dt = min(self.clock.tick(FPS) / 1000.0, 1.0 / FPS)
        self.swap_time -= dt
        for event in pygame.event.get():
            if event.type == KEYUP:
                self.input(event.key)
            elif event.type == QUIT:
                self.quit()
            elif self.swap_time == 0.0:
                self.quit()
        self.board.tick(dt)

def input(self, key):
    """
    Respond to the player pressing `key`.
    """
    if key == K_q:
        self.quit()
    elif key == K_f:
        if VERTICAL == False:
            VERTICAL = True
        elif VERTICAL == True:
            VERTICAL = False
    elif key == K_RIGHT and self.cursor[0] < self.board.w - 2:
        self.cursor[0] += 1
    elif key == K_LEFT and self.cursor[0] > 0:
        self.cursor[0] -= 1
    elif key == K_DOWN and self.cursor[1] < self.board.h - 1:
        self.cursor[1] += 1
    elif key == K_UP and self.cursor[1] > 0:
        self.cursor[1] -= 1
    elif key == K_SPACE and not self.board.busy():
        self.swap()

def swap(self):
    """
    Swap the two cells under the cursor and update the player's score.
    """
    swap_penalties = int(self.swap_time / DELAY_PENALTY_SECONDS)
    self.swap_time = 1
    self.board.swap(self.cursor)

def draw(self):
    self.board.draw(self.display)
    self.draw_score()
    self.draw_time()
    if VERTICAL == False:
        self.draw_cursor()
    elif VERTICAL == True:
        self.draw_cursor2()
    pygame.display.update()

def draw_time(self):
    s = int(self.swap_time)
    text = self.font.render('{}:{:02}'.format(s / 60, s % 60),
                            True, BLACK)
    self.display.blit(text, (TEXT_OFFSET, WINDOW_HEIGHT - 170))

def draw_score(self):
    total_score = self.score

def draw_cursor(self):
    topLeft = (MARGIN + self.cursor[0] * SHAPE_WIDTH,
            MARGIN + self.cursor[1] * SHAPE_HEIGHT - 68)
    topRight = (topLeft[0] + SHAPE_WIDTH * 2, topLeft[1])
    bottomLeft = (topLeft[0], topLeft[1] + SHAPE_HEIGHT)
    bottomRight = (topRight[0], topRight[1] + SHAPE_HEIGHT)
    pygame.draw.lines(self.display, WHITE, True,
            [topLeft, topRight, bottomRight, bottomLeft], 3)

def draw_cursor2(self):
      topLeft = (MARGIN + self.cursor[0] * SHAPE_WIDTH,
               MARGIN + self.cursor[1] * SHAPE_HEIGHT - 68)
      topRight = (topLeft[0] + SHAPE_WIDTH, topLeft[1])
      bottomLeft = (topLeft[0], topLeft[1] + SHAPE_HEIGHT * 2)
      bottomRight = (topRight[0], topRight[1] + SHAPE_HEIGHT * 2)
      pygame.draw.lines(self.display, WHITE, True,
                  [topLeft, topRight, bottomRight, bottomLeft], 3)

if __name__ == '__main__':
    Game().play()

编辑:

我决定编辑这篇文章,而不是开始一篇新文章,因为它是关于同一个问题。我在这方面取得了更多进展,并按照建议将所有内容更改为 True/False。但是,每当您按“F”切换栏时,我都会遇到“UnboundLocalError:分配前引用的局部变量'VERTICAL'”错误。我一直试图解决的另一个问题是,一旦切换条,它就不会以正确的方向交换碎片。如果条是垂直的,它仍然水平交换瓷砖。编辑前的代码已更改,现在包括整个游戏。

关于错误的任何想法?如果我将 VERTICAL 设置为 True,则条形图会切换,因此绘图代码是正确的,所以我认为它与按键有关。另外,关于交换的任何想法以使其垂直交换?

4

0 回答 0