mirror of
https://github.com/Relintai/pandemonium_demo_projects.git
synced 2024-12-21 13:56:50 +01:00
139 lines
4.6 KiB
GDScript3
139 lines
4.6 KiB
GDScript3
|
extends Node
|
||
|
# This file manages the creation and deletion of Chunks.
|
||
|
|
||
|
const CHUNK_MIDPOINT = Vector3(0.5, 0.5, 0.5) * Chunk.CHUNK_SIZE
|
||
|
const CHUNK_END_SIZE = Chunk.CHUNK_SIZE - 1
|
||
|
|
||
|
var render_distance setget _set_render_distance
|
||
|
var _delete_distance = 0
|
||
|
var effective_render_distance = 0
|
||
|
var _old_player_chunk = Vector3() # TODO: Vector3i
|
||
|
|
||
|
var _generating = true
|
||
|
var _deleting = false
|
||
|
|
||
|
var _chunks = {}
|
||
|
|
||
|
onready var player = $"../Player"
|
||
|
|
||
|
|
||
|
func _process(_delta):
|
||
|
_set_render_distance(Settings.render_distance)
|
||
|
var player_chunk = (player.transform.origin / Chunk.CHUNK_SIZE).round()
|
||
|
|
||
|
if _deleting or player_chunk != _old_player_chunk:
|
||
|
_delete_far_away_chunks(player_chunk)
|
||
|
_generating = true
|
||
|
|
||
|
if not _generating:
|
||
|
return
|
||
|
|
||
|
# Try to generate chunks ahead of time based on where the player is moving.
|
||
|
player_chunk.y += round(clamp(player.velocity.y, -render_distance / 4, render_distance / 4))
|
||
|
|
||
|
# Check existing chunks within range. If it doesn't exist, create it.
|
||
|
for x in range(player_chunk.x - effective_render_distance, player_chunk.x + effective_render_distance):
|
||
|
for y in range(player_chunk.y - effective_render_distance, player_chunk.y + effective_render_distance):
|
||
|
for z in range(player_chunk.z - effective_render_distance, player_chunk.z + effective_render_distance):
|
||
|
var chunk_position = Vector3(x, y, z)
|
||
|
if player_chunk.distance_to(chunk_position) > render_distance:
|
||
|
continue
|
||
|
|
||
|
if _chunks.has(chunk_position):
|
||
|
continue
|
||
|
|
||
|
var chunk = Chunk.new()
|
||
|
chunk.chunk_position = chunk_position
|
||
|
_chunks[chunk_position] = chunk
|
||
|
add_child(chunk)
|
||
|
return
|
||
|
|
||
|
# If we didn't generate any chunks (and therefore didn't return), what next?
|
||
|
if effective_render_distance < render_distance:
|
||
|
# We can move on to the next stage by increasing the effective distance.
|
||
|
effective_render_distance += 1
|
||
|
else:
|
||
|
# Effective render distance is maxed out, done generating.
|
||
|
_generating = false
|
||
|
|
||
|
|
||
|
func get_block_global_position(block_global_position):
|
||
|
var chunk_position = (block_global_position / Chunk.CHUNK_SIZE).floor()
|
||
|
if _chunks.has(chunk_position):
|
||
|
var chunk = _chunks[chunk_position]
|
||
|
var sub_position = block_global_position.posmod(Chunk.CHUNK_SIZE)
|
||
|
if chunk.data.has(sub_position):
|
||
|
return chunk.data[sub_position]
|
||
|
return 0
|
||
|
|
||
|
|
||
|
func set_block_global_position(block_global_position, block_id):
|
||
|
var chunk_position = (block_global_position / Chunk.CHUNK_SIZE).floor()
|
||
|
var chunk = _chunks[chunk_position]
|
||
|
var sub_position = block_global_position.posmod(Chunk.CHUNK_SIZE)
|
||
|
if block_id == 0:
|
||
|
chunk.data.erase(sub_position)
|
||
|
else:
|
||
|
chunk.data[sub_position] = block_id
|
||
|
chunk.regenerate()
|
||
|
|
||
|
# We also might need to regenerate some neighboring chunks.
|
||
|
if Chunk.is_block_transparent(block_id):
|
||
|
if sub_position.x == 0:
|
||
|
_chunks[chunk_position + Vector3.LEFT].regenerate()
|
||
|
elif sub_position.x == CHUNK_END_SIZE:
|
||
|
_chunks[chunk_position + Vector3.RIGHT].regenerate()
|
||
|
if sub_position.z == 0:
|
||
|
_chunks[chunk_position + Vector3.FORWARD].regenerate()
|
||
|
elif sub_position.z == CHUNK_END_SIZE:
|
||
|
_chunks[chunk_position + Vector3.BACK].regenerate()
|
||
|
if sub_position.y == 0:
|
||
|
_chunks[chunk_position + Vector3.DOWN].regenerate()
|
||
|
elif sub_position.y == CHUNK_END_SIZE:
|
||
|
_chunks[chunk_position + Vector3.UP].regenerate()
|
||
|
|
||
|
|
||
|
func clean_up():
|
||
|
for chunk_position_key in _chunks.keys():
|
||
|
var thread = _chunks[chunk_position_key]._thread
|
||
|
if thread:
|
||
|
thread.wait_to_finish()
|
||
|
_chunks = {}
|
||
|
set_process(false)
|
||
|
for c in get_children():
|
||
|
c.free()
|
||
|
|
||
|
|
||
|
func _delete_far_away_chunks(player_chunk):
|
||
|
_old_player_chunk = player_chunk
|
||
|
# If we need to delete chunks, give the new chunk system a chance to catch up.
|
||
|
effective_render_distance = max(1, effective_render_distance - 1)
|
||
|
|
||
|
var deleted_this_frame = 0
|
||
|
# We should delete old chunks more aggressively if moving fast.
|
||
|
# An easy way to calculate this is by using the effective render distance.
|
||
|
# The specific values in this formula are arbitrary and from experimentation.
|
||
|
var max_deletions = clamp(2 * (render_distance - effective_render_distance), 2, 8)
|
||
|
# Also take the opportunity to delete far away chunks.
|
||
|
for chunk_position_key in _chunks.keys():
|
||
|
if player_chunk.distance_to(chunk_position_key) > _delete_distance:
|
||
|
var thread = _chunks[chunk_position_key]._thread
|
||
|
if thread:
|
||
|
thread.wait_to_finish()
|
||
|
_chunks[chunk_position_key].queue_free()
|
||
|
_chunks.erase(chunk_position_key)
|
||
|
deleted_this_frame += 1
|
||
|
# Limit the amount of deletions per frame to avoid lag spikes.
|
||
|
if deleted_this_frame > max_deletions:
|
||
|
# Continue deleting next frame.
|
||
|
_deleting = true
|
||
|
return
|
||
|
|
||
|
# We're done deleting.
|
||
|
_deleting = false
|
||
|
|
||
|
|
||
|
func _set_render_distance(value):
|
||
|
render_distance = value
|
||
|
_delete_distance = value + 2
|