it would be really amazing if you could help us with this problem . We're currently working on a game project at school and we want to create a Bomberman-like labyrinth game.
The problem is the following:
the position and the speed of the bombs depend on the mouse which is tracked using float values but is getting way too high if you click far away so it can skip collision detection because it just goes too fast at times. Now there are some other weird errors when you line up the bomb with the bottom of a block and some other collisions.
Is the method colliderect the error ? Do we need to check for each site or are there any better solutions to this problem. We also thought about just using 4 directions but even that didn't work out properly.
I really hope somebody out there can help.
class Block( pygame.sprite.Sprite):
def __init__(self,color = SCHWARZ, width = 40, height = 40, movable = 0):
super().__init__()
self.width = width
self.height = height
self.color = color
self.movable = movable
self.image = pygame.Surface((width,height))
self.image.fill(color)
self.properties()
def properties(self):
self.rect = self.image.get_rect()
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
def set_pos(self, x, y):
self.rect.x = x
self.rect.y = y
def render(self,window):
pygame.draw.rect(window,self.color,(self.rect.x,self.rect.y,self.width,self.height))
class Bomb(Block):
def __init__(self,player, target_x, target_y,countdown,zeit,color = BLAU):
super().__init__(color, width = 20, height = 20)
self.target_x = round(target_x)
self.target_y = round(target_y)
self.countdown = countdown
self.zeit = zeit
self.player_x = player.rect.left + player.width/2
self.player_y = player.rect.top + player.height/2
self.set_pos(player.rect.center[0]-self.width/2,player.rect.center[1]-self.height/2)
self.truepos = list(self.rect.center)
self.setspeed(3)
def setspeed(self,factor):
self.speed_x = (self.target_x - self.player_x)/100
self.speed_y = (self.target_y - self.player_y)/100
"""self.direction = [0,0]
if self.target_x - self.player_x < 0:
self.direction[0] = -1
elif self.target_x - self.player_x > 0:
self.direction[0] = 1
if self.target_y - self.player_y > 0:
self.direction[1] = 1
elif self.target_y - self.player_y < 0:
self.direction[1] = -1
self.speed_x = self.direction[0]* factor
self.speed_y = self.direction[1]* factor
print(self.speed_x)
print(self.speed_y)"""
def move(self):
if self.speed_x != 0:
self.collision()
if self.speed_y != 0:
self.collision()
def update(self):
self.move()
if self.countdown > 0:
self.countdown -= self.zeit
elif self.countdown <= 0:
bomblist_list.remove(self)
def collision(self):
for block in block_list:
if block.movable != 1:
if self.rect.colliderect(block.rect):
self.distance = [abs(block.rect.centerx - (self.truepos[0] + self.speed_x)), abs(block.rect.centery - (self.truepos[1] + self.speed_y))]
if self.distance[0] > self.distance[1]:
if self.speed_x < 0:
self.rect.left = block.rect.right - self.speed_x
elif self.speed_x > 0:
self.rect.right = block.rect.left -self.speed_x
self.speed_x = -self.speed_x
elif self.distance[0] < self.distance[1]:
if self.speed_y < 0:
self.rect.top = block.rect.bottom - self.speed_y
elif self.speed_y > 0:
self.rect.bottom = block.rect.top - self.speed_y
self.speed_y = -self.speed_y
self.truepos[0] += self.speed_x
self.truepos[1] += self.speed_y
self.rect.center = self.truepos
# -------- Main Program Loop -----------
while not done:
clock.tick(fps)
millisekunde = float(clock.tick(fps))
zeit = millisekunde /500
vergangen += zeit
# --- Main event loop
for event in pygame.event.get(): # User macht irgendwas
if event.type == pygame.QUIT: # Wenn der User Quit klickt
done = True # Verlässt die Schleife
elif event.type == pygame.KEYDOWN:
pass
#if event.key == pygame.K_UP:
#if e
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
mx,my = pygame.mouse.get_pos()
bomb = Bomb(player,mx,my,3,zeit)
bomblist_list.append(bomb)
planted = True
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player.move(speedx,0)
if keys[pygame.K_LEFT]:
player.move(-speedx,0)
if keys[pygame.K_DOWN]:
player.move(0,speedy)
if keys[pygame.K_UP]:
player.move(0,-speedy)
if planted:
for bomb in bomblist_list:
bomb.update()
screen.fill(SCHWARZ)
for block in block_list:
block.render(screen)
if planted:
for bomb in bomblist_list:
bomb.render(screen)
pygame.draw.rect(screen,red,player.rect)
pygame.display.update()
pygame.quit()
When checking collision on fast moving items that can pass an object in one frame, what you want to do is store it's position on the last frame and get it's position on the current frame. With this you can check if a it should have collided with anything between the last and current frame.
Then you can amend it's position by putting it back to where it should have stopped and subsequently draw the object.
Related
I have been having this issue where when i run my code, the sprites keep jittering even though the screen doesn't force them to do this.
This is where I define the screen size
This is where I update my actions and draw
Here is the part where I load my images from the spritesheet
Any help will be much appreciated.
I tried importing the sprites from the spritesheet onto the game with the animations all working however there are no animations and the sprite continues to jitter up and down
if you would like the code for my fighter class please see below:
def load_images(self, sprite_sheet, animation_steps):
#extract the certain animation images from spritesheet
animation_list = []
for y, animation in enumerate(animation_steps):
temporary_img_list = []
for x in range(animation):
temporaryimg = sprite_sheet.subsurface(x , y , self.size, self.size)
temporary_img_list.append(pygame.transform.scale(temporaryimg, (self.size * self.image_scale, self.image_scale * self.size)))
animation_list.append(temporary_img_list)
print(animation_list)
return animation_list
def move(self, screen_base, screen_height, surface, target):
speed = 10
gravity = 2
dx = 0
dy = 0
self.running = False
self.attack_type = 0
#key-presses recall
key = pygame.key.get_pressed() = pygame.key.get_pressed()
#can only happen if no other attack is occuring
if self.attacking == False:
#movement defined here
if key[pygame.K_a]:
dx = -speed
self.running = True
if key[pygame.K_d]:
dx = speed
self.running = True
#jump is defined here
if key[pygame.K_w] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack is defined here
if key[pygame.K_k] or key[pygame.K_j]:
self.attack(surface, target)
#determine which attack type was used when the key was pressed
if key[pygame.K_k]:
self.attack_type = 1
if key[pygame.K_j]:
self.attack_type = 2
#apply gravity for the jumps
self.vel_y += gravity
dy += self.vel_y
#this makes sure that the character does not go off the screen
if self.rect.left + dx < 0:
dx = -self.rect.left
if self.rect.right + dx > screen_base:
dx = screen_base - self.rect.right
if self.rect.bottom + dy > screen_height - 207:
self.vel_y = 0
self.jump = False
dy = screen_height - 207 - self.rect.bottom
#ensures the players face each other
if target.rect.centerx > self.rect.centerx:
self.flip = False
else:
self.flip = True
#apply the attack cooldown to balance the amount of attacks possible
if self.attack_cooldown > 0:
self.attack_cooldown -= 1
#update the player position constantly
self.rect.x += dx
self.rect.y += dy
#handle the animation updates
def update(self):
#check what action the player is currently doing
if self.health <= 0:
self.health = 0
self.alive = False
self.update_action(6)
elif self.hit == True:
self.update_action(5)
elif self.attacking == True:
if self.attack_type == 1:
self.update_action(5) # 3 = Attack
elif self.attack_type == 2:
self.update_action(6) # 4 = Attack2
elif self.jump == True:
self.update_action(7) # 7 = Jump
elif self.running == True:
self.update_action(2) # 2 = Run
else:
self.update_action(0) # 0 = Idle
animation_cooldown = 50
#update the image
self.image = self.animation_list[self.action][self.frameindex]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.frameindex += 1
self.updatetime = pygame.time.get_ticks()
#check if the animation has finished
if self.frameindex >= len(self.animation_list[self.action]):
#if the player is dead end the animation
if self.alive == False:
self.frameindex = len(self.animation_list[self.action]) - 1
else:
self.frameindex = 0
#check if an attack was executed
if self.action == 5 or self.action == 6:
self.attacking = False
self.attack_cooldown = 20
#check if damage was taken
if self.action == 7:
self.hit = False
#if the player was in the middle on an attack, then the attack is stopped
self.attacking= False
self.attack_cooldown = 20
def attack(self, surface, target):
if self.attack_cooldown == 0:
self.attacking = True
attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect):
target.health -= 5
target.hit = True
pygame.draw.rect(surface, (0, 255, 0), attacking_rect)
def update_action(self, new_action):
#check if the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frameindex = 0
self.update_time = pygame.time.get_ticks()
def draw(self, surface):
img = pygame.transform.flip(self.image, self.flip, False)
#pygame.draw.rect(surface,(255, 0, 0), self.rect)
surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))
Most likely the sprites are not cropped well from the sprite sheet. If there is random spacing on the cropped sprites (for example: the sprites are different sizes or some sprites have a border at the bottom and some sprites have a border at the top), then you get a jittery and wobbly character.
I am trying to follow this tutorial https://opensource.com/article/19/11/simulate-gravity-python to allow my sprite to go to the bottom edge of the screen and nothing more. But following the tutorial code to the point where it says:
... Make your gravity function look like this:
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty
even though the player doesn't stop at the bottom edge of the screen when using gravity.
I tried to add one more ty as suggested in the tutorial where it says
... An easy fix is to make your player sprite bounce higher by adding another -ty to its new Y position after it hits the bottom of the game world:
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty-ty
so that my code looks like this:
import pygame
import sys
import os
'''
Objects
'''
class Enemy(pygame.sprite.Sprite):
'''
Spawn an enemy
'''
def __init__(self, x, y, img):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('images', img))
#self.image.convert_alpha()
#self.image.set_colorkey(ALPHA)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.counter = 0
def move(self):
'''
enemy movement
'''
distance = 20
speed = 8
if self.counter >= 0 and self.counter <= distance:
self.rect.x += speed
elif self.counter >= distance and self.counter <= distance * 2:
self.rect.x -= speed
else:
self.counter = 0
self.counter += 1
class Level:
def __init__(self):
self.enemy_list = pygame.sprite.Group() # create enemy group
def bad_1(self, lvl, eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
self.enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return self.enemy_list
def bad_2(self, lvl, eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'spr.png') # spawn enemy
self.enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return self.enemy_list
class Player(pygame.sprite.Sprite):
'''
Spawn a player
'''
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty-ty-ty-ty-ty-ty
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.health = 10
self.frame = 0
self.images = []
for i in range(1, 5):
img = pygame.image.load(
os.path.join('images', 'hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(ALPHA)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
def control(self, x, y):
'''
control player movement
'''
self.movex += x
self.movey += y
def update(self):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame // ani]
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[(self.frame // ani)]
'''
Setup
'''
worldx = 560
worldy = 420
fps = 40 # frame rate
ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
BLUE = (25, 25, 200)
BLACK = (23, 23, 23)
WHITE = (254, 254, 254)
ALPHA = (0, 255, 0)
world = pygame.display.set_mode([worldx, worldy])
backdrop = pygame.image.load(os.path.join('images', 'stage.png')).convert()
backdropbox = world.get_rect()
player = Player() # spawn player
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
l = Level()
eloc = [200,20]
enemy_list = l.bad_1(1, eloc)
eloc = [100,10]
enemy_list = l.bad_2(1, eloc)
'''
Main loop
'''
while main == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps, 0)
if event.key == pygame.K_UP or event.key == ord('w'):
print('jump')
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps, 0)
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
# world.fill(BLACK)
world.blit(backdrop, backdropbox)
player.gravity() # check gravity
player.update()
player_list.draw(world) #refresh player position
enemy_list.draw(world)
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
but the sprite doesn't stop at the bottom edge of the screen (again) and I get the following error when the code breaks by itself on execution:
Traceback (most recent call last):
File "game.py", line 188, in <module>
player.gravity() # check gravity
File "game.py", line 72, in gravity
self.rect.y = worldy-ty-ty-ty-ty-ty-ty
NameError: name 'ty' is not defined
If I try to decrease or increase (depending on how much bigger or smaller) the screen size where it says:
worldx = 560
worldy = 420
I get the following error:
Traceback (most recent call last):
File "game.py", line 188, in <module>
player.gravity() # check gravity
File "game.py", line 72, in gravity
self.rect.y = worldy-ty
NameError: name 'ty' is not defined
I have encountered this question Pygame Gravity Script but comparing I could not understand where the code failure arises.
The original images are from here https://opengameart.org/sites/default/files/opp2_sprites.zip but I have separated in imgur for easy explanation:
content folder images:
For enemy (sprit) is yeti.png: https://imgur.com/GNNcU6z
For background is stage.png: https://imgur.com/YyiEJ0q
and the image of the player:
spr.png: https://imgur.com/1fYXa7Y
In original code on https://opensource.com/article/19/11/simulate-gravity-python is
ty = 64 #tile size
but you don't have it in your code - and it gives your error.
But you could use rect.bottom and then you may not need ty
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.bottom > worldy and self.movey >= 0: # <-- uses bottom
self.movey = 0
self.rect.bottom = worldy # <-- uses bottom
BTW: If you will add plaforms to game then you can create platform at the bottom (below bottom border) and then it should stop player.
BTW: To stop on platform it would need
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
for p in plat_hit_list:
#self.health -= 1
#print(self.health)
self.rect.bottom = p.rect.top
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
for g in ground_hit_list:
#self.health -= 1
#print(self.health)
self.rect.bottom = g.rect.top
It would be good also to reduce gravity speed because in very big speed it may jump below platform without checking collision with platform.
def gravity(self):
self.movey += 3.2 # how fast player falls
#print(self.rect.bottom, worldy)
# reduce speed so it will not jump out platfrom
if self.movey >= 15:
self.movey = 6
if self.rect.bottom > worldy and self.movey >= 0:
self.movey = 0
self.rect.bottom = worldy
#print(self.rect.bottom, worldy)
I am trying to create a retro style 2d shooter game and am currently making a homing missile. I need to randomly select a sprite from a group (pygame.sprite.Group) and find out its x and y coords. I have already made it home in on these coords so I do not need help with that. My code is:
an Enemy class (they are all the same just with different sizes and pictures):
class EnemyShipLevel1(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("C:/Users /Me/PycharmProjects/Game folder/SpaceShip game/Enemy1.png")
self.rect = self.image.get_rect()
self.rect.y = -50
self.rect.centerx = random.randint(15, SCREEN_WIDTH-15)
self.change_y = 0
self.cooldown = 0
self.targetx = 500
self.health = 1
if self.targetx >= self.rect.centerx:
self.change_x = 2
else:
self.change_x = -2
def update(self):
self.rect.x += self.change_x
self.rect.y += self.change_y
if self.rect.centery < 200:
self.rect.centery += 2
self.get_target()
if self.targetx > self.rect.centerx+10:
self.change_x = 3
elif self.targetx < self.rect.centerx-10:
self.change_x = -3
else:
self.change_x = 0
self.cooldown -= 1
if self.rect.left < screen_rect.left:
self.change_x = -self.change_x
if self.rect.right > screen_rect.right:
self.change_x = -self.change_x
if self.cooldown < 0 and (self.rect.centerx-50 < self.targetx < self.rect.centerx+50) and player.trans is False:
self.cooldown = random.randint(15, 25)
laser = NormalLaser(self.rect.centery, self.rect.centerx, False, 0, 8)
all_sprite_list.add(laser)
enemy_laser_list.add(laser)
player_laser_hit_list = pygame.sprite.spritecollide(self, player_laser_list, True)
for laser in player_laser_hit_list:
if self.health < 1:
all_sprite_list.remove(self)
enemy_list.remove(self)
else:
self.health -= 1
def get_target(self):
self.targetx = player.rect.centerx
Targeting Laser class (I know you can't bend lasers):
class TargetingLaser(pygame.sprite.Sprite):
def __init__(self, start_x, start_y, dest_x, dest_y):
super().__init__()
self.floating_point_x = start_x
self.floating_point_y = start_y
self.x_diff = dest_x - start_x
self.y_diff = dest_y - start_y
self.angle = math.atan2(self.y_diff, self.x_diff)
self.velocity = 10
self.change_x = math.cos(self.angle) * self.velocity
self.change_y = math.sin(self.angle) * self.velocity
self.image = pygame.image.load("C:/Users/Minecraft/PycharmProjects/Game folder/SpaceShip game/Laser.png")
self.image = pygame.transform.rotate(self.image, -(math.degrees(self.angle)+90))
self.rect = self.image.get_rect()
self.rect.centerx = start_x
self.rect.centery = start_y
def update(self):
self.floating_point_y += self.change_y
self.floating_point_x += self.change_x
self.rect.y = int(self.floating_point_y)
self.rect.x = int(self.floating_point_x)
if self.rect.top < screen_rect.top:
player_laser_list.remove(self)
enemy_laser_list.remove(self)
all_sprite_list.remove(self)
if self.rect.bottom > screen_rect.bottom:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
if self.rect.left < screen_rect.left:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
if self.rect.right > screen_rect.right:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
Game Loop:
all_sprite_list = pygame.sprite.Group()
enemy_list = pygame.sprite.Group()
player_list = pygame.sprite.GroupSingle()
player_laser_list = pygame.sprite.Group()
enemy_laser_list = pygame.sprite.Group()
live_list = pygame.sprite.Group()
health_bar_list = pygame.sprite.GroupSingle()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
screen_rect = screen.get_rect()
pygame.display.set_caption('Game')
clock = pygame.time.Clock()
Play = True
player = Player(SCREEN_WIDTH/2, SCREEN_HEIGHT-30)
all_sprite_list.add(player)
player_list.add(player)
for counter in range(2):
enemy = EnemyShipLevel1()
all_sprite_list.add(enemy)
enemy_list.add(enemy)
live1 = LivesIcon(5, 0, 1)
live_list.add(live1)
live1 = LivesIcon(27, 30, 2)
live_list.add(live1)
live1 = LivesIcon(50, 0, 3)
live_list.add(live1)
live1 = LivesIcon(72, 30, 4)
live_list.add(live1)
live1 = LivesIcon(95, 0, 5)
live_list.add(live1)
all_sprite_list.add(live_list)
health_bar = PlayerHealthBar(0, 690)
all_sprite_list.add(health_bar)
health_bar_list.add(health_bar)
ship = EnemyShipLevel3()
all_sprite_list.add(ship)
enemy_list.add(ship)
while Play is True:
# Shooting
if player.shooting is True:
if player.laser_cooldown < 0:
player.laser_cooldown = 5
if player.last_shot == "left":
player.last_shot = "right"
laser = NormalLaser(player.rect.centery-2, player.rect.centerx + 11, True, 0, 8)
all_sprite_list.add(laser)
player_laser_list.add(laser)
else:
player.last_shot = "left"
laser = NormalLaser(player.rect.centery-2, player.rect.centerx - 11, True, 0, 8)
all_sprite_list.add(laser)
player_laser_list.add(laser)
if player.rocket_cooldown < 0:
player.rocket_cooldown = 15
for counter in range(10):
laser = TargetingLaser(player.rect.centerx,player.rect.centery, player.rect.centerx, 0)
all_sprite_list.add(laser)
player_laser_list.add(laser)
# Shooting
# Levels
if len(enemy_list) == 0:
all_sprite_list.remove(enemy_laser_list)
all_sprite_list.remove(player_laser_list)
enemy_laser_list.empty()
player_laser_list.empty()
player.level += 1
for counter in range(player.spawn_enemies1):
enemy = EnemyShipLevel1()
all_sprite_list.add(enemy)
enemy_list.add(enemy)
for counter in range(player.spawn_enemies2):
enemy2 = EnemyShipLevel2()
all_sprite_list.add(enemy2)
enemy_list.add(enemy2)
for counter in range(player.spawn_enemies3):
ship = EnemyShipLevel3()
all_sprite_list.add(ship)
enemy_list.add(ship)
if player.level == 2:
player.spawn_enemies1 = 3
player.spawn_enemies2 = 1
if player.level == 3:
player.spawn_enemies1 = 5
player.spawn_enemies2 = 2
if player.level == 4:
player.spawn_enemies1 = 10
player.spawn_enemies2 = 5
if player.level == 5:
player.spawn_enemies1 = 12
player.spawn_enemies2 = 8
player.spawn_enemies3 = 1
# Levels
# End Game
if player.lives == 0:
Play = False
# End Game
# Keys
for event in pygame.event.get():
if event.type == pygame.QUIT:
Play = False
elif event.type == pygame.KEYDOWN:
if (event.key == pygame.K_a) or (event.key == pygame.K_LEFT):
player.change_x = -5
elif (event.key == pygame.K_d) or (event.key == pygame.K_RIGHT):
player.change_x = 5
if event.key == pygame.K_SPACE:
player.shooting = True
elif event.type == pygame.KEYUP:
player.rotation = 0
player.change_x = 0
player.change_y = 0
if event.key == pygame.K_SPACE:
player.shooting = False
# Keys
all_sprite_list.update()
screen.fill(BLACK)
all_sprite_list.draw(screen)
enemy_laser_list.draw(screen)
player_laser_list.draw(screen)
pygame.display.update()
clock.tick(60)
quit()
Each time you kill all of the spawned enemies then the game spawns in more and adds them to enemy_list().
If you are asking how to randomly choose a sprite then use a random.randint(0, 30) (or the number of sprites) then do a long series of if statements. Example Code.
import random
import pygame
class Enemy:
def __init__()
random_sprite = random.randint(0, 1)
# Then put in two variables which equal to the png (I will use sprite1 and sprite2)
def sprite_render
if random_sprite == 0:
# Just blit sprite1
elif random_sprite == 1:
# Just blit sprite2
(The reason why I just commented the actions for the image stuff is because almost everyone does it in a different way and that is not the main point of this answer.)
You could assign a number attribute to each enemy.
For example, you could start off your enemy function like this:
class EnemyShipLevel1(pygame.sprite.Sprite):
number = None
def __init__(self,number):
super().__init__()
self.number = number
self.image = pygame.image.load("C:/Users /Me/PycharmProjects/Game folder/SpaceShip game/Enemy1.png")
Now when you spawn in the enemies, you would add the number attribute:
for counter in range(2): # change this number to use more than 2 enemies (I use 30 later)
enemy = EnemyShipLevel1(counter)
all_sprite_list.add(enemy)
enemy_list.add(enemy)
All you have to do is randomly choose the sprite:
choice = random.randrange(0,30) # between 0 and the number of enemies spawned
for i in enemy_list:
if i.number == choice:
# make i the target of the laser
target_x = i.rect.x
target_y = i.rect.y
#imports
import pygame
#globals
SCREENWIDTH = 800
SCREENHEIGHT = 480
class Player(pygame.sprite.Sprite()):
#relevant variables
change_x = 0
change_y = 0
level = None
def __init__(self):
pygame.sprite.Sprite.__init__()
self.width = 25
self.height = 25
self.image = pygame.Surface([width,height])
self.image.fill(pygame.color.Color('RED'))
def update(self):
""" Move the player. """
# Move left/right
self.rect.x += self.change_x
# See if we hit anything
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, 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
elif self.change_x < 0:
# Otherwise if we are moving left, do the opposite.
self.rect.left = block.rect.right
self.change_x = 0
# Move up/down
self.rect.y += self.change_y
# Check and see if we hit anything
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, 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
elif self.change_y < 0:
self.rect.top = block.rect.bottom
# Stop our vertical movement
self.change_y = 0
def go_left(self):
self.change_x = -1
def go_right(self):
self.change_x = 1
def go_up(self):
self.change_y = -1
def go_down(self):
self.change_y = 1
def stop(self):
self.change_x = 0
#collideable object
class Wall(pygame.sprite.Sprite):
level = None
def __init__(self, width, height):
pygame.sprite.Sprite.__init__()
self.image = pygame.Surface([width,height])
self.image.fill(pygame.color.Color('Yellow'))
self.rect = self.image.get_rect()
#collideable object that will later take players to different rooms
class Door(Wall):
level = None
def __init__(self):
Wall.__init__()
self.width = 5
self.height = 30
class Level(object):
background = None
world_shift_x = 0
world_shift_y=0
level_limit_x = -1000
level_limit_y = -2000
def __init__(self, player):
self.wall_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
self.door_list = pygame.sprite.Group()
def update(self):
self.wall_list.update()
self.enemy_list.update()
self.door_list.update()
def draw(self):
screen.fill(pygame.color.Color('Black'))
self.wall_list.draw(screen)
self.enemy_list.draw(screen)
self.door_list.draw(screen)
def shift_world(self, shift_x, shift_y):
#scrolls the screen at a speed of "shift x or y"
self.world_shift_x += shift_x
self.world_shift_y += shift_y
#shifts items within the world with the world
for wall in self.wall_list:
wall.rect.x += shift_x
wall.rect.y += shift_y
for door in self.door_list:
door.rect.x += shift_x
door.rect.y += shift_y
for enemy in self.enemy_list:
enemy.rect.x += shift_x
enemy.rect.y += shift_y
class Level01(Level):
#test level
def __init__(self,player):
Level.__init__()
self.level_limit_x = -1000
self.level_limit_y = -2000
level = [[200,800,0,SCREENHEIGHT],
[200,600,50,SCREENHEIGHT],
[850,200,50,0],
[600,200,50,-50]
]
for wall in level:
block = Wall(wall[0], wall[1])
block.x = wall[2]
block.y = wall[3]
block.player = player
self.wall_list.add(wall)
def main():
pygame.init()
size = [SCREENWIDTH, SCREENHEIGHT]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Game")
player = Player()
level_list = []
level_list.append(Level_01(player))
current_levelno = 0
current_level = level_list[current_level_no]
active_sprite_list = pygame.sprite.Group()
player.level = current_level
player.rect.x = 340
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.go_left()
if event.key == pygame.K_d:
player.go_right()
if event.key == pygame.K_w:
player.jump()
if event.type == pygame.KEYUP:
if event.key == pygame.K_a and player.change_x < 0:
player.stop()
if event.key == pygame.K_d and player.change_x > 0:
player.stop()
active_sprite_list.update()
current_level.update()
if player.rect.right >= 380:
diff_x = player.rect.right - 380
player.rect.right = 380
current_level.shift_world(-diff_x)
if player.rect.left <= 120:
diff_x = 120 - player.rect.left
player.rect.left = 120
current_level.shift_world(diff_x)
if player.rect.bottom <= 700:
diff_y = player.rect.bottom - 700
player.rect.bottom = 700
current_level.shift_world(-diff_y)
if player.rect.top >= 100:
diff_y = 100 - player.rect.bottom
player.rect.bottom = 100
current_level.shift_world(diff_y)
current_position = player.rect.x + current_level.world_shift
if current_position < current_level.level_limit:
if current_level_no < len(level_list)-1:
player.rect.x = 120
current_level_no += 1
current_level = level_list[current_level_no]
player.level = current_level
else:
done = True
current_level.draw(screen)
active_sprite_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
The endgame of this code is to make a functional top down game with the goals of being a psychological horror. So far the mechanics in play are collision with walls, and, by extension, doors, levels, and character movements.
In total, all I can tell anyone is that this error has to do with everything in the code that isn't globals and imports(unless I'm wrong, which would make more sense to me.), and that it has something to do with
A) Code line 140
B) Throws the error under the specific line under 'sprite.py' that says 'self.add(*group) (which is line 142 in the 'sprite.py' file)
The stuff I can speculate about in this is that perhaps the code is throwing an infinite loop, as watching the console shows a code in the IDLE Shell, or the messages section of the pyscripter(what I'm using to program this), which iterates indefinitely before the program terminates. No visuals are created, and nothing appears to be happening, save the infinite loop. The code message is 'File "C:\Python33\lib\site-packages\pygame\sprite.py", line 142, in add self.add(*group) I can't say for sure, it's probably better that you run the code yourself to see what you can gather.
I'm making a space invaders type game using python and pygame. I currently have where I can move my user ship back and forth along the bottom with my arrow keys, and my enemies spawn and move back and forth across the top. My problem is with the enemies. I have it set so if they hit 100 pixels from the edge of the game screen, they start going back the other direction. This results in an overlap while the ones that have hit the limit are already heading back and the ones that haven't hit the limit are still going the original direction. I want it so that when the enemies on the edge hit the limit, all the enemies immediately go back in the other direction, just like in space invaders. My code is below. I have KeepFormation under enemies to try to do what I wanted, but it isn't working. Thank you!
from pygame import *
size_x = 900
size_y = 650
class Object:
def disp(self, screen):
screen.blit(self.sprite, self.rect)
class User_Ship(Object):
def __init__(self):
self.sprite = image.load("mouse.bmp")
self.rect = self.sprite.get_rect()
self.rect.centerx = size_x/2
self.rect.centery = size_y - 40
self.count = 0
self.move_x = 0
self.move_y = 0
def checkwith(self, otherrect):
if self.rect.colliderect(otherrect):
exit()
def cycle(self):
self.rect.centerx += self.move_x
if self.rect.centerx < 100:
self.rect.centerx = 100
if self.rect.centerx > size_x - 100:
self.rect.centerx = size_x - 100
self.rect.centery += self.move_y
if self.rect.centery < 0:
self.rect.centery = 800
def right(self):
self.move_x += 10
def left(self):
self.move_x -= 10
def stop_x(self):
self.move_x = 0
def stop_y(self):
self.move_y = 0
def shoot(self):
if keys[pygame.K_SPACE]:
self.Bullet.x = self.rect.centerx
self.Bullet.y = self.rect.centery
self.Bullet.speed = 10
class Enemys(Object):
def __init__(self):
self.sprite = image.load("ball.bmp")
self.rect = self.sprite.get_rect()
self.rect.centerx = size_x/3
self.rect.centery = 200
self.mval = -2
def KeepFormation(self, otherrect):
if self.rect.colliderect(otherrect):
self.rect.centerx += 5
def cycle(self):
self.rect.centerx += self.mval
if self.rect.centerx < 100:
self.mval = 2
elif self.rect.centerx > (size_x - 100):
self.mval = -2
class Bullet(Object):
def __init__(self):
self.sprite = image.load("missile.png")
self.rect = self.sprite.get_rect()
self.rect.centerx = -100
self.rect.centery = 100
self.speed = 0
EnemyList = []
for i in range(14):
EnemyList.append(Enemys())
EnemyList[i].rect.centerx = (i) * 50
# EnemyList[i].count = i * 20
init()
screen = display.set_mode((size_x, size_y))
m = User_Ship()
en = Enemys()
b = Bullet()
clock = time.Clock()
while True:
for e in event.get():
if e.type == QUIT:
quit()
if e.type == KEYDOWN:
if e.key == K_RIGHT:
m.right()
elif e.key == K_LEFT:
m.left()
if e.type == KEYUP:
if e.key == K_RIGHT or e.key == K_LEFT:
m.stop_x()
m.cycle()
screen.fill((255,255,255))
for enemy in EnemyList:
enemy.cycle()
enemy.disp(screen)
enemy.KeepFormation(enemy.rect)
m.disp(screen)
b.disp(screen)
display.flip()
clock.tick(60)
Add a flag in
def cycle(self):
self.rect.centerx += self.mval
if self.rect.centerx < 100:
self.mval = 2
return True # Direction change!
elif self.rect.centerx > (size_x - 100):
self.mval = -2
return True
return False # no change
But then you need to make this change happen only once. Thus...
done_once = False
for enemy in EnemyList:
if not done_once:
change = enemy.cycle()
if change and not done_once:
# call the new enemy function that changes every enemy's direction
done_once = True
enemy.disp(screen)
enemy.KeepFormation(enemy.rect)
Process this flag such that everyone should change direction now. You may need another function & direction_tracking variable in your Enemy class. Good luck & good job!