2014-01-12 50 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