how do i move obstacles faster for dino game - python

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:
# [...]

Related

How to rotate an image to the cursor? [duplicate]

This question already has answers here:
How to rotate an image(player) to the mouse direction?
(2 answers)
Still Having Problems With Rotating Cannon's Properly Towards The Player Pygame
(1 answer)
How do I make image rotate with mouse python [duplicate]
(1 answer)
How do I make my player rotate towards mouse position?
(1 answer)
Closed 4 months ago.
I have a shotgun attached to my player. I want this shotgun to point at the cursor. Ive tried searching this up on google, but each time i tried the answer my shotgun kinda moved and rotated. How can i make it so it only rotates?
My code:
import pygame
import numpy
import math
import random
screen = pygame.display.set_mode((1280, 720))
class Player:
def __init__(self, pos: tuple, gravity: tuple, sprite: pygame.Surface, maxVel, minVel, maxVelY):
self.pos = pos
self.velocity = (0,0)
self.gravity = gravity
self.sprite = sprite
self.maxVel = maxVel
self.minVel = minVel
self.maxVelY = maxVelY
def addForce(self, force: tuple):
self.velocity = numpy.add(self.velocity, force)
def resetVelocity(self):
self.velocity = (0,0)
def update(self):
global scene, highscore, score, playerrect
self.velocity = numpy.add(self.velocity, (0, self.gravity/10))
if self.velocity[0] > self.maxVel:
self.velocity[0] = self.maxVel
elif self.velocity[0] < self.minVel:
self.velocity[0] = self.minVel
if self.velocity[1] > self.maxVelY:
self.velocity[1] = self.maxVelY
self.pos = numpy.subtract(self.pos, self.velocity)
if self.pos[1] < -50:
self.pos[1] += 770
elif self.pos[1] > 770:
scene = menu
self.pos = (self.sprite.get_rect(center=(1280/2, 720/2))[0], self.sprite.get_rect(center=(1280/2, 720/2))[1])
if highscore < score:
highscore = score
if self.pos[0] < 0:
self.pos[0] += 1330
if self.pos[0] > 1280:
self.pos[0] -= 1330
def draw(self): # Here is the problem
global shotgun
screen.blit(self.sprite, self.pos)
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - self.pos[0], mouse_y - self.pos[1]
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
rotshotgun = pygame.transform.rotate(shotgun, int(angle)) # Im thinking this doesnt work correctly
screen.blit(rotshotgun, (self.pos[0]+50, self.pos[1]+50))
class Particle:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
def draw(self):
pygame.draw.rect(screen, self.color, (self.x, self.y, 10, 10))
player = pygame.image.load("other/player.png").convert_alpha()
player = pygame.transform.scale(player, (100, 100))
player = Player((590, 310), -9.81, player, 10, -10, 20)
clock = pygame.time.Clock()
new_velocity = (0,0)
game = 0
menu = 1
scene = menu
shotgun = pygame.image.load("other/shotgun.png").convert_alpha()
shotgun = pygame.transform.scale(shotgun, (83, 66))
pygame.font.init()
font = pygame.font.Font("font/AlfaSlabOne-Regular.ttf", 150)
font2 = pygame.font.Font("font/PoorStory-Regular.ttf", 50)
font3 = pygame.font.SysFont("Arial", 350)
font4 = pygame.font.Font("font/Nunito-Black.ttf", 75)
titel = font.render("ShootShot", True, (0, 255, 0))
clicktext = font2.render("Click to start!", True, (0,0,0))
scoretext = font3.render("0", True, (0,0,0))
with open("other/highscore.txt", "r") as f:
highscore = f.read()
highscore.replace("\n", "")
highscore = int(highscore)
highscoretext = font4.render(f"Highscore: {highscore}", True, (0,0,0))
coins = []
for x in range(6):
coins.append(pygame.transform.scale(pygame.image.load(f"coin/coin{x+1}.png"), (50, 50)))
number = 0
particles = []
colors = [(255, 0, 0),
(0, 0, 255),
(255, 255, 0),
(255, 0, 255),
(0, 255, 255),
(255, 125, 0),
(125, 255, 0),
(0, 255, 125),
(0, 125, 255),
(125, 0, 255)]
r = True
scoretext.set_alpha(75)
scoretext = scoretext.convert_alpha()
coinLocation = (random.randint(0, 1280), random.randint(0, 720))
titlerect = titel.get_rect(center=(1280/2, 150))
scorerect = scoretext.get_rect(center=(1280/2, 720/2))
highscorerect = highscoretext.get_rect(center=(1280/2, 50))
number2 = 0
currentCoin = 0
coin = pygame.Rect(coinLocation[0], coinLocation[1], 50, 50)
score = 0
while r:
clock.tick(45)
screen.fill((255,255,255))
mousePos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
r = False
if event.type == pygame.MOUSEBUTTONDOWN:
if scene == game:
new_velocity = numpy.subtract(player.pos, mousePos)*-1
player.resetVelocity()
player.addForce(new_velocity/10)
else:
scene = game
score = 0
new_velocity = numpy.subtract(player.pos, mousePos) * -1
player.resetVelocity()
player.addForce(new_velocity / 10)
screen.blit(scoretext, scorerect)
player.draw()
if scene == game:
player.update()
screen.blit(coins[currentCoin], coinLocation)
number2 += 0.15
currentCoin = math.ceil(number2)
if currentCoin > 5:
currentCoin = 0
number2 = -1
playerrect = pygame.Rect(player.pos[0], player.pos[1], 50, 50)
if coin.colliderect(playerrect):
score += 1
scoretext = font3.render(str(score), True, (0, 0, 0))
scoretext.set_alpha(75)
coinLocation = (random.randint(0, 1280), random.randint(0, 720))
coin = pygame.Rect(coinLocation[0], coinLocation[1], 50, 50)
elif scene == menu:
highscoretext = font4.render(f"Highscore: {highscore}", True, (0, 0, 0))
scoretext = font3.render("0", True, (0, 0, 0))
scoretext = scoretext.convert_alpha()
scoretext.set_alpha(75)
score = 0
screen.blit(titel, titlerect)
number += 0.1
clickTextAddY = math.cos(number)*30
screen.blit(clicktext, (525, 600-clickTextAddY))
particles.append(Particle(random.randint(250, 1075), random.randint(100, 200), random.choice(colors)))
for particle in particles:
particle.draw()
if len(particles) > 70:
particles.pop(random.randint(0, len(particles)-1))
screen.blit(highscoretext, highscorerect)
pygame.display.flip()
with open("other/highscore.txt", "w") as f:
f.write(str(highscore))
pygame.quit()
I hope anyone can help me with this.
As i already said, i have tried searcing it up. The shotgun pointed to my mouse, but it also moved.

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.

How do I make the collisions work in this side scroller?

import pygame #importing the pygame library
pygame.init()
all_sprites = pygame.sprite.Group()
obstacle_group = pygame.sprite.Group()
player_group = pygame.sprite.Group()
defines the sprite groups we'll be calling on later
####Variables
width, height = 626, 375
fontObj = pygame.font.Font('Alsina Ultrajada.TTF', 16)
pygame.display.set_caption("side scroller")
screen = pygame.display.set_mode((width, height))
bg = pygame.image.load("achtergrondPixel.png")
gameOver = pygame.image.load("gameOver.png")
R_char = pygame.transform.scale(pygame.image.load("character.png"), (100, 100))
L_char = pygame.transform.flip((R_char), True, False)
char = R_char
jade = (55, 255, 20)
red = (255, 0, 0)
hitbox = (150, 200, 100, 100)
here we are making our player class, first we are initializing our player
class Player(pygame.sprite.Sprite): #making a class for our character so we can easily call the variables
def __init__(self, x, y, width, height, pos):
global player_group
super().__init__(player_group, all_sprites)
self.x = x
self.y = y
self.width = width
self.height = height
self.charFlip = False
self.isJump = False
self.jumpCount = 10
self.isFalling = False
self.fallCount = int(1)
self.pos = pos
self.rect = pygame.Rect((self.x - self.pos + 21),(self.y + 20), (self.width - 33), (self.height- 33))
self.add(player_group)
here we are making the function that draws our character, we also have a jump system and an falling system
def draw(self, screen):
screen.blit(char, (self.x, self.y))
if self.isFalling:
if self.y <= 200 and self.x < 488 and self.x > 573: #hier moet var worden aangemaakt voor als hij op platform staat
self.y -= (self.fallCount**2) * 0.1 * -1
self.fallCount += 1
else:
self.isFalling = False
self.fallCount = 1
if self.isJump:
if self.jumpCount >= 0:
neg = 1
if self.jumpCount == 0:
self.isFalling = True
self.y -= (self.jumpCount**2) * .25 * neg
self.jumpCount -= 1
else:
self.isJump = False
self.jumpCount = 10
self.hitbox = pygame.Rect((self.x - self.pos + 21),(self.y + 20), (self.width - 33), (self.height- 33))
if pygame.sprite.spritecollideany(self, obstacle_group):
print("collide")
Here we are making the green rectangle that is our obstacle in the game, first initializing it and that drawing it to the screen in the color jade
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, pos):
global obstacle_group
super().__init__(obstacle_group, all_sprites)
self.pos = pos
self.x = x
self.y = y
self.width = width
self.height = height
self.rect = pygame.Rect((self.x - self.pos),self.y, self.width, self.height)
#self.add(obstacle_group)
def draw(self, screen, pos):
pygame.draw.rect(screen, jade, pygame.Rect((self.x - self.pos), self.y, self.width, self.height))
self.hitbox = pygame.Rect((self.x - self.pos),self.y, self.width, self.height)
pygame.draw.rect(screen, red, self.hitbox, 1)
pos is the variable we use to scroll the background
pos = 0
here we are making the player character and the only obstacle in the side scroller
obstacle2 = Obstacle(300, 200, 100, 20, pos)
nkdMonkey = Player(150, 200, 100, 100, pos)
FPS = 60
run = True
clock = pygame.time.Clock()
this is our main drawing function that we call on in our main while loop
def draw_window():
screen.blit(bg, ((-1 * pos), 0))
textSurfaceObj = fontObj.render((str(pos)), False, (240,240,240, 255))
screen.blit(textSurfaceObj, (40,40))
obstacle2.draw(screen, pos)
nkdMonkey.draw(screen)
#pygame.draw.rect(screen, red, nkdMonkey.hitbox, 1)
#for if you want to see the hitbox of the player which is used for collisions
pygame.display.update()
this is our while loop
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#all_sprites = pygame.sprite.Group()
#obstacle_group = pygame.sprite.Group()
#player_group = pygame.sprite.Group()
keys_pressed = pygame.key.get_pressed()
#Checks if the front and and back button is pressed and if so, changes pos.
if keys_pressed[pygame.K_d] and pos < 1874:
pos += 2
char = R_char
if keys_pressed[pygame.K_d] and keys_pressed[
pygame.K_LSHIFT] and pos < 2000:
pos += 5
char = R_char
if keys_pressed[pygame.K_a] and pos > 0:
pos -= 2
char = L_char
if keys_pressed[pygame.K_a] and keys_pressed[pygame.K_LSHIFT] and pos > 0:
pos -= 5
char = L_char
if keys_pressed[pygame.K_w] and nkdMonkey.isJump == False:
nkdMonkey.isJump = True
if nkdMonkey.y > 200 and nkdMonkey.x > 488 and nkdMonkey.x < 573:
nkdMonkey.y -= 1
#if nkdMonkey.x > 488 and nkdMonkey.x < 573:
#nkdMonkey.isFalling = True
if pos > 1980:
run = False
this is the point we can't figure out, we want it to print collide when the two sprites are colliding
if pygame.sprite.spritecollideany(nkdMonkey, obstacle_group):
print("collide")
all_sprites.update()
draw_window()
Here we made a simple end screen
screen.fill(jade)
screen.blit(pygame.image.load("character.png"), (0, 70))
text = fontObj.render('You win!!', True, (0,0,0, 255))
textRect = text.get_rect()
score = fontObj.render(str(pos), True, red)
screen.blit(score, (40,40))
textRect.center = (width // 2, (height // 2 + 20))
screen.blit(text, textRect)
pygame.display.flip()
pygame.sprite.spritecollideany attributes of the pygame.sprite.Sprite objects to detect a collision. Therefore you have to update the location of the rectangle by the position of the player:
nkdMonkey.rect.topleft = (nkdMonkey.x - nkdMonkey.pos), nkdMonkey.y)
if pygame.sprite.spritecollideany(nkdMonkey, obstacle_group):
print("collide")

Pygame, Changing scenes with new player x,y makes the player not move because its in a loop, how do I fix this?

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

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()

Categories

Resources