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()
How would I get the enemy to avoid the characters line at all costs while still trying to eliminate him? The code I have as far makes the enemy run straight to the player and die pretty much straight away.
Does anyone have any idea how to keep the enemy from touching the players line?
Here is the code:
import random
import pygame, sys
from pygame.locals import *
import time
GREEN=(0,125,0)
BLUE=(0,0,125)
Yplayer = 225/2
Xplayer = 350
playerlinesavedata = []
playercurrent = []
Xplayerchange=0
Yplayerchange=0
Yenemy = 450
Xenemy = 300
enemylinesavedata = []
enemycurrent = []
Xenemychange=0
Yenemychange=0
windowX = 900
windowY = 600
pygame.init()
DISPLAY=pygame.display.set_mode((windowX,windowY),0,32)
def enemymovement():
global Xplayer,Yplayer,Xenemy,Yenemy
enemy1 = True
enemydirection = random.randint(1,4)
# enemy moves toward player
if Xenemy > Xplayer: enemydirection = 1
elif Xenemy < Xplayer: enemydirection = 2
elif Yenemy > Yplayer: enemydirection = 3
elif Yenemy < Yplayer: enemydirection = 4
while enemy1 == True:
if enemydirection == 1:
Xenemychange= -random.randint(1,4)
Xenemy+=Xenemychange
if enemydirection == 2:
Xenemychange= random.randint(1,4)
Xenemy+=Xenemychange
if enemydirection == 3:
Yenemychange= -random.randint(1,4)
Yenemy+=Yenemychange
if enemydirection == 4:
Yenemychange= random.randint(1,4)
Yenemy+=Yenemychange
enemylinesavedata.append([Xenemy,Yenemy])
enemy()
break
def enemy():
for XnY in enemylinesavedata:
pygame.draw.rect(DISPLAY,BLUE, [XnY[0],XnY[1],5,5])
def player():
for XnY in playerlinesavedata:
pygame.draw.rect(DISPLAY,GREEN,[XnY[0],XnY[1],5,5])
def main():
global playerlinesavedata, enemylinesavedata
global Xplayer, Yplayer, Xenemy, Yenemy
WHITE = (255,255,255)
size = 5
clock = pygame.time.Clock()
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
Xplayer += (keys[pygame.K_d] - keys[pygame.K_a]) * size
Yplayer += (keys[pygame.K_s] - keys[pygame.K_w]) * size
playerlinesavedata.append((Xplayer, Yplayer))
enemymovement()
player_rectlist = [pygame.Rect(x, y, size, size) for x, y in playerlinesavedata]
enemy_rectlist = [pygame.Rect(x, y, size, size) for x, y in enemylinesavedata]
if player_rectlist[-1].collidelist(enemy_rectlist) >= 0:
pygame.quit()
sys.exit()
if enemy_rectlist[-1].collidelist(player_rectlist) >= 0:
playerlinesavedata = []
Yplayer = 225//2
Xplayer = 350
enemylinesavedata = []
Yenemy = 450
Xenemy = 300
DISPLAY.fill(WHITE)
player()
enemy()
pygame.display.update()
main()
It is a bad practice to use globalize variables. It is much better to use python classes as the pygame objects.
Here is a cleaned up version of your code so that you or other users on Stack Overflow can help you with your problem more easily:
import random
import pygame, sys
from pygame.locals import *
import time
pygame.init()
GREEN = (0, 125, 0)
BLUE = (0, 0, 125)
WHITE = (255,255,255)
windowX = 900
windowY = 600
wn = pygame.display.set_mode((windowX, windowY), 0, 32)
class Object:
def __init__(self, X, Y, enemy=False, color=GREEN):
self.enemy = enemy
self.X = X
self.Y = Y
self.current = []
self.Xchange = 0
self.Ychange = 0
self.direction = 0
self.rectlist = []
self.color = color
self.size = 5
def movement(self):
for rect in self.rectlist:
pygame.draw.rect(wn, self.color, rect)
if self.enemy:
if self.X > player.X:
self.direction = 1
elif self.X < player.X:
self.direction = 2
elif self.Y > player.Y:
self.direction = 3
elif self.Y < player.Y:
self.direction = 4
if self.direction == 1:
self.Xchange = -random.randint(1,4)
self.X += self.Xchange
if self.direction == 2:
self.Xchange = random.randint(1,4)
self.X += self.Xchange
if self.direction == 3:
self.Ychange = -random.randint(1,4)
self.Y += self.Ychange
if self.direction == 4:
self.Ychange = random.randint(1,4)
self.Y += self.Ychange
else:
keys = pygame.key.get_pressed()
player.X += (keys[pygame.K_d] - keys[pygame.K_a]) * player.size
player.Y += (keys[pygame.K_s] - keys[pygame.K_w]) * player.size
self.rectlist.append(pygame.Rect(self.X, self.Y, self.size, self.size))
player = Object(350, 225 / 2)
enemy = Object(450, 300, enemy=True, color=BLUE)
def main():
clock = pygame.time.Clock()
size = 5
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
player.movement()
enemy.movement()
if player.rectlist[-1].collidelist(enemy.rectlist) >= 0:
pygame.quit()
sys.exit()
if enemy.rectlist[-1].collidelist(player.rectlist) >= 0:
player.rectlist = []
player.X = 350
player.Y = 225 // 2
enemy.rectlist = []
enemy.X = 450
enemy.Y = 300
pygame.display.update()
wn.fill(WHITE)
main()
Here is my implementation of how to let the enemy avoid the player, though it is very buggy, as in, it will go through the player's line
most of the time:
import random
import pygame, sys
from pygame.locals import *
import time
pygame.init()
GREEN = (0, 125, 0)
BLUE = (0, 0, 125)
WHITE = (255,255,255)
windowX = 900
windowY = 600
wn = pygame.display.set_mode((windowX, windowY), 0, 32)
class Object:
def __init__(self, X, Y, enemy=False, color=GREEN):
self.enemy = enemy
self.X = X
self.Y = Y
self.current = []
self.Xchange = 0
self.Ychange = 0
self.direction = 0
self.rectlist = []
self.color = color
self.size = 5
self.bounce = 0
def movement(self):
for rect in self.rectlist:
pygame.draw.rect(wn, self.color, rect)
if self.enemy:
enemydirection = random.randint(1,4)
if not self.bounce:
if self.X > player.X:
self.direction = 1
elif self.X < player.X:
self.direction = 2
elif self.Y > player.Y:
self.direction = 3
elif self.Y < player.Y:
self.direction = 4
else:
self.bounce -= 1
if self.direction == 1:
self.Xchange = -random.randint(1,4)
self.X += self.Xchange
if self.direction == 2:
self.Xchange = random.randint(1,4)
self.X += self.Xchange
if self.direction == 3:
self.Ychange = -random.randint(1,4)
self.Y += self.Ychange
if self.direction == 4:
self.Ychange = random.randint(1,4)
self.Y += self.Ychange
else:
keys = pygame.key.get_pressed()
player.X += (keys[pygame.K_d] - keys[pygame.K_a]) * player.size
player.Y += (keys[pygame.K_s] - keys[pygame.K_w]) * player.size
self.rectlist.append(pygame.Rect(self.X, self.Y, self.size, self.size))
player = Object(350, 225 / 2)
enemy = Object(450, 300, enemy=True, color=BLUE)
def main():
clock = pygame.time.Clock()
size = 5
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
player.movement()
enemy.movement()
if player.rectlist[-1].collidelist(enemy.rectlist) >= 0:
pygame.quit()
sys.exit()
if enemy.rectlist[-1].collidelist(player.rectlist) >= 0:
if not enemy.bounce:
while enemy.rectlist[-1].collidelist(player.rectlist) >= 0:
enemy.rectlist.pop()
enemy.rectlist.pop()
enemy.rectlist.pop()
enemy.X, enemy.Y = enemy.rectlist[-1].x, enemy.rectlist[-1].y
enemy.direction = enemy.direction - 2 if enemy.direction - 2 else enemy.direction + 2
enemy.bounce = 40
pygame.display.update()
wn.fill(WHITE)
main()
Output: