Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 56 additions & 57 deletions TrimIndicator/TrimIndicatorAddon.cs
Original file line number Diff line number Diff line change
@@ -1,63 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace TrimIndicator
{
[KSPAddon(KSPAddon.Startup.Flight, once: false)]
public class TrimIndicatorAddon : MonoBehaviour
{
void Start()
{
LoadSettings();

var gaugesObject = FindObjectOfType<KSP.UI.Screens.Flight.LinearControlGauges>().gameObject;

_pitchTrimLabel = new TrimLabel(gaugesObject, new Vector3(34F, -15.5F), isVertical: true);
_yawTrimLabel = new TrimLabel(gaugesObject, new Vector3(-24F, -63.5F), isVertical: false);
_rollTrimLabel = new TrimLabel(gaugesObject, new Vector3(-24F, -25F), isVertical: false);

if(_showWheelTrim)
{
_wheelThrottleTrimLabel = new TrimLabel(gaugesObject, new Vector3(62F, -15.5F), isVertical: true);
_wheelSteerTrimLabel = new TrimLabel(gaugesObject, new Vector3(-24F, -76F), isVertical: false);
}
}

void Update()
{
var ctrlState = FlightInputHandler.state;

_pitchTrimLabel?.SetValue(ctrlState?.pitchTrim ?? 0);
_yawTrimLabel?.SetValue(ctrlState?.yawTrim ?? 0);
_rollTrimLabel?.SetValue(ctrlState?.rollTrim ?? 0);
_wheelThrottleTrimLabel?.SetValue(ctrlState?.wheelThrottleTrim ?? 0);
_wheelSteerTrimLabel?.SetValue(-(ctrlState?.wheelSteerTrim ?? 0));
}

void LoadSettings()
{
try
{
var settings = ConfigNode.Load(SettingsFilePath);
settings.TryGetValue("ShowWheelTrim", ref _showWheelTrim);
}
catch(Exception exception)
{
print($"{nameof(TrimIndicator)}: Cannot load settings. {exception}");
}
}

bool _showWheelTrim;

TrimLabel _pitchTrimLabel;
TrimLabel _yawTrimLabel;
TrimLabel _rollTrimLabel;
TrimLabel _wheelThrottleTrimLabel;
TrimLabel _wheelSteerTrimLabel;

static readonly string SettingsFilePath =
$"{KSPUtil.ApplicationRootPath}GameData/{nameof(TrimIndicator)}/{nameof(TrimIndicator)}.settings";
}
[KSPAddon(KSPAddon.Startup.Flight, once: false)]
public class TrimIndicatorAddon : MonoBehaviour
{
void Start()
{
string SettingsFilePath =
$"{KSPUtil.ApplicationRootPath}GameData/{nameof(TrimIndicator)}/{nameof(TrimIndicator)}.settings";
LoadSettings(SettingsFilePath);

var gaugesObject = FindObjectOfType<KSP.UI.Screens.Flight.LinearControlGauges>().gameObject;

_pitchTrimLabel = new TrimLabel(gaugesObject, new Vector3(34F, -15.5F), isVertical: true);
_yawTrimLabel = new TrimLabel(gaugesObject, new Vector3(-24F, -63.5F), isVertical: false);
_rollTrimLabel = new TrimLabel(gaugesObject, new Vector3(-24F, -25F), isVertical: false);

if (_showWheelTrim)
{
_wheelThrottleTrimLabel = new TrimLabel(gaugesObject, new Vector3(62F, -15.5F), isVertical: true);
_wheelSteerTrimLabel = new TrimLabel(gaugesObject, new Vector3(-24F, -76F), isVertical: false);
}
}

void Update()
{
var ctrlState = FlightInputHandler.state;

_pitchTrimLabel?.SetValue(ctrlState?.pitchTrim ?? 0);
_yawTrimLabel?.SetValue(ctrlState?.yawTrim ?? 0);
_rollTrimLabel?.SetValue(ctrlState?.rollTrim ?? 0);
_wheelThrottleTrimLabel?.SetValue(ctrlState?.wheelThrottleTrim ?? 0);
_wheelSteerTrimLabel?.SetValue(-(ctrlState?.wheelSteerTrim ?? 0));
}

void LoadSettings(string path)
{
try
{
var settings = ConfigNode.Load(path);
settings.TryGetValue("ShowWheelTrim", ref _showWheelTrim);
}
catch (Exception exception)
{
print($"{nameof(TrimIndicator)}: Cannot load settings. {exception}");
}
}

bool _showWheelTrim;

TrimLabel _pitchTrimLabel;
TrimLabel _yawTrimLabel;
TrimLabel _rollTrimLabel;
TrimLabel _wheelThrottleTrimLabel;
TrimLabel _wheelSteerTrimLabel;


}
}
204 changes: 102 additions & 102 deletions TrimIndicator/TrimLabel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,106 +7,106 @@

namespace TrimIndicator
{
class TrimLabel
{
public TrimLabel(GameObject parent, Vector3 location, bool isVertical)
{
_sliderDirection = isVertical ? Vector3.up : Vector3.right;
_sliderAmplitude = isVertical ? VerticalSliderAmplitude : HorizontalSliderAmplitude;
var textAlignment = isVertical ? TextAlignmentOptions.BaselineLeft : TextAlignmentOptions.Baseline;

var baseObject = MakeDummyObject(parent, location);
_slider = MakeDummyObject(baseObject);

_textMeshes = new[]
{
MakeTextMesh(_slider, -OutlineWidth * _sliderDirection, BackgroundColor, textAlignment),
MakeTextMesh(_slider, +OutlineWidth * _sliderDirection, BackgroundColor, textAlignment),
MakeTextMesh(_slider, Vector3.zero, ForegroundColor, textAlignment),
};
}

public void SetValue(float value)
{
if(value != _lastValue)
{
int steps = (int)Math.Round(value / Step);
string text = steps != 0
? (steps < 0 ? "−" : "+") + Math.Abs(steps).ToString(CultureInfo.InvariantCulture)
: string.Empty;

_slider.transform.localPosition = _sliderAmplitude * GetSliderPosition(value) * _sliderDirection;

foreach(var textMesh in _textMeshes)
textMesh.text = text;

_lastValue = value;
}
}

readonly Vector3 _sliderDirection;
readonly float _sliderAmplitude;
readonly GameObject _slider;
readonly IEnumerable<TextMeshProUGUI> _textMeshes;

float _lastValue;


static GameObject MakeObject(GameObject parent, Vector3 relativeLocation)
{
var gameObject = new GameObject { layer = LayerMask.NameToLayer("UI") };
gameObject.transform.SetParent(parent.transform, false);
gameObject.transform.localPosition = relativeLocation;
return gameObject;
}

static GameObject MakeDummyObject(GameObject parent, Vector3 relativeLocation = default(Vector3))
{
var gameObject = MakeObject(parent, relativeLocation);
// Just an empty text mesh. Consider replacing by something more adequate
var textMesh = gameObject.AddComponent<TextMeshProUGUI>();
textMesh.autoSizeTextContainer = true;
return gameObject;
}

static TextMeshProUGUI MakeTextMesh(GameObject parent, Vector3 relativeLocation, Color color, TextAlignmentOptions alignment)
{
var gameObject = MakeObject(parent, relativeLocation);

var textMesh = gameObject.AddComponent<TextMeshProUGUI>();
textMesh.autoSizeTextContainer = true;
textMesh.isOverlay = true;
textMesh.enableWordWrapping = false;
textMesh.alignment = alignment;
textMesh.font = Font;
textMesh.fontMaterial = FontMaterial;
textMesh.fontSize = FontSize;
textMesh.color = color;

return textMesh;
}

static float GetSliderPosition(float value) =>
(float)(Math.Atan(value * SliderPositionSteepness) / Math.Atan(SliderPositionSteepness));

static TMP_FontAsset _font;
static TMP_FontAsset Font => _font ??=
Resources.LoadAll<TMP_FontAsset>("Fonts")
.FirstOrDefault(f => f.name == "Calibri SDF");

static Material _fontMaterial;
static Material FontMaterial => _fontMaterial ??=
Resources.LoadAll<Material>("Fonts")
.FirstOrDefault(f => f.name == "Calibri SDF Material");

static readonly Color BackgroundColor = new Color(0.235F, 0.274F, 0.310F); // #3C464F
static readonly Color ForegroundColor = new Color(0.827F, 0.827F, 0.827F); // #D3D3D3
const float FontSize = 17F;
const float OutlineWidth = 1.5F;
const float HorizontalSliderAmplitude = 39F;
const float VerticalSliderAmplitude = 49F;
const double SliderPositionSteepness = 20;

const float Step = 0.002F;
}
class TrimLabel
{
public TrimLabel(GameObject parent, Vector3 location, bool isVertical)
{
_sliderDirection = isVertical ? Vector3.up : Vector3.right;
_sliderAmplitude = isVertical ? VerticalSliderAmplitude : HorizontalSliderAmplitude;
var textAlignment = isVertical ? TextAlignmentOptions.BaselineLeft : TextAlignmentOptions.Baseline;

var baseObject = MakeDummyObject(parent, location);
_slider = MakeDummyObject(baseObject);

_textMeshes = new[]
{
MakeTextMesh(_slider, -OutlineWidth * _sliderDirection, BackgroundColor, textAlignment),
MakeTextMesh(_slider, +OutlineWidth * _sliderDirection, BackgroundColor, textAlignment),
MakeTextMesh(_slider, Vector3.zero, ForegroundColor, textAlignment),
};
}

public void SetValue(float value)
{
if (value != _lastValue)
{
int steps = (int)Math.Round(value / Step);
string text = steps != 0
? (steps < 0 ? "−" : "+") + Math.Abs(steps).ToString(CultureInfo.InvariantCulture)
: string.Empty;

_slider.transform.localPosition = _sliderAmplitude * GetSliderPosition(value) * _sliderDirection;

foreach (var textMesh in _textMeshes)
textMesh.text = text;

_lastValue = value;
}
}

readonly Vector3 _sliderDirection;
readonly float _sliderAmplitude;
readonly GameObject _slider;
readonly IEnumerable<TextMeshProUGUI> _textMeshes;

float _lastValue;


static GameObject MakeObject(GameObject parent, Vector3 relativeLocation)
{
var gameObject = new GameObject { layer = LayerMask.NameToLayer("UI") };
gameObject.transform.SetParent(parent.transform, false);
gameObject.transform.localPosition = relativeLocation;
return gameObject;
}

static GameObject MakeDummyObject(GameObject parent, Vector3 relativeLocation = default(Vector3))
{
var gameObject = MakeObject(parent, relativeLocation);
// Just an empty text mesh. Consider replacing by something more adequate
var textMesh = gameObject.AddComponent<TextMeshProUGUI>();
textMesh.autoSizeTextContainer = true;
return gameObject;
}

static TextMeshProUGUI MakeTextMesh(GameObject parent, Vector3 relativeLocation, Color color, TextAlignmentOptions alignment)
{
var gameObject = MakeObject(parent, relativeLocation);

var textMesh = gameObject.AddComponent<TextMeshProUGUI>();
textMesh.autoSizeTextContainer = true;
textMesh.isOverlay = true;
textMesh.enableWordWrapping = false;
textMesh.alignment = alignment;
textMesh.font = Font;
textMesh.fontMaterial = FontMaterial;
textMesh.fontSize = FontSize;
textMesh.color = color;

return textMesh;
}

static float GetSliderPosition(float value) =>
(float)(Math.Atan(value * SliderPositionSteepness) / Math.Atan(SliderPositionSteepness));

static TMP_FontAsset _font;
static TMP_FontAsset Font => _font ??=
Resources.LoadAll<TMP_FontAsset>("Fonts")
.FirstOrDefault(f => f.name == "Calibri SDF");

static Material _fontMaterial;
static Material FontMaterial => _fontMaterial ??=
Resources.LoadAll<Material>("Fonts")
.FirstOrDefault(f => f.name == "Calibri SDF Material");

static readonly Color BackgroundColor = new Color(0.235F, 0.274F, 0.310F); // #3C464F
static readonly Color ForegroundColor = new Color(0.827F, 0.827F, 0.827F); // #D3D3D3
const float FontSize = 17F;
const float OutlineWidth = 1.5F;
const float HorizontalSliderAmplitude = 39F;
const float VerticalSliderAmplitude = 49F;
const double SliderPositionSteepness = 20;

const float Step = 0.002F;
}
}