Pygame is a bit laggy. Is it because of the many calculations? - python
import pygame
import math
import random
import time
pygame.init()
clock = pygame.time.Clock()
width = 800
height = 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Shadow Warrior")
#its like that because this reduses lag a lot and help for better gameplay
def loadify(imgname):
return pygame.image.load(imgname).convert_alpha(screen)
#Models:
logo = loadify("textures/logo.png")
pygame.display.set_icon(logo)
wallModel = loadify("textures/wall.png")
groundModel = loadify("textures/ground.png")
weaponModel = loadify("textures/weapon.png")
enemyBullet = loadify("textures/bullet.png")
playerBullet = loadify("textures/enemyBullet.png")
crosshair = loadify("textures/crosshair.png")
playerWalkAnimR = [loadify("textures\walk0.png"),
loadify("textures\walk1.png"),
loadify("textures\walk2.png")]
playerWalkAnimL = [loadify("textures\walk3.png"),
loadify("textures\walk4.png"),
loadify("textures\walk5.png")]
pygame.mouse.set_visible(False)
tileSize = 64
level = 0
hitParticles = []
weapons = []
removed_bullets = []
enemies = []
backgrounds = []
walls = []
all_bullets = []
# Classes
class Particle:
def __init__(self, x, y, velocityX, velocityY, radius, color):
self.x = x
self.y = y
self.velocityX = velocityX
self.velocityY = velocityY
self.radius = radius
self.color = color
self.lifetime = random.randrange(50, 100)
def draw(self, screen, camx, camy):
self.lifetime -= 1
self.x += self.velocityX
self.y += self.velocityY
pygame.draw.circle(screen, self.color, (self.x - camx, self.y - camy), self.radius)
class Background:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self, camx, camy):
screen.blit(groundModel, (self.x - camx - tileSize / 2, self.y - camy - tileSize / 2))
class Wall:
def __init__(self, x, y):
self.x = x
self.y = y
#Check if wall collides with smth
def collidesWith(self, other):
return pygame.Rect(self.x, self.y, tileSize, tileSize).colliderect(other.x, other.y, tileSize-16, tileSize)
def draw(self, camx, camy):
screen.blit(wallModel, (self.x - camx - tileSize / 2, self.y - camy - tileSize / 2))
class Bullet:
def __init__(self, x, y, velx, vely, isPlayer):
self.x = x
self.y = y
self.velx = velx
self.vely = vely
self.isPlayer = isPlayer
def collidesWith(self, other):
return pygame.Rect(self.x, self.y, tileSize - 16, tileSize - 16).colliderect(other.x, other.y, tileSize-36, tileSize-36)
def update(self):
self.x += self.velx
self.y += self.vely
for bullet in all_bullets:
for wall in walls:
if bullet.collidesWith(wall):
removed_bullets.append(bullet)
for i in removed_bullets:
if i in all_bullets:
all_bullets.remove(i)
def draw(self, camx, camy):
if (self.isPlayer):
screen.blit(pygame.transform.smoothscale(playerBullet, (16, 16)), (self.x - camx + 5, self.y - camy + 7))
else: screen.blit(pygame.transform.smoothscale(enemyBullet, (16, 16)), (self.x - camx + 5, self.y - camy + 7))
class slimeEnemy:
def __init__(self, x, y):
self.x = x
self.y = y
self.bulletSpeed = 1
self.aliveAnimations = [loadify("textures\slime_animation_0.png"),
loadify("textures\slime_animation_1.png"),
loadify("textures\slime_animation_2.png"),
loadify("textures\slime_animation_3.png"),]
self.animationsCount = 0
self.attackRate = 60
self.resetOffset = 0
self.SlimeHp = 7 + level * 2
self.slimeCollisionDmg = 1 + level/2
self.dmg = 1 + level / 2
self.isAlive = True
# self.isBoss = isBoss
#adding offset so the enemy doesnt move directly towards the player
self.offsetX = random.randrange(-150, 150)
self.offsetY = random.randrange(-150, 150)
def collidesWithAnything(self):
for wall in walls:
if wall.collidesWith(self):
return True
return False
def healthBar(self):
pygame.draw.rect(screen, (0, 255, 0), (self.x - camx - 20, self.y + 40 - camy, self.SlimeHp*10 + level*2, 7))
def collidesWith(self, other):
return pygame.Rect(self.x, self.y, tileSize-36, tileSize-36).colliderect(other.x, other.y, tileSize-36, tileSize-36)
def update(self):
# move enemy accordingly
if self.resetOffset == 0:
self.offsetX = random.randrange(-400, 400)
self.offsetY = random.randrange(-400, 400)
self.resetOffset = random.randrange(120, 150)
else: self.resetOffset -= 1
if player.x + self.offsetX > self.x:
self.x += 1
if self.collidesWithAnything():
self.x -= 1
elif player.x + self.offsetX < self.x:
self.x -= 1
if self.collidesWithAnything():
self.x += 1
if player.y + self.offsetY > self.y:
self.y += 1
if self.collidesWithAnything():
self.y -= 1
elif player.y + self.offsetY < self.y:
self.y -= 1
if self.collidesWithAnything():
self.y += 1
self.healthBar()
def attack(self):
for i in range(3):
angle = random.randrange(0, 360)
bulletSpeed_x = self.bulletSpeed * math.cos(angle) + random.uniform(-5, 5)
bulletSpeed_y = self.bulletSpeed * math.sin(angle) + random.uniform(-5, 5)
all_bullets.append(Bullet(self.x, self.y, bulletSpeed_x, bulletSpeed_y, False))
def drawAlive(self, camx, camy):
if self.animationsCount + 1 == 32:
self.animationsCount = 0
self.animationsCount += 1
if self.attackRate == 0:
self.attackRate = 60
self.attack()
self.attackRate -= 1
screen.blit(pygame.transform.scale(self.aliveAnimations[self.animationsCount// 8], (32, 30)), (self.x - camx, self.y - camy))
class Weapon:
def __init__(self, bulletSpeed, fireRate, bulletDmg):
self.shooting = False
self.bulletSpeed = bulletSpeed
self.fireRate = fireRate
self.bulletDmg = bulletDmg
self.energy = 100
self.i = 0
def handle_weapons(self, screen):
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - width/2, mouse_y - height/2
angle = (180/math.pi) * - math.atan2(rel_y, rel_x)
#draw crosshair
screen.blit(crosshair, (mouse_x, mouse_y+5))
#rotate and draw weapon accordingly
if angle > 90 or angle < -90:
player_weapon_copy = pygame.transform.rotate(pygame.transform.flip(weaponModel, True, False), angle - 180)
else:
player_weapon_copy = pygame.transform.rotate(weaponModel, angle)
screen.blit(player_weapon_copy, (width/2 +5 - int(player_weapon_copy.get_width()/2), height/2 + 15 - int(player_weapon_copy.get_height()/2)))
def Shooting(self):
if self.shooting:
#calculates the angles the bullet should travel :/
if self.i % self.fireRate == 0:
if (self.energy == 0):
pass
else:
mouse_x, mouse_y = pygame.mouse.get_pos()
distance_x = mouse_x - width/2
distance_y = mouse_y - height/2
angle = math.atan2(distance_y, distance_x)
bulletSpeed_x = self.bulletSpeed * math.cos(angle)
bulletSpeed_y = self.bulletSpeed * math.sin(angle)
all_bullets.append(Bullet(player.x, player.y, bulletSpeed_x, bulletSpeed_y, True))
self.energy -= 1
self.i += 1
class Player:
def __init__(self, x, y):
self.x = int(x)
self.y = int(y)
self.leftPr = False
self.rightPr = False
self.downPr = False
self.upPr = False
self.speed = 4
self.health = 5
self.maxHp = 5
self.animationsCount = 0
def healthBar(self):
pygame.draw.rect(screen, (255, 0, 0), (20, 25, 200, 10))
pygame.draw.rect(screen, (0, 255, 0), (20, 25, self.health*40, 10))
def energyBar(self):
pygame.draw.rect(screen, (255, 0, 0), (20, 55, 200, 10))
pygame.draw.rect(screen, (48, 117, 255), (20, 55, weapon.energy*2, 10))
def collidesWithAnything(self):
for wall in walls:
if wall.collidesWith(self):
return True
return False
def update(self):
if self.leftPr and not self.rightPr:
self.x -= self.speed
if self.collidesWithAnything():
self.x += self.speed
if self.rightPr and not self.leftPr:
self.x += self.speed
if self.collidesWithAnything():
self.x -= self.speed
if self.upPr and not self.downPr:
self.y -= self.speed
if self.collidesWithAnything():
self.y += self.speed
# revert if collision
if self.downPr and not self.upPr:
self.y += self.speed
if self.collidesWithAnything():
self.y -= self.speed
# check for enemy colisions
def animations(self):
if self.animationsCount + 1 >= 36:
self.animationsCount = 0
if self.rightPr:
screen.blit(pygame.transform.scale(playerWalkAnimR[self.animationsCount//12],(64, 64)), (width/2 - 40, height/2 - 30))
elif self.leftPr:
screen.blit(pygame.transform.scale(playerWalkAnimL[self.animationsCount//12],(64, 64)), (width/2 - 40, height/2 - 30))
else:
screen.blit(pygame.transform.scale(playerWalkAnimR[0],(64, 64)), (width/2 - 40, height/2 - 30))
self.animationsCount += 1
weapon = Weapon(10, 20, 2)
player = Player(1, 0)
#Loads map from file
def loadMapFromFile(path):
walls.clear()
backgrounds.clear()
enemies.clear()
with open(path, "r") as f:
y = 0
enemyLocations = []
# bossLocations = []
for line in f.readlines():
x = 0
for char in line:
if char != ' ' and char != "\n":
backgrounds.append(Background(x * tileSize, y * tileSize))
if char == '#':
walls.append(Wall(x * tileSize, y * tileSize))
elif char == 'p' or char == 'P':
player.x = x * tileSize
player.y = y * tileSize
elif char == 'e' or char == 'E':
if random.randint(1, 100) <= 40:
enemyLocations.append((x * tileSize, y * tileSize))
# elif char == 'b' or char == 'B':
# if random.randint(1, 100) <= 10:
# bossLocations.append((x * tileSize, y * tileSize, "True"))
x += 1
y += 1
for enemyL in enemyLocations:
enemies.append(slimeEnemy(*enemyL))
# for enemyL in bossLocations:
# enemies.append(slimeEnemy(*enemyL, "True"))
def gameOverScreen():
pygame.font.init()
font = pygame.font.Font('arial.ttf', 32)
text = font.render('GAME OVER', True, (255, 0, 0), (0, 0, 0))
text.set_colorkey((0, 0, 0))
screen.blit(text, (width/2-80,height/2-40))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
loadMapFromFile('maps/Map0.txt')
hitCooldown = 75
cooldown = 600
#running loop
gameOver = False
running = True
while running:
if gameOver:
gameOverScreen()
pass
else:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player.upPr = True
if event.key == pygame.K_s:
player.downPr = True
if event.key == pygame.K_d:
player.rightPr = True
if event.key == pygame.K_a:
player.leftPr = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_w:
player.upPr = False
if event.key == pygame.K_s:
player.downPr = False
if event.key == pygame.K_d:
player.rightPr = False
if event.key == pygame.K_a:
player.leftPr = False
if event.type == pygame.MOUSEBUTTONDOWN:
weapon.shooting = True
if event.type == pygame.MOUSEBUTTONUP:
weapon.shooting = False
# updating
camx = player.x - width / 2
camy = player.y - height / 2
screen.fill((0, 0, 0))
player.update()
#check if slime got hit
for bullet in all_bullets:
bullet.update()
for enemy in enemies:
if enemy.collidesWith(player):
if hitCooldown < 0:
hitCooldown = 75
player.health -= enemy.slimeCollisionDmg
if player.health <= 0:
gameOver = True
elif enemy.collidesWith(bullet) and bullet.isPlayer:
removed_bullets.append(bullet)
for i in range(10):
hitParticles.append(Particle(enemy.x, enemy.y, random.randrange(-5, 5)/10, random.randrange(-5, 5)/10, 4, (108, 216, 32)))
enemy.SlimeHp -= weapon.bulletDmg
if enemy.SlimeHp <= 0:
enemy.isAlive = False
if player.health >= player.maxHp:
player.health = player.maxHp
else:
player.health += 1
if weapon.energy >= 100:
weapon.energy = 100
else:
weapon.energy += 2
if bullet.collidesWith(player) and not bullet.isPlayer:
if hitCooldown < 0:
hitCooldown = 75
player.health -= enemy.dmg
if player.health <= 0:
gameOver = True
removed_bullets.append(bullet)
for bg in backgrounds:
bg.draw(camx, camy)
for wall in walls:
wall.draw(camx, camy)
# update all other entities
#draw bullet
for bullet in all_bullets:
bullet.draw(camx, camy)
# slime animations and remove dead slimes
for enemy in enemies:
enemy.update()
if enemy.isAlive:
enemy.drawAlive(camx, camy)
if not enemy.isAlive:
enemies.remove(enemy)
# draw particles
for particle in hitParticles:
if particle.lifetime > 0:
particle.draw(screen, camx, camy)
else:
hitParticles.pop(hitParticles.index(particle))
player.animations()
weapon.handle_weapons(screen)
weapon.Shooting()
player.healthBar()
player.energyBar()
if len(enemies) == 0:
if cooldown <= 0:
cooldown = 600
loadMapFromFile("maps\Map{0}.txt".format(level+1))
level+=1
hitCooldown -= 1
cooldown -= 1
# finally update screen
pygame.display.update()
clock.tick(60)
I know it is written really badly but I'm learning. The game is really laggy and I was wondering how I can make it run faster and smoother. Also is the lag created from the many calculations or am I just dumb? The lag comes when there are a lot of enemies on the map and they shoot at the same time. Can I fix the lag by making all the enemies shoot in different times? The game is for a school project.
I think your biggest issue is this:
class Bullet:
[ ... ]
def update(self):
self.x += self.velx
self.y += self.vely
for bullet in all_bullets: # <-- HERE
for wall in walls:
if bullet.collidesWith(wall):
removed_bullets.append(bullet)
for i in removed_bullets:
if i in all_bullets:
all_bullets.remove(i)
Then in the main loop:
#check if slime got hit
for bullet in all_bullets: # <-- AGAIN
bullet.update()
The code is iterating through the bullets many more times than is necessary. By the time all your bullet objects are updated, they have been processed /N/ * /N/ times (i.e.: N-squared). So as you get more any more bullets, this square grows catastrophically.
Try changing your Bullet.update() to only check itself. This will probably fix your lag issues - or at least help a lot. It will also "correct" your object encapsulation. A single Bullet has no business knowing about all the others, let alone checking collisions on their behalf.
class Bullet:
[ ... ]
def update(self):
self.x += self.velx
self.y += self.vely
for wall in walls:
if self.collidesWith(wall):
all_bullets.remove( self )
break
If you have any more N-squared loops like this, it will give the best speedup reworking them first.
Related
Pygame collisions is wierd [duplicate]
This question already has answers here: Pygame: Collision by Sides of Sprite (5 answers) How can I rewrite my collision logic in my pygame platformer? (1 answer) Closed 16 days ago. So Ive been at this one problem for more than a week now and i feel like Ive tried everything. Nothing works, Ive looked it up, but at the same time I don't want to just copy code i would like to actually understand it. def x_collision(self): for tile in tiles: self.rect = pygame.Rect(self.x, self.y, self.size, self.size) if tile.colliderect(self.rect): if self.x_vel > 0: self.x = tile.right if self.x_vel < 0: self.x = tile.left - self.size def y_collision(self): for tile in tiles: self.rect = pygame.Rect(self.x, self.y, self.size, self.size) if tile.colliderect(self.rect): if self.y_vel > 0: self.y = tile.bottom if self.y_vel < 0: self.y = tile.top This is just the two main functions for the collision located inside the player class. Ive gotten the collisions to work on each axis but when i merge the two together it just goes insane, I'm guessing this has something to do with the position of the player being set in the wrong place but that shouldn't be the problem considering im not checking for the collision in one function but instead in two different for each the x and y. Just, it's probably because of some stupid reason and it shouldn't be that hard if you're not dumb like me but i just can't seem to figure it out Also if you're testing the code just delete everything that has something to do with sound! Here is the entire code for reference (i know it's messy but im very new to this so good luck!) 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(): 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 rebuild_level(): global tiles, enemy_spawns tiles = [] enemy_spawns = [] build_level() 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 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, x, y): global projectiles self.speed = 5 self.x = x self.y = y self.x_vel = 0 self.y_vel = 0 self.px_vel = 0 self.py_vel = 0 self.size = tile_size self.gunsurface = pygame.Surface((90, 25), pygame.SRCALPHA) self.healthboost = 1 self.health = 100 * 1 self.rotated_gunsurface = self.gunsurface self.gunsurface.fill((150, 150, 150)) self.gunrect = self.gunsurface.get_rect() projectiles = [Projectile(-100, 0, 0)] self.last_shot_time = 0 self.cooldown = 0.5 self.is_slowed = False dx = 0 dy = 0 self.angle = math.atan2(dy, dx) self.force = 72 self.forceCounterCuzIAmBadAtPython = self.force self.rect = pygame.Rect(self.x, self.y, self.size, self.size) self.alive = True self.colliding = False def x_collision(self): for tile in tiles: self.rect = pygame.Rect(self.x, self.y, self.size, self.size) if tile.colliderect(self.rect): if self.x_vel > 0: self.x = tile.right if self.x_vel < 0: self.x = tile.left - self.size def y_collision(self): for tile in tiles: self.rect = pygame.Rect(self.x, self.y, self.size, self.size) if tile.colliderect(self.rect): if self.y_vel > 0: self.y = tile.bottom if self.y_vel < 0: self.y = tile.top def Movement(self): global tiley_offset, tilex_offset self.keys = pygame.key.get_pressed() tilex_offset += self.x_vel self.x_collision() if self.x_vel != 0: self.x_vel -= self.px_vel self.forceCounterCuzIAmBadAtPython -= 1 if self.forceCounterCuzIAmBadAtPython <= 0: self.x_vel = 0 tiley_offset += self.y_vel self.y_collision() if self.y_vel != 0: self.y_vel -= self.py_vel if self.forceCounterCuzIAmBadAtPython <= 0: self.y_vel = 0 rebuild_level() 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): global dx, dy if pygame.key.get_pressed()[pygame.K_e]: current_time = time.time() if current_time - self.last_shot_time > self.cooldown: self.forceCounterCuzIAmBadAtPython = self.force 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) self.angle = math.atan2(dy, dx) projectiles.append(Projectile(self.x+self.size/2, self.y+self.size/2, self.angle)) self.last_shot_time = current_time self.x_vel = math.cos(self.angle) * self.speed self.y_vel = math.sin(self.angle) * self.speed self.px_vel = self.x_vel / self.force self.py_vel = self.y_vel / self.force def Render(self): self.rect = pygame.Rect(self.x, self.y, self.size, self.size) pygame.draw.rect(screen, (230, 100, 100), self.rect) self.Gun() 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) projectiles = [Projectile(-100, 0, 0)] def main(): player = Player(screen.get_width()/2-tile_size/2, screen.get_height()/2-tile_size/2) 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) build_level() 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, 250) number_of_enemies = len(enemies) if spawn_enemies_chance == 1 and number_of_enemies <= 4: Spawn_enemies(1,player) draw_level() # enemy for enemy in enemies: if enemy.alive: enemy.Movement() enemy.Spawning() for projectile in projectiles: if enemy.hitbox.colliderect(projectile.hitbox): enemy.health -= 1 pop_sound.play() if enemy.health <= 0: try: enemy.alive = False enemies.remove(enemy) except: print("list.remove(x): x not in list... Enemy moment") projectiles.remove(projectile) # Get Hurt for enemy in enemies: if enemy.hitbox.colliderect(player): player.health -= 10 enemies.remove(enemy) # Projectiles for i, projectile in enumerate(projectiles): projectile.Move() projectile.Render() if projectiles != []: if projectile.x < 0 or projectile.x > screen.get_width() or projectile.y < 0 or projectile.y > screen.get_height(): projectiles.pop(i) for tile in tiles: if projectiles != []: if tile.colliderect(projectile.hitbox): try: projectiles.pop(i) except: print(i," pop index out of range, don't mind it :)") # player player.Alive() player.Shoot() player.Movement() 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()
Why is my enemybullets shooting from wrong area in Pygame
I am currently creating a top-down shooter in Pygame and currently need my enemies to shoot bullets at my player. The problem is that when I move my character the position where the bullets are shooting from moves as well when they are meant to be shot from the enemies at all times. I have watched many different tutorials but non of which have proven helpful. Hoping someone can help. import pygame import sys import math import random import time import multiprocessing from pygame import mixer pygame.init() displayWidth = 100 displayHeight = 200 enemytime = time.time() + 10 enemyshoottime = time.time() + 1 #Enemy class Enemy1(object): def __init__(self, x, y): self.x = x self.y = y self.hit_box = (self.x-10, self.y -10, 70, 70) self.animation_images = [pygame.image.load("Enemy1_animation_0.png"), pygame.image.load("Enemy1_animation_1.png"), pygame.image.load("Enemy1_animation_2.png"), pygame.image.load("Enemy1_animation_3.png")] self.animation_count = 0 self.reset_offset = 0 self.offset_x = random.randrange(-150, 150) self.offset_y = random.randrange(-150, 150) self.health = 4 def main(self, display): if self.animation_count + 1 == 16: self.animation_count = 0 self.animation_count += 1 if self.reset_offset == 0: self.offset_x = random.randrange(-150, 150) self.offset_y = random.randrange(-150, 150) self.reset_offset = random.randrange(120, 150) else: self.reset_offset -= 1 if player.x + self.offset_x > self.x-display_scroll[0]: self.x += 1 elif player.x + self.offset_x < self.x-display_scroll[0]: self.x -= 1 if player.y + self.offset_y > self.y-display_scroll[1]: self.y += 1 elif player.y + self.offset_y < self.y-display_scroll[1]: self.y -= 1 display.blit(pygame.transform.scale(self.animation_images[self.animation_count//4], (50, 50)), (self.x-display_scroll[0], self.y-display_scroll[1])) self.hit_box = (self.x-display_scroll[0]-10, self.y-display_scroll[1]-10, 70, 70) pygame.draw.rect(display, (255, 0, 0), self.hit_box, 2) #Enemy Bullet class EnemyBullet: def __init__(self, x, y, playerx, playery): self.x = x self.y = y self.playerx = 300 self.playery = 300 self.speed = 7 self.angle = math.atan2(y-playerx, x-playery) self.x_vel = math.cos(self.angle) * self.speed self.y_vel = math.sin(self.angle) * self.speed def main(self, display): self.x -= int(self.x_vel) self.y -= int(self.y_vel) EnemyBulletRect = pygame.draw.circle(display, (255,0,0), (self.x, self.y), 5) #list's enemies = [ Enemy2(800, -200), Enemy3(-300, 500), Enemy4(1000, 400)] enemy = Enemy1(600, 400) enemy_bullets = [] sounds = ['explosion1.mp3', 'explosion2.mp3', 'explosion3.mp3'] player = Player(400, 300, 32, 32) display_scroll = [0,0] player_bullets = [] while True: display.fill((0, 0, 0)) display.blit(displayImage, (0, 0)) #display.blit(ImageBackground, (0, 0)) display.blit(Earth, (700, 100)) show_score(textX, textY) for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() pygame.quit() #Enemy shoots if time.time() >= enemyshoottime: enemy_bullets.append(EnemyBullet(enemy.x, enemy.y, playerx, playery)) from playsound import playsound playsound('lazer.mp3', block=False) enemyshoottime = time.time() + 1 for bullets in enemy_bullets: bullets.main(display) #spawn enemies if time.time() >= enemytime: # Time to spawn a new enemy. enemies.append( Enemy3( 100, 500 ) ) enemies.append( Enemy3( 600, 400 ) ) # Reset the alarm. enemytime = time.time() + 10
The motion vector of the bullet can be calculated from the normalized direction vector (Unit vector) multiplied by the velocity. The unit vector is obtained by dividing the components of the vector by the Euclidean length of the vector. Do not round the velocity before adding it to the position, but round the position when using it to draw the bullet. If you round the component vectors before adding them or round the position attributes themselves, there will be an inaccuracy that accumulates over time. Round only the values that are used in pygame.draw.circle Use round instead of int. class EnemyBullet: def __init__(self, x, y, playerx, playery): self.x = x self.y = y self.speed = 7 dx = playerx - x dy = playery - y d = math.sqrt(dx*dx, dy*dy) # or d = math.hypot(dx, dy) self.x_vel = self.speed * dx / d self.y_vel = self.speed * dy / d def main(self, display): self.x += self.x_vel self.y += self.y_vel EnemyBulletRect = pygame.draw.circle( display, (255,0,0), (round(self.x), round(self.y)), 5)
How do I get the enemy to chase the player? I would also like to know how to jump onto the moving object when I jump and not just go through it
moving object. It currently moves left and right across the screen and I would like the user to be able to jump on it. def moving_object(): global x_speed, y_speed pygame.draw.rect(win, (200, 140, 150), rectangle) rectangle.x += x_speed if rectangle.right >= 500 or rectangle.left <= 0: x_speed *= -1 class for my enemy. They currently move left and right across the screen but I would like them to follow the user. They can also not jump class enemy(): walkRight = [pygame.image.load('R1E.png'), pygame.image.load('R2E.png'), pygame.image.load('R3E.png'), pygame.image.load('R4E.png'), pygame.image.load('R5E.png'), pygame.image.load('R6E.png'), pygame.image.load('R7E.png'), pygame.image.load('R8E.png'), pygame.image.load('R9E.png'), pygame.image.load('R10E.png'), pygame.image.load('R11E.png')] walkLeft = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), pygame.image.load('L3E.png'), pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), pygame.image.load('L6E.png'), pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), pygame.image.load('L9E.png'), pygame.image.load('L10E.png'), pygame.image.load('L11E.png')] def __init__(self, x, y, width, height, end): self.x = x self.y = y self.width = width self.height = height self.end = end self.WalkCount = 0 self.vel = 3 self.path = [self.x, self.end] self.hitbox = (self.x + 17, self.y + 2, 31, 57) self.health = 10 self.visible = True def draw(self, win): self.move() if self.visible: if self.WalkCount + 1 >= 33: self.WalkCount = 0 if self.vel > 0: win.blit(self.walkRight[self.WalkCount //3], (self.x, self.y)) self.WalkCount += 1 else: win.blit(self.walkLeft[self.WalkCount //3], (self.x, self.y)) self.WalkCount += 1 pygame.draw.rect(win, (255, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 50, 10)) pygame.draw.rect(win, (0, 255, 0), (self.hitbox[0], self.hitbox[1] - 20, 50, 10)) self.hitbox = (self.x + 17, self.y + 2, 31, 57) def move(self): if self.vel > 0: if self.x + self.vel < self.path[1]: self.x += self.vel else: self.vel = self.vel * -1 self.WalkCount = 0 else: if self.x - self.vel > self.path[0]: self.x += self.vel else: self.vel = self.vel * -1 self.WalkCount = 0 def hit(self): if self.health > 0: self.health -= 1 else: self.visible = False pass
So here is one way to make a pathfinding algorithm: import pygame import time pygame.init() screen = pygame.display.set_mode((700, 500)) clock = pygame.time.Clock() to_coords = [550, 100] from_coords = (150, 400) def go_to_koeff(from_, to): dx = to[0] - from_[0] dy = to[1] - from_[1] if dx != 0: dx /= abs(dx) if dy != 0: dy /= abs(dy) return dx, dy follow_coords_x = from_coords[0] follow_coords_y = from_coords[1] velocity = 1 run = True while run: clock.tick(60) screen.fill((0, 0, 0)) pygame.draw.circle(screen, (0, 255, 0), to_coords, 5) pygame.draw.circle(screen, (255, 0, 0), from_coords, 5) pygame.draw.circle(screen, (255, 255, 255), (follow_coords_x, follow_coords_y), 5) koeff = go_to_koeff((follow_coords_x, follow_coords_y), to_coords) follow_coords_x += velocity * koeff[0] follow_coords_y += velocity * koeff[1] keys = pygame.key.get_pressed() if keys[pygame.K_UP]: to_coords[1] -= 3 if keys[pygame.K_DOWN]: to_coords[1] += 3 if keys[pygame.K_LEFT]: to_coords[0] -= 3 if keys[pygame.K_RIGHT]: to_coords[0] += 3 for event in pygame.event.get(): if event.type == pygame.QUIT: run = False pygame.display.update() pygame.quit() so basically the whole algorithm is that go_to_koeff() function: that function returns the koefficent of direction that can be used to change where the white dot moves that can be seen where the coefficient is multiplied by velocity. This algorithm isn't perfect but it finds the position so there is that. (You can play around a bit by moving the green dot with arrow keys to see how it moves) Another idea that came to mind was tracking player position in a list and making the enemy follow all the coords in the list which could be even worse but it will feel like it is actually following You. Also I just noticed that there is some useless change of variable names, but it doesn't affect anything much.
x coordinates of enemy doesn't change (pygame)
I'm currently making submarine game with pygame. I wanted enemy to move left and right. However, although I added code for changing x coordinates they wont change. Error actualy started when I added one more enemy to the game. Before when there was only one enemy this problem didn't occur. I tried to search for problem myself, but unsuccesfully. Here's the code: import pygame pygame.init() run = True screen = pygame.display.set_mode((600, 500), pygame.FULLSCREEN) pygame.display.set_caption('Podmornca') background = pygame.image.load('background1.png') bottom = pygame.image.load('bottom.png') bottomx = 100 shoot = pygame.mixer.Sound('torpedo-5.wav') clock = pygame.time.Clock() enemyL = pygame.image.load('enemyL.png') enemyD = pygame.image.load('enemyD.png') torpL = pygame.image.load('torpL.png') torpD = pygame.image.load('torpD.png') hit = pygame.mixer.Sound('torpedo-fire.wav') explosion = pygame.mixer.Sound('explosion.wav') ekspl = pygame.image.load('explosion.png') font = pygame.font.SysFont('bauhaus93', 40) win = font.render('YOU WON!', 1, (255, 0, 0)) lose = font.render('GAME OVER!', 1, (255, 0, 0)) class enemy1(): def __init__(self, x, y, v): self.x = x self.y = y self.v = v self.start = 1300 self.end = 1800 self.left = True self.right = False self.way = -1 self.projectilex = x self.projectiley = y self.shot = False self.frame = (self.x, self.y, 200, 92) self.life = 300 def shoot(self): if self.shot: shoot.play() if self.left: screen.blit(torpL, (self.projectilex, self.projectiley + 40)) self.projectilex += 10 * self.way if self.right: screen.blit(torpD, (self.projectilex, self.projectiley + 40)) self.projectilex += 10 * self.way if self.projectilex > 600 or self.projectilex < 0: self.shot = False self.projectilex = self.x self.projectiley = self.y def move(self): if self.life > 0: if self.left: self.frame = (self.x + 10, self.y, 177, 92) pygame.draw.rect(screen, (255,0,0), (self.x + 60, self.y - 5, self.life * 0.25, 4 ), 0) self.way = -1 if self.x > self.end: screen.blit(enemyL, (self.x, self.y)) self.x -= self.v if self.x == self.end: self.left = False self.right = True if self.right: self.way = 1 self.frame = (self.x + 10, self.y, 177, 92) if self.x < self.start: pygame.draw.rect(screen, (255,0,0), (self.x + 60, self.y - 5, self.life * 0.25, 4 ), 0) screen.blit(enemyD, (self.x, self.y)) self.x += self.v if self.x == self.start: self.left = True self.right = False if self.x - player.x <= 300: self.shoot() self.shot = True else: explosion.play() class enemy2(): def __init__(self, x, y, v): self.x = x self.y = y self.v = v self.start = 1900 self.end = 2400 self.left = True self.right = False self.way = -1 self.projectilex = x self.projectiley = y self.shot = False self.frame = (self.x, self.y, 200, 92) self.frame = 300 def shoot(self): if self.shot: shoot.play() if self.left: screen.blit(torpL, (self.projectilex, self.projectiley + 40)) self.projectilex += 10 * self.way if self.right: screen.blit(torpD, (self.projectilex, self.projectiley + 40)) self.projectilex += 10 * self.way if self.projectilex > 600 or self.projectilex < 0: self.shot = False self.projectilex = self.x self.projectiley = self.y def move(self): if self.life > 0: if self.left: self.frame = (self.x + 10, self.y, 177, 92) pygame.draw.rect(screen, (255,0,0), (self.x + 60, self.y - 5, self.life * 0.25, 4 ), 0) self.way = -1 if self.x > self.end: screen.blit(enemyL, (self.x, self.y)) self.x -= self.v if self.x == self.end: self.left = False self.right = True if self.right: self.way = 1 self.frame = (self.x + 10, self.y, 177, 92) if self.x < self.start: pygame.draw.rect(screen, (255,0,0), (self.x + 60, self.y - 5, self.life * 0.25, 4), 0) screen.blit(enemyD, (self.x, self.y)) self.x += self.v if self.x == self.start: self.left = True self.right = False else: explosion.play() submarine1 = enemy1(1300, 250, 5) submarine2 = enemy2(1900, 250, 5) right = True left = False def graphics(): global bottomx clock.tick(60) screen.blit(background, (0,0)) screen.blit(bottom, (bottomx, 250)) if bottomx < -594: bottomx = 1194 submarine1.move() podmornca2.premik() pygame.display.flip() while run: for event in pygame.event.get(): if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: run = False if event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT: bottomx += 5 submarine1.start += 5 submarine2.start += 5 submarine1.end += 5 submarine2.end += 5 if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: bottomx -= 5 submarine1.start -= 5 submarine2.start -= 5 submarine1.end -= 5 submarine2.end -= 5 if submarine1.life < 0 and submarine2.life < 0: screen.blit(ekspl, (submarine1.x, submarine1.y)) screen.blit(ekspl, (submarine1.x + 5, submarine1.y)) screen.blit(ekspl, (submarine2.x, submarine2.y)) screen.blit(ekspl, (submarine2.x + 5, submarine2.y)) screen.blit(end, (200, 250)) graphics() pygame.quit()
A couple things can help... As stated above, just use one enemy class and derive both submarines from it like: sub1 = enemy(...) sub2 = enemy(...) Your main problem with the moving right is that you have your start and end points reversed. You move right towards the start, so start must be greater than end. You have them flipped. In several spots, you should be using if-else or if-elif-else instead of a bunch of if statements to clean up the logic and make sure one or the other executes. Good luck!
python error 'global name 'player' is not defined'
ive been learning python on my own for the past cople months and ive started making games about a weak ago (with pygame) and i have absolutly no idea what i did wrong this is my code (btw im making pong): import pygame pygame.init() display_width = 800 display_height = 600 black = (0, 0, 0) white = (255, 255, 255) gameDisplay = pygame.display.set_mode((display_width, display_height)) pygame.display.set_caption('pong') clock = pygame.time.Clock() FPS = 60 class Player(): def __init__(self): self.padWid = 8 self.padHei = 64 self.x = display_width - 16 self.y = (display_height/2) - (self.padHei/2) self.speed = 3 self.score = 0 self.scoreFont = pygame.font.Font('freesansbold.ttf', 40) def scoring(self): self.text = self.scoreFont.render(str(self.score),True,white) gameDisplay.blit(self.text, [(display_width * 0.75),30]) def movement(self): key = pygame.key.get_pressed() if key[pygame.K_UP]: self.y -= self.speed elif key[pygame.K_DOWN]: self.y += self.speed if self.y <= 0: self.y = 0 elif self.y >= display_height - self.padHei: self.y = display_height - self.padHei def draw(self): pygame.draw.rect(gameDisplay, white, [self.x, self.y, self.padWid, self.padHei]) class Enemy(): def __init__(self): self.padWid = 8 self.padHei = 64 self.x = 16 self.y = (display_height/2) - (self.padHei/2) self.speed = 3 self.score = 0 self.scoreFont = pygame.font.Font('freesansbold.ttf', 40) def scoring(self): self.text = self.scoreFont.render(str(self.score),True,white) gameDisplay.blit(self.text, [(display_width * 0.23),30]) def movement(self): key = pygame.key.get_pressed() if key[pygame.K_w]: self.y -= self.speed elif key[pygame.K_s]: self.y += self.speed if self.y <= 0: self.y = 0 elif self.y >= display_height - self.padHei: self.y = display_height - self.padHei def draw(self): pygame.draw.rect(gameDisplay, white, [self.x, self.y, self.padWid, self.padHei]) class Ball(): def __init__(self): self.x = display_width/2 self.y = display_height/2 self.radius = 4 self.x_speed = -3 self.y_speed = 3 def movement(self): self.x += self.x_speed self.y += self.y_speed if self.y <= self.radius or self.y >= display_height - self.radius: self.y *= -1 if self.x + self.radius >= display_width: self.__init__() enemy.score += 1 elif self.x - self.radius <= 0: self.__init__() player.score += 1 for n in range(-self.radius, [player.padHei + self.radius]): if self.y == player.y + n: if self.x + self.radius >= player.x: self.x_speed *= -1 break n += 1 for n in range(-self.radius, [enemy.padHei + self.radius]): if self.y == player.y + n: if self.x - self.radius <= enemy.x + enemy.w: self.x_speed *= -1 break n += 1 def draw(self): pygame.draw.circle(gameDisplay, white, [self.x, self.y], self.radius) def game_loop(): running = True player = Player() enemy = Enemy() ball = Ball() while running: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() gameDisplay.fill(black) player.movement() enemy.movement() ball.movement() player.draw() enemy.draw() ball.draw() player.scoring() enemy.scoring() pygame.display.update() game_loop() I always get the following error: Traceback (most recent call last): File "C:\programs\python pong\pong.py", line 159, in <module> game_loop() File "C:\programs\python pong\pong.py", line 148, in game_loop ball.movement() File "C:\programs\python pong\pong.py", line 112, in movement for n in range(-self.radius, [player.padHei + self.radius]): NameError: global name 'player' is not defined
You create playerin the function game_loop(), but this variable is local, so you are not able to see it outside that function. Pass the player as an argument in the ball.movement() function. In this way: def movement(self, player): self.x += self.x_speed self.y += self.y_speed ... ... ball.movement(player)