pygame platformer collision causes weird spacing issue - python

I am working on a platformer and have a weird issue with the collision. So far, I have a player and some platforms and the collision does work but the horizontal collision leaves some space between the player and the level and I don't understand why.
Here is the code for the player:
import pygame
from pygame.math import Vector2
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32,64))
self.image.fill('red')
self.rect = self.image.get_rect(center = (350,250))
self.direction = Vector2(0,0)
self.speed = 8
self.gravity = 0.8
self.jump_speed = -16
self.on_ground = False
def get_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]: self.direction.x = 1
elif keys[pygame.K_LEFT]: self.direction.x = -1
else: self.direction.x = 0
if keys[pygame.K_SPACE] and self.on_ground:
self.jump()
self.on_ground = False
def jump(self):
self.direction.y = self.jump_speed
def apply_gravity(self):
self.direction.y += self.gravity
def apply_movement(self):
# horizontal movement
self.rect.x += self.direction.x * self.speed
# vertical movement
self.rect.y += self.direction.y
def update(self):
# movement methods
self.get_input()
self.apply_gravity()
self.apply_movement()
and here the code for the level
import pygame, sys
from player import Player
basic = [
'0000000000000',
'0000000000000',
'0010011000010',
'0111000010110',
'0111000110111',
'1111111111111',
'1111111111111',]
class Level:
def __init__(self):
self.player = pygame.sprite.GroupSingle(Player())
self.tiles = pygame.sprite.Group()
self.setup_level()
def setup_level(self):
for row_index, row in enumerate(basic):
for col_index,cell in enumerate(row):
if cell == '1':
x = col_index * 100
y = row_index * 100
self.tiles.add(Tile((x,y)))
def player_collision(self):
player = self.player.sprite
player_size = player.rect.size
# vertical collision check
future_y = player.rect.top + player.direction.y
future_player_y = pygame.Rect((player.rect.left,future_y),player_size)
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(future_player_y):
if player.direction.y >= 0:
player.on_ground = True
player.rect.bottom = sprite.rect.top
player.direction.y = 0
else:
player.rect.top = sprite.rect.bottom
player.direction.y = 0
# horizontal collision check
future_x = self.player.sprite.rect.left + (self.player.sprite.direction.x * self.player.sprite.speed)
future_player_x = pygame.Rect((future_x,self.player.sprite.rect.top),player_size)
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(future_player_x):
if player.direction.x < 0:
player.rect.left = sprite.rect.right
player.rect.right += player.speed
elif player.direction.x > 0:
player.rect.right = sprite.rect.left
player.rect.right -= player.speed
def run(self):
self.tiles.draw(screen)
self.player.update()
self.player_collision()
self.player.draw(screen)
class Tile(pygame.sprite.Sprite):
def __init__(self,pos):
super().__init__()
self.image = pygame.Surface((100,100))
self.image.fill('green')
self.rect = self.image.get_rect(topleft = pos)
if __name__ == '__main__':
pygame.init()
screen = pygame.display.set_mode((1000,640))
clock = pygame.time.Clock()
debug_font = pygame.font.Font(None,40)
level = Level()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('black')
level.run()
pygame.display.update()
clock.tick(60)
I am using future_player_y because I had some earlier problems with animation frames and detecting whether the player was on the ground or not. I applied the same logic to the horizontal axis but I guess it wouldn't be strictly needed there.
The result looks like this and the collisions do work (kinda) but the red rectangle cannot move any closer to the tile to its left.
Just for clarity, here is a zoomed in look at the area I am talking about:

You need to separate the movement in the x and y direction.
Remove the apply_movement method:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# movement methods
self.get_input()
self.apply_gravity()
# self.apply_movement() <--- DELETE
Apply the movement separately before the collision test:
class Level:
# [...]
def player_collision(self):
player = self.player.sprite
# vertical collision check
player.rect.y += player.direction.y
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(player.rect):
if player.direction.y >= 0:
player.on_ground = True
player.rect.bottom = sprite.rect.top
player.direction.y = 0
else:
player.rect.top = sprite.rect.bottom
player.direction.y = 0
# horizontal collision check
player.rect.x += player.direction.x * player.speed
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(player.rect):
if player.direction.x < 0:
player.rect.left = sprite.rect.right
elif player.direction.x > 0:
player.rect.right = sprite.rect.left

Related

Player does not stop at bottom edge of screen when using gravity in pygame tutorial

I am trying to follow this tutorial https://opensource.com/article/19/11/simulate-gravity-python to allow my sprite to go to the bottom edge of the screen and nothing more. But following the tutorial code to the point where it says:
... Make your gravity function look like this:
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty
even though the player doesn't stop at the bottom edge of the screen when using gravity.
I tried to add one more ty as suggested in the tutorial where it says
... An easy fix is to make your player sprite bounce higher by adding another -ty to its new Y position after it hits the bottom of the game world:
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty-ty
so that my code looks like this:
import pygame
import sys
import os
'''
Objects
'''
class Enemy(pygame.sprite.Sprite):
'''
Spawn an enemy
'''
def __init__(self, x, y, img):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('images', img))
#self.image.convert_alpha()
#self.image.set_colorkey(ALPHA)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.counter = 0
def move(self):
'''
enemy movement
'''
distance = 20
speed = 8
if self.counter >= 0 and self.counter <= distance:
self.rect.x += speed
elif self.counter >= distance and self.counter <= distance * 2:
self.rect.x -= speed
else:
self.counter = 0
self.counter += 1
class Level:
def __init__(self):
self.enemy_list = pygame.sprite.Group() # create enemy group
def bad_1(self, lvl, eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
self.enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return self.enemy_list
def bad_2(self, lvl, eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'spr.png') # spawn enemy
self.enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return self.enemy_list
class Player(pygame.sprite.Sprite):
'''
Spawn a player
'''
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty-ty-ty-ty-ty-ty
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.health = 10
self.frame = 0
self.images = []
for i in range(1, 5):
img = pygame.image.load(
os.path.join('images', 'hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(ALPHA)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
def control(self, x, y):
'''
control player movement
'''
self.movex += x
self.movey += y
def update(self):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame // ani]
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[(self.frame // ani)]
'''
Setup
'''
worldx = 560
worldy = 420
fps = 40 # frame rate
ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
BLUE = (25, 25, 200)
BLACK = (23, 23, 23)
WHITE = (254, 254, 254)
ALPHA = (0, 255, 0)
world = pygame.display.set_mode([worldx, worldy])
backdrop = pygame.image.load(os.path.join('images', 'stage.png')).convert()
backdropbox = world.get_rect()
player = Player() # spawn player
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
l = Level()
eloc = [200,20]
enemy_list = l.bad_1(1, eloc)
eloc = [100,10]
enemy_list = l.bad_2(1, eloc)
'''
Main loop
'''
while main == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps, 0)
if event.key == pygame.K_UP or event.key == ord('w'):
print('jump')
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps, 0)
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
# world.fill(BLACK)
world.blit(backdrop, backdropbox)
player.gravity() # check gravity
player.update()
player_list.draw(world) #refresh player position
enemy_list.draw(world)
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
but the sprite doesn't stop at the bottom edge of the screen (again) and I get the following error when the code breaks by itself on execution:
Traceback (most recent call last):
File "game.py", line 188, in <module>
player.gravity() # check gravity
File "game.py", line 72, in gravity
self.rect.y = worldy-ty-ty-ty-ty-ty-ty
NameError: name 'ty' is not defined
If I try to decrease or increase (depending on how much bigger or smaller) the screen size where it says:
worldx = 560
worldy = 420
I get the following error:
Traceback (most recent call last):
File "game.py", line 188, in <module>
player.gravity() # check gravity
File "game.py", line 72, in gravity
self.rect.y = worldy-ty
NameError: name 'ty' is not defined
I have encountered this question Pygame Gravity Script but comparing I could not understand where the code failure arises.
The original images are from here https://opengameart.org/sites/default/files/opp2_sprites.zip but I have separated in imgur for easy explanation:
content folder images:
For enemy (sprit) is yeti.png: https://imgur.com/GNNcU6z
For background is stage.png: https://imgur.com/YyiEJ0q
and the image of the player:
spr.png: https://imgur.com/1fYXa7Y
In original code on https://opensource.com/article/19/11/simulate-gravity-python is
ty = 64 #tile size
but you don't have it in your code - and it gives your error.
But you could use rect.bottom and then you may not need ty
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.bottom > worldy and self.movey >= 0: # <-- uses bottom
self.movey = 0
self.rect.bottom = worldy # <-- uses bottom
BTW: If you will add plaforms to game then you can create platform at the bottom (below bottom border) and then it should stop player.
BTW: To stop on platform it would need
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
for p in plat_hit_list:
#self.health -= 1
#print(self.health)
self.rect.bottom = p.rect.top
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
for g in ground_hit_list:
#self.health -= 1
#print(self.health)
self.rect.bottom = g.rect.top
It would be good also to reduce gravity speed because in very big speed it may jump below platform without checking collision with platform.
def gravity(self):
self.movey += 3.2 # how fast player falls
#print(self.rect.bottom, worldy)
# reduce speed so it will not jump out platfrom
if self.movey >= 15:
self.movey = 6
if self.rect.bottom > worldy and self.movey >= 0:
self.movey = 0
self.rect.bottom = worldy
#print(self.rect.bottom, worldy)

Space Invaders movement is acting really weird [duplicate]

I've created a Space Invaders clone in Python using the PyGame modules but I'm running into some difficulty getting them to move down together when reaching the edge of the game screen.
How would I make it so when the aliens reach the edge of the game screen they all simultaneously change direction and drop down a level?
import pygame
import random
class spaceInvader(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load("spaceInvader.png")
self.x = 200
self.y = 390
self.shots = []
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_RIGHT]:
self.x+=dist
elif key[pygame.K_LEFT]:
self.x-=dist
def draw(self, surface):
surface.blit(self.image,(self.x,self.y))
for s in self.shots:
s.draw(screen)
class Alien(pygame.sprite.Sprite):
def __init__(self,x,y,direction,alienType):
pygame.sprite.Sprite.__init__(self)
self.AlienType = alienType
self.Direction = direction
if alienType == 1:
alienImage = pygame.image.load("alien1.png")
self.Speed = 1
self.Score = 5
if alienType == 2:
alienImage = pygame.image.load("alien2.png")
self.Score = 15
self.Speed = 1
if alienType == 3:
alienImage = pygame.image.load("alien3.png")
self.Score = 10
self.Speed = 1
if alienType == 4:
alienImage = pygame.image.load("alien4.png")
self.Score = 20
self.Speed = 1
if alienType == 5:
alienImage = pygame.image.load("alien5.png")
self.Score = 25
self.Speed = 1
self.image = pygame.Surface([26, 50])
self.image.set_colorkey(black)
self.image.blit(alienImage,(0,0))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def moveAliens(self):
if self.Direction == "right":
self.rect.x += self.Speed
if self.Direction == "left":
self.rect.x -= self.Speed
pygame.init()
pygame.mouse.set_visible(False)
screen = pygame.display.set_mode([400,400])
allAliens = pygame.sprite.Group()
spaceInvader = spaceInvader()
pygame.display.set_caption("Space Attack")
background_image = pygame.image.load("Galaxy.png").convert()
pygame.mouse.set_visible(True)
done = False
clock = pygame.time.Clock()
black =( 0, 0, 0)
white =( 255,255,255)
red = (255, 0, 0)
score = 0
enemies = []
#For X coords
spawnPositions = [50,100,150,200,250,300,350]
yCoord = 10
for n in range(5):
for i in range(len(spawnPositions)):
xCoord = spawnPositions[i]
alienType = random.randint(1,5)
alien = Alien(xCoord, yCoord,"right", alienType)
allAliens.add(alien)
yCoord = yCoord + 15
loop = 0
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
for alien in (allAliens.sprites()):
if alien.rect.x < 0:
alien.rect.y = alien.rect.y + 15
alien.Direction = "right"
if alien.rect.x > 395:
alien.rect.y = alien.rect.y + 15
alien.Direction = "left"
loop =+1
for alien in (allAliens.sprites()):
alien.moveAliens()
spaceInvader.handle_keys()
screen.blit(background_image,[0,0])
spaceInvader.draw(screen)
allAliens.draw(screen)
pygame.display.flip()
clock.tick(20)
pygame.quit()
Thanks.
Your problem lies here:
for alien in (allAliens.sprites()):
if alien.rect.x < 0:
alien.rect.y = alien.rect.y + 15
alien.Direction = "right"
if alien.rect.x > 395:
alien.rect.y = alien.rect.y + 15
alien.Direction = "left"
loop =+1
I assume the aliens are currently individually dropping down?
You need to change this so that when one alien triggers these if statements, all alien's y and direction are appropriately set, not just the one hitting the side.
# Space Invader
import turtle
import os
import random
import math
# Set up the screen.
wn = turtle.Screen()
wn.setup(700,700)
wn.bgcolor("lightblue")
wn.title("Space Invaders")
# Draw border
border = turtle.Turtle()
border.speed("fastest")
border.color("white")
border.penup()
border.setposition(-300,-270)
border.pendown()
border.pensize(3)
for side in range(4):
border.forward(560)
border.left(90)
border.hideturtle()
# Set the score to 0.
score = 0
# Draw the score.
score_pen = turtle.Turtle()
score_pen.speed("fastest")
score_pen.color("white")
score_pen.penup()
score_pen.setposition(-290,260)
scorestring = "Score: %s" %score
score_pen.write(scorestring, False, align="left", font = ("Arial",14,"normal"))
score_pen.hideturtle()
# Create the player turtle
player = turtle.Turtle()
player.color("black")
player.speed("fastest")
player.shape("triangle")
player.penup()
player.setposition(0,-265)
player.setheading(90)
playerspeed = 15
# Create enemy
#enemy = turtle.Turtle()
#enemy.shape("circle")
#enemy.color("red")
#enemy.penup()
#enemy.speed(2)
#enemy.setposition(-200,250)
#enemyspeed = 2
# Choose number of enemies.
number_of_enemies = 5
# Create an empty list of enemies.
enemies = []
# Add enemies to list.
for i in range(number_of_enemies):
# Create enemy
enemies.append(turtle.Turtle())
for enemy in enemies:
enemy.shape("circle")
enemy.color("red")
enemy.penup()
enemy.speed(2)
x = random.randint(-200,200)
y = random.randint(100,250)
enemy.setposition(x,y)
enemyspeed = 2
# Create the player's bullet.
bullet = turtle.Turtle()
bullet.color("yellow")
bullet.shape("triangle")
bullet.penup()
bullet.speed("fastest")
bullet.setheading(90)
bullet.shapesize(.5,.5)
bullet.hideturtle()
bulletspeed = 20
# Define bullet state
# ready - ready to fire
# fire - bullet is firing
bulletstate = "ready"
# Move the player left and right.
def move_left():
x = player.xcor()
x -= playerspeed
if x < -280:
x = -280
player.setx(x)
def move_right():
x = player.xcor()
x += playerspeed
if x >250:
x = 250
player.setx(x)
def fire_bullet():
# Declare the bulletstate as a global if it needs changed.
global bulletstate
if bulletstate == "ready":
bulletstate = "fire"
# Move the bullet just above the player.
x = player.xcor()
y = player.ycor() + 10
bullet.setposition(x,y)
bullet.showturtle()
def isCollision(t1,t2):
dx = t1.xcor() - t2.xcor()
dy = t1.ycor()- t2.ycor()
distance = dx**2 + dy**2
distance1 = math.sqrt(distance)
if distance1 < 15:
return True
else:
return False
# Create keyboard bindings
wn.onkey(move_left, "Left")
wn.onkey(move_right,"Right")
wn.onkey(fire_bullet,"space")
wn.listen()
# Main game loop
while True:
for enemy in enemies:
# Move enemy
x = enemy.xcor()
x += enemyspeed
enemy.setx(x)
# Move the enemy back and down
if enemy.xcor()>250:
# Move all the enemies down
for e in enemies:
y = e.ycor()
y -= 40
e.sety(y)
# Change the enemy direction
enemyspeed *= -1
if enemy.xcor()< -280:
# Move all the enemies down
for e in enemies:
y = e.ycor()
y -= 40
e.sety(y)
# change the enemy direction
enemyspeed *= -1
# Check for the collision between bullet and the enemy.
if isCollision(bullet ,enemy):
# Reset the bullet
bullet.hideturtle()
bulletstate = "ready"
bullet.setposition(0,-400)
# Reset the enemy
x = random.randint(-200,200)
y = random.randint(100,250)
enemy.setposition(x,y)
# Update the score.
score += 1
scorestring = "Score: %s" %score
score_pen.clear()
score_pen.write(scorestring, False, align = "left",font = ("Arial",14, "normal"))
if isCollision(player,enemy):
player.hideturtle()
enemy.hideturtle()
print("Game Over")
break
# Move the bullet.
if bulletstate == "fire":
y = bullet.ycor()
y += bulletspeed
bullet.sety(y)
# Check to see if the bullet has gone to the top.
if bullet.ycor() > 275:
bullet.hideturtle()
bulletstate = "ready"
turtle.mainloop()

Pygame Collision with variable speed

it would be really amazing if you could help us with this problem . We're currently working on a game project at school and we want to create a Bomberman-like labyrinth game.
The problem is the following:
the position and the speed of the bombs depend on the mouse which is tracked using float values but is getting way too high if you click far away so it can skip collision detection because it just goes too fast at times. Now there are some other weird errors when you line up the bomb with the bottom of a block and some other collisions.
Is the method colliderect the error ? Do we need to check for each site or are there any better solutions to this problem. We also thought about just using 4 directions but even that didn't work out properly.
I really hope somebody out there can help.
class Block( pygame.sprite.Sprite):
def __init__(self,color = SCHWARZ, width = 40, height = 40, movable = 0):
super().__init__()
self.width = width
self.height = height
self.color = color
self.movable = movable
self.image = pygame.Surface((width,height))
self.image.fill(color)
self.properties()
def properties(self):
self.rect = self.image.get_rect()
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
def set_pos(self, x, y):
self.rect.x = x
self.rect.y = y
def render(self,window):
pygame.draw.rect(window,self.color,(self.rect.x,self.rect.y,self.width,self.height))
class Bomb(Block):
def __init__(self,player, target_x, target_y,countdown,zeit,color = BLAU):
super().__init__(color, width = 20, height = 20)
self.target_x = round(target_x)
self.target_y = round(target_y)
self.countdown = countdown
self.zeit = zeit
self.player_x = player.rect.left + player.width/2
self.player_y = player.rect.top + player.height/2
self.set_pos(player.rect.center[0]-self.width/2,player.rect.center[1]-self.height/2)
self.truepos = list(self.rect.center)
self.setspeed(3)
def setspeed(self,factor):
self.speed_x = (self.target_x - self.player_x)/100
self.speed_y = (self.target_y - self.player_y)/100
"""self.direction = [0,0]
if self.target_x - self.player_x < 0:
self.direction[0] = -1
elif self.target_x - self.player_x > 0:
self.direction[0] = 1
if self.target_y - self.player_y > 0:
self.direction[1] = 1
elif self.target_y - self.player_y < 0:
self.direction[1] = -1
self.speed_x = self.direction[0]* factor
self.speed_y = self.direction[1]* factor
print(self.speed_x)
print(self.speed_y)"""
def move(self):
if self.speed_x != 0:
self.collision()
if self.speed_y != 0:
self.collision()
def update(self):
self.move()
if self.countdown > 0:
self.countdown -= self.zeit
elif self.countdown <= 0:
bomblist_list.remove(self)
def collision(self):
for block in block_list:
if block.movable != 1:
if self.rect.colliderect(block.rect):
self.distance = [abs(block.rect.centerx - (self.truepos[0] + self.speed_x)), abs(block.rect.centery - (self.truepos[1] + self.speed_y))]
if self.distance[0] > self.distance[1]:
if self.speed_x < 0:
self.rect.left = block.rect.right - self.speed_x
elif self.speed_x > 0:
self.rect.right = block.rect.left -self.speed_x
self.speed_x = -self.speed_x
elif self.distance[0] < self.distance[1]:
if self.speed_y < 0:
self.rect.top = block.rect.bottom - self.speed_y
elif self.speed_y > 0:
self.rect.bottom = block.rect.top - self.speed_y
self.speed_y = -self.speed_y
self.truepos[0] += self.speed_x
self.truepos[1] += self.speed_y
self.rect.center = self.truepos
# -------- Main Program Loop -----------
while not done:
clock.tick(fps)
millisekunde = float(clock.tick(fps))
zeit = millisekunde /500
vergangen += zeit
# --- Main event loop
for event in pygame.event.get(): # User macht irgendwas
if event.type == pygame.QUIT: # Wenn der User Quit klickt
done = True # Verlässt die Schleife
elif event.type == pygame.KEYDOWN:
pass
#if event.key == pygame.K_UP:
#if e
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
mx,my = pygame.mouse.get_pos()
bomb = Bomb(player,mx,my,3,zeit)
bomblist_list.append(bomb)
planted = True
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player.move(speedx,0)
if keys[pygame.K_LEFT]:
player.move(-speedx,0)
if keys[pygame.K_DOWN]:
player.move(0,speedy)
if keys[pygame.K_UP]:
player.move(0,-speedy)
if planted:
for bomb in bomblist_list:
bomb.update()
screen.fill(SCHWARZ)
for block in block_list:
block.render(screen)
if planted:
for bomb in bomblist_list:
bomb.render(screen)
pygame.draw.rect(screen,red,player.rect)
pygame.display.update()
pygame.quit()
When checking collision on fast moving items that can pass an object in one frame, what you want to do is store it's position on the last frame and get it's position on the current frame. With this you can check if a it should have collided with anything between the last and current frame.
Then you can amend it's position by putting it back to where it should have stopped and subsequently draw the object.

how to target a sprite randomly

I am trying to create a retro style 2d shooter game and am currently making a homing missile. I need to randomly select a sprite from a group (pygame.sprite.Group) and find out its x and y coords. I have already made it home in on these coords so I do not need help with that. My code is:
an Enemy class (they are all the same just with different sizes and pictures):
class EnemyShipLevel1(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("C:/Users /Me/PycharmProjects/Game folder/SpaceShip game/Enemy1.png")
self.rect = self.image.get_rect()
self.rect.y = -50
self.rect.centerx = random.randint(15, SCREEN_WIDTH-15)
self.change_y = 0
self.cooldown = 0
self.targetx = 500
self.health = 1
if self.targetx >= self.rect.centerx:
self.change_x = 2
else:
self.change_x = -2
def update(self):
self.rect.x += self.change_x
self.rect.y += self.change_y
if self.rect.centery < 200:
self.rect.centery += 2
self.get_target()
if self.targetx > self.rect.centerx+10:
self.change_x = 3
elif self.targetx < self.rect.centerx-10:
self.change_x = -3
else:
self.change_x = 0
self.cooldown -= 1
if self.rect.left < screen_rect.left:
self.change_x = -self.change_x
if self.rect.right > screen_rect.right:
self.change_x = -self.change_x
if self.cooldown < 0 and (self.rect.centerx-50 < self.targetx < self.rect.centerx+50) and player.trans is False:
self.cooldown = random.randint(15, 25)
laser = NormalLaser(self.rect.centery, self.rect.centerx, False, 0, 8)
all_sprite_list.add(laser)
enemy_laser_list.add(laser)
player_laser_hit_list = pygame.sprite.spritecollide(self, player_laser_list, True)
for laser in player_laser_hit_list:
if self.health < 1:
all_sprite_list.remove(self)
enemy_list.remove(self)
else:
self.health -= 1
def get_target(self):
self.targetx = player.rect.centerx
Targeting Laser class (I know you can't bend lasers):
class TargetingLaser(pygame.sprite.Sprite):
def __init__(self, start_x, start_y, dest_x, dest_y):
super().__init__()
self.floating_point_x = start_x
self.floating_point_y = start_y
self.x_diff = dest_x - start_x
self.y_diff = dest_y - start_y
self.angle = math.atan2(self.y_diff, self.x_diff)
self.velocity = 10
self.change_x = math.cos(self.angle) * self.velocity
self.change_y = math.sin(self.angle) * self.velocity
self.image = pygame.image.load("C:/Users/Minecraft/PycharmProjects/Game folder/SpaceShip game/Laser.png")
self.image = pygame.transform.rotate(self.image, -(math.degrees(self.angle)+90))
self.rect = self.image.get_rect()
self.rect.centerx = start_x
self.rect.centery = start_y
def update(self):
self.floating_point_y += self.change_y
self.floating_point_x += self.change_x
self.rect.y = int(self.floating_point_y)
self.rect.x = int(self.floating_point_x)
if self.rect.top < screen_rect.top:
player_laser_list.remove(self)
enemy_laser_list.remove(self)
all_sprite_list.remove(self)
if self.rect.bottom > screen_rect.bottom:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
if self.rect.left < screen_rect.left:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
if self.rect.right > screen_rect.right:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
Game Loop:
all_sprite_list = pygame.sprite.Group()
enemy_list = pygame.sprite.Group()
player_list = pygame.sprite.GroupSingle()
player_laser_list = pygame.sprite.Group()
enemy_laser_list = pygame.sprite.Group()
live_list = pygame.sprite.Group()
health_bar_list = pygame.sprite.GroupSingle()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
screen_rect = screen.get_rect()
pygame.display.set_caption('Game')
clock = pygame.time.Clock()
Play = True
player = Player(SCREEN_WIDTH/2, SCREEN_HEIGHT-30)
all_sprite_list.add(player)
player_list.add(player)
for counter in range(2):
enemy = EnemyShipLevel1()
all_sprite_list.add(enemy)
enemy_list.add(enemy)
live1 = LivesIcon(5, 0, 1)
live_list.add(live1)
live1 = LivesIcon(27, 30, 2)
live_list.add(live1)
live1 = LivesIcon(50, 0, 3)
live_list.add(live1)
live1 = LivesIcon(72, 30, 4)
live_list.add(live1)
live1 = LivesIcon(95, 0, 5)
live_list.add(live1)
all_sprite_list.add(live_list)
health_bar = PlayerHealthBar(0, 690)
all_sprite_list.add(health_bar)
health_bar_list.add(health_bar)
ship = EnemyShipLevel3()
all_sprite_list.add(ship)
enemy_list.add(ship)
while Play is True:
# Shooting
if player.shooting is True:
if player.laser_cooldown < 0:
player.laser_cooldown = 5
if player.last_shot == "left":
player.last_shot = "right"
laser = NormalLaser(player.rect.centery-2, player.rect.centerx + 11, True, 0, 8)
all_sprite_list.add(laser)
player_laser_list.add(laser)
else:
player.last_shot = "left"
laser = NormalLaser(player.rect.centery-2, player.rect.centerx - 11, True, 0, 8)
all_sprite_list.add(laser)
player_laser_list.add(laser)
if player.rocket_cooldown < 0:
player.rocket_cooldown = 15
for counter in range(10):
laser = TargetingLaser(player.rect.centerx,player.rect.centery, player.rect.centerx, 0)
all_sprite_list.add(laser)
player_laser_list.add(laser)
# Shooting
# Levels
if len(enemy_list) == 0:
all_sprite_list.remove(enemy_laser_list)
all_sprite_list.remove(player_laser_list)
enemy_laser_list.empty()
player_laser_list.empty()
player.level += 1
for counter in range(player.spawn_enemies1):
enemy = EnemyShipLevel1()
all_sprite_list.add(enemy)
enemy_list.add(enemy)
for counter in range(player.spawn_enemies2):
enemy2 = EnemyShipLevel2()
all_sprite_list.add(enemy2)
enemy_list.add(enemy2)
for counter in range(player.spawn_enemies3):
ship = EnemyShipLevel3()
all_sprite_list.add(ship)
enemy_list.add(ship)
if player.level == 2:
player.spawn_enemies1 = 3
player.spawn_enemies2 = 1
if player.level == 3:
player.spawn_enemies1 = 5
player.spawn_enemies2 = 2
if player.level == 4:
player.spawn_enemies1 = 10
player.spawn_enemies2 = 5
if player.level == 5:
player.spawn_enemies1 = 12
player.spawn_enemies2 = 8
player.spawn_enemies3 = 1
# Levels
# End Game
if player.lives == 0:
Play = False
# End Game
# Keys
for event in pygame.event.get():
if event.type == pygame.QUIT:
Play = False
elif event.type == pygame.KEYDOWN:
if (event.key == pygame.K_a) or (event.key == pygame.K_LEFT):
player.change_x = -5
elif (event.key == pygame.K_d) or (event.key == pygame.K_RIGHT):
player.change_x = 5
if event.key == pygame.K_SPACE:
player.shooting = True
elif event.type == pygame.KEYUP:
player.rotation = 0
player.change_x = 0
player.change_y = 0
if event.key == pygame.K_SPACE:
player.shooting = False
# Keys
all_sprite_list.update()
screen.fill(BLACK)
all_sprite_list.draw(screen)
enemy_laser_list.draw(screen)
player_laser_list.draw(screen)
pygame.display.update()
clock.tick(60)
quit()
Each time you kill all of the spawned enemies then the game spawns in more and adds them to enemy_list().
If you are asking how to randomly choose a sprite then use a random.randint(0, 30) (or the number of sprites) then do a long series of if statements. Example Code.
import random
import pygame
class Enemy:
def __init__()
random_sprite = random.randint(0, 1)
# Then put in two variables which equal to the png (I will use sprite1 and sprite2)
def sprite_render
if random_sprite == 0:
# Just blit sprite1
elif random_sprite == 1:
# Just blit sprite2
(The reason why I just commented the actions for the image stuff is because almost everyone does it in a different way and that is not the main point of this answer.)
You could assign a number attribute to each enemy.
For example, you could start off your enemy function like this:
class EnemyShipLevel1(pygame.sprite.Sprite):
number = None
def __init__(self,number):
super().__init__()
self.number = number
self.image = pygame.image.load("C:/Users /Me/PycharmProjects/Game folder/SpaceShip game/Enemy1.png")
Now when you spawn in the enemies, you would add the number attribute:
for counter in range(2): # change this number to use more than 2 enemies (I use 30 later)
enemy = EnemyShipLevel1(counter)
all_sprite_list.add(enemy)
enemy_list.add(enemy)
All you have to do is randomly choose the sprite:
choice = random.randrange(0,30) # between 0 and the number of enemies spawned
for i in enemy_list:
if i.number == choice:
# make i the target of the laser
target_x = i.rect.x
target_y = i.rect.y

Python throws the error "RuntimeError: maximum recursion depth exceeded while calling a Python object"

#imports
import pygame
#globals
SCREENWIDTH = 800
SCREENHEIGHT = 480
class Player(pygame.sprite.Sprite()):
#relevant variables
change_x = 0
change_y = 0
level = None
def __init__(self):
pygame.sprite.Sprite.__init__()
self.width = 25
self.height = 25
self.image = pygame.Surface([width,height])
self.image.fill(pygame.color.Color('RED'))
def update(self):
""" Move the player. """
# Move left/right
self.rect.x += self.change_x
# See if we hit anything
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
# If we are moving right,
# set our right side to the left side of the item we hit
if self.change_x > 0:
self.rect.right = block.rect.left
elif self.change_x < 0:
# Otherwise if we are moving left, do the opposite.
self.rect.left = block.rect.right
self.change_x = 0
# Move up/down
self.rect.y += self.change_y
# Check and see if we hit anything
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
# Reset our position based on the top/bottom of the object.
if self.change_y > 0:
self.rect.bottom = block.rect.top
elif self.change_y < 0:
self.rect.top = block.rect.bottom
# Stop our vertical movement
self.change_y = 0
def go_left(self):
self.change_x = -1
def go_right(self):
self.change_x = 1
def go_up(self):
self.change_y = -1
def go_down(self):
self.change_y = 1
def stop(self):
self.change_x = 0
#collideable object
class Wall(pygame.sprite.Sprite):
level = None
def __init__(self, width, height):
pygame.sprite.Sprite.__init__()
self.image = pygame.Surface([width,height])
self.image.fill(pygame.color.Color('Yellow'))
self.rect = self.image.get_rect()
#collideable object that will later take players to different rooms
class Door(Wall):
level = None
def __init__(self):
Wall.__init__()
self.width = 5
self.height = 30
class Level(object):
background = None
world_shift_x = 0
world_shift_y=0
level_limit_x = -1000
level_limit_y = -2000
def __init__(self, player):
self.wall_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
self.door_list = pygame.sprite.Group()
def update(self):
self.wall_list.update()
self.enemy_list.update()
self.door_list.update()
def draw(self):
screen.fill(pygame.color.Color('Black'))
self.wall_list.draw(screen)
self.enemy_list.draw(screen)
self.door_list.draw(screen)
def shift_world(self, shift_x, shift_y):
#scrolls the screen at a speed of "shift x or y"
self.world_shift_x += shift_x
self.world_shift_y += shift_y
#shifts items within the world with the world
for wall in self.wall_list:
wall.rect.x += shift_x
wall.rect.y += shift_y
for door in self.door_list:
door.rect.x += shift_x
door.rect.y += shift_y
for enemy in self.enemy_list:
enemy.rect.x += shift_x
enemy.rect.y += shift_y
class Level01(Level):
#test level
def __init__(self,player):
Level.__init__()
self.level_limit_x = -1000
self.level_limit_y = -2000
level = [[200,800,0,SCREENHEIGHT],
[200,600,50,SCREENHEIGHT],
[850,200,50,0],
[600,200,50,-50]
]
for wall in level:
block = Wall(wall[0], wall[1])
block.x = wall[2]
block.y = wall[3]
block.player = player
self.wall_list.add(wall)
def main():
pygame.init()
size = [SCREENWIDTH, SCREENHEIGHT]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Game")
player = Player()
level_list = []
level_list.append(Level_01(player))
current_levelno = 0
current_level = level_list[current_level_no]
active_sprite_list = pygame.sprite.Group()
player.level = current_level
player.rect.x = 340
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.go_left()
if event.key == pygame.K_d:
player.go_right()
if event.key == pygame.K_w:
player.jump()
if event.type == pygame.KEYUP:
if event.key == pygame.K_a and player.change_x < 0:
player.stop()
if event.key == pygame.K_d and player.change_x > 0:
player.stop()
active_sprite_list.update()
current_level.update()
if player.rect.right >= 380:
diff_x = player.rect.right - 380
player.rect.right = 380
current_level.shift_world(-diff_x)
if player.rect.left <= 120:
diff_x = 120 - player.rect.left
player.rect.left = 120
current_level.shift_world(diff_x)
if player.rect.bottom <= 700:
diff_y = player.rect.bottom - 700
player.rect.bottom = 700
current_level.shift_world(-diff_y)
if player.rect.top >= 100:
diff_y = 100 - player.rect.bottom
player.rect.bottom = 100
current_level.shift_world(diff_y)
current_position = player.rect.x + current_level.world_shift
if current_position < current_level.level_limit:
if current_level_no < len(level_list)-1:
player.rect.x = 120
current_level_no += 1
current_level = level_list[current_level_no]
player.level = current_level
else:
done = True
current_level.draw(screen)
active_sprite_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
The endgame of this code is to make a functional top down game with the goals of being a psychological horror. So far the mechanics in play are collision with walls, and, by extension, doors, levels, and character movements.
In total, all I can tell anyone is that this error has to do with everything in the code that isn't globals and imports(unless I'm wrong, which would make more sense to me.), and that it has something to do with
A) Code line 140
B) Throws the error under the specific line under 'sprite.py' that says 'self.add(*group) (which is line 142 in the 'sprite.py' file)
The stuff I can speculate about in this is that perhaps the code is throwing an infinite loop, as watching the console shows a code in the IDLE Shell, or the messages section of the pyscripter(what I'm using to program this), which iterates indefinitely before the program terminates. No visuals are created, and nothing appears to be happening, save the infinite loop. The code message is 'File "C:\Python33\lib\site-packages\pygame\sprite.py", line 142, in add self.add(*group) I can't say for sure, it's probably better that you run the code yourself to see what you can gather.

Categories

Resources