pygame inaccurate collision detection and movement
在www.teachyourselfpython.com上的教程之后,我有一个用python3在pygame中开发的游戏的开始。这里有main.py,player.py和wall.py(分别具有player和walls类)。 播放器类包含用于碰撞检测和移动的代码。 不幸的是,玩家确实避免了平铺,但是在碰撞时,移动到了屏幕右侧的某个位置,而不是预期的移动(只是停止了)。 有谁能够帮助您解决此逻辑并纠正碰撞时的错误/不良运动。 下面是三个文件:main.py,player.py和walls.py。
主PY
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | #main.py import pygame import random from player import Player from collectable import Collectable from walls import Wall pygame.init() BLACK=(0,0,0) WHITE=(255,255,255) RED=(255,0,0) GREEN =(0,255,0) BLUE=(0,0,255) GOLD=(255,215,0) WIDTH=500 HEIGHT=500 size= (WIDTH,HEIGHT) screen=pygame.display.set_mode(size) pygame.display.set_caption("The Life Game") done = False clock=pygame.time.Clock() wall_list=pygame.sprite.Group() all_sprites = pygame.sprite.Group() enemy_list = pygame.sprite.Group() player=Player() player.walls=wall_list all_sprites.add(player) for i in range(random.randrange(100,200)): whiteStar = Collectable(WHITE, 3, 3,"White Star","Rect") whiteStar.rect.x = random.randrange(size[0]) whiteStar.rect.y = random.randrange(size[1]) all_sprites.add(whiteStar) for i in range(50): enemy = Collectable(RED,6, 6,"Enemy","Ellipse") enemy.rect.x = random.randrange(300) enemy.rect.y = random.randrange(300) enemy_list.add(enemy) all_sprites.add(enemy) coin1 = Collectable(GOLD,50,50,"Coin","Ellipse") coin1.rect.x=440 coin1.rect.y=0 all_sprites.add(coin1) coin2 = Collectable(GOLD,50,50,"Coin","Ellipse") coin2.rect.x=0 coin2.rect.y=440 all_sprites.add(coin2) enemy = Collectable(RED,100,100,"Enemy","Ellipse") enemy.rect.x=70 enemy.rect.y=230 all_sprites.add(enemy) #Make the walls (x_pos,y_pos, width, height,colour) wall=Wall(0,0,10,600,GREEN) wall_list.add(wall) all_sprites.add(wall_list) wall = Wall(50, 300, 400, 10,RED) wall_list.add(wall) all_sprites.add(wall_list) wall = Wall(10, 200, 100, 10,BLUE) wall_list.add(wall) all_sprites.add(wall_list) score=0 health=100 #- - - - - - - - - - - - - -Main Program Loop - - - - - - - - - - - - - - - - def main(): done=False score=0 health=100 while not done: #- - - - - - Main event loop (this is where code for handling keyboard and mouse clicks will go) #Loop until the user clicks the 'x' button (to close program) for event in pygame.event.get(): #User does something if event.type == pygame.QUIT: #If the user clicked close done = True #set the done flag to 'true' to exit the loop keys = pygame.key.get_pressed() #checking pressed keys if keys[pygame.K_LEFT]: player.moveLeft(5) if keys[pygame.K_RIGHT]: player.moveRight(5) if keys[pygame.K_UP]: player.moveUp(5) if keys[pygame.K_DOWN]: player.moveDown(5) #>>----------DRAW SECTION ----------------------------------- #Clear the screen to BLACK. Any drawing commands should be put BELOW this or they will be reased with this command screen.fill(BLACK) #Select the font to be used (size, bold, italics, etc) font_score = pygame.font.SysFont('Calibri',20,True,False) font_health = pygame.font.SysFont('Calibri',20,True,False) #Printing a variable (score or health) to the screen involves converting the score (if integer) to a string first.score_label = font_score.render("Score:" + str(score),True,BLACK) health_label = font_health.render("Health:"+str(health),True,WHITE) score_label = font_score.render("Score:" + str(score),True, WHITE) #Now we can use this line of code to put the image of the text on the screen at a given position screen.blit(score_label,[100,480]) screen.blit(health_label,[190,480]) #>>---------UPDATE SECTION / Put the logic of your game here (i.e. how objects move, when to fire them, etc) all_sprites.update() if coin1.collision_with(player): score=score+1 coin1.kill() coin1.rect.x=-20 coin1.rect.y=-330 if coin2.collision_with(player): score=score+1 coin2.kill() coin2.rect.x=-20 coin2.rect.y=-330 if enemy.collision_with(player): health=health-25 enemy.kill() enemy.rect.x=-20 enemy.rect.y=-330 enemy.update() #-------------PRINTING VARIABLES LIKE SCORE TO SCREEN #Any drawing/graphics code should go here all_sprites.draw(screen) #Update the screen to show whatever you have drawn pygame.display.flip() #Set the frames per second (e.g. 30, 60 etc) clock.tick(120) main() |
玩家
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import pygame import random from walls import Wall class Player(pygame.sprite.Sprite): #-------------------Define Variables here speed=0 #------------------Initialise Constructor def __init__(self): pygame.sprite.Sprite.__init__(self) self.image=pygame.image.load("player.png") self.rect = self.image.get_rect() #SET THE INITIAL SPEED TO ZERO self.change_x = 0 self.change_y = 0 #--------------Fetch the rectangle object that has the dimensions of the image self.rect =self.image.get_rect() #---------------Define movement def moveRight(self,pixels): self.rect.x+=pixels def moveLeft(self,pixels): self.rect.x-=pixels def moveUp(self,pixels): self.rect.y-=pixels def moveDown(self,pixels): self.rect.y+=pixels # Make our top-left corner the passed-in location. def settopleft(): self.rect = self.image.get_rect() self.rect.y = y self.rect.x = x # Set speed vector self.change_x = 0 self.change_y = 0 self.walls = None def changespeed(self, x, y): """ Change the speed of the player.""" self.change_x += x self.change_y += y def update(self): # Did this update cause us to hit a wall? block_hit_list = pygame.sprite.spritecollide(self, self.walls, False) for block in block_hit_list: # If we are moving right, set our right side to the left side of # the item we hit if self.change_x > 0: self.rect.right = block.rect.left else: # Otherwise if we are moving left, do the opposite. self.rect.left = block.rect.right # Move up/down self.rect.y += self.change_y # Check and see if we hit anything block_hit_list = pygame.sprite.spritecollide(self, self.walls, False) for block in block_hit_list: # Reset our position based on the top/bottom of the object. if self.change_y > 0: self.rect.top = block.rect.top else: self.rect.top = block.rect.bottom |
墙
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pygame class Wall(pygame.sprite.Sprite): #Wall a player can run into def __init__(self, x, y, width, height,colour): #Constructor fo rthe wall that the player can run into #call the parent's constructor super().__init__() #Make a green wall, of the size specified in paramenters self.image=pygame.Surface([width,height]) self.image.fill(colour) #Make the"passed-in" location ,the top left corner self.rect=self.image.get_rect() self.rect.y=y self.rect.x=x |
附件中还有播放器和bg的图像:
好吧,让我们先谈谈运动问题。
1 2 | if self.change_y > 0: self.rect.bottom = block.rect.top |
对于碰撞检测,可以使用
这是您的更新代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | import sys import random import pygame as pg pg.init() class Wall(pg.sprite.Sprite): """Wall a player can run into.""" def __init__(self, x, y, width, height, colour): super().__init__() self.image = pg.Surface([width, height]) self.image.fill(colour) # Make the"passed-in" location the top left corner. self.rect = self.image.get_rect(topleft=(x, y)) class Collectable(pg.sprite.Sprite): """A collectable item.""" def __init__(self, colour, x, y, image, rect): super().__init__() self.image = pg.Surface((5, 5)) self.image.fill(colour) self.rect = self.image.get_rect(topleft=(x, y)) class Player(pg.sprite.Sprite): def __init__(self): pg.sprite.Sprite.__init__(self) self.image = pg.Surface((30, 30)) self.image.fill((50, 150, 250)) self.rect = self.image.get_rect() #SET THE INITIAL SPEED TO ZERO self.change_x = 0 self.change_y = 0 self.health = 100 def update(self): # Move left/right. self.rect.x += self.change_x # Did this update cause us to hit a wall? block_hit_list = pg.sprite.spritecollide(self, self.walls, False) for block in block_hit_list: # If we are moving right, set our right side to the left side of # the item we hit if self.change_x > 0: self.rect.right = block.rect.left else: # Otherwise if we are moving left, do the opposite. self.rect.left = block.rect.right # Move up/down. self.rect.y += self.change_y # Check and see if we hit anything. block_hit_list = pg.sprite.spritecollide(self, self.walls, False) for block in block_hit_list: # Reset our position based on the top/bottom of the object. if self.change_y > 0: self.rect.bottom = block.rect.top else: self.rect.top = block.rect.bottom BLACK = (0,0,0) WHITE = (255,255,255) GREEN = (0,255,0) RED = (255,0,0) BLUE = (0,0,255) GOLD = (255,215,0) size = (500, 500) screen = pg.display.set_mode(size) pg.display.set_caption("The Life Game") wall_list = pg.sprite.Group() all_sprites = pg.sprite.Group() enemy_list = pg.sprite.Group() coins = pg.sprite.Group() player = Player() player.walls = wall_list all_sprites.add(player) for i in range(random.randrange(100,200)): x = random.randrange(size[0]) y = random.randrange(size[1]) whiteStar = Collectable(WHITE, x, y,"White Star","Rect") all_sprites.add(whiteStar) for i in range(50): x = random.randrange(size[0]) y = random.randrange(size[1]) enemy = Collectable(RED, x, y,"Enemy","Ellipse") enemy_list.add(enemy) all_sprites.add(enemy) coin1 = Collectable(GOLD,240,200,"Coin","Ellipse") coin2 = Collectable(GOLD,100,340,"Coin","Ellipse") all_sprites.add(coin1, coin2) coins.add(coin1, coin2) # Make the walls. walls = [Wall(0,0,10,600,GREEN), Wall(50, 300, 400, 10,RED), Wall(10, 200, 100, 10,BLUE)] wall_list.add(walls) all_sprites.add(walls) def main(): clock = pg.time.Clock() done = False score = 0 font_score = pg.font.SysFont('Calibri',20,True,False) font_health = pg.font.SysFont('Calibri',20,True,False) while not done: for event in pg.event.get(): if event.type == pg.QUIT: done = True # Do the movement in the event loop by setting # the player's change_x and y attributes. elif event.type == pg.KEYDOWN: if event.key == pg.K_LEFT: player.change_x = -3 elif event.key == pg.K_RIGHT: player.change_x = 3 elif event.key == pg.K_UP: player.change_y = -3 elif event.key == pg.K_DOWN: player.change_y = 3 elif event.type == pg.KEYUP: if event.key == pg.K_LEFT and player.change_x < 0: player.change_x = 0 elif event.key == pg.K_RIGHT and player.change_x > 0: player.change_x = 0 elif event.key == pg.K_UP and player.change_y < 0: player.change_y = 0 elif event.key == pg.K_DOWN and player.change_y > 0: player.change_y = 0 # UPDATE SECTION / Put the logic of your game here (i.e. how # objects move, when to fire them, etc). all_sprites.update() # spritecollide returns a list of the collided sprites in the # passed group. Iterate over this list to do something per # collided sprite. Set dokill argument to True to kill the sprite. collided_enemies = pg.sprite.spritecollide(player, enemy_list, True) for enemy in collided_enemies: player.health -= 25 collided_coins = pg.sprite.spritecollide(player, coins, True) for coin in collided_coins: score += 1 # DRAW SECTION screen.fill(BLACK) all_sprites.draw(screen) health_label = font_health.render("Health:"+str(player.health),True,WHITE) score_label = font_score.render("Score:" + str(score),True, WHITE) screen.blit(score_label,[100,480]) screen.blit(health_label,[190,480]) pg.display.flip() clock.tick(60) if __name__ == '__main__': main() pg.quit() sys.exit() |