feat(world): voxel chunks + procedural underwater generation + biomes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
110
scripts/world/ChunkManager.gd
Normal file
110
scripts/world/ChunkManager.gd
Normal file
@@ -0,0 +1,110 @@
|
||||
extends Node3D
|
||||
class_name ChunkManager
|
||||
|
||||
const CHUNK_SIZE: int = 16
|
||||
|
||||
@export var render_distance: int = 4
|
||||
@export var world_seed: int = 12345
|
||||
|
||||
var chunks: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
_load_initial_chunks()
|
||||
|
||||
func _load_initial_chunks() -> void:
|
||||
for cx: int in range(-render_distance, render_distance + 1):
|
||||
for cy: int in range(-render_distance, render_distance + 1):
|
||||
for cz: int in range(-render_distance, render_distance + 1):
|
||||
_load_chunk(Vector3i(cx, cy, cz))
|
||||
|
||||
func update_player_position(pos: Vector3) -> void:
|
||||
var player_chunk: Vector3i = world_to_chunk_coord(pos)
|
||||
|
||||
var to_unload: Array[Vector3i] = []
|
||||
for coord: Vector3i in chunks.keys():
|
||||
var dist: Vector3i = coord - player_chunk
|
||||
if abs(dist.x) > render_distance or abs(dist.y) > render_distance or abs(dist.z) > render_distance:
|
||||
to_unload.append(coord)
|
||||
|
||||
for coord: Vector3i in to_unload:
|
||||
_unload_chunk(coord)
|
||||
|
||||
for cx: int in range(player_chunk.x - render_distance, player_chunk.x + render_distance + 1):
|
||||
for cy: int in range(player_chunk.y - render_distance, player_chunk.y + render_distance + 1):
|
||||
for cz: int in range(player_chunk.z - render_distance, player_chunk.z + render_distance + 1):
|
||||
var coord: Vector3i = Vector3i(cx, cy, cz)
|
||||
if not chunks.has(coord):
|
||||
_load_chunk(coord)
|
||||
|
||||
func break_block(world_pos: Vector3) -> int:
|
||||
var chunk_coord: Vector3i = world_to_chunk_coord(world_pos)
|
||||
if not chunks.has(chunk_coord):
|
||||
return 0
|
||||
var chunk: Chunk = chunks[chunk_coord]
|
||||
var local: Vector3i = world_to_local(world_pos)
|
||||
var old_id: int = chunk.get_block(local.x, local.y, local.z)
|
||||
if old_id == BlockDatabase.BlockType.AIR:
|
||||
return 0
|
||||
chunk.set_block(local.x, local.y, local.z, BlockDatabase.BlockType.AIR)
|
||||
chunk.generate_mesh()
|
||||
return old_id
|
||||
|
||||
func place_block(world_pos: Vector3, block_id: int) -> bool:
|
||||
var chunk_coord: Vector3i = world_to_chunk_coord(world_pos)
|
||||
if not chunks.has(chunk_coord):
|
||||
return false
|
||||
var chunk: Chunk = chunks[chunk_coord]
|
||||
var local: Vector3i = world_to_local(world_pos)
|
||||
var current: int = chunk.get_block(local.x, local.y, local.z)
|
||||
if current != BlockDatabase.BlockType.AIR and current != BlockDatabase.BlockType.WATER:
|
||||
return false
|
||||
chunk.set_block(local.x, local.y, local.z, block_id)
|
||||
chunk.generate_mesh()
|
||||
return true
|
||||
|
||||
func get_block(world_pos: Vector3) -> int:
|
||||
var chunk_coord: Vector3i = world_to_chunk_coord(world_pos)
|
||||
if not chunks.has(chunk_coord):
|
||||
return BlockDatabase.BlockType.AIR
|
||||
var chunk: Chunk = chunks[chunk_coord]
|
||||
var local: Vector3i = world_to_local(world_pos)
|
||||
return chunk.get_block(local.x, local.y, local.z)
|
||||
|
||||
func world_to_chunk_coord(pos: Vector3) -> Vector3i:
|
||||
return Vector3i(
|
||||
int(floor(pos.x / float(CHUNK_SIZE))),
|
||||
int(floor(pos.y / float(CHUNK_SIZE))),
|
||||
int(floor(pos.z / float(CHUNK_SIZE)))
|
||||
)
|
||||
|
||||
func chunk_to_world(coord: Vector3i) -> Vector3:
|
||||
return Vector3(
|
||||
float(coord.x * CHUNK_SIZE),
|
||||
float(coord.y * CHUNK_SIZE),
|
||||
float(coord.z * CHUNK_SIZE)
|
||||
)
|
||||
|
||||
func world_to_local(pos: Vector3) -> Vector3i:
|
||||
var ix: int = int(floor(pos.x))
|
||||
var iy: int = int(floor(pos.y))
|
||||
var iz: int = int(floor(pos.z))
|
||||
return Vector3i(
|
||||
((ix % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE,
|
||||
((iy % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE,
|
||||
((iz % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE
|
||||
)
|
||||
|
||||
func _load_chunk(coord: Vector3i) -> void:
|
||||
var data: PackedInt32Array = WorldGenerator.generate_chunk(coord.x, coord.y, coord.z, world_seed)
|
||||
var chunk: Chunk = Chunk.new()
|
||||
add_child(chunk)
|
||||
chunk.init_blocks(data)
|
||||
chunk.position = chunk_to_world(coord)
|
||||
chunk.generate_mesh()
|
||||
chunks[coord] = chunk
|
||||
|
||||
func _unload_chunk(coord: Vector3i) -> void:
|
||||
if chunks.has(coord):
|
||||
var chunk: Chunk = chunks[coord]
|
||||
chunk.queue_free()
|
||||
chunks.erase(coord)
|
||||
Reference in New Issue
Block a user