I have been writing this code recently and I wanted collision-detection but I have never done it before and I need help. this code is written with python and pygame so it should be simple but I'm not sure whether I should have the whole world as a transparent image
import pygame, os, itertools
from pygame.locals import *
w = 640
h = 480
pink = (0,179,179)
player_x = 39
player_y = 320
def setup_background():
screen.fill((pink))
screen.blit(playerImg, (player_x,player_y))
screen.blit(brick_tile, (0,0))
pygame.display.flip()
pygame.init()
screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()
playerImg = pygame.image.load('img/player.png').convert_alpha()
brick_tile = pygame.image.load('img/map.png').convert_alpha()
class Player(pygame.sprite.Sprite):
allsprites = pygame.sprite.Group()
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('img/player.png')
self.rect = self.image.get_rect()
class World(pygame.sprite.Sprite):
allsprites = pygame.sprite.Group()
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('img/map.png')
self.rect = self.image.get_rect()
player = Player()
world = World()
done = False
while not done:
setup_background()
block_hit_list = pygame.sprite.spritecollide(player, world.allsprites, True)
for world in block_hit_list:
print("WORKING")
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == K_RIGHT:
player_x += 5
Your sprite collision detection is almost there but incorrect. You seem to know the basics of this concept in this line:
block_hit_list = pygame.sprite.spritecollide(player, world.allsprites, True)
You are almost there, but here is how you do it. In a pygame.sprite.spritecollide() function, you need the sprite's name, the group's name of the other sprite, and either True or False. In your case, you just need to make a group for your world (assuming you want the things in that order) :
world_group = pygame.sprite.Group(world)
This line is necessary for your detection code, and will enable that sprite to be removed. If you want to add another world to that group, so you have plenty of worlds, do this and add some code around it if you want to:
world_group.add(world)
The add() function will add a sprite to that group. I recommend you do a loop to make lots of them if necessary. Now to the collision code!
Now we are ready to do this function correctly. I would delete this line first:
block_hit_list = pygame.sprite.spritecollide(player, world.allsprites, True)
That would be unnecessary and useless for now. You will need to add this if statement in the while loop but outside your for loop:
if pygame.sprite.spritecollide(player, world_group, x):
#Do something
Why did I put x instead of True or False? Because that will be your decision. If you want to delete the sprite in the group after contact, replace x with True. If you don't want to do that, replace x with False. These are the basics of collision detection code, and I hope this helps you!
Related
import pygame, sys
start = True
class Player(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.attack_animation = False
self.sprites = []
self.sprites.append(pygame.image.load('crossHair.png'))
self.sprites.append(pygame.image.load('crossHair_2.png'))
self.sprites.append(pygame.image.load('crossHair_3.png'))
self.sprites.append(pygame.image.load('crossHair_4.png'))
self.current_sprite = 0
self.image = self.sprites[self.current_sprite]
self.image.set_colorkey('white')
for items in self.sprites:
items.set_colorkey('white')
self.rect = self.image.get_rect()
self.rect.topleft = [pos_x,pos_y]
def attack(self):
self.attack_animation = True
self.image.set_colorkey('white')
def update(self,speed):
self.image.set_colorkey('white')
self.current_sprite += speed
if int(self.current_sprite) >= len(self.sprites):
self.attack_animation = False
self.current_sprite = 0
self.image = self.sprites[int(self.current_sprite)]
# General setup
pygame.init()
clock = pygame.time.Clock()
# Game Screen
screen_width = 400
screen_height = 400
mouse = pygame.mouse.get_pos()
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Sprite Animation")
# Creating the sprites and groups
moving_sprites = pygame.sprite.Group()
while True:
globals()['mouse'] = pygame.mo[![now this is the problem][1]][1]use.get_pos()
player = Player(mouse[0],mouse[1])
moving_sprites.add(player)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
player.attack()
# Drawing
screen.fill((0,0,0))
screen.set_colorkey('white')
moving_sprites.draw(screen)
moving_sprites.update(0.04)
pygame.display.flip()
clock.tick(120)
this is creatiing my player / crossHair every 120th of a second and it leaves behind it's last sprite
now the problem with that is it leaves behind my last sprite and if i put
moving_sprites = pygame.sprite.Group()
in while loop then it won't animate anybody can solve this pls answer my question...
This is not the way to animate sprites. Creating a new sprite every frame is bad practice and a waste of performance since all objects have to be recreated. Even more you load the images in the constructor of the Player class. If you do this every frame, then every frame the images have to be loaded from the volume and decoded.
Create the sprite once before the application loop and change the attributes of the sprite object in the loop. This will give you the best performance:
class Player(pygame.sprite.Sprite):
# [...]
def set_pos(self, pos_x, pos_y):
self.rect.topleft = (pos_x, pos_y)
moving_sprites = pygame.sprite.Group()
player = Player(0, 0)
moving_sprites.add(player)
while True:
player.set_pos(*pygame.mouse.get_pos())
# [...]
I can't see what's wrong with the below code. All I want to do is make the frog move across the screen, but it is simply redrawing many, many frogs all one pixel apart. How do I move the frog rather than just draw it again?
import pygame
from pygame.constants import *
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
class Frog(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.transform.scale(pygame.image.load('frog.png'), (64, 64))
self.rect = self.image.get_rect()
self.dx = 1
def update(self, *args):
self.rect.x += self.dx
running = True
frog = Frog()
entities = pygame.sprite.Group()
entities.add(frog)
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
entities.update()
entities.draw(screen)
That is how you do it in Pygame, you just redraw objects every iteration to give the illusion that they're moving but you must cover up the previous drawn objects by filling your window with a solid color e.g.
screen.fill((255, 255, 255))
This should be at the start of your game loop so you have a fresh canvas for drawing your objects each iteration.
while running:
screen.fill((255,255,255))
for event in pygame.event.get():
if event.type == QUIT:
running = False
entities.update()
entities.draw(screen)
pygame.display.update()
You may have to use the pygame.display.update() function to update the whole screen rather than just your entities.
Please help me I am starting a game and my sprite is not showing on screen. Take a look, I am using two files, which include pygame and classes. I hope that's enough information.
Adventure.py --
import pygame, random
pygame.init()
BROWN = (205,192,176)
DEEPBROWN = (139,131,120)
CL = (156,102,31)
from LittleMan import LittleMan
playerSprite = LittleMan(CL, 200, 300)
size = (1000, 600)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Adventure")
all_sprites_list = pygame.sprite.Group()
playerSprite.rect.x = 200
playerSprite.rect.y = 300
carryOn = True
clock = pygame.time.Clock()
while carryOn:
for event in pygame.event.get():
screen.fill(BROWN)
pygame.draw.rect(screen, DEEPBROWN, [55, 250, 900, 70],0)
all_sprites_list.draw(screen)
all_sprites_list.add()
all_sprites_list.update()
pygame.display.flip()
clock.tick(60)
if event.type == pygame.QUIT:
carryOn = False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_x: #Pressing the x Key will quit the game
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
LittleMan.moveLeft(5)
if keys[pygame.K_RIGHT]:
LittleMan.moveRight(5)
LitlleMan.py --
import pygame
CL = (156,102,31)
WHITE = (255,255,255)
class LittleMan (pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
self.image = pygame.Surface([50, 75])
self.image.fill(CL)
self.image.set_colorkey(WHITE)
pygame.draw.rect(self.image, CL, [0, 0, width, height])
self.rect = self.image.get_rect()
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
Anyone know why this could be? I've looked everywhere but I've done it in two files and no-one seems to have an answer to that and if there is a decent answer please link it. Thank you.
I think the real crux of the problem is the code is not adding playerSprite to the all_sprites_list. If a sprite is not in this list, the sprite update and paint calls do not include it. At first I thought the initial position of the sprite may be off-screen, so I parameterised the screen dimensions, and positioned the sprite in the middle.
There's a bunch of other indentation issues in the question's code too, but I think these may be from pasting the question into SO.
I cleaned-up and re-organised the code, it seems to run, and pressing Left/right moves the brown box.
I merged both files together to make my debugging easier, my apologies.
import pygame, random
pygame.init()
BROWN = (205,192,176)
DEEPBROWN = (139,131,120)
CL = (156,102,31)
WHITE = (255,255,255)
WINDOW_WIDTH=500
WINDOW_HEIGHT=500
# Setup the pyGame window
size = (WINDOW_WIDTH, WINDOW_HEIGHT)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Adventure")
class LittleMan (pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
self.image = pygame.Surface([50, 75])
self.image.fill(CL)
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.rect.center = ( WINDOW_WIDTH//2 , WINDOW_HEIGHT//2 )
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
# Create the player sprite
playerSprite = LittleMan(CL, 200, 300)
# Add user sprite into PyGame sprites list
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(playerSprite);
clock = pygame.time.Clock()
carryOn = True
while carryOn:
# Handle user input
for event in pygame.event.get():
if event.type == pygame.QUIT:
carryOn = False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_x: #Pressing the x Key will quit the game
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
playerSprite.moveLeft(5)
if keys[pygame.K_RIGHT]:
playerSprite.moveRight(5)
# Update and Reapint the screen
screen.fill(BROWN)
pygame.draw.rect(screen, DEEPBROWN, [55, 250, 900, 70],0)
all_sprites_list.update()
all_sprites_list.draw(screen)
pygame.display.flip()
clock.tick(60)
The LittleMan class does not include an update() function, which all_sprites_list.update() would normally call. I expect you just haven't needed this part yet.
EDIT: More notes on the sprite update() function ~
The sprite's update() function is called by pygame during the all_sprites_list.update() function. So that means any sprite added to this group, has its update run quasi-automatically. Ideally all sprites have an update function, and it handles the look, position and collisions (etc.) of the sprite.
The idea behind this function is to do any updates to the sprite. So of you had a sprite that was moving, this function would calculate the next position, and set the sprite's self.rect. Or perhaps that sprite is animated - the update function would set the sprite's image to the next frame of the animation based on the time.
Obviously all this work can be performed outside of the update function. But it provides a simple and clean programming mechanism for sprite mechanics.
For example, a projectile flies off screen, does the program still compute its location, speed, etc.?
If so, how to release it?
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()
sprite = pygame.image.load(sprite_image_filename)
x = 0.
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.blit(background, (0,0))
screen.blit(sprite, (x, 100))
x+= 10.
pygame.display.update()
Yes, the location, speed, etc. still have to be computed, otherwise no object that is off-screen could ever enter the screen area again. Pygame is smart enough not to attempt to render these objects.
It's usually advisable to use pygame sprites and sprite groups which allow you to remove sprites simply by calling self.kill(). You could also use lists or sets to store your objects, but then you have to write a bit more code yourself.
So I'd first define a pygame.Rect (the game_area) with the size of your screen or a bit larger (in the example below I use a smaller one). Rects have a contains method that you can use to check if your sprite's rect is inside the game_area rect. If the sprite is outside, just call self.kill() and pygame will remove the sprite from all associated sprite groups.
import random
import pygame as pg
from pygame.math import Vector2
class Projectile(pg.sprite.Sprite):
def __init__(self, pos, game_area):
super().__init__()
self.image = pg.Surface((5, 5))
self.image.fill(pg.Color('aquamarine2'))
self.rect = self.image.get_rect(center=pos)
self.vel = Vector2(2, 0).rotate(random.randrange(360))
self.pos = Vector2(pos)
self.game_area = game_area
def update(self):
self.pos += self.vel
self.rect.center = self.pos
if not self.game_area.contains(self.rect):
self.kill()
def main():
screen = pg.display.set_mode((640, 480))
game_area = pg.Rect(60, 60, 520, 360)
game_area_color = pg.Color('aquamarine2')
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(Projectile(game_area.center, game_area))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.add(Projectile(game_area.center, game_area))
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.draw.rect(screen, game_area_color, game_area, 2)
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Yes it does, but since you have only a single projectile (incremented using x), you can easily choose what to do using a few if statements. The process becomes harder when there are multiple projectiles (which you need to store in a container), you should apply this.
Here is an example
for projectile in projectile_list:
# Check if the position is inside the screen
if 0 < projectile.x < WIDTH and 0 < projectile.y < HEIGHT:
# Do position operations
This way, you only process what is required. You can apply similar method to remove unused projectiles from the list or whatever container you are using.
This is my code so far, I can move and it places out a blip to pick up, I just need to know how to register that and move the blip to a new random spot!
I am very new to pygame and not 100% fluent in python either, but I'm decent. If there are pdf:s good for intermediate coders when it comes to python that would be wonderful!
import sys, pygame, os, math
from random import randint
pygame.init()
size = width, height = 800, 600
speed = [2, 2]
black = 1, 1, 1
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Pick up the squares!')
UP='up'
DOWN='down'
LEFT='left'
RIGHT='right'
ball = pygame.image.load("ball.png")
ballrect = ball.get_rect()
ballx = 400
bally = 300
blip = pygame.image.load("blip.png")
bliprect = blip.get_rect()
blipx = randint(1,800)
blipy = randint(1,600)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
clock = pygame.time.Clock()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ballx -= 5
if keys[pygame.K_RIGHT]:
ballx += 5
if keys[pygame.K_UP]:
bally -= 5
if keys[pygame.K_DOWN]:
bally +=5
screen.fill(black)
screen.blit(ball, (ballx,bally))
screen.blit(blip, (blipx, blipy))
pygame.display.update()
clock.tick(40)
Use the colliderect method of rectangles:
if ballrect.colliderect(bliprect):
print 'Do something here'
A basic collision detection works like this (assuming you are working with two rectangles):
def does_collide(rect1, rect2):
if rect1.x < rect2.x + rect2.width and rect1.x + rect1.width > rect2.x \
and rect1.y < rect2.y + rect2.height and rect1.height + rect1.y > rect2.y:
return True
return False
Fortunately Pygame is packed with such methods, so you should go with #MalikBrahimi's answer - using colliderect function call, which will do the math for you.
You can also try using pygame.sprite.spritecollide():
if pygame.sprite.spritecollide(a, b, False):
pass
#Do something
a here is your variable name for the class for one of the sprites in the collision. b here is the group that your second sprite will be. You can set the last one to True, which will remove the sprite from the group. Setting to False will keep it the way it is. Here is an example:
if pygame.sprite.spritecollide(my_ball, ballGroup, False):
Ball.speed[0] = -Ball.speed[0]
hit.play()
Ball.move()
The variable name for my sprite is my_ball. The group containing the other sprite(s) in this collision is ballGroup. I set the last one to False to keep all the sprites on the surface. If I set to True, the sprite from ballGroup will be removed from the group and screen. I hope this helps you!