
Godot Genre Sandbox
- 132 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-genre-sandbox for development tasks
About
godot-genre-sandbox: A skill for development. This provides functionality for development workflows.
- godot-genre-sandbox
Godot Genre Sandbox by the numbers
- 132 all-time installs (skills.sh)
- +8 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,693 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-sandboxAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 132 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-genre-sandbox for development tasks
Files
Genre: Sandbox
Physical simulation, emergent play, and player creativity define this genre.
NEVER Do (Expert Anti-Patterns)
Performance & Scalability
- NEVER use individual
RigidBodynodes for every block; strictly use Static Colliders for the world and reserve physics for dynamic props. - NEVER simulate the entire world every frame; strictly process "Dirty" chunks with active changes. Sleeping chunks must consume zero CPU.
- NEVER update
MultiMeshbuffers every frame; strictly batch changes and only rebuild the buffer when a modification completes (e.g., player stops painting). - NEVER use standard Godot
Nodesfor every grid cell; strictly use PackedInt32Arrays or typed Dictionaries to keep RAM overhead minimal. - NEVER raycast against every individual voxel for placement; strictly use Grid Quantization (
floor(pos/size)) for direct O(1) cell calculation. - NEVER render every block face in a chunk; strictly generate an
ArrayMeshthat only pushes visible exterior faces to the GPU (Culling/Greedy Meshing).
Data & Persistence
- NEVER save raw arrays of every block transform; strictly use Run-Length Encoding (RLE) (e.g., "Air x 50,000") to compress uniform spaces.
- NEVER load massive terrain chunks synchronously; strictly use
ResourceLoader.load_threaded_request()to prevent frame stutter. - NEVER use standard text
.tscnfiles for voxel datasets; strictly use binary `.res` files for 10x faster parsing. - NEVER ignore Floating-Point Precision limits (32,768 units); strictly implement floating-origin shifting for massive worlds.
Systems & Architecture
- NEVER hardcode element interactions (
if water and fire); strictly use a Property System where interactions emerge from material attributes (flammability, density). - NEVER trust client-side placement in multiplayer; strictly require the Server to validate bounds and resources.
- NEVER manipulate the SceneTree from background generation threads; strictly use
call_deferred()or Mutex locks for safety. - NEVER leave orphaned chunks in memory; strictly track loaded regions and call
queue_free()on discarded branches.
---
🛠 Expert Components (scripts/)
Original Expert Patterns
- voxel_chunk_manager.gd - Professional chunk management using
MultiMeshInstance3Dwith batch update logic. - cellular_automata_liquid.gd - Optimized simulation of liquids and powders using property-based density checks.
- voxel_world.gd - Top-level world controller for grid state, tool-based editing, and chunk lifecycle.
Modular Components
- sandbox_patterns.gd - Utility collection for async chunk loading, multithreading, and origin shifting.
Architecture Patterns
1. Element System (Property-Based Emergence)
Model material properties, not behaviors. Interactions emerge from overlapping properties.
# element_data.gd
class_name ElementData extends Resource
enum Type { SOLID, LIQUID, GAS, POWDER }
@export var id: String = "air"
@export var type: Type = Type.GAS
@export var density: float = 0.0 # For liquid flow direction
@export var flammable: float = 0.0 # 0-1: Chance to ignite
@export var ignition_temp: float = 400.0
@export var conductivity: float = 0.0 # For electricity/heat
@export var hardness: float = 1.0 # Mining time multiplier
# EDGE CASE: What if two elements have same density but different types?
# SOLUTION: Use secondary sort (type enum priority: SOLID > LIQUID > POWDER > GAS)
func should_swap_with(other: ElementData) -> bool:
if density == other.density:
return type > other.type # Enum comparison: SOLID(0) > GAS(3)
return density > other.density2. Cellular Automata Grid (Falling Sand Simulation)
Update order matters. Top-down prevents "teleporting" godot-particles.
# world_grid.gd
var grid: Dictionary = {} # Vector2i -> ElementData
var dirty_cells: Array[Vector2i] = []
func _physics_process(_delta: float) -> void:
# CRITICAL: Sort top-to-bottom to prevent double-moves
dirty_cells.sort_custom(func(a, b): return a.y < b.y)
for pos in dirty_cells:
simulate_cell(pos)
dirty_cells.clear()
func simulate_cell(pos: Vector2i) -> void:
var cell = grid.get(pos)
if not cell: return
match cell.type:
ElementData.Type.LIQUID, ElementData.Type.POWDER:
# Try down, then down-left, then down-right
var targets = [pos + Vector2i.DOWN,
pos + Vector2i(- 1, 1),
pos + Vector2i(1, 1)]
for target in targets:
var neighbor = grid.get(target)
if neighbor and cell.should_swap_with(neighbor):
swap_cells(pos, target)
mark_dirty(target)
return
ElementData.Type.GAS:
# Gases rise (inverse of liquids)
var targets = [pos + Vector2i.UP,
pos + Vector2i(-1, -1),
pos + Vector2i(1, -1)]
# Same swap logic...
# EDGE CASE: What if multiple godot-particles want to move into same cell?
# SOLUTION: Only mark target dirty, don't double-swap. Next frame resolves conflicts.3. Tool System (Strategy Pattern)
Decouple input from world modification.
# tool_base.gd
class_name Tool extends Resource
func use(world_pos: Vector2, world: WorldGrid) -> void: pass
# tool_brush.gd
extends Tool
@export var element: ElementData
@export var radius: int = 1
func use(world_pos: Vector2, world: WorldGrid) -> void:
var grid_pos = Vector2i(floor(world_pos.x), floor(world_pos.y))
# Circle brush pattern
for x in range(-radius, radius + 1):
for y in range(-radius, radius + 1):
if x*x + y*y <= radius*radius: # Circle boundary
var target = grid_pos + Vector2i(x, y)
world.set_cell(target, element)
# FALLBACK: If element placement fails (e.g., occupied by indestructible block)?
# Check world.can_place(target) before set_cell(), show visual feedback.4. Chunk-Based Rendering (3D Voxels)
Only render visible faces. Use greedy meshing to merge adjacent blocks.
# See scripts/voxel_chunk_manager.gd for full implementation
# EXPERT DECISION TREE:
# - Small worlds (<100k blocks): Single MeshInstance with SurfaceTool
# - Medium worlds (100k-1M blocks): Chunked MultiMesh (see script)
# - Large worlds (>1M blocks): Chunked + greedy meshing + LODSave System for Sandbox Worlds
# chunk_save_data.gd
class_name ChunkSaveData extends Resource
@export var chunk_coord: Vector2i
@export var rle_data: PackedInt32Array # [type_id, count, type_id, count...]
# EXPERT TECHNIQUE: Run-Length Encoding
static func encode_chunk(grid: Dictionary, chunk_pos: Vector2i, chunk_size: int) -> ChunkSaveData:
var data = ChunkSaveData.new()
data.chunk_coord = chunk_pos
var run_type: int = -1
var run_count: int = 0
for y in range(chunk_size):
for x in range(chunk_size):
var world_pos = chunk_pos * chunk_size + Vector2i(x, y)
var cell = grid.get(world_pos)
var type_id = cell.id if cell else 0 # 0 = air
if type_id == run_type:
run_count += 1
else:
if run_count > 0:
data.rle_data.append(run_type)
data.rle_data.append(run_count)
run_type = type_id
run_count = 1
# Flush final run
if run_count > 0:
data.rle_data.append(run_type)
data.rle_data.append(run_count)
return data
# COMPRESSION RESULT: Empty chunk (16×16 = 256 blocks of air)
# Without RLE: 256 integers = 1024 bytes
# With RLE: [0, 256] = 8 bytes (128x compression!)Physics Joints for Player Creations
# joint_tool.gd
func create_hinge(body_a: RigidBody2D, body_b: RigidBody2D, anchor: Vector2) -> void:
var joint = PinJoint2D.new()
joint.global_position = anchor
joint.node_a = body_a.get_path()
joint.node_b = body_b.get_path()
joint.softness = 0.5 # Allows slight flex
add_child(joint)
# EDGE CASE: What if bodies are deleted while joint exists?
# Joint will auto-break in Godot 4.x, but orphaned Node leaks memory.
# SOLUTION:
body_a.tree_exiting.connect(func(): joint.queue_free())
body_b.tree_exiting.connect(func(): joint.queue_free())
# FALLBACK: Player attaches joint to static geometry?
# Check `body.freeze == false` before creating joint.Godot-Specific Expert Notes
- `MultiMeshInstance3D.multimesh.instance_count`: MUST be set before buffer allocation. Cannot dynamically grow — requires recreation.
- `RigidBody2D.sleeping`: Bodies auto-sleep after 2 seconds of no movement. Use
apply_central_impulse(Vector2.ZERO)to force wake without adding force. - `GridMap` vs `MultiMesh`: GridMap uses MeshLibrary (great for variety), MultiMesh uses single mesh (great for speed). Combine: GridMap for structures, MultiMesh for terrain.
- Continuous CD:
continuous_cdrequires convex collision shapes. UseCapsuleShape2Dfor projectiles, NOTRectangleShape2D.
---
🚀 Elite Technical Implementations (Batch 09)
1. Greedy-Meshing Pattern (Quad Optimization)
Rendering individual voxels is a bottleneck. Greedy meshing combines adjacent identical faces into single large quads. For maximum performance, bypass the SceneTree and submit generated arrays directly to the RenderingServer.
class_name VoxelChunkMesher extends RefCounted
## Generates optimized mesh data and pushes it to the RenderingServer.
static func build_greedy_mesh(chunk_transform: Transform3D, scenario_rid: RID) -> RID:
var vertices := PackedVector3Array()
var normals := PackedVector3Array()
var indices := PackedInt32Array()
# ... algorithm calculates optimized quads ...
var surface_array := []
surface_array.resize(Mesh.ARRAY_MAX)
surface_array[Mesh.ARRAY_VERTEX] = vertices
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
var mesh_rid := RenderingServer.mesh_create()
RenderingServer.mesh_add_surface_from_arrays(mesh_rid, Mesh.PRIMITIVE_TRIANGLES, surface_array)
var instance_rid := RenderingServer.instance_create()
RenderingServer.instance_set_base(instance_rid, mesh_rid)
RenderingServer.instance_set_transform(instance_rid, chunk_transform)
RenderingServer.instance_set_scenario(instance_rid, scenario_rid)
return instance_rid2. Voxel-GI-Server (Dynamic Global Illumination)
For procedural sandbox worlds, use VoxelGI to provide real-time indirect lighting. Interface with the RenderingServer to allocate GI data for chunks dynamically as they are generated.
class_name VoxelGIServerManager extends Node
var _gi_instance_rid: RID
func _ready() -> void:
RenderingServer.voxel_gi_set_quality(RenderingServer.VOXEL_GI_QUALITY_LOW)
_gi_instance_rid = RenderingServer.voxel_gi_create()
RenderingServer.instance_set_scenario(_gi_instance_rid, get_world_3d().scenario)
func allocate_chunk_gi(chunk_aabb: AABB) -> void:
# Allocation requires to_cell_xform and level_counts buffers
RenderingServer.voxel_gi_allocate_data(_gi_instance_rid, Transform3D(), chunk_aabb, Vector3i(64,64,64), PackedByteArray(), PackedByteArray(), PackedByteArray(), PackedInt32Array())
RenderingServer.voxel_gi_set_dynamic_range(_gi_instance_rid, 2.0)3. Blueprint-Sharing (Base64/JSON Serialization)
Allow players to share creations via simple strings. Use JSON for readable serialization and DisplayServer for clipboard integration.
class_name BlueprintManager extends Node
## Exports chunk data to the OS clipboard.
static func export_blueprint_to_clipboard(blueprint_data: Dictionary) -> void:
var json_string: String = JSON.stringify(blueprint_data)
DisplayServer.clipboard_set(json_string)
## Imports blueprint from clipboard.
static func import_blueprint_from_clipboard() -> Dictionary:
var json_string: String = DisplayServer.clipboard_get()
var parsed_data = JSON.parse_string(json_string)
return parsed_data if parsed_data is Dictionary else {}- Master Skill: godot-master
# skills/genre-sandbox/scripts/cellular_automata_liquid.gd
extends Node
## Cellular Automata Liquid (Expert Pattern)
## Logic for falling sand/water simulation in a grid.
## NOT a Node that enters the scene tree per cell, but a logic handler.
class_name CellularAutomataLiquid
const TYPE_EMPTY = 0
const TYPE_SAND = 1
const TYPE_WATER = 2
const TYPE_WALL = 3
static func simulate_grid(grid: Dictionary, width: int, height: int, dirty_cells: Array) -> void:
# Sort dirty cells bottom-up, to process floor first.
# Actually for falling sand, we want to process BOTTOM UP so a falling stack moves together?
# No, iterating bottom-up allows a grain to fall into the empty space created by the grain below it moving?
# Actually, standard is: Iterate bottom-up.
# Simple sort for example (Optimization: Use a proper dirty rect or active list)
dirty_cells.sort_custom(func(a,b): return a.y > b.y) # Higher Y is lower on screen usually, check coord system
# Assuming Y+ is Down.
var processed = {} # Prevent double moves
for pos in dirty_cells:
if processed.has(pos): continue
var type = grid.get(pos, TYPE_EMPTY)
if type == TYPE_SAND:
_update_sand(grid, pos, processed)
elif type == TYPE_WATER:
_update_water(grid, pos, processed)
static func _update_sand(grid: Dictionary, pos: Vector2i, processed: Dictionary) -> void:
var down = pos + Vector2i(0, 1)
if not grid.has(down): # Empty
_move(grid, pos, down, processed)
else:
var down_left = pos + Vector2i(-1, 1)
var down_right = pos + Vector2i(1, 1)
# Random disperse
var moves = [down_left, down_right]
moves.shuffle()
for m in moves:
if not grid.has(m):
_move(grid, pos, m, processed)
return
static func _update_water(grid: Dictionary, pos: Vector2i, processed: Dictionary) -> void:
var down = pos + Vector2i(0, 1)
if not grid.has(down):
_move(grid, pos, down, processed)
return
# Flow sideways
var left = pos + Vector2i(-1, 0)
var right = pos + Vector2i(1, 0)
var moves = [left, right]
moves.shuffle()
for m in moves:
if not grid.has(m):
_move(grid, pos, m, processed)
return
static func _move(grid: Dictionary, from: Vector2i, to: Vector2i, processed: Dictionary) -> void:
var type = grid[from]
grid.erase(from)
grid[to] = type
processed[to] = true
# Note: This simple dict approach is not performant for 100k cells.
# Use PackedByteArray and flat indexing for real implementations.
## EXPERT USAGE:
## Call from _physics_process on the active chunk data.
extends Node3D
class_name DynamicPlacementValidator
## Expert Grid Placement (Godot 4.6).
## Low-level physics intersection check for obstruction validation.
@export var grid_size: float = 2.0
@export var ghost_shape: Shape3D
func get_grid_pos(raw_pos: Vector3) -> Vector3:
return raw_pos.snapped(Vector3.ONE * grid_size)
func is_spot_clear(pos: Vector3) -> bool:
var space = get_world_3d().direct_space_state
var query = PhysicsShapeQueryParameters3D.new()
query.shape = ghost_shape
query.transform = Transform3D(Basis(), pos)
# Expert Pattern: Query physics server directly for instant results
var results = space.intersect_shape(query, 1)
return results.is_empty()
## [SKILL NOTICE]: Use 'intersect_shape()' on the physics space state
## for instant placement validation. Avoid using Area3D signals for this.
# sandbox_patterns.gd
extends Node
# 1. Background Async Chunk Loading
# EXPERT NOTE: Use ResourceLoader for non-blocking asset loading during exploration.
func load_chunk_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
func get_loaded_chunk(path: String) -> PackedScene:
if ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_LOADED:
return ResourceLoader.load_threaded_get(path)
return null
# 2. Multithreaded Terrain Generation
# EXPERT NOTE: Dispatches array math to available worker threads to prevent stalls.
func generate_terrain_mesh() -> void:
WorkerThreadPool.add_task(_compute_noise_arrays, true, "TerrainGen")
func _compute_noise_arrays() -> void:
# Heavy noise/array computation here
pass
# 3. Thread-Safe Data Mutation (Mutex)
# EXPERT NOTE: Synchronize access to shared chunk data across threads.
var _chunk_mutex := Mutex.new()
func safely_update_chunk_data(callback: Callable) -> void:
_chunk_mutex.lock()
callback.call()
_chunk_mutex.unlock()
# 4. Modifying 3D GridMaps (Block Placement)
# EXPERT NOTE: High-performance cell manipulation for voxel-style interactions.
func place_block(grid: GridMap, world_pos: Vector3, block_id: int) -> void:
var map_coords := grid.local_to_map(world_pos)
grid.set_cell_item(map_coords, block_id)
# 5. Saving Voxel Data to Binary
# EXPERT NOTE: .res is highly optimized for large arrays compared to text formats.
func save_binary_world_data(resource: Resource, path: String) -> void:
ResourceSaver.save(resource, path)
# 6. Procedural Geometry (SurfaceTool)
# EXPERT NOTE: Building custom meshes dynamically for terrain/buildings.
func build_custom_mesh() -> Mesh:
var st := SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
st.set_normal(Vector3.UP)
st.add_vertex(Vector3(-1, 0, -1))
st.add_vertex(Vector3(1, 0, -1))
st.add_vertex(Vector3(0, 0, 1))
return st.commit()
# 7. Serializing Chunk Entities
# EXPERT NOTE: Recursively save child node states into a dictionary.
func serialize_chunk(nodes: Array[Node]) -> Dictionary:
var data := {}
for node in nodes:
if node.has_method(&"save"):
data[node.name] = node.call(&"save")
return data
# 8. Fast Physics Picking for Interaction
# EXPERT NOTE: Optimized raycasting for voxel targeting.
func get_aimed_block(cam: Camera3D, screen_pos: Vector2) -> Dictionary:
var origin := cam.project_ray_origin(screen_pos)
var direction := cam.project_ray_normal(screen_pos)
var query := PhysicsRayQueryParameters3D.create(origin, origin + direction * 10)
return cam.get_world_3d().direct_space_state.intersect_ray(query)
# 9. VoxelGI Runtime Allocation
# EXPERT NOTE: Assign procedural data into the Global Illumination server natively.
func setup_global_illumination(gi_rid: RID, aabb: AABB, data: PackedByteArray) -> void:
RenderingServer.voxel_gi_allocate_data(gi_rid, Transform3D.IDENTITY, aabb, Vector3i(64,64,64), data, data, data, PackedInt32Array())
# 10. Large World Origin Shifting
# EXPERT NOTE: Periodically reset the world origin to prevent floating-point jitter.
func shift_world_origin(new_origin: Vector3) -> void:
for child in get_tree().root.get_children():
if child is Node3D:
child.global_position -= new_origin
extends Node
class_name SandboxWorldSerializer
## Expert Sandbox Serialization (Godot 4.6).
## Serializes all player-placed 'Persist' nodes to JSON.
const SAVE_PATH = "user://sandbox_world.json"
func save_world() -> void:
var data = []
for node in get_tree().get_nodes_in_group("Persist"):
if node.scene_file_path.is_empty(): continue
var node_data = {
"res": node.scene_file_path,
"pos": var_to_str(node.global_position),
"rot": var_to_str(node.global_rotation)
}
data.append(node_data)
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
file.store_string(JSON.stringify(data))
func load_world() -> void:
if not FileAccess.file_exists(SAVE_PATH): return
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
var data = JSON.parse_string(file.get_as_text())
for entry in data:
var scene = load(entry.res) as PackedScene
var inst = scene.instantiate() as Node3D
get_tree().current_scene.add_child(inst)
inst.global_position = str_to_var(entry.pos)
inst.global_rotation = str_to_var(entry.rot)
## [SKILL NOTICE]: Use 'scene_file_path' and 'str_to_var()' to cleanly
## serialize complex 3D world states into lightweight JSON files.
# skills/genre-sandbox/code/voxel_chunk_manager.gd
extends Node3D
## Voxel Chunk Manager Expert Pattern
## Manages chunked rendering using MultiMeshInstance3D for optimization.
@export var chunk_size: Vector3i = Vector3i(16, 16, 16)
@export var voxel_mesh: Mesh
var _chunks: Dictionary = {} # Dictionary of Vector3i -> MultiMeshInstance3D
func set_voxel(world_pos: Vector3i, type: int) -> void:
var chunk_coord = Vector3i(
floor(float(world_pos.x) / chunk_size.x),
floor(float(world_pos.y) / chunk_size.y),
floor(float(world_pos.z) / chunk_size.z)
)
var local_pos = world_pos - (chunk_coord * chunk_size)
_update_chunk(chunk_coord, local_pos, type)
func _update_chunk(coord: Vector3i, local_pos: Vector3i, type: int) -> void:
if not _chunks.has(coord):
_create_chunk(coord)
var mm = _chunks[coord]
# In an expert implementation, we would manage a typed buffer here.
# For the pattern, we show the transformation logic.
var index = _get_index(local_pos)
var transform = Transform3D(Basis(), Vector3(local_pos))
if type > 0:
mm.multimesh.set_instance_transform(index, transform)
# mm.multimesh.set_instance_custom_data(index, Color(type, 0, 0))
else:
# Hide the voxel by moving it out of view or scaling to 0
mm.multimesh.set_instance_transform(index, Transform3D(Basis().scaled(Vector3.ZERO), Vector3.ZERO))
func _create_chunk(coord: Vector3i) -> void:
var mm = MultiMeshInstance3D.new()
mm.multimesh = MultiMesh.new()
mm.multimesh.transform_format = MultiMesh.TRANSFORM_3D
mm.multimesh.use_custom_data = true
mm.multimesh.instance_count = chunk_size.x * chunk_size.y * chunk_size.z
mm.multimesh.mesh = voxel_mesh
add_child(mm)
mm.global_position = Vector3(coord * chunk_size)
_chunks[coord] = mm
func _get_index(pos: Vector3i) -> int:
return pos.x + (pos.y * chunk_size.x) + (pos.z * chunk_size.x * chunk_size.y)
## EXPERT NOTE:
## For performance, NEVER update MultiMesh instance transforms every frame.
## Only update when the voxel data changes.
## For 'genre-sandbox' games like Minecraft, use a custom MESH GENERATOR
## (SurfaceTool) to create an optimized "Greedy Meshed" chunk instead of
## individual meshes to reduce draw calls from thousands to one.
extends MeshInstance3D
class_name VoxelChunkMesher
## Expert Voxel Meshing (Godot 4.6).
## Uses SurfaceTool with background threading (WorkerThreadPool).
const CHUNK_SIZE = 16
var _voxels: PackedByteArray # 16x16x16 = 4096 bytes
func regenerate_mesh() -> void:
# Expert Pattern: Offload generation to save frame budget
WorkerThreadPool.add_task(_build_surface)
func _build_surface() -> void:
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
for x in CHUNK_SIZE:
for y in CHUNK_SIZE:
for z in CHUNK_SIZE:
if _get_voxel(x, y, z) > 0:
_add_visible_faces(st, x, y, z)
st.index()
st.generate_normals()
var final_mesh = st.commit()
# Thread-safe assignment
call_deferred("set_mesh", final_mesh)
func _add_visible_faces(st: SurfaceTool, x: int, y: int, z: int) -> void:
# Face Culling: Only add faces if the neighbor is transparent/air
if _get_voxel(x, y + 1, z) == 0:
_add_face(st, Vector3(x, y + 1, z), Vector3.UP)
func _get_voxel(x, y, z) -> int:
if x < 0 or x >= CHUNK_SIZE or y < 0 or y >= CHUNK_SIZE or z < 0 or z >= CHUNK_SIZE:
return 0
return _voxels[x + (y * CHUNK_SIZE) + (z * CHUNK_SIZE * CHUNK_SIZE)]
## [SKILL NOTICE]: Use 'WorkerThreadPool' to generate voxel meshes.
## This prevents frame drops when players modify large chunks of the world.
# skills/genre-sandbox/scripts/voxel_world.gd
extends Node3D
## Voxel World Manager (Expert Pattern)
## Handles chunk management for voxel worlds.
## Barebones structure for Dictionary-based sparse storage.
class_name VoxelWorld
@export var chunk_size: int = 16
@export var render_distance: int = 4
@export var player: Node3D
var chunks: Dictionary = {} # Vector3i -> ChunkNode
func _process(delta: float) -> void:
if not player: return
var player_chunk = _world_to_chunk(player.global_position)
_update_chunks(player_chunk)
func _update_chunks(center: Vector3i) -> void:
# 1. Identify needs
var needed = []
for x in range(-render_distance, render_distance + 1):
for y in range(-2, 3): # Height limit usually smaller
for z in range(-render_distance, render_distance + 1):
needed.append(center + Vector3i(x, y, z))
# 2. Unload
var to_remove = []
for key in chunks:
if not key in needed:
to_remove.append(key)
for key in to_remove:
chunks[key].queue_free()
chunks.erase(key)
# 3. Load (Simplistic main thread for example, should be Threaded)
for key in needed:
if not chunks.has(key):
_create_chunk(key)
func _create_chunk(coord: Vector3i) -> void:
var chunk = Node3D.new() # Placeholder for MeshInstance
chunk.name = "Chunk_%s_%s_%s" % [coord.x, coord.y, coord.z]
chunks[coord] = chunk
add_child(chunk)
# Trigger generation thread here
func _world_to_chunk(pos: Vector3) -> Vector3i:
return Vector3i(floor(pos.x / chunk_size), floor(pos.y / chunk_size), floor(pos.z / chunk_size))
## EXPERT USAGE:
## extend this script to integrate actual mesh generation.