2015-02-11 46 views
1

我想在Python 3.2.5中使用Pygame 1.9.2a0制作一个突破克隆。 代码比真正需要的更复杂,但是这是我在python/pygame中的第一个程序,我习惯于在自己的类/对象中分解东西。在碰撞检测后Pygame 1.9.2a0列表清除错误

无论如何 - 问题的关键在于当我的球与砖块发生碰撞时,它会检测到碰撞,但它不会将它们从图纸中移除 - 如果它未从图纸中移除,从列表中删除。

 for brick in bricks_.bricks: 
     if brick.collidepoint((ball_.pos.x+ball_.rad), (ball_.pos.y+ball_.rad)) or (brick.collidepoint((ball_.pos.x-ball_.rad),(ball_.pos.y-ball_.rad))): 
      ball_.speed.y = -ball_.speed.y 
      to_remove = [brick] 
      for brick in to_remove: 
       bricks_.bricks.remove(brick) 

我试图实现一种技术,这是从早期的问题,发现在stackoverflow但它仍然无法正常工作。

当我将代码放入绘图函数中时,它在再次绘制该图块之前将其删除。问题是我只运行一次我的创建砖块功能,所以我不明白为什么它正在绘制从列表中删除的砖块。

这是创建和绘制功能:

def create(self): 
    y_margin = 40 
    self.bricks = [] 
    for i in range(2): 
     """ Calculates x margin by subtracting by the number of pixels the blocks will take, 
     divides this on 2 so you get the "spacing" on both sides of the blocks. 
     Subtracts half a block width from this number and then it's aligned perfectly. 
     I do not know why I need to subtract half a block. 
     """ 
     x_margin = ((screen[0]-8*BRICK_W)/2)-(BRICK_W/2) 
     for j in range(8): 
      self.pos = (Vector2D((x_margin), (y_margin))) 
      self.bricks.append(pygame.Rect(self.pos.x, self.pos.y, BRICK_W, BRICK_H)) 
      x_margin += BRICK_W+5 
     y_margin += BRICK_H+5 

def draw(self): 
    for brick in self.bricks: 
     pygame.draw.rect(screen_, WHITE, brick) 

自从我决定与collisiondetection我得到另一个错误,我想如果没有块会消失的简易公路。下面是游戏的图片:http://i.stack.imgur.com/0thU6.gif

人的整个代码谁愿意来看一看: (国体不漂亮,但ATLEAST它适用于这种低级别的代码)

# Standard libraries 
    import sys, math, random 

# Importing third party libraries 
import pygame 
from pygame.locals import * 

# Global constants 
screen = (600, 600) 
WHITE = (255, 255, 255) 
BLUE = (0, 0, 255) 
BLACK = (0, 0 ,0) 
PLATFORM_W = 100 
PLATFORM_H = 20 
ball_dia = 20 
BRICK_W = 30 
BRICK_H = 15 


# Set screen 
screen_ = pygame.display.set_mode(screen, 0 , 32) 

# Platform Y coordinate 
platform_Y = screen[1] - PLATFORM_H - 10 #This can be modified to fit aesthetics 

# Restrictions 
platform_MAX_X = screen[0] - PLATFORM_W 
BALL_MAX_X = screen[0] - ball_dia+PLATFORM_H 
BALL_MAX_Y = screen[1] - ball_dia 


## ======================================*Vector2D*============================================== ## 

class Vector2D: 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

    def __repr__(Self): 
     return 'Vector(X: {x}, Y: {y})'.format(x = self.x, y = self.y) 

    def __add__(self, b): 
     return Vector2D(self.x + b.x, self.y +b.y) 

    def __sub__(self, b): 
     return Vector2D(self.x - b.x, self.y - b.y) 

    def __mul__(self, b): 

     try: 
      b = float(b) 
      return Vector2D(self.x * b, self.y * b) 
     except ValueError: 
      print("Ooops! Right value must be a float") 
      raise 

    def magnitue(self): 
     try: 
      m = self.magnitude() 
      return Vector2D(self.x/m, self.y/m) 
     except ZeroDivisionError: 
      print("Ooops, cannot normalize a zero vector") 
      raise 

    def copy(self): 
     return Vector2D(self.x, self.y) 



    """ 
    Get distance is basically getting the distance from a to b, 
    in this case vector a and vector b 
    """ 
def get_distance(a, b): 
     """ Using the distance formula which is derived from the Pythagorean theorem, 
     http://www.mathwarehouse.com/algebra/distance_formula/index.php """ 

     a = Vector2D(a.x, a.y) 
     b = Vector2D(b.x, b.y) 

     return (((((a.x-b.x)**2)+((a.y-b.y)**2)))**.5) 

## =========================================*Platform*=========================================== ## 


class Platform: 
    """ This is the platform that the player can control in x direction with arrow keys """ 
    def __init__(self): 
     self.x = screen[0]/2 - PLATFORM_W/2 
     self.y = platform_Y 
     self.width = PLATFORM_W 
     self.height = PLATFORM_H 
     self.pos = Vector2D(self.x, self.y) 
     self.tileRect = pygame.Rect(self.x, self.y, self.width, self.height) 

    def clique(self): 
     """ This is the one doing the magic to the Platform by getting the new x coordinates from 
     the input function. It then updates it's position data accordingly and draws the Platform 
     on the new information. """ 
     self.x = Input().x 
     self.pos = Vector2D(self.x, self.y) 

     # Making a variable that is equal to the rectangle platform 
     # This will be used for collision detection. 
     self.tileRect = pygame.Rect(self.x, self.y, self.width, self.height) 


     self.draw() 

    def draw(self): 
     pygame.draw.rect(screen_, BLUE, (self.pos.x, self.pos.y, self.width, self.height)) 



platform_ = Platform() 

## ===========================================*Bricks*========================================= ## 


class Bricks: 
    """ Bricks that will be removed after hit by the ball. 
    They are created using for loops. Change the ranges on the for loops to change 
    amounts of bricks """ 
    def __init__(self): 
     pass 


    def create(self): 
     y_margin = 40 
     self.bricks = [] 
     for i in range(2): 
      """ Calculates x margin by subtracting by the number of pixels the blocks will take, 
      divides this on 2 so you get the "spacing" on both sides of the blocks. 
      Subtracts half a block width from this number and then it's aligned perfectly. 
      I do not know why I need to subtract half a block. 
      """ 
      x_margin = ((screen[0]-8*BRICK_W)/2)-(BRICK_W/2) 
      for j in range(8): 
       self.pos = (Vector2D((x_margin), (y_margin))) 
       self.bricks.append(pygame.Rect(self.pos.x, self.pos.y, BRICK_W, BRICK_H)) 
       x_margin += BRICK_W+5 
      y_margin += BRICK_H+5 

    def draw(self): 
     for brick in self.bricks: 
      pygame.draw.rect(screen_, WHITE, brick) 


bricks_ = Bricks() 

## ========================================*Ball*============================================ ## 


class Ball: 
    """ A ball that will move, change direction if it hits platform, walls or bricks. 
    """ 
    def __init__(self): 
     self.rad = ball_dia/2 
     self.speed = Vector2D(0, 0) 
     self.pos = Vector2D(platform_.x+(PLATFORM_W/2), platform_.y-self.rad) 
     self.status = 0 
     self.gameover = False 

    def move(self): 
     ball_.speed = input_.speed 
     ball_.pos += ball_.speed 

     """ 
     Basic wall detection. Check all walls, subtracts the radius of the ball. 
     """ 
     if self.pos.x > BALL_MAX_X - self.rad: 
      self.pos.x = BALL_MAX_X - self.rad 
      self.speed.x *= -1 
     if self.pos.x < 0 + self.rad: 
      self.pos.x = 0 + self.rad 
      self.speed.x *= -1 
     if self.pos.y > BALL_MAX_Y - self.rad: 
      self.gameover = True 
     if self.pos.y < 0 + self.rad: 
      self.pos.y = 0 + self.rad 
      self.speed.y *= -1 


     """ 
     Inter is the centre position of the rectangle platform. This can be used 
     for collision detection. """ 
     inter = Vector2D(platform_.pos.x+PLATFORM_W/2, platform_.pos.y-PLATFORM_H/2) 
     d = get_distance(inter, self.pos) 

     """ Here we are checking if the rectangle platform are colliding with the point 
     ball's coordinates + its radius. If that is the case we are also checking which 
     side of the platform the ball is colliding on and having two different multipliers 
     giving it a feel of randomness and having a bounce to the other direction, 
     this we get by multiplying it by -1 (i.e)""" 
     if platform_.tileRect.collidepoint((self.pos.x+self.rad), (self.pos.y+self.rad)) or (platform_.tileRect.collidepoint((self.pos.x-self.rad),(self.pos.y-self.rad))): 
      if self.pos.x > inter.x: 
       self.speed.x *= -random.randrange(1,4) 
       self.speed.y *= -random.randrange(1,4) 
      if self.pos.x < inter.x: 
       self.speed.x *= -random.randrange(2, 4) 
       self.speed.y *= -random.randrange(2, 4) 

     for brick in bricks_.bricks: 
      if brick.collidepoint((ball_.pos.x+ball_.rad), (ball_.pos.y+ball_.rad)) or (brick.collidepoint((ball_.pos.x-ball_.rad),(ball_.pos.y-ball_.rad))): 
       ball_.speed.y = -ball_.speed.y 
       to_remove = [brick] 
       for brick in to_remove: 
        bricks_.bricks.remove(brick) 

     if self.speed.x > 10: 
      self.speed.x *= 0.5 
     if self.speed.x < -10: 
      self.speed.x *= 0.5 
     if self.speed.y > 10: 
      self.speed.y *= 0.5 
     if self.speed.y < -10: 
      self.speed.y *= 0.5 

     ball_.draw() 

    def collisions(self): 
     pass 

    def draw(self): 
     if self.gameover == False: 
      pygame.draw.circle(screen_, WHITE, (int(self.pos.x), int(self.pos.y)), int(self.rad)) 


ball_ = Ball() 



## ======================================*Engine*============================================== ## 

class Engine: 
    """ The engine initiates the game, takes care of events, 
    show stats and messages and basically run all the other parts 
    of the program """ 
    def __init__(self): 
     self.alive = True 
     self.retarded = False 
     pygame.display.set_caption("Rektball by #TeamRekt") 
     self.clock = pygame.time.Clock() 

     if pygame.font: 
      self.font = pygame.font.Font(None, 30) 
     else: 
      self.font = None 


    """ 
    The eventhandler is a function that will check pygame.events, 
    for either pygame.QUIT or the ESC button. If either is executed it will set the boolean 
    to false and it will quit pygame using the built in pygame.quit() function. 
    """ 
    def event_handler(self): 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       self.alive = False 
       pygame.quit() 
      if event.type == pygame.KEYDOWN: 
       if event.key == pygame.K_ESCAPE: 
        self.alive = False 
        pygame.quit() 

    """ 
    Show message will basically show a message in the middle of the screen. 
    It will use blit to create/render/draw/show the text. 
    """ 
    def show_message(self, message): 
     if self.font: 
      size = self.font.size(message) 
      font_surface = self.font.render(message, False, WHITE) 
      x = (screen[0] - size[0])/2 
      y = (screen[1] - size[1]) /2 
      screen_.blit(font_surface, (x,y)) 


    """ 
    The run-loop which runs this whole game. Everything is handled in this loop. 
    """ 
    def run(self): 
     while self.alive: 
      self.event_handler() 
      screen_.fill(BLACK) 
      self.time_passed = self.clock.tick(30) 
      statrun.Starts() 

      if statrun.start == False: 
       statrun.Starts() 

      statrun.Runs() 

      if ball_.gameover == True: 
       statrun.Gameovers() 

      pygame.display.update() 

## =======================================*Input*============================================= ## 


class Input: 
    """ This will take care of inputs, 
    i.e. keys being pressed down and use it to change 
    parts of the code to move the ball, the platform etc. """ 
    def __init__(self): 
     self.x = platform_.pos.x 
     self.speed = Vector2D(0,0) 
     self.status = False 
     self.check_input() 

    def check_input(self): 
     keys = pygame.key.get_pressed() 

     if keys[pygame.K_LEFT]: 
      self.x -= 10 
      if self.x < 0: 
       self.x = 0 
     if keys[pygame.K_RIGHT]: 
      self.x += 10 
      if self.x > platform_MAX_X: 
       self.x = platform_MAX_X 
     if keys[pygame.K_SPACE] and (self.status == False): 
      self.status = True 
      self.speed = Vector2D((-random.randrange(4, 10)),(-random.randrange(4,10))) 


input_ = Input()  

## ==================================================================================== ## 

class State_run: 
    """ 
    This was supposed to be part of a bigger state-machine code, 
    but after hitting the wall for too many hours I decided to abandon 
    the project, but keeping a little bit of the structure. 
    It is being called by boolean in the run function inside the engine object/class. 
    This is not a very good solution, but as I said, I have spent a few hours (days...), 
    and I just had to wrap this up. 
    """ 


    """ The init function will start the boolean circus, 
    although the boolean will not be used if it works as planned, 
    it's a fallback boolean. """ 
    def __init__(self): 
     self.start = False 

    def Starts(self): 
     platform_.draw() 
     ball_.draw() 
     bricks_.create() 
     bricks_.draw() 
     self.start = True 

    def Runs(self): 
     input_.check_input() 
     if input_.status != True: 
      Engine().show_message("Press space to start game") 
     if input_.status == True: 
      ball_.move() 
      platform_.clique() 
      bricks_.draw() 

    def Wins(self): 
     Engine().show_message("You have won the game") 

    def Gameovers(self): 
     Engine().show_message("You have lost the game") 
     ball_.speed = Vector2D(0,0) 

statrun = State_run() 

## ==================================================================================== ## 



""" Runs the program by first initiating pygame using the builtin function, pygame.init(), 
then it runs the Engine().run() function which is doing all the work. """ 
if __name__ == "__main__": 
    pygame.init() 

    Engine().run() 

回答

0
for brick in to_remove: 
    bricks_.bricks.remove(brick) 

的第二个循环中的“砖”会遮挡第一个循环中前一个“砖”。它必须改变。

for i in to_remove: 
    bricks_.bricks.remove(i) 

或得到第二个for循环出第一个循环。我认为你正在收集基于某些条件被删除的对象。你必须取回第二个for循环,如果是这样,就不需要改变循环变量名称。

'to_remove'列表具有单个元素。看来它必须有多个元素,将其初始化为一个空列表,如to_remove = [],并附加诸如to_remove.append(brick)之类的对象。

看来这个函数调用是可以改变的。 brick.collidepoint((ball_.pos.x+ball_.rad), (ball_.pos.y+ball_.rad))brick.collidepoint(ball_.pos.x + ball_.rad, ball_.pos.y + ball_.rad)

to_remove = [] 
cpoint1 = brick.collidepoint(ball_.pos.x + ball_.rad, ball_.pos.y + ball_.rad) 
cpoint2 = brick.collidepoint(ball_.pos.x - ball_.rad, ball_.pos.y - ball_.rad) 
for brick in bricks_.bricks: 
    if cpoint1 or cpoint2: 
     ball_.speed.y = -ball_.speed.y 
     to_remove.append(brick) 

for brick in to_remove: 
    bricks_.bricks.remove(brick) 
+0

发现我的问题。 这是在 '高清启动(个体经营): platform_.draw() ball_.draw() bricks_.create() bricks_.draw() self.start = TRUE' 其中正在运行的所有时间,当我认为它不是...... – IndentationFaulter 2015-02-11 17:02:23