问题
I've been working on this project about tanks (based on game Tank Trouble) and I've made walls appear on the screen. How can I make that when projectiles/bullets collide with the wall they ricochet from it?
In the future, I am planning on the bullet colliding with a player/enemy.
Any help will be appreciated!
Here is game code:
class Game:
def __init__(self):
self.run = True
self.screen_width = 1060
self.screen_height = 798
self.image = pygame.image.load("bin/sprites/background/background1.png")
self.image = pygame.transform.scale(self.image, (self.screen_width, self.screen_height))
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height))
# all_sprites is used to update and draw all sprites together.
self.all_sprites = pygame.sprite.Group()
# for collision detection with enemies.
self.bullet_group = pygame.sprite.Group()
# for collision detection with walls.
self.wall_list = pygame.sprite.Group()
self.tank = Tank()
self.all_sprites.add(self.tank)
self.enemy = Enemy()
self.all_sprites.add(self.enemy)
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
# -------------- Walls --------------
self.wallx = [0, 0, 1044, 0, 0, 260, 146, 130, 146, 146, 130, 146, 390, 522, 390, 260, 390, 406, 522, 522, 522, 652,
652, 782, 914, 782, 782, 914, 914, 914, 652, 652, 782, ]
self.wally = [0, 0, 0, 782, 260, 0, 130, 130, 522, 390, 652, 652, 0, 0, 130, 260, 260, 390, 260, 260, 522, 130, 130,
0, 0, 260, 390, 390, 522, 652, 522, 652, 522, ]
self.wallWidth = [16, 1060, 16, 1060, 130, 16, 130, 16, 130, 130, 16, 130, 16, 16, 148, 146, 16, 132, 16, 130, 16,
278, 16, 16, 16, 278, 148, 16, 148, 148, 146, 146, 16, ]
self.wallHeight = [798, 16, 798, 16, 16, 130, 16, 408, 16, 16, 146, 16, 146, 146, 16, 16, 408, 16, 146, 16, 260, 16,
296, 146, 146, 16, 16, 148, 16, 16, 16, 16, 276]
for i in range(len(self.wallx)):
self.wall = Wall(self.wallx[i], self.wally[i], self.wallWidth[i], self.wallHeight[i])
self.wall_list.add(self.wall)
self.all_sprites.add(self.wall)
def handle_events(self):
self.enemy.handle_events()
# responsible for colliding with walls
tank_pos = pygame.math.Vector2(self.tank.pos)
self.tank.handle_events()
if pygame.sprite.spritecollide(self.tank, self.wall_list, False):
self.tank.pos = tank_pos
self.tank.rect.center = round(tank_pos[0]), round(tank_pos[1])
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.run = False
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.run = False
if event.key == pygame.K_SPACE:
bullet = Bullet(self.tank)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def update(self):
# Calls `update` methods of all contained sprites.
self.all_sprites.update()
def player_score(self):
font = pygame.font.SysFont("bin/font/Montserrat.ttf", 35)
tank_score = font.render("PLAYER: " + str(self.tank.score), 1, white)
self.screen.blit(tank_score, (200, 750))
def draw(self):
self.screen.blit(self.image, (0, 0))
self.all_sprites.draw(self.screen) # Draw the contained sprites.
self.player_score()
pygame.display.update()
class Tank(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bin/sprites/player/player_tank1.png")
self.org_image = self.image.copy()
# A nicer way to set the start pos with `get_rect`.
self.rect = self.image.get_rect(center=(70, 600))
self.vel = 3
self.angle = 270 # starts looking right
self.direction = pygame.Vector2(1, 0)
self.pos = pygame.Vector2(self.rect.center)
self.hp = 1
self.score = 0
def handle_events(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.angle += 3
if keys[pygame.K_RIGHT]:
self.angle -= 3
if keys[pygame.K_UP] and self.rect.left - 5 > 0 and self.rect.top - 5 > 0 and self.rect.right + 5 < 1060 and self.rect.bottom + 5 < 798:
self.move(-3)
if keys[pygame.K_DOWN] and self.rect.left - 5 > 0 and self.rect.top - 5 > 0 and self.rect.right + 5 < 1060 and self.rect.bottom + 5 < 798:
self.move(3)
self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
self.image = pygame.transform.rotate(self.org_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
def move(self, vel):
direction = pygame.Vector2(0, vel).rotate(-self.angle)
self.pos += direction
self.rect.center = round(self.pos[0]), round(self.pos[1])
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bin/sprites/enemy/enemy_tank.png")
self.org_image = self.image.copy()
self.spawnx = [600, 850, 860] # spawning x coord
self.spawny = [70, 200, 700] # spawning y coord
self.i = random.randint(0, len(self.spawnx) - 1)
# A nicer way to set the start pos with `get_rect`.
self.rect = self.image.get_rect(center=(self.spawnx[self.i], self.spawny[self.i]))
self.vel = 3
self.hp = 1
self.score = 0
if self.i == 0:
self.angle = 180
elif self.i == 1:
self.angle = 90
elif self.i == 2:
self.angle = 0
self.direction = pygame.Vector2(1, 0)
self.pos = pygame.Vector2(self.rect.center)
def handle_events(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
self.angle += 3
if keys[pygame.K_d]:
self.angle -= 3
if keys[pygame.K_w]:
self.move(-3)
if keys[pygame.K_s]:
self.move(3)
self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
self.image = pygame.transform.rotate(self.org_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
def move(self, vel):
direction = pygame.Vector2(0, vel).rotate(-self.angle)
self.pos += direction
self.rect.center = round(self.pos[0]), round(self.pos[1])
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
super().__init__()
# Make a wall, of the size specified in the parameters
self.image = pygame.Surface([width, height])
self.image.fill(dark_gray) # change wall color
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class Bullet(pygame.sprite.Sprite):
def __init__(self, tank):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bin/sprites/bullet/bullet.png")
self.image = pygame.transform.scale(self.image, (16, 16))
self.rect = self.image.get_rect()
self.rect.centerx = tank.rect.centerx + 3 # How much pixels from tank turret on x axis
self.rect.centery = tank.rect.centery - 25 # How much pixels from tank turret on y axis
self.angle = tank.angle
self.pos = pygame.Vector2(self.rect.center)
self.direction = pygame.Vector2(0, -10).rotate(-self.angle)
self.lives = 4 # how many times bounces
def update(self):
game = Game()
self.pos += self.direction
self.rect.center = round(self.pos[0]), round(self.pos[1])
if self.rect.left < 0 or self.rect.left < game.wall.rect.y:
self.direction.x *= -1
self.rect.left = 0
self.pos.x = self.rect.centerx
self.lives -= 1
if self.lives == 0:
return self.kill()
bounce.play()
if self.rect.right > 1060:
self.direction.x *= -1
self.rect.right = 1060
self.pos.x = self.rect.centerx
self.lives -= 1
if self.lives == 0:
return self.kill()
bounce.play()
if self.rect.top < 0:
self.direction.y *= -1
self.rect.top = 0
self.pos.y = self.rect.centery
self.lives -= 1
if self.lives == 0:
return self.kill()
bounce.play()
if self.rect.bottom > 798:
self.direction.y *= -1
self.rect.right = 798
self.pos.y = self.rect.centery
self.lives -= 1
if self.lives == 0:
return self.kill()
bounce.play()
回答1:
I have a solution. First check for a collision with bullet.rect.colliderect(wall.rect)
, then check if the center of the bullet is left of the wall, then it must be colliding on the left side, using this logic, you can do the same for the other sides.
So in the bullet update, i added:
for wall in game.wall_list:
if self.rect.colliderect(wall.rect): #if collided
if self.rect.centerx < wall.rect.left:
self.direction.x *= -1
self.rect.right = wall.rect.left
self.lives -= 1
if self.lives == 0:
return self.kill()
break # break so dont check every other wall
if self.rect.centerx > wall.rect.right:
self.direction.x *= -1
self.rect.left = wall.rect.right
self.lives -= 1
if self.lives == 0:
return self.kill()
break
if self.rect.centery < wall.rect.top:
self.direction.y *= -1
self.rect.bottom = wall.rect.top
self.lives -= 1
if self.lives == 0:
return self.kill()
break
if self.rect.centery > wall.rect.bottom:
self.direction.y *= -1
self.rect.top = wall.rect.bottom
self.lives -= 1
if self.lives == 0:
return self.kill()
break
来源:https://stackoverflow.com/questions/61135231/how-does-bullet-reflect-of-the-wall