diff --git a/project.godot b/project.godot index c73b0b8..8362343 100644 --- a/project.godot +++ b/project.godot @@ -59,4 +59,5 @@ camera_move={ [rendering] +driver/threads/thread_model=2 environment/defaults/default_clear_color=Color(0.658824, 0.329412, 0, 1) diff --git a/scenes/Main.tscn b/scenes/Main.tscn index 6bd6210..79b2b08 100644 --- a/scenes/Main.tscn +++ b/scenes/Main.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://nfayf78xiuap"] +[gd_scene load_steps=6 format=3 uid="uid://nfayf78xiuap"] [ext_resource type="Script" path="res://scripts/Main.gd" id="1_ysxum"] [ext_resource type="Script" path="res://scripts/Control.gd" id="3_1t1c8"] [ext_resource type="PackedScene" uid="uid://2we3txfr812u" path="res://scenes/Camera_zoom_2d.tscn" id="4_rx82t"] [ext_resource type="Script" path="res://scripts/EntityPlacer.gd" id="5_8jju5"] +[ext_resource type="Script" path="res://scripts/Minimap.gd" id="5_rg28x"] [node name="Main" type="Node2D"] script = ExtResource("1_ysxum") @@ -84,7 +85,25 @@ offset_right = 936.0 offset_bottom = 256.0 horizontal_alignment = 2 +[node name="Minimap" type="Container" parent="UILayer"] +anchors_preset = -1 +anchor_top = -0.044 +anchor_right = 0.172 +anchor_bottom = 0.161 +offset_left = 1696.0 +offset_top = 735.36 +offset_right = 1807.68 +offset_bottom = 1032.16 +script = ExtResource("5_rg28x") + +[node name="CameraMarker" type="Sprite2D" parent="UILayer/Minimap"] +position = Vector2(296, 392) + +[node name="MinimapSprite" type="Sprite2D" parent="UILayer/Minimap"] +position = Vector2(416, 472) + [connection signal="set_camera_position" from="." to="CameraZoom2D" method="_on_set_camera_position"] +[connection signal="worldgen_ready" from="." to="UILayer/Minimap" method="_on_main_worldgen_ready"] [connection signal="pressed" from="UILayer/Control/ConstructionPanel/button_residental" to="UILayer/Control" method="_on_button_residental_pressed"] [connection signal="pressed" from="UILayer/Control/ConstructionPanel/button_commercial" to="UILayer/Control" method="_on_button_commercial_pressed"] [connection signal="pressed" from="UILayer/Control/ConstructionPanel/button_industrial" to="UILayer/Control" method="_on_button_industrial_pressed"] diff --git a/scripts/ChunkHandler.gd b/scripts/ChunkHandler.gd index 4eb7bbe..70ccee6 100644 --- a/scripts/ChunkHandler.gd +++ b/scripts/ChunkHandler.gd @@ -12,34 +12,51 @@ extends Node2D var 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 ) + +# for threading +var chunk_queue:Array = [] var mutex:Mutex var semaphore:Semaphore var thread:Thread var exit_thread = false +var _timer:Timer + + +func _exit_tree(): + mutex.lock() + exit_thread = true + mutex.unlock() + + semaphore.post() + thread.wait_to_finish() + func _init() -> void: self.name = "ChunkHandler" 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()) + update_chunks() -func _ready(): +func _ready(): mutex = Mutex.new() semaphore = Semaphore.new() exit_thread = false thread = Thread.new() thread.start(start_chunkgen, Thread.PRIORITY_NORMAL) + + _timer = Timer.new() + add_child(_timer) + _timer.connect("timeout", _on_timer_timeout, 0) + _timer.set_wait_time(0.05) + _timer.set_one_shot(false) + _timer.start() +func _on_timer_timeout(): + clean_up_chunks() func start_chunkgen(): while true: @@ -52,35 +69,22 @@ func start_chunkgen(): if should_exit: break - if Globals.chunk_queue.size() > 0: + # work on emptying the generation queue + if chunk_queue.size() > 0: mutex.lock() - var vars = Globals.chunk_queue.pop_front() + var vars = 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(): + mutex.lock() for key in chunks: var chunk = chunks[key] if chunk.should_remove: chunk.queue_free() chunks.erase(key) - + mutex.unlock() func correction_factor(d) -> float: if Globals.CAMERA_ZOOM_LEVEL < 0.6: @@ -91,8 +95,7 @@ func correction_factor(d) -> float: return d * ( 1 + 2 * (1-Globals.CAMERA_ZOOM_LEVEL) ) -func get_chunk(x:int, y:int): - var key = str(y) + "," + str(x) +func get_chunk(key:Vector2i): if self.chunks.has(key): return chunks.get(key) @@ -108,14 +111,6 @@ func load_chunk(y:int, x:int, key): 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(): var p_x = int(Globals.CAMERA_POSITION.x- Globals.CHUNK_SIZE.x) / Globals.TILE_SIZE_X / Globals.CHUNK_SIZE.x var p_y = int(Globals.CAMERA_POSITION.y- Globals.CHUNK_SIZE.y) / Globals.TILE_SIZE_Y / Globals.CHUNK_SIZE.y @@ -130,17 +125,21 @@ func update_chunks(): # 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: + + var key = Vector2i(y,x) + var chunk = null + + if chunks.has(key): + chunk = get_chunk(key) + if (abs(x - p_x) <= zoom_corrected && abs(y - p_y) <= zoom_corrected): - var key = str(y) + "," + str(x) - if chunks.has(key): - var chunk = get_chunk(x,y) - if chunk != null: - chunk.should_remove = false + if chunk != null: + chunk.should_remove = false else: mutex.lock() - Globals.chunk_queue.push_back([Vector2i(x, y), key]) + chunk_queue.push_back([Vector2i(x, y), key]) mutex.unlock() semaphore.post() - - + elif chunks.has(key): + chunks[key].should_remove = true diff --git a/scripts/Control.gd b/scripts/Control.gd index be33a0a..f91b29f 100644 --- a/scripts/Control.gd +++ b/scripts/Control.gd @@ -4,6 +4,7 @@ extends Control signal button_pressed(button_name) @onready var debug_info = get_node(Globals.DEBUGINFO_NODE) +@onready var minimap:Minimap # name, position var buttons = { @@ -19,14 +20,15 @@ var buttons = { # Called when the node enters the scene tree for the first time. func _ready(): create_buttons() + minimap = Minimap.new() + # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta): debug_info.set_text( str(get_viewport().get_mouse_position()) +"\n" + "FPS " + str(Engine.get_frames_per_second()) + "\n" + - "Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL) + "\n" + - "Chunk queue: " + str(Globals.chunk_queue.size()) + "\n" + + "Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL) + "\n" + "Camera pos: " + str(Globals.CAMERA_POSITION) ) diff --git a/scripts/Globals.gd b/scripts/Globals.gd index 3e8f91c..14864cb 100644 --- a/scripts/Globals.gd +++ b/scripts/Globals.gd @@ -4,7 +4,6 @@ extends Node var chunks_loaded:int = 0 -var chunk_queue:Array = [] var worlgen_ready:bool = false ################################### diff --git a/scripts/Main.gd b/scripts/Main.gd index 1559e15..0d92128 100644 --- a/scripts/Main.gd +++ b/scripts/Main.gd @@ -9,6 +9,7 @@ class_name Main extends Node2D +signal worldgen_ready signal set_camera_position(pos:Vector2) # The idea is for the user to be able to choose the map from GUI later @@ -19,7 +20,7 @@ var map_filenames:Array = [ "res://maps/tampere_256px.png", "res://maps/tampere_10x10km_4096px.png" ] -var map_filename:String = map_filenames[1] +var map_filename:String = map_filenames[2] var _world_generator:WorldGenerator var _chunk_handler:ChunkHandler #var _2d_camera:CameraZoom2D @@ -41,11 +42,14 @@ func _ready(): # 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) + Globals.worlgen_ready = true else: - push_error("World generation failed :-(") + push_error("World generation failed :-(") + + _chunk_handler = ChunkHandler.new() + add_child(_chunk_handler) + + emit_signal("worldgen_ready") # center camera to world map emit_signal( diff --git a/scripts/Minimap.gd b/scripts/Minimap.gd new file mode 100644 index 0000000..b1b3509 --- /dev/null +++ b/scripts/Minimap.gd @@ -0,0 +1,54 @@ +class_name Minimap +extends Node + +@onready var minimap_texture:ImageTexture = null +@onready var sprite:Sprite2D + +# Called when the node enters the scene tree for the first time. +func _ready(): + minimap_texture = ImageTexture.new() + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(_delta): + pass + + +func _on_main_worldgen_ready(): + print("test") + self.generate_minimap() + self.set_minimap() + + +func generate_minimap() -> void: + var image = Image.new() + print(Globals.map_size) + image = Image.create(Globals.map_size, Globals.map_size, false, Image.FORMAT_RGB8) + image.resize(Globals.map_size, Globals.map_size) + for y in Globals.map_size: + for x in Globals.map_size: + var color:Color + + match Globals.map_terrain_data[y][x]: + Globals.TILE_WATER: + color = Color(0,0,255) + Globals.TILE_TERRAIN: + color = Color(148,113,71) + Globals.TILE_FOREST: + color = Color(0,255,0) + _: #default + color = Color(255,0,255) + + image.set_pixel(x, y, color) + + minimap_texture = ImageTexture.create_from_image(image) + + +func set_minimap() -> void: + sprite = self.get_child(1) + sprite.texture = minimap_texture + sprite.set_scale(Vector2i(2,2)) + sprite.set_position(Vector2(0, 0)) + + +