1extends Node 2# This file manages the creation and deletion of Chunks. 3 4const CHUNK_MIDPOINT = Vector3(0.5, 0.5, 0.5) * Chunk.CHUNK_SIZE 5const CHUNK_END_SIZE = Chunk.CHUNK_SIZE - 1 6 7var render_distance setget _set_render_distance 8var _delete_distance = 0 9var effective_render_distance = 0 10var _old_player_chunk = Vector3() # TODO: Vector3i 11 12var _generating = true 13var _deleting = false 14 15var _chunks = {} 16 17onready var player = $"../Player" 18 19 20func _process(_delta): 21 _set_render_distance(Settings.render_distance) 22 var player_chunk = (player.transform.origin / Chunk.CHUNK_SIZE).round() 23 24 if _deleting or player_chunk != _old_player_chunk: 25 _delete_far_away_chunks(player_chunk) 26 _generating = true 27 28 if not _generating: 29 return 30 31 # Try to generate chunks ahead of time based on where the player is moving. 32 player_chunk.y += round(clamp(player.velocity.y, -render_distance / 4, render_distance / 4)) 33 34 # Check existing chunks within range. If it doesn't exist, create it. 35 for x in range(player_chunk.x - effective_render_distance, player_chunk.x + effective_render_distance): 36 for y in range(player_chunk.y - effective_render_distance, player_chunk.y + effective_render_distance): 37 for z in range(player_chunk.z - effective_render_distance, player_chunk.z + effective_render_distance): 38 var chunk_position = Vector3(x, y, z) 39 if player_chunk.distance_to(chunk_position) > render_distance: 40 continue 41 42 if _chunks.has(chunk_position): 43 continue 44 45 var chunk = Chunk.new() 46 chunk.chunk_position = chunk_position 47 _chunks[chunk_position] = chunk 48 add_child(chunk) 49 return 50 51 # If we didn't generate any chunks (and therefore didn't return), what next? 52 if effective_render_distance < render_distance: 53 # We can move on to the next stage by increasing the effective distance. 54 effective_render_distance += 1 55 else: 56 # Effective render distance is maxed out, done generating. 57 _generating = false 58 59 60func get_block_global_position(block_global_position): 61 var chunk_position = (block_global_position / Chunk.CHUNK_SIZE).floor() 62 if _chunks.has(chunk_position): 63 var chunk = _chunks[chunk_position] 64 var sub_position = block_global_position.posmod(Chunk.CHUNK_SIZE) 65 if chunk.data.has(sub_position): 66 return chunk.data[sub_position] 67 return 0 68 69 70func set_block_global_position(block_global_position, block_id): 71 var chunk_position = (block_global_position / Chunk.CHUNK_SIZE).floor() 72 var chunk = _chunks[chunk_position] 73 var sub_position = block_global_position.posmod(Chunk.CHUNK_SIZE) 74 if block_id == 0: 75 chunk.data.erase(sub_position) 76 else: 77 chunk.data[sub_position] = block_id 78 chunk.regenerate() 79 80 # We also might need to regenerate some neighboring chunks. 81 if Chunk.is_block_transparent(block_id): 82 if sub_position.x == 0: 83 _chunks[chunk_position + Vector3.LEFT].regenerate() 84 elif sub_position.x == CHUNK_END_SIZE: 85 _chunks[chunk_position + Vector3.RIGHT].regenerate() 86 if sub_position.z == 0: 87 _chunks[chunk_position + Vector3.FORWARD].regenerate() 88 elif sub_position.z == CHUNK_END_SIZE: 89 _chunks[chunk_position + Vector3.BACK].regenerate() 90 if sub_position.y == 0: 91 _chunks[chunk_position + Vector3.DOWN].regenerate() 92 elif sub_position.y == CHUNK_END_SIZE: 93 _chunks[chunk_position + Vector3.UP].regenerate() 94 95 96func clean_up(): 97 for chunk_position_key in _chunks.keys(): 98 var thread = _chunks[chunk_position_key]._thread 99 if thread: 100 thread.wait_to_finish() 101 _chunks = {} 102 set_process(false) 103 for c in get_children(): 104 c.free() 105 106 107func _delete_far_away_chunks(player_chunk): 108 _old_player_chunk = player_chunk 109 # If we need to delete chunks, give the new chunk system a chance to catch up. 110 effective_render_distance = max(1, effective_render_distance - 1) 111 112 var deleted_this_frame = 0 113 # We should delete old chunks more aggressively if moving fast. 114 # An easy way to calculate this is by using the effective render distance. 115 # The specific values in this formula are arbitrary and from experimentation. 116 var max_deletions = clamp(2 * (render_distance - effective_render_distance), 2, 8) 117 # Also take the opportunity to delete far away chunks. 118 for chunk_position_key in _chunks.keys(): 119 if player_chunk.distance_to(chunk_position_key) > _delete_distance: 120 var thread = _chunks[chunk_position_key]._thread 121 if thread: 122 thread.wait_to_finish() 123 _chunks[chunk_position_key].queue_free() 124 _chunks.erase(chunk_position_key) 125 deleted_this_frame += 1 126 # Limit the amount of deletions per frame to avoid lag spikes. 127 if deleted_this_frame > max_deletions: 128 # Continue deleting next frame. 129 _deleting = true 130 return 131 132 # We're done deleting. 133 _deleting = false 134 135 136func _set_render_distance(value): 137 render_distance = value 138 _delete_distance = value + 2 139