moved chunkgen to 2nd thread, initial solution
This commit is contained in:
parent
675b451723
commit
ad598f980e
7 changed files with 134 additions and 70 deletions
|
@ -3,12 +3,13 @@ extends TileMap
|
|||
|
||||
var x:int = -1
|
||||
var y:int = -1
|
||||
var should_remove:bool = true
|
||||
var should_remove:bool = false
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _init(xpos:int, ypos:int):
|
||||
func _init(ypos:int, xpos:int, sr: bool):
|
||||
self.x = xpos
|
||||
self.y = ypos
|
||||
self.should_remove = sr
|
||||
|
||||
self.name = "Chunk [%d,%d]" % [x, y]
|
||||
self.set_tileset(Globals.TILESET_TERRAIN)
|
||||
|
|
|
@ -5,18 +5,23 @@ extends Camera2D
|
|||
var is_panning_camera = false
|
||||
var tween
|
||||
|
||||
|
||||
func camera_zoom_in() -> void:
|
||||
_set_camera_zoom_level(Globals.CAMERA_ZOOM_LEVEL - Globals.CAMERA_ZOOM_FACTOR)
|
||||
|
||||
|
||||
func camera_zoom_out() -> void:
|
||||
_set_camera_zoom_level(Globals.CAMERA_ZOOM_LEVEL + Globals.CAMERA_ZOOM_DURATION)
|
||||
|
||||
|
||||
func get_camera_position():
|
||||
return self.position
|
||||
|
||||
func _process(delta) -> void:
|
||||
|
||||
func _process(_delta) -> void:
|
||||
Globals.CAMERA_POSITION = self.position
|
||||
|
||||
|
||||
func _set_camera_zoom_level(value: float) -> void:
|
||||
Globals.CAMERA_ZOOM_LEVEL = clamp(value, Globals.CAMERA_MIN_ZOOM_LEVEL, Globals.CAMERA_MAX_ZOOM_LEVEL)
|
||||
|
||||
|
@ -29,9 +34,11 @@ func _set_camera_zoom_level(value: float) -> void:
|
|||
Globals.CAMERA_ZOOM_DURATION
|
||||
)
|
||||
|
||||
|
||||
func _on_set_camera_position(pos: Vector2) -> void:
|
||||
self.position = pos
|
||||
|
||||
|
||||
func _unhandled_input(event):
|
||||
if event.is_action_pressed("camera_zoom_in"):
|
||||
camera_zoom_in()
|
||||
|
|
|
@ -10,9 +10,12 @@ extends Node2D
|
|||
# which is extremely slow in godot 4.0, 4096x4096 takes minutes to fill with set_cell() commands
|
||||
|
||||
var chunks:Dictionary = {}
|
||||
var unready_chunks:Dictionary = {}
|
||||
var window_width = DisplayServer.window_get_size(0).x
|
||||
var distance = abs((window_width/(Globals.CHUNK_SIZE.x*Globals.TILE_SIZE_X)) / 2 +1 )
|
||||
var mutex:Mutex
|
||||
var semaphore:Semaphore
|
||||
var thread:Thread
|
||||
var exit_thread = false
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
|
@ -20,23 +23,55 @@ func _init() -> void:
|
|||
|
||||
|
||||
func _process(_delta):
|
||||
# if !Globals.worlgen_ready || Globals.chunk_queue.size() > 64:
|
||||
# return
|
||||
update_chunks()
|
||||
clean_up_chunks()
|
||||
reset_chunks()
|
||||
|
||||
#print(self.get_child_count())
|
||||
|
||||
|
||||
func _ready():
|
||||
#thread = Thread.new()
|
||||
#print(distance)
|
||||
pass
|
||||
mutex = Mutex.new()
|
||||
semaphore = Semaphore.new()
|
||||
exit_thread = false
|
||||
|
||||
thread = Thread.new()
|
||||
thread.start(start_chunkgen, Thread.PRIORITY_NORMAL)
|
||||
|
||||
|
||||
func add_chunk(x:int, y:int) -> void:
|
||||
var key = str(y) + "," + str(x)
|
||||
if chunks.has(key):
|
||||
return
|
||||
func start_chunkgen():
|
||||
while true:
|
||||
semaphore.wait()
|
||||
|
||||
load_chunk(x, y, key)
|
||||
mutex.lock()
|
||||
var should_exit = exit_thread # Protect with Mutex.
|
||||
mutex.unlock()
|
||||
|
||||
if should_exit:
|
||||
break
|
||||
|
||||
if Globals.chunk_queue.size() > 0:
|
||||
mutex.lock()
|
||||
var vars = Globals.chunk_queue.pop_front()
|
||||
mutex.unlock()
|
||||
|
||||
load_chunk(vars[0].y, vars[0].x, vars[1])
|
||||
|
||||
|
||||
# Thread must be disposed (or "joined"), for portability.
|
||||
func _exit_tree():
|
||||
# Set exit condition to true.
|
||||
mutex.lock()
|
||||
exit_thread = true # Protect with Mutex.
|
||||
mutex.unlock()
|
||||
|
||||
# Unblock by posting.
|
||||
semaphore.post()
|
||||
|
||||
# Wait until it exits.
|
||||
thread.wait_to_finish()
|
||||
|
||||
|
||||
func clean_up_chunks():
|
||||
|
@ -47,17 +82,14 @@ func clean_up_chunks():
|
|||
chunks.erase(key)
|
||||
|
||||
|
||||
func clear_chunk(pos:Vector2i) -> void:
|
||||
self.chunks[pos.y][pos.x].clear()
|
||||
|
||||
|
||||
func correction_factor(distance) -> float:
|
||||
func correction_factor(d) -> float:
|
||||
if Globals.CAMERA_ZOOM_LEVEL < 0.6:
|
||||
return distance * 2.0
|
||||
return d * 2.0
|
||||
elif Globals.CAMERA_ZOOM_LEVEL > 1.0:
|
||||
return distance
|
||||
return d
|
||||
else:
|
||||
return distance * ( 1 + 2 * (1-Globals.CAMERA_ZOOM_LEVEL) )
|
||||
return d * ( 1 + 2 * (1-Globals.CAMERA_ZOOM_LEVEL) )
|
||||
|
||||
|
||||
func get_chunk(x:int, y:int):
|
||||
var key = str(y) + "," + str(x)
|
||||
|
@ -67,17 +99,21 @@ func get_chunk(x:int, y:int):
|
|||
return null
|
||||
|
||||
|
||||
func load_chunk(x:int, y:int, key:String):
|
||||
var chunk = Chunk.new(x,y)
|
||||
self.add_child(chunk)
|
||||
chunks[key] = chunk
|
||||
func load_chunk(y:int, x:int, key):
|
||||
var chunk = Chunk.new(y, x, false)
|
||||
call_deferred("add_child", chunk)
|
||||
|
||||
Globals.chunks_loaded += 1
|
||||
mutex.lock()
|
||||
chunks[key] = chunk
|
||||
mutex.unlock()
|
||||
|
||||
|
||||
func reset_chunks():
|
||||
# avoid trying to edit already removed chunks
|
||||
mutex.lock()
|
||||
for key in chunks:
|
||||
chunks[key].should_remove = true
|
||||
mutex.unlock()
|
||||
|
||||
|
||||
func update_chunks():
|
||||
|
@ -88,13 +124,23 @@ func update_chunks():
|
|||
# based on current zoom level.
|
||||
var zoom_corrected = correction_factor(distance)
|
||||
|
||||
# iterate through all the chunks. if a chunk is in camera range,
|
||||
# and it exists, it should not be removed
|
||||
# if chunk should be in range and it doesn't exist, it will be
|
||||
# created by adding the coords to a work queue
|
||||
for y in Globals.map_size/Globals.CHUNK_SIZE.y:
|
||||
for x in Globals.map_size/Globals.CHUNK_SIZE.x:
|
||||
if (abs(x - p_x) <= zoom_corrected && abs(y - p_y) <= zoom_corrected):
|
||||
add_chunk(x, y)
|
||||
|
||||
var key = str(y) + "," + str(x)
|
||||
if chunks.has(key):
|
||||
var chunk = get_chunk(x,y)
|
||||
if chunk != null:
|
||||
chunk.should_remove = false
|
||||
else:
|
||||
mutex.lock()
|
||||
Globals.chunk_queue.push_back([Vector2i(x, y), key])
|
||||
mutex.unlock()
|
||||
semaphore.post()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ func _process(_delta):
|
|||
str(get_viewport().get_mouse_position()) +"\n" +
|
||||
"FPS " + str(Engine.get_frames_per_second()) + "\n" +
|
||||
"Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL) + "\n" +
|
||||
"Chunks loaded: " + str(Globals.chunks_loaded) + "\n" +
|
||||
"Chunk queue: " + str(Globals.chunk_queue.size()) + "\n" +
|
||||
"Camera pos: " + str(Globals.CAMERA_POSITION)
|
||||
)
|
||||
|
||||
|
|
|
@ -4,23 +4,25 @@
|
|||
extends Node
|
||||
|
||||
var chunks_loaded:int = 0
|
||||
var chunk_queue:Array = []
|
||||
var worlgen_ready:bool = false
|
||||
|
||||
###################################
|
||||
# CHUNK AND TERRAIN SETTINGS #
|
||||
###################################
|
||||
|
||||
# world map chunk size
|
||||
const CHUNK_SIZE:Vector2i = Vector2i(32,32)
|
||||
|
||||
# tilemap tile types
|
||||
enum {TILE_WATER, TILE_TERRAIN, TILE_FOREST, TILE_BOG}
|
||||
|
||||
# tilemap layers
|
||||
enum {LAYER_TERRAIN, LAYER_BUILDINGS}
|
||||
const TILESET_TERRAIN:TileSet = preload("res://scenes/Chunk.tres")
|
||||
|
||||
func choose_randomly(list_of_entries):
|
||||
return list_of_entries[randi() % list_of_entries.size()]
|
||||
|
||||
# map size is based on input image x*y pixel size
|
||||
var map_size:int
|
||||
var map_size:int = 0
|
||||
|
||||
# store terrain type (water, land, forest etc. for every map cell)
|
||||
var map_terrain_data:Array[Array] = [[]]
|
||||
|
|
|
@ -32,25 +32,18 @@ func _init():
|
|||
# )
|
||||
Globals.CAMERA_POSITION = Vector2(16*256/2, 16*256/2)
|
||||
|
||||
#func _process(_delta):
|
||||
# Globals.CAMERA_POSITION = _2d_camera.position
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
# create a new world and worldgenerator
|
||||
_world_generator = WorldGenerator.new()
|
||||
_chunk_handler = ChunkHandler.new()
|
||||
#_2d_camera = CameraZoom2D.new()
|
||||
|
||||
# add chunk handler if worldgen was successful
|
||||
if _world_generator.generate_world(map_filename):
|
||||
Globals.worlgen_ready = true
|
||||
_chunk_handler = ChunkHandler.new()
|
||||
add_child(_chunk_handler)
|
||||
#add_child(_2d_camera)
|
||||
|
||||
# for y in Globals.map_size/Globals.CHUNK_SIZE.y:
|
||||
# for x in Globals.map_size/Globals.CHUNK_SIZE.x:
|
||||
# #if (y + x) % 2 == 0:
|
||||
# _chunk_handler.load_chunk(x, y)
|
||||
else:
|
||||
push_error("World generation failed :-(")
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ extends RefCounted
|
|||
|
||||
var image:Image = Image.new()
|
||||
|
||||
|
||||
var directions:Array = [
|
||||
Vector2i(0,1), # south
|
||||
Vector2i(1,0), # east
|
||||
|
@ -10,6 +11,7 @@ var directions:Array = [
|
|||
Vector2i(-1,0) # west
|
||||
]
|
||||
|
||||
|
||||
func are_coords_valid(value:int, bounds:Vector2i, errmsg:String) -> bool:
|
||||
if bounds.x > value or value > bounds.y:
|
||||
errmsg = errmsg % [value, bounds.x, bounds.y]
|
||||
|
@ -18,6 +20,11 @@ func are_coords_valid(value:int, bounds:Vector2i, errmsg:String) -> bool:
|
|||
|
||||
return true
|
||||
|
||||
|
||||
func choose_randomly(list_of_entries):
|
||||
return list_of_entries[randi() % list_of_entries.size()]
|
||||
|
||||
|
||||
func choose_tile(tile:Vector2i, selected, surrounding) -> Array:
|
||||
var surrounding_tiles:Array = []
|
||||
|
||||
|
@ -37,15 +44,16 @@ func choose_tile(tile:Vector2i, selected, surrounding) -> Array:
|
|||
if selected_tile == null:
|
||||
tile_coords = Globals.td[selected].get("default")[0]
|
||||
elif selected_tile.size() > 1:
|
||||
tile_coords = Globals.choose_randomly(selected_tile)
|
||||
tile_coords = choose_randomly(selected_tile)
|
||||
else:
|
||||
tile_coords = selected_tile[0]
|
||||
|
||||
return [
|
||||
tile_coords,
|
||||
0 if selected_tile else Globals.choose_randomly([0,1,2,3])
|
||||
0 if selected_tile else choose_randomly([0,1,2,3])
|
||||
]
|
||||
|
||||
|
||||
# Generates biomes, like forest and bog
|
||||
func generate_biomes() -> void:
|
||||
# generate a new noisemap which should emulate forest-looking areas
|
||||
|
@ -81,6 +89,7 @@ func generate_biomes() -> void:
|
|||
Globals.map_terrain_data[y][x] = Globals.TILE_FOREST
|
||||
# can add other tresholds here for other biomes
|
||||
|
||||
|
||||
func generate_world(filename) -> bool:
|
||||
var image_size:Vector2i
|
||||
|
||||
|
@ -106,30 +115,31 @@ func generate_world(filename) -> bool:
|
|||
var start = Time.get_ticks_usec()
|
||||
read_image_pixel_data()
|
||||
var end = Time.get_ticks_usec()
|
||||
print("read image data ", (end-start)/1000.0, "ms")
|
||||
print("1/5: read image data ", (end-start)/1000.0, "ms")
|
||||
|
||||
start = Time.get_ticks_usec()
|
||||
smooth_land_features(Globals.TILE_WATER) # smooth water
|
||||
end = Time.get_ticks_usec()
|
||||
print("smooth water ", (end-start)/1000.0, "ms")
|
||||
print("2/5: smooth water ", (end-start)/1000.0, "ms")
|
||||
|
||||
start = Time.get_ticks_usec()
|
||||
generate_biomes()
|
||||
end = Time.get_ticks_usec()
|
||||
print("generate biomes ", (end-start)/1000.0, "ms")
|
||||
print("3/5: generate biomes ", (end-start)/1000.0, "ms")
|
||||
|
||||
start = Time.get_ticks_usec()
|
||||
smooth_land_features(Globals.TILE_FOREST) # smooth out forest
|
||||
end = Time.get_ticks_usec()
|
||||
print("smooth forest ", (end-start)/1000.0, "ms")
|
||||
print("4/5: smooth forest ", (end-start)/1000.0, "ms")
|
||||
|
||||
start = Time.get_ticks_usec()
|
||||
select_tilemap_tiles()
|
||||
end = Time.get_ticks_usec()
|
||||
print("select tiles ", (end-start)/1000.0, "ms")
|
||||
print("5/5: select tiles ", (end-start)/1000.0, "ms")
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func read_image_pixel_data():
|
||||
# initialize the array to have enough rows
|
||||
Globals.map_terrain_data.resize(Globals.map_size)
|
||||
|
@ -147,6 +157,7 @@ func read_image_pixel_data():
|
|||
else:
|
||||
Globals.map_terrain_data[y][x] = Globals.TILE_TERRAIN
|
||||
|
||||
|
||||
func select_tilemap_tiles() -> void:
|
||||
for y in Globals.map_terrain_data.size():
|
||||
for x in Globals.map_terrain_data[y].size():
|
||||
|
@ -159,9 +170,10 @@ func select_tilemap_tiles() -> void:
|
|||
Globals.map_tile_data[y][x] = choose_tile(Vector2i(x,y), Globals.TILE_TERRAIN, Globals.TILE_FOREST)
|
||||
|
||||
Globals.TILE_FOREST:
|
||||
Globals.map_tile_data[y][x] = [Vector2i(5,1), Globals.choose_randomly([0,1,2,3])]
|
||||
Globals.map_tile_data[y][x] = [Vector2i(5,1), choose_randomly([0,1,2,3])]
|
||||
|
||||
_: #default
|
||||
push_error("should be never here in worldgen!")
|
||||
pass
|
||||
|
||||
|
||||
|
@ -188,6 +200,7 @@ func smooth_land_features(tile_type:int) -> void:
|
|||
Globals.TILE_TERRAIN
|
||||
)
|
||||
|
||||
|
||||
# TEMP SPAGHETTI SOLUTION
|
||||
func smooth_forest_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
||||
# now we are supposed to be inspecting a tile with land
|
||||
|
@ -222,7 +235,8 @@ func smooth_forest_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
|||
_:
|
||||
return
|
||||
|
||||
#smooth_forest_recursively(pos, selected, comp)
|
||||
smooth_forest_recursively(pos, selected, comp)
|
||||
|
||||
|
||||
func smooth_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
||||
# now we are supposed to be inspecting a tile with land
|
||||
|
@ -256,6 +270,7 @@ func smooth_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
|||
|
||||
smooth_recursively(pos, selected, comp)
|
||||
|
||||
|
||||
func validate_mapgen_params() -> bool:
|
||||
if !are_coords_valid(
|
||||
Globals.map_size,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue