2015-02-11 28 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