2014-01-12 49 views
0

我正在學習面向對象的Python並理解類的主要原理並從類創建對象,但是我需要解釋一些東西。Re:下面的pygame代碼。我正努力想方設法讓我的頭腦在創建精靈列表時發生什麼,以及創建球對象的代碼下的兩行代碼(allsprites.add等)。換句話說,什麼是精靈,爲什麼創建它們的列表?爲什麼不是剛剛從課堂創建的球對象?爲什麼需要將其添加到精靈列表?這是怎麼回事?任何解釋將不勝感激。瞭解面向對象的python - 在pygame中初始化對象

""" 
Sample Breakout Game 

Sample Python/Pygame Programs 
Simpson College Computer Science 
http://programarcadegames.com/ 
http://simpson.edu/computer-science/ 
""" 

# --- Import libraries used for this program 

import math 
import pygame 

# Define some colors 
black = (0, 0, 0) 
white = (255, 255, 255) 
blue = (0, 0, 255) 

# Size of break-out blocks 
block_width = 23 
block_height = 15 

class Block(pygame.sprite.Sprite): 
    """This class represents each block that will get knocked out by the ball 
    It derives from the "Sprite" class in Pygame """ 

    def __init__(self, color, x, y): 
     """ Constructor. Pass in the color of the block, 
      and its x and y position. """ 

     # Call the parent class (Sprite) constructor 
     pygame.sprite.Sprite.__init__(self) 

     # Create the image of the block of appropriate size 
     # The width and height are sent as a list for the first parameter. 
     self.image = pygame.Surface([block_width, block_height]) 

     # Fill the image with the appropriate color 
     self.image.fill(color) 

     # Fetch the rectangle object that has the dimensions of the image 
     self.rect = self.image.get_rect() 

     # Move the top left of the rectangle to x,y. 
     # This is where our block will appear.. 
     self.rect.x = x 
     self.rect.y = y 


class Ball(pygame.sprite.Sprite): 
    """ This class represents the ball   
     It derives from the "Sprite" class in Pygame """ 

    # Speed in pixels per cycle 
    speed = 10.0 

    # Floating point representation of where the ball is 
    x = 0.0 
    y = 180.0 

    # Direction of ball (in degrees) 
    direction = 200 

    width = 10 
    height = 10 

    # Constructor. Pass in the color of the block, and its x and y position 
    def __init__(self): 
     # Call the parent class (Sprite) constructor 
     pygame.sprite.Sprite.__init__(self) 

     # Create the image of the ball 
     self.image = pygame.Surface([self.width, self.height]) 

     # Color the ball 
     self.image.fill(white) 

     # Get a rectangle object that shows where our image is 
     self.rect = self.image.get_rect() 

     # Get attributes for the height/width of the screen 
     self.screenheight = pygame.display.get_surface().get_height() 
     self.screenwidth = pygame.display.get_surface().get_width() 

    def bounce(self, diff): 
     """ This function will bounce the ball 
      off a horizontal surface (not a vertical one) """ 

     self.direction = (180 - self.direction) % 360 
     self.direction -= diff 

    def update(self): 
     """ Update the position of the ball. """ 
     # Sine and Cosine work in degrees, so we have to convert them 
     direction_radians = math.radians(self.direction) 

     # Change the position (x and y) according to the speed and direction 
     self.x += self.speed * math.sin(direction_radians) 
     self.y -= self.speed * math.cos(direction_radians) 

     # Move the image to where our x and y are 
     self.rect.x = self.x 
     self.rect.y = self.y 

     # Do we bounce off the top of the screen? 
     if self.y <= 0: 
      self.bounce(0) 
      self.y = 1 

     # Do we bounce off the left of the screen? 
     if self.x <= 0: 
      self.direction = (360 - self.direction) % 360 
      self.x = 1 

     # Do we bounce of the right side of the screen? 
     if self.x > self.screenwidth - self.width: 
      self.direction = (360 - self.direction) % 360 
      self.x = self.screenwidth - self.width - 1 

     # Did we fall off the bottom edge of the screen? 
     if self.y > 600: 
      return True 
     else: 
      return False 

class Player(pygame.sprite.Sprite): 
    """ This class represents the bar at the bottom that the player controls. """ 

    def __init__(self): 
     """ Constructor for Player. """ 
     # Call the parent's constructor 
     pygame.sprite.Sprite.__init__(self) 

     self.width = 75 
     self.height = 15 
     self.image = pygame.Surface([self.width, self.height]) 
     self.image.fill((white)) 

     # Make our top-left corner the passed-in location. 
     self.rect = self.image.get_rect() 
     self.screenheight = pygame.display.get_surface().get_height() 
     self.screenwidth = pygame.display.get_surface().get_width() 

     self.rect.x = 0 
     self.rect.y = self.screenheight-self.height 

    def update(self): 
     """ Update the player position. """ 
     # Get where the mouse is 
     pos = pygame.mouse.get_pos() 
     # Set the left side of the player bar to the mouse position 
     self.rect.x = pos[0] 
     # Make sure we don't push the player paddle 
     # off the right side of the screen 
     if self.rect.x > self.screenwidth - self.width: 
      self.rect.x = self.screenwidth - self.width 

# Call this function so the Pygame library can initialize itself 
pygame.init() 

# Create an 800x600 sized screen 
screen = pygame.display.set_mode([800, 600]) 

# Set the title of the window 
pygame.display.set_caption('Breakout') 

# Enable this to make the mouse disappear when over our window 
pygame.mouse.set_visible(0) 

# This is a font we use to draw text on the screen (size 36) 
font = pygame.font.Font(None, 36) 

# Create a surface we can draw on 
background = pygame.Surface(screen.get_size()) 

# Create sprite lists 
blocks = pygame.sprite.Group() 
balls = pygame.sprite.Group() 
allsprites = pygame.sprite.Group() 

# Create the player paddle object 
player = Player() 
allsprites.add(player) 

# Create the ball 
ball = Ball() 
allsprites.add(ball) 
balls.add(ball) 

# The top of the block (y position) 
top = 80 

# Number of blocks to create 
blockcount = 32 

# --- Create blocks 

# Five rows of blocks 
for row in range(5): 
    # 32 columns of blocks 
    for column in range(0, blockcount): 
     # Create a block (color,x,y) 
     block = Block(blue, column * (block_width + 2) + 1, top) 
     blocks.add(block) 
     allsprites.add(block) 
    # Move the top of the next row down 
    top += block_height + 2 

# Clock to limit speed 
clock = pygame.time.Clock() 

# Is the game over? 
game_over = False 

# Exit the program? 
exit_program = False 

# Main program loop 
while exit_program != True: 

    # Limit to 30 fps 
    clock.tick(30) 

    # Clear the screen 
    screen.fill(black) 

    # Process the events in the game 
    for event in pygame.event.get(): 
     if event.type == pygame.QUIT: 
      exit_program = True 

    # Update the ball and player position as long 
    # as the game is not over. 
    if not game_over: 
     # Update the player and ball positions 
     player.update() 
     game_over = ball.update() 

    # If we are done, print game over 
    if game_over: 
     text = font.render("Game Over", True, white) 
     textpos = text.get_rect(centerx=background.get_width()/2) 
     textpos.top = 300 
     screen.blit(text, textpos) 

    # See if the ball hits the player paddle 
    if pygame.sprite.spritecollide(player, balls, False): 
     # The 'diff' lets you try to bounce the ball left or right 
     # depending where on the paddle you hit it 
     diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2) 

     # Set the ball's y position in case 
     # we hit the ball on the edge of the paddle 
     ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1 
     ball.bounce(diff) 

    # Check for collisions between the ball and the blocks 
    deadblocks = pygame.sprite.spritecollide(ball, blocks, True) 

    # If we actually hit a block, bounce the ball 
    if len(deadblocks) > 0: 
     ball.bounce(0) 

     # Game ends if all the blocks are gone 
     if len(blocks) == 0: 
      game_over = True 

    # Draw Everything 
    allsprites.draw(screen) 

    # Flip the screen and show what we've drawn 
    pygame.display.flip() 

pygame.quit() 
+1

我建議你先熟悉OOP。之後,你會知道爲什麼這個代碼像A,爲什麼這樣的代碼像B. – aIKid

+0

除了@aIKid,請嘗試練習!編寫自己的代碼(在閱讀完任何編程主題的書籍或信息之後)可以讓您輕鬆理解。有時閱讀不是由你寫的代碼,是瞭解事情的一種難以理解的方式。 – Christian

回答

1

你並不需要添加球和塊精靈列表 - 它只是一個方便的事情。您可以手動檢查每個球碰撞,但它更容易只是告訴pygame的檢查它們都爲你

# See if the ball hits the player paddle 
if pygame.sprite.spritecollide(player, balls, False): 
    # The 'diff' lets you try to bounce the ball left or right 
    # depending where on the paddle you hit it 
    diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2) 

    # Set the ball's y position in case 
    # we hit the ball on the edge of the paddle 
    ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1 
    ball.bounce(diff) 

您可以得出每樣東西到屏幕上分別對每一幀,但它更容易只是告訴pygame的這樣做對你:

# Draw Everything 
allsprites.draw(screen) 

東西都可以在一個以上的list的要求,例如球被添加到balls列表中,這樣你可以很容易地進行碰撞,而且還加入了對allsprites列表以便您可以輕鬆地在屏幕上繪製所有內容

# Create the ball 
ball = Ball() 
allsprites.add(ball) 
balls.add(ball) 

編輯: 的一個重要區別是,allsprites實際上是一個sprite.Group。它裏面有一個精靈列表,但它也有其他方法,如draw

爲了解決「什麼是雪碧」的問題,它只是一個在屏幕上繪製的東西。像sprite.Group.draw這樣的pygame方法需要一些具有某些屬性的東西 - 例如update。確保您提供所有這些屬性的最簡單方法是使用正確的名稱,以子類 Sprite,但這也是一個(強烈推薦)方便的事情 - 例如,這是從pygame源代碼:

儘管可以設計不從 從下面的Sprite和AbstractGroup類派生的精靈和組類,但強烈建議您在添加Sprite或組 類時擴展那些類。

那麼專門不繼承Sprite幫你嗎?我們來看看源代碼。以下是如何找到源代碼的Python模塊:

>>> import pygame.sprite 
>>> pygame.sprite.__file__ 
'c:\\Python27\\lib\\site-packages\\pygame\\sprite.py' 
>>> 

每個Python模塊具有__file__屬性,它告訴你在源位於(以及不太每一個)。如果您在編輯器中打開它,向下滾動,你看到的類定義雪碧:

class Sprite(object): 
    """simple base class for visible game objects 
    pygame.sprite.Sprite(*groups): return Sprite 

    The base class for visible game objects. Derived classes will want to 
    override the Sprite.update() and assign a Sprite.image and 
    Sprite.rect attributes. The initializer can accept any number of 
    Group instances to be added to. 

    When subclassing the Sprite, be sure to call the base initializer before 
    adding the Sprite to Groups. 
    """ 

    def __init__(self, *groups): 
     self.__g = {} # The groups the sprite is in 
     if groups: self.add(groups) 

    def add(self, *groups): 
     """add the sprite to groups 
     Sprite.add(*groups): return None 

     Any number of Group instances can be passed as arguments. The 
     Sprite will be added to the Groups it is not already a member of. 
     """ 
     has = self.__g.__contains__ 
     for group in groups: 
      if hasattr(group, '_spritegroup'): 
       if not has(group): 
        group.add_internal(self) 
        self.add_internal(group) 
      else: self.add(*group) 

    def remove(self, *groups): 
     """remove the sprite from groups 
     Sprite.remove(*groups): return None 

     Any number of Group instances can be passed as arguments. The Sprite will 
     be removed from the Groups it is currently a member of. 
     """ 
     has = self.__g.__contains__ 
     for group in groups: 
      if hasattr(group, '_spritegroup'): 
       if has(group): 
        group.remove_internal(self) 
        self.remove_internal(group) 
      else: self.remove(*group) 

    def add_internal(self, group): 
     self.__g[group] = 0 

    def remove_internal(self, group): 
     del self.__g[group] 

    def update(self, *args): 
     """method to control sprite behavior 
     Sprite.update(*args): 

     The default implementation of this method does nothing; it's just a 
     convenient "hook" that you can override. This method is called by 
     Group.update() with whatever arguments you give it. 

     There is no need to use this method if not using the convenience 
     method by the same name in the Group class. 
     """ 
     pass 

    def kill(self): 
     """remove the Sprite from all Groups 
     Sprite.kill(): return None 

     The Sprite is removed from all the Groups that contain it. This won't 
     change anything about the state of the Sprite. It is possible to continue 
     to use the Sprite after this method has been called, including adding it 
     to Groups. 
     """ 
     for c in self.__g.keys(): 
      c.remove_internal(self) 
     self.__g.clear() 

    def groups(self): 
     """list of Groups that contain this Sprite 
     Sprite.groups(): return group_list 

     Return a list of all the Groups that contain this Sprite. 
     """ 
     return self.__g.keys() 

    def alive(self): 
     """does the sprite belong to any groups 
     Sprite.alive(): return bool 

     Returns True when the Sprite belongs to one or more Groups. 
     """ 
     return (len(self.__g) != 0) 

    def __repr__(self): 
     return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g)) 

因此,在總結,你不子類Sprite - 你可以只提供所有這些方法在你自己的 - 但它更容易,如果你這樣做;)

+0

謝謝。這使得事情在我的腦海中點擊。非常感謝。 – sw123456

+0

@ samwickins32很酷,很高興幫助。嘗試對一些行進行註釋並查看什麼是中斷 - 這是瞭解代碼的好方法。嘗試用每個精靈的單個'draw'語句替換'allsprites.draw(screen)'行 - 或者爲更小的列表。玩代碼水泥的理解,並讓你知道什麼時候你不太明白事情是如何工作的。 –

+0

你太棒了!非常感謝。非常清楚,你真的幫助我更多地理解這一點。我非常感謝你的時間。 – sw123456