Moving an image in PyGame hides all of the other images - python

When I move an image around my PyGame screen, if it happens to pass over another image, those onderneath get hidden and cannot be seen again. Why might this be?
My code is:
def main():
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
background = pygame.image.load('Background.png').convert()
screen.blit(background, (0, 0)) # draw the background screen
human1 = pygame.image.load('Human1.gif').convert()
human1_position = pygame.Rect(10, BOTTOM_FLOOR, CHARACTER_WIDTH, CHARACTER_HEIGHT)
screen.blit(human1, human1_position) # draw the first human
human2 = pygame.image.load('Human2.gif').convert()
human2_position = pygame.Rect(100, BOTTOM_FLOOR, CHARACTER_WIDTH, CHARACTER_HEIGHT)
screen.blit(human2, human2_position) # draw the second human
human3 = pygame.image.load('Human3.gif').convert()
human3_position = pygame.Rect(190, BOTTOM_FLOOR, CHARACTER_WIDTH, CHARACTER_HEIGHT)
screen.blit(human3, human3_position) # draw the third human
pygame.display.update() # display all graphical elements
move = STOP # Variable to track what the current move is. Initially, stopped
move_character = 1 # Variable to track which character is being moved
while True:
#
# See what the user did...
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
# Check if there is a move currently happening. The program
# will only do something if the previous move is stopped
if move == STOP:
# Check which key was pressed...
if event.key == pygame.K_1:
# if key pressed is the '1' key, move first human (and check if moving from bottom to top or other way)
move_character = 1 # the first character is to be moved
if human1_position.top < 300:
# If the 'top' of the human's position is less
# than 300 then it means the human is currenlty
# on the top half of the screen, and so
# the next move will be to move it downards
move = DOWN
else:
move = UP
# Check which key was pressed...
if event.key == pygame.K_2:
# if key pressed is the '2' key, move second human (and check if moving from bottom to top or other way)
move_character = 2 # the first character is to be moved
if human2_position.top < 300:
# If the 'top' of the human's position is less
# than 300 then it means the human is currenlty
# on the top half of the screen, and so
# the next move will be to move it downards
move = DOWN
else:
move = UP
screen.blit(background, human1_position, human1_position) # erase old position of first human
if (move == UP):
#they want to move up, so first check the lift is down with them.
if (liftLoc == "down"):
#they are both down, they can be moved into the lift
if (human1_position.left <= 245):
human1_position = human1_position.move(2, 0) # move first human
pygame.display.update()
else:
move = STOP
else:
print "Wrong floor!"

So the problem you are having, is that you are only rendering/blitting the images you move over when you start, so they get "unrendered" or rendered over.
To get rid of this problem, like all other real time rendering engines, you should render everything you want someone to see, every frame you want something to happen.
I shall not give you the code correction, but just moving the screen.blit of every image into your while loop. Their position will obviously affect the rendering order.

Related

Can't click on image again, what's wrong with my pygame code?

Okay, I'am trying to create a Tom and Jerry game with the pygame library.
The game focuses on catching mice by clicking on them as they appear in their holes. The problem
is that sometimes a cat appears instead of a mouse and should the player erroneously click on the
cat (s)he looses all earned points, but the game continues.
The mouse is an image of a mouse and the cat is an image of an cat.
If you click on the mouse, you get mouse, otherwise the cat gets the points.
The code is a mess, that's because I don't know what I'am doing and just set an another event loop because then it works, because it runs after I create the mouse. It works to click on the mouse but then you click somewhere else and after that it's like you did not clicked on the mouse.
The mouse is created in a loop and is supposed to wait for 5 seconds and if you click on the mouse within these seconds then an appropriate message prints out in the console ,,Jerry clicked!" else "1 click". If you don't click on the mouse within 5 seconds a image covers the mouse so she disappears.
Now, what I'am trying to do right now is to print the message 1 click when the player does not click on anything but print 1 click jerry clicked when the player clicks on the mouse. I have a image of the mousehole and then I put the mouse on the mousehole, that is, on an another image.
This code works with one image at least:
pygame.init()
width=350;
height=400
screen = pygame.display.set_mode( (width, height ) )
pygame.display.set_caption('clicked on image')
redSquare = pygame.image.load("images/red-square.png").convert()
x = 20; # x coordnate of image
y = 30; # y coordinate of image
screen.blit(redSquare , ( x,y)) # paint to screen
pygame.display.flip() # paint screen one time
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
x, y = event.pos
if redSquare.get_rect().collidepoint(x, y):
print('clicked on image')
#loop over, quite pygame
pygame.quit()
My problem is that, when I click on the mouse and then I don't click on the mouse I can't click on the mouse again at another position.
So what's wrong? What I'am doing wrong here?
Here is my code:
import pygame
from pygame import *
from random import *
init()
run = True
screen = (800,800)
screen = display.set_mode(screen)
xpos = 0
ypos = 0
mouseorcatxpos = 5
mouseorcatypos = 0
mousehole = image.load("mousehole.png").convert()
cat = image.load("tom.png")
jerry = image.load("jerry.png")
def makeholes():
global ypos
global xpos
for holey in range(1,9):
for holex in range(1,9):
screen.blit(mousehole,(xpos,ypos))
display.flip()
xpos += 100
ypos += 100
xpos = 0
def mouseorcat():
global xpos
mouseorcatxpos = 5
ypos = 0
for mousecaty in range(1,9):
pygame.event.pump()
for mousecatx in range(1,9):
randommouse = randint(1, 3)
randomcat = randint(1, 10)
if(randommouse == 2):
screen.blit(jerry, (mouseorcatxpos, ypos))
display.flip()
for event in pygame.event.get():
if (event.type == MOUSEBUTTONDOWN):
if jerry.get_rect().collidepoint(xpos, ypos) == False:
print("l clicked!")
x, y = event.pos
if jerry.get_rect().collidepoint(xpos, y):
print("JERRY CLICKED!!")
x, y = event.pos
print(x, y)
time.wait(5000)
#screen.blit(mousehole, (mouseorcatxpos - 5, ypos))
display.flip()
elif(randomcat == 2):
screen.blit(cat, (mouseorcatxpos, ypos))
display.flip()
time.wait(1500)
screen.blit(mousehole, (mouseorcatxpos-5, ypos))
display.flip()
mouseorcatxpos += 100
mouseorcatxpos = 0
ypos += 100
makeholes()
while run == True:
for event in pygame.event.get():
mouseorcat()
if event.type == QUIT:
run = False
I rewrote your game to show you how I would do it.
To keep track of the time and to limit the framerate I used a pygame.time.Clock and a timer variable. The clock returns the time in milliseconds since clock.tick was called the last time, which is used to increase the timer variable. The cat just replaces the mouse after two seconds and the mouse is set to a new position. I use pygame.Rects to store the positions, but you could also use lists or tuples.
import sys
import random
import pygame
pygame.init()
size = (800, 800)
screen = pygame.display.set_mode(size)
# Images replaced by pygame.Surface. Do that too
# in the future before you post your code.
mousehole = pygame.Surface((40, 40)).convert()
mousehole.fill(pygame.Color(30, 30, 30))
cat = pygame.Surface((40, 40)).convert()
cat.fill(pygame.Color(110, 110, 130))
jerry = pygame.Surface((40, 40)).convert()
jerry.fill(pygame.Color(190, 130, 0))
# Create the background image and blit the holes.
background = pygame.Surface(size).convert()
for holey in range(8):
for holex in range(8):
background.blit(mousehole, (holex*100, holey*100))
def new_position():
"""Return a random position between 0-700 in steps of 100."""
return (random.randrange(0, 701, 100), random.randrange(0, 701, 100))
def main():
fps = 30
clock = pygame.time.Clock()
jerry_rect = jerry.get_rect() # Stores jerry's position and size.
jerry_rect.topleft = new_position() # New random position.
# The cat is outside of the screen first.
cat_rect = cat.get_rect(topleft=(-100, -100))
points = 0
timer = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if jerry_rect.collidepoint(event.pos):
points += 1
print('Jerry caught! Points:', points)
timer = 0
jerry_rect.topleft = new_position()
else:
print('Missed. Points:', points)
# Run logic.
timer += clock.tick(fps) / 1000 # timer + seconds since last tick.
if timer > 2: # Cat catches mouse after 2 seconds.
cat_rect.topleft = jerry_rect.topleft
jerry_rect.topleft = new_position()
timer = 0
points = 0
print('Tom caught Jerry.')
# Draw.
# Clear the screen by blitting the bg.
screen.blit(background, (0, 0))
screen.blit(jerry, jerry_rect)
screen.blit(cat, cat_rect)
pygame.display.flip()
if __name__ == '__main__':
main()
pygame.quit()
sys.exit()
Side notes:
Don't use star imports (from module import *), because that can make code harder to read. If you want you can use from pygame.locals import *, if it's the only star import.
Don't use global variables, because they can make code harder to read, understand and maintain. Pass variables to functions as arguments and then return the result.
Update: Some notes about your program:
The first big problem is that your game has two event loops and the important one is deeply nested inside of two other for loops and a if. The event loop should be directly under the main while loop (one indentation level (when you have more experience you can put it into a function or class method)).
The two for loops seem to have the purpose to let the code run until randommouse or randomcat are 2. To run code until a condition is met is the purpose of a while loop. But in this case you should better just pick a random number and write the if/elif conditions so that they always apply. For example, you want a 2/3 chance for mouse and 1/3 for a cat,
random_number = random.randint(1, 3)
if random_number < 3:
print("2/3 probability. It's a mouse")
else:
print("1/3 probability. It's a cat")
Or use random.choice with a list:
>>> random.choice(['mouse', 'mouse', 'cat'])
'mouse'
time.wait(5000) shouldn't be used because the game just hangs in this time. You can't even close the window. Limit the framerate and get the time since the last tick with a pygame.time.Clock.
pygame.event.pump() is not needed.
If you call get_rect() without an argument, the rect is positioned at (0, 0).
if jerry.get_rect().collidepoint(xpos, y):
That's the reason why clicking on jerry only works in the top row, and because you use the global xpos here. Since xpos is 0, the whole top row counts as Jerry.
You can pass coordinates to get_rect like so (you can also use center or other args instead of topleft):
jerry_rect = jerry.get_rect(topleft=(50, 100))
I'm sorry but I don't think I can simply fix your code. I've tried it several times, but I always end up re-writing it completely.
I begin by extracting the event loop out of the two nested for loops, then remove these loops, create rects for the mouse and cat, fix the collision detection, add a timer and so on. Take a close look at my example and try to rewrite your game in a similar way, and keep asking questions if you don't understand something.

Pygame Error: Display surface quit (Why does this happen?)

Over the summer I decided to make a version of pong using the pygame module in Python. I already have the game logic and now I am just working on a title screen for some added touch. My main goal for this specific function is to be able to select an option on the screen and have it become highlighted orange. However, I keep getting this weird error error every time I try to scroll down via downward button the program ends and displays this error:
error: display Surface quit
From what I've read this comes from declaring things after pygame.quit() has been initiated so I took it out of my code temporarily, yet the same thing happened without it. WTF?
My code (It has a few unused variable due to the fact that I just copied and pasted it to another python doc):
import pygame,random, time
pygame.init()
#GLOBAL VARIABLES
mode='normal'#Sets game theme
playerx=0 #Paddle x for gamer
playery=0 #paddle y for gamer
aix=0 #paddle x for computer
aiy=0 #paddle y form computer
x=400 #ball x coord
y=300 #ball y coord
speed_ballx=0 #xball pixel change during post blit
speed_bally=0 #yball pixel change during post blit
r=0 #redval
g=0 #greval
b=0 #blueval
#GRAPHIC ART
paddle=pygame.image.load('paddle.PNG')
ball=pygame.image.load('ball.PNG')
logo=pygame.image.load('logo.PNG')
comicsans = pygame.font.SysFont("comicsansms", 22) #comicsans font initialization
startoption = comicsans.render("START", True, (255,255,255))
instructionoption = comicsans.render("HELP", True, (255,255,255))
select=pygame.image.load('optionselect.PNG')
def pong():
main_surface.blit(ball, (x,y))#prints ball
def intsructions():
pygame.quit()
def menu():
end=False #will end while loop and end menu
opt=1 #used to decide what option the player is selecting
main_surface = pygame.display.set_mode((815,615))#creates new window
while end==False:
main_surface.blit(logo, (15,15))
pygame.display.flip()#renders all blitted objects
#The following if statements blit orange highlight on to the selected option
if opt==1:
main_surface.blit(select,(12,140))
if opt==2:
main_surface.blit(select,(12,170))
main_surface.blit(startoption, (15,140))
main_surface.blit(instructionoption, (15,170))
pygame.display.flip()#renders all blitted objects
for ev in pygame.event.get():
if ev.type == pygame.QUIT: # Window close button clicked?
end=True
if ev.type == pygame.KEYDOWN:
if ev.key == pygame.K_DOWN and opt==1: #moving down an option
opt=opt+1
print 2
if ev.key == pygame.K_DOWN and opt==2: #moving from last option (selection restarts at top)
opt=1
if ev.key == pygame.K_KP_ENTER and opt==1: #selecting START
pong()
if ev.key == pygame.K_DOWN and opt==1: #selecting INSTRUCTIONS
intsructions()
pygame.quit()
menu()
I don't see you defining your surface anywhere.
Just define it.

pygame.key.set_repeat for Joystick

I'm building a menu using pygame and I want to make it navigable using a specific gamepad. Ideally I want to be able to press and hold *down" on the D-pad repeatedly, or get something like on a keyboard where the first button press has a delay before repeatedly entering the same character (seemingly).
I'm trying to emulate the pygame.key.set_repeat(...) function for a Joystick. my approach so far has been
pygame.time.set_timer(pygame.USEREVENT, 10)
DELAY_TIME = 0.250 #ms
y_delay = True
while not done:
for event in pygame.event.get():
y_axis = gamepad.get_axis(1)
if y_axis > 0.5: # pushing down
main_menu.move_down()
redraw() #redraw everything on the surface before sleeping
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False #don't delay the next time the y axis is used
elif y_axis < -0.5: #pushing up
# repetitive I know, but I'm still working on it
main_menu.move_up()
redraw()
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False
else:
y_delay = True # delay the next time
my issue is if someone taps up or down faster than DELAY_TIME they are limited to the DELAY_TIME before they can move again. Also if someone releases and depresses the up/down button within the time.sleep interval, python never sees that it was released at all and doesn't allow for a delay.
Maybe there's a way to do this using events or mapping the joystick to keys somehow? qjoypad doesn't cut it for me, and joy2keys is trash. I would need to do the mapping within the python program.
Sleep causes the program to halt execution, so it's not a viable option. You can also do this without using set_timer and events. I did it using a couple of flags and pygame.time's get_ticks.
import pygame
from pygame.locals import *
def main():
pygame.init()
pygame.display.set_mode((480, 360))
gamepad = pygame.joystick.Joystick(0)
gamepad.init()
delay = 1000
neutral = True
pressed = 0
last_update = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
move = False
if gamepad.get_axis(1) == 0:
neutral = True
pressed = 0
else:
if neutral:
move = True
neutral = False
else:
pressed += pygame.time.get_ticks() - last_update
if pressed > delay:
move = True
pressed -= delay
if move:
print "move"
last_update = pygame.time.get_ticks()
if __name__ == "__main__":
main()
pygame.quit()
When get_axis indicates no motion, the neutral flag is set, and the pressed timer is reset, causing the move flag to remain unset. When the neutral flag is unset, if it's newly set, the move flag is set. If it's not newly set, the pressed timer increases, and move is set only if the pressed timer is greater than delay.

How to display player's score on Pygame?

I'm a new programmer working on a memory game for my computer science summative.The game goes like this: the computer displays random boxes at random positions and then the user has to guess where the boxes are and click on it.
I'm basically done, except right now I'm trying to create 5 different levels that range in level of difficulty. eg level 1 will display 2 boxes and level 2 will display 5, etc. And then if the user gets through all levels they can play again. I know its a lot but I really want to get an A on this.
But right now I'm stuck because it doesn't really work until I try to close the window, and even then it only goes halfway. Any help would be appreciated.
import pygame , sys
import random
import time
size=[500,500]
pygame.init()
screen=pygame.display.set_mode(size)
# Colours
LIME = (0,255,0)
RED = (255, 0, 0)
BLACK = (0,0,0)
PINK = (255,102,178)
SALMON = (255,192,203)
WHITE = (255,255,255)
LIGHT_PINK = (255, 181, 197)
SKY_BLUE = (176, 226, 255)
screen.fill(BLACK)
# Width and Height of game box
width=50
height=50
# Margin between each cell
margin = 5
rows = 20
columns = 20
# Set title of screen
pygame.display.set_caption("Spatial Recall")
# Used to manage how fast the screen updates
clock=pygame.time.Clock()
coord=[]
# Create a 2 dimensional array. A two dimesional
# array is simply a list of lists.
def resetGrid():
grid = []
for row in range(rows):
# Add an empty array that will hold each cell
# in this row
grid.append([])
for column in range(columns):
grid[row].append(0) # Append a cell
return grid
def displayAllPink(pygame):
for row in range(rows):
for column in range(columns):
color = LIGHT_PINK
pygame.draw.rect(screen,color,[(margin+width)*column + margin,(margin+height)*row+margin,width,height])
pygame.display.flip()
def displayOtherColor(pygame,grid):
coord = []
for i in range(random.randint(2,5)):
x = random.randint(2, rows-1)
y = random.randint(2, columns-1)
color = LIME
pygame.draw.rect(screen,color,[(margin+width)*y + margin,(margin+height)*x+margin,width,height])
coord.append((x,y))
grid[x][y] = 1
pygame.display.flip()
time.sleep(1)
return coord
def runGame(gameCount,coord,pygame,grid):
pygame.event.clear()
pygame.display.set_caption("Spatial Recall: Level "+ str(gameCount))
pygame.time.set_timer(pygame.USEREVENT,1000)
time = 0
#clock.tick(
# -------- Main Program Loop -----------
#Loop until the user clicks the close button.
done = False
while done==False:
event = pygame.event.wait() # User did something
if event.type == pygame.QUIT: # If user clicked close
done=True # Flag that we are done so we exit this loop
pygame.event.clear()
print "Game ",gameCount, "ends"
elif event.type == pygame.USEREVENT:
time = time + 1
pygame.display.set_caption("Spatial Recall: Level "+ str(gameCount) + " Time: "+ str(time))
if time == 100:
done = True
pygame.display.set_caption("Time out, moving to next level")
pygame.event.clear()
return False
elif event.type == pygame.MOUSEBUTTONDOWN:
# User clicks the mouse. Get the position
pos = pygame.mouse.get_pos()
# Change the x/y screen coordinates to grid coordinates
column=pos[0] // (width+margin)
row=pos[1] // (height+margin)
if (row,column) in coord:
print coord
coord.remove((row,column))
print coord
color = LIME
pygame.draw.rect(screen,color,[(margin+width)*column + margin,(margin+height)*row+margin,width,height])
if coord == []:
done=True
pygame.display.set_caption("Time out, moving to next level")
pygame.event.clear()
return True
else:
color = RED
pygame.draw.rect(screen,color,[(margin+width)*column + margin,(margin+height)*row+margin,width,height])
pygame.display.flip()
def startTheGame(gameCount):
grid = resetGrid()
displayAllPink(pygame)
coord = displayOtherColor(pygame,grid)
displayAllPink(pygame)
runGame(gameCount,coord,pygame,grid)
for i in range(2):
startTheGame(i+1)
pygame.quit ()
You may want to use the pygame.font module. http://pygame.org/docs/ref/font.html
First, load a font, either from a file or from one of the system font functions.
Call YourFontObject.render(your_text_string). That'll return a Surface that contains the string rendered in the given font. Note, you can't use newline (\n) characters! You'll have to do the spacing yourself.
Blit this Surface onto the screen after everything else so nothing will obscure it.
Also, you don't need the pygame parameter in your functions.
Hope this helps.

Menu problem with PyGame MOUSEBUTTONDOWN event

Yes, that title wasn't worded very properly at all.
Ok, here's what we've got - a Python program using the pyGame library, and we're making a game. We start in a menu environment main.py. When the user clicks on one of the menu buttons, an action is performed. The program checks for clicks on menu items using the following code:
if event.type == pygame.MOUSEBUTTONDOWN:
mousePos = pygame.mouse.get_pos()
for item in buttons: # For each button
X = item.getXPos() # Check if the mouse click was...
Y = item.getYPos() # ...inside the button
if X[0] < mousePos[0] < X[1] and Y[0] < mousePos[1] < Y [1]:
# If it was
item.action(screen) # Do something
When the user clicks on the "Play Game" button, it opens a sub-module, playGame.py. In this sub-module is another pyGame loop etc.
Part of the game is to hold the left mouse button to 'grow' circles out of the current position (It's a puzzle game, and it makes sense in context). Here is my code for doing this:
mouseIsDown == False
r = 10
circleCentre = (0,0)
[...other code...]
if mouseIsDown == True:
# This grown the circle's radius by 1 each frame, and redraws the circle
pygame.draw.circle(screen, setColour(currentColourID), circleCentre, r, 2)
r += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
runningLevel = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# User has pressed mouse button, wants to draw new circle
circleCentre = pygame.mouse.get_pos()
mouseIsDown = True
elif event.type == pygame.MOUSEBUTTONUP:
# Stop drawing the circle and store it in the circles list
mouseIsDown = False
newCircle = Circle(circleCentre, r, currentColourID)
circles.append(newCircle)
circleCount += 1
r = 10 # Reset radius
The problem I have is that the user's left mouse click from the main menu is persisting into the playGame.py module, and causing it to create and store a new circle, of radius 10 and at position (0,0). Both of these are the default values.
This only happens for the one frame after the menu.
Is there any way to prevent this, or is it a flaw in my code?
All help greatly appreciated, as always. If you need more code or explanation of these snippets, let me know.
If you'd like the full code, it's on GitHub.
You could use MOUSEBUTTONUP instead of MOUSBUTTONDOWN in the menu.
Does adding pygame.event.clear() to the top of Play fix it?

Categories