diff --git a/addons/rts_framework/features/vision/fog_of_war.gd b/addons/rts_framework/features/vision/fog_of_war.gd new file mode 100644 index 0000000..5e7532c --- /dev/null +++ b/addons/rts_framework/features/vision/fog_of_war.gd @@ -0,0 +1,94 @@ +@tool +extends MeshInstance3D +class_name FogOfWarManager + +@export var vision_manager : VisionManager: + set(value): + _vision_manager = value + if _vision_manager: + _apply_fog_texture_from_vision_manager() + get: + return _vision_manager + +# Private backing field +var _vision_manager : VisionManager + +@export_category("Fog Values") + +## The size of a single pixel in the 3D world +var _texture_units_per_world_unit: int = 2 +@export_range(1, 10000, 1,"suffix:px/length") var texture_units_per_world_unit : int = 2 : # px/length + set(value): + if material_override: + material_override.set_shader_parameter("texture_units_per_world_unit", value) + _texture_units_per_world_unit = value + get: + return _texture_units_per_world_unit + +## Color of the generated fog +@export var fog_color : Color : + set(value): + if material_override: + material_override.set_shader_parameter("color", value) + get: + if material_override: + return material_override.get_shader_parameter("color") + return Color.BLACK + +## Controls the size of the fade-out effect at the edges of the visible area, creating a smooth transition between visible and fog areas. +@export var outer_margin_for_fade_out : float : + set(value): + if material_override: + material_override.set_shader_parameter("outer_margin_for_fade_out", value) + get: + if material_override: + return material_override.get_shader_parameter("outer_margin_for_fade_out") + return 0.0 + +@export_category("Debug Values") +## Shows small texture of the fog +@export var debug_texture_view : bool = false: + set(value): + if material_override: + material_override.set_shader_parameter("debug_texture_view", value) + get: + if material_override: + return material_override.get_shader_parameter("debug_texture_view") + return false + +## Controls the size of the debug texture view when enabled +@export_range(0, 1) var debug_texture_view_size : float = 0.2: + set(value): + if material_override: + material_override.set_shader_parameter("debug_texture_view_size", value) + get: + if material_override: + return material_override.get_shader_parameter("debug_texture_view_size") + return 0.2 + +# Private backing field to store the texture +var _fog_texture : ViewportTexture +var fog_texture : ViewportTexture: + set(value): + _fog_texture = value # Store in backing field + if material_override: # Check if material exists + material_override.set_shader_parameter("world_visibility_texture", value) + get: + return _fog_texture # Return from backing field + + +func _ready() -> void: + if vision_manager: + await vision_manager.ready + var fog_texture_result = vision_manager.get_fog_texture() + if fog_texture_result: + fog_texture = fog_texture_result + else: + push_warning("FogOfWarManager: 'vision_manager' is not assigned – fog texture cannot be applied.") + +func _apply_fog_texture_from_vision_manager() -> void: + if _vision_manager == null: + return + var fog_texture_result = _vision_manager.get_fog_texture() + if fog_texture_result: + fog_texture = fog_texture_result diff --git a/addons/rts_framework/features/vision/fog_of_war_manager.gd.uid b/addons/rts_framework/features/vision/fog_of_war.gd.uid similarity index 100% rename from addons/rts_framework/features/vision/fog_of_war_manager.gd.uid rename to addons/rts_framework/features/vision/fog_of_war.gd.uid diff --git a/addons/rts_framework/features/vision/fog_of_war.tscn b/addons/rts_framework/features/vision/fog_of_war.tscn new file mode 100644 index 0000000..261ebf5 --- /dev/null +++ b/addons/rts_framework/features/vision/fog_of_war.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=6 format=3 uid="uid://71g6dowvqb5i"] + +[ext_resource type="Script" uid="uid://de87xn1b8u07i" path="res://addons/rts_framework/features/vision/fog_of_war.gd" id="1_vrsar"] +[ext_resource type="Shader" uid="uid://cbo2vdgrqc60a" path="res://addons/rts_framework/features/vision/detailed_fog_of_war.gdshader" id="2_oehrr"] + +[sub_resource type="ViewportTexture" id="ViewportTexture_m5cnj"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_imaik"] +resource_local_to_scene = true +render_priority = 2 +shader = ExtResource("2_oehrr") +shader_parameter/color = Color(0, 0, 0, 1) +shader_parameter/world_visibility_texture = SubResource("ViewportTexture_m5cnj") +shader_parameter/texture_units_per_world_unit = 2 +shader_parameter/outer_margin_for_fade_out = 0.0 +shader_parameter/debug_texture_view = false +shader_parameter/debug_texture_view_size = 0.2 + +[sub_resource type="QuadMesh" id="QuadMesh_3f4cn"] +flip_faces = true +size = Vector2(2, 2) + +[node name="FogOfWar" type="MeshInstance3D"] +material_override = SubResource("ShaderMaterial_imaik") +cast_shadow = 0 +extra_cull_margin = 16384.0 +mesh = SubResource("QuadMesh_3f4cn") +script = ExtResource("1_vrsar") diff --git a/addons/rts_framework/features/vision/fog_of_war_manager.gdshader b/addons/rts_framework/features/vision/fog_of_war_manager.gdshader deleted file mode 100644 index 12ba7d9..0000000 --- a/addons/rts_framework/features/vision/fog_of_war_manager.gdshader +++ /dev/null @@ -1,28 +0,0 @@ -shader_type canvas_item; - -uniform float blurr_factor : hint_range(0.0, 10.0) = 2.0; -uniform bool remove_alpha = true; -uniform bool overlay = true; - -uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; - -void fragment() -{ - vec4 blurred_color; - if (overlay) - { - blurred_color = textureLod(screen_texture, SCREEN_UV, blurr_factor); - } - else - { - blurred_color = textureLod(TEXTURE, UV, blurr_factor); - } - if (remove_alpha) - { - COLOR.rgb = blurred_color.rgb; - } - else - { - COLOR = blurred_color; - } -} diff --git a/addons/rts_framework/features/vision/fog_of_war_manager.gdshader.uid b/addons/rts_framework/features/vision/fog_of_war_manager.gdshader.uid deleted file mode 100644 index e7a5914..0000000 --- a/addons/rts_framework/features/vision/fog_of_war_manager.gdshader.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cx0yukamm8x8x diff --git a/addons/rts_framework/features/vision/minimap.gd b/addons/rts_framework/features/vision/minimap.gd index 937cf2a..d1411f3 100644 --- a/addons/rts_framework/features/vision/minimap.gd +++ b/addons/rts_framework/features/vision/minimap.gd @@ -8,9 +8,9 @@ class_name Minimap const DynamicCircle2D : PackedScene = preload("res://addons/rts_framework/features/vision/dynamic_circle_2d.tscn") -@export var fog_of_war_manager : FogOfWarManager +@export var vision_manager : VisionManager -var _unit_to_circles_mapping : Dictionary = {} +var _vision_data : Dictionary ## Texture representing the fog of war alpha layer ## @@ -35,37 +35,39 @@ var fog_texture : Texture2D : # fog_texture multiply the images above. This to f @onready var _minimap_viewport: SubViewport = find_child("CombinedViewport") as SubViewport func _ready() -> void: - assert(fog_of_war_manager != null, "Minimap missing fog of war manager node. Minimap Node Name: " + self.name) - var fog_of_war_combined_viewport = fog_of_war_manager.combined_viewport - if fog_of_war_combined_viewport: - var fog_texture_result = fog_of_war_manager.combined_viewport.get_texture() - if fog_texture_result: - fog_texture = fog_texture_result - else: - push_error("Failed to retrieve fog of war texture. Minimap Node Name: " + self.name) + if vision_manager == null: + push_error("Minimap missing VisionManager – minimap will be disabled. Minimap Node Name: " + self.name) + set_physics_process(false) + set_process(false) + return + var fog_texture_result = vision_manager.get_fog_texture() + if fog_texture_result: + fog_texture = fog_texture_result else: - push_error("Failed to retrieve fog of war combined viewport. Minimap Node Name: " + self.name) + push_error("Failed to retrieve fog of war texture. Minimap Node Name: " + self.name) + _vision_data = vision_manager.get_vision_data() + if _vision_data == null: + push_error("VisionManager returned null vision data. Minimap Node Name: " + self.name) + set_physics_process(false) # Disable physics processing to prevent runtime errors + return # Abort further initialisation – minimap cannot function without data + if not Engine.is_editor_hint(): var circle = find_child("EditorOnlyCircle") if circle: circle.queue_free() func _physics_process(_delta : float) -> void: - var visible_units : Dictionary = {} - var units_to_sync = fog_of_war_manager.get_visible_units() - for unit in units_to_sync: + for unit in _vision_data.keys(): if not unit.is_revealing(): continue - visible_units[unit] = 1 - if not _unit_is_mapped(unit): + if not _unit_is_minimap_mapped(unit): _map_unit_to_new_circles_body(unit) - _sync_vision_to_unit(unit) - for mapped_unit in _unit_to_circles_mapping: - if not mapped_unit in visible_units: - _cleanup_mapping(mapped_unit) -func _unit_is_mapped(unit : BaseEntity) -> bool: - return unit in _unit_to_circles_mapping +func _unit_is_minimap_mapped(unit : BaseEntity) -> bool: + if _vision_data != null: + if unit in _vision_data: + return _vision_data[unit].minimap_circle != null # If unit has minimap_circle + return false ## Creates visibility representation for a unit on the minimap ## Parameters: @@ -73,32 +75,33 @@ func _unit_is_mapped(unit : BaseEntity) -> bool: ## - default_color: The default color of the visibility circle ## - default_radius: The default radius of visibility func _map_unit_to_new_circles_body(unit : BaseEntity, default_color : Color = Color.BLUE, default_radius : int = 5) -> void: - var minimap_circle = DynamicCircle2D.instantiate() # Make a white circle 2D - - if unit.has_method("get_team_color"): # If unit has get_team_color, use that color - minimap_circle.color = unit.get_team_color() - else: # if doesn't have the function, use the default one - minimap_circle.color = default_color # Set color - - if unit.has_method("get_sight_range"): # Set circle size to world units and unit sight range - minimap_circle.radius = unit.get_sight_range() # If has sight range, then use it - else: - minimap_circle.radius = default_radius # If doesn't have sight range, then use default + if _vision_data != null: + var minimap_circle = DynamicCircle2D.instantiate() # Make a white circle 2D - _minimap_viewport.add_child(minimap_circle) # Add the view circle 2D to fog of war viewport. In the fog of war viewport it create an image for the fog of war. - _unit_to_circles_mapping[unit] = minimap_circle # Keep map to connect unit with fog of war view - -func _sync_vision_to_unit(unit : BaseEntity) -> void: - var unit_pos_3d = unit.global_transform.origin - var unit_pos_2d = Vector2(unit_pos_3d.x, unit_pos_3d.z) * fog_of_war_manager.texture_units_per_world_unit - _unit_to_circles_mapping[unit].position = unit_pos_2d - -func _cleanup_mapping(unit : BaseEntity) -> void: - _unit_to_circles_mapping[unit].queue_free() - _unit_to_circles_mapping.erase(unit) + if unit.has_method("get_team_color"): # If unit has get_team_color, use that color + minimap_circle.color = unit.get_team_color() + else: # if doesn't have the function, use the default one + minimap_circle.color = default_color # Set color + + if unit.has_method("get_sight_range"): # Set circle size to world units and unit sight range + minimap_circle.radius = unit.get_sight_range() # If has sight range, then use it + else: + minimap_circle.radius = default_radius # If doesn't have sight range, then use default + + var did_draw : bool = draw_node_on_minimap(minimap_circle) # Add the view circle 2D to fog of war viewport. In the fog of war viewport it create an image for the fog of war. + + if did_draw: + _vision_data[unit].minimap_circle = minimap_circle # Keep map to connect unit with fog of war view + # place at the correct coordinates straight away + _vision_data[unit].sync_position(vision_manager.texture_units_per_world_unit) + else: + minimap_circle.queue_free() -func _exit_tree() -> void: - # Clean up all remaining circles - for unit in _unit_to_circles_mapping.keys(): - _cleanup_mapping(unit) - _unit_to_circles_mapping.clear() +## Addes draw_node(:CanvasItem) in way to draw it over other (preview) things in the minimap +## +## Return true on success, false on fail +func draw_node_on_minimap(draw_node : CanvasItem) -> bool: + if _minimap_viewport: + _minimap_viewport.add_child(draw_node) + return true + return false diff --git a/addons/rts_framework/features/vision/unit_vision_data.gd b/addons/rts_framework/features/vision/unit_vision_data.gd new file mode 100644 index 0000000..48da065 --- /dev/null +++ b/addons/rts_framework/features/vision/unit_vision_data.gd @@ -0,0 +1,154 @@ +extends Resource +class_name UnitVisionData + +const EMPTY_RADIUS : float = -1 +const EMPTY_COLOR : Color = Color.TRANSPARENT +const EMPTY_VECTOR3 : Vector3 = Vector3.INF +const EMPTY_VECTOR2 : Vector2 = Vector2.INF + +var unit : BaseEntity = null +var fow_circle : Node2D = null +var shroud_circle : Node2D = null +var minimap_circle : Node2D = null +var shape_3d : CollisionShape3D = null + +# fow_circle +var fow_circle_radius : float : + get: + if fow_circle == null: + return EMPTY_RADIUS + return fow_circle.radius + set(value): + if fow_circle == null: + return + fow_circle.radius = value + emit_changed() + +var fow_circle_color : Color : + get: + if fow_circle == null: + return EMPTY_COLOR + return fow_circle.color + set(value): + if fow_circle == null: + return + fow_circle.color = value + emit_changed() + +# shroud_circle +var shroud_circle_radius : float : + get: + if shroud_circle == null: + return EMPTY_RADIUS + return shroud_circle.radius + set(value): + if shroud_circle == null: + return + shroud_circle.radius = value + emit_changed() + +var shroud_circle_color : Color : + get: + if shroud_circle == null: + return EMPTY_COLOR + return shroud_circle.color + set(value): + if shroud_circle == null: + return + shroud_circle.color = value + emit_changed() + +# minimap_circle +var minimap_circle_radius : float : + get: + if minimap_circle == null: + return EMPTY_RADIUS + return minimap_circle.radius + set(value): + if minimap_circle == null: + return + minimap_circle.radius = value + emit_changed() + +var minimap_circle_color : Color : + get: + if minimap_circle == null: + return EMPTY_COLOR + return minimap_circle.color + set(value): + if minimap_circle == null: + return + minimap_circle.color = value + emit_changed() + +# shape_3d +var sight_range : float : + get: + if shape_3d == null || shape_3d.shape == null: + return EMPTY_RADIUS + return shape_3d.shape.radius + set(value): + if shape_3d == null || shape_3d.shape == null: + return + shape_3d.shape.radius = value + emit_changed() + +# All +var position : Vector3 : + get: + if shape_3d: + return shape_3d.position + return EMPTY_VECTOR3 + set(value): # Prevent setting + return + +static func create(unit : BaseEntity, fow_circle : Node2D, shroud_circle : Node2D, shape_3d : CollisionShape3D , minimap_circle : Node2D = null) -> UnitVisionData: + var instance = UnitVisionData.new() + instance.unit = unit + instance.fow_circle = fow_circle + instance.shroud_circle = shroud_circle + instance.shape_3d = shape_3d + if minimap_circle != null: + instance.minimap_circle = minimap_circle + return instance + +# TODO: Talk about having a RefCounted in the unit itself and +# in vision manager have weakref. +# That way, when unit is freed, it will automateclly free this resource +func _notification(what): + if what == NOTIFICATION_PREDELETE: + if fow_circle: + fow_circle.queue_free() + if shroud_circle: + shroud_circle.queue_free() + if minimap_circle: + minimap_circle.queue_free() + if shape_3d: + shape_3d.queue_free() + +func sync_position(texture_units_per_world_unit : int) -> void: + var unit_pos_3d = unit.global_transform.origin + var unit_pos_2d = Vector2(unit_pos_3d.x, unit_pos_3d.z) * texture_units_per_world_unit + + # Check if any component exists to determine if we need to emit a change + var should_emit : bool = false + + if fow_circle: + should_emit = should_emit_sync_position_helper(should_emit, fow_circle.position, unit_pos_2d) + fow_circle.position = unit_pos_2d + if shroud_circle: + should_emit = should_emit_sync_position_helper(should_emit, shroud_circle.position, unit_pos_2d) + shroud_circle.position = unit_pos_2d + if minimap_circle: + should_emit = should_emit_sync_position_helper(should_emit, minimap_circle.position, unit_pos_2d) + minimap_circle.position = unit_pos_2d + if shape_3d: + should_emit = should_emit_sync_position_helper(should_emit, shape_3d.position, unit_pos_3d) + shape_3d.position = unit_pos_3d + if should_emit: + emit_changed() + +func should_emit_sync_position_helper(should_emit : bool, preview, current) -> bool: + if !should_emit: + return preview != current + return true diff --git a/addons/rts_framework/features/vision/unit_vision_data.gd.uid b/addons/rts_framework/features/vision/unit_vision_data.gd.uid new file mode 100644 index 0000000..b4c6215 --- /dev/null +++ b/addons/rts_framework/features/vision/unit_vision_data.gd.uid @@ -0,0 +1 @@ +uid://drtk5p4vt4s5s diff --git a/addons/rts_framework/features/vision/fog_of_war_manager.gd b/addons/rts_framework/features/vision/vision_manager.gd similarity index 63% rename from addons/rts_framework/features/vision/fog_of_war_manager.gd rename to addons/rts_framework/features/vision/vision_manager.gd index 16e4b07..f79eb7b 100644 --- a/addons/rts_framework/features/vision/fog_of_war_manager.gd +++ b/addons/rts_framework/features/vision/vision_manager.gd @@ -1,7 +1,6 @@ @tool -@icon("res://addons/rts_framework/features/vision/fog_of_war_icon.svg") extends BaseManager -class_name FogOfWarManager +class_name VisionManager const DynamicCircle2D : PackedScene = preload("res://addons/rts_framework/features/vision/dynamic_circle_2d.tscn") @@ -9,17 +8,21 @@ const VisibilityShape3D : PackedScene = preload("res://addons/rts_framework/fea const DEFAULT_SIZE : Vector2i = Vector2i(100, 100) -## Color of area the units saw and not seeing currenty. Can see in Debug Texture View -@export var fog_circle_color : Color = Color(0.25, 0.25, 0.25) +## Color of area the units saw and not seeing currently. Can see in Debug Texture View +@export var fog_circle_color : Color = Color(1.0, 1.0, 1.0) ## Color of area the units see around them. Can see in Debug Texture View -@export var shroud_circle_color : Color = Color(1.0, 1.0, 1.0) +@export var shroud_circle_color : Color = Color(0.25, 0.25, 0.25) @export_category("Fog Values") ## The size of a single pixel in the 3D world -@export_range(1, 10000, 1,"suffix:px/length") var texture_units_per_world_unit : int = 2 : # px/length +@export_range(1, 10000, 1,"suffix:px/length") var texture_units_per_world_unit : int : # px/length set(value): - find_child("ScreenOverlay").material_override.set_shader_parameter("texture_units_per_world_unit", value) - texture_units_per_world_unit = value + _texture_units_per_world_unit = value + get: + return _texture_units_per_world_unit +# private backing store +var _texture_units_per_world_unit: int = 2 + ## AAA @export var map_mesh_node : MeshInstance3D: set(value): @@ -38,38 +41,14 @@ const DEFAULT_SIZE : Vector2i = Vector2i(100, 100) if map_mesh_node.mesh: return map_mesh_node.mesh.size return fog_size -## Color of the generated fog -@export var fog_color : Color : - set(value): - find_child("ScreenOverlay").material_override.set_shader_parameter("color", value) - get: - return find_child("ScreenOverlay").material_override.get_shader_parameter("color") -## TODO add description for outer_margin_for_fade_out. -@export var outer_margin_for_fade_out : float : - set(value): - find_child("ScreenOverlay").material_override.set_shader_parameter("outer_margin_for_fade_out", value) - get: - return find_child("ScreenOverlay").material_override.get_shader_parameter("outer_margin_for_fade_out") @export_category("Debug Values") -## Revels the whole fog. -@export var revel_fog : bool = false: +## Reveals the whole fog. +@export var reveal_fog : bool = false: set(value): find_child("Revealer").set_visible(value) get: return find_child("Revealer").is_visible() -## Shows small texture of the fog -@export var debug_texture_view : bool = false: - set(value): - find_child("ScreenOverlay").material_override.set_shader_parameter("debug_texture_view", value) - get: - return find_child("ScreenOverlay").material_override.get_shader_parameter("debug_texture_view") -## Shows small texture of the fog -@export_range(0, 1) var debug_texture_view_size : float = 0.2: - set(value): - find_child("ScreenOverlay").material_override.set_shader_parameter("debug_texture_view_size", value) - get: - return find_child("ScreenOverlay").material_override.get_shader_parameter("debug_texture_view_size") @export_group("Editor Only Circle") ## TODO: Add description @@ -91,15 +70,12 @@ const DEFAULT_SIZE : Vector2i = Vector2i(100, 100) get: return find_child("EditorOnlyCircle").position -#TODO Think if to merge both Dictionary or not -var _unit_to_circles_mapping : Dictionary = {} -var _unit_to_shape_3d_mapping : Dictionary = {} +var _unit_to_vision_data : Dictionary = {} # Dictionary @onready var _revealer : ColorRect = find_child("Revealer") @onready var _fog_viewport : SubViewport = find_child("FogViewport") @onready var _fog_viewport_container : SubViewportContainer = find_child("FogViewportContainer") @onready var _combined_viewport : SubViewport = find_child("CombinedViewport") -@onready var _screen_overlay : MeshInstance3D = find_child("ScreenOverlay") @onready var _visibility_field : Area3D = find_child("VisibilityField") # No set function, to prevent setting from outside @@ -108,9 +84,6 @@ var combined_viewport : SubViewport : return _combined_viewport as SubViewport func _ready() -> void: - _screen_overlay.material_override.set_shader_parameter( - "texture_units_per_world_unit", texture_units_per_world_unit - ) if not Engine.is_editor_hint(): _revealer.hide() var circle = find_child("EditorOnlyCircle") @@ -128,10 +101,11 @@ func _physics_process(_delta : float) -> void: if not _unit_is_mapped(unit): _map_unit_to_new_circles_body(unit) _sync_vision_to_unit(unit) - for mapped_unit in _unit_to_circles_mapping: - if not mapped_unit in units_synced: + for mapped_unit in _unit_to_vision_data.keys(): + if (not mapped_unit in units_synced) or (not mapped_unit.is_revealing()): _cleanup_mapping(mapped_unit) else: + update_configuration_warnings() if map_mesh_node: # If there is a map mesh, then update in editor the size if map_mesh_node.mesh: fog_size = map_mesh_node.mesh.size @@ -148,40 +122,35 @@ func resize(map_size: Vector2) -> void: func _unit_is_mapped(unit : BaseEntity) -> bool: - return unit in _unit_to_circles_mapping + return unit in _unit_to_vision_data # Create a fog of war visibility circles and visibility shape 3d func _map_unit_to_new_circles_body(unit : BaseEntity) -> void: var effective_sight_range = unit.sight_range * texture_units_per_world_unit; + var shroud_circle = DynamicCircle2D.instantiate() # Make a white circle 2D - shroud_circle.color = fog_circle_color # Set color + shroud_circle.color = shroud_circle_color# Set color shroud_circle.radius = effective_sight_range # Set circle size to world units and unit sight range _fog_viewport.add_child(shroud_circle) # Add the view circle 2D to fog of war viewport. In the fog of war viewport it create an image for the fog of war. + var fow_circle = DynamicCircle2D.instantiate() - fow_circle.color = shroud_circle_color + fow_circle.color = fog_circle_color fow_circle.radius = effective_sight_range _fog_viewport_container.add_sibling(fow_circle) - _unit_to_circles_mapping[unit] = [shroud_circle, fow_circle] # Keep map to connect unit with fog of war view + var visibility_shape_3d = VisibilityShape3D.instantiate() # Make a cylinder visibility_shape_3d.shape.radius = effective_sight_range # Cylinder radius equal to sight range _visibility_field.add_child(visibility_shape_3d) # Add the shape to the VisibilityField(Area3D node type) - _unit_to_shape_3d_mapping[unit] = visibility_shape_3d # Map unit and shape + + _unit_to_vision_data[unit] = UnitVisionData.create(unit, fow_circle, shroud_circle, visibility_shape_3d) func _sync_vision_to_unit(unit : BaseEntity) -> void: - var unit_pos_3d = unit.global_transform.origin - var unit_pos_2d = Vector2(unit_pos_3d.x, unit_pos_3d.z) * texture_units_per_world_unit - _unit_to_circles_mapping[unit][0].position = unit_pos_2d - _unit_to_circles_mapping[unit][1].position = unit_pos_2d - _unit_to_shape_3d_mapping[unit].position = unit_pos_3d + _unit_to_vision_data[unit].sync_position(texture_units_per_world_unit) func _cleanup_mapping(unit : BaseEntity) -> void: - _unit_to_circles_mapping[unit][0].queue_free() - _unit_to_circles_mapping[unit][1].queue_free() - _unit_to_circles_mapping.erase(unit) - _unit_to_shape_3d_mapping[unit].queue_free() - _unit_to_shape_3d_mapping.erase(unit) + _unit_to_vision_data.erase(unit) func get_visible_units() -> Array[Node3D]: @@ -202,3 +171,18 @@ func _on_visibility_field_body_exited(body: Node3D) -> void: #print_debug("_on_visibility_field_body_exited") if body is UnitEntity: body.visible = false + +func get_vision_data() -> Dictionary: + return _unit_to_vision_data + + +func get_fog_texture() -> ViewportTexture: + var viewport = combined_viewport if combined_viewport else $CombinedViewport + var texture = viewport.get_texture() if viewport else null + return texture + +func _get_configuration_warnings() -> PackedStringArray: + var warnings : Array[String] = [] + if fog_size == DEFAULT_SIZE: + warnings.append("Fog of war size is default size") + return PackedStringArray(warnings) diff --git a/addons/rts_framework/features/vision/vision_manager.gd.uid b/addons/rts_framework/features/vision/vision_manager.gd.uid new file mode 100644 index 0000000..aebfb0b --- /dev/null +++ b/addons/rts_framework/features/vision/vision_manager.gd.uid @@ -0,0 +1 @@ +uid://cwlqpivbl7yqa diff --git a/addons/rts_framework/features/vision/fog_of_war_manager.tscn b/addons/rts_framework/features/vision/vision_manager.tscn similarity index 53% rename from addons/rts_framework/features/vision/fog_of_war_manager.tscn rename to addons/rts_framework/features/vision/vision_manager.tscn index f8ac39e..8ea795f 100644 --- a/addons/rts_framework/features/vision/fog_of_war_manager.tscn +++ b/addons/rts_framework/features/vision/vision_manager.tscn @@ -1,36 +1,49 @@ -[gd_scene load_steps=9 format=3 uid="uid://71g6dowvqb5i"] - -[ext_resource type="Script" uid="uid://de87xn1b8u07i" path="res://addons/rts_framework/features/vision/fog_of_war_manager.gd" id="1_vrsar"] -[ext_resource type="PackedScene" uid="uid://clbjgy724q2si" path="res://addons/rts_framework/features/vision/dynamic_circle_2d.tscn" id="2_g5u0g"] -[ext_resource type="Shader" uid="uid://cbo2vdgrqc60a" path="res://addons/rts_framework/features/vision/detailed_fog_of_war.gdshader" id="2_oehrr"] -[ext_resource type="Shader" uid="uid://cx0yukamm8x8x" path="res://addons/rts_framework/features/vision/fog_of_war_manager.gdshader" id="3_rx4ay"] - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_sr23m"] -shader = ExtResource("3_rx4ay") +[gd_scene load_steps=5 format=3 uid="uid://c58yspxllteqr"] + +[ext_resource type="Script" uid="uid://cwlqpivbl7yqa" path="res://addons/rts_framework/features/vision/vision_manager.gd" id="1_m01pg"] +[ext_resource type="PackedScene" uid="uid://clbjgy724q2si" path="res://addons/rts_framework/features/vision/dynamic_circle_2d.tscn" id="2_7q840"] + +[sub_resource type="Shader" id="Shader_w8nhr"] +code = "shader_type canvas_item; + +uniform float blurr_factor : hint_range(0.0, 10.0) = 2.0; +uniform bool remove_alpha = true; +uniform bool overlay = true; + +uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; + +void fragment() +{ + vec4 blurred_color; + if (overlay) + { + blurred_color = textureLod(screen_texture, SCREEN_UV, blurr_factor); + } + else + { + blurred_color = textureLod(TEXTURE, UV, blurr_factor); + } + if (remove_alpha) + { + COLOR.rgb = blurred_color.rgb; + } + else + { + COLOR = blurred_color; + } +} +" + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_jyswr"] +shader = SubResource("Shader_w8nhr") shader_parameter/blurr_factor = 1.0 shader_parameter/remove_alpha = false shader_parameter/overlay = true -[sub_resource type="ViewportTexture" id="ViewportTexture_m5cnj"] -viewport_path = NodePath("CombinedViewport") - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_imaik"] -resource_local_to_scene = true -render_priority = 2 -shader = ExtResource("2_oehrr") -shader_parameter/color = Color(0, 0, 0, 1) -shader_parameter/world_visibility_texture = SubResource("ViewportTexture_m5cnj") -shader_parameter/texture_units_per_world_unit = 2 -shader_parameter/outer_margin_for_fade_out = 0.0 -shader_parameter/debug_texture_view = false -shader_parameter/debug_texture_view_size = 0.2 - -[sub_resource type="QuadMesh" id="QuadMesh_3f4cn"] -flip_faces = true -size = Vector2(2, 2) - -[node name="FogOfWarManager" type="Node"] -script = ExtResource("1_vrsar") +[node name="VisionManager" type="Node"] +script = ExtResource("1_m01pg") +texture_units_per_world_unit = 2 +metadata/_custom_type_script = "uid://cg181bb316phb" [node name="CombinedViewport" type="SubViewport" parent="."] disable_3d = true @@ -61,7 +74,7 @@ size = Vector2i(500, 500) render_target_clear_mode = 2 render_target_update_mode = 4 -[node name="EditorOnlyCircle" parent="CombinedViewport" instance=ExtResource("2_g5u0g")] +[node name="EditorOnlyCircle" parent="CombinedViewport" instance=ExtResource("2_7q840")] position = Vector2(30, 30) radius = 25 @@ -76,7 +89,7 @@ grow_horizontal = 2 grow_vertical = 2 [node name="BlurrOverlay" type="ColorRect" parent="CombinedViewport"] -material = SubResource("ShaderMaterial_sr23m") +material = SubResource("ShaderMaterial_jyswr") anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -85,12 +98,6 @@ offset_bottom = -100.0 grow_horizontal = 2 grow_vertical = 2 -[node name="ScreenOverlay" type="MeshInstance3D" parent="."] -material_override = SubResource("ShaderMaterial_imaik") -cast_shadow = 0 -extra_cull_margin = 16384.0 -mesh = SubResource("QuadMesh_3f4cn") - [node name="VisibilityField" type="Area3D" parent="."] [connection signal="body_entered" from="VisibilityField" to="." method="_on_visibility_field_body_entered"] diff --git a/demo/game.tscn b/demo/game.tscn index 7c5f38f..4c9ab0b 100644 --- a/demo/game.tscn +++ b/demo/game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=18 format=3 uid="uid://c4by2q2g1r3em"] +[gd_scene load_steps=22 format=3 uid="uid://c4by2q2g1r3em"] [ext_resource type="Texture2D" uid="uid://cj6w0rv0tko5d" path="res://demo/assets/textures/green.png" id="1_0crr1"] [ext_resource type="Script" uid="uid://cuyhslhlbyrr2" path="res://demo/scripts/rts_controller.gd" id="2_yunhj"] @@ -9,7 +9,9 @@ [ext_resource type="PackedScene" uid="uid://bdjx0t2xkn5gg" path="res://addons/rts_framework/features/camera/camera_controller.tscn" id="6_3gdxu"] [ext_resource type="PackedScene" uid="uid://ctoeptkckw6xq" path="res://addons/rts_framework/features/selection/selection_box.tscn" id="6_x1kyj"] [ext_resource type="PackedScene" uid="uid://d1t1ag5bk5u53" path="res://demo/scenes/entities/units/soldier.tscn" id="7_p3hpf"] -[ext_resource type="PackedScene" uid="uid://71g6dowvqb5i" path="res://addons/rts_framework/features/vision/fog_of_war_manager.tscn" id="8_12bj3"] +[ext_resource type="PackedScene" uid="uid://71g6dowvqb5i" path="res://addons/rts_framework/features/vision/fog_of_war.tscn" id="8_12bj3"] +[ext_resource type="PackedScene" uid="uid://c58yspxllteqr" path="res://addons/rts_framework/features/vision/vision_manager.tscn" id="8_fqft5"] +[ext_resource type="Shader" uid="uid://cbo2vdgrqc60a" path="res://addons/rts_framework/features/vision/detailed_fog_of_war.gdshader" id="9_wlnpp"] [ext_resource type="PackedScene" uid="uid://bo2b1x0u1j1dg" path="res://addons/rts_framework/features/vision/minimap.tscn" id="11_hn7rm"] [sub_resource type="Environment" id="Environment_tavya"] @@ -32,7 +34,21 @@ size = Vector2(500, 500) [sub_resource type="BoxShape3D" id="BoxShape3D_m28nb"] size = Vector3(100, 0.1, 100) -[sub_resource type="ViewportTexture" id="ViewportTexture_wlnpp"] +[sub_resource type="ViewportTexture" id="ViewportTexture_fqft5"] +viewport_path = NodePath("CombinedViewport") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_ri1tv"] +resource_local_to_scene = true +render_priority = 2 +shader = ExtResource("9_wlnpp") +shader_parameter/color = Color(0, 0, 0, 1) +shader_parameter/world_visibility_texture = SubResource("ViewportTexture_fqft5") +shader_parameter/texture_units_per_world_unit = 2 +shader_parameter/outer_margin_for_fade_out = 0.0 +shader_parameter/debug_texture_view = false +shader_parameter/debug_texture_view_size = 0.0 + +[sub_resource type="ViewportTexture" id="ViewportTexture_ri1tv"] viewport_path = NodePath("CombinedViewport") [node name="Game" type="Node3D"] @@ -61,15 +77,18 @@ shape = SubResource("BoxShape3D_m28nb") [node name="Soldier" parent="Entities" instance=ExtResource("7_p3hpf")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.92926, 0.211501, 6.07202) +visible = true material = ExtResource("3_1d533") [node name="Soldier2" parent="Entities" instance=ExtResource("7_p3hpf")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.94615, 0.211501, 6.07202) +visible = true material = ExtResource("4_6ku06") team = 1 [node name="Soldier3" parent="Entities" groups=["revealed_units"] instance=ExtResource("7_p3hpf")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10.5574, 0.211501, 6.07202) +visible = true material = ExtResource("3_1d533") [node name="RTSController" type="Node" parent="."] @@ -82,8 +101,16 @@ selection_box = NodePath("../../SelectionBox") [node name="CommandManager" type="Node" parent="RTSController"] script = ExtResource("4_lcg0b") -[node name="FogOfWarManager" parent="RTSController" instance=ExtResource("8_12bj3")] -fog_size = Vector2i(300, 300) +[node name="VisionManager" parent="RTSController" node_paths=PackedStringArray("map_mesh_node") instance=ExtResource("8_fqft5")] +map_mesh_node = NodePath("../../Map/NavigationRegion3D/MeshInstance3D") +fog_size = Vector2i(500, 500) + +[node name="FogOfWar" parent="." node_paths=PackedStringArray("vision_manager") instance=ExtResource("8_12bj3")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.242398, 0, 0) +material_override = SubResource("ShaderMaterial_ri1tv") +skeleton = NodePath("../RTSController") +vision_manager = NodePath("../RTSController/VisionManager") +debug_texture_view_size = 0.0 [node name="CameraHolder" parent="." instance=ExtResource("6_3gdxu")] @@ -108,7 +135,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="Minimap" parent="HUD/Panel/CenterContainer" node_paths=PackedStringArray("fog_of_war_manager") instance=ExtResource("11_hn7rm")] +[node name="Minimap" parent="HUD/Panel/CenterContainer" node_paths=PackedStringArray("vision_manager") instance=ExtResource("11_hn7rm")] layout_mode = 2 -texture = SubResource("ViewportTexture_wlnpp") -fog_of_war_manager = NodePath("../../../../RTSController/FogOfWarManager") +texture = SubResource("ViewportTexture_ri1tv") +vision_manager = NodePath("../../../../RTSController/VisionManager")