
Godot Genre Open World
- 135 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-genre-open-world for development tasks
About
godot-genre-open-world: A skill for development. This provides functionality for development workflows.
- godot-genre-open-world
Godot Genre Open World by the numbers
- 135 all-time installs (skills.sh)
- +8 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,658 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-genre-open-worldAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 135 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-genre-open-world for development tasks
Files
Genre: Open World
Expert blueprint for open worlds balancing scale, performance, and player engagement.
NEVER Do (Expert Anti-Patterns)
World & Persistence
- NEVER prioritize Map Size over Density; empty landscapes are poor design. Strictly focus on Points of Interest (POIs) within every 30 seconds of travel.
- NEVER save the entire world state; strictly use Delta Persistence to record only unique changes (chopped trees, looted chests) to prevent massive save files.
- NEVER load large chunks or scenes synchronously; strictly use `ResourceLoader.load_threaded_request()` to prevent "Loading Hitches" and frame freezes.
- NEVER manipulate the active SceneTree directly from a background thread; strictly use `call_deferred()` to safely apply background thread chunk instantiations back to the main thread.
- NEVER keep distant, unloaded chunks in memory; strictly
queue_free()and nullify references to prevent Out-Of-Memory (OOM) crashes. - NEVER bake massive collision into one mesh; strictly break the world into chunks with local collision regions for efficient physics queries.
- NEVER save high-volume entity states in text formats (.tscn/.json); strictly use Binary Serialization (
store_var) for high-speed I/O.
Physics & Performance
- NEVER ignore the "Floating Origin" jitter beyond 8,192 units; strictly implement a World-Shift system or enable Large World Coordinates (Double Precision) in project settings.
- NEVER process physics or AI at extreme distances; strictly use Spatial Partitioning to disable logic for entities in far-away, inactive chunks.
- NEVER calculate physics-sensitive state in
_process(); strictly use_physics_process()for deterministic interaction at fluctuating framerates. - NEVER spawn individual
MeshInstance3Dnodes for massive foliage; strictly use MultiMeshInstance3D to batch hundreds of thousands of meshes into a single GPU draw call. - NEVER move
OccluderInstance3Dnodes at runtime; this forces a CPU BVH rebuild and causes severe micro-stuttering. - NEVER leave
CSGShape3Dnodes active in exported builds; strictly bake them into staticArrayMeshgeometry before shipping. - NEVER compile complex shaders during gameplay; strictly perform "warm-up" during loading or enable project-wide caching.
- NEVER rely solely on automatic mesh decimation; strictly use VisibilityRange (HLOD) to substitute complex materials with cheap imposters or completely hide objects at extreme distances.
Logic & Architecture
- NEVER perform global A* searches across the entire massive world; strictly use
NavigationPathQueryParameters3Dto limit pathfinding to localized active regions. - NEVER use
find_child()or deep tree iteration for global state (e.g., Time of Day); strictly use Scene Groups (call_group()) for optimized broadcasting. - NEVER synchronize complex Resource types over the network; strictly serialize world changes into primitive Dictionaries or PackedByteArrays.
---
🛠 Expert Components (scripts/)
Original Expert Patterns
- world_streamer.gd - Professional-grade chunk management and streaming engine with background threading.
- floating_origin_shifter.gd - World-offset correction system to prevent floating-point precision jitter.
Modular Components
- async_chunk_loader.gd - Background world streaming system using threaded resource loading.
- multimesh_foliage_manager.gd - Server-side GPU batching for thousands of landscape entities.
- hlod_configurator.gd - Distance-based mesh swapping and imposter management using VisibilityRange.
- hlod_visibility_config.gd - Distance-based geometry swapping using VisibilityRange (HLOD).
- binary_save_manager.gd - High-performance serialization for large-scale world persistence.
- chunk_limited_pathfinder.gd - NavigationServer-level query limits to optimize AI in dense worlds.
- server_prop_spawner.gd - Extreme optimization using RenderingServer RIDs to bypass SceneTree.
- dynamic_lod_adjuster.gd - Real-time adaptive performance scaling for global mesh LOD.
- group_weather_broadcaster.gd - Efficient decoupled environmental updates using SceneTree grouping.
- landscape_height_query.gd - Nodeless physics floor-height queries for large-scale landscapes.
---
Core Loop
1. Traverse: Player moves across vast distances (foot, vehicle, mount). 2. Discover: Player finds Points of Interest (POIs) dynamically. 3. Quest: Player accepts tasks that require travel. 4. Progress: World state changes based on player actions. 5. Immerse: Dynamic weather, day/night cycles affect gameplay.
Skill Chain
| Phase | Skills | Purpose |
|---|---|---|
| 1. Tera | godot-3d-world-building, shaders | Large scale terrain, tri-planar mapping |
| 2. Opti | level-of-detail, multithreading | HLOD, background loading, occlusion |
| 3. Data | godot-save-load-systems | Saving state of thousands of objects |
| 4. Nav | godot-navigation-pathfinding | AI pathfinding on large dynamic maps |
| 5. Core | floating-origin | Preventing precision jitter at 10,000+ units |
Architecture Overview
1. The Streamer (Chunk Manager)
Loading and unloading the world around the player.
# world_streamer.gd
extends Node3D
@export var chunk_size: float = 100.0
@export var render_distance: int = 4
var active_chunks: Dictionary = {}
func _process(delta: float) -> void:
var player_chunk = Vector2i(player.position.x / chunk_size, player.position.z / chunk_size)
update_chunks(player_chunk)
func update_chunks(center: Vector2i) -> void:
# 1. Determine needed chunks
var needed = []
for x in range(-render_distance, render_distance + 1):
for y in range(-render_distance, render_distance + 1):
needed.append(center + Vector2i(x, y))
# 2. Unload old
for chunk in active_chunks.keys():
if chunk not in needed:
unload_chunk(chunk)
# 3. Load new (Threaded)
for chunk in needed:
if chunk not in active_chunks:
load_chunk_async(chunk)2. Floating Origin
Solving the floating point precision error (jitter) when far from (0,0,0).
# floating_origin.gd
extends Node
const THRESHOLD: float = 5000.0
func _process(delta: float) -> void:
if player.global_position.length() > THRESHOLD:
shift_world(-player.global_position)
func shift_world(offset: Vector3) -> void:
# Move the entire world opposite to the player's position
# So the player creates the illusion of moving, but logic stays near 0,0
for node in get_tree().get_nodes_in_group("world_root"):
node.global_position += offset3. Quest State Database
Tracking "Did I kill the bandits in Chunk 45?" when Chunk 45 is unloaded.
# global_state.gd
var chunk_data: Dictionary = {} # Vector2i -> Dictionary
func set_entity_dead(chunk_id: Vector2i, entity_id: String) -> void:
if not chunk_data.has(chunk_id):
chunk_data[chunk_id] = {}
chunk_data[chunk_id][entity_id] = { "dead": true }Key Mechanics Implementation
HLOD (Hierarchical Level of Detail)
Merging 100 houses into 1 simple mesh when viewed from 1km away.
- Near: High Poly House + Props.
- Far: Low Poly Billboard / Imposter mesh.
- Very Far: Part of the Terrain texture.
Points of Interest (Discovery)
Compass bar logic.
func update_compass() -> void:
for poi in active_pois:
var direction = player.global_transform.basis.z
var to_poi = (poi.global_position - player.global_position).normalized()
var angle = direction.angle_to(to_poi)
# Map angle to UI positionGodot-Specific Tips
- VisibilityRange: Use specific
visibility_range_beginandendon MeshInstance3D to handle LODs without a dedicated LOD node. - Thread: Use
Thread.new()for loading chunks to prevent frame stutters. - OcclusionCulling: Bake occlusion for large cities. For open fields, simple distance culling is often enough.
Common Pitfalls
1. The "Empty" World: huge map, nothing to do. Fix: Density > Size. Smaller, denser maps are better than vast empty deserts. 2. Save File Bloat: Save file is 500MB. Fix: Only save changes (Delta compression). If a rock hasn't moved, don't save it. 3. Physics at Distance: Physics break far away. Fix: Disable physics processing for chunks > 2 units away. Use simple "simulation" for distant logic.
---
🚀 Elite Technical Implementations (Batch 09)
1. World-Origin-Shifting Pattern (Floating Origin)
Prevent physics jitter and rendering glitches at extreme distances (>5,000 units) by shifting the entire world back to the origin. This pattern snaps the world root opposite to the player's movement threshold.
class_name WorldOriginShifter extends Node
@export var shift_threshold: float = 4000.0
var _player_camera: Camera3D
func _process(_delta: float) -> void:
if not is_instance_valid(_player_camera):
_player_camera = get_viewport().get_camera_3d()
return
if _player_camera.global_position.length() > shift_threshold:
_perform_origin_shift()
func _perform_origin_shift() -> void:
var shift_vector: Vector3 = -_player_camera.global_position
var world_root = get_tree().get_first_node_in_group("world_root") as Node3D
if world_root:
world_root.global_position += shift_vector
# Broadcast signal so AI/Nav can update their internal coordinates2. HLOD-System (Hierarchical Level of Detail)
Optimize draw calls by merging distant objects into a single proxy mesh. Use the Visibility Range properties on GeometryInstance3D to swap high-detail children for a low-poly proxy automatically based on camera distance.
class_name HLODConfigurator extends Node3D
@export var hlod_proxy_mesh: MeshInstance3D
@export var transition_distance: float = 150.0
func _ready() -> void:
if not hlod_proxy_mesh: return
# The proxy only appears when far away
hlod_proxy_mesh.visibility_range_begin = transition_distance
for child in get_children():
if child is MeshInstance3D and child != hlod_proxy_mesh:
# High-detail children disappear when the proxy appears
child.visibility_parent = hlod_proxy_mesh.get_path()3. Async-Streamer-Controller (Threaded Chunking)
Seamlessly load and unload world chunks using ResourceLoader.load_threaded_request(). This prevents the main thread from blocking during heavy I/O, ensuring a stutter-free exploration experience.
class_name AsyncChunkStreamer extends Node
func request_chunk_load(chunk_path: String) -> void:
var err = ResourceLoader.load_threaded_request(chunk_path)
if err == OK:
set_process(true)
func _process(_delta: float) -> void:
# Check status of pending requests
var status = ResourceLoader.load_threaded_get_status(path)
if status == ResourceLoader.THREAD_LOAD_LOADED:
var chunk_scene = ResourceLoader.load_threaded_get(path) as PackedScene
var chunk_instance = chunk_scene.instantiate()
# Add to world...- Master Skill: godot-master
# async_chunk_loader.gd
extends Node
class_name AsyncChunkLoader
# Background Async Chunk Streamer
# Loads massive open-world chunks dynamically without stalling the main thread.
var _loading_tasks: Dictionary = {}
func request_chunk_load(chunk_id: String, path: String) -> void:
# Pattern: Use ResourceLoader.load_threaded_request for non-blocking I/O.
var error := ResourceLoader.load_threaded_request(path)
if error == OK:
_loading_tasks[chunk_id] = path
func _process(_delta: float) -> void:
for chunk_id in _loading_tasks.keys():
var path: String = _loading_tasks[chunk_id]
var status := ResourceLoader.load_threaded_get_status(path)
if status == ResourceLoader.THREAD_LOAD_LOADED:
var chunk_scene := ResourceLoader.load_threaded_get(path) as PackedScene
# EXTREMELY IMPORTANT: Defer instantiation to the main thread.
call_deferred("_finalize_chunk_instance", chunk_id, chunk_scene)
_loading_tasks.erase(chunk_id)
func _finalize_chunk_instance(_chunk_id: String, scene: PackedScene) -> void:
if scene:
var instance := scene.instantiate()
add_child(instance)
# binary_save_manager.gd
extends Node
class_name BinarySaveManager
# Efficient Binary Serialization
# Saves massive world-states (thousands of entity flags) with minimal I/O overhead.
func save_world_state(data: Dictionary, path: String) -> void:
# Pattern: Use FileAccess.store_var with full_objects=false for clean data.
var file := FileAccess.open(path, FileAccess.WRITE)
if file:
file.store_var(data, false)
file.close()
func load_world_state(path: String) -> Dictionary:
if not FileAccess.file_exists(path): return {}
var file := FileAccess.open(path, FileAccess.READ)
var data = file.get_var(false)
file.close()
return data if data is Dictionary else {}
# chunk_limited_pathfinder.gd
extends Node
class_name ChunkLimitedPathfinder
# Chunk-Isolated AI Pathfinding
# Restricts pathfinding searches to valid navigation regions to save CPU.
func find_path_in_region(start: Vector3, end: Vector3, allowed_regions: Array[RID]) -> PackedVector3Array:
# Pattern: Use NavigationServer3D directly for granular query control.
var params := NavigationPathQueryParameters3D.new()
params.start_position = start
params.target_position = end
# EXTREMELY IMPORTANT: Limits search to current/adjacent chunks only.
params.included_regions = allowed_regions
var result := NavigationPathQueryResult3D.new()
NavigationServer3D.query_path(params, result)
return result.path
# dynamic_lod_adjuster.gd
extends Node
class_name DynamicLODAdjuster
# Dynamic Performance/LOD Bias Adjuster
# Adjusts global mesh LOD thresholds based on real-time FPS stability.
func _process(_delta: float) -> void:
var fps := Engine.get_frames_per_second()
var viewport := get_viewport()
# Pattern: Degrade mesh quality smoothly to maintain frame targets.
if fps < 50.0:
viewport.mesh_lod_threshold = lerp(viewport.mesh_lod_threshold, 3.0, 0.05)
elif fps > 65.0:
viewport.mesh_lod_threshold = lerp(viewport.mesh_lod_threshold, 1.0, 0.05)
# godot-master/scripts/open_world_floating_origin_shifter.gd
extends Node
## Floating Origin Shifter (Expert Pattern)
## Cyclically resets world origin to keep player near (0,0,0) and preserve float precision.
class_name FloatingOriginShifter
signal origin_shifted(offset: Vector3)
@export var threshold: float = 4000.0 # Setup safe buffer (limit is ~10k usually)
@export var world_root: Node3D # Parent of all world objects
@export var player: Node3D
func _physics_process(delta: float) -> void:
if not player: return
var dist = player.global_position.length()
if dist > threshold:
_shift_origin()
func _shift_origin() -> void:
var shift_vector = -player.global_position
# Keep Y if you want, usually full 3D shift is better
shift_vector.y = 0 # Optional: Don't shift Y if you want height absolute
# 1. Shift Root
# Note: If player is child of world_root, this moves player too?
# Strategy: Player usually SEPARATE from world content or handled carefully.
# If using PhysicsServer directly, shift creates a warp.
# Simplest Godot approach: Move everything EXCEPT player, then warp player?
# OR: Move everything including player.
print("Shifting Origin by: ", shift_vector)
# Move all root level entities
for node in get_tree().get_nodes_in_group("world_entities"):
if node is Node3D:
node.global_position += shift_vector
# Move player
player.global_position += shift_vector
# Notify systems (e.g. Trail renderers needs clear)
origin_shifted.emit(shift_vector)
## EXPERT USAGE:
## Group all static/dynamic world objects as "world_entities".
## Attach to autoload or persistent manager.
# group_weather_broadcaster.gd
extends Node
class_name GroupWeatherBroadcaster
# Global Environment Group Broadcasting
# Decouples weather logic from individual entities using high-speed group calls.
func apply_weather_state(weather_type: StringName) -> void:
# Pattern: Avoid iterating nodes manually. Use call_group_flags for efficiency.
get_tree().call_group_flags(
SceneTree.GROUP_CALL_DEFERRED,
&"environment_reactors",
&"_on_weather_update",
weather_type
)
# hlod_visibility_config.gd
extends Node
class_name HLODVisibilityConfig
# VisibilityRange (HLOD) Configuration
# Swaps high-poly geometry for low-poly impostors across distance boundaries.
@export var high_detail_node: GeometryInstance3D
@export var low_detail_node: GeometryInstance3D
@export var transition_dist: float = 150.0
func _ready() -> void:
if not high_detail_node or not low_detail_node: return
# High-detail mesh fades out at transition.
high_detail_node.visibility_range_end = transition_dist
high_detail_node.visibility_range_end_margin = 10.0
high_detail_node.visibility_range_fade_mode = GeometryInstance3D.VISIBILITY_RANGE_FADE_SELF
# Low-detail impostor fades in at transition.
low_detail_node.visibility_range_begin = transition_dist
low_detail_node.visibility_range_begin_margin = 10.0
low_detail_node.visibility_range_fade_mode = GeometryInstance3D.VISIBILITY_RANGE_FADE_DEPENDENCIES
# landscape_height_query.gd
extends Node3D
class_name LandscapeHeightQuery
# Grid-Based Raycast Floor Query
# Uses PhysicsDirectSpaceState for high-performance height checks for object placement.
func get_height_at(pos_x: float, pos_z: float) -> float:
var space_state := get_world_3d().direct_space_state
# Query from skyward downwards.
var ray_start := Vector3(pos_x, 5000.0, pos_z)
var ray_end := Vector3(pos_x, -1000.0, pos_z)
var query := PhysicsRayQueryParameters3D.create(ray_start, ray_end)
var result := space_state.intersect_ray(query)
if not result.is_empty():
return result.position.y
return 0.0
extends Node3D
class_name LODLogicEnabler
## Expert LOD Logic (Godot 4.6).
## Performance-first logic throttling based on distance.
@export var ai_node: Node3D
@export var player: Node3D
@export var disable_dist_sq: float = 6400.0 # 80 meters squared
func _ready() -> void:
# Optimization: Use a slow timer for distance checks
var t = Timer.new()
t.wait_time = 0.5
t.timeout.connect(_check_distance)
add_child(t)
t.start()
func _check_distance() -> void:
if not player or not ai_node: return
var d2 = global_position.distance_squared_to(player.global_position)
if d2 > disable_dist_sq:
ai_node.process_mode = PROCESS_MODE_DISABLED
else:
ai_node.process_mode = PROCESS_MODE_INHERIT
## [SKILL NOTICE]: Use 'distance_squared_to' for LOD checks.
## It skips expensive square-root math, vital for 1000s of entities.
# multimesh_foliage_manager.gd
extends MultiMeshInstance3D
class_name MultiMeshFoliageManager
# High-Performance Foliage Renderer
# Batch-renders thousands of static meshes (trees, rocks) in a single GPU draw call.
func setup_batch(mesh: Mesh, transforms: Array[Transform3D]) -> void:
# Pattern: Use MultiMesh to bypass individual Node3D overhead.
multimesh = MultiMesh.new()
multimesh.transform_format = MultiMesh.TRANSFORM_3D
multimesh.mesh = mesh
# Pre-allocate count to prevent resizing during population.
multimesh.instance_count = transforms.size()
for i in range(transforms.size()):
multimesh.set_instance_transform(i, transforms[i])
# Pattern: Enable visibility range to cull entire batches based on distance.
visibility_range_end = 500.0
# server_prop_spawner.gd
extends Node
class_name ServerPropSpawner
# Nodeless Server-Side Object Placement
# Spawns visuals directly in RenderingServer to bypass SceneTree overhead.
var _spawned_rids: Array[RID] = []
func spawn_visual_prop(mesh_rid: RID, material_rid: RID, xform: Transform3D) -> RID:
# Pattern: Create rendering instance directly.
var instance_rid := RenderingServer.instance_create()
RenderingServer.instance_set_base(instance_rid, mesh_rid)
RenderingServer.instance_set_surface_override_material(instance_rid, 0, material_rid)
RenderingServer.instance_set_transform(instance_rid, xform)
# Assign to current 3D world scenario.
var scenario := get_world_3d().scenario
RenderingServer.instance_set_scenario(instance_rid, scenario)
_spawned_rids.append(instance_rid)
return instance_rid
func clear_all() -> void:
for rid in _spawned_rids:
RenderingServer.free_rid(rid)
_spawned_rids.clear()
extends Node
class_name WorldOriginShifter
## Expert Origin Shifting (Godot 4.6).
## Resets world origin to (0,0,0) to prevent jitter.
@export var world_root: Node3D
@export var player: Node3D
@export var threshold: float = 8192.0 # 32-bit float safe limit
var total_offset: Vector3 = Vector3.ZERO
func _physics_process(_delta: float) -> void:
if player.global_position.length() > threshold:
_perform_shift()
func _perform_shift() -> void:
var shift = -player.global_position
total_offset += shift
# Shift world root and update physics interpolation
world_root.global_position += shift
player.global_position += shift
player.reset_physics_interpolation()
# Sync shaders (Global Uniforms)
RenderingServer.global_shader_parameter_set("world_offset", total_offset)
## [SKILL NOTICE]: Use 'reset_physics_interpolation()' during
## origin shifts to prevent 1-frame visual 'streaking' or 'warping'.
# godot-master/scripts/open_world_world_streamer.gd
extends Node3D
## World Streamer (Expert Pattern)
## Manages loading/unloading of open world sectors/chunks based on distance.
## Uses background threads to prevent stutters.
class_name WorldStreamer
@export var tile_size: float = 256.0
@export var load_radius: int = 2
@export var player: Node3D
var active_tiles: Dictionary = {} # Vector2i -> Node (Loaded Scene)
var load_queue: Array[Vector2i] = []
var thread: Thread
var semaphore: Semaphore
var mutex: Mutex
var exit_thread: bool = false
func _ready() -> void:
mutex = Mutex.new()
semaphore = Semaphore.new()
thread = Thread.new()
thread.start(_loader_thread_func)
func _process(delta: float) -> void:
if not player: return
var center = _get_tile_coord(player.global_position)
# Simple unloader
var keep = []
for x in range(-load_radius, load_radius + 1):
for z in range(-load_radius, load_radius + 1):
keep.append(center + Vector2i(x, z))
var to_unload = []
for k in active_tiles:
if not k in keep:
to_unload.append(k)
for k in to_unload:
active_tiles[k].queue_free()
active_tiles.erase(k)
# Queue loader
for k in keep:
if not active_tiles.has(k) and not k in load_queue:
load_queue.append(k)
semaphore.post()
func _loader_thread_func() -> void:
while true:
semaphore.wait()
if exit_thread: break
mutex.lock()
if load_queue.is_empty():
mutex.unlock()
continue
var coord = load_queue.pop_front()
mutex.unlock()
# Simulate loading / Actual loading
# var scene = load("res://world/chunks/chunk_%d_%d.tscn" % [coord.x, coord.y])
# CALL DEFERRED to add to tree
call_deferred("_finish_load", coord, Node3D.new()) # Placeholder
func _finish_load(coord: Vector2i, node: Node) -> void:
if active_tiles.has(coord):
node.queue_free() # Duplicate
return
add_child(node)
node.global_position = Vector3(coord.x * tile_size, 0, coord.y * tile_size)
active_tiles[coord] = node
func _get_tile_coord(pos: Vector3) -> Vector2i:
return Vector2i(round(pos.x / tile_size), round(pos.z / tile_size))
func _exit_tree() -> void:
exit_thread = true
semaphore.post()
thread.wait_to_finish()
## EXPERT USAGE:
## Configure tile_size to match terrain chunks.
## Ensure scene files follow a naming convention or use a Resource directory.