Even better map
This commit is contained in:
@ -1,8 +1,7 @@
|
||||
# MapIcon.gd
|
||||
# space_simulation/scenes/UI/map_icon.gd
|
||||
class_name MapIcon
|
||||
extends Control
|
||||
|
||||
# A signal this icon emits when it is clicked, passing its body reference.
|
||||
signal selected(body: RigidBody2D)
|
||||
|
||||
@onready var name_label: Label = $NameLabel
|
||||
@ -10,12 +9,20 @@ signal selected(body: RigidBody2D)
|
||||
var body_reference: RigidBody2D
|
||||
var dot_color: Color = Color.WHITE
|
||||
|
||||
# --- Animation Variables ---
|
||||
var hover_tween: Tween
|
||||
var outline_progress: float = 0.0 # 0.0 for corners, 1.0 for full outline
|
||||
|
||||
const CORNER_BASE_PROGRESS = 0.25
|
||||
const OUTLINE_FULL_PROGRESS = 1.0
|
||||
|
||||
# --- FIX: Create a setter function for the hover_progress variable ---
|
||||
# This ensures that whenever the value is changed (especially by a Tween),
|
||||
# the icon is immediately redrawn to show the change.
|
||||
var hover_progress: float = 0.0:
|
||||
set(value):
|
||||
hover_progress = value
|
||||
queue_redraw()
|
||||
|
||||
func _ready() -> void:
|
||||
# Connect mouse enter/exit signals to handle hover effects.
|
||||
mouse_entered.connect(_on_mouse_entered)
|
||||
mouse_exited.connect(_on_mouse_exited)
|
||||
|
||||
@ -23,7 +30,6 @@ func initialize(body: RigidBody2D):
|
||||
body_reference = body
|
||||
name_label.text = body.name
|
||||
|
||||
# Determine the dot color based on the body's type.
|
||||
if body is Spaceship:
|
||||
dot_color = Color.CYAN
|
||||
elif body is CelestialBody:
|
||||
@ -32,26 +38,22 @@ func initialize(body: RigidBody2D):
|
||||
"Planet": dot_color = Color.DODGER_BLUE
|
||||
"Moon": dot_color = Color.PURPLE
|
||||
|
||||
# Set the rich tooltip text with sensor data.
|
||||
self.tooltip_text = _generate_tooltip_text()
|
||||
|
||||
|
||||
# --- Custom Drawing ---
|
||||
func _draw() -> void:
|
||||
var center = size / 2.0
|
||||
var dot_radius = 4.0
|
||||
|
||||
# Draw the central dot representing the object.
|
||||
draw_circle(center, dot_radius, dot_color)
|
||||
|
||||
# --- Draw the hover outline ---
|
||||
var outline_size = size * 0.8
|
||||
var outline_rect = Rect2(Vector2.ZERO, outline_size)
|
||||
outline_rect.position = center - (outline_rect.size / 2.0)
|
||||
|
||||
# This value (0 to 1) is controlled by the hover tween.
|
||||
var progress = outline_progress
|
||||
var corner_len = outline_rect.size.x * 0.25 # Length of the corner lines
|
||||
var draw_progress = lerp(CORNER_BASE_PROGRESS, OUTLINE_FULL_PROGRESS, hover_progress)
|
||||
|
||||
var corner_len = outline_rect.size.x * 0.5
|
||||
|
||||
var top_left = outline_rect.position
|
||||
var top_right = outline_rect.position + Vector2(outline_rect.size.x, 0)
|
||||
@ -60,52 +62,44 @@ func _draw() -> void:
|
||||
|
||||
var line_color = Color(Color.GRAY, 0.75)
|
||||
|
||||
# Draw horizontal and vertical lines for each corner based on progress.
|
||||
# Top-left corner
|
||||
draw_line(top_left, top_left + Vector2(corner_len * progress, 0), line_color, 1.0)
|
||||
draw_line(top_left, top_left + Vector2(0, corner_len * progress), line_color, 1.0)
|
||||
draw_line(top_left, top_left + Vector2(corner_len * draw_progress, 0), line_color, 1.0)
|
||||
draw_line(top_left, top_left + Vector2(0, corner_len * draw_progress), line_color, 1.0)
|
||||
|
||||
# Top-right corner
|
||||
draw_line(top_right, top_right - Vector2(corner_len * progress, 0), line_color, 1.0)
|
||||
draw_line(top_right, top_right + Vector2(0, corner_len * progress), line_color, 1.0)
|
||||
draw_line(top_right, top_right - Vector2(corner_len * draw_progress, 0), line_color, 1.0)
|
||||
draw_line(top_right, top_right + Vector2(0, corner_len * draw_progress), line_color, 1.0)
|
||||
|
||||
# Bottom-left corner
|
||||
draw_line(bottom_left, bottom_left + Vector2(corner_len * progress, 0), line_color, 1.0)
|
||||
draw_line(bottom_left, bottom_left - Vector2(0, corner_len * progress), line_color, 1.0)
|
||||
draw_line(bottom_left, bottom_left + Vector2(corner_len * draw_progress, 0), line_color, 1.0)
|
||||
draw_line(bottom_left, bottom_left - Vector2(0, corner_len * draw_progress), line_color, 1.0)
|
||||
|
||||
# Bottom-right corner
|
||||
draw_line(bottom_right, bottom_right - Vector2(corner_len * progress, 0), line_color, 1.0)
|
||||
draw_line(bottom_right, bottom_right - Vector2(0, corner_len * progress), line_color, 1.0)
|
||||
draw_line(bottom_right, bottom_right - Vector2(corner_len * draw_progress, 0), line_color, 1.0)
|
||||
draw_line(bottom_right, bottom_right - Vector2(0, corner_len * draw_progress), line_color, 1.0)
|
||||
|
||||
# --- Input and Hover Handling ---
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed():
|
||||
emit_signal("selected", body_reference)
|
||||
|
||||
# No changes are needed here; the tweens will automatically use the new setter function.
|
||||
func _on_mouse_entered():
|
||||
if hover_tween: hover_tween.kill()
|
||||
hover_tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
||||
hover_tween.tween_property(self, "outline_progress", 1.0, 0.2)
|
||||
hover_tween.tween_property(self, "hover_progress", 1.0, 0.2)
|
||||
|
||||
func _on_mouse_exited():
|
||||
if hover_tween: hover_tween.kill()
|
||||
hover_tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
||||
hover_tween.tween_property(self, "outline_progress", 0.0, 0.3)
|
||||
hover_tween.tween_property(self, "hover_progress", 0.0, 0.3)
|
||||
|
||||
# --- Tooltip Data Generation ---
|
||||
func _generate_tooltip_text() -> String:
|
||||
var info = [body_reference.name]
|
||||
|
||||
if body_reference is CelestialBody:
|
||||
var celestial = body_reference as CelestialBody
|
||||
# Calculate orbital period (Kepler's 3rd Law) - simplified for tooltip
|
||||
if is_instance_valid(celestial.primary):
|
||||
var mu = OrbitalMechanics.G * celestial.primary.mass
|
||||
var r = celestial.global_position.distance_to(celestial.primary.global_position)
|
||||
var period_seconds = TAU * sqrt(pow(r, 3) / mu)
|
||||
info.append("Orbital Period: %s" % _format_seconds_to_mmss(period_seconds))
|
||||
|
||||
# Count moons if it's a planet
|
||||
var moon_count = 0
|
||||
for child in celestial.get_children():
|
||||
if child is CelestialBody and child.get_class_name() == "Moon":
|
||||
@ -119,7 +113,6 @@ func _generate_tooltip_text() -> String:
|
||||
|
||||
return "\n".join(info)
|
||||
|
||||
# Helper function for formatting time in the tooltip
|
||||
func _format_seconds_to_mmss(seconds: float) -> String:
|
||||
var total_seconds = int(seconds)
|
||||
var minutes = total_seconds / 60
|
||||
|
||||
@ -25,7 +25,7 @@ layout_mode = 2
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="NavigationUI/NavigationScreen/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(250, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_horizontal = 4
|
||||
|
||||
[node name="TargetLabel" type="Label" parent="NavigationUI/NavigationScreen/HBoxContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
@ -71,22 +71,26 @@ unique_name_in_owner = true
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 3.0
|
||||
script = ExtResource("2_ys00n")
|
||||
map_icon_scene = ExtResource("3_378us")
|
||||
|
||||
[node name="RightHandPanel" type="VBoxContainer" parent="NavigationUI/NavigationScreen/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 300)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_horizontal = 4
|
||||
|
||||
[node name="ShipStatusLabel" type="Label" parent="NavigationUI/NavigationScreen/HBoxContainer/RightHandPanel"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="NavigationUI/NavigationScreen/HBoxContainer/RightHandPanel"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="SubViewportContainer" type="SubViewportContainer" parent="NavigationUI/NavigationScreen/HBoxContainer/RightHandPanel"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 8
|
||||
|
||||
[node name="SubViewport" type="SubViewport" parent="NavigationUI/NavigationScreen/HBoxContainer/RightHandPanel/SubViewportContainer"]
|
||||
unique_name_in_owner = true
|
||||
|
||||
@ -8,8 +8,10 @@ signal body_selected_for_planning(body: RigidBody2D)
|
||||
# You will need to drag your MapIcon.tscn file into this slot in the Inspector.
|
||||
@export var map_icon_scene: PackedScene
|
||||
|
||||
const CULLING_DISTANCE_THRESHOLD = 30 # Pixels. If icons are closer than this, they might be culled.
|
||||
const CULLING_ZOOM_THRESHOLD = 0.5 # Only start culling when zoomed out past this level.
|
||||
# The minimum distance in PIXELS between icons before a label is culled.
|
||||
const LABEL_CULLING_PIXEL_THRESHOLD = 40.0
|
||||
# The minimum distance in PIXELS before the *entire icon* is culled.
|
||||
const ICON_CULLING_PIXEL_THRESHOLD = 20.0 # Should be smaller than the label threshold
|
||||
|
||||
# --- Map State ---
|
||||
var map_scale: float = 0.001 # Start zoomed out
|
||||
@ -82,33 +84,54 @@ func _update_icon_positions():
|
||||
|
||||
var map_center = get_rect().size / 2.0
|
||||
|
||||
var icon_positions = [] # For culling
|
||||
|
||||
|
||||
# --- Step 1: Calculate all icon screen positions ---
|
||||
var icon_data_for_frame = []
|
||||
for body in icon_map:
|
||||
var icon = icon_map[body]
|
||||
|
||||
# Calculate the position of the body relative to the map's focal point.
|
||||
var relative_pos = body.global_position - focal_body.global_position
|
||||
# Reset all icons and labels to be visible at the start of the frame.
|
||||
icon.visible = true
|
||||
icon.name_label.visible = true
|
||||
|
||||
# Apply map scale (zoom) and offset (pan), then center it on its pivot.
|
||||
icon.position = (relative_pos * map_scale) + map_offset + map_center - (icon.size / 2)
|
||||
|
||||
if map_scale < CULLING_ZOOM_THRESHOLD:
|
||||
for i in range(icon_positions.size()):
|
||||
var item_a = icon_positions[i]
|
||||
if not item_a.icon.visible: continue
|
||||
var relative_pos = body.global_position - focal_body.global_position
|
||||
var final_screen_pos = (relative_pos * map_scale) + map_offset + map_center
|
||||
|
||||
# Store the data we need for the culling pass.
|
||||
icon_data_for_frame.append({
|
||||
"screen_pos": final_screen_pos,
|
||||
"body": body,
|
||||
"icon": icon
|
||||
})
|
||||
|
||||
# Set the icon's position for rendering.
|
||||
icon.position = final_screen_pos - (icon.size / 2)
|
||||
|
||||
# --- Step 2: Perform screen-space culling ---
|
||||
for i in range(icon_data_for_frame.size()):
|
||||
var data_a = icon_data_for_frame[i]
|
||||
|
||||
# Skip this icon if it has already been hidden by a previous check.
|
||||
if not data_a.icon.visible:
|
||||
continue
|
||||
|
||||
for j in range(i + 1, icon_data_for_frame.size()):
|
||||
var data_b = icon_data_for_frame[j]
|
||||
if not data_b.icon.visible: continue
|
||||
|
||||
var distance = data_a.screen_pos.distance_to(data_b.screen_pos)
|
||||
|
||||
for j in range(i + 1, icon_positions.size()):
|
||||
var item_b = icon_positions[j]
|
||||
if not item_b.icon.visible: continue
|
||||
|
||||
if item_a.pos.distance_to(item_b.pos) < CULLING_DISTANCE_THRESHOLD:
|
||||
# Icons are overlapping, hide the one with less mass
|
||||
if item_a.body.mass > item_b.body.mass:
|
||||
item_b.icon.visible = false
|
||||
else:
|
||||
item_a.icon.visible = false
|
||||
# --- Tier 1: Cull the entire icon if they are very close ---
|
||||
if distance < ICON_CULLING_PIXEL_THRESHOLD:
|
||||
if data_a.body.mass > data_b.body.mass:
|
||||
data_b.icon.visible = false
|
||||
else:
|
||||
data_a.icon.visible = false
|
||||
# --- Tier 2: If not culled, check if we should cull just the label ---
|
||||
elif distance < LABEL_CULLING_PIXEL_THRESHOLD:
|
||||
if data_a.body.mass > data_b.body.mass:
|
||||
data_b.icon.name_label.visible = false
|
||||
else:
|
||||
data_a.icon.name_label.visible = false
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
|
||||
@ -171,62 +171,87 @@ func _generate_and_place_bodies(primary: RigidBody2D) -> void:
|
||||
print("Star system generation complete.")
|
||||
|
||||
GameManager.register_star_system(self)
|
||||
|
||||
# Creates moons and stations around a primary body.
|
||||
func _create_moons_and_stations(primary: RigidBody2D) -> void:
|
||||
var num_moons = randi_range(min_moons, max_moons)
|
||||
var num_planetary_stations = randi_range(min_planetary_stations, max_planetary_stations)
|
||||
var moon_orbit_radius = min_planetary_orbit_radius
|
||||
|
||||
# 2. Create an "inventory" of bodies to be placed.
|
||||
# Start with a base orbital radius around the parent planet.
|
||||
var next_local_orbit_radius = min_planetary_orbit_radius
|
||||
|
||||
# Create an "inventory" of bodies to be placed.
|
||||
var bodies_to_place = []
|
||||
for i in range(num_planetary_stations):
|
||||
bodies_to_place.append({"type": "station", "num": i})
|
||||
for i in range(num_moons):
|
||||
bodies_to_place.append({"type": "moon", "num": i})
|
||||
|
||||
# 3. Randomize the order of placement.
|
||||
# Randomize the order of placement.
|
||||
bodies_to_place.shuffle()
|
||||
|
||||
for body in bodies_to_place:
|
||||
match body.type:
|
||||
"station":
|
||||
# Generate a space station.
|
||||
var station_instance = station_scene.instantiate() as CelestialBody
|
||||
system_data.stations.append(station_instance)
|
||||
station_instance.name = "Station " + str(body.num + 1)
|
||||
station_instance.primary = primary
|
||||
station_instance.mass = STATION_MASS
|
||||
var station_extra_orbit = randf_range(0, 20)
|
||||
station_instance.orbit_radius_real = moon_orbit_radius + station_extra_orbit
|
||||
moon_orbit_radius = station_instance.orbit_radius_real + randf_range(40, 100)
|
||||
|
||||
# Use the local orbit radius for this station.
|
||||
station_instance.orbit_radius_real = next_local_orbit_radius
|
||||
_create_body_in_ring(primary, station_instance)
|
||||
|
||||
# Increment the *local* orbit radius for the next body.
|
||||
next_local_orbit_radius += randf_range(40, 100)
|
||||
|
||||
"moon":
|
||||
var moon_instance = moon_scene.instantiate() as CelestialBody
|
||||
system_data.moons.append(moon_instance)
|
||||
moon_instance.name = "Moon " + str(body.num + 1)
|
||||
moon_instance.primary = primary
|
||||
moon_instance.mass = MOON_MASS
|
||||
var moon_extra_orbit = randf_range(0, 20)
|
||||
moon_instance.orbit_radius_real = moon_orbit_radius + moon_extra_orbit
|
||||
|
||||
# Use the local orbit radius for this moon.
|
||||
moon_instance.orbit_radius_real = next_local_orbit_radius
|
||||
_create_body_in_ring(primary, moon_instance)
|
||||
moon_orbit_radius = moon_instance.orbit_radius_real + randf_range(40, 100)
|
||||
|
||||
# Increment the *local* orbit radius for the next body.
|
||||
next_local_orbit_radius += randf_range(40, 100)
|
||||
|
||||
# --- FIX for stations orbiting moons ---
|
||||
var num_moon_stations = randi_range(min_moon_stations, max_moon_stations)
|
||||
|
||||
var moon_inner_orbit = randf_range(2, 5)
|
||||
var next_moon_station_orbit = randf_range(20, 50) # Start closer for moon stations
|
||||
|
||||
for i in range(num_moon_stations):
|
||||
# Generate a space station.
|
||||
var station_instance = station_scene.instantiate() as CelestialBody
|
||||
system_data.stations.append(station_instance)
|
||||
station_instance.name = "Station " + str(i + 1)
|
||||
station_instance.primary = moon_instance
|
||||
station_instance.mass = STATION_MASS
|
||||
station_instance.orbit_radius_real = moon_inner_orbit
|
||||
|
||||
# Set the orbit relative to the moon.
|
||||
station_instance.orbit_radius_real = next_moon_station_orbit
|
||||
_create_body_in_ring(moon_instance, station_instance)
|
||||
|
||||
moon_inner_orbit = moon_inner_orbit + randf_range(2, 5)
|
||||
# Increment for the next station around this *same* moon.
|
||||
next_moon_station_orbit += randf_range(20, 50)
|
||||
|
||||
#var num_moon_stations = randi_range(min_moon_stations, max_moon_stations)
|
||||
#
|
||||
#var moon_inner_orbit = randf_range(2, 5)
|
||||
#
|
||||
#for i in range(num_moon_stations):
|
||||
## Generate a space station.
|
||||
#var station_instance = station_scene.instantiate() as CelestialBody
|
||||
#system_data.stations.append(station_instance)
|
||||
#station_instance.name = "Station " + str(i + 1)
|
||||
#station_instance.primary = moon_instance
|
||||
#station_instance.mass = STATION_MASS
|
||||
#station_instance.orbit_radius_real = moon_inner_orbit
|
||||
#_create_body_in_ring(moon_instance, station_instance)
|
||||
#
|
||||
#moon_inner_orbit = moon_inner_orbit + randf_range(2, 5)
|
||||
|
||||
|
||||
# Creates a single asteroid belt.
|
||||
@ -256,11 +281,21 @@ func _create_asteroid_belt(primary: RigidBody2D, initial_offset: float) -> Aster
|
||||
|
||||
# Helper function to instantiate and place a body in a ring.
|
||||
func _create_body_in_ring(primary: CelestialBody, body_instance: CelestialBody) -> void:
|
||||
var initial_position_vector = Vector2(body_instance.orbit_radius_real, 0).rotated(randf() * TAU)
|
||||
body_instance.global_position = primary.global_position + initial_position_vector
|
||||
|
||||
# 1. Parent the new body to its primary FIRST.
|
||||
# This establishes the correct local coordinate space.
|
||||
primary.add_child(body_instance)
|
||||
|
||||
# 2. Calculate the position vector relative to the parent.
|
||||
var initial_position_vector = Vector2(body_instance.orbit_radius_real, 0).rotated(randf() * TAU)
|
||||
|
||||
# 3. Set the LOCAL position of the body.
|
||||
# Godot will now correctly calculate its global_position based on the parent's position.
|
||||
body_instance.position = initial_position_vector
|
||||
|
||||
# 4. Now that the global position is correct, calculate the velocity for a stable orbit.
|
||||
# This part remains the same, as physics calculations need the final global positions.
|
||||
body_instance.linear_velocity = OrbitalMechanics.calculate_circular_orbit_velocity(body_instance, primary)
|
||||
|
||||
print("Created " + body_instance.name + " with radius " + str(body_instance.orbit_radius_real) + " and mass " + str(body_instance.mass))
|
||||
print("Initial orbital velocity is: " + str(body_instance.linear_velocity))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user