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.
Related
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.
I have been trying to modify the code from this Tutorial so that after a bullet strikes an enemy the player.png image is shown at position x = 60 and y = 48. But the image does not remain fixed, it just appears and disappears. I don't know exactly where the wrong or missing element is in the code but I suspect that one of the causes is some misuse of my part of the for loop within the draw_reaction function.
My player.png image
My MWE code:
import pygame
import random
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# --- Classes
class Block(pygame.sprite.Sprite):
""" This class represents the block. """
def __init__(self, color):
# Call the parent class (Sprite) constructor
super().__init__()
self.image = pygame.Surface([20, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
""" This class represents the Player. """
def __init__(self):
""" Set up the player on creation. """
# Call the parent class (Sprite) constructor
super().__init__()
self.image = pygame.Surface([20, 20])
self.image.fill(RED)
self.rect = self.image.get_rect()
def update(self):
""" Update the player's position. """
# Get the current mouse position. This returns the position
# as a list of two numbers.
pos = pygame.mouse.get_pos()
# Set the player x position to the mouse x position
self.rect.x = pos[0]
class Bullet(pygame.sprite.Sprite):
""" This class represents the bullet . """
def __init__(self):
# Call the parent class (Sprite) constructor
super().__init__()
self.image = pygame.Surface([4, 10])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
def update(self):
""" Move the bullet. """
self.rect.y -= 3
# --- Create the window
# Initialize Pygame
pygame.init()
# Set the height and width of the screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
# --- Sprite lists
# This is a list of every sprite. All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()
# List of each block in the game
block_list = pygame.sprite.Group()
# List of each bullet
bullet_list = pygame.sprite.Group()
# --- Create the sprites
for i in range(0,1,2):
# This represents a block
block = Block(BLUE)
# Set a random location for the block
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(350)
# Add the block to the list of objects
block_list.add(block)
all_sprites_list.add(block)
# Create a red player block
player = Player()
all_sprites_list.add(player)
player_image = pygame.image.load("player.png").convert()
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
score = 0
player.rect.y = 370
# -------- Main Program Loop -----------
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
# Fire a bullet if the user clicks the mouse button
bullet = Bullet()
# Set the bullet so it is where the player is
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
# Add the bullet to the lists
all_sprites_list.add(bullet)
bullet_list.add(bullet)
# --- Game logic
# Call the update() method on all the sprites
# Calculate mechanics for each bullet
for bullet in bullet_list:
# See if it hit a block
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
# For each block hit, remove the bullet and add to the score
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
# Remove the bullet if it flies up off the screen
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
def draw_reaction():
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
screen.blit(player_image, [60, 48])
all_sprites_list.update()
# --- Draw a frame
# Clear the screen
screen.fill(WHITE)
draw_reaction()
# Draw all the spites
all_sprites_list.draw(screen)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# --- Limit to 20 frames per second
clock.tick(60)
pygame.quit()
EDIT UPDATE: The game has only one enemy that appears in a random place each time the game starts again.
EDIT UPDATE 2: I know that in most games after a collision the enemy is removed from the screen. This also happens in my code. But for a particular need of mine I need that, as an consequence, an image (player.png) remains fixed constantly after this bullet collision with the enemy.
The problem is caused by the draw_reaction() function only drawing the player_image during the absolute-time the bullet is colliding with a block. So it shows the image for a single frame (One 60th of a second), but then on the next loop the collision is no longer occurring (the bullet is removed), so it is never drawn.
There are a number of ways around this, but I'm not sure of the purpose of showing this bitmap, so maybe they're not as helpful as they should be.
The easiest fix is to probably turn the player image into a sprite, and simply add it to the existing all_sprites_list when the bullet-hit triggers, and then take it away some time later (or how ever it should work).
class PlayerShip( pygame.sprite.Sprite ):
def __init__(self):
super().__init__()
self.image = pygame.image.load( "player.png" ) #.convert()
self.rect = self.image.get_rect()
self.rect.topleft = ( 60, 48 )
Then later on:
player_sprite = PlayerShip()
and
def draw_reaction():
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
# TODO: ensure it's not showing already
all_sprites_list.add( player_sprite ) # add the player sprite
break # only add once
EDIT: Code snippets updated based on comment
I'm having trouble figuring out how to change the layers of images. What I try to achieve is moving a player around the screen and when it comes across an object, box, stone, etc., the player appears on top of the object when its bottom coord is bigger than the object and behind when the coord is smaller than the object. Instead it's always in front or behind the object.
I've searched online and couldn't quite get what I want and I had found a post on Stack Overflow which demonstrated the usage of pygame.sprite.LayeredUpdates(). It explained the idea but I still can't figure out how to change the image layer while running the program. A simple code demonstration would be nice.
You can find a working example of LayeredUpdates here.
Note that the documentation of pygame is wrong: your sprites need a _layer attribute, not layer.
To actually change the layer of a sprite, you have to use the functions of LayeredUpdates, like change_layer or switch_layer.
Give your objects a self._layer attribute and set it to the self.rect.bottom coord. Sprites with a higher layer number appear above the sprites with lower numbers. When the player moves, you can call the change_layer method of the LayeredUpdates group to set the layer of the sprite in the group to the current rect.bottom position. Here's a complete example:
import random
import pygame as pg
PLAYER_IMG = pg.Surface((30, 50))
PLAYER_IMG.fill(pg.Color('dodgerblue1'))
TRIANGLE_IMG = pg.Surface((50, 50), pg.SRCALPHA)
pg.draw.polygon(TRIANGLE_IMG, (240, 120, 0), [(0, 50), (25, 0), (50, 50)])
class Player(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = PLAYER_IMG
self.rect = self.image.get_rect(center=pos)
# The sprite will be added to this layer in the LayeredUpdates group.
self._layer = self.rect.bottom
class Triangle(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = TRIANGLE_IMG
self.rect = self.image.get_rect(center=pos)
# The sprite will be added to this layer in the LayeredUpdates group.
self._layer = self.rect.bottom
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.LayeredUpdates()
player = Player((50, 80))
all_sprites.add(player)
for _ in range(20):
all_sprites.add(Triangle((random.randrange(600), random.randrange(440))))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
if keys[pg.K_d]:
player.rect.x += 5
if keys[pg.K_a]:
player.rect.x -= 5
if keys[pg.K_w]:
player.rect.y -= 5
if keys[pg.K_s]:
player.rect.y += 5
# If any of the wasd keys are pressed, change the layer.
if any((keys[pg.K_w], keys[pg.K_a], keys[pg.K_s], keys[pg.K_d])):
# Set the layer of the player sprite to its rect.bottom position.
all_sprites.change_layer(player, player.rect.bottom)
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
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.
Please any help would be greatly appreciated.
I am writing a game with Pygame and after creating all the classes and methods I need. When I run the game I see five alien characters of my game showing up from the left side of the screen joined together before I actually see what i wanted my code to display (aliens moving at random positions down the screen).
here is my code:
class Alien():
def __init__(self, image):
self.x = random.randrange(20,width - 20)
self.y = random.randrange(-200, -20)
self.speed = random.randint(1, 5)
self.image = pygame.image.load(os.path.join("../images", image)).convert_alpha()
self.rect = self.image.get_rect()
def move_down(self):
self.rect.y += self.speed
def draw(self, screen):
screen.blit(self.image, self.rect)
its implementation
for i in range (20):
aliens = Alien("alien.png")
enemies.append(aliens)
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(white)
for i in range(len(enemies)):
enemies[i].move_down()
enemies[i].draw(screen)
enemies[i].check_landed()
pygame.display.flip()
clock.tick(30)
note: I have removed some code for clarity.
result
You store the position of the aliens in the fields self.x and self.y, but to draw them, you actually don't use self.x and self.y, but self.rect.
You create self.rect by calling self.image.get_rect(), and when you call get_rect() on a Surface, the position of the Rect is always (0, 0).
So the x coordinate is always 0, hence they are all on the left side of the screen.
I suggest rewriting your code to:
class Alien():
# pass the Surface to the instance instead of the filename
# this way, you only load the image once, not once for each alien
def __init__(self, image):
self.speed = random.randint(1, 5)
self.image = image
# since we're going to use a Rect of drawing, let's use the
# same Rect to store the correct position of the alien
self.rect = self.image.get_rect(top=random.randrange(-200, -20), left=random.randrange(20,width - 20))
def move_down(self):
# the Rect class has a lot of handy functions like move_ip
self.rect.move_ip(0, self.speed)
def draw(self, screen):
screen.blit(self.image, self.rect)
# load the image once. In more complex games, you usually
# want to abstract the loading/storing of images
alienimage = pygame.image.load(os.path.join("../images", 'alien.png')).convert_alpha()
for _ in range (20):
aliens = Alien(alienimage)
enemies.append(aliens)
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(white)
# just use a "regular" for loop to loop through all enemies
for enemy in enemies:
enemy.move_down()
enemy.draw(screen)
enemy.check_landed()
pygame.display.flip()
clock.tick(30)
You could go further and use the Sprite- and Group-class to further generalize your code, but that's another topic.