133 lines
4.4 KiB
GDScript
133 lines
4.4 KiB
GDScript
extends Node3D
|
|
class_name ChunkManager
|
|
|
|
const CHUNK_SIZE: int = 16
|
|
|
|
@export var render_distance: int = 2
|
|
@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 _regen_border_neighbors(chunk_coord: Vector3i, local: Vector3i) -> void:
|
|
# Regen adjacent chunk only if block is on the border face
|
|
var offsets: Array[Vector3i] = []
|
|
if local.x == 0:
|
|
offsets.append(Vector3i(-1, 0, 0))
|
|
elif local.x == CHUNK_SIZE - 1:
|
|
offsets.append(Vector3i(1, 0, 0))
|
|
if local.y == 0:
|
|
offsets.append(Vector3i(0, -1, 0))
|
|
elif local.y == CHUNK_SIZE - 1:
|
|
offsets.append(Vector3i(0, 1, 0))
|
|
if local.z == 0:
|
|
offsets.append(Vector3i(0, 0, -1))
|
|
elif local.z == CHUNK_SIZE - 1:
|
|
offsets.append(Vector3i(0, 0, 1))
|
|
for offset: Vector3i in offsets:
|
|
var neighbor: Vector3i = chunk_coord + offset
|
|
if chunks.has(neighbor):
|
|
chunks[neighbor].generate_mesh()
|
|
|
|
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()
|
|
_regen_border_neighbors(chunk_coord, local)
|
|
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()
|
|
_regen_border_neighbors(chunk_coord, local)
|
|
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)
|