How to Add a Cool Down Between Collision Events in Python? - python

We are currently creating a game with python at my school. The game I'm making involves dodging enemies, but there is a problem: multiple collision events get activated at once, taking away all the players health. I found the temporary solution of teleporting the player but, if an enemy is at the new location, they get stuck there and die. I tried adding a few invincibility frames but it's behaving strangely.
def collision(player, hit_sprite):
global score
global score_text
global lives
play = player.get_image_name()
if play == "player_sprite_049":
hit = hit_sprite.get_image_name()
if hit == "green_star_96c":
stage.remove_sprite(score_text)
stage.remove_sprite(hit_sprite)
score += 1
score_text = codesters.Text(str(score), 200, 200, "white")
return score_text
if hit == "enemy_bullet_62c" or hit == "the_floater_1ee" or hit == "the_chaser_4a0":
lives -= 1
player.load_image("player_sprite_invincible_867")
stage.wait(2)
if lives <= 0:
stage.remove_sprite(player)
GAME = codesters.Text("GAME OVER", 0, 0, "red")
if lives <= 3:
stage.remove_sprite(H3)
if lives <= 2:
stage.remove_sprite(H2)
if lives <= 1:
stage.remove_sprite(H1)
player.load_image("player_sprite_049")
else:
sound = codesters.Sound("drum_bell")
sound.play()
player.event_collision(collision)

Add an attribute to the player, that stores the object which had hit the player:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.was_hit_by = None
Discard the collision in the function collision if the player collides several times in a row with the same object:
def collision(player, hit_sprite):
if player.was_hit_by == hit_sprite:
reteurn:
player.was_hit_by = hit_sprite
# [...]
Reset the was_hit_by attribute if the player does not collide with any object:
if collide:
collision(player, hit_sprite)
else
player.was_hit_by = None

Related

How do I move multiple identical rectangles in a list separately?

I'm trying to spawn an infinite amount of enemies at a given rate, and have them move towards the player. However, I can only manage to get two to spawn, one for each original rectangle where they spawn. This is because the movement overwrites the rectangle they spawn with, so any further enemies simply spawn on top of one of the first two. This is using the move_ip() function, which i think may be the problem, but attempting to just use move() results in no movement at all.
def main():
global DISPLAYSURF, FPSCLOCK
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
pygame.display.set_caption ('Ronin')
checkForQuit()
enemies = []
playerX = 730
enemyImage = pygame.image.load('enemyidle.png')
enemySurf = pygame.transform.scale(enemyImage, (PLAYERWIDTH, PLAYERHEIGHT))
leftRect = pygame.Rect(0, 735, PLAYERWIDTH, PLAYERHEIGHT)
rightRect = pygame.Rect(1500, 735, PLAYERWIDTH, PLAYERHEIGHT)
moveLeft = moveRight = moveUp = moveDown = False
enemycounter = 0
while True:
checkForQuit()
#Draw the background
DISPLAYSURF.blit(backgroundSurf, backgroundRect)
#draw the player
DISPLAYSURF.blit(playerSurf, playerRect)
#time the enemy spawn and draw the enemies
enemy = random.choice([leftRect, rightRect])
if enemycounter < ENEMYSPAWNRATE:
enemycounter += 1
if enemycounter == ENEMYSPAWNRATE:
enemycounter = 0
enemies.append(enemy)
enemyAI(enemies, playerRect)
for e in enemies:
DISPLAYSURF.blit(enemySurf, e)
pygame.display.update()
FPSCLOCK.tick(FPS)
def enemyAI(enemies, playerRect):
for e in enemies:
if e.left > playerRect.right:
e.move_ip(-1 * PLAYERSPEED +5, 0)
if e.right < playerRect.left:
e.move_ip(PLAYERSPEED -5, 0)
main()
I can see in the debugger that each new enemy added to the enemies list has the same coordinates as one of the first two existing enemies. But I want them to appear at the original coordinates specified by either leftRect or rightRect.
You are mostly correct. Your problem lies in creating two rectangles, and then assigning each enemy to one of those two rectangles.
Because the enemy = random.choice(...) creates an alias for either leftRect or rightRect, and because you enemies.append(enemy), you have a list that consists of aliases to the same two rectangles.
Try making a .copy() of the rectangle after you randomly choose it:
startLocations = (leftRect, rightRect)
def new_enemy():
"""Return a new enemy rect randomly in one of the start locations"""
return random.choice(startLocations).copy()
while True:
# ... as before ...
#time the enemy spawn and draw the enemies
if enemycounter < ENEMYSPAWNRATE:
enemycounter += 1
else:
enemycounter = 0
enemies.append(new_enemy())
enemyAI(enemies, playerRect)
# ... as before ...

How do i instance sprites in Pygame? Do i need too?

So im working on a game for some coursework in my computing course, im almost finished but for the life of me i cant get multiple of the Spider sprite to spawn in at the correct locations or reset properly between levels. I've tried adding different instances to groups before but i always get a different error with each method that i try. the code is below and im fairly new to pygame so sorry for the messy code..
#
class Spider(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load( 'Assets/Level-Assets/Level 1/Enemies/Spider_Down.png')
self.rect = self.image.get_rect()
self.Health = 3
self.Spawned = False
self.range = 100
self.Gravity= 6
self.pos_difference = 0
self.Active = False
self.Fall = False
self.Spiders = []
##################################################################################################################
def Set_Position(self):
if self.Spawned == False:
self.rect.center = (World.mapx, World.mapy)
Enemies.add(self)
self.Spawned = True
else:
self.rect.center = self.rect.center
######################################################################################################################
def update(self):
self.pos_difference = Player.rect.centery - self.rect.centery
if Player.rect.centerx >= self.rect.centerx -4 or Player.rect.centerx >= self.rect.centerx + 4:
if Player.rect.centery > self.rect.centery:
if self.pos_difference < 200:
self.Active = True
self.Fall = True
if self.Active == True:
if self.Fall == True:
self.rect.centery += self.Gravity
This is the code for the Spider, its not actually doing anything until it is called within the World class, which is where i believe the majority of the problem is...
def DisplayWorld(self):
self.MapLoad = False
for Row in range(len(self.newMap)):
for Col in range(len(self.newMap[Row])):
self.mapx = Col * 64
self.mapy = ((Row + self.Height_modifier) * 64)
self.tile_pos = (self.mapx, self.mapy )
if int(self.newMap[Row][Col]) == 1:
self.rect = self.Brick_Center.get_rect(left = (self.mapx) , bottom = (self.mapy))
self.World_sprites.add(World)
self.Camera_Pos_Check()
Player.Collision_Check()
Spider.Collision_Check()
Shoot.Collision_Check()
self.World_sprites.draw(screen)
elif int(self.newMap[Row][Col]) == 2:
Enemies.add(Spider(screen))
def main():
play = True
while play:
if pygame.key.get_pressed()[pygame.K_ESCAPE]:
play = False
if not Sprites.sprites():
Sprites.add(Player,Spider)
print(Sprites)
clock.tick(CLOCKRATE)
pygame.mouse.set_visible(False)
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
screen.blit(bgi,(0,0))
screen.blit(bgi,(0,500))
World.findLevel()
Sprites.update()
Enemies.draw(screen)
Sprites.draw(screen)
if Shoot.bullet == True:
Shoot.update()
for b in range(len(Shoot.bullets)):
screen.blit(Shoot.image, (Shoot.bullets[b][0],Shoot.bullets[b][1]))
UI.Health_Display()
pygame.display.flip()
Sprites = pygame.sprite.Group()
Enemies = pygame.sprite.Group()
UI = User_Interface()
World = World()
Player = Proto()
Shoot = Shoot()
Portal = Portals()
Spider = Spider()
main()
I've found your problem: you overwrite your Spider class by an instance of it (Spider()) and then give it the same name. Thus you're consistently adding the same single spider to your enemies list. Instead, you should remove this definition on the bottom of your file and where ever you're adding the (multiple) spider(s), you should create this instance.
In a more general remark, it is considered bad style (and not too great for performance) to use global variables as widely as you do. It'd be much better to pass them around between functions and classes. Also, the CamelCase capitalization you use for all of your variables is commonly only used for classes. I'd recommend checking up on pep8 to learn more about how to use common Python styles. It makes these kinds of problems easier to spot, less likely to occur, and simplifies the reading for anyone involved. Using these points properly might even improve your grade significantly ;).

Pygame Jumper collision detection not working

I am trying to make a game similar to what is seen in google chrome when the internet is down for my A Level computer science course work. I have run into an issue regarding collision detection and any help would be greatly appreciated.
When i try to declare more than one platform to stop player movement (collision detection) all platforms stop stopping player movement. But when i have only one platform stopping player movement it will work.
All of my code is located here: https://github.com/VincenzoLaRoche/ComputerScienceCourseWork
Your problem is that your are handling the platforms completely separate. So if you stand on one, you are not touching the other one so it makes you fall. For this to stop, you have to modify the t1o player methods collision_detect and do like so:
def collision_detect(self, platform):
if self.x > platform.x and self.x < platform.x2:
if self.y + 40 == platform.y:
return True
else:
return False
def do(self):
self.keys()
self.move()
self.draw()
c1 = self.collision_detect(platform(0, 500, 800, 10))
c2 = self.collision_detect(platform(0, 480, 400, 10))
if c1 or c2:
self.yVel = 0
Constants.CANJUMP = True
else:
self.yVel = 5
Constants.CANJUMP = False

Issue with while loop in game.

I'm making a game and it has different tiles obviously. But I've come to an issue in my main Game while loop.
def play():
player = player1()
while True:
room = ClubWorld.tile_at(player.x, player.y)
print(room.intro_text())
choose_action(room, player)
Example of a Tile in my game:
class GirlTile(MapTile):
def __init__(self,x,y):
self.meet_girl = Girls()
super().__init__(x, y)
def intro_text(self):
return "Hey whats up, my name is {}".format(self.meet_girl.name)
This loop keeps going as long as I'm on a game tile. It produces the available actions you have and lets the other functions know your position. it also outputs that tiles intro text, where my problem lies. I want the game to only output the intro text upon entry into a tile, once that happens i only want it to display the actions available. Suggestions?
You can keep previous_room and compare with room:
def play():
player = player1()
previous_room = None
while True:
room = ClubWorld.tile_at(player.x, player.y)
if room != previous_room:
print(room.intro_text())
previous_room = room
choose_action(room, player)
Or keep player previous position previous_x, previous_y and compare with new position
def play():
player = player1()
previous_x = None
previous_y = None
while True:
if player.x != previous_x or player.y != previous_y :
room = ClubWorld.tile_at(player.x, player.y)
print(room.intro_text())
previous_x = player.x
previous_y = player.y
choose_action(room, player)

Trouble using the same image for multiple objects

I'm making a game with Pygame and I'm trying to animate asteroids that get hit with a bullet. The animation works fine if there's just one asteroid type on the screen. I run into trouble when there's two identical asteroids on the screen. It seems that the first one on is the only one that uses my animation.
For example, say the game spawns two small asteroids, these are the exact same and I'm using glob.glob to get the 4 animation images, then I'll just use those images to create a little animation by looping over them. This works with one of the asteroids, the other will not play the animation. I'm thinking it must be because only one object can use specific images at any one time?
I'm sorry for the terrible explaination.
Here's the asteroid class I have and the call to create the animation:
class Asteroid(pygame.sprite.Sprite):
def __init__(self, screen, size):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.size = size
self.pos_x = random.randint(100,900)
pos_y= random.randint(100, 700)
self.pos_y = -pos_y
self.vel_y = 3
self.update_main = True
self.animation_path = ""
#load the image then put it above the screen, ready to drop
if self.size == "small":
self.asteroid_image = pygame.image.load("asteroid_small.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_small/*.tga"
elif self.size == "medium":
self.asteroid_image = pygame.image.load("asteroid_medium.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_medium/*.tga"
elif self.size == "med_large":
self.asteroid_image = pygame.image.load("asteroid_med_large.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_med_large/*.tga"
elif self.size == "small_med":
self.asteroid_image = pygame.image.load("asteroid_small_med.png")
elif self.size == "small_1":
self.asteroid_image = pygame.image.load("small1.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/small/*.tga"
else:
self.asteroid_image = pygame.image.load("asteroid_large.tga")
self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_large/*.tga"
#make the asteroid transparent
corner = self.asteroid_image.get_at((0,0))
self.asteroid_image.set_colorkey(corner, RLEACCEL)
#randomly rotate the image
self.new_asteroid_image = pygame.transform.rotate(self.asteroid_image, random.randint(0,340))
self.rect = self.new_asteroid_image.get_rect()
self.rect.x, self.rect.y = ((self.pos_x, self.pos_y))
self.start_animate = False
self.times = 0
#load the asteroid animation
self.animation = glob.glob(self.animation_path)
self.animation.sort()
self.animation_pos = 0
self.animation_max = len(self.animation)-1
self.animation_speed_init = 50
self.animation_speed = self.animation_speed_init
def update(self):
#if asteroid has left the screen, place it back to the top
if self.rect.y > 900:
pos_y = random.randint(100,500)
self.rect.y = -pos_y
self.rect.x = random.randint(100,900)
self.rect.y += self.vel_y
self.position = self.rect.x, self.rect.y
self.screen.blit(self.new_asteroid_image.convert(), self.position)
if self.start_animate == True:
self.animation_speed += 25
if self.animation_speed > 60:
self.update_main = False
self.new_asteroid = pygame.image.load(self.animation[self.times])
corner = self.new_asteroid.get_at((0,0))
self.new_asteroid.set_colorkey(corner, RLEACCEL)
self.new_asteroid_image = self.new_asteroid
self.animation_speed = 0
self.animation_pos += 1
self.times += 1
if self.times > self.animation_max:
self.kill()
def start_animation(self):
self.start_animate = True
#create asteroids at the start of the game
if keys[K_SPACE]:
game_over = False
player = Space_Ship("space_ship.tga", screen)
asteroid_small = Asteroid(screen, "small")
asteroid_large = Asteroid(screen, "large")
asteroid_medium = Asteroid(screen, "medium")
asteroid_med_large = Asteroid(screen, "med_large")
asteroid_group.add(asteroid_small, asteroid_large, asteroid_medium, asteroid_med_large)
player_group.add(player)
score = 0
#add asteroids after the game has started
if len(asteroid_group) < difficulty:
random_asteroid_number = random.randint(0,5)
if random_asteroid_number == 0:
#large asteroid
asteroid_large = Asteroid(screen, "large")
asteroid_group.add(asteroid_large)
elif random_asteroid_number == 1:
#medium asteroid
asteroid_medium = Asteroid(screen, "medium")
asteroid_group.add(asteroid_medium)
elif random_asteroid_number == 2:
#medium large asteroid
asteroid_med_large = Asteroid(screen, "med_large")
asteroid_group.add(asteroid_med_large)
elif random_asteroid_number == 3:
#small medium asteroid
asteroid_small_med = Asteroid(screen, "small_med")
asteroid_group.add(asteroid_small_med)
elif random_asteroid_number == 4:
#small_1 asteroid
asteroid_small_1 = Asteroid(screen, "small_1")
asteroid_group.add(asteroid_small_1)
else:
#small asteroid
asteroid_small = Asteroid(screen, "small")
asteroid_group.add(asteroid_small)
#bullet - asteroid collision
collisions = pygame.sprite.groupcollide(bullet_group, asteroid_group, True, False)
if collisions != None:
for i in collisions.values():
for sprite in i:
channel2.play(asteroid_audio)
if sprite == asteroid_large:
score += 3
sprite.start_animation()
elif sprite == asteroid_small_1 or sprite == asteroid_med_large or sprite == asteroid_medium or sprite == asteroid_small:
score += 1
sprite.start_animation()
else:
score += 1
sprite.kill()
Although I can't be sure because the bit where you call update is not shown, I have a few ideas of why it is doing what it is. It seems that the second asteroid's update is not being called. This could be for several reasons. The first reason is if you do not implement groups to update you asteroid. Basically, if you only call your single instance of the asteroid, the second one will not do anything, even show up on the screen. If this is the case, you will need to make a group, perhaps a render updates group but even a list would be fine. This would mean you could do something like:
asteroids.update()
or
for asteroid in asteroids:
asteroid.update()
the above two codes assume that you have some kind of grouping of asteroids, called asteroids.
Another possible problem, if you are implementing groups, is if the second asteroid is not getting added to this group. If you tell it to add itself to the group in the init function, this problem will not be there, however, if you call it outside of this function, you may not be doing so when you make your second asteroid. To address this problem just move the part where you add the asteroid into a group that will be updated to the init function of asteroids.

Categories