- Genre: Real-Time Strategy (RTS)
- Expert blueprint for RTS games balancing strategy, micromanagement, and performance.
- NEVER Do
- NEVER pathfinding jitter
- — Units pushing each other endlessly. Enable RVO avoidance (NavigationAgent2D.avoidance_enabled with radius).
- NEVER excessive micromanagement
- — Automate mundane tasks (auto-attack nearby enemies, auto-resume gathering after drop-off).
- NEVER _process on every unit
- — For 100+ units, use central UnitManager iterator. Saves massive function call overhead.
- NEVER skip command queuing
- — Players expect Shift+Click to chain commands. Store Array[Command] and process sequentially.
- NEVER forget fog of war
- — Unvisited areas should be hidden. Use SubViewport + shader mask for performance.
- Available Scripts
- MANDATORY
-
- Read the appropriate script before implementing the corresponding pattern.
- rts_selection_manager.gd
- Mouse-box unit selection with shift-add. Camera projection for 3D units, command issuing with queue support (shift+right click).
- Core Loop
- Gather
-
- Units collect resources (Gold, Wood, etc.).
- Build
-
- Construct base buildings to unlock tech/units.
- Train
-
- Produce an army of diverse units.
- Command
-
- Micromanage units in real-time battles.
- Expand
- Secure map control and resources. Skill Chain Phase Skills Purpose 1. Controls godot-input-handling , camera-rts Selection box, camera panning/zoom 2. Units navigation-server , state-machines Pathfinding, avoidance, states (Idle/Move/Attack) 3. Systems fog-of-war , building-system Map visibility, grid placement 4. AI behavior-trees , utility-ai Enemy commander logic 5. Polish ui-minimap , godot-particles Strategic overview, battle feedback Architecture Overview 1. Selection Manager (Singleton or Commander Node) Handles mouse input for selecting units.
selection_manager.gd
extends Node2D var selected_units : Array [ Unit ] = [ ] var drag_start : Vector2 var is_dragging : bool = false @ onready var selection_box : Panel = $SelectionBox func _unhandled_input ( event ) : if event is InputEventMouseButton and event . button_index == MOUSE_BUTTON_LEFT : if event . pressed : start_selection ( event . position ) else : end_selection ( event . position ) elif event is InputEventMouseMotion and is_dragging : update_selection_box ( event . position ) func end_selection ( end_pos : Vector2 ) : is_dragging = false selection_box . visible = false var rect = Rect2 ( drag_start , end_pos - drag_start ) . abs ( ) if Input . is_key_pressed ( KEY_SHIFT ) :
Add to selection
pass else : deselect_all ( )
Query physics server for units in rect
var query = PhysicsShapeQueryParameters2D . new ( ) var shape = RectangleShape2D . new ( ) shape . size = rect . size query . shape = shape query . transform = Transform2D ( 0 , rect . get_center ( ) )
... execute query and add units to selected_units
for unit in selected_units : unit . set_selected ( true ) func issue_command ( target_position : Vector2 ) : for unit in selected_units : unit . move_to ( target_position ) 2. Unit Controller (State Machine) Units need robust state management to handle commands and auto-attacks.
unit.gd
- extends
- CharacterBody2D
- class_name
- Unit
- enum
- State
- {
- IDLE
- ,
- MOVE
- ,
- ATTACK
- ,
- HOLD
- }
- var
- state
- :
- State
- =
- State
- .
- IDLE
- var
- command_queue
- :
- Array
- [
- Command
- ]
- =
- [
- ]
- @
- onready
- var
- nav_agent
- :
- NavigationAgent2D
- =
- $NavigationAgent2D
- func
- move_to
- (
- target
- :
- Vector2
- )
- :
- nav_agent
- .
- target_position
- =
- target
- state
- =
- State
- .
- MOVE
- func
- _physics_process
- (
- delta
- )
- :
- if
- state
- ==
- State
- .
- MOVE
- :
- if
- nav_agent
- .
- is_navigation_finished
- (
- )
- :
- state
- =
- State
- .
- IDLE
- return
- var
- next_pos
- =
- nav_agent
- .
- get_next_path_position
- (
- )
- var
- direction
- =
- global_position
- .
- direction_to
- (
- next_pos
- )
- velocity
- =
- direction
- *
- speed
- move_and_slide
- (
- )
- 3. Fog of War
- A system to hide unvisited areas. Usually implemented with a texture and a shader.
- Grid Approach
-
- 2D array of "visibility" values.
- Viewport Texture
-
- A
- SubViewport
- drawing white circles for units on a black background. This texture is then used as a mask in a shader on a full-screen
- ColorRect
- overlay.
- shader_type canvas_item;
- uniform sampler2D visibility_texture;
- uniform vec4 fog_color : source_color;
- void fragment() {
- float visibility = texture(visibility_texture, UV).r;
- COLOR = mix(fog_color, vec4(0,0,0,0), visibility);
- }
- Key Mechanics Implementation
- Command Queue
- Allow players to chain commands (Shift-Click).
- Implementation
-
- Store commands in an
- Array
- . When one finishes, pop the next.
- Visuals
-
- Draw lines showing the queued path.
- Resource Gathering
- Nodes
- :
- ResourceNode
- (Tree/GoldMine) and
- DropoffPoint
- (TownCenter).
- Logic
- :
- Move to Resource.
- Work (Timer).
- Move to Dropoff.
- Deposit (Global Economy update).
- Repeat.
- Common Pitfalls
- Pathfinding Jitter
-
- Units pushing each other endlessly.
- Fix
-
- Use RVO (Reciprocal Velocity Obstacles) built into Godot's
- NavigationAgent2D
- (properties
- avoidance_enabled
- ,
- radius
- ).
- Too Much Micro
-
- Automate mundane tasks (auto-attack nearby, auto-gather behavior).
- Performance
-
- Too many nodes.
- Fix
-
- Use
- MultiMeshInstance2D
- for rendering thousands of units if needed, and run logic on a
- Server
- node rather than individual scripts for mass units.
- Godot-Specific Tips
- Avoidance
- :
- NavigationAgent2D
- has built-in RVO avoidance. Make sure to call
- set_velocity()
- and use the
- velocity_computed
- signal for the actual movement!
- Server Architecture
-
- For 100+ units, don't use
- _process
- on every unit. Have a central
- UnitManager
- iterate through active units to save function call overhead.
- Groups
- Use Groups heavily ( Units , Buildings , Resources ) for easy selection filters. Reference Master Skill: godot-master