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.
Related
I am making a pong game with pygame. The check_win() function checks if one side has won and for some reason, it the point counter keeps going up on the right side when detected, but not on the left side. So when it detects the ball hit the left wall, it works as intended, but when it hits the right side, it keeps incrementing the point.
here the code
import pygame
pygame.init()
class Pad:
def __init__(self, x, win, score_x):
self.win = win
self.x = x
self.y = 250
self.speed = 5
self.score = 0
self.score_x = score_x
self.color = (255, 255, 255)
self.font = pygame.font.Font("font.ttf", 25)
self.base = pygame.Rect(0, 0, 30, 100)
self.base.center = (self.x, self.y)
self.collide_base = pygame.Rect(0, 0, 10, self.base.height)
def render(self, type_):
if type_ == 0:
self.collide_base.right = self.base.right
if type_ == 1:
self.collide_base.left = self.base.left
self.collide_base.y = self.base.y
pygame.draw.rect(self.win, self.color, self.base)
def move(self, keys, type_):
if type_ == 0:
if keys[pygame.K_w]:
self.base.centery -= self.speed
if keys[pygame.K_s]:
self.base.centery += self.speed
elif type_ == 1:
if keys[pygame.K_UP]:
self.base.centery -= self.speed
if keys[pygame.K_DOWN]:
self.base.centery += self.speed
def collide_edge(self):
if self.base.bottom >= self.win.get_height():
self.base.y -= self.speed
if self.base.top <= 0:
self.base.y += self.speed
def draw_score(self):
score = self.font.render(str(self.score), False, self.color)
self.win.blit(score, (self.score_x, self.win.get_height() - (self.win.get_height() - 20)))
class Ball:
def __init__(self, win):
self.win = win
self.color = (255, 255, 255)
self.leftWin = False
self.rightWin = False
self.speedx = 5
self.speedy = 5
self.x = self.win.get_width() / 2
self.y = self.win.get_height() / 2
self.base = pygame.Rect(0, 0, 20, 20)
self.base.center = (self.x, self.y)
def render(self):
self.base.center = (self.x, self.y)
pygame.draw.ellipse(self.win, self.color, self.base)
def move(self):
if self.base.bottom >= self.win.get_height():
self.speedy *= -1
if self.base.right >= self.win.get_width():
self.rightWin = True
if self.base.top <= 0:
self.speedy *= -1
if self.base.left <= 0:
self.leftWin = True
self.x += self.speedx
self.y += self.speedy
class Game:
def __init__(self):
self.width = 750
self.height = 500
self.running = True
self.color = (0, 0, 0)
self.fps = 60
self.win = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE)
self.clock = pygame.time.Clock()
self.pad1 = Pad(50, self.win, self.width / 2 + 25)
self.pad2 = Pad(700, self.win, self.width / 2 - 40)
self.ball = Ball(self.win)
self.line = []
def draw_line(self):
for x in range(20):
if x % 2 == 0:
rect = pygame.Rect(0, 0, 20, 40)
rect.center = (self.width / 2, x * 40)
self.line.append(rect)
for rect in self.line:
pygame.draw.rect(self.win, (255, 255, 255), rect)
def check_pad(self):
if self.ball.base.colliderect(self.pad1.base):
self.ball.x += 10
self.ball.speedx *= -1
if self.ball.base.colliderect(self.pad2.base):
self.ball.x -= 10
self.ball.speedx *= -1
def check_win(self):
if self.ball.leftWin:
self.ball.base.center = (self.width / 2, self.height / 2)
self.ball.leftWin = False
self.ball.speedx = 5
self.ball.speedy = 5
self.pad1.score += 1
if self.ball.rightWin:
self.ball.base.center = (self.width / 2, self.height / 2)
self.ball.rightWin = False
self.ball.speedx = 5
self.ball.speedy = 5
self.pad2.score += 1
def run(self):
while self.running:
self.clock.tick(self.fps)
self.win.fill(self.color)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
keys = pygame.key.get_pressed()
self.draw_line()
self.check_pad()
self.check_win()
self.ball.render()
self.ball.move()
self.pad1.render(0)
self.pad1.move(keys, 0)
self.pad1.collide_edge()
self.pad1.draw_score()
self.pad2.render(1)
self.pad2.move(keys, 1)
self.pad2.collide_edge()
self.pad2.draw_score()
pygame.display.update()
pygame.quit()
if __name__ == '__main__':
Game().run()
You have forgotten to update self.ball.x and self.ball.y. Note, self.base.center is set from self.x, self.y in the function render.
class Game:
# [...]
def check_win(self):
if self.ball.leftWin:
self.ball.base.center = (self.width / 2, self.height / 2)
self.ball.x, self.ball.y = self.ball.base.center # <---
self.ball.leftWin = False
self.ball.speedx = 5
self.ball.speedy = 5
self.pad1.score += 1
if self.ball.rightWin:
self.ball.base.center = (self.width / 2, self.height / 2)
self.ball.x, self.ball.y = self.ball.base.center # <---
self.ball.rightWin = False
self.ball.speedx = 5
self.ball.speedy = 5
self.pad2.score += 1
I'm trying my best to make a dino game replica and I'm stuck at increasing the speed of the obstacles that comes towards the character. The speed of 5 just seems to be the magic number but when I change it to six the rock doesn't get detected and ultimately doesn't spawn the next rock.
I don't know why.
there are no errors except for when the rock doesn't get spawned and I get an index out of range:
Traceback (most recent call last):
File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 109, in <module>
main()
File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 102, in main
if game.collide():
File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 71, in collide
olh = self.obstacles[0].hitBox[0]
IndexError: list index out of range
here is what I got:
main.py
import pygame
import math
import random
from player import Player
from objects import Obstacle
WIDTH, HEIGHT = 700, 400
class Game:
def __init__(self, playerSprite=None, obSprite= None):
self.playerSprite = playerSprite
self.obSprite = obSprite
self.player = Player(50, 50, 50, 50, 8, (0, 0, 0), None)
self.obstacle = Obstacle(1, (800, 350, 50, 50), 5, None)
self.obstacles = [self.obstacle]
self.spawnGap = -100
self.speed = 5
def spawn(self):
for obstacle in self.obstacles:
#if its at the specific spot than spawn a rock
if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap:
#get shape of rock
type = round(random.randint(0, 3))
rect = (700, 350, 50, 50)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (50, 50))
if type == 1:
rect = (700, 350, 25, 50)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (25, 50))
if type == 2 :
rect = (700, 375, 50, 25)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (50, 25))
if type == 3:
rect = (700, 350, 75, 50)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (75, 50))
#increase the place in which the rock spawns
self.spawnGap += 10
#create a new obstacle and append it to the obstacle array
self.obstacle = Obstacle(type, rect, 5, None)
self.obstacles.append(self.obstacle)
#delete obstacle when its at the end
if obstacle.pos.x < -obstacle.w:
index = self.obstacles.index(obstacle)
del self.obstacles[index]
def draw(self, win):
self.spawn()
self.hitBoxes()
self.player.draw(window)
for obstacle in self.obstacles:
obstacle.draw(window)
def collide(self):
plh = self.player.hitBox[0]
prh = self.player.hitBox[0] + self.player.hitBox[2]
pth = self.player.hitBox[1]
pbh = self.player.hitBox[1] + self.player.hitBox[3]
olh = self.obstacles[0].hitBox[0]
orh = self.obstacles[0].hitBox[0] + self.obstacles[0].hitBox[2]
oth = self.obstacles[0].hitBox[1]
obh = self.obstacles[0].hitBox[1] + self.obstacles[0].hitBox[3]
if prh >= olh and plh <= orh:
if pbh >= oth:
return True
else:
return False
def hitBoxes(self):
key = pygame.key.get_pressed()
if key[pygame.K_a]:
self.player.showHitBoxes = True
for obstacle in self.obstacles:
obstacle.showHitBoxes = True
if key[pygame.K_s]:
self.player.showHitBoxes = False
for obstacle in self.obstacles:
obstacle.showHitBoxes = False
def main():
done = False
pressed = False
game = Game()
time = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
window.fill((255, 255, 255))
game.draw(window)
if game.collide():
done = True
pygame.draw.line(window, (255, 0, 0), (WIDTH/2, 0), (WIDTH/2, HEIGHT))
pygame.draw.line(window, (0, 0, 255), (WIDTH/2 + game.spawnGap, 0), (WIDTH/2 + game.spawnGap, HEIGHT))
pygame.display.update()
time.tick(60)
pygame.quit()
main()
objects.py
import pygame
from pygame.math import Vector2
class Obstacle:
def __init__(self, type, rect, speed, rockSprite=None):
self.obSprite = rockSprite
self.xSpawn = rect[0]
self.ySpawn = rect[1]
self.pos = Vector2(self.xSpawn, self.ySpawn)
self.w = rect[2]
self.h = rect[3]
self.type = type
self.speed = speed
self.hitBox = ()
self.showHitBoxes = False
def draw(self, win):
self.hitBox = (self.pos.x, self.pos.y, self.w, self.h)
if self.showHitBoxes:
pygame.draw.rect(win, (255, 0, 0), self.hitBox, 2)
if self.obSprite != None:
win.blit(self.obSprite, self.pos)
else:
pygame.draw.rect(win, (0, 0, 0), (self.pos.x, self.pos.y, self.w, self.h))
self.update()
def update(self):
self.pos.x -= self.speed
player.py
import pygame
from pygame.math import Vector2
WIDTH, HEIGHT = 700, 400
class Player:
def __init__(self, x, y, w, h, jumpVel, color, sprite=None):
self.playerSprite = sprite
self.x = x
self.y = y
self.w = w
self.h = h
self.pos = Vector2(self.x, self.y)
self.color = color
self.gravity = 0.3
self.vel = self.gravity
self.jVel = jumpVel
self.touching_surface = False
self.canJump = True
self.jumping = False
self.hitBox = ()
self.showHitBoxes = False
def draw(self, win):
self.hitBox = (self.pos.x + 0, self.pos.y + 10, 50, 30)
if self.showHitBoxes:
pygame.draw.rect(win, (255, 0, 0), self.hitBox, 2)
if self.playerSprite != None:
win.blit(self.playerSprite, self.pos)
else:
pygame.draw.rect(win, (255, 0, 0), (self.pos.x, self.pos.y, 50, 50))
self.update()
def update(self):
mouse = pygame.mouse.get_pressed()
if self.pos.y + self.h >= HEIGHT:
self.touching_surface = True
self.vel = 0
self.pos.y = HEIGHT - self.h
else:
self.touching_surface = False
self.vel += self.gravity
if self.pos.x <= 0:
self.pos.x = 0
elif self.pos.x + self.w >= WIDTH:
self.pos.x = WIDTH - self.w
if self.touching_surface:
self.canJump = True
else:
self.canJump = False
if mouse[0] == 1 and self.canJump and not self.jumping:
self.jumping = True
self.canJump = False
self.vel = -self.jVel
if mouse[0] == 0:
self.jumping = False
self.pos.y += self.vel
and I suspect the issue is in the spawn function in the Game class in main.py
I've been trying to work on this for a couple days now and I still cannot solve my issue.
The problem is the condition
if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap:
# [...]
This condition is only True when the obstacle is exactly at a certain position. If the speed changes, the obstacle does not exactly hit the position.
Test on a small range instead of a position. e.g.:
right = obstacle.pos.x + obstacle.w
target = WIDTH/2 + self.spawnGap
if traget <= right < traget + speed:
# [...]
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)
As the title suggests if I change scenes with a new player position it gets stuck because its in the loop I understand why its happening I just don't know how to make it move with its new position, I have tried player.x,player.y = new x and new y but it goes to the location and gets stuck, there is a player class and also a scene class I think thats where the problem lies, I am still a beginner and im surprised I got this far, help is appreciated thank you!
import sys
from pygame.locals import *
import time
pygame.init()
display_w = 1024
display_h = 768
world = pygame.Surface((2500, 2500)) # 2550, 2500
window = pygame.display.set_mode((display_w, display_h), HWSURFACE | DOUBLEBUF | RESIZABLE)
pygame.display.set_icon(pygame.image.load("game_icon.png").convert_alpha())
pygame.display.set_caption("Work in progress")
clock = pygame.time.Clock()
font = pygame.font.SysFont("PressStart2P.ttf", 30, False)
tiles = pygame.image.load("Tile_set.png")
tiles1 = pygame.image.load("Tile_set1.png")
background = [
pygame.image.load("background.png").convert(),
pygame.image.load("background1.png").convert(),
]
black = 0, 0, 0
blue = 0, 0, 255
class Player(object):
"""The controllable player in game"""
def __init__(self, x, y, width, height, speed):
self.x, self.y = x, y
self.width, self.height = width, height
self.image = pygame.image.load("sprite_sheet.png").convert_alpha()
self.speed = speed
self.timer = 0
self.frames = 1
self.animation_direction = 2
self.rect = self.image.subsurface(0, 0, 28, 45)
self.interact = pygame.Rect(self.x, self.y, 10, 10)
self.shadow = tiles.subsurface(363, 468, 31, 19)
self.pos_x, self.pos_y = 105, 77
def animation(self):
x_coord = 50 * self.frames
y_coord = 50 * self.animation_direction
self.character = self.image.subsurface(x_coord, y_coord, self.width, self.height).convert_alpha()
self.timer += 0
if self.timer >= 10:
self.timer = 0
self.frames += 1
if self.frames >= 9:
self.frames = 1
def movement(self,):
self.keys = pygame.key.get_pressed()
if self.keys[pygame.K_LEFT] or self.keys[pygame.K_a]:
self.x -= self.speed
self.pos_x += player.speed
self.animation_direction = 1
self.timer += 2
if self.keys[pygame.K_RIGHT] or self.keys[pygame.K_d]:
self.x += self.speed
self.pos_x -= player.speed
self.animation_direction = 3
self.timer += 2
if self.keys[pygame.K_UP] or self.keys[pygame.K_w]:
self.y -= self.speed
self.pos_y += player.speed
self.animation_direction = 0
self.timer += 2
if self.keys[pygame.K_DOWN] or self.keys[pygame.K_s]:
self.y += self.speed
self.pos_y -= player.speed
self.animation_direction = 2
self.timer += 2
def interaction(self):
if self.keys[pygame.K_e] or self.keys[pygame.K_x]:
if self.animation_direction == 1:
self.interact = pygame.Rect(self.x - 15, self.y, 30, 40)
if self.animation_direction == 3:
self.interact = pygame.Rect(self.x + 15, self.y, 30, 40)
if self.animation_direction == 0:
self.interact = pygame.Rect(self.x, self.y - 15, 30, 40)
if self.animation_direction == 2:
self.interact = pygame.Rect(self.x, self.y + 15, 30, 40)
def draw(self):
world.blit(self.shadow, (self.x, self.y + 35))
world.blit(self.character, (self.x, self.y))
player = Player(375, 275, 30, 50, 3.5)
class Door:
def __init__(self, x, y, width, height):
self.x, self.y = x, y
self.width, self.height = width, height
self.image = tiles.subsurface(pygame.Rect(328, 432, width, height))
# self.rect = self.image.subsurface(0, 0, 32, 32)
def draw(self):
world.blit(self.image, (self.x, self.y))
door = Door(368, 35, 32, 64)
class Tree:
def __init__(self, x, y, width, height):
self.x, self.y, self.width, self.height = x, y, width, height
self.image = (pygame.transform.scale(tiles.subsurface(pygame.Rect(99, 147, self.width, self.height)), (62, 82)))
def amount(self):
for y in range(0, 1650, 50):
world.blit(self.image, (0, y))
for y in range(0, 1650, 50):
world.blit(self.image, (45, y))
for y in range(0, 1650, 50):
world.blit(self.image, (90, y))
def draw(self):
world.blit(self.image, (self.x, self.y))
tree = Tree(230, 35, 48, 58)
door_list = [
pygame.Rect(door.x, door.y, door.width, door.height),
]
sprite_list = [
pygame.Rect(tree.x, tree.y, tree.width, tree.height),
pygame.Rect(0, 0, 1000, 1),
pygame.Rect(0, 0, 1, 1000),
pygame.Rect(800, 0, 1, 1000),
pygame.Rect(0, 600, 1000, 1),
pygame.Rect(0, 0, 800, 64)
# grass.rect.get_rect(topleft=(grass.x, grass.y)),
]
tree_list = [pygame.Rect(tree.x, tree.y, tree.width, tree.height),
]
class Scene:
def __init__(self):
self.sceneNum = 1
def Scene_1(self):
window.fill(black)
window.blit(world, (player.pos_x, player.pos_y))
world.blit(background[0], (0, 0))
player_pos = (player.x, player.y)
world_pos = (player.pos_x, player.pos_y)
player.movement()
player.interaction()
player_rect = pygame.Rect(player.x, player.y, player.width, player.height)
for tile in sprite_list:
if player_rect.colliderect(tile):
(player.x, player.y) = player_pos
(player.pos_x, player.pos_y) = world_pos
print("hit!")
player.frames = 0
for doors in door_list:
if player.interact.colliderect(doors):
print("Interaction is working")
scenes.sceneNum += 1
for trees in tree_list:
if player.interact.colliderect(trees):
print("Tree!")
door.draw()
# Sprites.grass.draw()
tree.draw()
player.animation()
player.draw()
def Scene_2(self):
window.fill(black)
window.blit(world, (player.pos_x, player.pos_y))
world.blit(background[1], (0,0))
player_pos = (player.x, player.y)
world_pos = (player.pos_x, player.pos_y)
player.x, player.y = 718, 1223
player.pos_x, player.pos_y = -238,-871
player.movement()
player.interaction()
player_rect = pygame.Rect(player.x, player.y, player.width, player.height)
for trees in tree_list:
if player_rect.colliderect(trees):
(player.x, player.y) = player_pos
(player.pos_x, player.pos_y) = world_pos
print("Tree!")
player.frames = 0
tree.amount()
player.animation()
player.draw()
def Draw_Scene(self):
if self.sceneNum == 1:
self.Scene_1()
elif self.sceneNum == 2:
self.Scene_2()
scenes = Scene()
running = True
while running:
start_time = time.time()
scenes.Draw_Scene()
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == VIDEORESIZE:
screen = pygame.display.set_mode((event.w, event.h), RESIZABLE)
elif event.type == pygame.KEYUP:
if player.keys:
player.frames = 0
if event.key == pygame.K_e or pygame.K_x:
player.interact = pygame.Rect(-10, -10, 5, 5)
print(player.x,player.y,player.pos_y,player.pos_x)
(clock.tick(60))
text = font.render("FPS " + str(int(1.0 / (time.time() - start_time))), True, blue)
window.blit(text, (30, 20))
pygame.display.flip()
I don't know if this would help, but divide the loop in to other loops, and when you want to change scenes, break the first one
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()