AeThex-Engine-Core/docs/GAME_DEVELOPMENT.md

724 lines
16 KiB
Markdown

# Game Development in AeThex
This guide covers the core game development concepts and systems in AeThex Engine.
## 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
### Working with Scenes
**Creating Scenes:**
```gdscript
# 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:**
```gdscript
# 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)
```
**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 objects
- `Node2D` - Base for all 2D game objects (has position, rotation, scale)
- `Node3D` - Base for all 3D game objects
- `Control` - Base for all UI elements
- `CanvasItem` - Base for anything that can be drawn
### Working with Node Hierarchy
**Adding/Removing Nodes:**
```gdscript
# 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:**
```gdscript
# 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:**
```gdscript
# 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.
### Built-in Signals
**Common Node Signals:**
- `ready` - Emitted when node is ready
- `tree_entered` - Node entered the scene tree
- `tree_exited` - Node left the scene tree
**Input Signals:**
- `Area2D.body_entered(body)` - Another body entered the area
- `Area2D.body_exited(body)` - Another body left the area
- `Button.pressed()` - Button was clicked
### Creating Custom Signals
```gdscript
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:**
```gdscript
# 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:**
```gdscript
button.pressed.connect(func(): print("Button clicked!"))
```
---
## Physics
### 2D Physics
**RigidBody2D** - Dynamic physics body affected by forces:
```gdscript
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):
```gdscript
extends StaticBody2D
func _ready():
# Static bodies don't move
# Used for terrain, walls, platforms
pass
```
**CharacterBody2D** - For player-controlled movement:
```gdscript
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:**
```gdscript
extends RigidBody3D
func _ready():
mass = 10.0
gravity_scale = 1.0
func apply_jump():
apply_central_impulse(Vector3.UP * 500)
```
**CharacterBody3D:**
```gdscript
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:**
```gdscript
# 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:**
```gdscript
# 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:**
```gdscript
# 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 button
- `CheckButton` - Toggle button
- `OptionButton` - Dropdown menu
**Text:**
- `Label` - Display text
- `RichTextLabel` - Text with formatting (BBCode)
- `LineEdit` - Single-line text input
- `TextEdit` - Multi-line text input
**Containers:**
- `VBoxContainer` - Vertical layout
- `HBoxContainer` - Horizontal layout
- `GridContainer` - Grid layout
- `MarginContainer` - Adds margins
- `PanelContainer` - Background panel
### Basic UI Example
```gdscript
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:**
```gdscript
# 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:**
```gdscript
# 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:**
```gdscript
# 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:**
```gdscript
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:**
```gdscript
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 audio
- `AudioStreamPlayer2D` - 2D positional audio
- `AudioStreamPlayer3D` - 3D spatial audio
### Playing Sounds
**Basic Audio:**
```gdscript
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:**
```gdscript
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:**
```gdscript
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:**
```gdscript
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:**
```gdscript
# Changes are hot-reloaded automatically
# No need to restart the game
# Scripts reload on save
```
**Studio Bridge:**
```gdscript
# 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:**
```bash
# 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:**
```bash
# 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:**
```bash
# 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 code
- `develop` - Integration branch for features
- `feature/*` - Individual features
- `hotfix/*` - Emergency fixes
- `release/*` - Release preparation
**Best Practices:**
1. Commit often with clear messages
2. Use branches for new features
3. Keep commits atomic (one logical change)
4. Pull before pushing
5. Review code before merging
6. Use `.gitattributes` for binary files:
```
*.tscn merge=binary
*.tres merge=binary
*.asset merge=binary
```
---
## Next Steps
- **API Reference:** See [API_REFERENCE.md](API_REFERENCE.md) for AeThex cloud services
- **First Game Tutorial:** Build a complete game in [FIRST_GAME_TUTORIAL.md](tutorials/FIRST_GAME_TUTORIAL.md)
- **Studio Integration:** Learn more at [STUDIO_INTEGRATION.md](STUDIO_INTEGRATION.md)
- **Architecture:** Understand the engine at [ARCHITECTURE_OVERVIEW.md](ARCHITECTURE_OVERVIEW.md)