How would you have an object move randomly in an area? - python

I have blitted 4 zombies to the screen in pygame. And the zombies need to wander in a specific region (let us say a 10 x 10 area) around where they were blitted. Apparently, making it so that after the player enters the said area, to have the zombie chase the player, is too difficult.
import pygame, random, os
from pygame.locals import *
pygame.init()
scr_width = 1020
scr_height = 510
screen = pygame.display.set_mode((scr_width, scr_height))
clock = pygame.time.Clock()
images = {}
path = 'Desktop/Files/Dungeon Minigame/'
filenames = [f for f in os.listdir(path) if f.endswith('.png')]
for name in filenames:
imagename = os.path.splitext(name)[0]
images[imagename] = pygame.image.load(os.path.join(path, name))
pygame.display.set_caption('Dungeon Minigame')
font = pygame.font.SysFont('Times_New_Roman', 27)
white = [240, 240, 240]
fps = 60
lives = 3
score = 0
playerX = 510
playerY = 220
playerxchange = 0
playerychange = 0
def player(x, y):
screen.blit(images['r_knight'], (playerX, playerY))
class Enemy:
def __init__(self):
self.x = random.randint(8, 800)
self.y = random.randint(8, 440)
self.moveX = 0
self.moveY = 0
def move(self):
self.speed = 3
def draw(self):
screen.blit(images['r_zombie'], (self.x, self.y))
def enemy(x, y):
screen.blit(images['r_zombie'], (x, y))
enemy_list = []
for i in range(4):
new_enemy = Enemy()
enemy_list.append(new_enemy)
while True:
for enemy in enemy_list:
enemy.move()
enemy.draw()
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
clock.tick(fps)
pygame.display.update()

Here is some code that makes the enemy follow the player
class Enemy:
def __init__(self, x, y, images, name):
self.x = x
self.y = y
self.images = images
self.animation_count = 0
self.offset_x = random.randrange(-600, 600)
self.offset_y = random.randrange(-600, 600)
self.reset_offset = 0
self.bullet_cooldown = 0
self.health = 50
def main(self, display, player_pos_x, player_pos_y):
if self.reset_offset == 0:
self.offset_x = random.randrange(-400, 400)
self.offset_y = random.randrange(-400, 400)
self.reset_offset = random.randrange(75, 100)
else:
self.reset_offset -= 1
if player_pos_x + self.offset_x > self.x]:
self.x += random.randrange(1, 3)
elif player_pos_x + self.offset_x < self.x:
self.x -= random.randrange(1, 3)
if player_pos_y + self.offset_y > self.y:
self.y += random.randrange(1, 3)
elif player_pos_y + self.offset_y < self.y:
self.y -= random.randrange(1, 3)
display.blit(image, (self.x, self.y))

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)

Rotate a rectangle over waves

I am trying to move a rectangle over the waves,trying to simulate a boat sailing.
For that, I rotate the rectangle using the height of the drawn lines and calculating the angle they form with the rectangle.
However, for some reason, in some points of the waves the rectangle is flickering.
The code shows the problem.
import sys
import math
import pygame
from pygame.locals import *
pygame.init()
SCREEN_WIDTH = 900
SCREEN_HEIGHT = 900
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()
BLUE = (0, 103, 247)
RED = (255,0,0)
watter_levels = [0 for _ in range(SCREEN_WIDTH)]
def draw_water(surface, dy):
amplitude = 35
global watter_levels
for x in range(SCREEN_WIDTH):
y = int(math.sin((x)*(0.01)) * amplitude + dy)
watter_levels[x] = y
pygame.draw.aaline(surface,BLUE,(x,y),(x,y))
def get_water_level(index):
if index <= 0:
return watter_levels[0]
if index >= SCREEN_WIDTH:
return watter_levels[-1]
return watter_levels[index]
font = pygame.font.Font(None,30)
def debug(info,x=10,y=10):
display_surf = pygame.display.get_surface()
debug_surface = font.render(str(info),True,'White')
debug_rect = debug_surface.get_rect(topleft=(x,y))
pygame.draw.rect(display_surf,'Black',debug_rect)
display_surf.blit(debug_surface,debug_rect)
class Ship(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((80,80),pygame.SRCALPHA)
self.image.fill('red')
self.rect = self.image.get_rect(topleft=(0,0))
self.copy_img = self.image.copy()
self.move = pygame.math.Vector2(0,0)
self.copy_img = self.image.copy()
self.velocity = 8
def rotate(self, angle):
self.image = pygame.transform.rotate(self.copy_img, int(angle))
self.rect = self.image.get_rect(center=(self.rect.center))
def update(self):
self.get_input()
self.rect.bottom = get_water_level(self.rect.centerx)
left_y = get_water_level(self.rect.left)
right_y = get_water_level(self.rect.right)
angle = 180*math.atan2(left_y-right_y,self.image.get_width())/math.pi
debug("angle: "+str(int(angle)))
print(self.rect.left,self.rect.right)
self.rotate(angle)
def replicate(self):
if self.rect.left == 363:
return
self.rect.x += 1
def get_input(self):
self.replicate()
# keys = pygame.key.get_pressed()
# if keys[K_LEFT]:
# self.move.x = -self.velocity
# elif keys[K_RIGHT]:
# self.move.x = self.velocity
# else:
# self.move.x = 0
# if keys[K_UP]:
# self.move.y = -self.velocity
# elif keys[K_DOWN]:
# self.move.y = self.velocity
# else:
# self.move.y = 0
# self.rect.x += self.move.x
# self.rect.y += self.move.y
# if self.rect.left <= 0:
# self.rect.left = 0
# if self.rect.right >= SCREEN_WIDTH:
# self.rect.right = SCREEN_WIDTH
ship_sprite = pygame.sprite.GroupSingle()
ship_sprite.add(Ship())
while True:
screen.fill((200,210,255,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
ship_sprite.update()
ship_sprite.draw(screen)
draw_water(screen,SCREEN_HEIGHT-300)
clock.tick(60)
pygame.display.update()
It's about accuracy. You can improve the result by storing the water level with floating point precision:
def draw_water(surface, dy):
amplitude = 35
global watter_levels
for x in range(SCREEN_WIDTH):
y = math.sin(x * 0.01) * amplitude + dy
watter_levels[x] = y
pygame.draw.aaline(surface, BLUE, (x, round(y)), (x, round(y)))
Furthermore you have to get the left and right from the "unrotated" rectangle:
class Ship(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((80,80),pygame.SRCALPHA)
self.image.fill('red')
self.rect = self.image.get_rect(topleft = (0, 0))
self.copy_img = self.image.copy()
self.copy_rect = pygame.Rect(self.rect)
self.move = pygame.math.Vector2(0, 0)
self.velocity = 8
def rotate(self, angle):
self.image = pygame.transform.rotate(self.copy_img, round(angle))
self.rect = self.image.get_rect(center = (self.copy_rect.center))
def update(self):
self.get_input()
self.copy_rect.bottom = round(get_water_level(self.copy_rect.centerx))
left_y = get_water_level(self.copy_rect.left)
right_y = get_water_level(self.copy_rect.right)
angle = math.degrees(math.atan2(left_y-right_y, self.copy_img.get_width()))
debug("angle: " + str(round(angle)))
print(self.copy_rect.left, self.copy_rect.right)
self.rotate(angle)
def replicate(self):
if self.copy_rect.left == 363:
return
self.copy_rect.x += 1

how would i get the enemy to avoid the characters line at all costs while still trying to eliminate him

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:

Pygame NameError: free variable 'lost' referenced before assignment in enclosing scope

I have recently been working on a Space Invaders game in Pygame via Tech with Tim YouTube tutorial and have encounter a name error on line 111:
NameError: free variable 'lost' referenced before assignment in
enclosing scope.
Link to the tutorial https://www.youtube.com/watch?v=Q-__8Xw9KTM&feature=youtu.be&t=1112
Code:
import pygame
import os
import time
import random
import math
import sys
WIDTH, HEIGHT = 750, 750
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Space Invaders")
#Game Variables
pygame.font.init()
pygame.init()
#Ships
RED_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_red_small.png'))
GREEN_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_green_small.png'))
BLUE_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_blue_small.png'))
YELLOW_SPACE_SHIP = pygame.image.load(os.path.join('assets','pixel_ship_yellow.png'))
#Lasers
RED_LASER = pygame.image.load(os.path.join('assets','pixel_laser_red.png'))
BLUE_LASER = pygame.image.load(os.path.join('assets','pixel_laser_blue.png'))
YELLOW_LASER = pygame.image.load(os.path.join('assets','pixel_laser_yellow.png'))
GREEN_LASER = pygame.image.load(os.path.join('assets','pixel_laser_green.png'))
#Background
BG = pygame.transform.scale2x(pygame.image.load(os.path.join('assets','background-black.png')).convert_alpha())
class Ship:
def __init__(self, x, y, health=100):
self.x = x
self.y = y
self.health = health
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cool_down_counter = 0
def draw(self, window):
screen.blit(self.ship_img, (self.x, self.y))
def get_width(self):
return self.ship_img.get_width()
def get_height(self):
return self.ship_img.get_height()
class Player(Ship):
def __init__(self,x,y, health=100):
super().__init__(x, y, health)
self.ship_img = YELLOW_SPACE_SHIP
self.laser_img = YELLOW_LASER
self.mask = pygame.mask.from_surface(self.ship_img)
self.max_health = health
class Enemy(Ship):
COLOUR_MAP = {
"red": (RED_SPACE_SHIP, RED_LASER),
"green": (GREEN_SPACE_SHIP, GREEN_LASER),
"blue": (BLUE_SPACE_SHIP, BLUE_LASER),
}
def __init__(self, x, y, colour, health=100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOUR_MAP[colour]
self.mask = pygame.mask.from_surface(self.ship_img)
def move(self, vel):
self.y += vel
def main():
run = True
FPS = 60
level = 0
lives = 5
main_font = pygame.font.SysFont("comicsans", 50)
lost_font = pygame.font.SysFont("comicsans", 60)
enemies = []
wave_length = 5
enemy_vel = 1
player_vel = 5
player = Player(300, 650)
clock = pygame.time.Clock()
def redraw_window():
screen.blit(BG, (0,0))
lives_label = main_font.render(f'Lives: {lives}', 1, (255,255,255))
level_label= main_font.render(f'Level: {level}', 1, (255,255,255))
screen.blit(lives_label, (10,10))
screen.blit(level_label, (WIDTH - level_label.get_width() - 10, 10))
for enemy in enemies:
enemy.draw(screen)
player.draw(screen)
if lost:
lost_label = lost_font.render("You Lost!!", 1, (255,255,255))
screen.blit(lost_label, (WIDTH/2 - lost_label.get_width()/2, 350))
pygame.display.update()
while True:
clock.tick(FPS)
redraw_window()
if lives <= 0 or player.health <= 0:
lost = True
lost_count += 1
if len(enemies) == 0:
level += 1
wave_length += 5
for i in range(wave_length):
enemy = Enemy(random.randrange(50, WIDTH-100), random.randrange(-1500, -100), random.choice(["red", "blue", "green"]))
enemies.append(enemy)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.x - player_vel > 0:
player.x -= player_vel
if keys[pygame.K_RIGHT] and player.x + player_vel + player.get_width() < WIDTH:
player.x += player_vel
if keys[pygame.K_UP] and player.y - player_vel > 0:
player.y -= player_vel
if keys[pygame.K_DOWN] and player.y + player_vel + player.get_height() < HEIGHT:
player.y += player_vel
for enemy in enemies:
enemy.move(enemy_vel)
if enemy.y + enemy.get_height() > HEIGHT:
lives -= 1
enemies.remove(enemy)
main()
For lost and lost_count, you need to mark them as global so the method does not treat them as local variables:
In your code, replace this:
def main():
With this:
lost = False
lost_count = 0
def main():
# use global variables
global lost
global lost_count

Categories

Resources