Pygame program stopping/crashing without any error logged - python

As a beginner im trying to create flappy bird to learn the basics of python and pygame. But when i run this code and click the button that is created the code just stops and python becomes not responsive. Does any one know why?
import pygame
import math
import random
import pyautogui as pg
pygame.init()
screenWidth, screenHeight = pg.size()
gameDisplay = pygame.display.set_mode((screenWidth, screenHeight), pygame.FULLSCREEN)
pygame.display.set_caption("Flappy by Tallik")
time = pygame.time.Clock()
crashed = False
bg = (0, 0, 0)
textSize = 32
font = pygame.font.Font("freesansbold.ttf", textSize)
buttonClicked = [0, 0]
mainGame = False
titleScreen = True
settings = False
changeBird = False
dead = False
phase = 1
def buttonFunc(x, y, width, height, color1, color2, text, textColor, textColor2, buttonNum, dissaperance):
global buttonClicked
mouseClick = pygame.mouse.get_pressed()
mouseX, mouseY = pygame.mouse.get_pos()
buttonText = font.render(str(text), True, textColor)
buttonText2 = font.render(str(text), True, textColor2)
textX = x + 10
textY = y + (height//3)
buttonClicked = [0, buttonNum]
if mouseX >= x and mouseX <= x + width and mouseY >= y and mouseY <= y + height:
pygame.draw.rect(gameDisplay, (color2), (x, y, width, height))
gameDisplay.blit(buttonText2, (textX, textY))
if mouseClick[0] == True:
buttonClicked[0] = 1
if dissaperance == False:
pygame.draw.rect(gameDisplay, (color2), (x, y, width, height))
gameDisplay.blit(buttonText2, (textX, textY))
buttonClicked[0] = 1
else:
gameDisplay.fill(bg)
else:
pygame.draw.rect(gameDisplay, (color1), (x, y, width, height))
gameDisplay.blit(buttonText, (textX, textY))
class bird(object):
def __init__(self, birdVelMultiplier, skin, width, height):
self.birdVelMultiplier = birdVelMultiplier
self.skin = skin
self.grav = -1
self.height = height
self.width = width
self.y = screenHeight//2
self.x = screenWidth//30
self.vel = screenHeight//100
self.jumps = 0
self.minVel = -20
self.maxVel = 20
def jump(self):
if keys[pygame.K_SPACE] and self.jumps == 0:
self.vel = -15
self.jumps += 1
if keys[pygame.K_UP] and self.jumps == 0:
self.vel = -15
self.jumps += 1
if not keys[pygame.K_UP] and not keys[pygame.K_SPACE]:
self.jumps = 0
def drawToGame(self, gameDisplay):
if self.y + self.height <= screenHeight:
if self.y >= 0:
if self.vel < self.maxVel and self.vel > self.minVel:
self.vel -= self.grav
self.y += self.vel
else:
if abs(self.vel)/self.vel == 1:
self.vel = self.maxVel
self.vel -= self.grav
self.y += self.vel
else:
self.vel = self.minVel
self.vel -= self.grav
self.y += self.vel
else:
self.y = 0
self.vel = 1
else:
self.y = screenHeight//2
self.vel = 0
pygame.draw.rect(gameDisplay, (0, 0, 255), (self.x, self.y, self.width, self.height))
class obstacle(object):
def __init__(self, speedMultiplier, width):
self.speedMultiplier = speedMultiplier
self.width = width
self.ranHeight = random.randrange(50, screenHeight - (screenHeight//16)*4)
self.ranHeight2 = (screenHeight - self.ranHeight) - 150
def spawn(self):
print(1)
def drawToSurface(self, gameDisplay):
print(3)
bird1 = bird(1, 1, 60, 60)
pipe = obstacle(1, 130)
sB = bird1
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill(bg)
keys = pygame.key.get_pressed()
if buttonClicked == [1, 1]:
phase = 4
if phase == 1:
buttonFunc(screenWidth//2, screenHeight//4, screenWidth//18, screenHeight//20, (115, 115, 115), (85, 85, 85), "Start!", (0, 0, 0), (0, 0, 0), 1, True)
elif phase == 2:
print("?")
elif phase == 3:
print("??")
elif phase == 4:
while dead == False:
sB.jump()
sB.drawToGame(gameDisplay)
pygame.display.update()
time.tick(30)
pygame.quit()
quit()
To specify: after i have pressed the button created by buttFunc and the phase variable gets to 4 the program just stops and no eroor message is recieved. Also when closing the program the IDLE shell says the program is still running. I posted all the code because i dont know what made it break.

The program does not "crash", you just have an infinite loop. You do not need a loop that controls the game in the application loop. The application loop is continuously executed. Remove while dead == False::
while not crashed and not dead:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill(bg)
keys = pygame.key.get_pressed()
if buttonClicked == [1, 1]:
phase = 4
if phase == 1:
buttonFunc(screenWidth//2, screenHeight//4, screenWidth//18, screenHeight//20, (115, 115, 115), (85, 85, 85), "Start!", (0, 0, 0), (0, 0, 0), 1, True)
elif phase == 2:
print("?")
elif phase == 3:
print("??")
elif phase == 4:
sB.jump()
sB.drawToGame(gameDisplay)
pygame.display.update()
time.tick(30)

Related

distorted audio when playing a sound with pygame

I am trying to make a streetfighter style like game with circles and rectangles(too lazy to draw the art). However, when I play try to play a punching sound, it sounds like it was distorted. Same goes for the KO sound effect I play when a player dies.
here are the sounds
https://audio.nathanli1818.repl.co/
heres the code:
import pygame
pygame.init()
colors = {
"white": (255, 255, 255),
"black": (0, 0, 0),
"blue": (71, 52, 237),
"green": (53, 189, 71),
"brown": (79, 21, 21),
"red": (184, 39, 39),
"purple": (145, 27, 209)
}
class Fighter:
def __init__(self, win, x, y, win_width, win_height, color, healthx, healthy):
self.win = win
self.gravity = 0
self.win_width = win_width
self.win_height = win_height
self.color = color
self.health = 100
self.healthx = healthx
self.healthy = healthy
self.dir_ = "right"
self.x = x
self.y = y
self.base = pygame.Rect(0, 0, 70, 100)
self.base.centerx = self.x
self.base.centery = self.y
self.attack_ = pygame.Rect(0, 0, 20, 20)
self.healthbar = pygame.Rect(0, 0, self.health * 3, 20)
self.healthbar.center = (self.healthx, self.healthy)
self.background = pygame.Rect(0, 0, 300, 20)
self.background.center = self.healthbar.center
self.punch = pygame.mixer.Sound("sounds/attack.wav")
self.punch.set_volume(0.3)
self.KO = pygame.mixer.Sound("sounds/KO.mp3")
self.KO.set_volume(0.3)
def render(self):
pygame.draw.ellipse(self.win, self.color, self.base)
self.x = self.base.centerx
self.y = self.base.centery
def move(self, x, y):
self.base.centerx = x
self.base.centery = y
def fall(self):
self.base.y += self.gravity
self.gravity += 1
if self.base.bottom >= self.win_height - 50:
self.gravity = 0
self.base.bottom = self.win_height - 50
def draw_healthbar(self):
if self.health <= 0:
flag = 0
if not flag:
self.KO.play()
flag += 1
self.health = 100
self.healthbar.width = self.health * 3
pygame.draw.rect(self.win, colors["red"], self.background)
pygame.draw.rect(self.win, colors["green"], self.healthbar)
def attack(self, type_):
if type_ == "punch":
flag = 0
if self.dir_ == "right":
if not flag:
self.punch.play()
flag += 1
self.attack_.center = (self.base.topright[0] + 35, self.base.centery - 10)
pygame.draw.rect(self.win, self.color, self.attack_)
elif self.dir_ == "left":
if not flag:
self.punch.play()
flag += 1
self.attack_.center = (self.base.topleft[0] - 35, self.base.centery - 10)
pygame.draw.rect(self.win, self.color, self.attack_)
class Background:
def __init__(self, win, win_width, win_height):
self.win = win
self.win_width = win_width
self.win_height = win_height
self.sky = (self.win_width, self.win_height - 100)
self.ground = (self.win_width, 100)
self.platform = (220, 30)
self.platformTop = (220, 5)
self.platformBottom = (220, 5)
self.sky_base = pygame.Rect(0, 0, *self.sky)
self.ground_base = pygame.Rect(0, 0, *self.ground)
self.ground_base.bottom = self.win_height
self.platform_base = pygame.Rect(0, 0, *self.platform)
self.platform_base.center = (self.win_width / 2, self.win_height / 2 + 15)
self.platform_top = pygame.Rect(0, 0, *self.platformTop)
self.platform_top.center = (self.platform_base.centerx, self.platform_base.top + self.platformBottom[1])
self.platform_bottom = pygame.Rect(0, 0, *self.platformBottom)
self.platform_bottom.center = (self.platform_base.centerx, self.platform_base.bottom - self.platformBottom[1])
def load(self):
pygame.draw.rect(self.win, colors["blue"], self.sky_base)
pygame.draw.rect(self.win, colors["green"], self.ground_base)
pygame.draw.rect(self.win, colors["brown"], self.platform_base)
class Game:
def __init__(self):
self.width = 900
self.height = 500
self.running = True
self.attackFlag1 = 0
self.attackFlag2 = 0
self.fps = 60
self.win = pygame.display.set_mode((self.width, self.height))
self.clock = pygame.time.Clock()
self.fighter = Fighter(self.win, 200, 450, self.width, self.height, colors["red"], 200, 50)
self.fighter2 = Fighter(self.win, 700, 450, self.width, self.height, colors["purple"], 700, 50)
self.background = Background(self.win, self.width, self.height)
pygame.display.set_caption("street fighter")
def fighter_1(self, keys):
if keys[pygame.K_a]:
if self.fighter.base.left <= 0:
self.fighter.x += 10
self.fighter.dir_ = "left"
x = self.fighter.x
self.fighter.move(x - 10, self.fighter.y)
if keys[pygame.K_d]:
if self.fighter.base.right >= self.width:
self.fighter.x -= 10
self.fighter.dir_ = "right"
x = self.fighter.x
self.fighter.move(x + 10, self.fighter.y)
if keys[pygame.K_w] and self.fighter.base.bottom == 450:
self.fighter.gravity = -20
if keys[pygame.K_s] and self.fighter.base.bottom != 450:
self.fighter.gravity += 10
if self.background.platform_top.colliderect(self.fighter.base):
if keys[pygame.K_w]:
self.fighter.gravity = -20
else:
self.fighter.gravity = 0
self.fighter.base.bottom = self.background.platform_top.bottom
if self.background.platform_bottom.colliderect(self.fighter.base):
self.fighter.gravity += 5
if keys[pygame.K_e]:
self.fighter.attack("punch")
if self.fighter.base.colliderect(self.fighter2.attack_):
if self.attackFlag2 == 0:
self.fighter.health -= 5
self.attackFlag2 += 1
def fighter_2(self, keys):
if keys[pygame.K_LEFT]:
if self.fighter2.base.left <= 0:
self.fighter2.x += 10
self.fighter2.dir_ = "left"
x = self.fighter2.x
self.fighter2.move(x - 10, self.fighter2.y)
if keys[pygame.K_RIGHT]:
if self.fighter2.base.right >= self.width:
self.fighter2.x -= 10
self.fighter2.dir_ = "right"
x = self.fighter2.x
self.fighter2.move(x + 10, self.fighter2.y)
if keys[pygame.K_UP] and self.fighter2.base.bottom == 450:
self.fighter2.gravity = -20
if keys[pygame.K_DOWN] and self.fighter2.base.bottom != 450:
self.fighter2.gravity += 10
if self.background.platform_top.colliderect(self.fighter2.base):
if keys[pygame.K_UP]:
self.fighter2.gravity = -20
else:
self.fighter2.gravity = 0
self.fighter2.base.bottom = self.background.platform_top.bottom
if self.background.platform_bottom.colliderect(self.fighter2.base):
self.fighter2.gravity += 5
if self.fighter2.base.colliderect(self.fighter.attack_):
if self.attackFlag1 == 0:
self.fighter2.health -= 50
self.attackFlag1 += 1
if keys[pygame.K_RSHIFT]:
self.fighter2.attack("punch")
def run(self):
while self.running:
self.clock.tick(self.fps)
self.background.load()
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_e:
self.fighter.attack_.y = 1000
self.attackFlag1 = 0
if event.type == pygame.KEYUP:
if event.key == pygame.K_RSHIFT:
self.fighter2.attack_.y = 1000
self.attackFlag2 = 0
keys = pygame.key.get_pressed()
self.fighter_1(keys)
self.fighter.fall()
self.fighter.draw_healthbar()
self.fighter.render()
self.fighter_2(keys)
self.fighter2.fall()
self.fighter2.draw_healthbar()
self.fighter2.render()
pygame.display.update()
pygame.quit()
if __name__ == '__main__':
Game().run()
on python 3.8.8
Your problem is coming from the fact that the sound is being played multiple times on top of itself. This happens because every frame, you check if pygame.K_e is held, and then play the sound if it is. However, when you press a key, it's likely that you'll be holding the key for more than a single frame, so this leads to the sound being played multiple times on top of itself in quick succession.
One way to rectify the problem would be to play the sound using a pygame.mixer.Channel, which only allows one sound to be playing at any time:
# Get a reference to a pygame channel
self.channel = pygame.mixer.Channel(0)
# Load punch sound effect
self.punch = pygame.mixer.Sound("sounds/attack.wav")
# ...
# Play using channel
self.channel.play(self.punch)
That said, this probably isn't actually what you want - rather, you should probably instead be checking for a 'key release' event instead of just checking whether the E key is held in your fighter_* functions. This will put sufficient time between calls to attack to prevent the distortion you're hearing from occurring.

problems with pygame controller support

import pygame
import os
import random
import time
import json
from pygame import joystick
pygame.font.init()
pygame.init()
WIDTH, HEIGHT = 1920, 1000
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("space invaders")
WHITE = (255, 255, 255)
#load images
RED_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_red_small.png")), (125, 100))
GREEN_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_green_small.png")), (125, 100))
BLUE_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_blue_small.png")), (125, 100))
#player
YELLOW_SPACE_SHIP = pygame.transform.rotate(pygame.transform.scale(pygame.image.load(os.path.join("Assets", "spaceship_yellow.png")), (154, 121)), 180)
#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"))
GREEN_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_green.png"))
#player laser
YELLOW_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_yellow.png"))
#background
BG = pygame.transform.scale(pygame.image.load(
os.path.join('Assets', 'space.png')), (WIDTH, HEIGHT))
class Laser():
def __init__(self, x, y, img):
self.x = x
self.y = y
self.img = img
self.mask = pygame.mask.from_surface(self.img)
def draw(self, window):
window.blit(self.img, (self.x, self.y))
def move(self, vel):
self.y += vel
def off_screen(self, height):
return not(self.y <= height and self.y >= 0)
def collision(self, obj):
return collide(self, obj)
class Ship:
COOLDOWN = 30
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):
window.blit(self.ship_img, (self.x, self.y))
for laser in self.lasers:
laser.draw(window)
def move_lasers(self, vel, obj):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
elif laser.collision(obj):
obj.health -= 10
self.lasers.remove(laser)
def cooldown(self):
if self.cool_down_counter >= self.COOLDOWN:
self.cool_down_counter = 0
elif self.cool_down_counter > 0:
self.cool_down_counter += 1
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
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
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x + 26, self.y - 57, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def move_lasers(self, vel, objs):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
else:
for obj in objs:
if laser.collision(obj):
objs.remove(obj)
if laser in self.lasers:
self.lasers.remove(laser)
def draw(self, window):
super().draw(window)
self.healthbar(window)
def healthbar(self, window):
pygame.draw.rect(window, (255, 0, 0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width(), 10))
pygame.draw.rect(window, (0, 255, 0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width()* (self.health/self.max_health),10))
class Enemy(Ship):
COLOR_MAP = {
"red": (RED_SPACE_SHIP, RED_LASER),
"green": (GREEN_SPACE_SHIP, GREEN_LASER),
"blue": (BLUE_SPACE_SHIP, BLUE_LASER)
}
def __init__(self, x, y, color, health = 100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOR_MAP[color]
self.mask = pygame.mask.from_surface(self.ship_img)
def move(self, vel):
self.y += vel
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x - 3, self.y + 20, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def collide(obj1, obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
def main():
run = True
FPS = 60
level = 0
lives = 5
lost = False
lost_count = 0
main_font = pygame.font.SysFont("comicsans", 75)
lost_font = pygame.font.SysFont("comicsans", 500)
enemies = []
wave_length = 5
player_vel = 7
laser_vel = 7
enemy_vel = 1
player = Player(WIDTH // 2, 650)
clock = pygame.time.Clock()
def redraw_window():
WIN.blit(BG, (0,0))
#draw text
level_label = main_font.render(f"Level: {level}", 1, WHITE)
lives_label = main_font.render(f"Lives: {lives}", 1, WHITE)
WIN.blit(lives_label, (10, 10))
WIN.blit(level_label, (WIDTH - level_label.get_width() - 10, 10))
player.draw(WIN)
for enemy in enemies:
enemy.draw(WIN)
if lost:
lost_label = lost_font.render("You Lost!", 1, (WHITE))
WIN.blit(lost_label, (WIDTH/2 - lost_label.get_width()/2, HEIGHT / 2 - lost_label.get_height() / 2))
pygame.display.update()
joysticks = []
for i in range(pygame.joystick.get_count()):
joysticks.append(pygame.joystick.Joystick(i))
for joystick in joysticks:
pygame.joystick.init()
print(pygame.joystick.get_init())
with open(os.path.join("ps4_keys.json"), 'r+') as file:
button_keys = json.load(file)
# 0: Left analog horizonal, 1: left analog verticle, 2: right analog horizonal
# 3: right analog verticle, 4: left Triger, 5: Right Trigger
analog_keys = {0:0, 1:0, 2:0, 3:0, 4:-1, 5:-1}
while run:
clock.tick(FPS)
redraw_window()
if lives <= 0 or player.health <= 0:
lost = True
lost_count += 1
if lost:
if lost_count > FPS * 3:
run = False
else:
continue
if len(enemies) == 0:
level += 1
wave_length += 5
for i in range(wave_length):
enemy = Enemy(random.randrange(100, 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:
quit()
if event.type == pygame.JOYAXISMOTION:
analog_keys[event.axis] = event.value
print(analog_keys)
if abs(analog_keys[0]) > .4:
if analog_keys[0] < -.7:
player.x -= 7
else:
continue
if analog_keys[0] < .7:
player.x += 7
for enemy in enemies[:]:
enemy.move(enemy_vel)
enemy.move_lasers(laser_vel, player)
if random.randrange(0, 120) == 1:
enemy.shoot()
if collide(enemy, player):
player.health -= 10
enemies.remove(enemy)
elif enemy.y + enemy.get_height() > HEIGHT:
lives -= 1
enemies.remove(enemy)
player.move_lasers(-laser_vel, enemies)
def main_menu():
title_font = pygame.font.SysFont("comicsans", 150)
run = True
while run:
WIN.blit(BG, (0,0))
title_label = title_font.render("Click the mouse to begin...", 1, WHITE)
WIN.blit(title_label, (WIDTH / 2 - title_label.get_width() / 2, HEIGHT / 2 - title_label.get_height() / 2))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
main_menu()
^^^
so this is my code (here is the JSON file as well):
{
"x": 0,
"circle": 1,
"square": 2,
"triangle": 3,
"share": 4,
"PS": 5,
"options": 6,
"left_stick_click": 7,
"right_stick_click": 8,
"L1": 9,
"R1": 10,
"up_arrow": 11,
"down_arrow": 12,
"left_arrow": 13,
"right_arrow": 14,
"touchpad": 15
}
I'm trying to make the player be controlled by the controller left joystick and it returns no errors but my player does not move and it is printing true from the print(pygame.joystick.get_init()) and printing the joystick amounts from: print(analog_keys) but the player does not move. Any idea why?
Do not use the JOYAXISMOTION event. The event does not occur continuously, it only occurs once when the axis changes.
Use pygame.joystick.Joystick.get_axis to get the current position of the axis and move the player depending on the axis:
def main():
# [...]
while run:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if joysticks:
joystick = joysticks[0]
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
player.x += round(7 * axis_x)
if abs(axis_y) > 0.1:
player.y += round(7 * axis_y)
# [...]
Minimal example:
import pygame
from pygame.locals import *
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
x, y = window.get_rect().center
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
x = (x + round(7 * axis_x)) % window.get_width()
if abs(axis_y) > 0.1:
y = (y + round(7 * axis_y)) % window.get_height()
window.fill(0)
pygame.draw.circle(window, (255, 0, 0), (x, y), 10)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()

Why is the program not stopping after the main loop is stopped?

I am trying to make my own version of Snake. I'm trying to set it up so that when a wall is hit, the main while loop stops running by setting run = False. This works when I close the program with the quit button, but for some reason is not working when a collision is detected. Any help?
Note: I did try moving the gameOver() function out of the updateScreen() function and putting it separately at the end of the loop, but that didn't work either.
import pygame
import time
import os
pygame.init()
width, height = (500, 500)
win = pygame.display.set_mode((width, height))
pygame.display.set_caption("Snake")
win.fill((0, 0, 0))
# Snake Class
class Snake:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.len = []
self.xvel = 0
self.yvel = 0
def draw(self, window):
win.fill((0, 0, 0))
pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.height))
def move(self):
self.x += self.xvel
self.y += self.yvel
def checkCol(self):
# Check if edge is hit
if self.x <= 0 or self.x + self.width >= width:
return True
elif self.y <= 0 or self.y + self.height >= height:
return True
else:
return False
sn = Snake(100, 100, 15, 15, (0, 255, 0))
# MAIN LOOP
def main():
run = True
FPS = 15
clock = pygame.time.Clock()
fontStyle = pygame.font.SysFont(None, 50)
def updateScreen():
sn.move()
sn.draw(win)
gameOver()
pygame.display.update()
def gameOver():
if sn.checkCol():
run = False
print("check")
def message(msg, color):
m = fontStyle.render(msg, True, color)
win.blit(m, [width / 2, height / 2])
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
sn.yvel = -10
sn.xvel = 0
if keys[pygame.K_s]:
sn.yvel = 10
sn.xvel = 0
if keys[pygame.K_a]:
sn.xvel = -10
sn.yvel = 0
if keys[pygame.K_d]:
sn.xvel = 10
sn.yvel = 0
updateScreen()
message("Game Over!", (255, 0, 0))
main()
use breakwhen your condition is met
if keys[pygame.K_d]:
sn.xvel = 10
sn.yvel = 0
break

how can I make the enemy move towards the player and predict its path in pygame

I am making a pygame game and I want my enemies follow the player and predict its path. I don't just want to reduce the distance between the player and the enemy. The number of enemies will be according to the level, every 3 levels will add a new enemy. I'm attaching my whole code along with a screenshot showing that my enemies are currently just moving in a straight line.
import pygame
import random
pygame.font.init()
width = 900
height = 600
screen = pygame.display.set_mode([width, height])
walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'),
pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'),
pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'),
pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'),
pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
char = pygame.image.load('standing.png')
bomb_pic = pygame.transform.scale(pygame.image.load('bomb.png'), (20,20))
bomb_explosion = pygame.transform.scale(pygame.image.load('explosion1.png'), (40,40))
pics = [bomb_pic, bomb_explosion]
# char_rect = char.get_rect()
enemy_Left = [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')]
x = 50
y = 50
width = 40
height = 60
vel = 5
isJump = False
jumpCount = 10
left = False
right = False
down = False
up = False
walkCount = 0
enemy_vel = 2
enemy_list = []
shop = pygame.transform.scale(pygame.image.load("shop.png"), (60, 60))
clock = pygame.time.Clock()
FPS = 60
font = pygame.font.Font('freesansbold.ttf', 32)
items_font = pygame.font.Font('freesansbold.ttf', 16)
bombs =[]
explosions = []
bag = {'bomb': 0}
print(bag["bomb"])
class Button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 20)
text = font.render(self.text, 1, (0, 0, 0))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def shop_run():
shop_bomb = Button((0, 200, 0), 820, 150, 60, 20, text="Bomb_b")
bright_green = (0, 255, 0)
green = (0, 200, 0)
shop_bomb.draw(screen)
def redrawGameWindow():
global walkCount
global font
global bag
global items_font
global enemy_list
global pics
current_time = pygame.time.get_ticks()
screen.fill([166, 166, 166])
for five_enemies in range(6):
random_enemy_location_y = random.randrange(100, 400)
random_enemy_location_x = random.randrange(800, 840)
enemy_list.append([random_enemy_location_x, random_enemy_location_y])
for enemies in range(6):
screen.blit(enemy_Left[enemies], enemy_list[enemies])
enemy_list[enemies][0] -= 0.3
pygame.draw.rect(screen, (0, 0, 0), (800, 0, 100, 600))
if x + char.get_width() < 60 and y + char.get_height() < 60:
shop_run()
screen.blit(shop, (0, 0))
screen.blit(font.render("Menu", True, (255,255,255)),(805, 10))
screen.blit(items_font.render("Bombs: "+ str(bag["bomb"]), True, (255, 255, 255)), (805, 550))
# screen.blit(bomb_explosion, (450, 300))
if walkCount + 1 >= 27:
walkCount = 0
if left:
screen.blit(walkLeft[walkCount // 3], (x, y))
walkCount += 1
elif right:
screen.blit(walkRight[walkCount // 3], (x, y))
walkCount += 1
elif down:
screen.blit(char, (x, y))
walkcount = 0
elif up:
screen.blit(char, (x, y))
walkcount = 0
else:
screen.blit(char, (x, y))
walkCount = 0
for i in reversed(range(len(bombs))):
pos, end_time = bombs[i]
if current_time > end_time:
bombs.pop(i)
# current_time_2 = pygame.time.get_ticks()
# for j in reversed(range(len(explosions))):
# pos2, end_time_2 = explosions[j]
# if current_time_2 > end_time_2:
# explosions.pop(j)
# else:
# screen.blit(bomb_explosion, pos2)
else:
screen.blit(pics[0], pos)
for j in reversed(range(len(explosions))):
pos, end_time_2 = explosions[j]
if current_time > end_time_2:
explosions.pop(j)
elif current_time > (end_time_2 - 2000):
screen.blit(pics[1], pos)
else:
continue
pygame.display.update()
def main():
run = True
# shopper()
pygame.display.set_caption("bomb-mania")
global x
global y
global width
global height
global vel
global isJump
global jumpCount
global left
global right
global down
global up
global walkCount
global bomb_pic
global font
global bombs
global explosions
while run:
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if x + char.get_width() < 60 and y + char.get_height() < 60:
buy = pygame.key.get_pressed()
if buy[pygame.K_b]:
bag["bomb"] += 1
print(bag["bomb"])
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and bag["bomb"] >= 1:
current_time_2 = pygame.time.get_ticks()
pos = x + char.get_width()/2, y + char.get_height() - 20
pos2 = ((x + char.get_width()/2)-10), y + char.get_height() - 30
end_time = current_time + 3000 # 3000 milliseconds = 3 seconds
end_time_2 = current_time_2 + 5000
explosions.append((pos2, end_time_2))
bombs.append((pos, end_time))
bag["bomb"] -= 1
redrawGameWindow()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel - 15:
x -= vel
left = True
right = False
down = False
up = False
elif keys[pygame.K_RIGHT] and x < 800 - vel - width:
x += vel
left = False
right = True
down = False
up = False
elif keys[pygame.K_DOWN] and y < 600 - height:
y += vel
left = False
right = False
down = True
up = False
elif keys[pygame.K_UP] and y > vel - 15:
y -= vel
left = False
right = False
down = False
up = True
else:
left = False
right = False
down = False
up = False
walkCount = 0
clock.tick(FPS)
pygame.display.flip()
main()
You'll need some vector math for this, so I recommend to restructure your code and learn how to use Sprites; you can find an example here.
To find an answer to your question ("predict the path"), you could google for intercept vector or pursuit vector. That should yield some results, such as How to calculate the vector of an interception? or Calculating Intercepting Vector.
For example, I translated the last answer of the second question and copy/pasted it into one of my answers, since a) I'm too lazy to write everything again and b) there's a single point of code I have to change to implement the intercept logic (the EnemyController class).
import pygame
import random
import math
from pygame import Vector2
SPRITE_SHEET = None
GREEN_SHIP = pygame.Rect(0, 292, 32, 32)
RED_SHIP = pygame.Rect(0, 324, 32, 32)
BLUE_SHIP = pygame.Rect(0, 356, 32, 32)
YELLOW_SHIP = pygame.Rect(0, 388, 32, 32)
class EnemyController:
def __init__(self, target):
self.direction = Vector2(1, 0)
self.target = target
def update(self, sprite, events, dt):
k = self.target.vel.magnitude() / sprite.speed;
distance_to_target = (sprite.pos - self.target.pos).magnitude()
b_hat = self.target.vel
c_hat = sprite.pos - self.target.pos
CAB = b_hat.angle_to(c_hat)
ABC = math.asin(math.sin(CAB) * k)
ACB = math.pi - (CAB + ABC)
j = distance_to_target / math.sin(ACB)
a = j * math.sin(CAB)
b = j * math.sin(ABC)
time_to_collision = b / self.target.vel.magnitude() if self.target.vel.magnitude() > 0 else 1
collision_pos = self.target.pos + (self.target.vel * time_to_collision)
v = sprite.pos - collision_pos
if v.length() > 0:
sprite.direction = -v.normalize()
if v.length() <= 10:
sprite.pos = pygame.Vector2(400, 100)
class PlayerController:
movement = {
pygame.K_UP: Vector2( 0, -1),
pygame.K_DOWN: Vector2( 0, 1),
pygame.K_LEFT: Vector2(-1, 0),
pygame.K_RIGHT: Vector2( 1, 0)
}
def update(self, sprite, events, dt):
pressed = pygame.key.get_pressed()
v = Vector2(0, 0)
for key in PlayerController.movement:
if pressed[key]:
v += PlayerController.movement[key]
sprite.direction = v
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
sprite.groups()[0].add(Explosion(sprite.pos))
class Animation:
def __init__(self, frames, speed, sprite):
self.sprite = sprite
self.speed = speed
self.ticks = 0
self.frames = frames
self.running = 0
self.start()
def cycle_func(self, iterable):
saved = []
for element in iterable:
yield element
saved.append(element)
if hasattr(self.sprite, 'on_animation_end'):
self.sprite.on_animation_end()
while saved:
for element in saved:
yield element
if hasattr(self.sprite, 'on_animation_end'):
self.sprite.on_animation_end()
def stop(self):
self.running = 0
if self.idle_image:
self.sprite.image = self.idle_image
def start(self):
if not self.running:
self.running = 1
self.cycle = self.cycle_func(self.frames)
self.sprite.image = next(self.cycle)
def update(self, dt):
self.ticks += dt
if self.ticks >= self.speed:
self.ticks = self.ticks % self.speed
if self.running:
self.sprite.image = next(self.cycle)
class AnimatedSprite(pygame.sprite.Sprite):
def __init__(self, pos, frames, speed):
super().__init__()
self.animation = Animation(frames, speed, self)
self.rect = self.image.get_rect(center=pos)
self.pos = Vector2(pos)
self.animation.start()
def update(self, events, dt):
self.animation.update(dt)
class Explosion(AnimatedSprite):
frames = None
def __init__(self, pos):
if not Explosion.frames:
Explosion.frames = parse_sprite_sheet(SPRITE_SHEET, pygame.Rect(0, 890, 64, 64), 6, 4)
super().__init__(pos, Explosion.frames, 50)
def on_animation_end(self):
self.kill()
class DirectionalImageSprite(pygame.sprite.Sprite):
directions = [(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1),(0,0)]
def __init__(self, pos, directional_images_rect):
super().__init__()
images = parse_sprite_sheet(SPRITE_SHEET, directional_images_rect, 9, 1)
self.images = { x: img for (x, img) in zip(DirectionalImageSprite.directions, images) }
self.direction = Vector2(0, 0)
self.image = self.images[(self.direction.x, self.direction.y)]
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
class SpaceShip(DirectionalImageSprite):
def __init__(self, pos, controller, directional_images_rect):
super().__init__(pos, directional_images_rect)
self.controller = controller
self.speed = 2
self.vel = pygame.Vector2(0, 0)
def update(self, events, dt):
super().update(events, dt)
if self.controller:
self.controller.update(self, events, dt)
self.vel = Vector2(0, 0)
if (self.direction.x, self.direction.y) in self.images:
self.image = self.images[(self.direction.x, self.direction.y)]
if self.direction.length():
self.vel = self.direction.normalize() * self.speed
self.pos += self.vel
self.rect.center = int(self.pos[0]), int(self.pos[1])
def parse_sprite_sheet(sheet, start_rect, frames_in_row, lines):
frames = []
rect = start_rect.copy()
for _ in range(lines):
for _ in range(frames_in_row):
frame = sheet.subsurface(rect)
frames.append(frame)
rect.move_ip(rect.width, 0)
rect.move_ip(0, rect.height)
rect.x = start_rect.x
return frames
def main():
screen = pygame.display.set_mode((800, 600))
global SPRITE_SHEET
SPRITE_SHEET = pygame.image.load("ipLRR.png").convert_alpha()
clock = pygame.time.Clock()
dt = 0
player = SpaceShip((400, 300), PlayerController(), YELLOW_SHIP)
enemy = SpaceShip((400, 100), EnemyController(player), GREEN_SHIP)
enemy.speed = 4
all_sprites = pygame.sprite.Group(
player,
enemy
)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
all_sprites.update(events, dt)
screen.fill((0, 0, 0))
all_sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(120)
main()

Command screen.fill() doesnt reset objects on the screen

Commands screen.fill() or screen.blit(), doesnt refresh my object on the screen. My player's object or biedronka's objects stays in the same position even then my program reset the whole screen. My game runs perfectly and i have only problem with this refreshing screen. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
…
import os
import random
import pygame
pygame.init()
pygame.display.set_caption('Biedronkaaa')
screen = pygame.display.set_mode((500, 500))
background_menu = pygame.image.load("tlo-menu.png")
background_game = pygame.image.load("tlo-gra.png")
def write(text, x, y, size):
cz = pygame.font.SysFont("Arial", size)
text = cz.render(text, 1, (204, 0, 0))
screen.blit(text, (x, y))
class Biedronki():
def __init__(self):
self.x = random.randint(10, 100)
self.y = random.randint(10, 100)
self.vx = random.randint(-4, 4)
self.vy = random.randint(-4, 4)
self.graphic = pygame.image.load(os.path.join('biedrona.png'))
self.size = 24
def draw(self):
screen.blit(self.graphic, (self.x, self.y))
def moves(self):
self.x += self.vx
self.y += self.vy
if self.x <= 0 or self.x >= 500 - self.size:
self.vx = self.vx * -1
if self.y <= 0 or self.y >= 500 - self.size:
self.vy = self.vy * -1
def collision(self, player):
x_srodek = self.x + self.size / 2
y_srodek = self.y + self.size / 2
if player.collidepoint(x_srodek, y_srodek):
return True
else:
return False
enemies = []
for i in range(20):
enemies.append(Biedronki())
# player cords
x_player = 450
y_player = 450
v = 20
player = pygame.Rect(x_player, y_player, 32, 32)
graphic_player = pygame.image.load(os.path.join('gracz.png'))
game = "menu"
points = 0
# mainloop
while True:
screen.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
if y_player - v > 0:
y_player -= v
if event.key == pygame.K_DOWN:
if y_player + v < 500 - 32:
y_player += v
if event.key == pygame.K_RIGHT:
if x_player + v < 500 - 32:
x_player += v
if event.key == pygame.K_LEFT:
if x_player - v > 0:
x_player -= v
if event.key == pygame.K_SPACE:
if game != "contest":
screen.blit(graphic_player, (x_player, y_player))
game = 'contest'
points = 0
player = pygame.Rect(x_player, y_player, 32, 32)
if game == "menu":
screen.blit(background_menu, (0, 0))
write("Press space to run", 140, 250, 20)
elif game == "contest":
points += 1
screen.blit(background_game, (0, 0))
for biedroneczka in enemies:
biedroneczka.moves()
biedroneczka.draw()
if biedroneczka.collision(player):
game = "end"
screen.blit(graphic_player, (x_player, y_player))
write(str(points), 30, 30, 20)
pygame.time.wait(15)
elif game == "end":
write("Niestety przegrywasz", 50, 290, 20)
write("Nacisnij spację, aby zagrać jeszcze raz", 50, 350, 20)
write("Twój wynik to: " + str(points), 50, 320, 20)
pygame.display.update()
Finally fixed it by added to the space key reseting objects and player position
if event.key == pygame.K_SPACE:
if game != "contest":
screen.blit(graphic_player, (x_player, y_player))
game = 'contest'
points = 0
x_player = 450
y_player = 450
for _ in range(20):
enemies.pop(0)
for _ in range(20):
enemies.append(Biedronki())
You need to do pygame.display.update() at the end of the main loop.

Categories

Resources