Skip to content

Instantly share code, notes, and snippets.

@programmingpixels
Last active August 23, 2022 04:35
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save programmingpixels/27b7f8f59ec53b401183c68f4be1634b to your computer and use it in GitHub Desktop.
Save programmingpixels/27b7f8f59ec53b401183c68f4be1634b to your computer and use it in GitHub Desktop.
import pygame
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
BLUE = (106, 159, 181)
WHITE = (255, 255, 255)
def create_surface_with_text(text, font_size, text_rgb, bg_rgb):
""" Returns surface with text written on """
font = pygame.freetype.SysFont("Courier", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)
return surface.convert_alpha()
class UIElement(Sprite):
""" An user interface element that can be added to a surface """
def __init__(self, center_position, text, font_size, bg_rgb, text_rgb):
"""
Args:
center_position - tuple (x, y)
text - string of text to write
font_size - int
bg_rgb (background colour) - tuple (r, g, b)
text_rgb (text colour) - tuple (r, g, b)
"""
self.mouse_over = False # indicates if the mouse over the element
# create the default image
default_image = create_surface_with_text(
text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
)
# create the image that shows when mouse is over the element
highlighted_image = create_surface_with_text(
text=text, font_size=font_size * 1.2, text_rgb=text_rgb, bg_rgb=bg_rgb
)
# add both images and their rects to lists
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center=center_position),
highlighted_image.get_rect(center=center_position),
]
# calls the init method of the parent sprite class
super().__init__()
# properties that vary the image and its rect when the mouse is over the element
@property
def image(self):
return self.images[1] if self.mouse_over else self.images[0]
@property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos):
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
else:
self.mouse_over = False
def draw(self, surface):
""" Draws element onto a surface """
surface.blit(self.image, self.rect)
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
# create a ui element
uielement = UIElement(
center_position=(400, 400),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Hello World",
)
# main loop
while True:
for event in pygame.event.get():
pass
screen.fill(BLUE)
uielement.update(pygame.mouse.get_pos())
uielement.draw(screen)
pygame.display.flip()
# call main when the script is run
if __name__ == "__main__":
main()
import pygame
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
BLUE = (106, 159, 181)
WHITE = (255, 255, 255)
def create_surface_with_text(text, font_size, text_rgb):
""" Returns surface with text written on """
font = pygame.freetype.SysFont("Courier", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_rgb)
return surface.convert_alpha()
class UIElement(Sprite):
""" An user interface element that can be added to a surface """
def __init__(self, center_position, text, font_size, text_rgb):
"""
Args:
center_position - tuple (x, y)
text - string of text to write
font_size - int
text_rgb (text colour) - tuple (r, g, b)
"""
self.mouse_over = False # indicates if the mouse over the element
# create the default image
default_image = create_surface_with_text(
text=text, font_size=font_size, text_rgb=text_rgb
)
# create the image that shows when mouse is over the element
highlighted_image = create_surface_with_text(
text=text, font_size=font_size * 1.2, text_rgb=text_rgb
)
# add both images and their rects to lists
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center=center_position),
highlighted_image.get_rect(center=center_position),
]
# calls the init method of the parent sprite class
super().__init__()
# properties that vary the image and its rect when the mouse is over the element
@property
def image(self):
return self.images[1] if self.mouse_over else self.images[0]
@property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos):
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
else:
self.mouse_over = False
def draw(self, surface):
""" Draws element onto a surface """
surface.blit(self.image, self.rect)
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
# create a ui element
uielement = UIElement(
center_position=(400, 400),
font_size=30,
text_rgb=WHITE,
text="Hello World",
)
# main loop
while True:
for event in pygame.event.get():
pass
screen.fill(BLUE)
uielement.update(pygame.mouse.get_pos())
uielement.draw(screen)
pygame.display.flip()
# call main when the script is run
if __name__ == "__main__":
main()
import pygame
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
from enum import Enum
BLUE = (106, 159, 181)
WHITE = (255, 255, 255)
def create_surface_with_text(text, font_size, text_rgb, bg_rgb):
""" Returns surface with text written on """
font = pygame.freetype.SysFont("Courier", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)
return surface.convert_alpha()
class UIElement(Sprite):
""" An user interface element that can be added to a surface """
def __init__(self, center_position, text, font_size, bg_rgb, text_rgb, action=None):
"""
Args:
center_position - tuple (x, y)
text - string of text to write
font_size - int
bg_rgb (background colour) - tuple (r, g, b)
text_rgb (text colour) - tuple (r, g, b)
action - the gamestate change associated with this button
"""
self.mouse_over = False
default_image = create_surface_with_text(
text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
)
highlighted_image = create_surface_with_text(
text=text, font_size=font_size * 1.2, text_rgb=text_rgb, bg_rgb=bg_rgb
)
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center=center_position),
highlighted_image.get_rect(center=center_position),
]
# assign button action
self.action = action
super().__init__()
@property
def image(self):
return self.images[1] if self.mouse_over else self.images[0]
@property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos, mouse_up):
""" Updates the mouse_over variable and returns the button's
action value when clicked.
"""
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
if mouse_up:
return self.action
else:
self.mouse_over = False
def draw(self, surface):
""" Draws element onto a surface """
surface.blit(self.image, self.rect)
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
quit_btn = UIElement(
center_position=(400, 500),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Quit",
action=GameState.QUIT,
)
# main loop
while True:
mouse_up = False
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
mouse_up = True
screen.fill(BLUE)
ui_action = quit_btn.update(pygame.mouse.get_pos(), mouse_up)
if ui_action is not None:
return
quit_btn.draw(screen)
pygame.display.flip()
class GameState(Enum):
QUIT = -1
if __name__ == "__main__":
main()
import pygame
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
from enum import Enum
BLUE = (106, 159, 181)
WHITE = (255, 255, 255)
def create_surface_with_text(text, font_size, text_rgb, bg_rgb):
""" Returns surface with text written on """
font = pygame.freetype.SysFont("Courier", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)
return surface.convert_alpha()
class UIElement(Sprite):
""" An user interface element that can be added to a surface """
def __init__(self, center_position, text, font_size, bg_rgb, text_rgb, action=None):
"""
Args:
center_position - tuple (x, y)
text - string of text to write
font_size - int
bg_rgb (background colour) - tuple (r, g, b)
text_rgb (text colour) - tuple (r, g, b)
action - the gamestate change associated with this button
"""
self.mouse_over = False
default_image = create_surface_with_text(
text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
)
highlighted_image = create_surface_with_text(
text=text, font_size=font_size * 1.2, text_rgb=text_rgb, bg_rgb=bg_rgb
)
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center=center_position),
highlighted_image.get_rect(center=center_position),
]
self.action = action
super().__init__()
@property
def image(self):
return self.images[1] if self.mouse_over else self.images[0]
@property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos, mouse_up):
""" Updates the mouse_over variable and returns the button's
action value when clicked.
"""
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
if mouse_up:
return self.action
else:
self.mouse_over = False
def draw(self, surface):
""" Draws element onto a surface """
surface.blit(self.image, self.rect)
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
game_state = GameState.TITLE
while True:
if game_state == GameState.TITLE:
game_state = title_screen(screen)
if game_state == GameState.NEWGAME:
game_state = play_level(screen)
if game_state == GameState.QUIT:
pygame.quit()
return
def title_screen(screen):
start_btn = UIElement(
center_position=(400, 400),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Start",
action=GameState.NEWGAME,
)
quit_btn = UIElement(
center_position=(400, 500),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Quit",
action=GameState.QUIT,
)
buttons = [start_btn, quit_btn]
while True:
mouse_up = False
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
mouse_up = True
screen.fill(BLUE)
for button in buttons:
ui_action = button.update(pygame.mouse.get_pos(), mouse_up)
if ui_action is not None:
return ui_action
button.draw(screen)
pygame.display.flip()
def play_level(screen):
return_btn = UIElement(
center_position=(140, 570),
font_size=20,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Return to main menu",
action=GameState.TITLE,
)
while True:
mouse_up = False
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
mouse_up = True
screen.fill(BLUE)
ui_action = return_btn.update(pygame.mouse.get_pos(), mouse_up)
if ui_action is not None:
return ui_action
return_btn.draw(screen)
pygame.display.flip()
class GameState(Enum):
QUIT = -1
TITLE = 0
NEWGAME = 1
if __name__ == "__main__":
main()
import pygame
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
from enum import Enum
from pygame.sprite import RenderUpdates
BLUE = (106, 159, 181)
WHITE = (255, 255, 255)
def create_surface_with_text(text, font_size, text_rgb, bg_rgb):
""" Returns surface with text written on """
font = pygame.freetype.SysFont("Courier", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)
return surface.convert_alpha()
class UIElement(Sprite):
""" An user interface element that can be added to a surface """
def __init__(self, center_position, text, font_size, bg_rgb, text_rgb, action=None):
"""
Args:
center_position - tuple (x, y)
text - string of text to write
font_size - int
bg_rgb (background colour) - tuple (r, g, b)
text_rgb (text colour) - tuple (r, g, b)
action - the gamestate change associated with this button
"""
self.mouse_over = False
default_image = create_surface_with_text(
text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
)
highlighted_image = create_surface_with_text(
text=text, font_size=font_size * 1.2, text_rgb=text_rgb, bg_rgb=bg_rgb
)
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center=center_position),
highlighted_image.get_rect(center=center_position),
]
self.action = action
super().__init__()
@property
def image(self):
return self.images[1] if self.mouse_over else self.images[0]
@property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos, mouse_up):
""" Updates the mouse_over variable and returns the button's
action value when clicked.
"""
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
if mouse_up:
return self.action
else:
self.mouse_over = False
def draw(self, surface):
""" Draws element onto a surface """
surface.blit(self.image, self.rect)
class Player:
""" Stores information about a player """
def __init__(self, score=0, lives=3, current_level=1):
self.score = score
self.lives = lives
self.current_level = current_level
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
game_state = GameState.TITLE
while True:
if game_state == GameState.TITLE:
game_state = title_screen(screen)
if game_state == GameState.NEWGAME:
player = Player()
game_state = play_level(screen, player)
if game_state == GameState.NEXT_LEVEL:
player.current_level += 1
game_state = play_level(screen, player)
if game_state == GameState.QUIT:
pygame.quit()
return
def title_screen(screen):
start_btn = UIElement(
center_position=(400, 400),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Start",
action=GameState.NEWGAME,
)
quit_btn = UIElement(
center_position=(400, 500),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Quit",
action=GameState.QUIT,
)
buttons = RenderUpdates(start_btn, quit_btn)
return game_loop(screen, buttons)
def play_level(screen, player):
return_btn = UIElement(
center_position=(140, 570),
font_size=20,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Return to main menu",
action=GameState.TITLE,
)
nextlevel_btn = UIElement(
center_position=(400, 400),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text=f"Next level ({player.current_level + 1})",
action=GameState.NEXT_LEVEL,
)
buttons = RenderUpdates(return_btn, nextlevel_btn)
return game_loop(screen, buttons)
def game_loop(screen, buttons):
""" Handles game loop until an action is return by a button in the
buttons sprite renderer.
"""
while True:
mouse_up = False
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
mouse_up = True
screen.fill(BLUE)
for button in buttons:
ui_action = button.update(pygame.mouse.get_pos(), mouse_up)
if ui_action is not None:
return ui_action
buttons.draw(screen)
pygame.display.flip()
class GameState(Enum):
QUIT = -1
TITLE = 0
NEWGAME = 1
NEXT_LEVEL = 2
if __name__ == "__main__":
main()
import pygame
import pygame.freetype
from pygame.sprite import Sprite
from pygame.rect import Rect
from enum import Enum
from pygame.sprite import RenderUpdates
BLUE = (106, 159, 181)
WHITE = (255, 255, 255)
def create_surface_with_text(text, font_size, text_rgb, bg_rgb):
""" Returns surface with text written on """
font = pygame.freetype.SysFont("Courier", font_size, bold=True)
surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)
return surface.convert_alpha()
class UIElement(Sprite):
""" An user interface element that can be added to a surface """
def __init__(self, center_position, text, font_size, bg_rgb, text_rgb, action=None):
"""
Args:
center_position - tuple (x, y)
text - string of text to write
font_size - int
bg_rgb (background colour) - tuple (r, g, b)
text_rgb (text colour) - tuple (r, g, b)
action - the gamestate change associated with this button
"""
self.mouse_over = False
default_image = create_surface_with_text(
text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
)
highlighted_image = create_surface_with_text(
text=text, font_size=font_size * 1.2, text_rgb=text_rgb, bg_rgb=bg_rgb
)
self.images = [default_image, highlighted_image]
self.rects = [
default_image.get_rect(center=center_position),
highlighted_image.get_rect(center=center_position),
]
self.action = action
super().__init__()
@property
def image(self):
return self.images[1] if self.mouse_over else self.images[0]
@property
def rect(self):
return self.rects[1] if self.mouse_over else self.rects[0]
def update(self, mouse_pos, mouse_up):
""" Updates the mouse_over variable and returns the button's
action value when clicked.
"""
if self.rect.collidepoint(mouse_pos):
self.mouse_over = True
if mouse_up:
return self.action
else:
self.mouse_over = False
def draw(self, surface):
""" Draws element onto a surface """
surface.blit(self.image, self.rect)
class Player:
""" Stores information about a player """
def __init__(self, score=0, lives=3, current_level=1):
self.score = score
self.lives = lives
self.current_level = current_level
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
game_state = GameState.TITLE
while True:
if game_state == GameState.TITLE:
game_state = title_screen(screen)
if game_state == GameState.NEWGAME:
player = Player()
game_state = play_level(screen, player)
if game_state == GameState.NEXT_LEVEL:
player.current_level += 1
game_state = play_level(screen, player)
if game_state == GameState.QUIT:
pygame.quit()
return
def title_screen(screen):
start_btn = UIElement(
center_position=(400, 400),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Start",
action=GameState.NEWGAME,
)
quit_btn = UIElement(
center_position=(400, 500),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Quit",
action=GameState.QUIT,
)
buttons = RenderUpdates(start_btn, quit_btn)
return game_loop(screen, buttons)
def play_level(screen, player):
return_btn = UIElement(
center_position=(140, 570),
font_size=20,
bg_rgb=BLUE,
text_rgb=WHITE,
text="Return to main menu",
action=GameState.TITLE,
)
nextlevel_btn = UIElement(
center_position=(400, 400),
font_size=30,
bg_rgb=BLUE,
text_rgb=WHITE,
text=f"Next level ({player.current_level + 1})",
action=GameState.NEXT_LEVEL,
)
buttons = RenderUpdates(return_btn, nextlevel_btn)
return game_loop(screen, buttons)
def game_loop(screen, buttons):
""" Handles game loop until an action is return by a button in the
buttons sprite renderer.
"""
while True:
mouse_up = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
mouse_up = True
screen.fill(BLUE)
for button in buttons:
ui_action = button.update(pygame.mouse.get_pos(), mouse_up)
if ui_action is not None:
return ui_action
buttons.draw(screen)
pygame.display.flip()
class GameState(Enum):
QUIT = -1
TITLE = 0
NEWGAME = 1
NEXT_LEVEL = 2
if __name__ == "__main__":
main()
@earlyraven
Copy link

In step4_additional.py when clicking the top right X, it quits, but several error messages are shown. To make it cleaner, I did the following:
Right after line 164 with pygame.quit()
add the following line:
sys.exit()
then, add the corresponding import at the top.
import sys

@programmingpixels Thanks for the video! Feel free to incorporate the above change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment