- Add stunning cyberpunk cover page with animated grid background - Integrate Mermaid diagrams for visual architecture and flow charts - Add flexible-alerts for tips, warnings, and notes throughout tutorials - Enhance index.html with 15+ professional plugins (tabs, progress bar, charts) - Add sequence diagrams for authentication and analytics flows - Improve readability with visual callouts and interactive elements - Add graph visualizations for system architecture - Better UX with keyboard shortcuts, word count, and edit links
17 KiB
Game Development in AeThex
This guide covers the core game development concepts and systems in AeThex Engine.
Tip
New to game development? Start with the Scene System and Node Hierarchy sections. These are the foundation of everything in AeThex.
Scene System
What is a Scene?
A scene is a collection of nodes organized in a tree structure. Scenes are the building blocks of your game - they can represent:
- Game levels
- UI screens
- Characters
- Items
- Prefabs/templates
graph TD
Scene[🎬 Scene File<br/>.tscn] --> Root[Root Node]
Root --> Child1[Child Node 1]
Root --> Child2[Child Node 2]
Root --> Child3[Child Node 3]
Child2 --> GrandChild1[Grandchild 1]
Child2 --> GrandChild2[Grandchild 2]
style Scene fill:#00ffff22,stroke:#00ffff
style Root fill:#ff00ff22,stroke:#ff00ff
Note
Scenes can be nested inside other scenes. This allows you to create reusable components and maintain a clean project structure.
Working with Scenes
Creating Scenes:
# Load a scene
var scene = load("res://levels/main_level.tscn")
# Instance a scene
var instance = scene.instantiate()
# Add to scene tree
add_child(instance)
Switching Scenes:
# Change to a new scene
get_tree().change_scene_to_file("res://levels/next_level.tscn")
# Or using a packed scene
var packed_scene = load("res://levels/next_level.tscn")
get_tree().change_scene_to_packed(packed_scene)
Warning
Always use
change_scene_to_file()orchange_scene_to_packed()to switch scenes. Manually removing and adding root nodes can cause issues with signal connections and autoloads.
Scene Lifecycle:
_enter_tree()- Called when node enters the scene tree_ready()- Called when node and children are ready_exit_tree()- Called when node exits the scene tree
Node Hierarchy
Understanding Nodes
Nodes are the fundamental building blocks in AeThex. Everything in your game is a node or inherits from the Node class.
Node Types
Common Node Classes:
Node- Base class for all scene objectsNode2D- Base for all 2D game objects (has position, rotation, scale)Node3D- Base for all 3D game objectsControl- Base for all UI elementsCanvasItem- Base for anything that can be drawn
Working with Node Hierarchy
Adding/Removing Nodes:
# Add a child node
var sprite = Sprite2D.new()
add_child(sprite)
# Remove a child
sprite.queue_free() # Deferred removal (safe)
sprite.free() # Immediate removal (use carefully)
# Get parent
var parent = get_parent()
# Reparent a node
sprite.reparent(new_parent)
Finding Nodes:
# Get a child by name
var player = get_node("Player")
# Or using shorthand
var player = $Player
# Get a node by path
var weapon = $Player/Weapon
var health = get_node("Player/Health")
# Find nodes by group
var enemies = get_tree().get_nodes_in_group("enemies")
# Find parent by type
var level = find_parent("Level")
Node Groups:
# Add to a group
add_to_group("enemies")
add_to_group("damageable")
# Check if in group
if is_in_group("enemies"):
print("This is an enemy!")
# Remove from group
remove_from_group("enemies")
# Call function on all nodes in group
get_tree().call_group("enemies", "take_damage", 10)
Signals and Callbacks
What are Signals?
Signals are AeThex's implementation of the observer pattern. They allow nodes to communicate without tight coupling.
sequenceDiagram
participant Player
participant Enemy
participant UI
Player->>Player: take_damage(10)
Player->>Player: emit_signal("health_changed", 90)
Player->>UI: health_changed(90)
UI->>UI: update_health_bar(90)
Note over Player,UI: Signals enable loose coupling
Player->>Player: health reaches 0
Player->>Player: emit_signal("player_died")
Player->>UI: player_died
Player->>Enemy: player_died
UI->>UI: show_game_over()
Enemy->>Enemy: celebrate()
style Player fill:#00ffff22,stroke:#00ffff
style UI fill:#ff00ff22,stroke:#ff00ff
Tip
Signals are perfect for situations where you want multiple systems to react to an event without creating dependencies between them.
Built-in Signals
Common Node Signals:
ready- Emitted when node is readytree_entered- Node entered the scene treetree_exited- Node left the scene tree
Input Signals:
Area2D.body_entered(body)- Another body entered the areaArea2D.body_exited(body)- Another body left the areaButton.pressed()- Button was clicked
Note
Most built-in nodes come with useful signals. Check the documentation for each node type to see what signals are available.
Creating Custom Signals
extends Node
# Declare signals
signal health_changed(new_health)
signal player_died
signal item_collected(item_name, quantity)
var health = 100
func take_damage(amount):
health -= amount
emit_signal("health_changed", health)
if health <= 0:
emit_signal("player_died")
func collect_item(item, qty):
emit_signal("item_collected", item, qty)
Connecting to Signals
Code-based Connections:
# Connect to a signal
player.health_changed.connect(_on_health_changed)
# With custom parameters
player.health_changed.connect(_on_health_changed.bind("extra_param"))
# One-shot connection (auto-disconnects after first emit)
player.health_changed.connect(_on_health_changed, CONNECT_ONE_SHOT)
# Disconnect from signal
player.health_changed.disconnect(_on_health_changed)
func _on_health_changed(new_health):
print("Health is now: ", new_health)
Lambda Connections:
button.pressed.connect(func(): print("Button clicked!"))
Physics
2D Physics
RigidBody2D - Dynamic physics body affected by forces:
extends RigidBody2D
func _ready():
# Set physics properties
mass = 10.0
gravity_scale = 1.0
linear_damp = 0.1
angular_damp = 0.1
func _physics_process(delta):
# Apply force
apply_force(Vector2(100, 0))
# Apply impulse (instant)
apply_impulse(Vector2(0, -500))
# Apply torque (rotation)
apply_torque(100)
StaticBody2D - Immovable physics body (walls, floors):
extends StaticBody2D
func _ready():
# Static bodies don't move
# Used for terrain, walls, platforms
pass
CharacterBody2D - For player-controlled movement:
extends CharacterBody2D
var speed = 300.0
var jump_velocity = -400.0
var gravity = 980.0
func _physics_process(delta):
# Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
# Handle jump
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
# Get input direction
var direction = Input.get_axis("move_left", "move_right")
velocity.x = direction * speed
# Move with collision detection
move_and_slide()
3D Physics
RigidBody3D:
extends RigidBody3D
func _ready():
mass = 10.0
gravity_scale = 1.0
func apply_jump():
apply_central_impulse(Vector3.UP * 500)
CharacterBody3D:
extends CharacterBody3D
var speed = 5.0
var jump_strength = 10.0
var gravity = 20.0
func _physics_process(delta):
if not is_on_floor():
velocity.y -= gravity * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_strength
var input_dir = Input.get_vector("left", "right", "forward", "back")
var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
velocity.x = direction.x * speed
velocity.z = direction.z * speed
move_and_slide()
Collision Shapes
Adding Collision Shapes:
# Collision shapes must be children of physics bodies
# Common shapes:
# - CollisionShape2D / CollisionShape3D
# - RectangleShape2D, CircleShape2D, CapsuleShape2D
# - BoxShape3D, SphereShape3D, CapsuleShape3D
# In code:
var collision = CollisionShape2D.new()
var shape = CircleShape2D.new()
shape.radius = 32
collision.shape = shape
add_child(collision)
Physics Layers
Collision Layers & Masks:
# Layer: What layers this body is on (up to 32)
collision_layer = 0b0001 # Layer 1
# Mask: What layers this body can collide with
collision_mask = 0b0010 # Can collide with layer 2
# Common setup:
# Layer 1: Player
# Layer 2: Enemies
# Layer 3: Environment
# Layer 4: Collectibles
# Player collides with enemies and environment:
collision_layer = 1 # Binary: 0001
collision_mask = 6 # Binary: 0110 (layers 2 and 3)
Checking Collisions:
# CharacterBody2D/3D
func _physics_process(delta):
move_and_slide()
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
print("Collided with: ", collision.get_collider().name)
# Area2D/3D signals
func _ready():
body_entered.connect(_on_body_entered)
func _on_body_entered(body):
if body.is_in_group("player"):
print("Player entered area!")
UI System
Control Nodes
Control is the base class for all UI elements. Common UI nodes:
Buttons:
Button- Standard push buttonCheckButton- Toggle buttonOptionButton- Dropdown menu
Text:
Label- Display textRichTextLabel- Text with formatting (BBCode)LineEdit- Single-line text inputTextEdit- Multi-line text input
Containers:
VBoxContainer- Vertical layoutHBoxContainer- Horizontal layoutGridContainer- Grid layoutMarginContainer- Adds marginsPanelContainer- Background panel
Basic UI Example
extends Control
func _ready():
# Create a button
var button = Button.new()
button.text = "Click Me"
button.pressed.connect(_on_button_pressed)
add_child(button)
# Create a label
var label = Label.new()
label.text = "Score: 0"
add_child(label)
func _on_button_pressed():
print("Button clicked!")
Layouts and Containers
Automatic Layouts:
# VBoxContainer - stacks children vertically
var vbox = VBoxContainer.new()
vbox.add_child(Button.new())
vbox.add_child(Button.new())
vbox.add_child(Button.new())
# HBoxContainer - arranges children horizontally
var hbox = HBoxContainer.new()
hbox.add_theme_constant_override("separation", 10) # 10px spacing
# GridContainer - grid layout
var grid = GridContainer.new()
grid.columns = 3
for i in 9:
grid.add_child(Button.new())
Anchors and Margins:
# Anchors determine where control is positioned (0.0 to 1.0)
var panel = Panel.new()
# Center the panel
panel.anchor_left = 0.5
panel.anchor_top = 0.5
panel.anchor_right = 0.5
panel.anchor_bottom = 0.5
# Offset from anchor point
panel.offset_left = -100
panel.offset_top = -50
panel.offset_right = 100
panel.offset_bottom = 50
Themes
Applying Themes:
# Load a theme
var theme = load("res://themes/main_theme.tres")
theme = theme # Apply to root Control
# Or set individual theme overrides
var button = Button.new()
button.add_theme_color_override("font_color", Color.CYAN)
button.add_theme_font_size_override("font_size", 24)
Responsive Design
Size Flags:
var label = Label.new()
# Expand horizontally
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
# Shrink to content
label.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
Handling Resize:
extends Control
func _ready():
get_viewport().size_changed.connect(_on_viewport_resized)
func _on_viewport_resized():
var viewport_size = get_viewport_rect().size
print("New size: ", viewport_size)
# Adjust UI layout
Audio System
AudioStreamPlayer
Three types of audio players:
AudioStreamPlayer- 2D positional audioAudioStreamPlayer2D- 2D positional audioAudioStreamPlayer3D- 3D spatial audio
Playing Sounds
Basic Audio:
extends Node
@onready var sfx_player = $AudioStreamPlayer
@onready var music_player = $MusicPlayer
func _ready():
# Load and play sound
var sound = load("res://sounds/jump.ogg")
sfx_player.stream = sound
sfx_player.play()
func play_sound(sound_path: String):
sfx_player.stream = load(sound_path)
sfx_player.play()
Music Management
Background Music:
extends Node
var current_music = null
var music_player = AudioStreamPlayer.new()
func _ready():
add_child(music_player)
music_player.finished.connect(_on_music_finished)
func play_music(music_path: String, loop: bool = true):
var music = load(music_path)
music_player.stream = music
music_player.play()
if loop:
music_player.finished.connect(func(): music_player.play())
current_music = music_path
func stop_music():
music_player.stop()
func fade_out_music(duration: float = 1.0):
var tween = create_tween()
tween.tween_property(music_player, "volume_db", -80, duration)
tween.tween_callback(stop_music)
func fade_in_music(duration: float = 1.0):
music_player.volume_db = -80
music_player.play()
var tween = create_tween()
tween.tween_property(music_player, "volume_db", 0, duration)
Sound Effects
Sound Effect Pool:
extends Node
var sfx_players = []
var pool_size = 8
func _ready():
# Create a pool of audio players
for i in pool_size:
var player = AudioStreamPlayer.new()
add_child(player)
sfx_players.append(player)
func play_sfx(sound_path: String, volume_db: float = 0.0):
# Find available player
for player in sfx_players:
if not player.playing:
player.stream = load(sound_path)
player.volume_db = volume_db
player.play()
return
# If all busy, use first one
sfx_players[0].stream = load(sound_path)
sfx_players[0].volume_db = volume_db
sfx_players[0].play()
3D Audio
Spatial Audio:
extends Node3D
@onready var audio_3d = $AudioStreamPlayer3D
func _ready():
# Configure 3D audio
audio_3d.max_distance = 50.0 # Max hearing distance
audio_3d.attenuation_model = AudioStreamPlayer3D.ATTENUATION_INVERSE_DISTANCE
audio_3d.unit_size = 1.0
# Play sound at this position
audio_3d.stream = load("res://sounds/explosion.ogg")
audio_3d.play()
func play_3d_sound_at(sound_path: String, position: Vector3):
var player = AudioStreamPlayer3D.new()
add_child(player)
player.global_position = position
player.stream = load(sound_path)
player.play()
# Remove when finished
await player.finished
player.queue_free()
Workflow & Tools
Studio IDE Integration
AeThex Studio provides a full IDE experience with:
Code Editor:
- GDScript syntax highlighting
- Autocomplete and intellisense
- Inline documentation
- Error checking and linting
- Code folding and navigation
Scene Tree:
- Visual node hierarchy
- Drag-and-drop node creation
- Inspector panel for properties
- Live scene editing
Asset Browser:
- File system navigation
- Asset preview (images, 3D models)
- Drag-and-drop import
- Asset metadata
Live Reload:
# Changes are hot-reloaded automatically
# No need to restart the game
# Scripts reload on save
Studio Bridge:
# Check if running in Studio
if AeThexStudio.is_available():
print("Running in AeThex Studio")
# Send messages to Studio
AeThexStudio.log_message("Custom debug info")
# Open file in Studio
AeThexStudio.open_file("res://scripts/player.gd")
Version Control
Git Integration:
# Initialize repository
cd your_project
git init
# AeThex-specific .gitignore
echo ".import/" >> .gitignore
echo "*.import" >> .gitignore
echo ".godot/" >> .gitignore
echo "export_presets.cfg" >> .gitignore
# Commit
git add .
git commit -m "Initial commit"
Collaboration Workflow:
# Create feature branch
git checkout -b feature/player-movement
# Make changes and commit
git add scripts/player.gd
git commit -m "Implement player movement"
# Push branch
git push origin feature/player-movement
# Create pull request on GitHub
# Review and merge
Managing Merge Conflicts:
# Update from main
git checkout main
git pull
# Merge into feature branch
git checkout feature/player-movement
git merge main
# If conflicts occur, resolve them in editor
# Then:
git add .
git commit -m "Resolve merge conflicts"
Branching Strategy:
main- Stable, production-ready codedevelop- Integration branch for featuresfeature/*- Individual featureshotfix/*- Emergency fixesrelease/*- Release preparation
Best Practices:
- Commit often with clear messages
- Use branches for new features
- Keep commits atomic (one logical change)
- Pull before pushing
- Review code before merging
- Use
.gitattributesfor binary files:
*.tscn merge=binary
*.tres merge=binary
*.asset merge=binary
Next Steps
- API Reference: See API_REFERENCE.md for AeThex cloud services
- First Game Tutorial: Build a complete game in FIRST_GAME_TUTORIAL.md
- Studio Integration: Learn more at STUDIO_INTEGRATION.md
- Architecture: Understand the engine at ARCHITECTURE_OVERVIEW.md