Implements tree visibility occlusion system.

Adds tree fading based on camera line of sight.
Trees now fade out when they obstruct the player's view,
improving visibility.

Also includes several fixes:
- Fixes tree distribution.
- Adds a see-through shader.
- Adds camera and occlusion scripts.
This commit is contained in:
Dan Baker 2025-07-01 10:32:21 +01:00
parent a1efaf6294
commit f98773237e
10 changed files with 314 additions and 137 deletions

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=4 format=3 uid="uid://c27fogucecn0r"]
[gd_scene load_steps=5 format=3 uid="uid://c27fogucecn0r"]
[ext_resource type="Script" uid="uid://lcedx3lau6v5" path="res://Entities/Tree/scripts/tree.gd" id="1_702jv"]
@ -8,6 +8,9 @@ height = 0.5
[sub_resource type="SphereShape3D" id="SphereShape3D_702jv"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_702jv"]
radius = 0.6
[node name="Tree" type="Node3D"]
script = ExtResource("1_702jv")
@ -20,3 +23,15 @@ shape = SubResource("CapsuleShape3D_s6kdm")
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractRange"]
shape = SubResource("SphereShape3D_702jv")
[node name="VisibilityArea" type="StaticBody3D" parent="." groups=["tree_visibility"]]
collision_layer = 2
collision_mask = 2
[node name="VisibilityShape" type="CollisionShape3D" parent="VisibilityArea"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.616604, 0)
shape = SubResource("CapsuleShape3D_702jv")
debug_color = Color(0.837648, 0.364145, 0.346694, 0.42)
[node name="LastBlocked" type="Timer" parent="."]
wait_time = 0.5

View file

@ -1,20 +1,22 @@
class_name TreeNode
extends Node3D
@onready var area: Area3D = $InteractRange
var tree_data: TreeDataResource
var model_instance: Node3D
var mesh_instances: Array[MeshInstance3D] = []
var original_materials: Array[Material] = []
var outline_material: Material
var base_circle: MeshInstance3D
var circle_material: Material
var is_invisible: bool = false
var fade_tween: Tween
@onready var area: Area3D = $InteractRange
@onready var last_blocked: Timer = $LastBlocked
func _ready():
setup_outline_material()
area.body_entered.connect(_on_player_entered)
area.body_exited.connect(_on_player_exited)
last_blocked.timeout.connect(_on_fade_timer_timeout)
func set_tree_data(data: TreeDataResource):
tree_data = data
@ -36,16 +38,9 @@ func spawn_model():
tree_data.apply_seasonal_colors(model_instance, Season.current)
add_child(model_instance)
# Create base circle after model is loaded
if not base_circle:
create_base_circle()
# Re-scan for mesh instances in the new model
find_all_mesh_instances(model_instance)
update_outline_materials()
# Apply any additional properties from tree_data
apply_tree_properties()
@ -59,7 +54,7 @@ func apply_tree_properties():
# Apply random scale based on tree properties
if tree_data.max_height > 0:
var scale_variation = randf_range(0.8, 1.2)
var scale_variation = randf_range(1.5, 2)
model_instance.scale *= scale_variation
func setup_outline_material() -> void:
@ -68,67 +63,113 @@ func setup_outline_material() -> void:
outline_material.shader = preload("res://outline.gdshader")
outline_material.set_shader_parameter("color", Vector3(0.702, 0.557, 0.259))
func update_outline_materials():
if mesh_instances.is_empty():
print("Warning: No MeshInstance3D found in model!")
return
# Store original materials for the new model
original_materials.clear()
for mesh in mesh_instances:
if mesh.get_surface_override_material(0):
original_materials.append(mesh.get_surface_override_material(0))
else:
original_materials.append(mesh.get_surface_override_material(0))
func find_all_mesh_instances(node: Node):
if node is MeshInstance3D:
mesh_instances.append(node)
for child in node.get_children():
find_all_mesh_instances(child)
func create_base_circle():
base_circle = MeshInstance3D.new()
add_child(base_circle)
func fade_out_tree_simple(duration: float = 0.25):
# Reset the timer
last_blocked.stop()
last_blocked.start()
# Create a flat cylinder for the circle
var cylinder = CylinderMesh.new()
cylinder.top_radius = 0.4 # Adjust size as needed
cylinder.bottom_radius = 0.4
cylinder.height = 1 # Very thin to make it look like a flat circle
cylinder.rings = 1
cylinder.radial_segments = 9 # More segments = smoother circle
base_circle.mesh = cylinder
if is_invisible:
return
# Create transparent material with outline
circle_material = StandardMaterial3D.new()
circle_material.flags_transparent = true
circle_material.albedo_color = Color(1.0, 0.843, 0.0, 0.0) # Fully transparent gold
is_invisible = true
# Add rim lighting effect for outline appearance
circle_material.rim_enabled = true
circle_material.rim = 1.0
circle_material.rim_tint = 1.0
circle_material.rim_color = Color(1.0, 0.843, 0.0) # Gold rim
base_circle.material_override = circle_material
# Kill any existing fade tween
if fade_tween:
fade_tween.kill()
base_circle.position.y = 1 # Slightly above ground to avoid z-fighting
for mesh_instance in mesh_instances:
fade_mesh_materials(mesh_instance, 1.0, 0.1, duration)
# Start hidden
base_circle.visible = true
# This gets called when the timer times out
func _on_fade_timer_timeout():
fade_in_tree_immediate()
func fade_in_tree_immediate(duration: float = 0.25):
if not is_invisible:
return
# Kill any existing fade tween
if fade_tween:
fade_tween.kill()
for mesh_instance in mesh_instances:
fade_mesh_materials(mesh_instance, 0.1, 1.0, duration)
is_invisible = false
func fade_mesh_materials(mesh_instance: MeshInstance3D, from_alpha: float, to_alpha: float, duration: float):
if not mesh_instance.mesh:
return
var surface_count = mesh_instance.mesh.get_surface_count()
for surface_id in range(surface_count):
var material = mesh_instance.get_surface_override_material(surface_id)
# Always ensure we have a unique material instance
if not material:
# No override material - duplicate from base material
var base_material = mesh_instance.mesh.surface_get_material(surface_id)
if base_material:
material = base_material.duplicate()
mesh_instance.set_surface_override_material(surface_id, material)
else:
# Override material exists - duplicate it to ensure uniqueness
material = material.duplicate()
mesh_instance.set_surface_override_material(surface_id, material)
# Only work with StandardMaterial3D
if material and material is StandardMaterial3D:
var std_mat = material as StandardMaterial3D
# Set transparency mode based on target alpha
if to_alpha < 1.0:
std_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
# Force shadow casting
mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON
# Set initial alpha
std_mat.albedo_color.a = from_alpha
# Create and store the tween
fade_tween = create_tween()
fade_tween.tween_method(
func(alpha: float):
std_mat.albedo_color.a = alpha,
from_alpha, to_alpha, duration
)
# When fading to full opacity, reset transparency mode
if to_alpha >= 1.0:
fade_tween.tween_callback(
func(): std_mat.transparency = BaseMaterial3D.TRANSPARENCY_DISABLED
)
func toggle_tree_visibility(duration: float = 1.0):
if is_invisible:
fade_in_tree_immediate(duration)
else:
fade_out_tree_simple(duration)
func _on_player_entered(body: Node3D):
if body.is_in_group("player"):
base_circle.visible = true
# Apply outline to all mesh instances
for mesh in mesh_instances:
mesh.material_overlay = outline_material
if not is_invisible:
for mesh in mesh_instances:
mesh.material_overlay = outline_material
func _on_player_exited(body: Node3D):
if body.is_in_group("player"):
Log.pr('Out of range...')
base_circle.visible = false
# Remove outline from all mesh instances
for mesh in mesh_instances:
mesh.material_overlay = null
if not is_invisible:
# Reset the material overlay to null
for mesh in mesh_instances:
mesh.material_overlay = null

View file

@ -44,7 +44,7 @@ static func calculate_tree_distribution(tree_preferences: Dictionary, density: f
return distribution
# Calculate total number of trees for this cell
var total_trees = int((density / 2) * 10) + randi_range(1, 3)
var total_trees = int((density / 5) * 10) + randi_range(1, 2)
#var total_trees = int((density - 0.6) * 25) + randi_range(3, 8)
# Normalize the chances to get proportions

View file

@ -64,6 +64,10 @@ jump={
]
}
[layer_names]
3d_physics/layer_4="TreeVisibility"
[network]
limits/debugger/max_chars_per_second=1000000

41
see_through.gdshader Normal file
View file

@ -0,0 +1,41 @@
shader_type spatial;
// Uniforms that can be controlled from GDScript
uniform float model_height : hint_range(0.1, 100.0) = 10.0;
uniform float transition_width : hint_range(0.01, 0.5) = 0.1;
uniform vec4 base_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float effect_strength : hint_range(0.0, 1.0) = 1.0;
uniform sampler2D main_texture : source_color;
uniform float metallic : hint_range(0.0, 1.0) = 0.0;
uniform float roughness : hint_range(0.0, 1.0) = 0.5;
varying vec3 world_position;
void vertex() {
world_position = VERTEX;
}
void fragment() {
// Get the base texture color
vec4 tex_color = texture(main_texture, UV);
// Calculate normalized Y position (0.0 at bottom, 1.0 at top)
// This uses the local vertex position - adjust based on your model's bounds
float normalized_y = (world_position.y + model_height * 0.5) / model_height;
// Define fade boundaries - top 80% invisible, bottom 20% visible
float fade_start = 0.2; // Start fading at 20% from bottom
float fade_end = fade_start - transition_width;
// Calculate position-based alpha
float position_alpha = smoothstep(fade_end, fade_start, normalized_y);
// Blend between full visibility and position-based alpha based on effect strength
float final_alpha = mix(1.0, position_alpha, effect_strength);
// Set material properties
ALBEDO = base_color.rgb * tex_color.rgb;
ALPHA = base_color.a * tex_color.a * final_alpha;
METALLIC = metallic;
ROUGHNESS = roughness;
}

1
see_through.gdshader.uid Normal file
View file

@ -0,0 +1 @@
uid://djv8r207ldqn1

View file

@ -1,10 +1,12 @@
[gd_scene load_steps=53 format=4 uid="uid://bwsugg4p50fjr"]
[gd_scene load_steps=55 format=4 uid="uid://bwsugg4p50fjr"]
[ext_resource type="Environment" uid="uid://cm77bbr0io118" path="res://Stages/Test3D/new_environment.tres" id="1_8ph61"]
[ext_resource type="Script" uid="uid://bwed2dwogfmxv" path="res://Entities/Player/scripts/player.gd" id="1_d602n"]
[ext_resource type="AnimationLibrary" uid="uid://bwnn7vpd0dqds" path="res://Common/animations/basic-movement.res" id="1_tfa5t"]
[ext_resource type="Script" uid="uid://bbjv6a7yg7m02" path="res://Stages/Test3D/camera_pivot.gd" id="2_sdmks"]
[ext_resource type="Shader" uid="uid://bsemnmdracd4m" path="res://Common/shaders/outline.gdshader" id="4_feu7y"]
[ext_resource type="Script" uid="uid://cjbk6jnxla4mn" path="res://Stages/Test3D/camera_3d.gd" id="5_fm6lr"]
[ext_resource type="PackedScene" uid="uid://isogcpkb8su4" path="res://Entities/Tree/assets/temp/tent_detailedOpen.glb" id="9_4jb6r"]
[ext_resource type="Script" uid="uid://bjco8musjqog4" path="res://Stages/Test3D/particles.gd" id="9_oiyue"]
[ext_resource type="PackedScene" uid="uid://cdbnr0jg2icaj" path="res://Entities/Tree/assets/temp/campfire_bricks.glb" id="13_qasnx"]
[ext_resource type="PackedScene" uid="uid://c4ovjmemnepdy" path="res://Entities/Tree/assets/temp/campfire_logs.glb" id="14_2xm50"]
@ -372,42 +374,6 @@ size = Vector2(2, 2)
[sub_resource type="BoxShape3D" id="BoxShape3D_tfa5t"]
size = Vector3(60, 0, 20)
[sub_resource type="Curve" id="Curve_5r2bu"]
_data = [Vector2(0.003125, 0.0237797), 0.0, 0.0, 0, 0, Vector2(0.21875, 0.877972), 0.0, 0.0, 0, 0, Vector2(0.41875, 0.194618), 0.0, 0.0, 0, 0, Vector2(0.478125, 0.576971), -7.27116, -7.27116, 0, 0, Vector2(0.621875, 0.227159), 0.0, 0.0, 0, 0, Vector2(0.7625, 0.365457), 0.0, 0.0, 0, 0, Vector2(1, 0.04005), 0.0, 0.0, 0, 0]
point_count = 7
[sub_resource type="CurveTexture" id="CurveTexture_23r73"]
curve = SubResource("Curve_5r2bu")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_p5fn2"]
emission_shape = 6
emission_ring_axis = Vector3(0, 1, 0)
emission_ring_height = 1.0
emission_ring_radius = 5.0
emission_ring_inner_radius = 0.0
emission_ring_cone_angle = 90.0
direction = Vector3(0.2, -1, 0)
spread = 5.0
initial_velocity_max = 10.0
scale_min = 0.1
alpha_curve = SubResource("CurveTexture_23r73")
collision_mode = 2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p5fn2"]
transparency = 1
cull_mode = 2
shading_mode = 0
vertex_color_use_as_albedo = true
albedo_color = Color(1, 1, 1, 0.807843)
albedo_texture = ExtResource("23_23r73")
use_particle_trails = true
[sub_resource type="RibbonTrailMesh" id="RibbonTrailMesh_5r2bu"]
material = SubResource("StandardMaterial3D_p5fn2")
size = 0.025
sections = 2
section_segments = 1
[sub_resource type="Curve" id="Curve_ukp6m"]
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.515625, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 3
@ -450,6 +416,42 @@ material = SubResource("StandardMaterial3D_hvb1l")
size = Vector2(0.2, 0.2)
orientation = 0
[sub_resource type="Curve" id="Curve_5r2bu"]
_data = [Vector2(0.003125, 0.0237797), 0.0, 0.0, 0, 0, Vector2(0.21875, 0.877972), 0.0, 0.0, 0, 0, Vector2(0.41875, 0.194618), 0.0, 0.0, 0, 0, Vector2(0.478125, 0.576971), -7.27116, -7.27116, 0, 0, Vector2(0.621875, 0.227159), 0.0, 0.0, 0, 0, Vector2(0.7625, 0.365457), 0.0, 0.0, 0, 0, Vector2(1, 0.04005), 0.0, 0.0, 0, 0]
point_count = 7
[sub_resource type="CurveTexture" id="CurveTexture_23r73"]
curve = SubResource("Curve_5r2bu")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_p5fn2"]
emission_shape = 6
emission_ring_axis = Vector3(0, 1, 0)
emission_ring_height = 1.0
emission_ring_radius = 5.0
emission_ring_inner_radius = 0.0
emission_ring_cone_angle = 90.0
direction = Vector3(0.2, -1, 0)
spread = 5.0
initial_velocity_max = 10.0
scale_min = 0.1
alpha_curve = SubResource("CurveTexture_23r73")
collision_mode = 2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p5fn2"]
transparency = 1
cull_mode = 2
shading_mode = 0
vertex_color_use_as_albedo = true
albedo_color = Color(1, 1, 1, 0.807843)
albedo_texture = ExtResource("23_23r73")
use_particle_trails = true
[sub_resource type="RibbonTrailMesh" id="RibbonTrailMesh_5r2bu"]
material = SubResource("StandardMaterial3D_p5fn2")
size = 0.025
sections = 2
section_segments = 1
[node name="Test3d" type="Node3D"]
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
@ -748,9 +750,10 @@ script = ExtResource("2_sdmks")
transform = Transform3D(1, 0, 0, 0, 1, 1.78814e-07, 0, -3.27826e-07, 1, 0, 0, 7)
projection = 1
current = true
size = 3.0
size = 4.0
near = 0.001
far = 100.0
script = ExtResource("5_fm6lr")
[node name="PostProcessing" type="MeshInstance3D" parent="SubViewportContainer/SubViewport/Player/CameraPivot/Camera3D"]
unique_name_in_owner = true
@ -759,6 +762,9 @@ visible = false
extra_cull_margin = 16384.0
mesh = SubResource("QuadMesh_tfa5t")
[node name="RayCast3D" type="RayCast3D" parent="SubViewportContainer/SubViewport/Player/CameraPivot/Camera3D"]
collision_mask = 3
[node name="Environment" type="Node3D" parent="SubViewportContainer/SubViewport"]
[node name="Ground" type="StaticBody3D" parent="SubViewportContainer/SubViewport/Environment"]
@ -781,17 +787,49 @@ unique_name_in_owner = true
[node name="Objects" type="Node3D" parent="SubViewportContainer/SubViewport/Camp"]
[node name="tent-canvas2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("23_5r2bu")]
transform = Transform3D(0.964438, 0, -0.264311, 0, 1, 0, 0.264311, 0, 0.964438, -1.40178, 0, -1.67128)
[node name="Camp" type="Node3D" parent="SubViewportContainer/SubViewport/Camp/Objects"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.718493, 0, -0.353187)
[node name="campfire_bricks2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("13_qasnx")]
[node name="tent_detailedOpen2" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp" instance=ExtResource("9_4jb6r")]
transform = Transform3D(-0.993544, 0, 0.113446, 0, 1, 0, -0.113446, 0, -0.993544, -0.933611, 0.05, -1.63291)
[node name="tent-canvas2" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp" instance=ExtResource("23_5r2bu")]
transform = Transform3D(0.964438, 0, -0.264311, 0, 1, 0, 0.264311, 0, 0.964438, -1.40178, 0, -1.67128)
visible = false
[node name="Campfire" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp" instance=ExtResource("13_qasnx")]
transform = Transform3D(0.7, 0, 0, 0, 0.7, 0, 0, 0, 0.7, -0.93138, 0.05, -0.326702)
[node name="campfire_logs2" parent="SubViewportContainer/SubViewport/Camp/Objects/campfire_bricks2" instance=ExtResource("14_2xm50")]
[node name="campfire_logs2" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp/Campfire" instance=ExtResource("14_2xm50")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0053246, 0, 0.0117908)
[node name="log2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("15_e0hgm")]
transform = Transform3D(0.440003, 0, 0.237482, 0, 0.5, 0, -0.237482, 0, 0.440003, -0.565749, 0.0532002, -0.598678)
[node name="Fire" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp/Campfire"]
transform = Transform3D(1.42857, 0, 0, 0, 1.42857, 0, 0, 0, 1.42857, 0, 0, 0)
amount = 50
lifetime = 0.4
speed_scale = 0.4
process_material = SubResource("ParticleProcessMaterial_xvexm")
draw_pass_1 = SubResource("QuadMesh_hvb1l")
[node name="OmniLight3D" type="OmniLight3D" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp/Campfire/Fire"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000509977, 0.121094, -0.00151992)
light_color = Color(0.89, 0.461613, 0.2136, 1)
light_energy = 0.923321
light_indirect_energy = 1.084
light_volumetric_fog_energy = 3.764
light_size = 0.105
shadow_enabled = true
distance_fade_enabled = true
distance_fade_begin = 386.01
distance_fade_shadow = 45.9
distance_fade_length = 28.05
script = ExtResource("22_ukp6m")
[node name="log2" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp" instance=ExtResource("15_e0hgm")]
transform = Transform3D(0.440003, 0, 0.237482, 0, 0.5, 0, -0.237482, 0, 0.440003, -0.566, 0.006, -0.599)
[node name="bed_floor2" parent="SubViewportContainer/SubViewport/Camp/Objects/Camp" instance=ExtResource("15_pbfwi")]
transform = Transform3D(0.72094, 0, -0.346764, 0, 0.8, 0, 0.346764, 0, 0.72094, -1.4942, 0.00999988, -0.784986)
[node name="statue_column2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("16_ynokf")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.654, 0, -2.707)
@ -805,9 +843,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.727, 0, 0.611)
[node name="statue_columnDamaged2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("17_pbfwi")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.609224, 0, 0.72)
[node name="bed_floor2" parent="SubViewportContainer/SubViewport/Camp/Objects" instance=ExtResource("15_pbfwi")]
transform = Transform3D(0.72094, 0, -0.346764, 0, 0.8, 0, 0.346764, 0, 0.72094, -1.54675, 0.0359214, -1.37165)
[node name="VFX" type="Node3D" parent="SubViewportContainer/SubViewport/Camp"]
[node name="Rain" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/Camp/VFX"]
@ -821,28 +856,6 @@ trail_lifetime = 0.1
process_material = SubResource("ParticleProcessMaterial_p5fn2")
draw_pass_1 = SubResource("RibbonTrailMesh_5r2bu")
[node name="Fire" type="GPUParticles3D" parent="SubViewportContainer/SubViewport/Camp/VFX"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.923367, 0.0346675, -0.309572)
amount = 50
lifetime = 0.4
speed_scale = 0.4
process_material = SubResource("ParticleProcessMaterial_xvexm")
draw_pass_1 = SubResource("QuadMesh_hvb1l")
[node name="OmniLight3D" type="OmniLight3D" parent="SubViewportContainer/SubViewport/Camp/VFX/Fire"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000509977, 0.121094, -0.00151992)
light_color = Color(0.89, 0.461613, 0.2136, 1)
light_energy = 0.716329
light_indirect_energy = 1.084
light_volumetric_fog_energy = 3.764
light_size = 0.105
shadow_enabled = true
distance_fade_enabled = true
distance_fade_begin = 386.01
distance_fade_shadow = 45.9
distance_fade_length = 28.05
script = ExtResource("22_ukp6m")
[node name="UISubViewportContainer" type="SubViewportContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0

View file

@ -1,6 +1,69 @@
extends Camera3D
@export var player: Node3D # Assign your player node in the editor
var speed_factor = 3
func _ready():
player = %Player
func _physics_process(delta):
position = lerp(position, %Player.position, speed_factor * delta)
func _process(delta):
if player:
check_line_of_sight_to_player()
func check_line_of_sight_to_player():
var start_pos = global_position
var end_pos = player.global_position
var direction = (end_pos - start_pos).normalized()
var max_distance = start_pos.distance_to(end_pos)
var space_state = get_world_3d().direct_space_state
var current_distance = 0.0
var hit_trees: Array[Node] = []
# Keep casting rays until we reach the player
while current_distance < max_distance - 0.1:
var current_start = start_pos + direction * current_distance
var current_end = end_pos
# Create ray query
var query = PhysicsRayQueryParameters3D.create(current_start, current_end)
var result = space_state.intersect_ray(query)
if result.is_empty():
# No obstacles, clear line of sight
break
var hit_object = result.collider
var hit_point = result.position
var distance_to_hit = current_start.distance_to(hit_point)
# If we hit a tree, add it to the list
if hit_object.is_in_group("tree_visibility"):
if hit_object not in hit_trees:
hit_trees.append(hit_object)
# Only fade if player is behind the tree
if is_player_behind_tree(hit_object):
tree_is_blocking_player(hit_object)
# Move past the hit point for next iteration
current_distance += distance_to_hit + 0.1
# Safety check to prevent infinite loops
if distance_to_hit < 0.01:
current_distance += 0.5
func is_player_behind_tree(tree_object: Node) -> bool:
var camera_pos = global_position
var player_pos = player.global_position
var tree_pos = tree_object.global_position
# Calculate distances from camera
var camera_to_tree_distance = camera_pos.distance_to(tree_pos)
var camera_to_player_distance = camera_pos.distance_to(player_pos)
# Tree should be faded if it's closer to camera than player
# (tree is between camera and player)
var buffer = 0.2 # Small buffer to avoid flickering
return camera_to_tree_distance < (camera_to_player_distance - buffer)
func tree_is_blocking_player(tree_object):
# Fade out the blocking tree
tree_object.get_parent().fade_out_tree_simple()

View file

@ -10,11 +10,10 @@ sky_material = SubResource("ProceduralSkyMaterial_lg8b7")
[resource]
background_mode = 2
background_color = Color(0.752941, 0.776471, 0.827451, 1)
background_color = Color(1, 1, 0.603922, 1)
sky = SubResource("Sky_7bk1c")
ambient_light_source = 2
ambient_light_color = Color(0.662745, 0.694118, 0.772549, 1)
ambient_light_energy = 0.5
ambient_light_source = 3
ambient_light_color = Color(0.800491, 0.800491, 0.800491, 1)
reflected_light_source = 2
tonemap_mode = 2
ssil_enabled = true