Lower generic docked world presentation
This commit is contained in:
parent
65bab4d2cd
commit
e44fcb0efa
6 changed files with 136 additions and 24 deletions
|
|
@ -190,6 +190,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
string combatActionId = "";
|
||||
string collectedPickupId = "";
|
||||
bool pickupCollected = false;
|
||||
bool docked = false;
|
||||
bool undocked = false;
|
||||
string stationObservedHostileId = "";
|
||||
long initialVersion = 0;
|
||||
float movementDistance = 0f;
|
||||
var commandReceipts = new Dictionary<string, EveUnitySceneCommandReceipt>(StringComparer.Ordinal);
|
||||
|
|
@ -251,6 +254,11 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
seededPickup.GetComponentsInChildren<Transform>(includeInactive: true).Length,
|
||||
Is.GreaterThan(1),
|
||||
"The generic client did not instantiate the provider-authored pickup asset.");
|
||||
var dockAction = provider.CurrentInputCapability.Actions
|
||||
.Single(candidate => string.Equals(candidate.ActionId, "pilot.dock", StringComparison.Ordinal));
|
||||
var undockAction = provider.CurrentInputCapability.Actions
|
||||
.Single(candidate => string.Equals(candidate.ActionId, "pilot.undock", StringComparison.Ordinal));
|
||||
var dock = runtime.SubmitActionIntent(playerId, dockAction.ActionId);
|
||||
var realtimeAction = provider.CurrentInputCapability?.Actions
|
||||
.FirstOrDefault(action => string.Equals(action.ActionId, "simulation.rate.realtime", StringComparison.Ordinal));
|
||||
Assert.That(realtimeAction, Is.Not.Null, "Terminus did not advertise its user-controlled simulation clock.");
|
||||
|
|
@ -268,6 +276,49 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
marker.GetComponentsInChildren<Transform>(includeInactive: true).Length,
|
||||
Is.GreaterThan(1),
|
||||
"The generic client substituted its primitive fallback instead of the provider-authored AssetBundle prefab.");
|
||||
var dockDeadline = Time.realtimeSinceStartup + 12f;
|
||||
while (Time.realtimeSinceStartup < dockDeadline &&
|
||||
(!commandReceipts.TryGetValue(dock.CommandId, out var observedDock) ||
|
||||
runtime.ActiveVersion < observedDock.SourceVersion ||
|
||||
!string.Equals(runtime.ActiveWorld.PresentationMode, "docked", StringComparison.Ordinal)))
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.1f);
|
||||
runtime.Refresh();
|
||||
}
|
||||
Assert.That(commandReceipts.TryGetValue(dock.CommandId, out var dockReceipt), Is.True);
|
||||
Assert.That(dockReceipt.State, Is.EqualTo("accepted").Or.EqualTo("reconciled"), dockReceipt.Message);
|
||||
Assert.That(runtime.ActiveWorld.PresentationMode, Is.EqualTo("docked"));
|
||||
Assert.That(runtime.ActiveWorld.CameraTargetEntityId, Is.Not.Empty.And.Not.EqualTo(playerId));
|
||||
Assert.That(FindMarker(root, runtime.ActiveWorld.CameraTargetEntityId), Is.Not.Null,
|
||||
"The daemon did not include the authoritative dock parent in the docked SoA view.");
|
||||
stationObservedHostileId = provider.CurrentEntityView.Identities
|
||||
.FirstOrDefault(identity => string.Equals(identity.Faction, "raider", StringComparison.Ordinal))?.EntityId ?? "";
|
||||
Assert.That(stationObservedHostileId, Is.Not.Empty,
|
||||
"The docked pilot did not inherit the station's sensor-derived hostile contacts.");
|
||||
Assert.That(runtime.ActiveWorld.SubjectVisible, Is.False);
|
||||
Assert.That(runtime.ActiveWorld.MovementEnabled, Is.False);
|
||||
Assert.That(FindMarker(root, playerId, includeInactive: true).gameObject.activeSelf, Is.False);
|
||||
docked = true;
|
||||
|
||||
var undock = runtime.SubmitActionIntent(playerId, undockAction.ActionId);
|
||||
var undockDeadline = Time.realtimeSinceStartup + 12f;
|
||||
while (Time.realtimeSinceStartup < undockDeadline &&
|
||||
(!commandReceipts.TryGetValue(undock.CommandId, out var observedUndock) ||
|
||||
runtime.ActiveVersion < observedUndock.SourceVersion ||
|
||||
!string.Equals(runtime.ActiveWorld.PresentationMode, "world", StringComparison.Ordinal)))
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.1f);
|
||||
runtime.Refresh();
|
||||
}
|
||||
Assert.That(commandReceipts.TryGetValue(undock.CommandId, out var undockReceipt), Is.True);
|
||||
Assert.That(undockReceipt.State, Is.EqualTo("accepted").Or.EqualTo("reconciled"), undockReceipt.Message);
|
||||
Assert.That(runtime.ActiveWorld.PresentationMode, Is.EqualTo("world"));
|
||||
Assert.That(runtime.ActiveWorld.CameraTargetEntityId, Is.EqualTo(playerId));
|
||||
Assert.That(runtime.ActiveWorld.SubjectVisible, Is.True);
|
||||
Assert.That(runtime.ActiveWorld.MovementEnabled, Is.True);
|
||||
Assert.That(FindMarker(root, playerId).gameObject.activeSelf, Is.True);
|
||||
undocked = true;
|
||||
|
||||
var initialPosition = marker.transform.position;
|
||||
initialVersion = runtime.ActiveVersion;
|
||||
var request = runtime.SubmitMoveVectorIntent(playerId, 1f, 0f, 1f);
|
||||
|
|
@ -337,17 +388,18 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
var visibleMarkers = root.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>();
|
||||
var hostileTargets = visibleMarkers
|
||||
.Where(candidate => candidate.Selectable && !candidate.Controllable &&
|
||||
string.Equals(candidate.EntityKind, "ship", StringComparison.OrdinalIgnoreCase) &&
|
||||
!string.Equals(candidate.Faction, marker.Faction, StringComparison.Ordinal))
|
||||
.OrderBy(candidate => Vector3.Distance(candidate.transform.position, marker.transform.position))
|
||||
.ToArray();
|
||||
Assert.That(
|
||||
hostileTargets,
|
||||
Is.Not.Empty,
|
||||
"No selectable hostile contact was lowered. Visible entities: " +
|
||||
var targetEntityId = hostileTargets.Length > 0
|
||||
? hostileTargets[0].EntityId
|
||||
: stationObservedHostileId;
|
||||
Assert.That(targetEntityId, Is.Not.Empty,
|
||||
"No hostile contact was available from pilot or shared station sensors. Visible entities: " +
|
||||
string.Join(", ", visibleMarkers.Select(candidate =>
|
||||
$"{candidate.EntityId}[kind={candidate.EntityKind},faction={candidate.Faction},selectable={candidate.Selectable}]")));
|
||||
var targetMarker = hostileTargets[0];
|
||||
var focus = runtime.SubmitTargetIntent(playerId, targetMarker.EntityId);
|
||||
var focus = runtime.SubmitTargetIntent(playerId, targetEntityId);
|
||||
var focusVersion = runtime.ActiveVersion;
|
||||
var trajectoryRenderer = root.AddComponent<EveUnityShotTrajectoryRenderer>();
|
||||
runtime.ShotAvailable += shot =>
|
||||
|
|
@ -464,7 +516,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
combatActionId,
|
||||
combatShot,
|
||||
collectedPickupId,
|
||||
pickupCollected);
|
||||
pickupCollected,
|
||||
docked,
|
||||
undocked);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -490,7 +544,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
string combatActionId,
|
||||
EveUnityShotReceipt combatShot,
|
||||
string collectedPickupId,
|
||||
bool pickupCollected)
|
||||
bool pickupCollected,
|
||||
bool docked,
|
||||
bool undocked)
|
||||
{
|
||||
var path = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_FACTS_PATH");
|
||||
if (string.IsNullOrWhiteSpace(path)) return;
|
||||
|
|
@ -509,6 +565,8 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
shotHit = combatShot?.Hit ?? false,
|
||||
pickupVisible = !string.IsNullOrWhiteSpace(collectedPickupId),
|
||||
pickupCollected = pickupCollected,
|
||||
docked = docked,
|
||||
undocked = undocked,
|
||||
pickupId = collectedPickupId ?? "",
|
||||
initialVersion = initialVersion,
|
||||
finalVersion = finalVersion,
|
||||
|
|
@ -535,6 +593,8 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
public bool shotHit;
|
||||
public bool pickupVisible;
|
||||
public bool pickupCollected;
|
||||
public bool docked;
|
||||
public bool undocked;
|
||||
public string pickupId;
|
||||
public long initialVersion;
|
||||
public long finalVersion;
|
||||
|
|
@ -562,9 +622,12 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
}
|
||||
}
|
||||
|
||||
private static EveUnityPlayableWorldEntityMarker FindMarker(GameObject root, string entityId)
|
||||
private static EveUnityPlayableWorldEntityMarker FindMarker(
|
||||
GameObject root,
|
||||
string entityId,
|
||||
bool includeInactive = false)
|
||||
{
|
||||
foreach (var marker in root.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>())
|
||||
foreach (var marker in root.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>(includeInactive))
|
||||
{
|
||||
if (string.Equals(marker.EntityId, entityId, StringComparison.Ordinal))
|
||||
return marker;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ namespace GameCult.Eve.UnityScene
|
|||
private readonly Transform _root;
|
||||
private readonly IEveUnityGameObjectAssetProvider _assetProvider;
|
||||
private readonly Dictionary<string, GameObject> _instances = new Dictionary<string, GameObject>(StringComparer.Ordinal);
|
||||
private string _subjectEntityId = "";
|
||||
private bool _subjectVisible = true;
|
||||
|
||||
public EveUnityGameObjectPlayableWorldSceneSink(
|
||||
Transform root,
|
||||
|
|
@ -37,6 +39,9 @@ namespace GameCult.Eve.UnityScene
|
|||
if (world == null) throw new ArgumentNullException(nameof(world));
|
||||
if (!string.IsNullOrWhiteSpace(world.WorldRootId))
|
||||
_root.name = world.WorldRootId;
|
||||
_subjectEntityId = world.PlayerEntityId;
|
||||
_subjectVisible = world.SubjectVisible;
|
||||
ApplySubjectVisibility();
|
||||
}
|
||||
|
||||
public void UpsertEntity(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)
|
||||
|
|
@ -64,6 +69,7 @@ namespace GameCult.Eve.UnityScene
|
|||
}
|
||||
|
||||
ApplyEntity(instance, entity, asset);
|
||||
ApplySubjectVisibility(instance, entity.EntityId);
|
||||
}
|
||||
|
||||
public void RemoveEntity(string entityId)
|
||||
|
|
@ -140,6 +146,19 @@ namespace GameCult.Eve.UnityScene
|
|||
else
|
||||
UnityEngine.Object.DestroyImmediate(instance);
|
||||
}
|
||||
|
||||
private void ApplySubjectVisibility()
|
||||
{
|
||||
foreach (var pair in _instances)
|
||||
ApplySubjectVisibility(pair.Value, pair.Key);
|
||||
}
|
||||
|
||||
private void ApplySubjectVisibility(GameObject instance, string entityId)
|
||||
{
|
||||
if (instance == null || !string.Equals(entityId, _subjectEntityId, StringComparison.Ordinal))
|
||||
return;
|
||||
instance.SetActive(_subjectVisible);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EveUnitySemanticEntityPresentation : MonoBehaviour
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ namespace GameCult.Eve.UnityScene
|
|||
}
|
||||
|
||||
var camera = cameraTransform != null ? cameraTransform : transform;
|
||||
var player = FindPlayerMarker(resolvedHost, activeWorld.PlayerEntityId);
|
||||
if (player == null)
|
||||
var targetMarker = FindEntityMarker(resolvedHost, activeWorld.CameraTargetEntityId);
|
||||
if (targetMarker == null)
|
||||
return false;
|
||||
|
||||
var cameraComponent = camera.GetComponent<Camera>();
|
||||
|
|
@ -64,8 +64,8 @@ namespace GameCult.Eve.UnityScene
|
|||
{
|
||||
cameraComponent.cullingMask = cullingMask;
|
||||
}
|
||||
var bounds = CalculateVisualBounds(player);
|
||||
var target = bounds?.center ?? player.transform.position;
|
||||
var bounds = CalculateVisualBounds(targetMarker);
|
||||
var target = bounds?.center ?? targetMarker.transform.position;
|
||||
var radius = bounds?.extents.magnitude ?? 0f;
|
||||
var verticalFov = cameraComponent != null ? cameraComponent.fieldOfView : 60f;
|
||||
var framingDistance = radius > 0f
|
||||
|
|
@ -84,9 +84,9 @@ namespace GameCult.Eve.UnityScene
|
|||
return true;
|
||||
}
|
||||
|
||||
private static Bounds? CalculateVisualBounds(EveUnityPlayableWorldEntityMarker player)
|
||||
private static Bounds? CalculateVisualBounds(EveUnityPlayableWorldEntityMarker target)
|
||||
{
|
||||
var renderers = player.GetComponentsInChildren<Renderer>(includeInactive: false);
|
||||
var renderers = target.GetComponentsInChildren<Renderer>(includeInactive: false);
|
||||
Bounds? bounds = null;
|
||||
foreach (var visual in renderers)
|
||||
{
|
||||
|
|
@ -121,17 +121,17 @@ namespace GameCult.Eve.UnityScene
|
|||
return host;
|
||||
}
|
||||
|
||||
private static EveUnityPlayableWorldEntityMarker? FindPlayerMarker(
|
||||
private static EveUnityPlayableWorldEntityMarker? FindEntityMarker(
|
||||
EveUnityPlayableWorldClientHost host,
|
||||
string playerEntityId)
|
||||
string entityId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(playerEntityId))
|
||||
if (string.IsNullOrWhiteSpace(entityId))
|
||||
return null;
|
||||
|
||||
var markers = host.SceneRoot.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>();
|
||||
var markers = host.SceneRoot.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>(includeInactive: true);
|
||||
foreach (var marker in markers)
|
||||
{
|
||||
if (marker != null && marker.EntityId == playerEntityId)
|
||||
if (marker != null && marker.EntityId == entityId)
|
||||
return marker;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace GameCult.Eve.UnityScene
|
|||
DateTimeOffset? issuedAt = null)
|
||||
{
|
||||
var resolvedHost = ResolveHost();
|
||||
if (resolvedHost == null || resolvedHost.ActiveWorld == null)
|
||||
if (resolvedHost == null || resolvedHost.ActiveWorld == null || !resolvedHost.ActiveWorld.MovementEnabled)
|
||||
return null;
|
||||
|
||||
var movement = EveUnityPlayableWorldMoveVector.FromCameraRelativeInput(
|
||||
|
|
|
|||
|
|
@ -125,7 +125,11 @@ namespace GameCult.Eve.UnityScene
|
|||
worldRoot.GetProp("entityViewSchema"),
|
||||
worldRoot.GetProp("zoneRenderPointerId"),
|
||||
worldRoot.GetProp("zoneRenderSchema"),
|
||||
worldRoot.GetProp("inputCapability"));
|
||||
worldRoot.GetProp("inputCapability"),
|
||||
worldRoot.GetProp("cameraTargetEntityId"),
|
||||
ParseBoolean(worldRoot.GetProp("subjectVisible", "true")),
|
||||
ParseBoolean(worldRoot.GetProp("movementEnabled", "true")),
|
||||
worldRoot.GetProp("presentationMode", "world"));
|
||||
}
|
||||
|
||||
private static EveUnityPlayableWorldEntity BuildPlayableEntity(EveSurfaceComponent component)
|
||||
|
|
@ -326,7 +330,11 @@ namespace GameCult.Eve.UnityScene
|
|||
string entityViewSchema = "",
|
||||
string zoneRenderPointerId = "",
|
||||
string zoneRenderSchema = "",
|
||||
string inputCapabilityPointerId = "")
|
||||
string inputCapabilityPointerId = "",
|
||||
string cameraTargetEntityId = "",
|
||||
bool subjectVisible = true,
|
||||
bool movementEnabled = true,
|
||||
string presentationMode = "world")
|
||||
{
|
||||
WorldRootId = worldRootId ?? "";
|
||||
StatePointerId = statePointerId ?? "";
|
||||
|
|
@ -345,6 +353,12 @@ namespace GameCult.Eve.UnityScene
|
|||
ZoneRenderPointerId = zoneRenderPointerId ?? "";
|
||||
ZoneRenderSchema = zoneRenderSchema ?? "";
|
||||
InputCapabilityPointerId = inputCapabilityPointerId ?? "";
|
||||
CameraTargetEntityId = string.IsNullOrWhiteSpace(cameraTargetEntityId)
|
||||
? PlayerEntityId
|
||||
: cameraTargetEntityId;
|
||||
SubjectVisible = subjectVisible;
|
||||
MovementEnabled = movementEnabled;
|
||||
PresentationMode = presentationMode ?? "";
|
||||
}
|
||||
|
||||
public string WorldRootId { get; }
|
||||
|
|
@ -371,6 +385,14 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
public string PlayerEntityId { get; }
|
||||
|
||||
public string CameraTargetEntityId { get; }
|
||||
|
||||
public bool SubjectVisible { get; }
|
||||
|
||||
public bool MovementEnabled { get; }
|
||||
|
||||
public string PresentationMode { get; }
|
||||
|
||||
public string MovementCommand { get; }
|
||||
|
||||
public string FocusCommand { get; }
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(projection.PlayableWorld.InputProfile, Is.EqualTo("arpg-third-person"));
|
||||
Assert.That(projection.PlayableWorld.CameraRig, Is.EqualTo("third-person-orbit"));
|
||||
Assert.That(projection.PlayableWorld.PlayerEntityId, Is.EqualTo("player-vanguard"));
|
||||
Assert.That(projection.PlayableWorld.CameraTargetEntityId, Is.EqualTo("player-vanguard"));
|
||||
Assert.That(projection.PlayableWorld.SubjectVisible, Is.True);
|
||||
Assert.That(projection.PlayableWorld.MovementEnabled, Is.True);
|
||||
Assert.That(projection.PlayableWorld.PresentationMode, Is.EqualTo("world"));
|
||||
Assert.That(projection.PlayableWorld.MovementCommand, Is.EqualTo("aetheria.daemon.move_intent"));
|
||||
Assert.That(projection.PlayableWorld.FocusCommand, Is.EqualTo("aetheria.daemon.focus"));
|
||||
Assert.That(projection.PlayableWorld.TargetCommand, Is.EqualTo("aetheria.daemon.target"));
|
||||
|
|
@ -1419,6 +1423,10 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
["inputProfile"] = "arpg-third-person",
|
||||
["cameraRig"] = "third-person-orbit",
|
||||
["playerEntityId"] = "player-vanguard",
|
||||
["cameraTargetEntityId"] = "player-vanguard",
|
||||
["subjectVisible"] = "true",
|
||||
["movementEnabled"] = "true",
|
||||
["presentationMode"] = "world",
|
||||
["movementCommand"] = "aetheria.daemon.move_intent",
|
||||
["focusCommand"] = "aetheria.daemon.focus",
|
||||
["targetCommand"] = "aetheria.daemon.target",
|
||||
|
|
|
|||
Reference in a new issue