Lower provider-owned planar camera composition
This commit is contained in:
parent
466cd10c71
commit
ab51d72c5e
4 changed files with 136 additions and 29 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
#nullable enable
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ namespace GameCult.Eve.UnityScene
|
|||
return false;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(activeWorld.CameraRig) &&
|
||||
activeWorld.CameraRig != "planar.top-down-follow.v1" &&
|
||||
activeWorld.CameraRig != "arpg.orbital-follow.v1" &&
|
||||
activeWorld.CameraRig != "third-person-orbit")
|
||||
{
|
||||
|
|
@ -53,7 +55,10 @@ namespace GameCult.Eve.UnityScene
|
|||
}
|
||||
|
||||
var camera = cameraTransform != null ? cameraTransform : transform;
|
||||
var player = FindPlayer(resolvedHost, activeWorld.PlayerEntityId);
|
||||
var targetEntityId = string.IsNullOrWhiteSpace(activeWorld.CameraTargetEntityId)
|
||||
? activeWorld.PlayerEntityId
|
||||
: activeWorld.CameraTargetEntityId;
|
||||
var player = FindEntity(resolvedHost, targetEntityId);
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
|
|
@ -69,10 +74,12 @@ namespace GameCult.Eve.UnityScene
|
|||
}
|
||||
cameraComponent.cullingMask = cullingMask;
|
||||
}
|
||||
if (activeWorld.CameraRig == "planar.top-down-follow.v1")
|
||||
return ApplyPlanarTopDown(activeWorld, camera, cameraComponent, player.position, deltaTime);
|
||||
|
||||
var bounds = CalculateVisualBounds(
|
||||
player,
|
||||
cameraComponent != null ? cameraComponent.cullingMask : -1,
|
||||
FindSelectedTarget(resolvedHost));
|
||||
cameraComponent != null ? cameraComponent.cullingMask : -1);
|
||||
var target = bounds?.center ?? player.position;
|
||||
var radius = bounds?.extents.magnitude ?? 0f;
|
||||
var verticalFov = cameraComponent != null ? cameraComponent.fieldOfView : 60f;
|
||||
|
|
@ -92,12 +99,45 @@ namespace GameCult.Eve.UnityScene
|
|||
return true;
|
||||
}
|
||||
|
||||
private static Bounds? CalculateVisualBounds(Transform player, int cullingMask, Transform? selectedTarget)
|
||||
private static bool ApplyPlanarTopDown(
|
||||
EveUnityPlayableWorldProjection world,
|
||||
Transform camera,
|
||||
Camera? cameraComponent,
|
||||
Vector3 target,
|
||||
float deltaTime)
|
||||
{
|
||||
var resolvedDistance = Mathf.Max(0.1f, world.CameraDistance);
|
||||
var verticalFov = Mathf.Clamp(world.CameraVerticalFieldOfViewDegrees, 1f, 179f);
|
||||
if (cameraComponent != null)
|
||||
cameraComponent.fieldOfView = verticalFov;
|
||||
var aspect = cameraComponent != null ? cameraComponent.aspect : 16f / 9f;
|
||||
var verticalHalfExtent = resolvedDistance * Mathf.Tan(verticalFov * 0.5f * Mathf.Deg2Rad);
|
||||
var horizontalHalfExtent = verticalHalfExtent * Mathf.Max(0.01f, aspect);
|
||||
var screenOffset = new Vector3(
|
||||
(Mathf.Clamp01(world.CameraTargetScreenX) - 0.5f) * 2f * horizontalHalfExtent,
|
||||
0f,
|
||||
(Mathf.Clamp01(world.CameraTargetScreenY) - 0.5f) * 2f * verticalHalfExtent);
|
||||
var desiredPosition = target + (Vector3.up * resolvedDistance) - screenOffset;
|
||||
var damping = Mathf.Max(0f, world.CameraPositionDamping);
|
||||
var t = deltaTime <= 0f || damping <= 0f
|
||||
? 1f
|
||||
: 1f - Mathf.Exp(-damping * deltaTime);
|
||||
camera.position = Vector3.Lerp(camera.position, desiredPosition, t);
|
||||
camera.rotation = Quaternion.LookRotation(Vector3.down, Vector3.forward);
|
||||
|
||||
RenderSettings.ambientMode = AmbientMode.Flat;
|
||||
RenderSettings.ambientLight = new Color(
|
||||
Mathf.Max(0f, world.AmbientLightR * world.AmbientLightIntensity),
|
||||
Mathf.Max(0f, world.AmbientLightG * world.AmbientLightIntensity),
|
||||
Mathf.Max(0f, world.AmbientLightB * world.AmbientLightIntensity),
|
||||
1f);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Bounds? CalculateVisualBounds(Transform player, int cullingMask)
|
||||
{
|
||||
Bounds? bounds = null;
|
||||
foreach (var root in selectedTarget == null
|
||||
? new[] { player }
|
||||
: new[] { player, selectedTarget! })
|
||||
foreach (var root in new[] { player })
|
||||
{
|
||||
foreach (var visual in root.GetComponentsInChildren<Renderer>(includeInactive: false))
|
||||
{
|
||||
|
|
@ -122,17 +162,6 @@ namespace GameCult.Eve.UnityScene
|
|||
return bounds;
|
||||
}
|
||||
|
||||
private static Transform? FindSelectedTarget(EveUnityPlayableWorldClientHost host)
|
||||
{
|
||||
var combat = EveUnityCombatPresentation.Find(host.ActiveProjection);
|
||||
if (combat == null || string.IsNullOrWhiteSpace(combat.SelectedTargetEntityId))
|
||||
return null;
|
||||
if (host.PresentedEntities?.CurrentGeneration != null &&
|
||||
host.PresentedEntities.TryGetByEntityId(combat.SelectedTargetEntityId, out var presented))
|
||||
return presented.Transform;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (driveInLateUpdate)
|
||||
|
|
@ -148,7 +177,7 @@ namespace GameCult.Eve.UnityScene
|
|||
return host;
|
||||
}
|
||||
|
||||
private static Transform? FindPlayer(
|
||||
private static Transform? FindEntity(
|
||||
EveUnityPlayableWorldClientHost host,
|
||||
string playerEntityId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -126,7 +126,15 @@ namespace GameCult.Eve.UnityScene
|
|||
worldRoot.GetProp("entityViewSchema"),
|
||||
worldRoot.GetProp("zoneRenderPointerId"),
|
||||
worldRoot.GetProp("zoneRenderSchema"),
|
||||
ParseStringList(worldRoot.GetProp("excludedRenderChannels")));
|
||||
ParseStringList(worldRoot.GetProp("excludedRenderChannels")),
|
||||
worldRoot.GetProp("cameraTargetEntityId"),
|
||||
ParseFloat(worldRoot.GetProp("cameraDistance"), 0f),
|
||||
ParseFloat(worldRoot.GetProp("cameraVerticalFieldOfViewDegrees"), 0f),
|
||||
ParseFloat(worldRoot.GetProp("cameraTargetScreenX"), 0.5f),
|
||||
ParseFloat(worldRoot.GetProp("cameraTargetScreenY"), 0.5f),
|
||||
ParseFloat(worldRoot.GetProp("cameraPositionDamping"), 0f),
|
||||
ParseVector3(worldRoot.GetProp("ambientLightColor")),
|
||||
ParseFloat(worldRoot.GetProp("ambientLightIntensity"), 1f));
|
||||
}
|
||||
|
||||
private static EveUnityPlayableWorldEntity BuildPlayableEntity(EveSurfaceComponent component)
|
||||
|
|
@ -337,7 +345,15 @@ namespace GameCult.Eve.UnityScene
|
|||
string entityViewSchema = "",
|
||||
string zoneRenderPointerId = "",
|
||||
string zoneRenderSchema = "",
|
||||
IReadOnlyList<string>? excludedRenderChannels = null)
|
||||
IReadOnlyList<string>? excludedRenderChannels = null,
|
||||
string cameraTargetEntityId = "",
|
||||
float cameraDistance = 0f,
|
||||
float cameraVerticalFieldOfViewDegrees = 0f,
|
||||
float cameraTargetScreenX = 0.5f,
|
||||
float cameraTargetScreenY = 0.5f,
|
||||
float cameraPositionDamping = 0f,
|
||||
(float r, float g, float b) ambientLightColor = default,
|
||||
float ambientLightIntensity = 1f)
|
||||
{
|
||||
WorldRootId = worldRootId ?? "";
|
||||
StatePointerId = statePointerId ?? "";
|
||||
|
|
@ -356,6 +372,16 @@ namespace GameCult.Eve.UnityScene
|
|||
ZoneRenderPointerId = zoneRenderPointerId ?? "";
|
||||
ZoneRenderSchema = zoneRenderSchema ?? "";
|
||||
ExcludedRenderChannels = excludedRenderChannels ?? Array.Empty<string>();
|
||||
CameraTargetEntityId = cameraTargetEntityId ?? "";
|
||||
CameraDistance = cameraDistance;
|
||||
CameraVerticalFieldOfViewDegrees = cameraVerticalFieldOfViewDegrees;
|
||||
CameraTargetScreenX = cameraTargetScreenX;
|
||||
CameraTargetScreenY = cameraTargetScreenY;
|
||||
CameraPositionDamping = cameraPositionDamping;
|
||||
AmbientLightR = ambientLightColor.r;
|
||||
AmbientLightG = ambientLightColor.g;
|
||||
AmbientLightB = ambientLightColor.b;
|
||||
AmbientLightIntensity = ambientLightIntensity;
|
||||
}
|
||||
|
||||
public string WorldRootId { get; }
|
||||
|
|
@ -376,6 +402,26 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
public string CameraRig { get; }
|
||||
|
||||
public string CameraTargetEntityId { get; }
|
||||
|
||||
public float CameraDistance { get; }
|
||||
|
||||
public float CameraVerticalFieldOfViewDegrees { get; }
|
||||
|
||||
public float CameraTargetScreenX { get; }
|
||||
|
||||
public float CameraTargetScreenY { get; }
|
||||
|
||||
public float CameraPositionDamping { get; }
|
||||
|
||||
public float AmbientLightR { get; }
|
||||
|
||||
public float AmbientLightG { get; }
|
||||
|
||||
public float AmbientLightB { get; }
|
||||
|
||||
public float AmbientLightIntensity { get; }
|
||||
|
||||
public string ViewId { get; }
|
||||
|
||||
public string PlayerEntityId { get; }
|
||||
|
|
|
|||
|
|
@ -398,7 +398,17 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(projection.PlayableWorld.ZoneRenderPointerId, Is.EqualTo("cultmesh://aetheria/world/zone-render"));
|
||||
Assert.That(projection.PlayableWorld.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
|
||||
Assert.That(projection.PlayableWorld.InputProfile, Is.EqualTo("arpg-third-person"));
|
||||
Assert.That(projection.PlayableWorld.CameraRig, Is.EqualTo("third-person-orbit"));
|
||||
Assert.That(projection.PlayableWorld.CameraRig, Is.EqualTo("planar.top-down-follow.v1"));
|
||||
Assert.That(projection.PlayableWorld.CameraTargetEntityId, Is.EqualTo("player-vanguard"));
|
||||
Assert.That(projection.PlayableWorld.CameraDistance, Is.EqualTo(150f));
|
||||
Assert.That(projection.PlayableWorld.CameraVerticalFieldOfViewDegrees, Is.EqualTo(60f));
|
||||
Assert.That(projection.PlayableWorld.CameraTargetScreenX, Is.EqualTo(0.9f));
|
||||
Assert.That(projection.PlayableWorld.CameraTargetScreenY, Is.EqualTo(0.55f));
|
||||
Assert.That(projection.PlayableWorld.CameraPositionDamping, Is.EqualTo(5f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightR, Is.EqualTo(0.2f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightG, Is.EqualTo(0.2f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightB, Is.EqualTo(0.2f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightIntensity, Is.EqualTo(1f));
|
||||
Assert.That(projection.PlayableWorld.ExcludedRenderChannels, Is.EqualTo(new[] { "map" }));
|
||||
Assert.That(projection.PlayableWorld.PlayerEntityId, Is.EqualTo("player-vanguard"));
|
||||
Assert.That(projection.PlayableWorld.MovementCommand, Is.EqualTo("aetheria.daemon.move_intent"));
|
||||
|
|
@ -1027,6 +1037,8 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
var hostObject = new GameObject("generic-eve-client");
|
||||
var cameraObject = new GameObject("generic-eve-camera");
|
||||
hostObject.SetActive(false);
|
||||
var ambientMode = RenderSettings.ambientMode;
|
||||
var ambientLight = RenderSettings.ambientLight;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -1051,6 +1063,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
rig.CameraTransform = cameraObject.transform;
|
||||
rig.RenderPolicySource = new FixedRenderChannelPolicy("map", 14);
|
||||
var camera = cameraObject.AddComponent<Camera>();
|
||||
camera.aspect = 16f / 9f;
|
||||
|
||||
var player = rootObject.GetComponentInChildren<EveUnityPlayableWorldEntityMarker>();
|
||||
Assert.That(player, Is.Not.Null);
|
||||
|
|
@ -1061,14 +1074,25 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
|
||||
Assert.That(rig.ApplyRig(0f), Is.True);
|
||||
Assert.That(camera.cullingMask & (1 << 14), Is.Zero);
|
||||
Assert.That(cameraObject.transform.position.y, Is.GreaterThan(0f));
|
||||
Assert.That(camera.fieldOfView, Is.EqualTo(60f).Within(0.001f));
|
||||
Assert.That(cameraObject.transform.position.y - player.transform.position.y, Is.EqualTo(150f).Within(0.001f));
|
||||
var viewport = camera.WorldToViewportPoint(player.transform.position);
|
||||
Assert.That(viewport.x, Is.EqualTo(0.9f).Within(0.001f));
|
||||
Assert.That(viewport.y, Is.EqualTo(0.55f).Within(0.001f));
|
||||
Assert.That(RenderSettings.ambientMode, Is.EqualTo(UnityEngine.Rendering.AmbientMode.Flat));
|
||||
Assert.That(RenderSettings.ambientLight.r, Is.EqualTo(0.2f).Within(0.001f));
|
||||
|
||||
var beforeFollow = cameraObject.transform.position;
|
||||
player.transform.position += Vector3.right * 10f;
|
||||
Assert.That(rig.ApplyRig(0.1f), Is.True);
|
||||
Assert.That(
|
||||
Vector3.Distance(cameraObject.transform.position, player.transform.position),
|
||||
Is.LessThan(50f),
|
||||
"An excluded map glyph changed pilot-camera framing.");
|
||||
cameraObject.transform.position.x - beforeFollow.x,
|
||||
Is.EqualTo(10f * (1f - Mathf.Exp(-0.5f))).Within(0.001f));
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenderSettings.ambientMode = ambientMode;
|
||||
RenderSettings.ambientLight = ambientLight;
|
||||
UnityEngine.Object.DestroyImmediate(cameraObject);
|
||||
UnityEngine.Object.DestroyImmediate(hostObject);
|
||||
UnityEngine.Object.DestroyImmediate(rootObject);
|
||||
|
|
@ -1122,7 +1146,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(firstPresentation.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
|
||||
Assert.That(firstPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
|
||||
Assert.That(firstPresentation.InputProfile, Is.EqualTo("arpg-third-person"));
|
||||
Assert.That(firstPresentation.CameraRig, Is.EqualTo("third-person-orbit"));
|
||||
Assert.That(firstPresentation.CameraRig, Is.EqualTo("planar.top-down-follow.v1"));
|
||||
Assert.That(firstPresentation.UpsertedEntities, Is.EqualTo(3));
|
||||
Assert.That(firstPresentation.RemovedEntities, Is.EqualTo(0));
|
||||
Assert.That(firstPresentation.ActiveEntities, Is.EqualTo(3));
|
||||
|
|
@ -1678,7 +1702,15 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
["zoneRenderSchema"] = "gamecult.aetheria.zone_render.v1",
|
||||
["assetManifest"] = "cultmesh://aetheria/assets/manifest",
|
||||
["inputProfile"] = "arpg-third-person",
|
||||
["cameraRig"] = "third-person-orbit",
|
||||
["cameraRig"] = "planar.top-down-follow.v1",
|
||||
["cameraTargetEntityId"] = "player-vanguard",
|
||||
["cameraDistance"] = "150",
|
||||
["cameraVerticalFieldOfViewDegrees"] = "60",
|
||||
["cameraTargetScreenX"] = "0.9",
|
||||
["cameraTargetScreenY"] = "0.55",
|
||||
["cameraPositionDamping"] = "5",
|
||||
["ambientLightColor"] = "0.2,0.2,0.2",
|
||||
["ambientLightIntensity"] = "1",
|
||||
["excludedRenderChannels"] = "map",
|
||||
["playerEntityId"] = "player-vanguard",
|
||||
["movementCommand"] = "aetheria.daemon.move_intent",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "org.gamecult.eve.unity-scene",
|
||||
"displayName": "Eve Unity Scene Lowering",
|
||||
"version": "0.3.21",
|
||||
"version": "0.3.22",
|
||||
"unity": "2021.3",
|
||||
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
|
||||
"dependencies": {
|
||||
|
|
|
|||
Reference in a new issue