I'm trying to program a simple top-down dungeon crawler in Pygame and I've already hit a roadblock in designing my collision response. So far I've programmed player movement, collision detection (only knows when a player hits a wall) and player-to-mouse rotation, the latter of which is disabled to simplify this solution.
I've tried the normal method of moving the character, checking for collision, changing the character's position if needed and then drawing all elements to the screen.
import pygame
from pygame.locals import *
import os
import sys
import math
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
pygame.font.init()
clock = pygame.time.Clock()
myfont = pygame.font.SysFont('sourcecodeproblack', 12)
BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
WALLS = [220, 40, 30]
SIZE = [240, 240]
DSIZE = [480, 480]
TSIZE = [720, 720]
dub = False
trip = False
def normal():
global screen
screen = pygame.display.set_mode(SIZE)
pygame.display.set_caption("METAL PACKET")
def full():
global screen
screen = pygame.display.set_mode((SIZE), pygame.FULLSCREEN)
pygame.display.set_caption("METAL PACKET: Fullscreen Edition")
def doubled():
global screen
global window
global res
global invscale
invscale = 1/2
res = DSIZE
window = pygame.display.set_mode(DSIZE)
screen = pygame.Surface(SIZE)
pygame.display.set_caption("METAL PACKET: Double Boogaloo")
def doubledfull():
global screen
global window
global res
global invscale
invscale = 1/2
res = DSIZE
window = pygame.display.set_mode((DSIZE), pygame.FULLSCREEN)
screen = pygame.Surface(SIZE)
pygame.display.set_caption("METAL PACKET: Fullscreen Boogaloo")
def tripled ():
global screen
global window
global res
global invscale
invscale = 1/3
res = TSIZE
window = pygame.display.set_mode(TSIZE)
screen = pygame.Surface(SIZE)
pygame.display.set_caption("METAL PACKET: Trifecta of Resolution")
def tripledfull ():
global screen
global window
global res
global invscale
invscale = 1/3
res = TSIZE
window = pygame.display.set_mode((TSIZE), pygame.FULLSCREEN)
screen = pygame.Surface(SIZE)
pygame.display.set_caption("METAL PACKET: Fullscreen Trifecta Edition")
print("")
print("1: 240 x 240")
print("2: 480 x 480")
print("3: 720 x 720")
print("4: 240 x 240 Fullscreen")
print("5: 480 x 480 Fullscreen")
print("6: 720 x 720 Fullscreen")
print("")
res = input("Choose a video mode. ")
if res == ("1"):
normal()
if res == ("2"):
doubled()
dub = True
if res == ("3"):
tripled()
trip = True
if res == ("4"):
full()
if res == ("5"):
doubledfull()
dub = True
if res == ("6"):
tripledfull()
trip = True
class Walls(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.currentsprite = ()
def draw(self):
self.image = pygame.image.load(self.currentsprite)
screen.blit(self.image, [0, 0])
room1 = Walls()
room1.currentsprite = ("room1.png")
collidelist = pygame.sprite.Group()
class WallColliders(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self, collidelist)
self.rectval = []
def collide(self):
self.rect = (self.rectval)
topwall = WallColliders()
topwall.rectval = [0, 0, 240, 20]
bottomwall = WallColliders()
bottomwall.rectval = [0, 160, 240, 20]
leftwall = WallColliders()
leftwall.rectval = [0, 0, 20, 180]
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.collide = 0
self.x = 0
self.y = 0
self.speed = 0
self.currentsprite = ("pac.png")
self.image = pygame.image.load(self.currentsprite)
self.rect = self.image.get_rect()
def collidedetect(self):
if pygame.sprite.spritecollideany(player, collidelist):
self.collide = 1
else:
self.collide = 0
def move(self):
key = pygame.key.get_pressed()
if key[K_LEFT] or key[ord("a")]:
self.x -= self.speed
if self.collide == 1:
self.x += self.speed
self.collide = 0
if key[K_RIGHT] or key[ord("d")]:
self.x += self.speed
if self.collide == 1:
self.x -= self.speed
self.collide = 0
if key[K_UP] or key[ord("w")]:
self.y -= self.speed
if self.collide == 1:
self.y += self.speed
self.collide = 0
if key[K_DOWN] or key[ord("s")]:
self.y += self.speed
if self.collide == 1:
self.y -= self.speed
self.collide = 0
if self.collide == 1:
print("collide")
def rotate(self):
pos = pygame.mouse.get_pos()
if dub == True or trip == True:
pos = pos[0] * invscale, pos[1] * invscale
mouse_x, mouse_y = pos
mouse_x, mouse_y = pos
rel_x, rel_y = mouse_x - self.x, mouse_y - self.y
self.angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.rotimage = pygame.transform.rotozoom(self.image, self.angle, 1)
self.rect = self.rotimage.get_rect(center=(self.rect.center))
def monitor(self):
textsurface = myfont.render(str(self.angle), False, (255, 255, 255))
screen.blit(textsurface,(50,210))
def draw(self):
#screen.blit(self.rotimage, [self.x, self.y])
screen.blit(self.image, [self.x, self.y])
self.rect = pygame.Rect(self.x, self.y, 20, 20)
player = Player()
player.x = 29
player.y = 89
player.speed = 3
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
print("")
print("See you next time.")
pygame.quit()
sys.exit(0)
done = True
screen.fill(BLACK)
room1.draw()
for x in collidelist:
x.collide()
player.collidedetect()
player.move()
#player.rotate()
#player.monitor()
player.draw()
if dub == True or trip == True:
pygame.transform.scale(screen, res, window)
pygame.display.flip()
clock.tick(60)
pygame.quit()
For some reason this will stick the player in the wall unless they press move diagonally instead of moving the player out of the wall and resetting collision. I would greatly appreciate it if someone could read through my code and suggest any improvement, optimisation or solution. Any example code would be great. Thanks.
Don't overcomplicate. When the player is moved, the calculate the new position and update the .rect property of the Player object:
class Player(pygame.sprite.Sprite):
# [...]
def move(self):
key = pygame.key.get_pressed()
if key[K_LEFT] or key[ord("a")]:
self.x -= self.speed
if key[K_RIGHT] or key[ord("d")]:
self.x += self.speed
if key[K_UP] or key[ord("w")]:
self.y -= self.speed
if key[K_DOWN] or key[ord("s")]:
self.y += self.speed
self.updaterect()
def updaterect(self):
self.rect = pygame.Rect(self.x, self.y, 20, 20)
In the main loop:
backup the player position
move the player
check for collision
if there is a collision, then restore the position of the player
while not done:
# [...]
pos = (player.x, player.y)
player.move()
player.collidedetect()
if player.collide:
player.x, player.y = pos
player.updaterect()
player.collide = False
player.draw()
# [...]
Of course this can be done in a method, too:
class Player(pygame.sprite.Sprite):
# [...]
def moveandcollide(self):
pos = (self.x, self.y)
self.move()
self.collidedetect()
if player.collide:
self.x, self.y = pos
self.updaterect()
self.collide = False
while not done:
# [...]
player.moveandcollide()
Related
My game is a top down shooter. When the player is stationary and is shooting in any direction, the bullet goes in the same direction as the mouse. However, when I move diagonally whilst shooting the bullet is no longer in the direction of the mouse position. So basically it works when the player is stationary, but not when I'm moving.
Edit: I will keep trying to fix this but here is a video to better understand the issue https://imgur.com/a/8QRr1PO
Here is the code:
import pygame
from sys import exit
import math
pygame.init()
# window and text
WIDTH = 1280
HEIGHT = 720
FPS = 60
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Shooting problem demo')
game_font = pygame.font.Font('freesansbold.ttf', 50)
clock = pygame.time.Clock()
# loads imgs
background = pygame.image.load("background/gamemap.png").convert()
plain_bg = pygame.image.load("background/plain_bg.png").convert()
bullet_img = pygame.image.load("bullets/bluebullet.png").convert_alpha()
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.image.load("handgun/move/survivor-move_handgun_0.png").convert_alpha()
self.image = pygame.transform.rotozoom(self.image, 0, 0.35)
self.base_player_image = self.image
self.pos = pos
self.base_player_rect = self.base_player_image.get_rect(center = pos)
self.rect = self.base_player_rect.copy()
self.player_speed = 10 # was 4
self.shoot = False
self.shoot_cooldown = 0
def player_turning(self):
self.mouse_coords = pygame.mouse.get_pos()
self.x_change_mouse_player = (self.mouse_coords[0] - (WIDTH // 2))
self.y_change_mouse_player = (self.mouse_coords[1] - (HEIGHT // 2))
self.angle = int(math.degrees(math.atan2(self.y_change_mouse_player, self.x_change_mouse_player)))
self.angle = (self.angle) % 360
self.image = pygame.transform.rotate(self.base_player_image, -self.angle)
self.rect = self.image.get_rect(center=self.base_player_rect.center)
def player_input(self):
self.velocity_x = 0
self.velocity_y = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.velocity_y = -self.player_speed
if keys[pygame.K_s]:
self.velocity_y = self.player_speed
if keys[pygame.K_d]:
self.velocity_x = self.player_speed
if keys[pygame.K_a]:
self.velocity_x = -self.player_speed
if self.velocity_x != 0 and self.velocity_y != 0: # moving diagonally
self.velocity_x /= math.sqrt(2)
self.velocity_y /= math.sqrt(2)
if keys[pygame.K_SPACE]:
self.shoot = True
self.is_shooting()
else:
self.shoot = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
self.shoot = False
def move(self):
self.base_player_rect.centerx += self.velocity_x
self.base_player_rect.centery += self.velocity_y
self.rect.center = self.base_player_rect.center
def is_shooting(self):
if self.shoot_cooldown == 0 and self.shoot:
self.bullet = Bullet(self.base_player_rect.centerx, self.base_player_rect.centery, self.angle)
self.shoot_cooldown = 20
bullet_group.add(self.bullet)
all_sprites_group.add(self.bullet)
def update(self):
self.player_turning()
self.player_input()
self.move()
if self.shoot_cooldown > 0:
self.shoot_cooldown -= 1
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, angle):
super().__init__()
self.image = bullet_img
self.image = pygame.transform.rotozoom(self.image, 0, 0.1)
self.image.set_colorkey((0,0,0))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.x = x
self.y = y
self.speed = 10
self.angle = angle
self.x_vel = math.cos(self.angle * (2*math.pi/360)) * self.speed
self.y_vel = math.sin(self.angle * (2*math.pi/360)) * self.speed
self.bullet_lifetime = 750
self.spawn_time = pygame.time.get_ticks()
def bullet_movement(self):
self.x += self.x_vel
self.y += self.y_vel
self.rect.x = int(self.x)
self.rect.y = int(self.y)
if pygame.time.get_ticks() - self.spawn_time > self.bullet_lifetime:
self.kill()
def update(self):
self.bullet_movement()
class Camera(pygame.sprite.Group):
def __init__(self):
super().__init__()
self.offset = pygame.math.Vector2()
self.floor_rect = background.get_rect(topleft = (0,0))
def custom_draw(self):
self.offset.x = player.rect.centerx - (WIDTH // 2)
self.offset.y = player.rect.centery - (HEIGHT // 2)
#draw the floor
floor_offset_pos = self.floor_rect.topleft - self.offset
screen.blit(background, floor_offset_pos)
for sprite in all_sprites_group:
offset_pos = sprite.rect.topleft - self.offset
screen.blit(sprite.image, offset_pos)
# Groups
all_sprites_group = pygame.sprite.Group()
player = Player((900,900))
all_sprites_group.add(player)
bullet_group = pygame.sprite.Group()
camera = Camera()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
screen.blit(plain_bg, (0,0))
camera.custom_draw()
all_sprites_group.update()
pygame.display.update()
clock.tick(FPS)
Actually, there is no problem at all. It is just an optical illusion. The projectile does not move relative to the player, but relative to the camera. The player is always in the center of the screen, because the player doesn't move, but the camera does. When the camera moves, all objects move with the camera.
For example, if you shoot a bullet to the right and move the player up, it will look like the bullet is moving diagonally to the right and down. To the right because it changes position, to the right and down because the player moves upwards.
To illustrate this, I reduced the speed of the player (self.player_speed = 2) and the speed of the bullet (self.speed = 4) and drew the scene on a checkered background:
if event.type == pygame.KEYUP: only makes sens in the event loop, but not in player_input. Shooting only one bullet at once just needs another condition (and not self.shoot):
if keys[pygame.K_SPACE] and not self.shoot:
self.shoot = True
self.is_shooting()
else:
self.shoot = False
Firts I am french so excuse-me for my english. I have recently discoverd pygame and I love that so decided to create a rpg on pygame and... I have a little problem : with many, many difficulty I made a scrolling system on my pygame's rpg I evn do a system how make sure player's image gets back when he go up, puts himself in profile ...
BUT (a big but) my player's image don't move, the scrolling, move and the player's rect also, I don't know I can solve that so please help me !!!!! The main.py file :
from game import Game
from level import *
pygame.init()
lvl = Level()
screen = pygame.display.set_mode((1024, 768))
pygame.display.set_caption("RPG")
game = Game()
cam = Camera(1024, 768)
running = True
lvl.generer()
print(game.player.rect)
clock = pygame.time.Clock()
while running:
cam.update(game.player)
#print(cam.rect.topleft)
if game.pressed.get(pygame.K_RIGHT) and game.player.rect.x + game.player.rect.width < 1080:
game.player.move_right()
lvl.afficher(screen, 0, 0, cam.x, cam.y)
#print(game.player.rect.x)
elif game.pressed.get(pygame.K_LEFT) and game.player.rect.x > 0:
game.player.move_left()
lvl.afficher(screen, 0, 0, cam.x, cam.y)
#print(game.player.rect.x)
elif game.pressed.get(pygame.K_DOWN):
game.player.move_down()
lvl.afficher(screen, 0, 0, cam.x, cam.y)
#screen.scroll(0, -game.player.velocity)
#print(game.player.rect.y)
elif game.pressed.get(pygame.K_UP):
game.player.move_up()
lvl.afficher(screen, 0, 0, cam.x, cam.y)
#screen.scroll(0, game.player.velocity)
#print(game.player.rect.y)
#print(cam.rect.x, cam.rect.y)
#print(cam.rect)
screen.blit(game.player.image, game.player.rect)
print(game.player.rect)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
elif event.type == pygame.KEYDOWN:
game.pressed[event.key] = True
elif event.type == pygame.KEYUP:
game.pressed[event.key] = False```
the player.py class :
```import pygame
class Player(pygame.sprite.Sprite):
def __init__(self, game):
super(Player, self).__init__()
self.game = game
self.health = 100
self.maxHealth = 100
self.attack = 10
self.velocity = 10
self.dimension = (64, 88)
self.image = pygame.image.load("assets/player/player_face/player_face.png")
self.original_image = pygame.image.load("assets/player/player_face/player_face.png")
self.image = pygame.transform.scale(self.image, self.dimension)
self.rect = self.image.get_rect()
self.rect.x = 512
self.rect.y = 384
self.face = [pygame.image.load("assets/player/player_face/player/sprite_00.png"), pygame.image.load("assets/player/player_face/player/sprite_01.png"), pygame.image.load("assets/player/player_face/player/sprite_02.png"), pygame.image.load("assets/player/player_face/player/sprite_03.png"), pygame.image.load("assets/player/player_face/player/sprite_04.png"), pygame.image.load("assets/player/player_face/player/sprite_05.png"), pygame.image.load("assets/player/player_face/player/sprite_06.png"), pygame.image.load("assets/player/player_face/player/sprite_07.png"), pygame.image.load("assets/player/player_face/player/sprite_08.png"), pygame.image.load("assets/player/player_face/player/sprite_09.png")]
def move_right(self):
self.rect.x += self.velocity
self.image = pygame.image.load("assets/player/player_profileLeft/player_profile_left.png")
self.image = pygame.transform.scale(self.image, self.dimension)
def move_left(self):
self.rect.x -= self.velocity
self.image = pygame.image.load("assets/player/player_profileRight/player_profile_right.png")
self.image = pygame.transform.scale(self.image, self.dimension)
def move_down(self):
self.rect.y += self.velocity
#self.image = pygame.image.load("assets/player/player_face/player_face.png")
#self.image = pygame.transform.scale(self.image, self.dimension)
i = 0
for elt in self.face:
self.image = self.face[i]
print("lol")
self.image = pygame.transform.scale(self.image, self.dimension)
i += 1
def move_up(self):
self.rect.y -= self.velocity
self.image = pygame.image.load("assets/player/player_back/player_dos.png")
self.image = pygame.transform.scale(self.image, self.dimension)
the level.py class :
from pygame.locals import *
from player import *
class Camera:
def __init__(self, widht, height):
self.rect = pygame.Rect(0, 0, widht, height)
self.widht = widht
self.height = height
self.topleft = list(self.rect.topleft)
self.x = self.topleft[0]
self.y = self.topleft[1]
def update(self, target):
self.rect.y = -target.rect.y + int(769 / 2)
self.rect.x = -target.rect.x + int(1024 / 2)
self.topleft = list(self.rect.topleft)
self.x = self.topleft[0]
self.y = self.topleft[1]
self.rect = pygame.Rect(self.rect.x, self.rect.y, self.widht, self.height)
#self.rect.topleft_x = self.rect.topleft(0)
#self.coo = (0, 0)
class Level:
def __init__(self):
self.structure = 0
def generer(self):
with open("niveau.txt", "r") as fichier:
structure_niveau = []
for ligne in fichier:
ligne_niveau = []
for sprite in ligne:
if sprite != '\n':
ligne_niveau.append(sprite)
structure_niveau.append(ligne_niveau)
self.structure = structure_niveau
def afficher(self, fenetre, x, y, camLeftX, camLeftY):
tailleSprite = 64
#Camera.__init__(self, x, y)
grass = pygame.image.load("assets/bloc/grass.png").convert_alpha()
tree = pygame.image.load("assets/bloc/tree_grass.png").convert_alpha()
no_texture = pygame.image.load("assets/bloc/no_texture.png")
num_ligne = 0
for ligne in self.structure:
num_case = 0
for sprite in ligne:
x = num_case * tailleSprite - camLeftX
y = num_ligne * tailleSprite - camLeftY
if sprite == 'G':
fenetre.blit(grass, (x, y))
elif sprite == 'T':
fenetre.blit(tree, (x, y))
#print(self.x, self.y)
else:
fenetre.blit(no_texture, (x, y))
num_case += 1
num_ligne += 1```
Ok I just found the solution but It's making an other problem I'll ask the question on a other post. At x = num_case * tailleSprite - camLeftX and y = num_ligne * tailleSprite - camLeftY on level.py just replace the - by a +
I use pygame and python. I want to make a simple game. Two tanks in scene shoot each other. I have tank class:
class playerObject(pygame.sprite.Sprite):
def __init__(self, rect):
super().__init__()
self.__original_image = pygame.image.load(config.PATH_TO_IMAGES + '\\tank.png').convert_alpha()
self.__original_image = pygame.transform.scale(self.__original_image, (rect.width, rect.height))
self.image = self.__original_image
self.rect = self.image.get_rect()
self.rect.x = rect.x
self.rect.y = rect.y
self.lookAtVector = Vector2(-3, 0)
def addBall(self):
if len(self.balls) < 5:
self.balls.append(ballObject(pygame.Rect(self.rect.centerx, self.rect.top, 10, 10)))
self.balls[-1].lookAtVector.x = self.lookAtVector.x
self.balls[-1].lookAtVector.y = self.lookAtVector.y
angle = 0
__original_image = 0
image = 0
lookAtVector = 0
balls = []
And bullet class:
class ballObject(pygame.sprite.Sprite):
def __init__(self, rect):
super().__init__()
self.image = pygame.image.load(config.PATH_TO_IMAGES + '\\tank.png').convert_alpha()
self.image = pygame.transform.scale(self.image, (5, 5))
self.rect = self.image.get_rect()
self.rect.x = rect.x
self.rect.y = rect.y
self.lookAtVector = Vector2(-3, 0)
lookAtVector = 0
currentSpeed = 1.5
But ball spawns on center of tank. How to do that ball will spawn on tank gun?
tank.png
bullet.png
It needs basic trigonometry.
At start keep distance from center of tank to end of its barrel
self.distance = self.rect.height//2
and later get position of barrel base on angle
def get_barrel_end(self):
rad = math.radians(self.angle)
x = self.rect.centerx - math.sin(rad) * self.distance
y = self.rect.centery - math.cos(rad) * self.distance
return x, y
So you have position for bullet.
I use module math but maybe you could do it with pygame.math
EDIT: The same with pygame.math.Vector2()
At start keep distance from center of tank to end of its barrel
self.distance_vector = pygame.math.Vector2(0, -self.rect.height//2)
and later get position of barrel base on angle
def get_barrel_end(self):
return self.rect.center + self.distance_vector.rotate(-self.angle)
Working example - it draws tank in center and bullet on the end of tank's barrel. Using left/right arrow you can rotate tank and bullet keeps its position on end of tank's barrel.
import pygame
import math
# --- constants ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 15
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# --- classes ---
class Tank():
def __init__(self, x, y):
self.image_original = pygame.image.load('tank.png').convert_alpha()
self.angle = 0
self.dirty = False
self.image = self.image_original.copy()
self.rect = self.image.get_rect(center=(x, y))
self.turn_left = False
self.turn_right = False
#self.distance = self.rect.height//2
self.distance_vector = pygame.math.Vector2(0, -self.rect.height//2)
def draw(self, screen):
screen.blit(self.image, self.rect)
def update(self):
if self.turn_left:
self.angle = (self.angle + 2) % 360
self.dirty = True
if self.turn_right:
self.angle = (self.angle - 2) % 360
self.dirty = True
if self.dirty:
self.dirty = False
#print(self.angle)
self.image = pygame.transform.rotate(self.image_original, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
def handle_event(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.turn_left = True
elif event.key == pygame.K_RIGHT:
self.turn_right = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
self.turn_left = False
elif event.key == pygame.K_RIGHT:
self.turn_right = False
#def get_barrel_end(self):
# rad = math.radians(self.angle)
# x = self.rect.centerx - math.sin(rad) * self.distance
# y = self.rect.centery - math.cos(rad) * self.distance
# return x, y
def get_barrel_end(self):
return self.rect.center + self.distance_vector.rotate(-self.angle)
class Bullet():
def __init__(self, x, y):
self.image = pygame.image.load('bullet.png').convert_alpha()
self.rect = self.image.get_rect(center=(x, y))
self.dirty = False
def draw(self, screen):
screen.blit(self.image, self.rect)
def update(self, x, y):
self.rect.center = (x, y)
def handle_event(self, event):
pass
# --- main ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))#, 32)
screen_rect = screen.get_rect()
tank = Tank(*screen_rect.center)
bullet = Bullet(*tank.get_barrel_end())
clock = pygame.time.Clock()
running = True
while running:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
tank.handle_event(event)
# - updates -
tank.update()
bullet.update(*tank.get_barrel_end())
# - draws -
screen.fill(WHITE)
tank.draw(screen)
bullet.draw(screen)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
import math
import random
import pygame
from pygame.locals import *
import sys
def events():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
pygame.init()
W = 700
H = 400
updater = pygame.time.Clock()
display = pygame.display.set_mode((700, 400))
pygame.display.set_caption("Skating_Game")
x = y = 0
surface = pygame.image.load("man2.png")
pygame.display.set_icon(surface)
class player:
def __init__(self, velocity, maxJumpRange):
self.velocity = velocity
self.maxJumpRange = maxJumpRange
def setLocation(self, x, y):
self.x = x
self.y = y
self.xVelocity = 0
self.jumping = False
self.jumpCounter = 0
self.falling = True
def keys(self):
k = pygame.key.get_pressed()
if k[K_LEFT]:
self.xVelocity = -self.velocity
elif k[K_RIGHT]:
self.xVelocity = self.velocity
else:
self.xVelocity = 0
if k[K_SPACE] and not self.jumping and not self.falling:
self.jumping = True
self.jumpCounter = 0
def move(self):
self.x += self.xVelocity
if self.jumping:
self.y -= self.velocity
self.jumpCounter += 1
if self.jumpCounter == self.maxJumpRange:
self.jumping = False
self.falling = True
elif self.falling:
if self.y <= H - 60 and self.y + self.velocity >= H - 60:
self.y = H - 60
self.falling = False
else:
self.y += self.velocity
def draw(self):
display = pygame.display.get_surface()
character = pygame.image.load("man3.png").convert_alpha()
display.blit(character, (self.x, self.y - 100))
#pygame.draw.circle(display, (255, 255, 255), (self.x, self.y - 25), 25, 0)
def do(self):
self.keys()
self.move()
self.draw()
P = player(3, 50)
P.setLocation(350, 0)
BLACK = ( 0, 0, 0)
g=0
font = pygame.font.SysFont("Plump", 30)
obstacle = pygame.image.load("obstacle.png").convert_alpha()
background = pygame.image.load("Road.png").convert()
x = 0
while True:
events()
rel_x = x % background.get_rect().width
display.blit(background, (rel_x - background.get_rect().width,0))
if rel_x < 700:
display.blit(background, (rel_x, 0))
x -= 1
g += 0.01
pygame.draw.rect(display, (255,255,255,128), [rel_x, 275, 150, 50])
display.blit(obstacle, (rel_x, 250))
text = font.render("Score: "+str(int(g)), True, (255, 255, 255))
display.blit(text, (0,0))
P.do()
if P.rect.collidepoint(self.x,self.y):
pygame.quit()
pygame.display.update()
updater.tick(200)
So if the player collides with the obstacle image the game should stop. How do i do this? I have made a class for the player and the obstacle is just an image which is constantly moving.
I was thinking maybe I could track the x and y coordinate of the player and obstacle and when their radius overlaps the game could stop.
Here's a working (simplified) version of your program with some comments. You have to create rects for the obstacle and the player and then check if the rects collide with the help of the colliderect method.
import sys
import pygame
from pygame.locals import *
pygame.init()
W = 700
H = 400
updater = pygame.time.Clock()
display = pygame.display.set_mode((700, 400))
PLAYER_IMAGE = pygame.Surface((30, 50))
PLAYER_IMAGE.fill(pygame.Color('dodgerblue1'))
class Player:
def __init__(self, x, y, velocity, maxJumpRange):
self.velocity = velocity
self.maxJumpRange = maxJumpRange
self.image = PLAYER_IMAGE # Give the player an image.
# Create a rect with the size of the PLAYER_IMAGE and
# pass the x, y coords as the topleft argument.
self.rect = self.image.get_rect(topleft=(x, y))
self.x = x
self.y = y
self.xVelocity = 0
self.jumping = False
self.jumpCounter = 0
self.falling = True
def keys(self):
k = pygame.key.get_pressed()
if k[K_LEFT]:
self.xVelocity = -self.velocity
elif k[K_RIGHT]:
self.xVelocity = self.velocity
else:
self.xVelocity = 0
if k[K_SPACE] and not self.jumping and not self.falling:
self.jumping = True
self.jumpCounter = 0
def move(self):
self.x += self.xVelocity
if self.jumping:
self.y -= self.velocity
self.jumpCounter += 1
if self.jumpCounter == self.maxJumpRange:
self.jumping = False
self.falling = True
elif self.falling:
if self.y >= H - 160: # Simplified a little.
self.y = H - 160
self.falling = False
else:
self.y += self.velocity
# Update the position of the rect, because it's
# used for the collision detection.
self.rect.topleft = self.x, self.y
def draw(self, display):
# Just draw the image here.
display.blit(self.image, (self.x, self.y))
def do(self):
self.keys()
self.move()
player = Player(350, 0, 3, 50)
obstacle = pygame.Surface((150, 50))
obstacle.fill(pygame.Color('sienna1'))
# Create a rect with the size of the obstacle image.
obstacle_rect = obstacle.get_rect()
g = 0
x = 0
FPS = 60 # Cap the frame rate at 60 or 30 fps. 300 is crazy.
while True:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# --- Update the game ---
player.do()
rel_x = x % display.get_width()
x -= 7
g += 0.01
obstacle_rect.topleft = rel_x, 250 # Update the position of the rect.
# --- Draw everything ---
display.fill((30, 30, 30))
display.blit(obstacle, (rel_x, 250))
if g > 30:
display.blit(obstacle, (rel_x+350, 250))
# Check if the obstacle rect and the player's rect collide.
if obstacle_rect.colliderect(player.rect):
print("Game over!") # And call pygame.quit and sys.exit if you want.
# Draw the image/surface of the player onto the screen.
player.draw(display)
# Draw the actual rects of the objects (for debugging).
pygame.draw.rect(display, (200, 200, 0), player.rect, 2)
pygame.draw.rect(display, (200, 200, 0), obstacle_rect, 2)
pygame.display.update()
updater.tick(FPS)
Pygame rectangles include a collidepoint and colliderect method that allows you to check to see if something intersects with a rectangle. So you could have rectangles drawn beneath the obstacle and check to see if the player's coordinates intersect with the rectangle. Like this:
if self.rect.collidepoint(self.x,self.y):
pygame.quit()
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I was working on a movement animations since I saw a youtuber explaining how to do it, but I'm getting this error:
TypeError: argument 1 must be pygame.Surface, not list
My code is about 500 lines.
# Pygame Template - skeleton for a new pygame project
import pygame
import random
import os
from os import path
vec = pygame.math.Vector2
width = 800
height = 600
FPS = 60
POWERUP_TIME = 5000
title = 'Parkourse'
# Player properties
player_acceleration = 0.5
player_friction = -0.12
player_gravity = 0.8
player_jump = 10
# Starting platforms
platforms_list = [(0,height-40,width,50), # Ground
(0,0,800,10), # Top
(0,0,10,600), # Left Border
(790,height-400,10,600),# Right Border
(250,height - 160,width-200,10), # Floor 1
(0,height - 280,width-200,10), # Floor 2
(250,height - 400,width-100,10)] # Floor 3
# Define Colors
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
# set up assets folders
game_folder = os.path.dirname(__file__)
image_folder = os.path.join(game_folder, "Image")
sound_folder = os.path.join(game_folder, "Sound")
# Initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((width,height))
pygame.display.set_caption(title)
clock = pygame.time.Clock()
# Load all game graphics
background = pygame.image.load(path.join(image_folder, "background.png")).convert()
background_rect = background.get_rect()
no_mvmt_0 = pygame.image.load(path.join(image_folder,"no_mvmt_0.png")).convert()
no_mvmt_1 = pygame.image.load(path.join(image_folder,"no_mvmt_1.png")).convert()
running_0 = pygame.image.load(path.join(image_folder,"running_0.png")).convert()
running_1 = pygame.image.load(path.join(image_folder,"running_1.png")).convert()
jumping_0 = pygame.image.load(path.join(image_folder,"jumping_0.png")).convert()
mini_no_mvmt = pygame.transform.scale(no_mvmt_0, (25,48))
mini_no_mvmt.set_colorkey(white)
scissors = pygame.image.load(path.join(image_folder,"scissors.png")).convert()
mob_left = pygame.image.load(path.join(image_folder,"mob_left.png")).convert()
power_upper_image = {}
power_upper_image['shield_0'] = pygame.image.load(path.join(image_folder,"shield_upper_0.png")).convert()
power_upper_image['shield_1'] = pygame.image.load(path.join(image_folder,"shield_upper_1.png")).convert()
power_upper_image['shield_2'] = pygame.image.load(path.join(image_folder,"shield_upper_2.png")).convert()
power_upper_image['life'] = pygame.image.load(path.join(image_folder,"life_upper.png")).convert()
power_upper_image['power'] = pygame.image.load(path.join(image_folder,"power.png")).convert()
explosion_animation = {}
explosion_animation['normal']=[]
explosion_animation['small']=[]
explosion_animation['player']=[]
for explosion in range(5):
explose = 'explosion_{}.png'.format(explosion)
image = pygame.image.load(path.join(image_folder, explose)).convert()
image.set_colorkey(white)
image.set_colorkey(black)
image_normal = pygame.transform.scale(image, (80,80))
explosion_animation['normal'].append(image_normal)
image_small = pygame.transform.scale(image, (30, 30))
explosion_animation['small'].append(image_small)
death = 'dying_{}.png'.format(explosion)
image = pygame.image.load(path.join(image_folder, death)).convert()
image.set_colorkey(white)
explosion_animation['player'].append(image)
#Load all game sounds
scream_sound = []
for scream in ["slightscream_0.wav", "slightscream_1.wav", "slightscream_2.wav",
"slightscream_3.wav", "slightscream_4.wav", "slightscream_5.wav",
"slightscream_6.wav", "slightscream_7.wav", "slightscream_8.wav",
"slightscream_9.wav", "slightscream_10.wav", "slightscream_11.wav",
"slightscream_12.wav", "slightscream_13.wav", "slightscream_14.wav"]:
scream_sound.append(pygame.mixer.Sound(path.join(sound_folder,scream)))
shoot_sound = pygame.mixer.Sound(path.join(sound_folder,"shoot.wav"))
shield = pygame.mixer.Sound(path.join(sound_folder,"shield.wav"))
life = pygame.mixer.Sound(path.join(sound_folder,"life.wav"))
special_power = pygame.mixer.Sound(path.join(sound_folder,"special_power.wav"))
death_sound = pygame.mixer.Sound(path.join(sound_folder,"death.ogg"))
explosion_sound = []
for sound in ["explosion.wav", "explosion_2.wav"]:
explosion_sound.append(pygame.mixer.Sound(path.join(sound_folder, sound)))
pygame.mixer.music.load(path.join(sound_folder,"gameplay.ogg"))
pygame.mixer.music.set_volume(0.6)
font_name = pygame.font.match_font('arial')
def draw_text (surf, text,color,size, x, y):
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, color)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surf.blit(text_surface, text_rect)
def newmob():
m = Mobs()
all_sprites.add(m)
mobs.add(m)
def draw_shield_bar(screen, x,y,percentage):
if percentage < 0:
percentage = 0
bar_lenght = 100
bar_height = 10
fill = (percentage / 100) * bar_lenght
outline_rect = pygame.Rect(x,y,bar_lenght,bar_height)
fill_rect = pygame.Rect(x,y,fill, bar_height)
pygame.draw.rect(screen, green, fill_rect)
pygame.draw.rect(screen, black, outline_rect, 2) # 2 is the the number of pixels
# of how wide you want the outline
# of the rectangle to be
def draw_lives (surface, x, y, lives, image):
for i in range(lives):
image_rect = image.get_rect()
image_rect.x = x + 30 * i
image_rect.y = y
surface.blit(image, image_rect)
score = 0
def show_game_over_screen():
screen.blit(background, background_rect)
draw_text(screen, "Dang..!",red,100, width/2, 200)
draw_text(screen, "Score: " + str(score),blue,30, width/2, 330)
draw_text(screen, "Press any key to retry",blue,30, width/2, 390)
pygame.display.flip()
waiting = True
while waiting:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYUP:
waiting = False
def show_start_screen():
screen.blit(background, background_rect)
draw_text(screen,"Parkourse!", green, 100, width/2, 200)
draw_text(screen, "Use the arrow keys to move, S to fire, and space to Jump",blue,30, width/2, 330)
draw_text(screen, "Press any key to begin",blue,30, width/2, 390)
pygame.display.flip()
waiting = True
while waiting:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYUP:
waiting = False
class Player (pygame.sprite.Sprite):
# Sprite for the player
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.load_movement_images()
self.image = self.standing_frame[0]
self.rect = self.image.get_rect()
self.pos = vec(50,500)
self.vel = vec(0,0)
self.acc = vec(0,0)
self.shield = 100
self.lives = 3
self.hidden = False
self.hide_timer = pygame.time.get_ticks()
self.power = 1
self.power_timer = pygame.time.get_ticks()
self.running = False
self.jumping = False
self.current_frame = 0
self.last_update = 0
def load_movement_images(self):
self.standing_frame = [no_mvmt_0, no_mvmt_1]
self.running_frame_right = [running_0,running_1]
self.running_frame_left = []
for frame in self.standing_frame:
frame.set_colorkey(white)
for frame in self.running_frame_right:
self.running_frame_left.append(pygame.transform.flip(frame,True,False)) # True is horizontaly, False is vertically
frame.set_colorkey(white)
self.jumping_frame = jumping_0
self.jumping_frame.set_colorkey(white)
def animate(self):
now = pygame.time.get_ticks()
if not self.jumping and not self.running:
if now - self.last_update > 350:
self.last_update = now
self.current_frame = (self.current_frame + 1) % len(self.standing_frame)
self.image = self.standing_frame
def jump(self):
# Jump only if standing on a Platform
self.rect.x +=1
hits = pygame.sprite.spritecollide(player,platforms, False)
self.rect.x -= 1
if hits:
self.vel.y = - player_jump
def update(self):
self.animate()
# timeout for powerups
if self.power >=2 and pygame.time.get_ticks() - self.power_time > POWERUP_TIME:
self.power -= 1
self.power_time = pygame.time.get_ticks()
# unhide if hidden
if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
self.hidden = False
self.pos = vec(30, 400)
self.acc = vec(0,player_gravity)
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT] or keystate[pygame.K_a]:
self.acc.x = -player_acceleration
if keystate[pygame.K_RIGHT] or keystate[pygame.K_d]:
self.acc.x = player_acceleration
if keystate[pygame.K_SPACE]:
player.jump()
# apply friction
self.acc.x += self.vel.x * player_friction
# equations of motions
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# wrap around the sides of the screen
if self.pos.x > 750:
self.pos.x = 750
if self.pos.x <= 0:
self.pos.x = 25
self.rect.midbottom = self.pos
def powerup(self):
self.power += 1
self.power_time = pygame.time.get_ticks()
def shoot(self):
if self.power == 1:
bullet = Bullet(self.pos.x + 5, self.pos.y - 20)
all_sprites.add(bullet)
bullets.add(bullet)
shoot_sound.play()
if self.power >= 2:
bullet1 = Bullet(self.pos.x + 5, self.pos.y - 20)
bullet2 = Bullet(self.pos.x + 35, self.pos.y -20)
all_sprites.add(bullet1)
all_sprites.add(bullet2)
bullets.add(bullet1)
bullets.add(bullet2)
shoot_sound.play()
def hide(self):
# hide the player temporarily
self.hidden = True
self.hide_timer = pygame.time.get_ticks()
self.pos = vec(0, 6000)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = scissors
self.rect = self.image.get_rect()
self.image.set_colorkey(white)
self.image = pygame.transform.scale(scissors, (30,15))
self.rect.bottom = y
self.rect.centerx = x
self.speedx = 10
def update(self):
self.rect.x += self.speedx
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
class Mobs(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = mob_left
self.rect = self.image.get_rect()
self.image.set_colorkey(white)
self.rect.x = random.randrange(0,800)
self.rect.y = 530
self.speedx = 2
def update(self):
self.rect.x -= self.speedx
if self.rect.right < 0:
self.rect.x = 800
class Explosion(pygame.sprite.Sprite):
def __init__(self, center, size, frame):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = explosion_animation[self.size][0]
self.rect = self.image.get_rect()
self.rect.center = center
self.frame = 0
self.last_update = pygame.time.get_ticks()
self.frame_rate = frame
def update(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.frame_rate:
self.last_update = now
self.frame += 1
if self.frame == len(explosion_animation[self.size]):
self.kill()
else:
center = self.rect.center
self.image = explosion_animation[self.size][self.frame]
self.rect = self.image.get_rect()
self.rect.center = center
class Normal_Power(pygame.sprite.Sprite):
def __init__(self, center):
pygame.sprite.Sprite.__init__(self)
self.type = random.choice(['shield_0','shield_1','shield_2'])
self.image = power_upper_image[self.type]
self.rect = self.image.get_rect()
self.image.set_colorkey(white)
self.rect.center = center
class Special_Power(pygame.sprite.Sprite):
def __init__(self, center):
pygame.sprite.Sprite.__init__(self)
self.type = random.choice(['life','power'])
self.image = power_upper_image[self.type]
self.rect = self.image.get_rect()
self.image.set_colorkey(white)
self.rect.center = center
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((w, h))
self.image.fill(black)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
pygame.mixer.music.play(loops=-1) # loops = -1 means that pygame will restart the song when it's finished
# Game loop
running = True
new_game = True
game_over = False
while running:
if new_game:
show_start_screen()
new_game = False
all_sprites = pygame.sprite.Group()
platforms = pygame.sprite.Group()
for plat in platforms_list:
p = Platform (*plat)
all_sprites.add(p)
platforms.add(p)
mobs = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
bullets = pygame.sprite.Group()
powerups = pygame.sprite.Group()
for i in range(1):
newmob()
score = 0
if game_over:
show_game_over_screen()
game_over = False
all_sprites = pygame.sprite.Group()
platforms = pygame.sprite.Group()
for plat in platforms_list:
p = Platform (*plat)
all_sprites.add(p)
platforms.add(p)
mobs = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
bullets = pygame.sprite.Group()
powerups = pygame.sprite.Group()
for i in range(1):
newmob()
score = 0
# Keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_s:
player.shoot()
# Updates
all_sprites.update()
# check if player hits a platform - only if falling
if player.vel.y > 0:
hits = pygame.sprite.spritecollide(player,platforms,False)
if hits:
player.pos.y = hits[0].rect.top
player.vel.y = 0
# check to see if a bullet hit a mob
hits = pygame.sprite.groupcollide(mobs,bullets,True,True)
for hit in hits:
score += 1
random.choice(explosion_sound).play()
expl = Explosion(hit.rect.center, 'normal', 50)
all_sprites.add(expl)
if random.random() > 0.75:
power = Normal_Power(hit.rect.center)
all_sprites.add(power)
powerups.add(power)
if random.random() > 0.90:
lives = Special_Power(hit.rect.center)
all_sprites.add(lives)
powerups.add(lives)
newmob()
# check to see if the mob hit the player
hits = pygame.sprite.spritecollide(player, mobs,True)
for hit in hits:
random.choice(explosion_sound).play()
player.shield -= 25
newmob()
expl = Explosion(hit.rect.center, 'small', 50)
all_sprites.add(expl)
if player.shield <= 0:
death_sound.play()
death_animation = Explosion(player.rect.center, 'player', 100)
all_sprites.add(death_animation)
player.hide()
player.lives -= 1
player.shield = 100
else:
random.choice(scream_sound).play()
# check if the player hit a powerup
hits = pygame.sprite.spritecollide(player, powerups, True)
for hit in hits:
if hit.type == 'shield_0':
player.shield += 5
if player.shield >= 100:
player.shield = 100
shield.play()
if hit.type == 'shield_1':
player.shield += 20
if player.shield >= 100:
player.shield = 100
shield.play()
if hit.type == 'shield_2':
player.shield += 20
if player.shield >= 100:
player.shield = 100
shield.play()
if hit.type == 'life':
player.lives += 1
if player.lives >= 3:
player.lives = 3
life.play()
if hit.type == 'power':
special_power.play()
player.powerup()
# if the player died and the explosion finished playing
if player.lives == 0 and not death_animation.alive():
game_over = True
# Draw / Render
screen.fill(white)
screen.blit(background, background_rect)
all_sprites.draw(screen)
draw_text(screen,str(score),red,30, width/ 2, 30)
draw_text(screen,"Score:",red,30, width / 2, 3)
draw_shield_bar(screen,90,20, player.shield)
draw_lives(screen,95,40,player.lives, mini_no_mvmt)
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
quit()
The error is caused by a sprite in your all_sprites group that has a list as its self.image attribute. I've just printed the sprites before the all_sprites.draw(screen) line
for sprite in all_sprites:
print(sprite, sprite.image)
and it was the player sprite which had a list as its image.
<Player sprite(in 1 groups)> [<Surface(40x25x32 SW)>, <Surface(40x25x32 SW)>]
In the load_movement_images method you define self.standing_frame as a list of two images/pygame.Surfaces and in __init__ you set self.image to the first item of that list. But in the animate method you set self.image to the whole list instead of the active image self.image = self.standing_frame and that leads to the error.
class Player (pygame.sprite.Sprite):
# Sprite for the player
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.load_movement_images()
# self.image is the first image in the
# self.standing_frame list.
self.image = self.standing_frame[0]
# ...
def load_movement_images(self):
self.standing_frame = [no_mvmt_0, no_mvmt_1]
# ...
def animate(self):
now = pygame.time.get_ticks()
if not self.jumping and not self.running:
if now - self.last_update > 350:
self.last_update = now
self.current_frame = (self.current_frame + 1) % len(self.standing_frame)
# Here you set the self.image to the self.standing_fram list instead one image.
self.image = self.standing_frame
# It should be:
# self.image = self.standing_frame[self.current_frame]