Skip to main content

Overview

Nyuron uses consistent UI patterns across all minigames to provide a familiar player experience. This guide documents the common UI components and their implementation.

Intro Panel

Every minigame starts with an intro panel that explains the game and provides a play button.

Structure

intro_panel (Control)
├── Background (ColorRect or Panel)
├── Label (game instructions)
└── Button ("Jugar" / "Play")

Implementation

@onready var intro_panel: Control = $intro_panel
@onready var play_button: Button = $intro_panel/Button

func _ready():
	intro_panel.visible = true
	play_button.pressed.connect(_on_play_pressed)
	
	# Hide game UI during intro
	backButton.visible = false
	# Disable gameplay
	player.process_mode = Node.PROCESS_MODE_DISABLED

func _on_play_pressed() -> void:
	var t := create_tween()
	t.tween_property(intro_panel, "modulate:a", 0.0, 0.4)
	await t.finished
	intro_panel.visible = false
	
	# Enable gameplay
	player.process_mode = Node.PROCESS_MODE_INHERIT
	backButton.visible = true

Example from food_catch

Location: minigames/food_catch/main.gd:106-127
func _ready() -> void:
	# ...
	intro_panel.visible = true
	player.process_mode = Node.PROCESS_MODE_DISABLED
	$SpawnTimer.stop()
	backButton.visible = false
	touch_left.visible = false
	touch_right.visible = false
	
	play_button.pressed.connect(_on_play_pressed)

Game Over Panel

Displays when the game ends, showing score, coins earned, and navigation options.

Structure

GameOverPanel (Panel)
├── Title (Label) - "¡Fin del Juego!" or "Pausa"
├── Score (Label) - "Puntos: X"
├── CoinsEarned (Label) - "Monedas obtenidas: +X"
└── Buttons (HBoxContainer)
    ├── RetryButton (TextureButton)
    └── BackButton (TextureButton)

Implementation

@onready var panel: Panel = $CanvasLayer/GameOverPanel
@onready var title: Label = $CanvasLayer/GameOverPanel/Title
@onready var score_lbl: Label = $CanvasLayer/GameOverPanel/Score
@onready var retry_btn: TextureButton = $CanvasLayer/GameOverPanel/Buttons/RetryButton
@onready var back_btn: TextureButton = $CanvasLayer/GameOverPanel/Buttons/BackButton

func _ready():
	panel.visible = false
	# Important: Allow interaction during pause
	retry_btn.process_mode = Node.PROCESS_MODE_ALWAYS
	back_btn.process_mode = Node.PROCESS_MODE_ALWAYS
	retry_btn.mouse_filter = Control.MOUSE_FILTER_STOP
	back_btn.mouse_filter = Control.MOUSE_FILTER_STOP
	
	retry_btn.pressed.connect(_on_retry_pressed)
	back_btn.pressed.connect(_on_back_pressed)

func _show_game_over() -> void:
	title.text = "¡Fin del Juego!"
	score_lbl.text = "Puntos: %d" % score
	$CanvasLayer/GameOverPanel/CoinsEarned.text = "Monedas obtenidas: +%d" % last_coins_gained
	panel.visible = true
	backButton.visible = false

Example from turtle_run

Location: minigames/turtle_run/scripts/Main.gd:190-197
func _show_game_over() -> void:
	title_lbl.text = "Fin del Juego"
	score_lbl.text = "Puntos: %d" % int(score)
	$UI/GameOverPanel/CoinsEarned.text = "Monedas obtenidas: +%d" % last_coins_gained
	
	panel.visible = true
	back_btn.visible = true

Pause Menu

The same GameOverPanel is reused for pause functionality with different text.

Implementation

func pause_game():
	is_paused = true
	
	# Stop game timers
	$SpawnTimer.paused = true
	
	# Disable gameplay
	player.process_mode = Node.PROCESS_MODE_DISABLED
	
	# Show pause panel
	panel.visible = true
	title.text = "Pausa"
	score_lbl.text = "Puntos: %d" % score
	back_btn.visible = true

func resume_game():
	is_paused = false
	
	# Resume timers
	$SpawnTimer.paused = false
	
	# Enable gameplay
	player.process_mode = Node.PROCESS_MODE_INHERIT
	
	# Hide panel
	panel.visible = false

HUD Elements

Score Label

Persistent score display during gameplay.
@onready var score_label: Label = $CanvasLayer/ScoreLabel

func _update_hud() -> void:
	score_label.text = "Puntos: %d" % score

Lives/Health Display

For games with lives:
@onready var lives_label: Label = $CanvasLayer/LivesLabel

func _update_hud() -> void:
	score_label.text = "Puntos: %d" % score
	lives_label.text = "Vidas: %d" % lives

Flash Effects

Provide visual feedback on score or damage:
func _flash_label(label: Label, flash_color: Color, in_time := 0.06, out_time := 0.22):
	var base := label.modulate
	var t := create_tween()
	t.tween_property(label, "modulate", flash_color, in_time)
	t.tween_property(label, "modulate", base, out_time)

func _hud_damage_flash(): 
	_flash_label(lives_label, Color(1.0, 0.4, 0.4))

func _hud_pop_flash():    
	_flash_label(score_label, Color("60e55aff"))
Example: minigames/food_catch/main.gd:61-71

Back Button

A persistent button that toggles pause.
@onready var backButton: Button = $CanvasLayer/backButton

func _ready():
	backButton.pressed.connect(_on_backButton_pressed)
	# Hidden during intro
	backButton.visible = false

func _on_backButton_pressed() -> void:
	if not is_paused:
		pause_game()
	else:
		resume_game()

Touch Controls

For mobile input, use transparent touch areas.

Structure

CanvasLayer
├── TouchLeft (ColorRect or TextureRect)
└── TouchRight (ColorRect or TextureRect)

Implementation

@onready var touch_left = $CanvasLayer/TouchLeft
@onready var touch_right = $CanvasLayer/TouchRight

func _ready():
	touch_left.gui_input.connect(_on_touch_input.bind(-1))
	touch_right.gui_input.connect(_on_touch_input.bind(1))
	
	# Hide during intro
	touch_left.visible = false
	touch_right.visible = false

func _on_touch_input(event: InputEvent, dir: float) -> void:
	if player == null:
		return
	
	if event is InputEventScreenTouch or event is InputEventMouseButton:
		if event.pressed:
			player.touch_dir = dir
		else:
			player.touch_dir = 0.0
Example: minigames/food_catch/main.gd:375-384

Floating Text

Score feedback that floats and fades out.

Scene Structure

FloatingText (Node2D)
└── Label

Script Template

extends Node2D

@onready var label = $Label

func show_text(text: String, color: Color):
	label.text = text
	label.modulate = color
	
	var tween = create_tween()
	tween.set_parallel(true)
	tween.tween_property(self, "position:y", position.y - 50, 1.0)
	tween.tween_property(label, "modulate:a", 0.0, 1.0)
	tween.finished.connect(queue_free)

Usage

@export var floating_text_scene: PackedScene

func _spawn_floating_text(text: String, color: Color) -> void:
	if floating_text_scene == null:
		return
	var ftxt = floating_text_scene.instantiate()
	ftxt.position = player.global_position + Vector2(20, -30)
	add_child(ftxt)
	ftxt.show_text(text, color)
Example: minigames/food_catch/main.gd:253-259

Difficulty Label

Displays difficulty progression messages.
@onready var difficulty_label: Label = $CanvasLayer/DifficultyLabel

func _show_difficulty_message(text: String) -> void:
	difficulty_label.text = text
	difficulty_label.visible = true
	difficulty_label.modulate.a = 0.0
	difficulty_label.scale = Vector2(1.4, 1.4)
	
	var t := create_tween()
	t.tween_property(difficulty_label, "modulate:a", 1.0, 0.2)
	t.parallel().tween_property(difficulty_label, "scale", Vector2.ONE, 0.2)
	t.tween_interval(1.0)
	t.tween_property(difficulty_label, "modulate:a", 0.0, 0.4)
	await t.finished
	difficulty_label.visible = false
Example: minigames/food_catch/main.gd:268-280

UI Best Practices

Set GameOverPanel and its buttons to PROCESS_MODE_ALWAYS so they remain interactive during pause:
panel.process_mode = Node.PROCESS_MODE_ALWAYS
retry_btn.process_mode = Node.PROCESS_MODE_ALWAYS
back_btn.process_mode = Node.PROCESS_MODE_ALWAYS
Ensure buttons can receive input:
retry_btn.mouse_filter = Control.MOUSE_FILTER_STOP
back_btn.mouse_filter = Control.MOUSE_FILTER_STOP
During the intro panel, hide gameplay UI:
backButton.visible = false
touch_left.visible = false
touch_right.visible = false
Animate UI changes with tweens for a polished feel:
var t := create_tween()
t.tween_property(intro_panel, "modulate:a", 0.0, 0.4)
await t.finished
Use consistent Spanish text across all games:
  • “Puntos: X”
  • “Vidas: X”
  • “Monedas obtenidas: +X”
  • “¡Fin del Juego!”
  • “Pausa”

Color Palette

Common colors used in Nyuron UI:
# Success/Score gain
Color("60e55aff")  # Green flash
Color(1, 1, 0.5)   # Yellow floating text

# Damage/Loss
Color(1.0, 0.4, 0.4)  # Red flash

# Bonus
Color(0.414, 0.993, 1.0, 1.0)  # Cyan

CanvasLayer Configuration

For HUD elements, use a CanvasLayer with follow viewport:
func _ready():
	var hud = $CanvasLayer
	hud.follow_viewport_enabled = true
Example: minigames/food_catch/main.gd:78

Next Steps

Creating a Minigame

Build a new minigame with these UI components

Mobile Adaptation

Optimize UI for mobile devices

Build docs developers (and LLMs) love