123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- 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:
- set(value):
- render_distance = value
- _delete_distance = value + 2
- var _delete_distance = 0
- var effective_render_distance = 0
- var _old_player_chunk = Vector3i()
- var _generating = true
- var _deleting = false
- var _chunks = {}
- @onready var player = $"../Player"
- func _process(_delta):
- render_distance = Settings.render_distance
- var player_chunk = Vector3i((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 = Vector3i(x, y, z)
- if Vector3(player_chunk).distance_to(Vector3(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: Vector3i):
- var chunk_position = Vector3i((block_global_position / Chunk.CHUNK_SIZE))
- if _chunks.has(chunk_position):
- var chunk = _chunks[chunk_position]
- var sub_position = Vector3i(Vector3(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: Vector3i, block_id):
- var chunk_position = Vector3i((Vector3(block_global_position) / Chunk.CHUNK_SIZE).floor())
- var chunk = _chunks[chunk_position]
- var sub_position = Vector3i(Vector3(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 + Vector3i.LEFT].regenerate()
- elif sub_position.x == CHUNK_END_SIZE:
- _chunks[chunk_position + Vector3i.RIGHT].regenerate()
- if sub_position.z == 0:
- _chunks[chunk_position + Vector3i.FORWARD].regenerate()
- elif sub_position.z == CHUNK_END_SIZE:
- _chunks[chunk_position + Vector3i.BACK].regenerate()
- if sub_position.y == 0:
- _chunks[chunk_position + Vector3i.DOWN].regenerate()
- elif sub_position.y == CHUNK_END_SIZE:
- _chunks[chunk_position + Vector3i.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 Vector3(player_chunk).distance_to(Vector3(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
|