Integrate gauges into cockpit dashboard
This commit is contained in:
1 parent
0b79bf9b82
commit
53288b48c8
7 files changed
+213
-17
No files matched your search
@@ -20,7 +20,7 @@ production sous `game/` n'en dépend.
|
||||
télémétrie typés.
|
||||
- Habitacle 3D solidaire de la coque : hublot occultant, consoles, commandes,
|
||||
éclairage, structure de coque renforcée, conduites, jauges analogiques et
|
||||
emplacement réservé au sonar.
|
||||
tableau d'instruments structuré, et emplacement réservé au sonar.
|
||||
- Instruments physiques à valeurs réelles ; le HUD reste réservé aux alertes,
|
||||
à l'éjection et aux états terminaux.
|
||||
- Pause qui suspend les ticks, confirmations de sortie et premier remappage avec
|
||||
|
||||
@@ -811,6 +811,9 @@ Complément du 3 octobre 2026 : le plafond, le boîtier supérieur et les faces
|
||||
jauges utilisent des textures bitmap dédiées. Les cadrans reçoivent en plus des
|
||||
graduations, une aiguille peinte et une vitre distincte ; le plafonnier est
|
||||
réduit et protégé par une grille pour conserver des proportions crédibles.
|
||||
Les jauges de charge batterie et de pression coque sont regroupées dans un
|
||||
binnacle solidaire du tableau de bord, hors du hublot. La façade du tableau est
|
||||
structurée par des rails, séparateurs, vis et banques de commutateurs physiques.
|
||||
|
||||
- Construire une coque intérieure 3D fixe par rapport au véhicule, indépendante
|
||||
du regard de la caméra.
|
||||
|
||||
@@ -12,8 +12,19 @@ var _pressure_needle: Node3D
|
||||
func _init(materials: CockpitMaterialPalette) -> void:
|
||||
_materials = materials
|
||||
name = "AnalogGauges"
|
||||
_battery_needle = _add_gauge("BatteryGauge", Vector3(-0.34, 0.48, -1.14), "BAT")
|
||||
_pressure_needle = _add_gauge("PressureGauge", Vector3(0.34, 0.48, -1.14), "BAR")
|
||||
_build_binnacle()
|
||||
_battery_needle = _add_gauge(
|
||||
"BatteryGauge",
|
||||
Vector3(-0.095, 0.315, 0.145),
|
||||
"%"
|
||||
)
|
||||
_pressure_needle = _add_gauge(
|
||||
"PressureGauge",
|
||||
Vector3(0.095, 0.315, 0.145),
|
||||
"BAR"
|
||||
)
|
||||
_add_binnacle_label("CHARGE BAT.", Vector3(-0.095, 0.235, 0.160))
|
||||
_add_binnacle_label("PRESSION COQUE", Vector3(0.095, 0.235, 0.160))
|
||||
|
||||
|
||||
func present(battery_ratio: float, pressure_ratio: float) -> void:
|
||||
@@ -25,11 +36,39 @@ func battery_needle_degrees() -> float:
|
||||
return 0.0 if _battery_needle == null else _battery_needle.rotation_degrees.z
|
||||
|
||||
|
||||
func _build_binnacle() -> void:
|
||||
CockpitMeshFactory.add_box(
|
||||
"BinnacleHousing",
|
||||
self,
|
||||
Vector3(0.0, 0.290, 0.095),
|
||||
Vector3(0.40, 0.180, 0.105),
|
||||
_materials.overhead_steel
|
||||
)
|
||||
CockpitMeshFactory.add_box(
|
||||
"BinnacleFoot",
|
||||
self,
|
||||
Vector3(0.0, 0.205, 0.075),
|
||||
Vector3(0.47, 0.040, 0.14),
|
||||
_materials.dark_steel
|
||||
)
|
||||
|
||||
|
||||
func _add_binnacle_label(caption: String, position: Vector3) -> void:
|
||||
var label := Label3D.new()
|
||||
label.name = "%sLabel" % caption.to_pascal_case().replace(" ", "")
|
||||
label.text = caption
|
||||
label.font_size = 12
|
||||
label.pixel_size = 0.00055
|
||||
label.modulate = Color(0.76, 0.72, 0.60)
|
||||
label.position = position
|
||||
add_child(label)
|
||||
|
||||
|
||||
func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3D:
|
||||
var gauge := Node3D.new()
|
||||
gauge.name = gauge_name
|
||||
gauge.position = position
|
||||
gauge.scale = Vector3(0.78, 0.78, 0.78)
|
||||
gauge.scale = Vector3(0.62, 0.62, 0.62)
|
||||
add_child(gauge)
|
||||
CockpitMeshFactory.add_box(
|
||||
"MountingPlate",
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
class_name CockpitDashboardDetailing
|
||||
extends Node3D
|
||||
|
||||
const CockpitMaterialPalette = preload(
|
||||
"res://game/presentation/cockpit/cockpit_material_palette.gd"
|
||||
)
|
||||
const CockpitMeshFactory = preload(
|
||||
"res://game/presentation/cockpit/cockpit_mesh_factory.gd"
|
||||
)
|
||||
|
||||
var _materials: CockpitMaterialPalette
|
||||
var _control_count := 0
|
||||
|
||||
|
||||
func _init(materials: CockpitMaterialPalette) -> void:
|
||||
_materials = materials
|
||||
name = "DashboardDetailing"
|
||||
_build_structural_frame()
|
||||
_build_switch_banks()
|
||||
|
||||
|
||||
func control_count() -> int:
|
||||
return _control_count
|
||||
|
||||
|
||||
func apply_screen_materials(dashboard: Node3D) -> void:
|
||||
for screen_name: String in ["ResourcesScreen", "NavigationScreen", "IntegrityScreen"]:
|
||||
var screen := dashboard.get_node_or_null(screen_name) as MeshInstance3D
|
||||
if screen != null:
|
||||
screen.material_override = _materials.instrument_screen
|
||||
|
||||
|
||||
func _build_structural_frame() -> void:
|
||||
CockpitMeshFactory.add_box(
|
||||
"TopRail",
|
||||
self,
|
||||
Vector3(0.0, 0.255, 0.115),
|
||||
Vector3(1.50, 0.055, 0.055),
|
||||
_materials.dark_steel
|
||||
)
|
||||
CockpitMeshFactory.add_box(
|
||||
"BottomRail",
|
||||
self,
|
||||
Vector3(0.0, -0.205, 0.115),
|
||||
Vector3(1.50, 0.050, 0.055),
|
||||
_materials.dark_steel
|
||||
)
|
||||
for x: float in [-0.755, 0.755]:
|
||||
CockpitMeshFactory.add_box(
|
||||
"SideRail",
|
||||
self,
|
||||
Vector3(x, 0.025, 0.115),
|
||||
Vector3(0.050, 0.46, 0.055),
|
||||
_materials.dark_steel
|
||||
)
|
||||
for x: float in [-0.238, 0.238]:
|
||||
CockpitMeshFactory.add_box(
|
||||
"ScreenSeparator",
|
||||
self,
|
||||
Vector3(x, 0.015, 0.118),
|
||||
Vector3(0.038, 0.39, 0.048),
|
||||
_materials.brass
|
||||
)
|
||||
for screw_position: Vector2 in [
|
||||
Vector2(-0.72, -0.18),
|
||||
Vector2(-0.72, 0.23),
|
||||
Vector2(0.72, -0.18),
|
||||
Vector2(0.72, 0.23),
|
||||
Vector2(-0.24, -0.18),
|
||||
Vector2(0.24, -0.18),
|
||||
]:
|
||||
CockpitMeshFactory.add_cylinder(
|
||||
"PanelScrew",
|
||||
self,
|
||||
Vector3(screw_position.x, screw_position.y, 0.151),
|
||||
0.010,
|
||||
0.010,
|
||||
_materials.brass,
|
||||
Vector3(90.0, 0.0, 0.0)
|
||||
)
|
||||
|
||||
|
||||
func _build_switch_banks() -> void:
|
||||
_add_switch_bank(-0.59, "AUX")
|
||||
_add_switch_bank(0.59, "POMPES")
|
||||
|
||||
|
||||
func _add_switch_bank(center_x: float, caption: String) -> void:
|
||||
CockpitMeshFactory.add_box(
|
||||
"SwitchPlate",
|
||||
self,
|
||||
Vector3(center_x, -0.205, 0.145),
|
||||
Vector3(0.25, 0.060, 0.020),
|
||||
_materials.overhead_steel
|
||||
)
|
||||
for offset: float in [-0.075, -0.025, 0.025, 0.075]:
|
||||
CockpitMeshFactory.add_cylinder(
|
||||
"ToggleSwitch",
|
||||
self,
|
||||
Vector3(center_x + offset, -0.195, 0.171),
|
||||
0.006,
|
||||
0.030,
|
||||
_materials.dark_steel,
|
||||
Vector3(90.0, 0.0, 0.0)
|
||||
)
|
||||
_control_count += 1
|
||||
var label := Label3D.new()
|
||||
label.name = "%sLabel" % caption.to_pascal_case()
|
||||
label.text = caption
|
||||
label.font_size = 10
|
||||
label.pixel_size = 0.00055
|
||||
label.modulate = Color(0.72, 0.69, 0.56)
|
||||
label.position = Vector3(center_x, -0.235, 0.165)
|
||||
add_child(label)
|
||||
@@ -5,6 +5,9 @@ const DiveUpdate = preload("res://game/domain/dive/dive_update.gd")
|
||||
const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd")
|
||||
const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd")
|
||||
const CockpitAnalogGauges = preload("res://game/presentation/cockpit/cockpit_analog_gauges.gd")
|
||||
const CockpitDashboardDetailing = preload(
|
||||
"res://game/presentation/cockpit/cockpit_dashboard_detailing.gd"
|
||||
)
|
||||
const CockpitMaterialPalette = preload("res://game/presentation/cockpit/cockpit_material_palette.gd")
|
||||
const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh_factory.gd")
|
||||
|
||||
@@ -14,9 +17,8 @@ var _dark_steel: Material
|
||||
var _overhead_steel: Material
|
||||
var _brass: Material
|
||||
var _copper: Material
|
||||
var _rubber: Material
|
||||
var _warm_emission: StandardMaterial3D
|
||||
var _analog_gauges: CockpitAnalogGauges
|
||||
var _dashboard_detailing: CockpitDashboardDetailing
|
||||
var _warm_practical: OmniLight3D
|
||||
var _emergency_light: OmniLight3D
|
||||
|
||||
@@ -28,7 +30,7 @@ func _ready() -> void:
|
||||
_build_life_support_pipes()
|
||||
_build_overhead_console()
|
||||
_build_panel_hardware()
|
||||
_build_analog_gauges()
|
||||
_build_dashboard_instrumentation()
|
||||
_build_practical_lights()
|
||||
|
||||
|
||||
@@ -74,6 +76,14 @@ func textures_are_loaded() -> bool:
|
||||
)
|
||||
|
||||
|
||||
func dashboard_control_count() -> int:
|
||||
return 0 if _dashboard_detailing == null else _dashboard_detailing.control_count()
|
||||
|
||||
|
||||
func gauges_are_dashboard_mounted() -> bool:
|
||||
return _analog_gauges != null and _analog_gauges.get_parent() == get_parent().get_node("Dashboard")
|
||||
|
||||
|
||||
func _create_materials() -> void:
|
||||
_materials = CockpitMaterialPalette.new()
|
||||
_painted_steel = _materials.painted_steel
|
||||
@@ -81,8 +91,6 @@ func _create_materials() -> void:
|
||||
_overhead_steel = _materials.overhead_steel
|
||||
_brass = _materials.brass
|
||||
_copper = _materials.copper
|
||||
_rubber = _materials.rubber
|
||||
_warm_emission = _materials.warm_emission
|
||||
|
||||
|
||||
func _apply_materials_to_existing_cockpit() -> void:
|
||||
@@ -91,7 +99,6 @@ func _apply_materials_to_existing_cockpit() -> void:
|
||||
[
|
||||
"CabinShell/LeftWall",
|
||||
"CabinShell/RightWall",
|
||||
"Dashboard/MainPanel",
|
||||
"LeftConsole",
|
||||
"RightConsole",
|
||||
]
|
||||
@@ -112,6 +119,7 @@ func _apply_materials_to_existing_cockpit() -> void:
|
||||
[
|
||||
"CabinShell/Ceiling",
|
||||
"PortholeAssembly/TopBulkhead",
|
||||
"Dashboard/MainPanel",
|
||||
]
|
||||
)
|
||||
_apply_material_to_paths(
|
||||
@@ -125,8 +133,6 @@ func _apply_materials_to_existing_cockpit() -> void:
|
||||
"VerticalKnob",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
func _apply_material_to_paths(material: Material, paths: Array[String]) -> void:
|
||||
var cockpit := get_parent()
|
||||
for path: String in paths:
|
||||
@@ -241,7 +247,7 @@ func _build_overhead_console() -> void:
|
||||
root,
|
||||
Vector3(0.0, 0.895, -0.985),
|
||||
Vector3(0.13, 0.010, 0.026),
|
||||
_warm_emission,
|
||||
_materials.warm_emission,
|
||||
Vector3(-8.0, 0.0, 0.0)
|
||||
)
|
||||
for guard_index in 5:
|
||||
@@ -262,7 +268,7 @@ func _build_overhead_console() -> void:
|
||||
Vector3(x, 0.965, -0.985),
|
||||
0.004,
|
||||
0.018,
|
||||
_rubber,
|
||||
_materials.rubber,
|
||||
Vector3(90.0, 0.0, 0.0)
|
||||
)
|
||||
|
||||
@@ -302,9 +308,15 @@ func _build_panel_hardware() -> void:
|
||||
root.add_child(label)
|
||||
|
||||
|
||||
func _build_analog_gauges() -> void:
|
||||
func _build_dashboard_instrumentation() -> void:
|
||||
var dashboard := get_parent().get_node_or_null("Dashboard") as Node3D
|
||||
if dashboard == null:
|
||||
return
|
||||
_dashboard_detailing = CockpitDashboardDetailing.new(_materials)
|
||||
dashboard.add_child(_dashboard_detailing)
|
||||
_dashboard_detailing.apply_screen_materials(dashboard)
|
||||
_analog_gauges = CockpitAnalogGauges.new(_materials)
|
||||
add_child(_analog_gauges)
|
||||
dashboard.add_child(_analog_gauges)
|
||||
|
||||
|
||||
func _build_practical_lights() -> void:
|
||||
|
||||
@@ -17,6 +17,7 @@ var rubber: Material
|
||||
var gauge_face: StandardMaterial3D
|
||||
var gauge_glass: StandardMaterial3D
|
||||
var needle_paint: StandardMaterial3D
|
||||
var instrument_screen: ShaderMaterial
|
||||
var warm_emission: StandardMaterial3D
|
||||
|
||||
|
||||
@@ -65,6 +66,7 @@ func _init() -> void:
|
||||
)
|
||||
gauge_glass = _glass_material()
|
||||
needle_paint = _plain_material(Color(0.68, 0.055, 0.022), 0.12, 0.44)
|
||||
instrument_screen = _screen_material()
|
||||
warm_emission = _emissive_material(Color(0.96, 0.52, 0.18), 2.4)
|
||||
|
||||
|
||||
@@ -167,6 +169,30 @@ func _glass_material() -> StandardMaterial3D:
|
||||
return material
|
||||
|
||||
|
||||
func _screen_material() -> ShaderMaterial:
|
||||
var shader := Shader.new()
|
||||
shader.code = """
|
||||
shader_type spatial;
|
||||
render_mode diffuse_burley, specular_schlick_ggx;
|
||||
|
||||
void fragment() {
|
||||
vec2 centered = UV - vec2(0.5);
|
||||
float vignette = 1.0 - smoothstep(0.28, 0.72, length(centered));
|
||||
float scanline = 0.94 + 0.06 * sin(UV.y * 720.0);
|
||||
float edge = mix(0.68, 1.0, vignette);
|
||||
vec3 phosphor = vec3(0.025, 0.105, 0.11) * edge * scanline;
|
||||
ALBEDO = phosphor;
|
||||
EMISSION = phosphor * 0.38;
|
||||
METALLIC = 0.08;
|
||||
ROUGHNESS = 0.18;
|
||||
SPECULAR = 0.82;
|
||||
}
|
||||
"""
|
||||
var material := ShaderMaterial.new()
|
||||
material.shader = shader
|
||||
return material
|
||||
|
||||
|
||||
func _emissive_material(color: Color, energy: float) -> StandardMaterial3D:
|
||||
var material := StandardMaterial3D.new()
|
||||
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
|
||||
@@ -16,7 +16,7 @@ const ProductionInputAdapter = preload(
|
||||
"res://game/infrastructure/input/production_input_adapter.gd"
|
||||
)
|
||||
|
||||
const TEST_COUNT := 56
|
||||
const TEST_COUNT := 58
|
||||
|
||||
var _failures: Array[String] = []
|
||||
|
||||
@@ -112,6 +112,8 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void:
|
||||
_expect(detailing.pipe_segment_count() >= 13, "cockpit exposes a complete life-support pipe run")
|
||||
_expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot")
|
||||
_expect(detailing.textures_are_loaded(), "cockpit loads its overhead and analog gauge textures")
|
||||
_expect(detailing.gauges_are_dashboard_mounted(), "analog gauges are mounted on the dashboard")
|
||||
_expect(detailing.dashboard_control_count() >= 8, "dashboard exposes physical auxiliary controls")
|
||||
_expect("kWh" in instruments.resources_text() and "kW" in instruments.resources_text(), "battery instrument has exact units")
|
||||
_expect("L/min" in instruments.resources_text(), "oxygen instrument has volume and consumption")
|
||||
_expect("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure")
|
||||
|
||||
Reference in new issue
Block a user