Pygame sprite assistance - python

I've basically just started developing with PyGame and I am having trouble with the whole Sprite concept. I have been looking every where for guides on how to use it, I just can't seem to find any. I would like to know the basic concept of how it all works. This is the code I have been working on:
#!/usr/bin/python
import pygame, sys
from pygame.locals import *
size = width, height = 320, 320
clock = pygame.time.Clock()
xDirection = 0
yDirection = 0
xPosition = 32
yPosition = 256
blockAmount = width/32
pygame.init()
screen = pygame.display.set_mode(size)
screen.fill([0, 155, 255])
pygame.display.set_caption("Mario Test")
background = pygame.Surface(screen.get_size())
mainCharacter = pygame.sprite.Sprite()
mainCharacter.image = pygame.image.load("data/character.png").convert()
mainCharacter.rect = mainCharacter.image.get_rect()
mainCharacter.rect.topleft = [xPosition, yPosition]
screen.blit(mainCharacter.image, mainCharacter.rect)
grass = pygame.sprite.Sprite()
grass.image = pygame.image.load("data/grass.png").convert()
grass.rect = grass.image.get_rect()
for i in range(blockAmount):
blockX = i * 32
blockY = 288
grass.rect.topleft = [blockX, blockY]
screen.blit(grass.image, grass.rect)
grass.rect.topleft = [64, 256]
screen.blit(grass.image, grass.rect.topleft )
running = False
jumping = False
falling = False
standing = True
jumpvel = 22
gravity = -1
while True:
for event in pygame.event.get():
if event.type == KEYDOWN and event.key == K_ESCAPE:
pygame.quit()
sys.exit()
elif event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
running = True
xRun = -5
elif event.key == K_RIGHT:
running = True
xRun = 5
elif event.key == K_UP or event.key == K_SPACE:
jumping = True
elif event.type == KEYUP:
if event.key == K_LEFT or event.key == K_RIGHT:
running = False
if running == True:
xPosition += xRun
if mainCharacter.rect.right >= width:
xPosition = xPosition - 10
print "hit"
running = False
elif mainCharacter.rect.left <= 0:
xPosition = xPosition + 10
print "hit"
running = False
screen.fill([0, 155, 255])
for i in range(blockAmount):
blockX = i * 32
blockY = 288
grass.rect.topleft = [blockX, blockY]
screen.blit(grass.image, grass.rect)
grass.rect.topleft = [64, 64]
screen.blit(grass.image, grass.rect.topleft )
if jumping:
yPosition -= jumpvel
print jumpvel
jumpvel += gravity
if jumpvel < -22:
jumping = False
if mainCharacter.rect.bottom == grass.rect.top:
jumping = False
if not jumping:
jumpvel = 22
mainCharacter.rect.topleft = [xPosition, yPosition]
screen.blit(mainCharacter.image,mainCharacter.rect)
clock.tick(60)
pygame.display.update()
basically I just want to know how to make these grass blocks into sprites in a group, so that when I add my player (also a sprite) I can determine whether or not he is in the air or not through the collision system. Can someone please explain to me how I would do this. All I am looking for is basically a kick starter because some of the documentation is quite bad in my opinion as it doesn't completely tell you how to use it. Any help is greatly appreciated :)

In Pygame, sprites are very minimal. They consist of two main parts: an image and a rect. The image is what is displayed on the screen and the rect is used for positioning and collision detection. Here's an example of how your grass image could be made into a Sprite:
grass = pygame.image.load("grass.png")
grass = grass.convert_alpha()
grassSprite = new pygame.sprite.Sprite()
grassSprite.image = grass
#This automatically sets the rect to be the same size as your image.
grassSprite.rect = grass.get_rect()
Sprites are pretty pointless on their own, since you can always keep track of images and positions yourself. The advantage is in using groups. Here's an example of how a group might be used:
myGroup = pygame.sprite.Group()
myGroup.add([sprite1,sprite2,sprite3])
myGroup.update()
myGroup.draw()
if myGroup.has(sprite2):
myGroup.remove(sprite2)
This code creates a group, adds three sprites to the group, updates the sprites, draws them, checks to see if sprite2 is in the group, then removes the sprite. It is mostly straight forward, but there are some things to note:
1) Group.add() can take either a single sprite or any iterable collection of sprites, such as a list or a tuple.
2) Group.update() calls the update methods of all the Sprites it contains. Pygame's Sprite doesn't do anything when its update method is called; however, if you make a subclass of Sprite, you can override the update method to make it do something.
3) Group.draw() blits the images of all the Group's Sprites to the screen at the x and y positions of their rects. If you want to move a sprite, you change its rect's x and y positions like so: mySprite.rect.x = 4 or mySprite.rect.y -= 7
One way to use Groups is to create a different group for each level. Then call the update and draw method of whichever group represents the current level. Since nothing will happen or be displayed unless those methods are called, all other levels will remain "paused" until you switch back to them. Here's an example:
levels = [pygame.sprite.Group(),pygame.sprite.Group(),pygame.sprite.Group()]
levels[0].add(listOfLevel0Sprites)
levels[1].add(listOfLevel1Sprites)
levels[2].add(listOfLevel2Sprites)
currentLevel = 0
while(True):
levels[currentLevel].update()
levels[currentLevel].draw()
I know this question is somewhat old, but I hope you still find it helpful!

Related

how do you create multiple rectangles and make them move independently

I am trying to create a flappy bird game with python but i cant get multiple walls to appear on the page and move across.
I was wondering if there was a way to make multiple walls (rectangles) and move them without defining them individually. Im using Pygame.
import pygame
pygame.init()
white = (255,255,255)
yellow = (255,200,0)
green = (0,255,0)
displayHeight = 600
displayWidth = 500
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
clock = pygame.time.Clock()
crash = False
def bird(b_X, b_Y, b_R, b_Colour):
pygame.draw.circle(gameDisplay, b_Colour, (b_X, b_Y), b_R)
def game_loop():
bX = 50
bY = 300
bR = 20
bColour = yellow
velocity = 0
gravity = 0.6
lift = -15
while crash == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.QUIT
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
velocity += lift
gameDisplay.fill(white)
bird(bX, bY, bR, bColour)
velocity = velocity + gravity
bY += velocity
bY = round(bY)
if bY > displayHeight:
bY = displayHeight
velocity = 0
if bY < 0:
bY = 0
velocity = 0
pygame.display.update()
clock.tick(60)
game_loop()
pygame.quit()
quit()
Is there a way to make a big population of blocks and move them individually without defining them one by one.
I do not know how the code exactly works out, but you can store multiple wall 'objects' in a list, and looping over that list every time you update the screen. In pseudocode, it works like this
wall-list = []
for wanted_wall_amount in range():
wall = create_a_wall
wall-list.append(wall)
game loop:
for wall in wall-list:
wall.x_coordinate += movespeed
because of the for loop within the game loop, every wall object you stored will update it's position, or whatever you want to move simultanously. I hope you understand the weird pseudocode

Creating multiple instances of bouncing ball pygame

So I am new to pygame and I made a program that will make a ball bounce according to gravity, and it works perfectly but now I want to be able to make infinite of them by creating one wherever I click. I know how to get the coordinates of the click, but I don't know how to create multiple circles. Would I somehow use a function or a class? Here's my code, sorry if it's a little messy.
import pygame,random,time
pygame.init()
infoObject = pygame.display.Info()
side = pygame.display.Info().current_h
side = side - ((1.0/12)*(pygame.display.Info().current_h))
side = int(side)
screen = pygame.display.set_mode([side, side])
pygame.display.set_caption("")
clock = pygame.time.Clock()
done = False
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (250,30,25)
GREEN = (25, 240, 25)
YELLOW = (250,240,25)
FPS = 60
x=100
y=100
t=0
r=10
direction=0
count = 0
def faller(x,y,color,size):
pygame.draw.circle(screen,color,(x,y),size,0)
def reverse():
global t,direction
if direction == 0:
direction = 1
t=t*4/5
elif direction == 1:
direction = 0
t=1
while not done:
clock.tick(FPS)
events = pygame.event.get()
screen.fill(BLACK)
for event in events:
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
done = True
if direction==0:
t+=1
y+=int((9.8/FPS)*t)
elif direction==1:
t-=1
y-=int((9.8/FPS)*t)
if t==0:
reverse()
if y+r>side:
y=side-r
faller(x,y,RED,r)
reverse()
else:
faller(x,y,RED,r)
if y==side-r:
count+=1
else:
count=0
if count>=3:
done = True
pygame.display.flip()
pygame.quit()
I would encourage you to experiment with classes and methods. Once you have a class Ball, you can make a list of the Ball objects and process them all at once. You could then use the .append() list method to add a new Ball on mouse click.
I would suggest you use variables such as:
pos_x,
pos_y,
vel_x,
vel_y,
This will make things much easier to understand if you wanted to implement collisions. Dont forget to comment your code :)

Testing for pygame sprite collision

I have been going through some pygame tutorials, and I have developed my own code based off of these tutorials. It is as follows:
#!/usr/bin/python
import pygame, sys
from pygame.locals import *
size = width, height = 320, 320
clock = pygame.time.Clock()
xDirection = 0
yDirection = 0
xPosition = 32
yPosition = 256
blockAmount = width/32
pygame.init()
screen = pygame.display.set_mode(size)
screen.fill([0, 155, 255])
pygame.display.set_caption("Mario Test")
background = pygame.Surface(screen.get_size())
mainCharacter = pygame.sprite.Sprite()
mainCharacter.image = pygame.image.load("data/character.png").convert()
mainCharacter.rect = mainCharacter.image.get_rect()
mainCharacter.rect.topleft = [xPosition, yPosition]
screen.blit(mainCharacter.image, mainCharacter.rect)
grass = pygame.sprite.Sprite()
grass.image = pygame.image.load("data/grass.png").convert()
grass.rect = grass.image.get_rect()
for i in range(blockAmount):
blockX = i * 32
blockY = 288
grass.rect.topleft = [blockX, blockY]
screen.blit(grass.image, grass.rect)
grass.rect.topleft = [64, 256]
screen.blit(grass.image, grass.rect.topleft )
running = False
jumping = False
falling = False
standing = True
jumpvel = 22
gravity = -1
while True:
for event in pygame.event.get():
if event.type == KEYDOWN and event.key == K_ESCAPE:
pygame.quit()
sys.exit()
elif event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
running = True
xRun = -5
elif event.key == K_RIGHT:
running = True
xRun = 5
elif event.key == K_UP or event.key == K_SPACE:
jumping = True
elif event.type == KEYUP:
if event.key == K_LEFT or event.key == K_RIGHT:
running = False
if running == True:
xPosition += xRun
if mainCharacter.rect.right >= width:
xPosition = xPosition - 10
print "hit"
running = False
elif mainCharacter.rect.left <= 0:
xPosition = xPosition + 10
print "hit"
running = False
screen.fill([0, 155, 255])
for i in range(blockAmount):
blockX = i * 32
blockY = 288
grass.rect.topleft = [blockX, blockY]
screen.blit(grass.image, grass.rect)
grass.rect.topleft = [64, 64]
screen.blit(grass.image, grass.rect.topleft )
if jumping:
yPosition -= jumpvel
print jumpvel
jumpvel += gravity
if jumpvel < -22:
jumping = False
if mainCharacter.rect.bottom == grass.rect.top:
jumping = False
if not jumping:
jumpvel = 22
mainCharacter.rect.topleft = [xPosition, yPosition]
screen.blit(mainCharacter.image,mainCharacter.rect)
clock.tick(60)
pygame.display.update()
So I have figured out how to make my dude jump, however I have placed a block in the sky and tried to do this:
if mainCharacter.rect.bottom == grass.rect.top:
jumping = False
to attempt to make the character stop jumping. Is there a built in function to test this or perhaps a way that works in my case. This doesn't work by the way, and I can't seem to figure out how to do it. Any help is greatly appreciated :)
The problem with your code is that your character is moving more than one pixel each step. For example, when moving at maximum velocity, your character moves 22 pixels each step. So if he's 10 pixels above the grass one step, he'll be 12 pixels below the grass on the next step. To fix this, you test to see whether the character is touching or below the grass, like so:
if mainCharacter.rect.bottom <= grass.rect.top:
jumping = False
Also, Pygame does have a built-in function to test collision detection:
rect1.colliderect(rect2) will return True if rect1 and rect2 are colliding, and will return False otherwise. However, it is slightly more costly than the aforementioned code, and it may cause the same problem if your character is moving so fast that he passes through the grass completely.

Pygame spritecollide error

I have a few questions about pygame. I am completely new to python/pygame and curious if for one, I am doing this properly, or if I am writing this sloppy.
And for my other question, when I use spritecollide, object seems to still be there even after the image disappears. Let me share the code
import pygame, time, random, sys, player, creep, weapon
from pygame.locals import *
pygame.init()
#Variables for the game
width = 700
height = 500
clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height), 0, 32)
pygame.display.set_caption('Creep')
#Create Characters of the game
player1 = player.Player()
player1.rect.x = 0
player1.rect.y = 0
comp = creep.Creep()
comp.rect.x = random.randrange(width)
comp.rect.y = random.randrange(height)
bullet = weapon.Weapon()
bullet.rect.x = -1
bullet.rect.y = -1
#Make Character Groups
good = pygame.sprite.Group(player1)
bad = pygame.sprite.Group(comp)
weap = pygame.sprite.Group(bullet)
while True:
clock.tick(60)
screen.fill((0,0,0))
#set up for game to get input
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
if event.type == KEYDOWN and event.key == K_c:
bullet.rect.x = player1.rect.x + 25
bullet.rect.y = player1.rect.y
#main controls
key = pygame.key.get_pressed()
if key[K_RIGHT]:
player1.rect.x = player1.moveRight(player1.rect.x)
if key[K_LEFT]:
player1.rect.x = player1.moveLeft(player1.rect.x)
if key[K_DOWN]:
player1.rect.y = player1.moveDown(player1.rect.y)
if key[K_UP]:
player1.rect.y = player1.moveUp(player1.rect.y)
if bullet.rect.x > -1:
weap.draw(screen)
bullet.rect.x = bullet.rect.x +5
pygame.sprite.spritecollide(bullet, bad, True)
pygame.sprite.spritecollide(comp, good, True)
#game functions
good.draw(screen)
bad.draw(screen)
pygame.display.flip()
So I have an image of a gun (player1, 'good' group), an image for the computer (comp, 'bad' group), and an image for a "bullet" when LCTRL is hit (bullet, 'weap' group).. when the bullet hits the image from the bad group, it disappears, which is what I want. But then when I move the player1 image in that direction, it will disappear as if the 'bad group' was still there. I hope this makes sense.
An example code of the classes I am calling on look like this:
import pygame
class Creep(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('creep.jpg')
self.rect = self.image.get_rect()
Any idea? and if there is a better way of going at this please let me know, I only started learning a week ago, and don't know if I am going in the proper direction, or not. Thanks!
I wasn't sure what some of your variables were. Note: If you put a sprite in multiple groups, then kill it, it will kill it in all groups automatically.
I started cleaning up the code.
#import time, sys, # not longer required
import player, creep, weapon
import random
import pygame
from pygame.locals import *
pygame.init()
#Variables for the game
width = 700
height = 500
clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height), 0, 32)
pygame.display.set_caption('Creep')
#Create Characters of the game
player1 = player.Player()
player1.rect.x = 0
player1.rect.y = 0
comp = creep.Creep()
comp.rect.topleft = random.randrange(width), random.randrange(height)
bullet = pygame.sprite.Sprite()# weapon.Weapon()
bullet.rect.topleft = (-1, -1)
#Make Character Groups
good = pygame.sprite.Group(player1)
bad = pygame.sprite.Group(comp)
weap = pygame.sprite.Group(bullet)
done = False
while not done:
clock.tick(60)
#set up for game to get input
for event in pygame.event.get():
if event.type == pygame.QUIT: done = True
if event.type == KEYDOWN:
if event.key == K_ESCAPE: done = True
elif event.key == K_c:
bullet.rect.x = player1.rect.x + 25
bullet.rect.y = player1.rect.y
#main controls
key = pygame.key.get_pressed()
if key[K_RIGHT]:
player.rect.x += 10
if key[K_LEFT]:
player1.rect.x -= 10
if key[K_DOWN]:
player.rect.y += 10
if key[K_UP]:
player.rect.y -= 10
# movement, collisions
pygame.sprite.spritecollide(bullet, bad, True)
pygame.sprite.spritecollide(comp, good, True)
# not sure what this was for? If you meant 'onscreen' or?
# You can kill it if it goes offscreen. Otherwise draw works if offscreen.
if bullet.rect.x > -1:
bullet.rect.x += 5
screen.fill(Color("black"))
weap.draw(screen)
#game functions
good.draw(screen)
bad.draw(screen)
pygame.display.flip()

Program detecting collision even though sprites aren't literally colliding 'pygame.sprite.collide_rect' [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed last month.
My goal is for my program to be able to detect collision when the ball sprite hits any one of the 'g_ball' sprites. The code apperently detects collision, and i have the "print" statement to test it...but it's CONSTANTLY printing "progress" even though none of the sprites are touching. Here is the code:
while 1:
screen.blit(background, (0,0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_a:
m_x = -4
s+=1
elif event.key == K_d:
m_x = +4
s2+=1
if event.type == KEYUP:
if event.key == K_a:
m_x = 0
elif event.key == K_d:
m_x = 0
x+= m_x
y+= m_y
ball = pygame.sprite.Sprite()
ball.image = pygame.image.load('red_ball.png').convert()
ball.rect = ball.image.get_rect()
ball.image.set_colorkey((white))
screen.blit(ball.image,(x,y))
if x > 640:
x = 0
if x < 0:
x = 640
g_ball = pygame.sprite.Sprite()
g_ball.image = pygame.image.load('green_ball.png').convert()
g_ball.rect = g_ball.image.get_rect()
g_ball.image.set_colorkey(white)
screen.blit(g_ball.image,(50,t))
t+=5
if t > 521:
t = 0
collision = pygame.sprite.collide_rect(ball, g_ball)
if collision == True:
print ('PROGRESS!!!')
It's because you have no offset set for the collision, you have only passed the offset to screen.blit.
Your fixed code would be this:
...
ball.rect = ball.image.get_rect()
ball.rect.topleft = (x, y)
...
g_ball.rect = g_ball.image.get_rect()
g_ball.rect.topleft = (50, t)
...
nightcracker is right. Do you realize what you are doing in this loop (which is supposed to run as fast as possible)? you are creating 2 new balls, you load the image from hd, you place them to (0,0), and then you print them manually on screen at a given position. This last part means you display them somewhere, but not where they really are (you did set their real position with ball.rect=ball.image.get_rect()). They really are at (0,0), and are colliding all the time.
The way you blit them to screen isn't good, you should use a renderer. Anyway you should probably try some tutorials first, learn what is a Surface and what is a Sprite. Be careful of what you put inside the main loop (why do you create new balls all the time ? they could be created once at startup), your code will be prettier, and your FPS will increase

Categories

Resources