redesigned in-game gui; added UiComponentsList class; added goal highlighting; fixed some bugs; code refactoring

This commit is contained in:
JakubR 2021-05-11 20:30:01 +02:00
parent 346fe641d4
commit 0fc5cedae6
5 changed files with 219 additions and 73 deletions

104
main.py
View File

@ -1,4 +1,5 @@
# libraries # libraries
import pygame
import time import time
from pyglet.gl import * # for blocky textures from pyglet.gl import * # for blocky textures
import random import random
@ -6,18 +7,15 @@ import random
import project_constants as const import project_constants as const
import minefield as mf import minefield as mf
import searching_algorithms.a_star as a_star import searching_algorithms.a_star as a_star
from display_assets import blit_graphics from display_assets import blit_graphics
from ui.input_box import * from project_constants import HIGHLIGHT, INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON
from ui.button import * from ui.ui_components_list import UiComponentsList
from ui.text_box import *
import json_generator as jg
def main(): def main():
pygame.init() pygame.init()
pygame.display.set_caption(const.V_NAME_OF_WINDOW) pygame.display.set_caption(const.V_NAME_OF_WINDOW)
jes = jg.JsonGenerator()
print(jes.get_grid())
# for blocky textures # for blocky textures
glEnable(GL_TEXTURE_2D) glEnable(GL_TEXTURE_2D)
@ -33,84 +31,86 @@ def main():
running = True running = True
in_menu = True in_menu = True
# counting coordinates for ui components ui_components_list = UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON])
ib_width, ib_height = 100, 65
bt_width, bt_height = 210, 55
ib_y = 200
ib1_x = const.SCREEN.get_width() / 2 - ib_width - 5
ib2_x = const.SCREEN.get_width() / 2 + 5
bt_x = const.SCREEN.get_width() / 2 - bt_width / 2
bt1_y = ib_y + ib_height + 10
bt2_y = bt1_y + bt_height + 10
# creating ui components used in menu
input1 = InputBox(
position=(ib1_x, ib_y),
dimensions=(ib_width, ib_height),
text="row",
input_centered=True,
clear_input_on_click=True
)
input2 = InputBox(
position=(ib2_x, ib_y),
dimensions=(ib_width, ib_height),
text="column",
input_centered=True,
clear_input_on_click=True
)
random_button = Button(position=(bt_x, bt1_y), dimensions=(bt_width, bt_height), text="random")
ok_button = Button(position=(bt_x, bt2_y), dimensions=(bt_width, bt_height), text="ok")
# initializing goal position # initializing goal position
to_x, to_y = 0, 0 row, column = 0, 0
# drawing map so black screen doesn't appear
blit_graphics(minefield)
while running: while running:
while running and in_menu: while running and in_menu:
const.SCREEN.fill((255, 255, 255))
events = pygame.event.get() events = pygame.event.get()
for event in events: for event in events:
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
running = False running = False
input1.run(const.SCREEN, pygame.mouse.get_pos(), events) # graphics (from display_assets)
input2.run(const.SCREEN, pygame.mouse.get_pos(), events) blit_graphics(minefield)
ok_button.draw(const.SCREEN, pygame.mouse.get_pos()) # drawing gui
random_button.draw(const.SCREEN, pygame.mouse.get_pos()) INPUT_ROW.run(const.SCREEN, pygame.mouse.get_pos(), events)
INPUT_COLUMN.run(const.SCREEN, pygame.mouse.get_pos(), events)
OK_BUTTON.draw(const.SCREEN, pygame.mouse.get_pos())
RANDOM_BUTTON.draw(const.SCREEN, pygame.mouse.get_pos())
if ok_button.is_clicked(pygame.mouse.get_pos(), events): # highlighting chosen tile destination (if exists)
in_menu = False if not(INPUT_ROW.empty() or INPUT_COLUMN.empty()):
if random_button.is_clicked(pygame.mouse.get_pos(), events): row = min(9, int(INPUT_ROW.get_input()))
input1.set_texts(user_input=str(random.randint(0, 9))) column = min(9, int(INPUT_COLUMN.get_input()))
input2.set_texts(user_input=str(random.randint(0, 9))) const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
# updating graphics
pygame.display.flip() pygame.display.flip()
ui_components_list.switch_selected_objects_with_arrow_keys(events, pygame.mouse.get_pos())
OK_BUTTON.set_flags(is_active=(not(INPUT_ROW.empty() or INPUT_COLUMN.empty())))
if OK_BUTTON.is_clicked(pygame.mouse.get_pos(), events) or OK_BUTTON.enter_pressed(events):
in_menu = False
if RANDOM_BUTTON.is_clicked(pygame.mouse.get_pos(), events) or RANDOM_BUTTON.enter_pressed(events):
INPUT_ROW.set_texts(user_input=str(random.randint(0, 9)))
INPUT_COLUMN.set_texts(user_input=str(random.randint(0, 9)))
clock.tick(const.V_FPS) clock.tick(const.V_FPS)
if running: if running:
to_x = int(input1.get_input()) for component in ui_components_list.selectable_ui_components:
to_y = int(input2.get_input()) component.set_flags(is_active=False)
action_sequence = a_star.graphsearch( action_sequence = a_star.graphsearch(
initial_state=a_star.State( initial_state=a_star.State(
row=minefield.agent.position[0], row=minefield.agent.position[0],
column=minefield.agent.position[1], column=minefield.agent.position[1],
direction=minefield.agent.direction), direction=minefield.agent.direction),
minefield=minefield, tox=to_x, toy=to_y) minefield=minefield, tox=row, toy=column)
while running and not in_menu: while running and not in_menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# FPS control # FPS control
clock.tick(const.V_FPS) clock.tick(const.V_FPS)
# graphics (from display_assets) # graphics (from display_assets)
blit_graphics(minefield) blit_graphics(minefield)
const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
# drawing ui components so they don't "disappear"
ui_components_list.draw_all(const.SCREEN, pygame.mouse.get_pos())
# updating graphics
pygame.display.flip()
# make the next move from sequence of actions # make the next move from sequence of actions
if any(action_sequence): if any(action_sequence):
action = action_sequence.pop(0) action = action_sequence.pop(0)
if action == const.Action.ROTATE_LEFT: if action == const.Action.ROTATE_LEFT:
@ -124,10 +124,12 @@ def main():
else: else:
in_menu = True in_menu = True
if not any([x.is_selected for x in ui_components_list.ui_components]):
INPUT_ROW.set_is_selected(True)
time.sleep(const.ACTION_INTERVAL) time.sleep(const.ACTION_INTERVAL)
# 'for event in pygame.event.get():
# if event.type == pygame.QUIT: for component in ui_components_list.selectable_ui_components:
# running = False component.set_flags(is_active=True)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -2,9 +2,14 @@ import pygame
import os import os
from enum import Enum from enum import Enum
from ui.button import Button
from ui.input_box import InputBox
# VARIABLE STARTS WITH ... IF IT'S # VARIABLE STARTS WITH ... IF IT'S
# V a value like a string or an int # V a value like a string or an int
# STRUCT a list or other structure of values # STRUCT a list or other structure of values
# FUNC a function
# OBJ a classes instance
# ASSET a png file (or other graphic format) # ASSET a png file (or other graphic format)
# MAP a JSON map file # MAP a JSON map file
@ -27,9 +32,14 @@ V_NUMBER_PADDING = 50
V_TILE_AREA_HEIGHT = V_TILE_SIZE * V_GRID_VER_TILES V_TILE_AREA_HEIGHT = V_TILE_SIZE * V_GRID_VER_TILES
V_TILE_AREA_WIDTH = V_TILE_SIZE * V_GRID_HOR_TILES V_TILE_AREA_WIDTH = V_TILE_SIZE * V_GRID_HOR_TILES
# side menu values
V_SIDE_MENU_WIDTH = 80
V_INPUT_BOX_HEIGHT = 55
V_BUTTON_HEIGHT = 40
SCREEN = pygame.display.set_mode( SCREEN = pygame.display.set_mode(
( (
V_TILE_AREA_WIDTH + 2 * V_SCREEN_PADDING + V_NUMBER_PADDING, # screen width V_TILE_AREA_WIDTH + 2 * V_SCREEN_PADDING + V_NUMBER_PADDING + V_SIDE_MENU_WIDTH + 10, # screen width
V_TILE_AREA_HEIGHT + 2 * V_SCREEN_PADDING + V_NUMBER_PADDING # screen height V_TILE_AREA_HEIGHT + 2 * V_SCREEN_PADDING + V_NUMBER_PADDING # screen height
) )
) )
@ -119,6 +129,65 @@ STRUCT_MINE_ATTRIBUTE_TYPES = {
"time": [int] "time": [int]
} }
# ============== #
# ==== FUNC ==== #
# ============== #
def get_tile_coordinates(position):
row, column = position
padding = V_SCREEN_PADDING + V_NUMBER_PADDING
return tuple((padding + column * V_TILE_SIZE, padding + row * V_TILE_SIZE))
# ============= #
# ==== OBJ ==== #
# ============= #
# initializing pygame modules so all instances can be constructed properly
pygame.init()
# creating ui components used in menu
HIGHLIGHT = pygame.transform.scale(
pygame.image.load(os.path.join(DIR_ASSETS, "old_tiles/tile_white.png")),
(V_TILE_SIZE, V_TILE_SIZE)
)
HIGHLIGHT.set_alpha(100)
_gui_width = V_SIDE_MENU_WIDTH
_ib_height, _bt_height = V_INPUT_BOX_HEIGHT, V_BUTTON_HEIGHT
_gui_x = V_TILE_AREA_WIDTH + 2 * V_SCREEN_PADDING + V_NUMBER_PADDING
_gui_y = SCREEN.get_height() / 2 - (2 * _ib_height + 2 * _bt_height + 30) / 2
INPUT_ROW = InputBox(
position=(_gui_x, _gui_y),
dimensions=(_gui_width, _ib_height),
text="row",
valid_input_characters="123456890",
input_centered=True,
clear_input_on_click=True
)
INPUT_COLUMN = InputBox(
position=(_gui_x, _gui_y + _ib_height + 10),
dimensions=(_gui_width, _ib_height),
text="column",
valid_input_characters="1234567890",
input_centered=True,
clear_input_on_click=True
)
RANDOM_BUTTON = Button(
position=(_gui_x, _gui_y + 2 * _ib_height + 20),
dimensions=(_gui_width, _bt_height),
text="random"
)
OK_BUTTON = Button(
position=(_gui_x, _gui_y + 2 * _ib_height + _bt_height + 30),
dimensions=(_gui_width, _bt_height),
text="ok"
)
# ============== # # ============== #
# ==== MAPS ==== # # ==== MAPS ==== #
# ============== # # ============== #

View File

@ -9,6 +9,8 @@ from ui.text_box import TextBox
class Button(TextBox): class Button(TextBox):
was_enter_hit = False
# constructor that can be used to set parameters of Button instance # constructor that can be used to set parameters of Button instance
def __init__( def __init__(
self, self,
@ -21,6 +23,7 @@ class Button(TextBox):
font_size=None, font_size=None,
is_visible=True, is_visible=True,
is_active=True, is_active=True,
is_selected=False,
fit_text=True, fit_text=True,
outline=True, outline=True,
irregular_outline=False, irregular_outline=False,
@ -30,10 +33,13 @@ class Button(TextBox):
outline_color=(50, 50, 50), outline_color=(50, 50, 50),
box_transform_hover=1.16, box_transform_hover=1.16,
box_transform_inactive=0.9, box_transform_inactive=0.9,
box_transform_selected=1.16,
font_transform_hover=1.24, font_transform_hover=1.24,
font_transform_inactive=0.85, font_transform_inactive=0.85,
font_transform_selected=1.24,
outline_transform_hover=1, outline_transform_hover=1,
outline_transform_inactive=0.9 outline_transform_inactive=0.9,
outline_transform_selected=1
): ):
# calling base class constructor # calling base class constructor
super().__init__( super().__init__(
@ -56,20 +62,27 @@ class Button(TextBox):
# setting attributes # setting attributes
self.is_active = is_active self.is_active = is_active
self.is_selected = is_selected
self.box_transform_hover = box_transform_hover self.box_transform_hover = box_transform_hover
self.box_transform_inactive = box_transform_inactive self.box_transform_inactive = box_transform_inactive
self.box_transform_selected = box_transform_selected
self.font_transform_hover = font_transform_hover self.font_transform_hover = font_transform_hover
self.font_transform_inactive = font_transform_inactive self.font_transform_inactive = font_transform_inactive
self.font_transform_selected = font_transform_selected
self.outline_transform_hover = outline_transform_hover self.outline_transform_hover = outline_transform_hover
self.outline_transform_inactive = outline_transform_inactive self.outline_transform_inactive = outline_transform_inactive
self.outline_transform_selected = outline_transform_selected
# counting colors on: hover, inactive # counting colors on: hover, inactive and selected
self.box_hover_color = get_fixed_rgb(_transform_rgb(self.box_color, self.box_transform_hover)) self.box_hover_color = get_fixed_rgb(_transform_rgb(self.box_color, self.box_transform_hover))
self.box_inactive_color = get_fixed_rgb(_transform_rgb(self.box_color, self.box_transform_inactive)) self.box_inactive_color = get_fixed_rgb(_transform_rgb(self.box_color, self.box_transform_inactive))
self.box_selected_color = get_fixed_rgb(_transform_rgb(self.box_color, self.box_transform_selected))
self.font_hover_color = get_fixed_rgb(_transform_rgb(self.font_color, self.font_transform_hover)) self.font_hover_color = get_fixed_rgb(_transform_rgb(self.font_color, self.font_transform_hover))
self.font_inactive_color = get_fixed_rgb(_transform_rgb(self.font_color, self.font_transform_inactive)) self.font_inactive_color = get_fixed_rgb(_transform_rgb(self.font_color, self.font_transform_inactive))
self.font_selected_color = get_fixed_rgb(_transform_rgb(self.font_color, self.font_transform_selected))
self.outline_hover_color = get_fixed_rgb(_transform_rgb(self.outline_color, self.outline_transform_hover)) self.outline_hover_color = get_fixed_rgb(_transform_rgb(self.outline_color, self.outline_transform_hover))
self.outline_inactive_color = get_fixed_rgb(_transform_rgb(self.outline_color, self.outline_transform_inactive)) self.outline_inactive_color = get_fixed_rgb(_transform_rgb(self.outline_color, self.outline_transform_inactive))
self.outline_selected_color = get_fixed_rgb(_transform_rgb(self.outline_color, self.outline_transform_selected))
# draws the Button instance # draws the Button instance
@_return_itself @_return_itself
@ -80,14 +93,20 @@ class Button(TextBox):
outline_attribute_value = self.outline_color outline_attribute_value = self.outline_color
# temporarily changing box and font color if necessary # temporarily changing box and font color if necessary
# is selected
if self.is_selected:
self.box_color = self.box_selected_color
self.font_color = self.font_selected_color
self.outline_color = self.outline_selected_color
# is active and mouse is over # is active and mouse is over
if self.is_active and self.is_over(mouse_position): elif self.is_active and self.is_over(mouse_position):
self.box_color = self.box_hover_color self.box_color = self.box_hover_color
self.font_color = self.font_hover_color self.font_color = self.font_hover_color
self.outline_color = self.outline_hover_color self.outline_color = self.outline_hover_color
# is not active # is not active
else: elif not self.is_active:
self.box_color = self.box_inactive_color self.box_color = self.box_inactive_color
self.font_color = self.font_inactive_color self.font_color = self.font_inactive_color
self.outline_color = self.outline_inactive_color self.outline_color = self.outline_inactive_color
@ -112,6 +131,20 @@ class Button(TextBox):
return False return False
# returns True if enter key is hit, or False otherwise
def enter_pressed(self, events):
if self.is_active and self.is_selected:
for event in events:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
return True
return self.was_enter_hit
# sets is_selected attribute to given value
def set_is_selected(self, is_selected):
self.is_selected = is_selected
# sets chosen color attributes # sets chosen color attributes
def set_colors( def set_colors(
self, self,

View File

@ -25,6 +25,7 @@ class InputBox(Button):
input_box_position=None, input_box_position=None,
input_box_dimensions=None, input_box_dimensions=None,
user_input="", user_input="",
valid_input_characters="abcdefghijklmnoprstuwxyz1234567890",
box_color=(195, 195, 195), box_color=(195, 195, 195),
input_box_color=(225, 245, 245), input_box_color=(225, 245, 245),
writing_highlight_color=(150, 255, 255), writing_highlight_color=(150, 255, 255),
@ -35,6 +36,7 @@ class InputBox(Button):
font_size=None, font_size=None,
is_visible=True, is_visible=True,
is_active=True, is_active=True,
is_selected=False,
input_centered=False, input_centered=False,
fit_text=True, fit_text=True,
clear_input_on_click=False, clear_input_on_click=False,
@ -46,8 +48,10 @@ class InputBox(Button):
outline_color=(50, 50, 50), outline_color=(50, 50, 50),
box_transform_hover=1, box_transform_hover=1,
box_transform_inactive=1, box_transform_inactive=1,
box_transform_selected=1,
font_transform_hover=1, font_transform_hover=1,
font_transform_inactive=1, font_transform_inactive=1,
font_transform_selected=1,
outline_transform_hover=1, outline_transform_hover=1,
outline_transform_inactive=0.9, outline_transform_inactive=0.9,
input_box_transform_hover=1.07, input_box_transform_hover=1.07,
@ -62,6 +66,7 @@ class InputBox(Button):
font_color=font_color, font_color=font_color,
is_visible=is_visible, is_visible=is_visible,
is_active=is_active, is_active=is_active,
is_selected=is_selected,
fit_text=fit_text, fit_text=fit_text,
outline=outline, outline=outline,
irregular_outline=irregular_outline, irregular_outline=irregular_outline,
@ -71,8 +76,10 @@ class InputBox(Button):
outline_color=outline_color, outline_color=outline_color,
box_transform_hover=box_transform_hover, box_transform_hover=box_transform_hover,
box_transform_inactive=box_transform_inactive, box_transform_inactive=box_transform_inactive,
box_transform_selected=box_transform_selected,
font_transform_hover=font_transform_hover, font_transform_hover=font_transform_hover,
font_transform_inactive=font_transform_inactive, font_transform_inactive=font_transform_inactive,
font_transform_selected=font_transform_selected,
outline_transform_hover=outline_transform_hover, outline_transform_hover=outline_transform_hover,
outline_transform_inactive=outline_transform_inactive outline_transform_inactive=outline_transform_inactive
) )
@ -95,6 +102,7 @@ class InputBox(Button):
self.input_box_position = _return_value_or_default(input_box_position, default_input_box_position) self.input_box_position = _return_value_or_default(input_box_position, default_input_box_position)
self.input_box_dimensions = _return_value_or_default(input_box_dimensions, default_input_box_dimensions) self.input_box_dimensions = _return_value_or_default(input_box_dimensions, default_input_box_dimensions)
self.user_input = user_input self.user_input = user_input
self.valid_input_characters = valid_input_characters
self.ib_color = get_fixed_rgb(input_box_color) self.ib_color = get_fixed_rgb(input_box_color)
self.typing_highlight_color = get_fixed_rgb(writing_highlight_color) self.typing_highlight_color = get_fixed_rgb(writing_highlight_color)
self.inner_box_color = get_fixed_rgb(inner_box_color) self.inner_box_color = get_fixed_rgb(inner_box_color)
@ -104,7 +112,6 @@ class InputBox(Button):
self.font = pygame.font.SysFont(font, self.font_size) self.font = pygame.font.SysFont(font, self.font_size)
self.input_font_size = max(15, floor(self.input_box_dimensions[1] - 2)) self.input_font_size = max(15, floor(self.input_box_dimensions[1] - 2))
self.input_font = pygame.font.SysFont(font, self.input_font_size) self.input_font = pygame.font.SysFont(font, self.input_font_size)
self.is_selected = False
self.is_active = is_active self.is_active = is_active
self.input_centered = input_centered self.input_centered = input_centered
self.ib_transform_hover = input_box_transform_hover self.ib_transform_hover = input_box_transform_hover
@ -224,8 +231,10 @@ class InputBox(Button):
for event in events: for event in events:
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE: if event.key == pygame.K_BACKSPACE:
if not self.backspace_pressed_down: if not self.backspace_pressed_down:
self.user_input = self.user_input[:-1] self.user_input = self.user_input[:-1]
self.backspace_pressed_down = True self.backspace_pressed_down = True
elif event.key == pygame.K_RETURN: elif event.key == pygame.K_RETURN:
@ -234,7 +243,8 @@ class InputBox(Button):
# if text isn't too long - adding a character # if text isn't too long - adding a character
elif rendered_input.get_width() + 10 < self.input_box_dimensions[0]: elif rendered_input.get_width() + 10 < self.input_box_dimensions[0]:
self.user_input += event.unicode if event.unicode in self.valid_input_characters:
self.user_input += event.unicode
elif event.type == pygame.KEYUP: elif event.type == pygame.KEYUP:
if event.key == pygame.K_BACKSPACE: if event.key == pygame.K_BACKSPACE:
@ -260,23 +270,13 @@ class InputBox(Button):
self.clear_input() self.clear_input()
# returns True if user input is empty, or False otherwise # returns True if user input is empty, or False otherwise
def is_input_empty(self): def empty(self):
return not any(self.user_input) return not any(self.user_input)
# clears user's input # clears user's input
def clear_input(self): def clear_input(self):
self.user_input = "" self.user_input = ""
# returns True if enter key is hit, or False otherwise
def is_enter_hit(self, events):
if self.is_active and self.is_selected:
for event in events:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
return True
return self.was_enter_hit
# returns InputBox'es user's input # returns InputBox'es user's input
def get_input(self): def get_input(self):
return self.user_input return self.user_input
@ -402,10 +402,6 @@ class InputBox(Button):
if fit_text is not None: if fit_text is not None:
self.fit_text = fit_text self.fit_text = fit_text
# sets is_selected with a given value
def set_is_selected(self, is_selected):
self.is_selected = is_selected
def _delete_characters_if_backspace_pressed(self): def _delete_characters_if_backspace_pressed(self):
if self.backspace_pressed_down and \ if self.backspace_pressed_down and \
(self.backspace_tick + 1) % self.deleting_speeds[0] == 0 and \ (self.backspace_tick + 1) % self.deleting_speeds[0] == 0 and \

46
ui/ui_components_list.py Normal file
View File

@ -0,0 +1,46 @@
import pygame
from ui.button import Button
class UiComponentsList:
selected = 0
def __init__(self, ui_components=None, select_first_item=True):
if ui_components is not None:
self.ui_components = ui_components
self.selectable_ui_components = list()
for component in ui_components:
self.selectable_ui_components.append(component) if issubclass(component.__class__, Button) else 0
else:
self.ui_components = list()
self.selectable_ui_components[0].set_is_selected(select_first_item)
def draw_all(self, window, mouse_position):
for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components):
component.draw(window)
for component in self.selectable_ui_components:
component.draw(window, mouse_position)
def switch_selected_objects_with_arrow_keys(self, events, mouse_position):
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.selectable_ui_components[self.selected].set_is_selected(False)
if event.key == pygame.MOUSEBUTTONDOWN:
selected_component = self.selectable_ui_components[self.selected]
selected_component.set_is_selected(selected_component.is_over(mouse_position))
if event.key == pygame.K_UP or event.key == pygame.K_LEFT:
self.selectable_ui_components[self.selected].set_is_selected(False)
self.selected = self.selected - 1 if self.selected != 0 else len(self.selectable_ui_components) - 1
self.selectable_ui_components[self.selected].set_is_selected(True)
if event.key == pygame.K_DOWN or event.key == pygame.K_RIGHT:
self.selectable_ui_components[self.selected].set_is_selected(False)
self.selected = (self.selected + 1) % len(self.selectable_ui_components)
self.selectable_ui_components[self.selected].set_is_selected(True)