PyGame Screen Returning Black when run - python-3.x

While trying to run a game in which is being developed for a project, the code would not work and causes the screen to be black. The Mycharacter is a character, enemy is for a enemy character, obstacle is for obstacles within the game. The design is supposed to be a character chasing the enemy while the enemy is chasing the character to gain points with obstructions in the way.
We are importing other classes into this function and main class.
All help will be appricated.
Heres how the code looks:
import pygame
import time
import random
import Mycharacter
import enemy
import obstacle
import json
GREEN = (0, 255, 0)
class Controller:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((800, 800))
self.background = pygame.Surface(self.screen.get_size()).convert()
#self.button_image = pygame.image.load("start_button.png").convert
self.button = pygame.Rect(350, 50, 100, 100)
#self.button_image.get_rect()
self.font = pygame.font.Font("Shark_Soft_Bites.TTF", 60)
self.text = pygame.font.Font.render(self.font,"Start", True, (255, 0, 0))
#self.score_text = pygame.font.Font.render("Highscore: "+ str())
self.file = open("highscores.json", "r")
self.currentState = "Start"
self.obstacles = []
for i in range(5):
x = random.randrange(50, 600)
y = random.randrange(60, 700)
self.obstacles.append(obstacle.Obstacle((x, y), 'rock.png' ))
self.enemies = []
for i in range(3) :
if i == 0:
x = 725
y = 100
elif i == 1:
x = 75
y = 700
elif i == 2:
x = 725
y = 700
self.enemies.append(enemy.Enemy((x, y), "enemy.png"))
self.Mycharacter = Mycharacter.Mycharacter((75, 100), "head6.png")
self.mysprites = pygame.sprite.Group((self.Mycharacter,) + tuple(self.enemies) + tuple(self.obstacles))
self.mysprites2 = pygame.sprite.Group(tuple(self.enemies) + tuple(self.obstacles))
self.score = 0
self.end_time = 0
self.start_time = 0
self.time = (self.end_time-self.start_time) * 1000
def mainLoop(self):
"""
This is the main loop for the game that calls the other functions in this class
in order to create a start screen, run the game, and present the high score and player
score at the end of the game.
Param list: None
Return list: None
"""
self.done = False
while not self.done:
if self.currentState == "start":
self.startGame(self)
elif self.currentState == "running":
self.start_time = pygame.time.get_ticks()
self.runGame(self)
elif self.currentState == "end":
self.endGame(self)
pygame.quit
def startGame(self):
"""
This is the function for the start of the game. It fills the screen
with a different color background and creates the start button that the user
can click on to begin running the game.
Param list: None
Returns: None
"""
start = True
while start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
start = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if self.button.collidepoint(mouse_pos):
self.currentState = "running"
start = False
self.screen.fill((255, 0, 255))
pygame.draw.rect(self.screen, (0, 0, 0), self.button)
self.screen.blit(self.text, (350,50))
pygame.display.flip()
pygame.quit
def runGame(self):
run = True
clock = pygame.time.Clock
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
run = False
if event.type == pygame.KEYDOWN:
#Under here is the game logic and i don't know if i should have the random enemy movement under here
if(event.key == pygame.K_UP):
self.Mycharacter.move_up()
elif(event.key == pygame.K_DOWN):
self.Mycharacter.move_down()
elif(event.key == pygame.K_LEFT):
self.Mycharacter.move_left()
elif(event.key == pygame.K_RIGHT):
self.Mycharacter.move_right()
self.score += 1
if self.score > 400:
if self.score > 800:
if self.score > 1200:
self.enemy.speed(18)
else:
self.enemy.speed(16)
else:
self.enemy.speed(14)
else:
self.enemy.speed(12)
self.mysprites.update()
#self.mysprites2.group_collide
#possibly sprite.groupcollide to save myself these two loops but I like the loops
#also idk if the dokill means it will .kill() or something else
for i in range(len(self.obstacles)):
if(pygame.sprite.collide_rect(self.Mycharacter, self.obstacle[i])):
self.Mycharacter.kill()
self.currentState = "end"
self.end_time = pygame.time.get_ticks()
run = False
for i in range(len(self.enemies)):
if(pygame.sprite.collide_rect(self.Mycharacter, self.enemies[i])):
self.Mycharacter.kill()
self.currentState = "end"
self.end_time = pygame.time.get_ticks()
run = False
self.screen.fill(GREEN)
#self.obstacles.draw(self.screen)
#self.enemies.draw(self.screen)
self.screen.blit(self.background, (0, 0))
self.mysprites.draw()
#self.screen.blit(self.Mycharacter,(self.Mycharacter.rect.x, self.Mycharacter.rect.y))
#drawing code
#update screen with what has been drawn
pygame.display.flip()
clock.tick(60)
pygame.quit
def endGame(self):
#self.screen = pygame.display.set_mode(800, 800)
#self.background = pygame.Surface(self.screen.get_size()).convert()
end = False
while not end:
for event in pygame.event.get():
if event == pygame.QUIT:
end = True
self.done = True
else:
line = json.load(self.file.readline())
num = line["one"]
self.file.close()
if self.time > int(num):
self.file = open("highscores.json", "w")
newstr = "highscore:"+str(num)
jsonstr = json.dump(newstr)
self.file.write(newstr)
self.file.close
self.file = open("highscores.json", "r")
newline = json.load(self.file.readline())
score = newline["highscore"]
self.file.close()
self.screen.fill((0, 0, 255))
#create text and rect to blit onto screen to display high score
self.screen.blit("Highscore: " + str(score), (350, 50))
self.screen.blit("Your Score: " + str(num), (350, 150))
pygame.display.flip()
pygame.quit
def main():
the_game = Controller()
the_game.mainLoop()
main()

The first problem that I can see is that you pass self as an argument to the different scene methods self.startGame(self) in the mainLoop. Just remove the self.
In the runGame method, you have indented a lot of your code incorrectly. The game logic and drawing code shouldn't be in the event loop but in the outer while loop. It should still draw everything, though, but only if events are in the queue (for example if the mouse gets moved).
Also, the self.background surface is just black because you never fill it with another color.
Side notes: You forgot some parentheses behind clock = pygame.time.Clock an pygame.quit.
In the endGame method, you load the json file every time an event occurs (f.e. mouse movements). You should better do that once per mouse click or something similar.
Here's a minimal example that works correctly:
import pygame
GREEN = pygame.Color('green')
class Controller:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((800, 800))
self.background = pygame.Surface(self.screen.get_size()).convert()
self.background.fill(GREEN) # Fill the background if it shouldn't be black.
self.clock = pygame.time.Clock()
self.currentState = "start"
self.button = pygame.Rect(350, 50, 100, 100)
def mainLoop(self):
self.done = False
while not self.done:
if self.currentState == "start":
self.startGame()
elif self.currentState == "running":
self.start_time = pygame.time.get_ticks()
self.runGame()
def startGame(self):
start = True
while start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
start = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if self.button.collidepoint(mouse_pos):
self.currentState = "running"
return # Back to the mainloop
self.screen.fill((255, 0, 255))
pygame.draw.rect(self.screen, (0, 0, 110), self.button)
pygame.display.flip()
self.clock.tick(60)
def runGame(self):
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
run = False
self.screen.blit(self.background, (0, 0))
pygame.draw.rect(self.screen, (200, 0, 0), [20, 30, 50, 90])
pygame.display.flip()
self.clock.tick(60)
def main():
the_game = Controller()
the_game.mainLoop()
main()
pygame.quit()

Related

My Text Input Fields class isn't working and I don't know what I am missing [duplicate]

I want to get some text input from the user in Python and display what they are typing in a text box, and when they press enter, it gets stored in a string.
I've looked everywhere, but I just can't find anything. I'm using Pygame.
You can define a rect as the area of the input box. If a pygame.MOUSEBUTTONDOWN event occurs, use the colliderect method of the input_box rect to check if it collides with the event.pos and then activate it by setting a active variable to True.
If the box is active you can type something and Pygame will generate pygame.KEYDOWN events which have a unicode attribute that you can simply add to a string, e.g. text += event.unicode. If the user presses enter, you can do something with the text string (in the example I just print it) and reset it to ''.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 32)
clock = pg.time.Clock()
input_box = pg.Rect(100, 100, 140, 32)
color_inactive = pg.Color('lightskyblue3')
color_active = pg.Color('dodgerblue2')
color = color_inactive
active = False
text = ''
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
print(text)
text = ''
elif event.key == pg.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
screen.fill((30, 30, 30))
# Render the current text.
txt_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(screen, color, input_box, 2)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's an object-oriented variant that allows you to easily create multiple input boxes:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = pg.Color('lightskyblue3')
COLOR_ACTIVE = pg.Color('dodgerblue2')
FONT = pg.font.Font(None, 32)
class InputBox:
def __init__(self, x, y, w, h, text=''):
self.rect = pg.Rect(x, y, w, h)
self.color = COLOR_INACTIVE
self.text = text
self.txt_surface = FONT.render(text, True, self.color)
self.active = False
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if self.rect.collidepoint(event.pos):
# Toggle the active variable.
self.active = not self.active
else:
self.active = False
# Change the current color of the input box.
self.color = COLOR_ACTIVE if self.active else COLOR_INACTIVE
if event.type == pg.KEYDOWN:
if self.active:
if event.key == pg.K_RETURN:
print(self.text)
self.text = ''
elif event.key == pg.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
# Re-render the text.
self.txt_surface = FONT.render(self.text, True, self.color)
def update(self):
# Resize the box if the text is too long.
width = max(200, self.txt_surface.get_width()+10)
self.rect.w = width
def draw(self, screen):
# Blit the text.
screen.blit(self.txt_surface, (self.rect.x+5, self.rect.y+5))
# Blit the rect.
pg.draw.rect(screen, self.color, self.rect, 2)
def main():
clock = pg.time.Clock()
input_box1 = InputBox(100, 100, 140, 32)
input_box2 = InputBox(100, 300, 140, 32)
input_boxes = [input_box1, input_box2]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for box in input_boxes:
box.handle_event(event)
for box in input_boxes:
box.update()
screen.fill((30, 30, 30))
for box in input_boxes:
box.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
There are also third party modules available like pygame_textinput.
Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:
repl.it/#Rabbid76/PyGame-TextInput
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text = ""
input_active = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
input_active = True
text = ""
elif event.type == pygame.KEYDOWN and input_active:
if event.key == pygame.K_RETURN:
input_active = False
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
window.fill(0)
text_surf = font.render(text, True, (255, 0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:
class TextInputBox(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
Pass the list of events to the update method of the Group that contains the Sprite:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
Minimal example: repl.it/#Rabbid76/PyGame-SpriteTextInput
import pygame
class TextInputBox(pygame.sprite.Sprite):
def __init__(self, x, y, w, font):
super().__init__()
self.color = (255, 255, 255)
self.backcolor = None
self.pos = (x, y)
self.width = w
self.font = font
self.active = False
self.text = ""
self.render_text()
def render_text(self):
t_surf = self.font.render(self.text, True, self.color, self.backcolor)
self.image = pygame.Surface((max(self.width, t_surf.get_width()+10), t_surf.get_height()+10), pygame.SRCALPHA)
if self.backcolor:
self.image.fill(self.backcolor)
self.image.blit(t_surf, (5, 5))
pygame.draw.rect(self.image, self.color, self.image.get_rect().inflate(-2, -2), 2)
self.rect = self.image.get_rect(topleft = self.pos)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text_input_box = TextInputBox(50, 50, 400, font)
group = pygame.sprite.Group(text_input_box)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
You can find a great module for Pygame text input here.
I have been using it for a while and I really like it. A tutorial how to use it is included in the description.
However, I have added the possibility to draw a (coloured) rectangle around the text, by adding a rect and a rect_color parameter to the *_init_() function and adding
if self.rect != None:
pygame.draw.rect(screen, self.rect_color, self.rect) #screen is my pygame display surface
to the update(self, events) function.
The pygame_gui module allows you to create a text_input box from the user by creating a UITextEntryLine instance. You'll need to set up an instance as in the quick start guide.
Create the text_input:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Get the text if enter is clicked:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = event.text
I have written a class that can handle text input
minimal example:
import pygame as pg
from pgtextbox import pgtextbox
pg.init()
screen=pg.display.set_mode((1000,500))
textbox=pgtextbox(200,20)
textbox.insertAtCurser('Hallo')
while True:
e = pg.event.wait(30000)
if e.type == pg.QUIT:
raise StopIteration
textbox.addPgEvent(e)#uses keydown events
print(textbox.text)
screen.fill((0,0,0))
screen.blit(textbox.render(),(10,0))
pg.display.flip()
pg.display.quit()
pgtextbox class:
import pygame as pg
class pgtextbox:#By K1521
def __init__(self,width=100,height=10,fontname=None):
self.surface=pg.Surface((width,height))
self.text=""
self.width=width
self.height=height
self.font=pg.font.Font(fontname,pgtextbox.getMaxFontSize(fontname,lineheight=height))
self.curserindex=0
self.cursersurface=pg.Surface((self.font.size("|")[0]//2,self.font.size("|")[1]))
self.cursersurface.fill((255,255,255))
#self.cursersurface=self.font.render("|",False,(255,255,255),(0,0,0))
self.offsety=int((height-self.font.get_linesize())/2)
self.offsetx=0
def curserpos(self):
return self.font.size(self.text[:self.curserindex])[0]
def addPgEvent(self,event):
if event.type==pg.KEYDOWN:
if event.key==pg.K_BACKSPACE:
self.deleteAtCurser()
elif event.key==pg.K_RIGHT:
self.offsetCurser(1)
elif event.key==pg.K_LEFT:
self.offsetCurser(-1)
else:
self.insertAtCurser(event.unicode)
def render(self):
self.surface.fill((0,0,0))
width=self.width-self.cursersurface.get_width()
text=self.font.render(self.text,False,(255,255,255),(0,0,0))
if self.curserindex>=0:
curserpos=self.curserpos()+self.offsetx
curserposnew=max(0,min(curserpos,width))
self.offsetx+=curserposnew-curserpos
curserpos=curserposnew
#if curserpos<0:
#self.offsetx-=curserpos
#curserpos=0
#if curserpos>width:
#curserpos=curserpos-width
#self.offsetx-=curserpos
else:
#self.offsetx=min(width-text.get_width(),0)
self.offsetx=0
self.surface.blit(text,(self.offsetx,self.offsety))
if self.curserindex>=0:
self.surface.blit(self.cursersurface,(curserpos,self.offsety))
#print((curserpos,self.offsety))
return self.surface
def insertAtCurser(self,t):
if self.curserindex<0:
self.curserindex=len(self.text)
self.text=self.text[:self.curserindex]+t+self.text[self.curserindex:]
self.curserindex+=len(t)
def deleteAtCurser(self,length=1):
if self.curserindex<0:
self.curserindex=len(self.text)
newcurserindex=max(0,self.curserindex-length)
self.text=self.text[:newcurserindex]+self.text[self.curserindex:]
self.curserindex=newcurserindex
def offsetCurser(self,i):
self.curserindex=max(min(self.curserindex+i,len(self.text)),0)
#staticmethod
def longestline(self,fontname,lines):
size=pg.font.Font(fontname,1000)
return max(lines,key=lambda t:size(t)[0])
#staticmethod
def getMaxFontSize(fontname,width=None,lineheight=None,line=None):
def font(size):
return pg.font.Font(fontname,size)
fontsize=float("inf")# inf
if width:
aproxsize=width*1000//font(1000).size(line)[0]
while font(aproxsize).size(line)[0]<width:
aproxsize+=1
while font(aproxsize).size(line)[0]>width:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
if lineheight:
aproxsize=lineheight*4//3
while font(aproxsize).get_linesize()<lineheight:
aproxsize+=1
while font(aproxsize).get_linesize()>lineheight:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
return fontsize
#staticmethod
def rendermultilinetext(text,width=None,height=10,fontname=None,antialias=False,color=(255,255,255),background=None):
if(len(text)-text.count("\n")==0):
return pg.Surface((0,0))
def font(size):
return pg.font.Font(fontname,size)
text=text.split("\n")
fontsize=1000000000# inf
longestline=None
if height:
longestline=pgtextbox.longestline(fontname,lines)
fontsize=pgtextbox.getMaxFontSize(fontname,width,lineheight,longestline)
font=font(fontsize)
width=font.size(longestline)[0]
lineheight=font.get_linesize()
heigth=len(text)*lineheight
textsurface=pg.Surface((width,heigth))
if background:
textsurface.fill(background)
for i,line in enumerate(text):
textsurface.blit(font.render(line,antialias,color,background),(0,i*lineheight))
return textsurface

text box missing keystrokes [duplicate]

I want to get some text input from the user in Python and display what they are typing in a text box, and when they press enter, it gets stored in a string.
I've looked everywhere, but I just can't find anything. I'm using Pygame.
You can define a rect as the area of the input box. If a pygame.MOUSEBUTTONDOWN event occurs, use the colliderect method of the input_box rect to check if it collides with the event.pos and then activate it by setting a active variable to True.
If the box is active you can type something and Pygame will generate pygame.KEYDOWN events which have a unicode attribute that you can simply add to a string, e.g. text += event.unicode. If the user presses enter, you can do something with the text string (in the example I just print it) and reset it to ''.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 32)
clock = pg.time.Clock()
input_box = pg.Rect(100, 100, 140, 32)
color_inactive = pg.Color('lightskyblue3')
color_active = pg.Color('dodgerblue2')
color = color_inactive
active = False
text = ''
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
print(text)
text = ''
elif event.key == pg.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
screen.fill((30, 30, 30))
# Render the current text.
txt_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(screen, color, input_box, 2)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's an object-oriented variant that allows you to easily create multiple input boxes:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = pg.Color('lightskyblue3')
COLOR_ACTIVE = pg.Color('dodgerblue2')
FONT = pg.font.Font(None, 32)
class InputBox:
def __init__(self, x, y, w, h, text=''):
self.rect = pg.Rect(x, y, w, h)
self.color = COLOR_INACTIVE
self.text = text
self.txt_surface = FONT.render(text, True, self.color)
self.active = False
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if self.rect.collidepoint(event.pos):
# Toggle the active variable.
self.active = not self.active
else:
self.active = False
# Change the current color of the input box.
self.color = COLOR_ACTIVE if self.active else COLOR_INACTIVE
if event.type == pg.KEYDOWN:
if self.active:
if event.key == pg.K_RETURN:
print(self.text)
self.text = ''
elif event.key == pg.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
# Re-render the text.
self.txt_surface = FONT.render(self.text, True, self.color)
def update(self):
# Resize the box if the text is too long.
width = max(200, self.txt_surface.get_width()+10)
self.rect.w = width
def draw(self, screen):
# Blit the text.
screen.blit(self.txt_surface, (self.rect.x+5, self.rect.y+5))
# Blit the rect.
pg.draw.rect(screen, self.color, self.rect, 2)
def main():
clock = pg.time.Clock()
input_box1 = InputBox(100, 100, 140, 32)
input_box2 = InputBox(100, 300, 140, 32)
input_boxes = [input_box1, input_box2]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for box in input_boxes:
box.handle_event(event)
for box in input_boxes:
box.update()
screen.fill((30, 30, 30))
for box in input_boxes:
box.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
There are also third party modules available like pygame_textinput.
Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:
repl.it/#Rabbid76/PyGame-TextInput
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text = ""
input_active = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
input_active = True
text = ""
elif event.type == pygame.KEYDOWN and input_active:
if event.key == pygame.K_RETURN:
input_active = False
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
window.fill(0)
text_surf = font.render(text, True, (255, 0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:
class TextInputBox(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
Pass the list of events to the update method of the Group that contains the Sprite:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
Minimal example: repl.it/#Rabbid76/PyGame-SpriteTextInput
import pygame
class TextInputBox(pygame.sprite.Sprite):
def __init__(self, x, y, w, font):
super().__init__()
self.color = (255, 255, 255)
self.backcolor = None
self.pos = (x, y)
self.width = w
self.font = font
self.active = False
self.text = ""
self.render_text()
def render_text(self):
t_surf = self.font.render(self.text, True, self.color, self.backcolor)
self.image = pygame.Surface((max(self.width, t_surf.get_width()+10), t_surf.get_height()+10), pygame.SRCALPHA)
if self.backcolor:
self.image.fill(self.backcolor)
self.image.blit(t_surf, (5, 5))
pygame.draw.rect(self.image, self.color, self.image.get_rect().inflate(-2, -2), 2)
self.rect = self.image.get_rect(topleft = self.pos)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text_input_box = TextInputBox(50, 50, 400, font)
group = pygame.sprite.Group(text_input_box)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
You can find a great module for Pygame text input here.
I have been using it for a while and I really like it. A tutorial how to use it is included in the description.
However, I have added the possibility to draw a (coloured) rectangle around the text, by adding a rect and a rect_color parameter to the *_init_() function and adding
if self.rect != None:
pygame.draw.rect(screen, self.rect_color, self.rect) #screen is my pygame display surface
to the update(self, events) function.
The pygame_gui module allows you to create a text_input box from the user by creating a UITextEntryLine instance. You'll need to set up an instance as in the quick start guide.
Create the text_input:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Get the text if enter is clicked:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = event.text
I have written a class that can handle text input
minimal example:
import pygame as pg
from pgtextbox import pgtextbox
pg.init()
screen=pg.display.set_mode((1000,500))
textbox=pgtextbox(200,20)
textbox.insertAtCurser('Hallo')
while True:
e = pg.event.wait(30000)
if e.type == pg.QUIT:
raise StopIteration
textbox.addPgEvent(e)#uses keydown events
print(textbox.text)
screen.fill((0,0,0))
screen.blit(textbox.render(),(10,0))
pg.display.flip()
pg.display.quit()
pgtextbox class:
import pygame as pg
class pgtextbox:#By K1521
def __init__(self,width=100,height=10,fontname=None):
self.surface=pg.Surface((width,height))
self.text=""
self.width=width
self.height=height
self.font=pg.font.Font(fontname,pgtextbox.getMaxFontSize(fontname,lineheight=height))
self.curserindex=0
self.cursersurface=pg.Surface((self.font.size("|")[0]//2,self.font.size("|")[1]))
self.cursersurface.fill((255,255,255))
#self.cursersurface=self.font.render("|",False,(255,255,255),(0,0,0))
self.offsety=int((height-self.font.get_linesize())/2)
self.offsetx=0
def curserpos(self):
return self.font.size(self.text[:self.curserindex])[0]
def addPgEvent(self,event):
if event.type==pg.KEYDOWN:
if event.key==pg.K_BACKSPACE:
self.deleteAtCurser()
elif event.key==pg.K_RIGHT:
self.offsetCurser(1)
elif event.key==pg.K_LEFT:
self.offsetCurser(-1)
else:
self.insertAtCurser(event.unicode)
def render(self):
self.surface.fill((0,0,0))
width=self.width-self.cursersurface.get_width()
text=self.font.render(self.text,False,(255,255,255),(0,0,0))
if self.curserindex>=0:
curserpos=self.curserpos()+self.offsetx
curserposnew=max(0,min(curserpos,width))
self.offsetx+=curserposnew-curserpos
curserpos=curserposnew
#if curserpos<0:
#self.offsetx-=curserpos
#curserpos=0
#if curserpos>width:
#curserpos=curserpos-width
#self.offsetx-=curserpos
else:
#self.offsetx=min(width-text.get_width(),0)
self.offsetx=0
self.surface.blit(text,(self.offsetx,self.offsety))
if self.curserindex>=0:
self.surface.blit(self.cursersurface,(curserpos,self.offsety))
#print((curserpos,self.offsety))
return self.surface
def insertAtCurser(self,t):
if self.curserindex<0:
self.curserindex=len(self.text)
self.text=self.text[:self.curserindex]+t+self.text[self.curserindex:]
self.curserindex+=len(t)
def deleteAtCurser(self,length=1):
if self.curserindex<0:
self.curserindex=len(self.text)
newcurserindex=max(0,self.curserindex-length)
self.text=self.text[:newcurserindex]+self.text[self.curserindex:]
self.curserindex=newcurserindex
def offsetCurser(self,i):
self.curserindex=max(min(self.curserindex+i,len(self.text)),0)
#staticmethod
def longestline(self,fontname,lines):
size=pg.font.Font(fontname,1000)
return max(lines,key=lambda t:size(t)[0])
#staticmethod
def getMaxFontSize(fontname,width=None,lineheight=None,line=None):
def font(size):
return pg.font.Font(fontname,size)
fontsize=float("inf")# inf
if width:
aproxsize=width*1000//font(1000).size(line)[0]
while font(aproxsize).size(line)[0]<width:
aproxsize+=1
while font(aproxsize).size(line)[0]>width:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
if lineheight:
aproxsize=lineheight*4//3
while font(aproxsize).get_linesize()<lineheight:
aproxsize+=1
while font(aproxsize).get_linesize()>lineheight:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
return fontsize
#staticmethod
def rendermultilinetext(text,width=None,height=10,fontname=None,antialias=False,color=(255,255,255),background=None):
if(len(text)-text.count("\n")==0):
return pg.Surface((0,0))
def font(size):
return pg.font.Font(fontname,size)
text=text.split("\n")
fontsize=1000000000# inf
longestline=None
if height:
longestline=pgtextbox.longestline(fontname,lines)
fontsize=pgtextbox.getMaxFontSize(fontname,width,lineheight,longestline)
font=font(fontsize)
width=font.size(longestline)[0]
lineheight=font.get_linesize()
heigth=len(text)*lineheight
textsurface=pg.Surface((width,heigth))
if background:
textsurface.fill(background)
for i,line in enumerate(text):
textsurface.blit(font.render(line,antialias,color,background),(0,i*lineheight))
return textsurface

Pygame, how do you fix a part of the screen being black when i make a game?

A part (specifically the top middle) of the screen is black, once i have added a background, but i can see the rest of the background.
I have tried running it a few times and i have tried searching what might have caused it, i don't have a game loop yet, because nothing is supposed to happen, that might be the problem... sorry the code is long...
import pygame
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
SCREEN_TITLE = "road cross game thing"
WHITE_COLOR = (255,255,255)
clock = pygame.time.Clock()
pygame.font.init()
font = pygame.font.SysFont("comicsans", 75)
title = SCREEN_TITLE
width = SCREEN_WIDTH
height = SCREEN_HEIGHT
class Game:
TICK_RATE = 60
title = SCREEN_TITLE
width = SCREEN_WIDTH
height = SCREEN_HEIGHT
image = pygame.image.load("photo.jpg")
game_screen = pygame.display.set_mode((width, height))
game_screen.fill(WHITE_COLOR)
game_screen.fill(WHITE_COLOR)
game_screen.blit(image, (0, 0))
pygame.display.set_caption(title)
is_game_over = False
did_win = False
direction = 0
while not is_game_over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_game_over = True
elif event.type == pygame.MOUSEBUTTONUP:
pos= pygame.mouse.get_pos()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
direction = 0
print(event)
game_screen.fill(WHITE_COLOR)
pygame.display.update()
clock.tick(TICK_RATE)
object_image1 = pygame.image.load("photo.jpg")
image1 = pygame.transform.scale(object_image1, (50, 50))
x_pos = 50
y_pos = 50
game_screen.blit(image1,(x_pos, y_pos))
pygame.init()
Game()
pygame.quit()
quit()
i expected the code to make the whole background my picture.
Your code is mostly there, it's just a bit un-organised.
The reason the screen updating is not what you expect is because the code is drawing and then updating in out-of-order. In a nutshell, it should make all the drawing operations first, then update() them all to the display. Obviously if you draw and image, then call fill(), the first draw operation is not going to be visible, since it's been over-painted.
The image1 does not fill the entire display because it's only stretched to ( 50, 50 ) whereas the screen is 1500 x 800.
While I was debugging the code, I moved the initialisation functions into a member function Game.__init__(), and the main game loop into a Game.run() function.
The OP's indentation is a little messed-up, I expect this is caused by the paste into SO (otherwise the program would not produce the described result). But it looks like you're loading images inside the main game loop. It's best to load resources like images, sounds, etc. only once before the user-event loop begins.
import pygame
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
TICK_RATE = 60
SCREEN_TITLE = "road cross game thing"
WHITE_COLOR = (255,255,255)
# Initialise PyGame
pygame.init()
pygame.display.set_caption( SCREEN_TITLE )
game_screen = pygame.display.set_mode( ( SCREEN_WIDTH, SCREEN_HEIGHT ) )
clock = pygame.time.Clock()
pygame.font.init()
font = pygame.font.SysFont("comicsans", 75)
class Game:
def __init__( self, game_screen ):
self.game_screen = game_screen
self.image = pygame.image.load( "photo.jpg" )
self.object_image1 = pygame.image.load( "photo.jpg" )
#self.image1 = pygame.transform.scale( self.object_image1, (50, 50) )
# Scale to fill window
self.image1 = pygame.transform.scale( self.object_image1, ( SCREEN_WIDTH, SCREEN_HEIGHT) )
def run( self ):
is_game_over = False
did_win = False
direction = 0
while not is_game_over:
# Handle user input events
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_game_over = True
elif event.type == pygame.MOUSEBUTTONUP:
pos= pygame.mouse.get_pos()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
direction = 0
print(event)
# Update the screen
#game_screen.fill( WHITE_COLOR )
#game_screen.blit( self.image, (0, 0) )
#x_pos = 50
#y_pos = 50
#game_screen.blit( self.image1, (x_pos, y_pos) )
# Fill the window with image1
game_screen.blit( self.image1, ( 0, 0 ) )
pygame.display.update()
clock.tick( TICK_RATE )
# Run the Game
game = Game( game_screen )
game.run()
pygame.quit()
quit()

Pygame screen gets cleared on flip, but only once

So I am trying to recreate Minesweeper in pygame. Since I am only drawing a single image over my current screen when a player clicks, I do not have frames. The problem is, when the player first clicks and I draw one image a flip the display. The first time I do that, it clears the background. After that, it doesn't clear the previous images. If I try and call flip first thing, nothing happens. I tried to flip after every image (Just trying things) and nothing changed.
Code:
import pygame, random, os, sys, math
pygame.init()
font = pygame.font.SysFont('Sans Serif 7', 15)
screenX = 1240
screenY = 720
sprites = pygame.image.load(os.path.dirname(os.path.abspath(__file__)) + "\\Minesweeper\\ImageSheet.png")
spriteList = []
#tiles = 16px, icons = 26px
class Minefield():
def __init__(self, width, height, mines, surface):
self.width = width
self.height = height
self.mine = sprites.subsurface(80, 49, 16, 16)
self.rows = []
self.surface = surface
for i in range(0, width):
self.rows.append([])
for i2 in range(0, height):
self.rows[i].append(-2)
for i in range(0, mines):
print(len(self.rows))
print(len(self.rows[1]))
x = random.randint(0, width-1)
y = random.randint(0, height-1)
print(x)
print(y)
self.rows[x][y]
self.render()
def clicked(self, rawPos):
pos = (math.floor(rawPos[0]/16), math.floor(rawPos[1]/16))
val = self.rows[pos[0]][pos[1]]
if val == -2:
mines = 1
if not pos[0] == 0 and not pos[1] == 0 and self.rows[pos[0]-1][pos[1]-1] == -1:
mines += 1
if not pos[0] == 0 and self.rows[pos[0]-1][pos[1]] == -1:
mines += 1
if not pos[0] == 0 and not pos[1] == self.height and self.rows[pos[0]-1][pos[1]+1] == -1:
mines += 1
if not pos[1] == 0 and self.rows[pos[0]][pos[1]-1] == -1:
mines += 1
if not pos[1] == self.height and self.rows[pos[0]][pos[1]+1] == -1:
mines += 1
if not pos[1] == 0 and not pos[0] == self.width and self.rows[pos[0]+1][pos[1]-1] == -1:
mines += 1
if not pos[0] == self.width and self.rows[pos[0]+1][pos[1]] == -1:
mines += 1
if not pos[1] == self.height and not pos[0] == self.width and self.rows[pos[0]+1][pos[1]+1] == -1:
mines += 1
print(mines)
self.surface.blit(spriteList[mines], (pos[0]*16, pos[1]*16))
pygame.display.flip()
elif val == -1:
playing = False
return
def render(self):
for i in range(0, self.width):
for i2 in range(0, self.height):
self.surface.blit(spriteList[0], (i*16, i2*16))
pygame.display.flip()
class Main():
def __init__(self):
self.screen = pygame.display.set_mode((screenX, screenY), pygame.RESIZABLE)
spriteList.append(sprites.subsurface(16, 49, 16, 16))
for i in range(0, 8):
print(i*16)
spriteList.append(sprites.subsurface(i*16, 65, 16, 16))
self.field = Minefield(50, 30, 30, self.screen)
def gameloop(self):
while True:
for event in pygame.event.get():
if event.type == pygame.VIDEORESIZE:
screenX = event.w
screenY = event.h
self.screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
if event.type == pygame.QUIT:
return
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
self.field.clicked(pos)
def stop(self):
pygame.display.quit()
pygame.quit()
sys.exit("Window Closed")
main = Main()
main.gameloop()
main.stop()
What I want:
On player click the only change is a 1 appears over the cell, but instead the background gets painted over by black.
pygame.display.set_mode() creates a new window surface. You've to render the background after recreating the window surface in the resize event. When the window was initialized, then immediately 1 resize event is pending.
def gameloop(self):
while True:
for event in pygame.event.get():
if event.type == pygame.VIDEORESIZE:
screenX = event.w
screenY = event.h
self.screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
self.field.render() # <-------------
Note, when the window was resized and the window surface is recreated then the entire scene has to be redrawn.
Probably your game will work only, if the window is not resizable. When you resize the window, then a new event occurs. The surface is recreated and the background is redrawn, but the "clicked" fields are lost. So they would have to be redrawn, too.

How do I create a button that changes sprites when clicked in pygame?

I am currently having trouble with buttons in pygame. At the moment I am testing myself in pygame and I am trying to create a flappy bird type of game. What I am trying to achieve is that when I click on the play button on the main menu it will change the button sprite and run the main game.
I have managed to define the button function and get it to switch to the main game when I click. The only problem is that it doesn't show a different sprite when clicked and I can click anywhere in the application to switch to the main game instead of having to click on the button.
Any help would be much appreciated.
Thanks in Advance
import time
import random
import pygame
from pygame.locals import *
pygame.init()
#Predefined Colors
white = (255,255,255)
black = (0,0,0)
red = (200,0,0)
light_red = (255,0,0)
yellow = (200,200,0)
light_yellow = (255,255,0)
green = (34,177,76)
light_green = (0,255,0)
blue = (0,0,255)
light_blue = (0, 0, 200)
player_list = (
# Red Bird
('assets/sprites/redbird-upflap.png', 'assets/sprites/redbird-midflap.png',
'assets/sprites/redbird-downflap.png'),
# Blue Bird
('assets/sprites/bluebird-upflap.png', 'assets/sprites/bluebird-midflap.png',
'assets/sprites/bluebird-downflap.png'),
# Yellow Bird
('assets/sprites/yellowbird-upflap.png', 'assets/sprites/yellowbird-midflap.png',
'assets/sprites/yellowbird-downflap.png')
)
background_list = (
('assets/sprites/background-day.png', 'assets/sprites/background-night.png')
)
pipe_list = (
('assets/sprites/pipe-green.png', 'assets/sprites/pipe-red.png')
)
FPS = 30
images, sounds = {}, {}
def main():
global base_x, base_y, clock, gameDisplay, display_height, display_width
display_width = 288
display_height = 512
base_x = 0
base_y = display_height * 0.79
clock = pygame.time.Clock()
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Flappy Bird")
#Loading icon sprite
images['Icon'] = pygame.image.load('assets/sprites/yellowbird-midflap.png')
pygame.display.set_icon(images['Icon'])
#Loading all the Numbers sprites
images['Numbers'] = (
pygame.image.load('assets/sprites/0.png').convert_alpha(),
pygame.image.load('assets/sprites/1.png').convert_alpha(),
pygame.image.load('assets/sprites/2.png').convert_alpha(),
pygame.image.load('assets/sprites/3.png').convert_alpha(),
pygame.image.load('assets/sprites/4.png').convert_alpha(),
pygame.image.load('assets/sprites/5.png').convert_alpha(),
pygame.image.load('assets/sprites/6.png').convert_alpha(),
pygame.image.load('assets/sprites/7.png').convert_alpha(),
pygame.image.load('assets/sprites/8.png').convert_alpha(),
pygame.image.load('assets/sprites/9.png').convert_alpha()
)
#Game Over Sprite
images['Game Over'] = pygame.image.load('assets/sprites/gameover.png').convert_alpha()
#Starting Game sprite
images['Starting Game'] = pygame.image.load('assets/sprites/startgame-screen.png').convert_alpha()
#Flappy Bird Logo sprite
images['Flappy Bird Logo'] = pygame.image.load('assets/sprites/flappybird-logo.png').convert_alpha()
#Base Ground sprite
images['Base Ground'] = pygame.image.load('assets/sprites/base.png').convert_alpha()
#Play Button Up sprite
images['Play Button Up'] = pygame.image.load('assets/sprites/playbutton-up.png').convert_alpha()
#Play Button Down sprite
images['Play Button Down'] = pygame.image.load('assets/sprites/playbutton-down.png').convert_alpha()
#Quit Button Up sprite
#images['Quit Button Up'] = pygame.image.load('assets/sprites/quitbutton-up.png').convert_alpha()
#Quit Button Down sprite
#images['Quit Button Down'] = pygame.image.load('assets/sprites/quitbutton-down.png').convert_alpha()
#Sounds
# sounds['Die'] = pygame.mixer.Sound('assets/audio/die.wav')
# sounds['Hit'] = pygame.mixer.Sound('assets/audio/hit.wav')
# sounds['Point'] = pygame.mixer.Sound('assets/audio/point.wav')
# sounds['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh.wav')
# sounds['wing'] = pygame.mixer.Sound('assets/audio/wing.wav')
while True:
#Select random Background sprites
random_background = random.randint(0, len(background_list) - 1)
images['Background'] = pygame.image.load(background_list[random_background]).convert()
#Select random Player sprites
random_player = random.randint(0, len(player_list) - 1)
images['Player'] = (
pygame.image.load(player_list[random_player][0]).convert_alpha(),
pygame.image.load(player_list[random_player][1]).convert_alpha(),
pygame.image.load(player_list[random_player][2]).convert_alpha()
)
#Select random Pipe sprite
random_pipe = random.randint(0, len(pipe_list) - 1)
images['Pipe'] = pygame.image.load(pipe_list[random_pipe])
main_menu()
pygame.display.update()
clock.tick(FPS)
def button(action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if action == 'Play':
button = images['Play Button Up'].get_rect()
for event in pygame.event.get():
if click[0] == 1:
if button.collidepoint(cur):
print ('Mouse Over')
images['Play Button Down']
main_game()
else:
gameDisplay.blit(images['Play Button Up'], (0, -10))
def main_menu():
global player_index, player_x, player_y
player_index = 0
player_x = int(display_width * 0.2)
player_y = int((display_height - images['Player'] [0].get_height()) / 2)
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
gameDisplay.blit(images['Flappy Bird Logo'], (50, -30))
gameDisplay.blit(images['Player'][player_index], (125, 140))
gameDisplay.blit(images['Play Button Up'], (10, 10))
button(action = 'Play')
pygame.display.update()
clock.tick(FPS)
def main_game():
gameExit = False
gameOver = False
player_x = 0
player_y = 0
while not gameExit:
if gameOver == True:
gameDisplay.blit(images['Game Over'], 50, 50)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Starting Game'], (0, 0))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
gameDisplay.blit(images['Player'][player_index], (player_x, player_y))
pygame.display.update()
clock.tick(FPS)
main()
Here's a minimal example to demonstrate how you can switch between different button images.
When the user presses a mouse button (a pygame.MOUSEBUTTONDOWN event is added to the queue), check if the event.pos collides with the button rect and if it collides, set the image to the "down" version.
When the user releases the button (pygame.MOUSEBUTTONUP event), just set the image back to the original version.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
GRAY = pg.Color('gray15')
BLUE = pg.Color('dodgerblue1')
LIGHTBLUE = pg.Color('lightskyblue1')
BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(LIGHTBLUE)
def main():
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
# Currently selected button image.
button_image = BUTTON_UP_IMG
button_rect = button_image.get_rect(topleft=(200, 200))
x = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if button_rect.collidepoint(event.pos):
button_image = BUTTON_DOWN_IMG
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
button_image = BUTTON_UP_IMG
if button_rect.collidepoint(event.pos):
print('Button pressed.')
x += 1
screen.fill(GRAY)
screen.blit(button_image, button_rect)
txt = font.render(str(x), True, BLUE)
screen.blit(txt, (260, 206))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
I would actually recommend to use classes, pygame sprites and sprite groups instead of just rects and images. Then you can easily create as many instances of the button class as you want.
import pygame as pg
pg.init()
GRAY= pg.Color('gray12')
BLUE = pg.Color('dodgerblue1')
FONT = pg.font.Font(None, 30)
BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(pg.Color('lightskyblue1'))
# The Button is a pygame sprite, that means we can add the
# instances to a sprite group and then update and render them
# by calling `sprite_group.update()` and `sprite_group.draw(screen)`.
class Button(pg.sprite.Sprite):
def __init__(self, pos, callback):
pg.sprite.Sprite.__init__(self)
self.image = BUTTON_UP_IMG
self.rect = self.image.get_rect(topleft=pos)
self.callback = callback
def handle_event(self, event):
"""Handle events that get passed from the event loop."""
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if self.rect.collidepoint(event.pos):
self.image = BUTTON_DOWN_IMG
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
self.image = BUTTON_UP_IMG
if self.rect.collidepoint(event.pos):
print('Button pressed.')
# Call the function that we passed during the
# instantiation. (In this case just `increase_x`.)
self.callback()
class Game:
def __init__(self):
self.screen = pg.display.set_mode((800, 600))
self.clock = pg.time.Clock()
self.x = 0
self.buttons = pg.sprite.Group(
Button((200, 200), callback=self.increase_x),
Button((500, 200), callback=self.decrease_x))
self.done = False
# A callback function that we pass to the button instance.
# It gets called if a collision in the handle_event method
# is detected.
def increase_x(self):
"""Increase self.x if button is pressed."""
self.x += 1
def decrease_x(self):
"""Decrease self.x if button is pressed."""
self.x -= 1
def run(self):
while not self.done:
self.handle_events()
self.run_logic()
self.draw()
self.clock.tick(30)
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
for button in self.buttons:
button.handle_event(event)
def run_logic(self):
self.buttons.update()
def draw(self):
self.screen.fill(GRAY)
self.buttons.draw(self.screen)
txt = FONT.render(str(self.x), True, BLUE)
self.screen.blit(txt, (360, 206))
pg.display.flip()
if __name__ == "__main__":
Game().run()
pg.quit()
Thanks for the Answer to my previous question your answer solved my previous issue, but now I have ran into another issue.
When I press my button on the main menu it changes sprites correctly and loads the main game correctly, but as soon as I move my mouse it switches back to the main menu. My best guess is that it is because it is looping the whole button sequence, but I am not entirely sure.
import pygame
pygame.init()
display_width = 288
display_height = 512
def main_menu():
done = False
play_button_image = images['Play Button Up']
play_button_rect = play_button_image.get_rect(topleft=(30,15))
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if play_button_rect.collidepoint(event.pos):
play_button_image = images['Play Button Down']
elif event.type == MOUSEBUTTONUP:
if event.button == 1:
play_button_image = images['Play Button Up']
if play_button_rect.collidepoint(event.pos):
main_game()
gameDisplay.blit(play_button_image, play_button_rect)
pygame.display.update()
clock.tick(FPS)
def main_game():
gameExit = False
gameOver = False
player_index = 0
player_x = int(display_width * 0.2)
player_y = int((display_height - images['Player'][0].get_height()) / 2)
player_index_gen = cycle([0, 1, 2, 1])
loop_iter = 0
starting_game_x = int((display_width - images['Starting Game'].get_width()) / 2)
starting_game_y = int(display_height * 0.12)
base_x = 0
base_shift = images['Base Ground'].get_width() - images['Background'].get_width()
player_move_vals = {'val': 0, 'dir': 1}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN or pygame.K_SPACE or pygame.K_UP:
sounds['Wing']
return {
'player_y': player_y + player_move_vals['val'],
'base_x': base_x,
'player_index_gen': player_index_gen
}
if (loop_iter + 1) % 5 == 0:
player_index = next(player_index_gen)
loop_iter = (loop_iter + 1) % 30
base_x = -((-base_x + 4) % base_shift)
player_move(player_move_vals)
# draw sprites
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Player'][player_index],
(player_x, player_y + player_move_vals['val']))
gameDisplay.blit(images['Starting Game'], (starting_game_x, starting_game_y))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
pygame.display.update()
clock.tick(FPS)
if __name__ == '__main__':
main()

Resources