# 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)