My collision detection isn't working and way to laggy - python
So here's just the base function:
def xCollision(self):
for tile in tiles:
if tile.colliderect(self.rect):
if self.x_vel >= 1:
self.rect.right = tile.left
elif self.x_vel <= -1:
self.rect.left = tile.right
def yCollision(self):
for tile in tiles:
if tile.colliderect(self.rect):
if self.y_vel >= 1:
self.rect.bottom = tile.top
elif self.y_vel <= -1:
self.rect.top = tile.bottom
This is inside the Player class.
Now, when i try this my frames go down to around 4 fps and it also isn't working.
About it not working it has to do something with the self.rect.something = tile.something because when i replace it with print() it prints whatever.
Here is the entire code for reference, And yes i just started out so don't think its gonna be any good ok?
import pygame
import random
import math
import time
# Initialize Pygame
pygame.init()
pygame.mixer.init()
# Set the size of the window
size = (900, 600)
screen = pygame.display.set_mode(size)
# Set the title of the window
pygame.display.set_caption("Classes testing")
clock = pygame.time.Clock()
fps = 60
pygame.mixer.music.load('PewPewProject/Retro Platform.wav')
pygame.mixer.music.set_volume(0.1)
pygame.mixer.music.play(-1)
pop_sound = pygame.mixer.Sound("PewPewProject/vine-boom.wav")
pop_sound.set_volume(0.5)
level = [
['1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'],
['1','E','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','E','0','0','0','1'],
['1','0','0','0','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','1','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','1','0','1','0','0','0','0','0','0','0','0','E','0','0','0','0','1'],
['1','0','0','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','E','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','E','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','1','1','1','0','1','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','1','0','0','0','1','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','1','0','1','1','1','0','E','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','1','0','1','S','1','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','1','0','0','0','1','0','0','0','0','0','1'],
['1','0','0','E','0','0','0','0','0','1','1','1','1','1','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1'],
['1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'],
]
tilex, tiley = [0, 0]
tilex_offset, tiley_offset = [0, 0]
tile_size = 50
tilemap = pygame.image.load('PewPewProject/tilemap.png')
tiles = []
collision_tiles = []
enemy_spawns = []
def Build_Level():
global tiley,tilex
tiley = -1
for rows in level:
tilex = -1
tiley += 1
for tile in rows:
tilex += 1
if tile == '1':
tile_rect = pygame.draw.rect(screen, (100, 100, 100), (tilex*tile_size+tilex_offset, tiley*tile_size+tiley_offset, tile_size, tile_size))
tiles.append(tile_rect)
elif tile == '0':
pass
elif tile == 'E':
enemy_spawns.append([tilex, tiley])
elif tile == 'S':
pygame.draw.rect(screen, (50, 255, 100), (tilex*tile_size+tilex_offset, tiley*tile_size+tiley_offset, tile_size, tile_size))
elif tile == 'R':
pygame.draw.rect(screen, (255, 50, 50), (tilex*tile_size+tilex_offset, tiley*tile_size+tiley_offset, tile_size, tile_size))
else:
print("Wrong Map Input")
def random_except(start, end, exception_start, exception_end):
num = random.randint(start, end)
while num >= exception_start and num <= exception_end:
num = random.randint(start, end)
return num
class Player:
def __init__(self, health, speed, x, y, size, healthboost, collision_tiles):
self.speed = speed
self.x = x
self.y = y
self.y_vel = 0
self.x_vel = 0
self.size = size
self.hitbox = pygame.draw.rect(screen, (230, 100, 100), (self.x, self.y, self.size, self.size))
self.gunsurface = pygame.Surface((90, 25), pygame.SRCALPHA)
self.healthboost = healthboost
self.health = health * healthboost
self.rotated_gunsurface = self.gunsurface
self.gunsurface.fill((150, 150, 150))
self.gunrect = self.gunsurface.get_rect()
self.rect = self.hitbox
self.projectiles = [Projectile(-100, 0, 0)]
self.last_shot_time = 0
self.cooldown = 0.5
self.is_slowed = False
self.alive = True
def xCollision(self):
for tile in tiles:
if tile.colliderect(self.rect):
if self.x_vel >= 1:
self.rect.right = tile.left
elif self.x_vel <= -1:
self.rect.left = tile.right
def yCollision(self):
for tile in tiles:
if tile.colliderect(self.rect):
if self.y_vel >= 1:
self.rect.bottom = tile.top
elif self.y_vel <= -1:
self.rect.top = tile.bottom
def Render(self):
self.rect = pygame.draw.rect(screen, (230, 100, 100), (self.x, self.y, self.size, self.size))
self.Gun()
def Alive(self):
if self.health <= 0:
self.alive = False
def GUI(self):
self.empty_health_bar = pygame.draw.rect(screen, (200, 20, 20), (screen.get_width()/4, 10,466, 25))
self.health_bar = pygame.draw.rect(screen, (20, 200, 20), (screen.get_width()/4, 10, self.health*4.66/self.healthboost, 25))
pygame.draw.line(screen, (0, 0, 0), (screen.get_width()/4, 10),(screen.get_width()/4+466, 10), 2)
pygame.draw.line(screen, (0, 0, 0), (screen.get_width()/4, 35),(screen.get_width()/4+466, 35), 2)
pygame.draw.line(screen, (0, 0, 0), (screen.get_width()/4, 10), (screen.get_width()/4, 35), 2)
pygame.draw.line(screen, (0, 0, 0), (screen.get_width()/4+466, 10), (screen.get_width()/4+466, 35), 2)
def Gun(self):
cursor_x, cursor_y = pygame.mouse.get_pos()
dx = cursor_x - (self.x + self.size)
dy = cursor_y - (self.y + self.size)
angle = math.atan2(dy, dx)
self.rotated_gunsurface = pygame.transform.rotate(self.gunsurface, math.degrees(angle * -1))
self.gunrect = self.rotated_gunsurface.get_rect(center = (self.x+self.size/2, self.y+self.size/2))
screen.blit(self.rotated_gunsurface, (self.gunrect.x, self.gunrect.y))
def Shoot(self):
if pygame.key.get_pressed()[pygame.K_e]:
current_time = time.time()
if current_time - self.last_shot_time > self.cooldown:
pop_sound.play()
cursor_x, cursor_y = pygame.mouse.get_pos()
dx = cursor_x - (self.x + self.size/2)
dy = cursor_y - (self.y + self.size/2)
angle = math.atan2(dy, dx)
self.projectiles.append(Projectile(self.x+self.size/2, self.y+self.size/2, angle))
self.last_shot_time = current_time
self.cooldown = 0.1
def Movement(self):
global tiley_offset, tilex_offset
keys = pygame.key.get_pressed()
if self.x >= screen.get_width()-100-self.size: # Move to the RIGHT
if keys[pygame.K_d]:
tilex_offset -= self.speed
if keys[pygame.K_a]:
self.x_vel = -self.speed
if not self.y <= 100:
if keys[pygame.K_w]:
self.y_vel = -self.speed
else:
if keys[pygame.K_w]:
tiley_offset += self.speed
if not self.y >= screen.get_height()-100-self.size:
if keys[pygame.K_s]:
self.y_vel = self.speed
else:
if keys[pygame.K_s]:
tiley_offset -= self.speed
elif self.x <= 100: # Move to the LEFT
if keys[pygame.K_a]:
tilex_offset += self.speed
if keys[pygame.K_d]:
self.x_vel = self.speed
if not self.y <= 100:
if keys[pygame.K_w]:
self.y_vel = -self.speed
else:
if keys[pygame.K_w]:
tiley_offset += self.speed
if not self.y >= screen.get_height()-100-self.size:
if keys[pygame.K_s]:
self.y_vel = self.speed
else:
if keys[pygame.K_s]:
tiley_offset -= self.speed
elif self.y >= screen.get_height()-100-self.size: # Move DOWN
if keys[pygame.K_s]:
tiley_offset -= self.speed
if keys[pygame.K_w]:
self.y_vel = -self.speed
if keys[pygame.K_a]:
self.x_vel =- self.speed
if keys[pygame.K_d]:
self.x_vel = self.speed
elif self.y <= 100: # Move UP
if keys[pygame.K_w]:
tiley_offset += self.speed
if keys[pygame.K_s]:
self.y_vel = self.speed
if keys[pygame.K_a]:
self.x_vel = -self.speed
if keys[pygame.K_d]:
self.x_vel = self.speed
else: # Default Movement
if keys[pygame.K_w]:
self.y_vel = -self.speed
if keys[pygame.K_s]:
self.y_vel = self.speed
if keys[pygame.K_a]:
self.x_vel = -self.speed
if keys[pygame.K_d]:
self.x_vel = self.speed
self.x += self.x_vel
self.xCollision()
self.y += self.y_vel
self.yCollision()
self.y_vel = 0
self.x_vel = 0
class Projectile:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.angle = angle
self.speed = 8
self.hitbox = pygame.Rect(self.x, self.y, 5,5)
def Move(self):
self.x += math.cos(self.angle) * self.speed
self.y += math.sin(self.angle) * self.speed
def Render(self):
self.hitbox = pygame.draw.circle(screen, (255, 255, 0), (int(self.x), int(self.y)), 7)
class Enemy:
def __init__(self, health, main_speed, tag, size, player, x, y):
self.health = health
self.main_speed = 3
self.tag = tag
self.size = size
self.player = player
self.speed = 3
self.x = x
self.y = y
self.alive = True
def Destroy(self):
self.alive = False
def Spawning(self):
self.hitbox = pygame.draw.rect(screen, (100, 240, 100), (self.x + tilex_offset, self.y + tiley_offset, self.size, self.size))
def Movement(self):
dx = self.player.x - self.x
dy = self.player.y - self.y
angle = math.atan2(dy - tiley_offset, dx - tilex_offset)
self.x += self.speed * math.cos(angle)
self.y += self.speed * math.sin(angle)
def main():
player = Player(100, 3, screen.get_width()/2-50, screen.get_height()/2-50, 50, 1, tiles)
enemies = []
def Spawn_enemies(num_enemies, player):
for i in range(num_enemies):
x, y = random.choice(enemy_spawns)
enemy = Enemy(1, 1, i, 25, player, x*tile_size, y*tile_size)
enemies.append(enemy)
print(x, y)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Main Loop
screen.fill((100, 100, 200))
# usage
spawn_enemies_chance = random.randint(0, 200)
number_of_enemies = len(enemies)
if spawn_enemies_chance == 1 and number_of_enemies <= 4:
Spawn_enemies(1,player)
Build_Level()
# enemy
for enemy in enemies:
if enemy.alive:
enemy.Movement()
enemy.Spawning()
for projectile in player.projectiles:
if enemy.hitbox.colliderect(projectile.hitbox):
enemy.health -= 1
pop_sound.play()
if enemy.health <= 0:
enemy.alive = False
enemies.remove(enemy)
player.projectiles.remove(projectile)
keys = pygame.key.get_pressed()
if keys[pygame.K_LSHIFT]: # Slow things down by 3 times
player.is_slowed = True
for enemy in enemies:
enemy.speed = enemy.main_speed / 3
for projectile in player.projectiles:
projectile.speed = 2.6
player.speed = 1
player.cooldown = 1.5
spawn_enemies_chance = random.randint(0, 1500)
else:
player.is_slowed = False
for enemy in enemies:
enemy.speed = enemy.main_speed
for projectile in player.projectiles:
projectile.speed = 8
player.speed = 3
player.cooldown = 0.5
spawn_enemies_chance = random.randint(0, 500)
# Get Hurt
for enemy in enemies:
if enemy.hitbox.colliderect(player):
player.health -= 10
enemies.remove(enemy)
# Projectiles
for i, projectile in enumerate(player.projectiles):
projectile.Move()
projectile.Render()
if projectile.x < 0 or projectile.x > screen.get_width() or projectile.y < 0 or projectile.y > screen.get_height():
player.projectiles.pop(i)
# player
player.Alive()
player.Movement()
player.Shoot()
player.Render()
# Renders the UI ontop of everything
player.GUI()
pygame.display.update()
clock.tick(fps)
pygame.quit()
# DO NOT DISTURB!
if __name__ == "__main__":
main()
I honestly don't know why its not working but i think i know why its lagging.
It has to check for collision with every tile every single frame which causes some lag if you have a big map i belive, that's just my thought tho.
The actual problem is not the collision detection, but that Build_Level is called in the application loop. Therefore the tiles and enemy_spawns list contain more and more elements over time and your code gets slower and slower. To solve your problem, separate the function into two functions, build_level and draw_level. Call build_level before the application loop, but draw_level in the application loop:
def build_level():
for tiley, rows in enumerate(level):
for tilex, tile in enumerate(rows):
if tile == '1':
tile_rect = pygame.Rect(tilex*tile_size+tilex_offset, tiley*tile_size+tiley_offset, tile_size, tile_size)
tiles.append(tile_rect)
elif tile == '0':
pass
elif tile == 'E':
enemy_spawns.append([tilex, tiley])
elif tile == 'S':
pass
elif tile == 'R':
pass
else:
print("Wrong Map Input")
def draw_level():
for tiley, rows in enumerate(level):
for tilex, tile in enumerate(rows):
color = None
if tile == '1':
color = (100, 100, 100)
elif tile == 'S':
color = (50, 255, 100)
elif tile == 'R':
color = (255, 50, 50)
if color:
pygame.draw.rect(screen, color, (tilex*tile_size+tilex_offset, tiley*tile_size+tiley_offset, tile_size, tile_size))
def main():
# [...]
build_level()
running = True
while running:
# [...]
draw_level()
Related
i have a problem: my enemies are stacked vertically, on top of my player which is weird, and they don't move to their predefined positions [duplicate]
This question already exists: in my shooting game, my players are positioned on top of my head (player) which is very abnormal and also once the game starts, they start shooting [closed] Closed 6 months ago. i have earlier tried to format my question properly but i hope this one is better. am creating a shooter game from https://github.com/russs123/Shooter. however my problem is that my enemies are stacked vertically upwards in a straight line when i run my game and the enemies don't start off at their predefined location. below is the full code i wrote. perhaps if you can run it, it will be better understood. import pygame, sys from player import Player import os import random import csv pygame.init() screen_width = 600 scroll_thresh = 200 screen_scroll = 0 bg_scroll = 0 screen_height = int(screen_width * 0.8) screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("Shooter Game") clock = pygame.time.Clock() fps = 60 # define game variables GRAVITY = 0.75 level = 1 ROWS = 16 COLS = 150 TILE_SIZE = screen_height // ROWS TILE_TYPES = 21 img_list = [] for x in range(TILE_TYPES): img = pygame.image.load(f"img/tile/{x}.png") img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE)) img_list.append(img) # color variables bg = (144, 201, 120) RED = (255, 0, 0) WHITE = (255, 255, 255) GREEN = (0, 255, 0) BLACK = (0, 0, 0) # define player action variables moving_left = False moving_right = False shoot = False grenade = False grenade_thrown = False # load up images pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha() pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha() mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha() sky_cloud_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha() bullet_img = pygame.image.load("img/icons/bullet.png").convert_alpha() grenade_img = pygame.image.load("img/icons/grenade.png").convert_alpha() # pick up boxes health_box_img = pygame.image.load("img/icons/health_box.png").convert_alpha() ammo_box_img = pygame.image.load("img/icons/ammo_box.png").convert_alpha() grenade_box_img = pygame.image.load("img/icons/grenade_box.png").convert_alpha() # name_dt = type(name) item_boxes = { "Health": health_box_img, "Ammo": ammo_box_img, "Grenade": grenade_img, } # define fonts font = pygame.font.SysFont("Futura", 30) def draw_text(text, font, text_col, x, y): img = font.render(text, True, text_col) screen.blit(img, (x, y)) def draw_bg(): screen.fill(bg) screen.blit(sky_cloud_img, (0, 0)) screen.blit(mountain_img, (0, screen_height - mountain_img.get_height() - 300)) screen.blit(pine1_img, (0, screen_height - pine1_img.get_height() - 150)) screen.blit(pine2_img, (0, screen_height - pine2_img.get_height())) class Soldier(pygame.sprite.Sprite): def __init__(self, char_type, x, y, scale, speed, ammo, grenades): pygame.sprite.Sprite.__init__(self) self.alive = True self.char_type = char_type self.speed = speed self.ammo = ammo self.start_ammo = ammo self.shoot_cooldown = 0 self.grenades = grenades self.health = 100 self.max_health = self.health self.direction = 1 self.vel_y = 0 self.jump = False self.in_air = True self.flip = False self.animation_list = [] self.frame_index = 0 self.action = 0 # ai specific variables self.move_counter = 0 self.vision = pygame.Rect(0, 0, 150, 20) self.idling = 0 self.idling_counter = 0 self.update_time = pygame.time.get_ticks() # load all images for the players animation_types = ["idle", "run", "jump", "death"] for animation in animation_types: temp_list = [] # reset temporary list of images # count number of files in the folder num_of_frames = len(os.listdir(f"img/{self.char_type}/{animation}")) for i in range(num_of_frames): img = pygame.image.load(f"img/{self.char_type}/{animation}/{i}.png").convert_alpha() img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale))) temp_list.append(img) self.animation_list.append(temp_list) self.image = self.animation_list[self.action][self.frame_index] self.rect = self.image.get_rect() self.rect.center = (x, y) self.width = self.image.get_width() self.height = self.image.get_height() def update(self): self.update_animation() self.check_alive() # update cooldown if self.shoot_cooldown > 0: self.shoot_cooldown -= 1 def move(self, moving_left, moving_right): screen_scroll = 0 dx = 0 dy = 0 if moving_left: dx = -self.speed self.flip = True self.direction = -1 if moving_right: dx = self.speed self.flip = False self.direction = 1 if self.jump == True and self.in_air == False: self.vel_y = -11 self.jump = False self.in_air = True # apply gravity self.vel_y += GRAVITY if self.vel_y > 10: self.vel_y dy += self.vel_y # check for collision for tile in world.obstacle_list: # check collision in the x direction if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): dx = 0 # check for collision in the y direction if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): # check if below the ground, i.e jumping if self.vel_y < 0: self.vel_y = 0 dy = tile[1].bottom - self.rect.top # check if above the ground, ie falling elif self.vel_y >= 0: self.vel_y = 0 self.in_air = False dy = tile[1].top - self.rect.bottom self.rect.x += dx self.rect.y += dy # update scroll based on player position if self.char_type == "player": if self.rect.right > screen_width - scroll_thresh or self.rect.left < scroll_thresh: self.rect.x = -dx screen_scroll = -dx return screen_scroll def shoot(self): if self.shoot_cooldown == 0 and self.ammo > 0: self.shoot_cooldown = 20 bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery, self.direction) bullet_group.add(bullet) # reduce ammo after each shot self.ammo -= 1 def ai(self): if self.alive and player.alive: if self.idling == False and random.randint(1, 200) == 1: self.update_action(0) self.idling = True self.idling_counter = 50 # check if ai is near the player if self.vision.colliderect(player.rect): # stop running and face the player self.update_action(0) self.shoot() else: if self.idling == False: if self.direction == 1: ai_moving_right = True else: ai_moving_right = False ai_moving_left = not ai_moving_right self.move(ai_moving_left, ai_moving_right) self.update_action(1) self.move_counter += 1 # update ai vision as the enemy moves self.vision.center = (self.rect.centerx + 75 * self.direction, self.rect.centery) if self.move_counter > TILE_SIZE: self.direction *= -1 self.move_counter *= -1 else: self.idling_counter -= 1 if self.idling_counter <= 0: self.idling = False # scroll self.rect.x = screen_scroll def update_animation(self): ANIMATION_COOLDOWN = 100 # update image depending on current index self.image = self.animation_list[self.action][self.frame_index] # check if enough time has passed since the last update if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN: self.update_time = pygame.time.get_ticks() self.frame_index += 1 # if animation has run out then restart to the first if self.frame_index >= len(self.animation_list[self.action]): if self.action == 3: self.frame_index = len(self.animation_list[self.action]) - 1 else: self.frame_index = 0 def check_alive(self): if self.health <= 0: self.health = 0 self.speed = 0 self.alive = False self.update_action(3) def update_action(self, new_action): # check if new action is different from the previous one if new_action != self.action: self.action = new_action # update animation settings self.frame_index = 0 self.update_time = pygame.time.get_ticks() def draw(self): screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect) class HealthBar(): def __init__(self, health, x, y, max_health): self.x = x self.y = y self.health = health self.max_health = max_health def draw(self, health): self.health = health pygame.draw.rect(screen, BLACK, (self.x - 2, self.y - 2, 154, 24)) pygame.draw.rect(screen, RED, (self.x, self.y, 150, 20)) ratio = self.health / self.max_health pygame.draw.rect(screen, GREEN, (self.x, self.y, 150 * ratio, 20)) class World(): def __init__(self): self.obstacle_list = [] def process_data(self, data): for y, row in enumerate(data): for x, tile in enumerate(row): if tile >= 0: img = img_list[tile] img_rect = img.get_rect() img_rect.x = x * TILE_SIZE img_rect.y = y * TILE_SIZE tile_data = (img, img_rect) if 0 <= tile <= 8: self.obstacle_list.append(tile_data) elif 9 <= tile <= 10: # water water = Water(img, x * TILE_SIZE, y * TILE_SIZE, ) water_group.add(water) pass elif 11 <= tile <= 14: # decoration decoration = Decoration(img, x * TILE_SIZE, y * TILE_SIZE) decoration_group.add(decoration) elif tile == 15: # create a player player = Soldier("player", x * TILE_SIZE, y * TILE_SIZE, 1.65, 5, 20, 5) health_bar = HealthBar(10, 10, player.health, player.health) elif tile == 16: # create enemy enemy = Soldier("enemy", x * TILE_SIZE, y * TILE_SIZE, 1.65, 2, 20, 0) enemy_group.add(enemy) elif tile == 17: # create ammo box item_box = ItemBox("Ammo", x * TILE_SIZE, y * TILE_SIZE) item_box_group.add(item_box) elif tile == 18: # create grenade box item_box = ItemBox("Grenade", x * TILE_SIZE, y * TILE_SIZE) item_box_group.add(item_box) elif tile == 19: # create health box item_box = ItemBox("Health", x * TILE_SIZE, y * TILE_SIZE) item_box_group.add(item_box) elif tile == 20: # create exit point exit = Exit(img, x * TILE_SIZE, y * TILE_SIZE, ) exit_group.add(exit) return player, health_bar def draw(self): for tile in self.obstacle_list: tile[1][0] += screen_scroll screen.blit(tile[0], tile[1]) class Decoration(pygame.sprite.Sprite): def __init__(self, img, x, y): pygame.sprite.Sprite.__init__(self) self.image = img self.rect = self.image.get_rect() self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) def update(self): self.rect.x += screen_scroll class Water(pygame.sprite.Sprite): def __init__(self, img, x, y): pygame.sprite.Sprite.__init__(self) self.image = img self.rect = self.image.get_rect() self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) def update(self): self.rect.x += screen_scroll class Exit(pygame.sprite.Sprite): def __init__(self, img, x, y): pygame.sprite.Sprite.__init__(self) self.image = img self.rect = self.image.get_rect() self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) class ItemBox(pygame.sprite.Sprite): def __init__(self, item_type, x, y): pygame.sprite.Sprite.__init__(self) self.item_type = item_type self.image = item_boxes[self.item_type] self.rect = self.image.get_rect() self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) def update(self): # scroll self.rect.x += screen_scroll # check if player has picked up the box if pygame.sprite.collide_rect(self, player): # check what kind of box it was if self.item_type == "Health": player.health += 25 if player.health > player.max_health: player.health = player.max_health elif self.item_type == "Ammo": player.ammo += 15 elif self.item_type == "Grenade": player.grenades += 3 # delete the item box self.kill() class Bullet(pygame.sprite.Sprite): def __init__(self, x, y, direction): pygame.sprite.Sprite.__init__(self) self.speed = 10 self.image = bullet_img self.rect = self.image.get_rect() self.rect.center = (x, y) self.direction = direction def update(self): self.rect.x += self.direction * self.speed # check if bullet has left the screen if self.rect.right < 0 and self.rect.left > screen_width: self.kill() # check for collision with level for tile in world.obstacle_list: if tile[1].colliderect(self.rect): self.kill() # check collision with characters if pygame.sprite.spritecollide(player, bullet_group, False): if player.alive: player.health -= 5 self.kill() for enemy in enemy_group: if pygame.sprite.spritecollide(enemy, bullet_group, False): if enemy.alive: enemy.health -= 25 self.kill() class Grenade(pygame.sprite.Sprite): def __init__(self, x, y, direction): pygame.sprite.Sprite.__init__(self) self.timer = 100 self.vel_y = -11 self.speed = 7 self.image = grenade_img self.rect = self.image.get_rect() self.rect.center = (x, y) self.direction = direction self.width = self.image.get_width() self.height = self.image.get_height() def update(self): self.vel_y += GRAVITY dx = self.direction * self.speed dy = self.vel_y # check for collision with level for tile in world.obstacle_list: # check if grenade has hit a wall if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): self.direction *= -1 dx = self.direction * self.speed if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): self.speed = 0 # check if below the ground, i.e thrown if self.vel_y < 0: self.y = 0 dy = tile[1].bottom - self.rect.top # check if above the ground, ie falling elif self.vel_y >= 0: self.vel_y = 0 dy = tile[1].top - self.rect.bottom # update grenade position self.rect.x += dx self.rect.y += dy # explosion countdown self.timer -= 1 if self.timer <= 0: self.kill() explosion = Explosion(self.rect.x, self.rect.y, 0.5) explosion_group.add(explosion) # do damage to anyone nearby if abs(self.rect.centerx - player.rect.centerx) < TILE_SIZE * 2 and \ abs(self.rect.centery - player.rect.centery) < TILE_SIZE * 2: player.health -= 50 for enemy in enemy_group: if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and \ abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2: enemy.health -= 50 class Explosion(pygame.sprite.Sprite): def __init__(self, x, y, scale): pygame.sprite.Sprite.__init__(self) self.images = [] for num in range(1, 6): img = pygame.image.load(f"img/explosion/exp{num}.png").convert_alpha() img = pygame.transform.scale(img, (int(img.get_width() * scale), (int(img.get_height() * scale)))) self.images.append(img) self.frame_index = 0 self.image = self.images[self.frame_index] self.rect = self.image.get_rect() self.rect.center = (x, y) self.counter = 0 def update(self): EXPLOSION_SPEED = 4 # update explosion animation self.counter += 1 if self.counter >= EXPLOSION_SPEED: self.counter = 0 self.frame_index += 1 # if animation is complete, then delete the explosion if self.frame_index >= len(self.images): self.kill() else: self.image = self.images[self.frame_index] # create sprite groups enemy_group = pygame.sprite.Group() bullet_group = pygame.sprite.Group() grenade_group = pygame.sprite.Group() explosion_group = pygame.sprite.Group() item_box_group = pygame.sprite.Group() decoration_group = pygame.sprite.Group() water_group = pygame.sprite.Group() exit_group = pygame.sprite.Group() # create empty tile list world_data = [] for row in range(ROWS): r = [-1] * COLS world_data.append(r) # load in level data and create world with open(f"level{level}_data.csv", newline="") as csvfile: reader = csv.reader(csvfile, delimiter=",") for x, row in enumerate(reader): for y, tile in enumerate(row): world_data[x][y] = int(tile) print(world_data) world = World() player, health_bar = world.process_data(world_data) run = True while run: clock.tick(fps) draw_bg() world.draw() health_bar.draw(player.health) # show ammo draw_text("AMMO:", font, WHITE, 10, 35) for x in range(player.ammo): screen.blit(bullet_img, (90 + (x * 10), 40)) # show grenade draw_text("GRENADES: ", font, WHITE, 10, 65) for x in range(player.grenades): screen.blit(grenade_img, (130 + (x * 15), 66)) player.update() player.draw() for enemy in enemy_group: enemy.ai() enemy.update() enemy.draw() # update and draw groups bullet_group.update() grenade_group.update() explosion_group.update() item_box_group.update() decoration_group.update() water_group.update() exit_group.update() bullet_group.draw(screen) grenade_group.draw(screen) explosion_group.draw(screen) item_box_group.draw(screen) decoration_group.draw(screen) water_group.draw(screen) exit_group.draw(screen) # update player actions, 1 is run, 0 = idle, 2 = in air if player.alive: # shoot bullet if shoot: player.shoot() elif grenade and grenade_thrown == False and player.grenades > 0: grenade = Grenade(player.rect.centerx + (0.5 * player.rect.size[0] * player.direction), player.rect.top, player.direction) grenade_group.add(grenade) # reduce grenades player.grenades -= 1 grenade_thrown = True if player.in_air: player.update_action(2) elif moving_left or moving_right: player.update_action(1) else: player.update_action(0) screen_scroll = player.move(moving_left, moving_right) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False # keyboard presses if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: moving_left = True if event.key == pygame.K_d: moving_right = True if event.key == pygame.K_SPACE: shoot = True if event.key == pygame.K_q: grenade = True if event.key == pygame.K_w and player.alive: player.jump = True if event.key == pygame.K_ESCAPE: run = False # keyboard button released if event.type == pygame.KEYUP: if event.key == pygame.K_a: moving_left = False if event.key == pygame.K_d: moving_right = False if event.key == pygame.K_SPACE: shoot = False if event.key == pygame.K_q: grenade = False grenade_thrown = False pygame.display.update() pygame.quit() i have looked at this code for days but cant seem to find out the problem exactly. so, the question once again is how to properly arrange the enemies on the screen at their various predefined positions and not directly on top of my player.
Bullets Are not Drawing on Simple Pygame
I am trying to create a simple bullet function in which am shooting bullets vertically. No bullets seem to be drawing in the program Help would be much appreciated. class Bullet(pg.sprite.Sprite): def __init__(self): super().__init__() pygame.sprite.Sprite.__init__(self) self.image = pg.image.load(os.path.join(path, 'img', 'bullet.png')) self.image = pygame.Surface([4, 10]) self.image.fill(RED) self.rect = self.image.get_rect() def update(self): self.rect.y -= 5 enter code here elif e.type == pg.KEYDOWN and e.key == pg.K_SPACE: # Fire a bullet if the user clicks the space button bullet = Bullet() # Set the bullet so it is where the player is bullet.rect.x = player.rect.x bullet.rect.y = player.rect.y # Add the bullet to the lists cars_group.add(bullet) bullet_list.add(bullet) # Calculate mechanics for each bullet for bullet in bullet_list: # See if it hit a block block_hit_list = pygame.sprite.spritecollide(bullet, cars_group, True) # For each block hit, remove the bullet and add to the score for block in block_hit_list: bullet_list.remove(bullet) cars_group.remove(bullet) disclosure this isnt fully my code Full code here: import pygame as pg import pygame.freetype import random import os import math os.environ['SDL_VIDEO_CENTERED'] = '1' path = os.path.dirname(os.path.abspath(__file__)) record_file = os.path.join(path, 'Record', 'record.txt') try: with open(record_file, 'x') as f: f.write(str(0)) except (BaseException, OSError): pass SIZE = WIDTH, HEIGHT = 800, 600 GREY = (128, 128, 128) GREEN = (0, 128, 0) WHITE = (200, 200, 200) RED = (230, 0, 0) rgb = [0, 250, 0] block = False pause = [False, True] count = [0] car_accident = 0 level = 40 start = 255 blink = 0 hit_old = None play, out = None, None def icon(): size, text = 32, '\u0056\u004F' sur = pg.Surface((size, size), pg.SRCALPHA) pg.draw.circle( sur, '#44475a59', (size // 2, size // 2), size // 2) font_text = pg.freetype.SysFont('Arial', 16, True) rect = font_text.get_rect(text) x, y = (size - rect.width) // 2, (size - rect.height) // 2 font_text.render_to(sur, (x, y), text, fgcolor='#ff0000') pg.display.set_icon(sur) pg.init() icon() pg.display.set_caption('Rally') screen = pg.display.set_mode(SIZE) FPS = 120 clock = pg.time.Clock() font = pygame.freetype.Font(os.path.join(path, 'font', 'seguisym.ttf'), 30) cars = [pg.image.load(os.path.join(path, 'img', 'car1.png')), pg.image.load(os.path.join(path, 'img', 'car2.png')), pg.image.load(os.path.join(path, 'img', 'car3.png'))] alarm = [pg.image.load(os.path.join(path, 'alarm', '1.png')), pg.image.load(os.path.join(path, 'alarm', '2.png'))] sound_car_accident = pg.mixer.Sound(os.path.join(path, 'sound', 'udar.wav')) sound_canister = pg.mixer.Sound(os.path.join(path, 'sound', 'canister.wav')) sound_accident = pg.mixer.Sound(os.path.join(path, 'sound', 'accident.wav')) button_start = pg.image.load(os.path.join(path, 'img', 'btn_play.png')) button_start_rect = button_start.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 100)) button_stop = pg.image.load(os.path.join(path, 'img', 'btn_exit.png')) button_stop_rect = button_stop.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 100)) fuel_image = pg.image.load(os.path.join(path, 'img', 'fuel.png')) canister_image = pg.image.load(os.path.join(path, 'img', 'canister.png')) water_image = pg.image.load(os.path.join(path, 'img', 'water.png')) bullet_image = pg.image.load(os.path.join(path, 'img', 'bullet.png')) u1_event = pg.USEREVENT + 1 pg.time.set_timer(u1_event, random.randrange(6000, 26001, 4000)) u2_event = pg.USEREVENT + 2 pg.time.set_timer(u2_event, random.randrange(13000, 28001, 5000)) class Player(pg.sprite.Sprite): def __init__(self): pg.sprite.Sprite.__init__(self) self.image = pg.image.load(os.path.join(path, 'img', 'car4.png')) self.orig_image = self.image self.angle = 0 self.speed = 2 self.acceleration = 0.02 self.rect = self.image.get_rect(center=(WIDTH - 20, HEIGHT - 70)) self.position = pg.math.Vector2() self.velocity = pg.math.Vector2() self.vx = 0 # velocity.x for speedometer def update(self): self.image = pg.transform.rotate(self.orig_image, self.angle) self.position += self.velocity self.rect = self.image.get_rect(center=self.position) keys = pg.key.get_pressed() if keys[pg.K_RIGHT]: self.velocity.x = self.speed self.angle -= 1 self.vx -= self.acceleration if self.angle < -25: self.angle = -25 elif keys[pg.K_LEFT]: self.velocity.x = -self.speed self.angle += 1 self.vx -= self.acceleration if self.angle > 25: self.angle = 25 else: self.vx += self.acceleration if self.vx < 0 else 0 self.velocity.x = 0 if self.angle < 0: self.angle += 1 elif self.angle > 0: self.angle -= 1 if keys[pg.K_UP]: self.velocity.y -= self.acceleration if self.velocity.y < -self.speed: self.velocity.y = -self.speed elif keys[pg.K_DOWN]: self.velocity.y += self.acceleration if self.velocity.y > self.speed: self.velocity.y = self.speed else: if self.velocity.y < 0: self.velocity.y += self.acceleration if self.velocity.y > 0: self.velocity.y = 0 elif self.velocity.y > 0: self.velocity.y -= self.acceleration if self.velocity.y < 0: self.velocity.y = 0 def for_speedometer(self): if self.vx <= -0.4: self.vx = -0.4 # left-right speedometer max if self.velocity.y < self.vx or self.velocity.y > 0: self.vx = self.velocity.y if self.vx >= 1.04: self.vx = 1.04 # speedometer min for speed=2 return self.vx class Alarm(pg.sprite.Sprite): def __init__(self): pg.sprite.Sprite.__init__(self) self.images = alarm self.index = 0 self.range = len(self.images) self.image = self.images[self.index] self.rect = self.image.get_rect() self.speed = 1 def update(self): self.index += 0.02 self.image = self.images[int(self.index % self.range)] self.rect.y += self.speed if self.rect.top > HEIGHT: self.kill() class Car(pg.sprite.Sprite): def __init__(self, x, y, img): pg.sprite.Sprite.__init__(self) if img == fuel_image: self.image = img self.speed = 0 elif img == canister_image or img == water_image: self.image = img self.speed = 1 else: self.image = pg.transform.flip(img, False, True) self.speed = random.randint(2, 3) self.rect = self.image.get_rect(center=(x, y)) def update(self): self.rect.y += self.speed if self.rect.top >= HEIGHT: if self is canister or self is water: self.kill() else: if 40 < player.rect.centerx < WIDTH - 40 \ and player.rect.top < HEIGHT and player.rect.bottom > 0: count[0] += 1 list_x.remove(self.rect.centerx) while True: self.rect.centerx = random.randrange(80, WIDTH, 80) if self.rect.centerx in list_x: continue else: list_x.append(self.rect.centerx) self.speed = random.randint(2, 3) self.rect.bottom = 0 break class Road(pg.sprite.Sprite): def __init__(self, x, y): pg.sprite.Sprite.__init__(self) self.image = pg.Surface(screen.get_size()) self.image.fill(GREY) pg.draw.line(self.image, GREEN, (20, 0), (20, 600), 40) pg.draw.line(self.image, GREEN, (780, 0), (780, 600), 40) for xx in range(10): for yy in range(10): pg.draw.line( self.image, WHITE, (40 + xx * 80, 0 if xx == 0 or xx == 9 else 10 + yy * 60), (40 + xx * 80, 600 if xx == 0 or xx == 9 else 50 + yy * 60), 5) self.rect = self.image.get_rect(topleft=(x, y)) self.speed = 1 def update(self): self.rect.y += self.speed if self.rect.top >= HEIGHT: self.rect.bottom = 0 class Volume(pg.sprite.Sprite): def __init__(self, x, y): pg.sprite.Sprite.__init__(self) self.image = pg.Surface((20, 140), pg.SRCALPHA) self.rect = self.image.get_rect(center=(x, y)) self.radius = 10 self.x = self.y = self.radius self.color_circle = (0, 255, 0, 128) self.color_rect = (0, 180, 0, 128) self.color_text = (255, 255, 255) self.alpha = 255 self.volume = 1 def update(self): self.image.set_alpha(self.alpha) pg.draw.rect( self.image, self.color_rect, [0, 0, *self.rect[2:]], border_radius=self.radius) pg.draw.circle(self.image, self.color_circle, (self.x, self.y), self.radius) text = str(round(self.volume * 100)) text_rect = font.get_rect(text, size=11) font.render_to( self.image, (self.x - text_rect[2] / 2., self.y - text_rect[3] / 2.), text, self.color_text, rotation=0, size=11) sp = "\U0001F507" if self.volume == 0 else "\U0001F508" if self.volume < 0.2 \ else "\U0001F509" if self.volume < 0.7 else "\U0001F50A" font.render_to( screen, (self.rect.x, self.rect.y - font.size), sp, [*WHITE, self.alpha]) def render(self, e_buttons, e_pos): if self.rect.left < e_pos[0] < self.rect.right and \ self.rect.top < e_pos[1] < self.rect.bottom and \ e_buttons: self.y = abs(self.rect.top - e_pos[1]) if self.y > self.rect.h - self.radius: self.y = self.rect.h - self.radius elif self.y < self.radius: self.y = self.radius self.volume = (100 - (self.y - self.radius) / 1.2) / 100. sound_car_accident.set_volume(self.volume) sound_canister.set_volume(self.volume) sound_accident.set_volume(self.volume) class Speedometer(pg.sprite.Sprite): def __init__(self): pg.sprite.Sprite.__init__(self) w, h = 150, 150 self.radius = 140 self.image = pg.Surface((w, h), pg.SRCALPHA) self.rect = self.image.get_rect(bottomright=(WIDTH, HEIGHT)) font.render_to(self.image, (w - 50, h - 50), 'km/h', WHITE, size=20) for deg in range(5, 84, 6): length = 18 if deg == 5 or deg == 23 or deg == 41 or deg == 59 or deg == 77 else 10 cos = math.cos(math.radians(deg)) sin = math.sin(math.radians(deg)) pg.draw.line( self.image, WHITE, (w - self.radius * cos, h - self.radius * sin), (w - (self.radius - length) * cos, h - (self.radius - length) * sin), 2) for value, deg in enumerate(range(9, 78, 17)): cos = math.cos(math.radians(deg)) sin = math.sin(math.radians(deg)) font.render_to(self.image, ( round(w - (self.radius - 30) * cos), round(h - (self.radius - 30) * sin)), str(value * 100), WHITE, size=15) def render(self): s = 30 - player.for_speedometer() * 25 # from 4 to 80 pg.draw.line( screen, RED, self.rect.bottomright, (self.rect.right - (self.radius - 10) * math.cos(math.radians(s)), self.rect.bottom - (self.radius - 10) * math.sin(math.radians(s))), 4) pg.draw.circle(screen, WHITE, self.rect.bottomright, 25) vol.update() class Bullet(pg.sprite.Sprite): def __init__(self): super().__init__() pygame.sprite.Sprite.__init__(self) self.image = pg.image.load(os.path.join(path, 'img', 'bullet.png')) self.image = pygame.Surface([4, 10]) self.image.fill(RED) self.rect = self.image.get_rect() def update(self): self.rect.y -= 5 all_sprite = pg.sprite.LayeredUpdates() cars_group = pg.sprite.Group() canister_group = pg.sprite.Group() bullet_list = pg.sprite.Group() for r in range(2): bg = Road(x=0, y=0) all_sprite.add(Road(0, 0 if r == 0 else -HEIGHT), layer=0) speedometer = Speedometer() all_sprite.add(speedometer, layer=0) player = Player() list_x = [] n = 0 while n < 6: car_x = random.randrange(80, WIDTH, 80) if car_x in list_x: continue else: list_x.append(car_x) cars_group.add(Car(car_x, random.randint( -cars[0].get_height() * 3, -cars[0].get_height()), cars[n] if n < 3 else random.choice(cars))) n += 1 fuel = Car(WIDTH - 80, 40, fuel_image) canister = Car(0, 0, canister_image) water = Car(0, 0, water_image) vol = Volume(20, HEIGHT - 80) all_sprite.add(*cars_group, layer=1) all_sprite.add(player, layer=3) all_sprite.add(fuel, layer=4) all_sprite.add(vol, layer=4) def my_record(): with open(record_file, 'r+') as d: record = d.read() if count[0] > int(record): record = str(count[0]) d.seek(0) d.truncate() d.write(record) return record def home_screen(b): screen.blit(all_sprite.get_sprite(0).image, (0, 0)) screen.blit(speedometer.image, speedometer.rect) button_start.set_alpha(start) button_stop.set_alpha(start) screen.blit(button_start, button_start_rect) screen.blit(button_stop, button_stop_rect) font.render_to(screen, (48, 10), f'Record: {rec}', [ *RED, 255 if count[0] <= record_old else 255 if int(b) % 2 else 0], size=24) font.render_to(screen, (48, 40), f'Points: {count[0]}', RED, size=24) font.render_to(screen, (48, 70), f'Accidents: {car_accident}', RED, size=24) vol.update() screen.blit(vol.image, vol.rect) rec = my_record() record_old = int(rec) game = True while game: for e in pg.event.get(): if e.type == pg.QUIT or e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE: game = False elif e.type == pg.KEYDOWN and e.key == pg.K_p and start == 0: pause.reverse() if pause[0]: pg.mouse.set_visible(True) vol.alpha = 255 else: pg.mouse.set_visible(False) vol.alpha = 0 elif e.type == pg.MOUSEMOTION and (start == 255 or pause[0]): vol.render(e.buttons[0], e.pos) elif e.type == pg.KEYDOWN and e.key == pg.K_SPACE: # Fire a bullet if the user clicks the space button bullet = Bullet() # Set the bullet so it is where the player is bullet.rect.x = player.rect.x bullet.rect.y = player.rect.y # Add the bullet to the lists cars_group.add(bullet) bullet_list.add(bullet) elif e.type == pg.MOUSEBUTTONDOWN and start == 255: if e.button == 1: if button_start_rect.collidepoint(e.pos): player.angle = 0 player.velocity.x, player.velocity.y = 0, 0 player.position.x, player.position.y = WIDTH - 400, HEIGHT - 70 player.update() all_sprite.remove_sprites_of_layer(2) water.kill() canister.kill() for cr in cars_group: cr.speed = random.randint(2, 3) cr.rect.bottom = 0 level = 40 car_accident = 0 count[0] = 0 start -= 1 record_old = int(rec) pg.mouse.set_visible(False) elif button_stop_rect.collidepoint(e.pos): game = False elif e.type == u1_event and not pause[0] and not all_sprite.has(water): # water.alive(): all_sprite.add(water, layer=0) water.rect.center = \ random.randrange(80, WIDTH, 80), -water.rect.h timer1 = random.randrange(6000, 26001, 4000) pg.time.set_timer(u1_event, timer1) elif e.type == u2_event and not pause[0] and not canister_group.has(canister): canister_group.add(canister) all_sprite.add(canister, layer=0) canister.rect.center = \ random.randrange(80, WIDTH, 80), -canister.rect.h timer2 = random.randrange(13000, 28001, 5000) pg.time.set_timer(u2_event, timer2) # Calculate mechanics for each bullet for bullet in bullet_list: # See if it hit a block block_hit_list = pygame.sprite.spritecollide(bullet, cars_group, True) # For each block hit, remove the bullet and add to the score for block in block_hit_list: bullet_list.remove(bullet) cars_group.remove(bullet) hit = pg.sprite.spritecollideany(player, cars_group) # hit -> sprite car if hit and hit.speed != 1: player.position.x += 50 * random.randrange(-1, 2, 2) player.angle = 50 * random.randrange(-1, 2, 2) hit.speed = 1 car_alarm = Alarm() all_sprite.add(car_alarm, layer=2) car_alarm.rect.center = hit.rect.center car_accident += 1 if car_accident > 10: car_accident = 10 sound_car_accident.play() if pg.sprite.spritecollide(player, canister_group, True): level = 40 sound_canister.play() if pg.sprite.collide_rect(player, water): if not block: player.angle = random.randint(60, 90) * random.randrange(-1, 2, 2) sound_accident.play() block = True else: block = False if start > 0: home_screen(blink) blink = 0 if blink > 99 else blink + .02 if start != 255: start -= 1 vol.alpha = start else: if not pause[0]: level -= .01 if level < 0 or car_accident > 9: rec = my_record() pg.mouse.set_visible(True) vol.alpha = start elif level < 10: rgb[:2] = 250, 0 elif level < 20: rgb[0] = 250 else: rgb[:2] = 0, 250 all_sprite.update() else: vol.update() all_sprite.draw(screen) pg.draw.rect( screen, rgb, (fuel.rect.left + 10, fuel.rect.bottom - level - 8, 21, level)) font.render_to(screen, (48, 10), f'accidents: {car_accident}', GREEN, size=24) font.render_to(screen, (48, HEIGHT - 30), f'{count[0]}', GREEN, size=24) speedometer.render() # speedometer pg.display.update() clock.tick(FPS) pg.display.set_caption(f'Rally FPS: {int(clock.get_fps())}') pygame.quit() # pg.image.save(screen, 'road.jpg') aos;dfihj ;aslkdjfh ;salkj askl;fjas;lkfjd a sdlfkj as;ldkfj as asld;kj f;laskjd asl;kjdf;lasjk f sdla ;fjkas;ldkf j asdlf jkasl;dkf j asld flasdkjf ;
You are drawing all the Sprites in the all_sprites Group: all_sprite.draw(screen) However, t he bullets are not in this Group Either add the bullets to the all_sprites Group or draw the Sprites contained in the bullet_list Group. Either elif e.type == pg.KEYDOWN and e.key == pg.K_SPACE: # [...] bullet_list.add(bullet) bullet_list.draw(screen) or bullet_list.draw(screen) Remove the bullets form all Groups using kill(): for bullet in bullet_list: # See if it hit a block block_hit_list = pygame.sprite.spritecollide(bullet, cars_group, True) # For each block hit, remove the bullet and add to the score for block in block_hit_list: bullet.kill()
What should I do in order to create multiple instances of enemies?
As a beginner, I am struggling to create multiple enemies in pygame. What could I possibly add or implement to my code in order to do so? Code: # WORK IN PROGRESS! # I followed techwithtim's tutorial # I do not own the images and sounds used in game # TODO Create multiple Enemies import pygame import random # Screen parameters pygame.init() screen = pygame.display.set_mode((640, 480)) pygame.display.set_caption("SPPACCE") bg = pygame.image.load("bg.png") font = pygame.font.SysFont('comicsans', 30, True) clock = pygame.time.Clock() score = 0 # Music & Sound effects bulletsound = pygame.mixer.Sound('sounds/bullet_soundeffect.wav') explosion = pygame.mixer.Sound('sounds/explosion_effect.wav') explosion2 = pygame.mixer.Sound('sounds/torpedo_explosion.wav') # Player parameters class Player(object): def __init__(self, x, y, height, width): self.x = x self.y = y self.height = height self.width = width self.player_vel = 5 def draw(self, screen): screen.blit(player_char, (self.x, self.y)) # Enemy parameters class Enemy(object): def __init__(self, x, y, height, width, end): self.x = x self.y = y self.height = height self.width = width self.enemy_vel = 1.5 self.end = end self.path = [self.x, self.end] self.hitbox = (self.x + 17, self.y + 2, 65, 65) self.health = 5 self.visible = True def draw(self, screen): self.move() if self.visible: self.hitbox = (self.x + 0, self.y, 65, 65) pygame.draw.rect(screen, (255, 0, 0), self.hitbox, 2) screen.blit(enemy_char, (self.x, self.y)) # Health bars pygame.draw.rect(screen, (0, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 65, 7)) pygame.draw.rect(screen, (255, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 65 - (12 * (5 - self.health)), 7)) def move(self): if self.enemy_vel > 0: if self.x < self.path[1] + self.enemy_vel: self.x += self.enemy_vel else: self.enemy_vel = self.enemy_vel * -1 self.x += self.enemy_vel else: if self.x > self.path[0] - self.enemy_vel: self.x += self.enemy_vel else: self.enemy_vel = self.enemy_vel * -1 self.x += self.enemy_vel def hit(self): if self.health > 0: self.health -= 1 else: self.visible = False explosion.play() global score score += 1 # Player Projectile parameters class Projectile(object): def __init__(self, x, y, color, radius): self.x = x self.y = y self.color = color self.radius = radius self.vel = 12.5 def draw(self, screen): pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius) # Images player_char = pygame.image.load('sprites/hotdog.png') enemy_char = pygame.image.load('sprites/hamburger.png') def blit(): # This draws the sprites player.draw(screen) enemy.draw(screen) for projectile in projectiles: projectile.draw(screen) score_text = font.render("Score: " + str(score), 1, (0, 109, 255)) version = font.render("Version 01 ", 1, (51, 153, 255)) screen.blit(score_text, (0, 0)) screen.blit(version, (520, 0)) shootloop = 0 if shootloop > 0: shootloop += 1 if shootloop > 2: shootloop = 0 player = Player(300, 400, 64, 64) enemy = Enemy(random.randint(10, 100), random.randint(20, 100), 64, 64, 480) enemy_count = random.randint(1, 10) projectiles = [] run = True while run: clock.tick(60) screen.fill((0, 0, 0)) screen.blit(bg, (0, 0)) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False # Movement keys with playeborders keys = pygame.key.get_pressed() if keys[pygame.K_s] and player.y < 480 - player.height - player.player_vel: player.y += player.player_vel if keys[pygame.K_w] and player.y > 280: player.y -= player.player_vel if keys[pygame.K_d] and player.x < 640 - player.width - player.player_vel: player.x += player.player_vel if keys[pygame.K_a] and player.x > player.player_vel: player.x -= player.player_vel for projectile in projectiles: if projectile.y - projectile.radius < enemy.hitbox[1] + enemy.hitbox[3] and projectile.y + projectile.radius > enemy.hitbox[1]: if enemy.visible == True: if projectile.x + projectile.radius > enemy.hitbox[0] and projectile.x - projectile.radius < enemy.hitbox[0] + enemy.hitbox[2]: enemy.hit() explosion2.play() projectiles.pop(projectiles.index(projectile)) if projectile.y < 640 and projectile.y > 0: projectile.y -= projectile.vel else: projectiles.pop(projectiles.index(projectile)) # Player shooting if keys[pygame.K_SPACE] and shootloop == 0: if len(projectiles) < 1: projectiles.append(Projectile(round(player.x + player.width //2), round(player.y + player.height //2), [255, 150, 0], 7)) blit() pygame.display.update() I tried to recycle code from my last game project, but it doesn't work because the code structures are way too different from each other. This project is OOP meanwhile the last one has scattered variables.
Similar to your projectiles make a list of Enemy objects: enemies = [] enemy_count = random.randint(3, 10) for i in range( enemy_count ): new_enemy = Enemy(random.randint(10, 100), random.randint(20, 100), 64, 64, 480) enemies.append( new_enemy ) projectiles = [] Update your blit() to draw a list of enemies: def blit(): # This draws the sprites player.draw(screen) for enemy in enemies: enemy.draw(screen) for projectile in projectiles: projectile.draw(screen) score_text = font.render("Score: " + str(score), 1, (0, 109, 255)) version = font.render("Version 01 ", 1, (51, 153, 255)) screen.blit(score_text, (0, 0)) screen.blit(version, (520, 0)) And check them all for collisions: for enemy in enemies: for projectile in projectiles: if projectile.y - projectile.radius < enemy.hitbox[1] + enemy.hitbox[3] and projectile.y + projectile.radius > enemy.hitbox[1]: if enemy.visible == True: if projectile.x + projectile.radius > enemy.hitbox[0] and projectile.x - projectile.radius < enemy.hitbox[0] + enemy.hitbox[2]: enemy.hit() explosion2.play() projectiles.pop(projectiles.index(projectile)) That should get you started. The changes are relatively simple because you already have the data compartmentalised in objects. Good job. EDIT: Re-spawning an enemy is simply a matter of adding another Enemy object to the enemies list: new_enemy = Enemy(random.randint(10, 100), random.randint(20, 100), 64, 64, 480) enemies.append( new_enemy )
Object to shoot a projectile
I'm trying to make my tanks shoot, and I did all the code I think I should have done but I don't know why the tanks aren't shooting anything. import pygame, assetloader from pygame.locals import * import random, time, math import pygame GRAD = math.pi/180 blue = (0, 0, 255) wallRects = [] bullets = [] maze = [[] for i in range(25)] assetloader.set_asset_path("assets/") I defined the Bullet Class here: def calculate_dir_with_angle(angle): direction = [0, 0] if (angle > 0 and angle < 180) or (angle > -360 and angle < -180): direction[0] = -1 elif (angle > -180 and angle < 0) or (angle > 180 and angle < 360): direction[0] = 1 elif (angle > -90 and angle < 90) or (angle > 270 and anlge < 360): direction[1] = -1 elif (angle > 90 and angle < 270) or (angle > -270 and angle < -90): direction[1] = 1 return direction class Bullet: def __init__(self, pos, r, angle): self.x = pos[0] self.y = pos[1] self.r = r self.counter = 50 direction = calculate_dir_with_angle(angle) self.vel = [direction[0] * 2, direction[1] * 2] def draw(self, screen): self.x = int(self.x) self.y = int(self.y) pygame.draw.circle(screen, (25, 25, 25), (self.x, self.y), (self.r)) def move(self): self.x += self.vel[0] self.y += self.vel[1] self.rect = pygame.Rect(self.x-self.r, self.y - self.r, 2 * self.r, 2 * self.r) for wr in wallRects: if self.rect.centery >= wr.top and self.rect.centery <= wr.bottom: if self.rect.left <= wr.right and self.rect.left > wr.left: self.vel[0] = -self.vel[0] self.x = wr.right + self.r + 1 self.rect.x = wr.right + 1 elif self.rect.right >= wr.left and self.rect.right < wr.right: self.vel[0] = -self.vel[0] self.x = wr.left + self.r - 1 self.rect.x = wr.left - 2 * self.r - 1 if self.rect.centerx >= wr.left and self.rect.centerx <= wr.right: if self.rect.top <= wr.bottom and self.rect.top > wr.top: self.vel[1] = -self.vel[1] self.y = wr.bottom + self.r + 1 self.rect.y = wr.bottom + 1 elif self.rect.bottom >= wr.top and self.rect.bottom < wr.bottom: self.vel[1] = -self.vel[1] self.y = wr.top - self.r - 1 self.rect.y = wr.top - 2 * self.r - 1 if self.counter > 0: self.counter -= 1 def generateRandomPosition(): row = random.randint(1, 23) col = random.randint(1, 23) while maze[row][col-1] != 0 or maze[row][col] != 0 or maze[row][col+1] != 0: row = random.randint(1, 23) col = random.randint(1, 23) return row, col Player 1: class Player(pygame.sprite.Sprite): def __init__(self, x, y, pos): pygame.sprite.Sprite.__init__(self) self.image, self.rect = assetloader.load_image("Tank.png", -1) self.rect.x = x self.rect.y = y self.rect.clamp_ip(screen.get_rect()) self.rows = pos[0] self.cols = pos[1] self.x = self.cols * gsize self.y = self.rows * gsize self.orig_image, self.orig_rect = assetloader.load_image("Tank.png", -1) self.orig_rect.x = self.x self.orig_rect.y = self.y self.orig_gun_pos = self.orig_rect.midtop self.ammo = 5 def checkCollisions(self): for b in bullets: if b.counter <= 0: if b.rect.colliderect(self.orig_rect): self.dead = True def calculate_gun_pos(self): self.orig_gun_pos = self.orig_rect.midtop new_y = self.orig_gun_pos[1] - self.orig_rect.centery new_x = self.orig_gun_pos[0] - self.orig_rect.centerx rads = self.dir * GRAD gun_x = (new_y * math.sin(rads)) + (new_x * math.cos(rads)) + (self.orig_rect.centerx) gun_y = (new_y * math.cos(rads)) - (new_x * math.sin(rads)) + (self.orig_rect.centery) self.gun_pos = (gun_x, gun_y) def shoot(self): if self.ammo > 0: self.calculate_gun_pos() b = Bullet(self.gun_pos, 3, self.dir) bullets.append(b) self.ammo -= 1 def draw(self, screen): image = pygame.transform.rotate(self.image, self.dir) screen.blit(image, self.rect) def update(self): oldCenter = self.rect.center self.rect = self.image.get_rect() self.rect.center = oldCenter screen_rect = screen.get_rect() keys = pygame.key.get_pressed() if keys[K_m]: p.shoot() if not screen_rect.contains(self.rect): self.rect.clamp_ip(screen_rect) Calling the functions: size = width, height = 500, 400 gsize = 25 start_x, start_y = 0, 0 bgColor = 255, 255, 255 pygame.init() screen = pygame.display.set_mode(size)#, pygame.FULLSCREEN) pygame.display.set_caption("Sample Sprite") clock = pygame.time.Clock() p = Player(width/2, height/4, (3,4)) coll_font = pygame.font.Font(None, 30) going = True while going: clock.tick(60) for event in pygame.event.get(): if event.type == QUIT: going = False elif event.type == KEYDOWN: if event.key == K_ESCAPE: going = False elif event.type == KEYDOWN: if event.key == K_m: p.shoot() for b in bullets: b.move() p.update() screen.fill(bgColor) p.draw(screen) pygame.display.flip() pygame.quit() How would I call the bullet to actually appear and fire because I have the Bullet class which gets called within the Player class in def shoot(self) so does anyone have an idea why the bullets aren't appearing?
I usually add bullets in this way: I pass the group that contains all sprites and the bullet group to the player instance and add new bullets to these group in the player's handle_event method. import pygame as pg from pygame.math import Vector2 pg.init() screen = pg.display.set_mode((640, 480)) screen_rect = screen.get_rect() FONT = pg.font.Font(None, 24) BG_COLOR = pg.Color('gray12') BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA) pg.draw.polygon( BULLET_IMAGE, pg.Color('aquamarine1'), [(0, 0), (20, 5), (0, 11)]) PLAYER_IMAGE = pg.Surface((50, 30), pg.SRCALPHA) pg.draw.polygon( PLAYER_IMAGE, pg.Color('dodgerblue1'), [(0, 0), (50, 15), (0, 30)]) class Player(pg.sprite.Sprite): def __init__(self, pos, all_sprites, bullet_group): super().__init__() self.image = PLAYER_IMAGE self.orig_image = self.image # Needed to preserve image quality. self.rect = self.image.get_rect(center=(pos)) self.pos = Vector2(pos) self.vel = Vector2(1, 0) self.angle = 0 self.angle_speed = 0 self.all_sprites = all_sprites self.bullet_group = bullet_group def handle_event(self, event): if event.type == pg.MOUSEBUTTONDOWN: # Left button fires a bullet from cannon center with # current angle. Add the bullet to the bullet_group. if event.button == 1: bullet = Bullet(self.pos, self.angle) self.bullet_group.add(bullet) self.all_sprites.add(bullet) elif event.type == pg.KEYDOWN: # Rotate self by setting the .angle_speed. if event.key in (pg.K_a, pg.K_LEFT): self.angle_speed = -3 elif event.key in (pg.K_d, pg.K_RIGHT): self.angle_speed = 3 elif event.type == pg.KEYUP: if event.key in (pg.K_a, pg.K_LEFT): self.angle_speed = 0 elif event.key in (pg.K_d, pg.K_RIGHT): self.angle_speed = 0 def update(self): self.pos += self.vel self.rect.center = self.pos if self.angle_speed != 0: self.rotate() def rotate(self): # Update the angle and the velocity vector. self.angle += self.angle_speed self.vel.rotate_ip(self.angle_speed) # Rotate the image and get a new rect with the previous center. self.image = pg.transform.rotozoom(self.orig_image, -self.angle, 1) self.rect = self.image.get_rect(center=self.rect.center) class Bullet(pg.sprite.Sprite): def __init__(self, pos, angle): super().__init__() self.image = pg.transform.rotate(BULLET_IMAGE, -angle) self.rect = self.image.get_rect(center=pos) # To apply an offset (40 pixels) to the start position, # create another vector and rotate it as well. offset = Vector2(40, 0).rotate(angle) # Add the offset vector to the position vector (the center). self.pos = Vector2(pos) + offset # Rotate the start velocity vector (9, 0) by the angle. self.vel = Vector2(9, 0).rotate(angle) def update(self): # Add the velocity to the pos to move the sprite. self.pos += self.vel self.rect.center = self.pos # Update the rect as well. # Remove bullets outside of the screen area. if not screen_rect.contains(self.rect): self.kill() def main(): clock = pg.time.Clock() all_sprites = pg.sprite.Group() # Bullets will be added to this group. bullet_group = pg.sprite.Group() # Pass the bullet group to the player. player = Player((300, 200), all_sprites, bullet_group) all_sprites.add(player) playing = True while playing: for event in pg.event.get(): if event.type == pg.QUIT: playing = False # Pass events to the player instance. player.handle_event(event) all_sprites.update() screen.fill(BG_COLOR) all_sprites.draw(screen) pg.display.update() clock.tick(30) if __name__ == '__main__': main() pg.quit()
adding a def main to the end of my pong game code
I'm trying to add a def main to the end of my pong game to make it easier to read, but i've ran into problems trying to do that. When I add the def main, I just get a black screen, but without it I get the whole game. import pygame SCR_WID, SCR_HEI = 640, 480 class Player(): def __init__(self): self.x, self.y = 16, SCR_HEI/2 self.speed = 3 self.padWid, self.padHei = 8, 64 self.score = 0 self.scoreFont = pygame.font.Font("imagine_font.ttf", 64) def scoring(self): scoreBlit = self.scoreFont.render(str(self.score), 1, (255, 255, 255)) screen.blit(scoreBlit, (32, 16)) if self.score == 10: print ("player 1 wins!") exit() def movement(self): keys = pygame.key.get_pressed() if keys[pygame.K_w]: self.y -= self.speed elif keys[pygame.K_s]: self.y += self.speed if self.y <= 0: self.y = 0 elif self.y >= SCR_HEI-64: self.y = SCR_HEI-64 def draw(self): pygame.draw.rect(screen, (255, 255, 255), (self.x, self.y, self.padWid, self.padHei)) class Enemy(Player): def __init__(self): self.x, self.y = SCR_WID-16, SCR_HEI/2 self.speed = 3 self.padWid, self.padHei = 8, 64 self.score = 0 self.scoreFont = pygame.font.Font("imagine_font.ttf", 64) def scoring(self): scoreBlit = self.scoreFont.render(str(self.score), 1, (255, 255, 255)) screen.blit(scoreBlit, (SCR_HEI+92, 16)) if self.score == 10: print ("Player 2 wins!") exit() def movement(self): keys = pygame.key.get_pressed() if keys[pygame.K_UP]: self.y -= self.speed elif keys[pygame.K_DOWN]: self.y += self.speed if self.y <= 0: self.y = 0 elif self.y >= SCR_HEI-64: self.y = SCR_HEI-64 def draw(self): pygame.draw.rect(screen, (255, 255, 255), (self.x, self.y, self.padWid, self.padHei)) class Ball(): def __init__(self): self.x, self.y = SCR_WID/2, SCR_HEI/2 self.speed_x = -3 self.speed_y = 3 self.size = 8 def movement(self): self.x += self.speed_x self.y += self.speed_y #wall col if self.y <= 0: self.speed_y *= -1 elif self.y >= SCR_HEI-self.size: self.speed_y *= -1 if self.x <= 0: self.__init__() enemy.score += 1 elif self.x >= SCR_WID-self.size: self.__init__() self.speed_x = 3 player.score += 1 ##wall col #paddle col #player for n in range(-self.size, player.padHei): if self.y == player.y + n: if self.x <= player.x + player.padWid: self.speed_x *= -1 break n += 1 #enemy for n in range(-self.size, enemy.padHei): if self.y == enemy.y + n: if self.x >= enemy.x - enemy.padWid: self.speed_x *= -1 break n += 1 ##paddle col def draw(self): pygame.draw.rect(screen, (255, 255, 255), (self.x, self.y, 8, 8)) SCR_WID, SCR_HEI = 640, 480 screen = pygame.display.set_mode((SCR_WID, SCR_HEI)) pygame.display.set_caption("Pong") pygame.font.init() clock = pygame.time.Clock() FPS = 60 def main(): ball = Ball() player = Player() enemy = Enemy() while True: #process for event in pygame.event.get(): if event.type == pygame.QUIT: print ("Game exited by user") exit() ##process #logic ball.movement() player.movement() enemy.movement() ##logic #draw screen.fill((0, 0, 0)) ball.draw() player.draw() player.scoring() enemy.draw() enemy.scoring() ##draw #_______ pygame.display.flip() clock.tick(FPS) main()
The call for main() on the end should be if __name__ == "__main__": main() So that you can run your code and get the function to run. Also your indentation seems a little off. Is your code structured correctly with regard to spaces?
As already answered, you should insert the following statement at the end of the script: if __name__ == "__main__": main() This answer will give you more info about the meaning of that statement. What does if __name__ == "__main__": do? In short, it tells the interpreter what it has to do with the function main(): 1) Run it automatically if the file has been executed as stand alone script. 2) Don't run if the file has been imported as module from another script. In your case, the wanted behaviour is the number one.