Lower semantic entity presentation state
This commit is contained in:
parent
d6bdeb8fe9
commit
0d97dcf56c
3 changed files with 112 additions and 2 deletions
|
|
@ -117,6 +117,15 @@ namespace GameCult.Eve.UnityScene
|
|||
marker = instance.AddComponent<EveUnityPlayableWorldEntityMarker>();
|
||||
|
||||
marker.Apply(entity, asset);
|
||||
|
||||
if (entity.Props.TryGetValue("presentationState", out var presentationState) &&
|
||||
!string.IsNullOrWhiteSpace(presentationState))
|
||||
{
|
||||
var semanticPresentation = instance.GetComponent<EveUnitySemanticEntityPresentation>();
|
||||
if (semanticPresentation == null)
|
||||
semanticPresentation = instance.AddComponent<EveUnitySemanticEntityPresentation>();
|
||||
semanticPresentation.Apply(entity.Props);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DestroyInstance(GameObject instance)
|
||||
|
|
@ -128,6 +137,69 @@ namespace GameCult.Eve.UnityScene
|
|||
}
|
||||
}
|
||||
|
||||
public sealed class EveUnitySemanticEntityPresentation : MonoBehaviour
|
||||
{
|
||||
private static readonly int Emission = Shader.PropertyToID("_Emission");
|
||||
private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
|
||||
private MaterialPropertyBlock? _properties;
|
||||
private Renderer[] _renderers = Array.Empty<Renderer>();
|
||||
private string _state = "";
|
||||
private float _activePulseSeconds = 1f;
|
||||
private float _triggeredPulseSeconds = 0.25f;
|
||||
private float _activeEmission = 1f;
|
||||
private float _triggeredEmission = 4f;
|
||||
|
||||
public string PresentationState => _state;
|
||||
|
||||
public void Apply(IReadOnlyDictionary<string, string> props)
|
||||
{
|
||||
props ??= new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
_state = Read(props, "presentationState");
|
||||
_activePulseSeconds = Positive(ReadFloat(props, "activePulseSeconds", _activePulseSeconds), 1f);
|
||||
_triggeredPulseSeconds = Positive(ReadFloat(props, "triggeredPulseSeconds", _triggeredPulseSeconds), 0.25f);
|
||||
_activeEmission = Math.Max(0f, ReadFloat(props, "activeEmission", _activeEmission));
|
||||
_triggeredEmission = Math.Max(0f, ReadFloat(props, "triggeredEmission", _triggeredEmission));
|
||||
_renderers = GetComponentsInChildren<Renderer>(true);
|
||||
ApplyAt(Time.time);
|
||||
}
|
||||
|
||||
public void ApplyAt(float timeSeconds)
|
||||
{
|
||||
var triggered = string.Equals(_state, "triggered", StringComparison.Ordinal);
|
||||
var active = triggered || string.Equals(_state, "active", StringComparison.Ordinal);
|
||||
var period = triggered ? _triggeredPulseSeconds : _activePulseSeconds;
|
||||
var peak = triggered ? _triggeredEmission : _activeEmission;
|
||||
var envelope = active
|
||||
? Mathf.Lerp(0.1f, 1f, Mathf.Abs(Mathf.Cos(Mathf.PI * timeSeconds / period)))
|
||||
: 0f;
|
||||
var value = peak * envelope;
|
||||
_properties ??= new MaterialPropertyBlock();
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
if (renderer == null) continue;
|
||||
renderer.GetPropertyBlock(_properties);
|
||||
_properties.SetFloat(Emission, value);
|
||||
_properties.SetColor(EmissionColor, Color.white * value);
|
||||
renderer.SetPropertyBlock(_properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update() => ApplyAt(Time.time);
|
||||
|
||||
private static string Read(IReadOnlyDictionary<string, string> props, string key) =>
|
||||
props.TryGetValue(key, out var value) ? value ?? "" : "";
|
||||
|
||||
private static float ReadFloat(IReadOnlyDictionary<string, string> props, string key, float fallback) =>
|
||||
props.TryGetValue(key, out var value) && float.TryParse(value,
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out var parsed)
|
||||
? parsed
|
||||
: fallback;
|
||||
|
||||
private static float Positive(float value, float fallback) => value > 0f ? value : fallback;
|
||||
}
|
||||
|
||||
public sealed class EveUnityResourcesAssetProvider : IEveUnityGameObjectAssetProvider
|
||||
{
|
||||
public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset)
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ namespace GameCult.Eve.UnityScene
|
|||
worldRoot.GetProp("assetManifest", worldRoot.GetProp("assetManifestUri")),
|
||||
worldRoot.GetProp("inputProfile"),
|
||||
worldRoot.GetProp("cameraRig"),
|
||||
worldRoot.GetProp("viewId"),
|
||||
worldRoot.GetProp("playerEntityId"),
|
||||
worldRoot.GetProp("movementCommand"),
|
||||
worldRoot.GetProp("focusCommand"),
|
||||
|
|
@ -142,7 +143,8 @@ namespace GameCult.Eve.UnityScene
|
|||
component.GetProp("focusCommand"),
|
||||
component.GetProp("moveCommand"),
|
||||
component.GetProp("targetCommand"),
|
||||
component.GetProp("actionCommand"));
|
||||
component.GetProp("actionCommand"),
|
||||
component.Props);
|
||||
}
|
||||
|
||||
private static EveSurfaceComponent? FindFirst(
|
||||
|
|
@ -308,6 +310,7 @@ namespace GameCult.Eve.UnityScene
|
|||
string assetManifest,
|
||||
string inputProfile,
|
||||
string cameraRig,
|
||||
string viewId,
|
||||
string playerEntityId,
|
||||
string movementCommand,
|
||||
string focusCommand,
|
||||
|
|
@ -320,6 +323,7 @@ namespace GameCult.Eve.UnityScene
|
|||
AssetManifest = assetManifest ?? "";
|
||||
InputProfile = inputProfile ?? "";
|
||||
CameraRig = cameraRig ?? "";
|
||||
ViewId = viewId ?? "";
|
||||
PlayerEntityId = playerEntityId ?? "";
|
||||
MovementCommand = movementCommand ?? "";
|
||||
FocusCommand = focusCommand ?? "";
|
||||
|
|
@ -338,6 +342,8 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
public string CameraRig { get; }
|
||||
|
||||
public string ViewId { get; }
|
||||
|
||||
public string PlayerEntityId { get; }
|
||||
|
||||
public string MovementCommand { get; }
|
||||
|
|
@ -372,7 +378,8 @@ namespace GameCult.Eve.UnityScene
|
|||
string focusCommand,
|
||||
string moveCommand,
|
||||
string targetCommand,
|
||||
string actionCommand)
|
||||
string actionCommand,
|
||||
IReadOnlyDictionary<string, string>? props = null)
|
||||
{
|
||||
NodeId = nodeId ?? "";
|
||||
EntityId = entityId ?? "";
|
||||
|
|
@ -391,6 +398,7 @@ namespace GameCult.Eve.UnityScene
|
|||
MoveCommand = moveCommand ?? "";
|
||||
TargetCommand = targetCommand ?? "";
|
||||
ActionCommand = actionCommand ?? "";
|
||||
Props = props ?? new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public string NodeId { get; }
|
||||
|
|
@ -426,6 +434,8 @@ namespace GameCult.Eve.UnityScene
|
|||
public string TargetCommand { get; }
|
||||
|
||||
public string ActionCommand { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, string> Props { get; }
|
||||
}
|
||||
|
||||
public sealed class EveUnitySceneNode
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(player.PositionZ, Is.EqualTo(0f));
|
||||
Assert.That(player.Controllable, Is.True);
|
||||
Assert.That(player.MoveCommand, Is.EqualTo("aetheria.daemon.move_intent"));
|
||||
Assert.That(player.Props["presentationState"], Is.EqualTo("active"));
|
||||
|
||||
var raider = FindEntity(projection.PlayableWorld, "raider-scout");
|
||||
Assert.That(raider.EntityKind, Is.EqualTo("enemy"));
|
||||
|
|
@ -95,6 +96,32 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(raider.TargetCommand, Is.EqualTo("aetheria.daemon.target"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SemanticEntityPresentationConsumesProviderPulseState()
|
||||
{
|
||||
var instance = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
try
|
||||
{
|
||||
var presentation = instance.AddComponent<EveUnitySemanticEntityPresentation>();
|
||||
presentation.Apply(new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["presentationState"] = "triggered",
|
||||
["activePulseSeconds"] = "1",
|
||||
["triggeredPulseSeconds"] = "0.25",
|
||||
["activeEmission"] = "100",
|
||||
["triggeredEmission"] = "1000"
|
||||
});
|
||||
presentation.ApplyAt(0.125f);
|
||||
|
||||
Assert.That(presentation.PresentationState, Is.EqualTo("triggered"));
|
||||
Assert.That(instance.GetComponent<Renderer>().HasPropertyBlock(), Is.True);
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(instance);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes()
|
||||
{
|
||||
|
|
@ -1324,6 +1351,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
["position"] = position,
|
||||
["rotationY"] = rotationY,
|
||||
["radius"] = radius,
|
||||
["presentationState"] = "active",
|
||||
["selectable"] = selectable ? "true" : "false",
|
||||
["controllable"] = controllable ? "true" : "false",
|
||||
["focusCommand"] = focusCommand,
|
||||
|
|
|
|||
Reference in a new issue