Pygame sprites - get the rect collision side
我是pygame的新手,我正在尝试获取基本知识。 我想创建障碍物并检测玩家矩形(代表精灵的碰撞盒)的哪一侧与障碍物矩形碰撞。 这样,我就可以在游戏中创建基础物理。
这是我所做的:
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 | import pygame class Player (pygame.sprite.Sprite): def __init__(self, x=0, y=0, s=100): super(Player,self).__init__() self.image = pygame.transform.scale(pygame.image.load("player.png"), (s, s)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class Block (pygame.sprite.Sprite): def __init__(self, x=0, y=500, s=100): super(Block, self).__init__() self.image = pygame.transform.scale(pygame.image.load("wall.png"),(s, s)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y pygame.init() display = pygame.display.set_mode((800,600)) player = Player() block = Block() sprites = pygame.sprite.Group() living = pygame.sprite.Group() noliving = pygame.sprite.Group() sprites.add(player) sprites.add(block) living.add(player) noliving.add(block) gravity = 1 xCh = 0 yCh = 0 speed = 1 while True: display.fill((159, 159, 159)) for liv in living: for noliv in noliving: if(not pygame.sprite.collide_rect(liv, noliv)): player.rect.y += gravity for event in pygame.event.get(): if(event.type == pygame.KEYDOWN): if(event.key == pygame.K_ESCAPE): quit() elif(event.key == pygame.K_a or event.key == pygame.K_LEFT): xCh = -speed elif(event.key == pygame.K_d or event.key == pygame.K_RIGHT): xCh = speed elif(event.type == pygame.KEYUP): xCh = 0 yCh = 0 elif(event.type == pygame.QUIT): quit() player.rect.x += xCh sprites.draw(display) pygame.display.update() |
玩家触摸该块时会停止掉落,但是如果我向左或向右走然后进入该块,则玩家会直接进入该块并停止从此处掉落。 我希望玩家无法通过障碍。 怎么做?
这是您的代码,其中包含建议的修复程序以及其他一些改进建议。
首先,第一件事:问题是您从未检查过水平移动是否会使块碰撞。您只进行了一次检查,实际上它仅在发生任何碰撞后才执行任何操作。并且在这种情况下,它将防止"重力"做任何事情。由于您要逐像素更新播放器,因此实际上几乎没有注意到碰撞的发生。解决方法是创建一个检查,以验证移动的块是否可以移动到某个位置,然后才允许移动。并为所有运动做这些,而不仅仅是重力运动。
因此,在这里,我将解释可以使此示例代码演变为完整游戏的改进。
在模块级别消除"浮动代码",并将所有内容放入函数中。这对于将来拥有甚至与起始屏幕(可能还有"再次播放"屏幕)一样简单的任何游戏来说都是至关重要的。我做了两个功能,一个创建设置
您的游戏环境并创建您的对象,以及主要的游戏功能。您应该在将来进一步分开"设置"阶段,
能够创建更多游戏对象或不同的游戏级别。
重用您的代码。您的
帧延迟:您的代码将简单地"尽可能快地"移动-这将在不同的计算机上创建不同的游戏速度,并使用100%的CPU-导致可能的计算机速度降低和功耗过多。我提供了30ms的硬编码暂停-这将使您的游戏以接近30FPS的速度移动,并在不同设备上保持稳定的速度。另一个需要进行的更改是您的重力和速度的硬编码" 1"值,这会使玩家一次移动1px,必须更改为更大的值。
最后但并非最不重要的一点是"我可以移动到那里"的逻辑:它实际上似乎很广泛-但可以理解,"可以使该对象移动到该位置"检查的纯英文描述很冗长。该方法实质上是:"存储当前位置"。"用给定的x和y位移在位置上假"。"检查我是否与某物发生碰撞"。"如果没有,请恢复先前的位置,并说允许移动"。"否则,请恢复y位置,检查是否在X方向上发生碰撞,如果有,则限制水平移动"。"恢复x位置,将y位置移动所需的量,检查是否发生碰撞。如果发生,则限制在y方向上的移动"。"如果运动不受x或y的限制,则只是组合运动会导致碰撞:限制两个方向的运动都是公平的"。"还原原始位置,返回允许的x和y位置更改,以及我们要与之碰撞的对象"。所有这些逻辑都放在"活动"对象类内部,因此可以随时使用,只要在主游戏逻辑中使用一行代码即可。
码:
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 | import pygame class GameObject(pygame.sprite.Sprite): def __init__(self, x=0, y=0, s=100, image=""): super(GameObject, self).__init__() if not image: image = self.image self.image = pygame.transform.scale(pygame.image.load(image), (s, s)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class Player(GameObject): image ="player.png" def can_move(self, xCh, yCh, group): old_pos = self.rect.x, self.rect.y self.rect.x += xCh self.rect.y += yCh collided_with = pygame.sprite.spritecollideany(self, group) if not collided_with: # No Collisions at all - allow movement self.rect.x = old_pos[0] self.rect.y = old_pos[1] return True, xCh, yCh, None # Check if the colision was due to horizontal movement: self.rect.y = old_pos[1] if pygame.sprite.spritecollideany(self, group): # Yes, then indicate horizontal movement should be 0 xCh = 0 # check if collision was due to vertical movement: self.rect.y += yCh self.rect.x = old_pos[0] if pygame.sprite.spritecollideany(self, group): # Yes - indicate vertical movemnt should be 0 yCh = 0 # if only diagonal movement causes collision, then # cancel movrment in both axes: # (i.e. just the diagonal movement would hit the # obstacle ) if not xCh == 0 and not yCh == 0: xCh = 0 yCh = 0 self.rect.x = old_pos[0] self.rect.y = old_pos[1] return False, xCh, yCh, collided_with class Block(GameObject): image ="wall.png" def setup(): global display, player, living, noliving, gravity, speed, sprites pygame.init() display = pygame.display.set_mode((800,600)) player = Player() block = Block(y=500) sprites = pygame.sprite.Group() living = pygame.sprite.Group() noliving = pygame.sprite.Group() sprites.add(player) sprites.add(block) living.add(player) noliving.add(block) gravity = 10 speed = 10 def main(): xCh = 0 yCh = 0 while True: display.fill((159, 159, 159)) for event in pygame.event.get(): if(event.type == pygame.KEYDOWN): if(event.key == pygame.K_ESCAPE): quit() elif(event.key == pygame.K_a or event.key == pygame.K_LEFT): xCh = -speed elif(event.key == pygame.K_d or event.key == pygame.K_RIGHT): xCh = speed elif(event.type == pygame.KEYUP): xCh = 0 yCh = 0 elif(event.type == pygame.QUIT): quit() yCh = gravity for liv in living: if liv == player: check_x_ch = xCh else: check_x_ch = 0 can_move, xCh, yCh, obstacle = liv.can_move(xCh, yCh, noliving) liv.rect.x += xCh liv.rect.y += yCh # Do other actions if"can_move" indicates a block was hit... sprites.draw(display) pygame.display.update() pygame.time.delay(30) setup() 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 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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | import pygame class GameObj(pygame.sprite.Sprite): def __init__(self, image, x, y, s): super(GameObj, self).__init__() self.image = pygame.transform.scale(pygame.image.load("img/"+ image), (s, s)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class Player(GameObj): leftUnable = False rightUnable = False jumpHeight = 200 jumpSpeed = 5 image ="player.png" inAir = True def __init__(self, x, y, s=100): super(Player,self).__init__(self.image, x, y, s) class Block(GameObj): image ="wall.png" def __init__(self, x, y, s=100): super(Block, self).__init__(self.image, x, y, s) def collideNum(sprite, group): total = 0 for member in group: if(pygame.sprite.collide_rect(sprite, member)): total += 1 return total def setup(): pygame.init() global display, player, block, sprites, living, noliving, clock display = pygame.display.set_mode((800, 600)) pygame.display.set_caption("Test") clock = pygame.time.Clock() player = Player(100, 0) block = Block(100, 300) block1 = Block(200, 400) block2 = Block(400, 400) sprites = pygame.sprite.Group() living = pygame.sprite.Group() noliving = pygame.sprite.Group() noliving.add(block) noliving.add(block1) noliving.add(block2) living.add(player) for liv in living: sprites.add(liv) for noliv in noliving: sprites.add(noliv) main() def main(): speed = 5 gravity = 5 xCh, yCh = 0, 0 player.leftUnable = False player.rightUnable = False while True: clock.tick(60) display.fill((184, 184, 184)) yCh = gravity for event in pygame.event.get(): if(event.type == pygame.QUIT): quit() elif(event.type == pygame.KEYDOWN): if(event.key == pygame.K_ESCAPE): quit() elif((event.key == pygame.K_a or event.key == pygame.K_LEFT) and not player.leftUnable): for noliv in noliving: if(pygame.sprite.collide_rect(player, noliv)): if(noliv.rect.left < player.rect.left < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5): player.leftUnable = True break else: xCh = -speed player.leftUnable = False else: xCh = -speed player.leftUnable = False elif((event.key == pygame.K_d or event.key == pygame.K_RIGHT) and not player.rightUnable): for noliv in noliving: if(pygame.sprite.collide_rect(player, noliv)): if(noliv.rect.left < player.rect.right < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5): player.rightUnable = True break else: xCh = speed player.rightUnable = False else: xCh = speed player.rightUnable = False elif(event.key == pygame.K_SPACE or event.key == pygame.K_w or event.key == pygame.K_UP): oldPos = player.rect.bottom xCh = 0 if(not player.inAir): while player.rect.bottom > oldPos - player.jumpHeight: clock.tick(60) display.fill((184, 184, 184)) for ev in pygame.event.get(): if(ev.type == pygame.KEYDOWN): if(ev.key == pygame.K_d or ev.key == pygame.K_RIGHT): xCh = speed elif(ev.key == pygame.K_a or ev.key == pygame.K_LEFT): xCh = -speed elif(ev.type == pygame.KEYUP): xCh = 0 player.rect.x += xCh player.rect.y -= player.jumpSpeed player.inAir = True sprites.draw(display) pygame.display.update() elif(event.type == pygame.KEYUP): xCh = 0 for liv in living: for noliv in noliving: if(pygame.sprite.collide_rect(liv, noliv)): liv.inAir = False break else: liv.inAir = True for noliv in noliving: if(pygame.sprite.collide_rect(player, noliv)): if(noliv.rect.left < player.rect.left < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5): player.leftUnable = True if(collideNum(player, noliving) == 1): player.inAir = True if(xCh < 0): xCh = 0 elif(noliv.rect.left < player.rect.right < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5): player.rightUnable = True if(collideNum(player, noliving) == 1): player.inAir = True if(xCh > 0): xCh = 0 else: player.leftUnable = False player.rightUnable = False else: player.leftUnable = False player.rightUnable = False if(not player.inAir): yCh = 0 if(player.rect.top > display.get_size()[1]): setup() player.rect.x += xCh player.rect.y += yCh sprites.draw(display) pygame.display.update() setup() |
运行良好。