Pygame-объект не имеет атрибута «rect» - PullRequest
1 голос
/ 02 апреля 2020

В настоящее время я делаю Memory, игру для школы, которая включает в себя сопоставление 8 пар изображений, которые спрятаны под обложками. Мне удалось настроить сетку 4x4, с таймером для оценки и рандомизации расположения изображений, но снятие крышки на плитках вызывает у меня некоторые проблемы. В итоге я получаю сообщение об ошибке: builtins.AttributeError: тип объекта «Плитка» не имеет атрибута «прямоугольник»

Трассировка:

Traceback (most recent call last):
  File "/home/user/Documents/Lab 4/memory2cover.py", line 179, in <module>
    main()
  File "/home/user/Documents/Lab 4/memory2cover.py", line 21, in <module>
    game.play()
  File "/home/user/Documents/Lab 4/memory2cover.py", line 92, in <module>
    self.handle_events()
  File "/home/user/Documents/Lab 4/memory2cover.py", line 117, in <module>
    Tile.handle_click(Tile, event)
  File "/home/user/Documents/Lab 4/memory2cover.py", line 175, in <module>
    if self.rect.collidepoint(pos):
builtins.AttributeError: type object 'Tile' has no attribute 'rect'

Мой класс плиток:

class Tile:
   # An object in this class represents a Tile 
   # Shared or Class Attributes
   surface = None
   fg_color = pygame.Color('black')
   border_width = 10
   cover = pygame.image.load('image0.bmp')

   @classmethod
   def set_surface(cls,surface_from_Game):
      cls.surface = pygame.display.get_surface()

   def __init__(self, x,y,width,height, image):
      # Initialize a Tile.
      # - self is the Tile to initialize
      # - x,y top left coordinates of the rectangle
      # - - width, height are the dimensions of the rectangle
      # Instance Attributes or Object Attributes
      self.rect = pygame.Rect(x,y,width,height)
      self.image = image
      self.covered = True

   def draw(self):
      # Draw the dot on the surface
      # - self is the Tile
      # then blit the tile content onto the surface
      pygame.draw.rect(Tile.surface, Tile.fg_color, self.rect,Tile.border_width)
      if self.covered:
         Tile.surface.blit(Tile.cover, self.rect)
      else:
         Tile.surface.blit(self.image, self.rect)

   def handle_click(self, event):
      if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
         pos = pygame.mouse.get_pos()
         if self.rect.collidepoint(pos):
            self.covered = False

и я не совсем уверен, почему он говорит, что у него нет атрибута 'rect'.

Вот мой полный код

# Memory 1

import pygame
import random

# User-defined functions
# board[row_index][col_index]

def main():
   # initialize all pygame modules (some need initialization)
   pygame.init()
   # create a pygame display window
   pygame.display.set_mode((500, 400))
   # set the title of the display window
   pygame.display.set_caption('Memory')   
   # get the display surface
   w_surface = pygame.display.get_surface() 
   # create a game object
   game = Game(w_surface)
   # start the main game loop by calling the play method on the game object
   game.play() 
   # quit pygame and clean up the pygame window
   pygame.display.quit()
   pygame.quit() 


# User-defined classes

class Game:
   # An object in this class represents a complete game.

   def __init__(self, surface):
      # Initialize a Game.
      # - self is the Game to initialize
      # - surface is the display window surface object

      # === objects that are part of every game that we will discuss
      self.surface = surface
      self.bg_color = pygame.Color('black')

      self.FPS = 70
      self.game_Clock = pygame.time.Clock()
      self.close_clicked = False
      self.continue_game = True

      # === game specific objects
      Tile.set_surface(self.surface) # this is how you call a class method
      self.board_size = 4
      self.board = []
      self.score = 0
      self.load_images()
      self.create_board()
      self.image_index = 0


   def load_images(self):
      # loads a specified amount of images into a list
      # that list of images is then shuffled so an images index is randomized
      self.image_list = []
      for self.image_index in range(1,9):
         self.image_list.append(pygame.image.load('image' + str(self.image_index) + '.bmp'))
      self.full_list = self.image_list + self.image_list
      random.shuffle(self.full_list)

   def create_board(self):
      # multiple rows and columns are appended to the board with the tiles added
      # utlizes the different images for each tile
      index = 0
      img = self.full_list[0]
      width = img.get_width()
      height = img.get_height()
      for row_index in range(0,self.board_size):
         self.row = []
         for col_index in range(0,self.board_size):
            #item = (row_index,col_index)
            # game = Game(), dot1 = Dot(), student = Student()
            image = self.full_list[index]
            x = width * col_index
            y = height * row_index      
            a_tile = Tile(x,y,width,height, image)
            self.row.append(a_tile)
            index = index + 1
         self.board.append(self.row)


   def play(self):
      # Play the game until the player presses the close box.
      # - self is the Game that should be continued or not.

      while not self.close_clicked:  # until player clicks close box
         # play frame
         self.handle_events()
         self.draw()            
         if self.continue_game:
            self.update()
         self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second 

   def draw_score(self):
      fg = pygame.Color('white')
      bg = pygame.Color('black')
      font = pygame.font.SysFont('',70)
      text_box = font.render(str(self.score),True,fg,bg)
      #x = self.surface.get_width() - text_box.get_width()
      #y = self.surface.get_height() - text_box.get_height()
      location = (self.surface.get_width() -70,0)
      self.surface.blit(text_box,location)   

   def handle_events(self):
      # Handle each user event by changing the game state appropriately.
      # - self is the Game whose events will be handled

      events = pygame.event.get()
      for event in events:
         if event.type == pygame.QUIT:
            self.close_clicked = True
         for item in self.row:
            Tile.handle_click(Tile, event)

   def draw(self):
      # Draw all game objects.
      # - self is the Game to draw
      coordinates = self.create_board()

      # clear the display surface first
      self.surface.fill(self.bg_color) 
      # drawing of the board
      for row in self.board:
         for tile in row:
            tile.draw()
      self.draw_score()
      pygame.display.update() # make the updated surface appear on the display

   def update(self):
      # Update the game objects for the next frame.
      # - self is the Game to update
      self.score = pygame.time.get_ticks()//1000



class Tile:
   # An object in this class represents a Tile 
   # Shared or Class Attributes
   surface = None
   fg_color = pygame.Color('black')
   border_width = 10
   cover = pygame.image.load('image0.bmp')

   @classmethod
   def set_surface(cls,surface_from_Game):
      cls.surface = pygame.display.get_surface()

   def __init__(self, x,y,width,height, image):
      # Initialize a Tile.
      # - self is the Tile to initialize
      # - x,y top left coordinates of the rectangle
      # - - width, height are the dimensions of the rectangle
      # Instance Attributes or Object Attributes
      self.rect = pygame.Rect(x,y,width,height)
      self.image = image
      self.covered = True

   def draw(self):
      # Draw the dot on the surface
      # - self is the Tile
      # then blit the tile content onto the surface
      pygame.draw.rect(Tile.surface, Tile.fg_color, self.rect,Tile.border_width)
      if self.covered:
         Tile.surface.blit(Tile.cover, self.rect)
      else:
         Tile.surface.blit(self.image, self.rect)

   def handle_click(self, event):
      if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
         pos = pygame.mouse.get_pos()
         if self.rect.collidepoint(pos):
            self.covered = False


main()

1 Ответ

3 голосов
/ 02 апреля 2020

Проблема заключается в следующем:

Tile.handle_click( Tile, event )

Код путает определение класса объекта Tile с созданным экземпляром объекта - как его "живая копия" ~ a_tile. Поэтому, когда он вызывает Tile.something(), __init__() никогда не вызывался, поэтому .rect не существует. Конечно, a_tile имеет .rect, потому что он был создан.

Вероятно, это должно быть что-то вроде:

   def handle_events(self):
      # Handle each user event by changing the game state appropriately.
      # - self is the Game whose events will be handled

      events = pygame.event.get()
      for event in events:
         if event.type == pygame.QUIT:
            self.close_clicked = True
         for item in self.row:
            item.handle_click( event )    # <-- HERE

За исключением @classmethod функций (то есть: что каждый другие языки вызывают static функции-члены), функции-члены почти никогда не вызываются в определении класса. Поэтому, если вы пишете Tile.something для вызова функции, вам следует задавать себе вопросы.

Если у вас нет объекта без внутреннего состояния, это нормально, если у вас есть функции @classmethod типа «один-к-нескольким». Иногда вы можете создать класс, скажем, конвертер единиц измерения, который находится в объекте просто для того, чтобы собрать целую кучу маленьких функций вместе. Все это будет вызвано определением, например: Units.furlongsToMetres( fur ). Для вашего Tile это не тот случай.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...