Lower generic thermal cockpit HUD

This commit is contained in:
Meta Vens 2026-07-12 06:57:28 +01:00
commit 2052b08371
4 changed files with 193 additions and 0 deletions

View file

@ -89,6 +89,8 @@ namespace GameCult.Eve.UnityScene
if (feedbackEffects == null) feedbackEffects = gameObject.AddComponent<EveUnityFeedbackEffectRenderer>();
feedbackEffects.Bind(this, Runtime.GameObjectAssetProvider);
var thermal = GetComponent<EveUnityThermalPresenter>();
var thermalHud = GetComponent<EveUnityThermalHudSink>();
if (thermalHud == null) thermalHud = gameObject.AddComponent<EveUnityThermalHudSink>();
if (thermal == null) thermal = gameObject.AddComponent<EveUnityThermalPresenter>();
thermal.Bind(this, Runtime.GameObjectAssetProvider as IEveUnityNativeAssetProvider);

View file

@ -0,0 +1,159 @@
using System;
using System.Globalization;
using UnityEngine;
using UnityEngine.UIElements;
#nullable enable
namespace GameCult.Eve.UnityScene
{
public sealed class EveUnityThermalHudSink : MonoBehaviour, IEveUnityThermalPresentationSink
{
private UIDocument? _document;
private VisualElement? _root;
private Label? _temperature;
private VisualElement? _temperatureMarker;
private VisualElement? _heatstrokeFill;
private VisualElement? _hypothermiaFill;
private Label? _heatstrokeValue;
private Label? _hypothermiaValue;
public EveUnityThermalPresentationFrame LastFrame { get; private set; }
public VisualElement? Root => _root;
private void Awake() => EnsureBuilt();
private void EnsureBuilt()
{
if (_root != null)
{
AttachToDocument();
return;
}
var panel = ScriptableObject.CreateInstance<PanelSettings>();
panel.name = "Eve thermal HUD panel";
panel.scaleMode = PanelScaleMode.ScaleWithScreenSize;
panel.referenceResolution = new Vector2Int(1920, 1080);
panel.match = 0.5f;
_document = gameObject.AddComponent<UIDocument>();
_document.panelSettings = panel;
_document.sortingOrder = 100;
_root = new VisualElement();
Build(_root);
AttachToDocument();
}
private void AttachToDocument()
{
var documentRoot = _document?.rootVisualElement;
if (documentRoot != null && _root != null && _root.parent == null)
documentRoot.Add(_root);
}
public void ApplyThermalPresentation(EveUnityThermalPresentationFrame frame)
{
EnsureBuilt();
LastFrame = frame;
if (_temperature == null) return;
_temperature.text = FormatTemperature(frame.CockpitTemperature);
_heatstrokeValue!.text = FormatPercent(frame.Heatstroke);
_hypothermiaValue!.text = FormatPercent(frame.Hypothermia);
_heatstrokeFill!.style.width = Length.Percent(Mathf.Clamp01(frame.Heatstroke) * 100);
_hypothermiaFill!.style.width = Length.Percent(Mathf.Clamp01(frame.Hypothermia) * 100);
_heatstrokeFill.style.backgroundColor = frame.HeatstrokeRisk
? new Color(1f, 0.2f, 0.1f, 0.95f) : new Color(0.86f, 0.38f, 0.16f, 0.9f);
_hypothermiaFill.style.backgroundColor = frame.HypothermiaRisk
? new Color(0.12f, 0.55f, 1f, 0.95f) : new Color(0.2f, 0.65f, 0.78f, 0.9f);
var normalizedTemperature = Mathf.InverseLerp(273f, 330f, frame.CockpitTemperature);
_temperatureMarker!.style.left = Length.Percent(normalizedTemperature * 100);
Root!.style.display = string.IsNullOrWhiteSpace(frame.EntityId) && frame.DeathWeight <= 0
? DisplayStyle.None : DisplayStyle.Flex;
}
private void Build(VisualElement root)
{
root.name = "eve-thermal-hud-root";
root.style.position = Position.Absolute;
root.style.left = 18;
root.style.bottom = 18;
root.style.width = 320;
root.style.height = 142;
root.style.paddingLeft = 12;
root.style.paddingRight = 12;
root.style.paddingTop = 10;
root.style.paddingBottom = 10;
root.style.backgroundColor = new Color(0.025f, 0.03f, 0.035f, 0.88f);
root.style.borderLeftWidth = root.style.borderRightWidth = 1;
root.style.borderTopWidth = root.style.borderBottomWidth = 1;
root.style.borderLeftColor = root.style.borderRightColor = new Color(0.35f, 0.42f, 0.46f, 0.8f);
root.style.borderTopColor = root.style.borderBottomColor = new Color(0.35f, 0.42f, 0.46f, 0.8f);
var header = new VisualElement { name = "cockpit-temperature" };
header.style.flexDirection = FlexDirection.Row;
header.style.justifyContent = Justify.SpaceBetween;
header.Add(Label("COCKPIT", 11, new Color(0.72f, 0.78f, 0.8f)));
_temperature = Label("-- K", 15, Color.white);
header.Add(_temperature);
root.Add(header);
var temperatureTrack = Track("temperature-track", 6);
temperatureTrack.style.marginTop = 7;
temperatureTrack.style.backgroundColor = new Color(0.12f, 0.48f, 0.7f, 0.8f);
_temperatureMarker = new VisualElement { name = "temperature-marker" };
_temperatureMarker.style.position = Position.Absolute;
_temperatureMarker.style.top = -2;
_temperatureMarker.style.width = 2;
_temperatureMarker.style.height = 10;
_temperatureMarker.style.backgroundColor = Color.white;
temperatureTrack.Add(_temperatureMarker);
root.Add(temperatureTrack);
root.Add(Meter("HEATSTROKE", "heatstroke", out _heatstrokeFill, out _heatstrokeValue));
root.Add(Meter("HYPOTHERMIA", "hypothermia", out _hypothermiaFill, out _hypothermiaValue));
}
private static VisualElement Meter(string title, string name, out VisualElement fill, out Label value)
{
var row = new VisualElement { name = name + "-meter" };
row.style.marginTop = 9;
var labels = new VisualElement();
labels.style.flexDirection = FlexDirection.Row;
labels.style.justifyContent = Justify.SpaceBetween;
labels.Add(Label(title, 10, new Color(0.72f, 0.78f, 0.8f)));
value = Label("0%", 10, Color.white);
labels.Add(value);
row.Add(labels);
var track = Track(name + "-track", 8);
fill = new VisualElement { name = name + "-fill" };
fill.style.height = Length.Percent(100);
fill.style.width = Length.Percent(0);
track.Add(fill);
row.Add(track);
return row;
}
private static VisualElement Track(string name, float height)
{
var track = new VisualElement { name = name };
track.style.position = Position.Relative;
track.style.height = height;
track.style.backgroundColor = new Color(0.12f, 0.14f, 0.15f, 1f);
return track;
}
private static Label Label(string text, float size, Color color)
{
var label = new Label(text);
label.style.fontSize = size;
label.style.color = color;
label.style.unityFontStyleAndWeight = FontStyle.Bold;
label.style.letterSpacing = 0;
return label;
}
private static string FormatTemperature(float value) =>
value.ToString("0.#", CultureInfo.InvariantCulture) + " K";
private static string FormatPercent(float value) =>
(Mathf.Clamp01(value) * 100).ToString("0", CultureInfo.InvariantCulture) + "%";
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9d50e2bf1d4c43c8a0e5d6af135dd095

View file

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UIElements;
namespace GameCult.Eve.UnityScene.Tests
{
@ -63,6 +65,34 @@ namespace GameCult.Eve.UnityScene.Tests
Assert.That(reconnect.HeatstrokeWeight, Is.Zero);
}
[Test]
public void ThermalHudLowersCockpitAndExposureMetersWithoutProviderTypes()
{
var root = new GameObject("thermal-hud-test");
try
{
var hud = root.AddComponent<EveUnityThermalHudSink>();
hud.ApplyThermalPresentation(new EveUnityThermalPresentationFrame(
"pilot", 340, 0.6f, 0.2f, true, false, 1, 0.5f, 0, 0, "", 0));
Assert.That(hud.Root, Is.Not.Null);
Assert.That(hud.Root!.Q<Label>(), Is.Not.Null);
Assert.That(hud.Root.Q<VisualElement>("heatstroke-fill").style.width.value.value,
Is.EqualTo(60).Within(0.01));
Assert.That(hud.Root.Q<VisualElement>("hypothermia-fill").style.width.value.value,
Is.EqualTo(20).Within(0.01));
Assert.That(hud.Root.Q<VisualElement>("temperature-marker").style.left.value.value,
Is.EqualTo(100).Within(0.01));
hud.ApplyThermalPresentation(default);
Assert.That(hud.Root.style.display.value, Is.EqualTo(DisplayStyle.None));
}
finally
{
UnityEngine.Object.DestroyImmediate(root);
}
}
private static EveUnityPlayableWorldProjection World(params EveUnityPlayableWorldEntity[] entities) =>
new EveUnityPlayableWorldProjection("world", "state", "assets", "input", "camera", "pilot",
"pilot", "move", "focus", "target", "action", entities);