Resolve combat effects by presentation role
This commit is contained in:
parent
cf8befbe33
commit
d1240f9fab
6 changed files with 67 additions and 5 deletions
|
|
@ -67,6 +67,12 @@ namespace GameCult.Eve.UnityScene
|
|||
return entry;
|
||||
return null;
|
||||
}
|
||||
|
||||
public EveUnityPlayableWorldAssetManifestEntry? FindByPresentationRole(string presentationRole)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(presentationRole)) return null;
|
||||
return _byEntityKind.TryGetValue(presentationRole, out var entry) ? entry : null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IEveUnityPlayableWorldAssetManifestSource
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ namespace GameCult.Eve.UnityScene
|
|||
{
|
||||
var trajectories = GetComponent<EveUnityShotTrajectoryRenderer>();
|
||||
if (trajectories == null) trajectories = gameObject.AddComponent<EveUnityShotTrajectoryRenderer>();
|
||||
trajectories.Bind(this);
|
||||
trajectories.Bind(this, Runtime.GameObjectAssetProvider);
|
||||
}
|
||||
|
||||
return Runtime.Connect();
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
public EveUnityPlayableWorldAssetManifestCache AssetManifests { get; }
|
||||
|
||||
public IEveUnityGameObjectAssetProvider? GameObjectAssetProvider { get; private set; }
|
||||
|
||||
public EveUnityPlayableWorldProjection? ActiveWorld => _client.ActiveWorld;
|
||||
|
||||
public EveUnitySceneProjection? ActiveProjection => _client.ActiveProjection;
|
||||
|
|
@ -95,6 +97,7 @@ namespace GameCult.Eve.UnityScene
|
|||
receiptSource,
|
||||
new EveUnityAssetRefResolver(),
|
||||
assetManifests);
|
||||
runtime.GameObjectAssetProvider = liveAssetProvider;
|
||||
return runtime;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ namespace GameCult.Eve.UnityScene
|
|||
Endpoint = Vector(node, "endpoint");
|
||||
DurationSeconds = Math.Max(0, Double(node, "presentationDuration", 0));
|
||||
PresentationKind = Get(node, "presentationKind");
|
||||
ItemKey = Get(node, "itemKey");
|
||||
ImpactKind = Get(node, "impactKind");
|
||||
PresentationIntensity = Math.Max(0, Double(node, "presentationIntensity", 1));
|
||||
}
|
||||
|
|
@ -50,6 +51,7 @@ namespace GameCult.Eve.UnityScene
|
|||
public EveUnityShotVector3 Endpoint { get; }
|
||||
public double DurationSeconds { get; }
|
||||
public string PresentationKind { get; }
|
||||
public string ItemKey { get; }
|
||||
public string ImpactKind { get; }
|
||||
public double PresentationIntensity { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -16,16 +16,18 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
private readonly List<ActiveTrajectory> _active = new List<ActiveTrajectory>();
|
||||
private EveUnityPlayableWorldClientHost? _host;
|
||||
private IEveUnityGameObjectAssetProvider? _assetProvider;
|
||||
private Material? _material;
|
||||
|
||||
public int ActiveTrajectoryCount => _active.Count;
|
||||
|
||||
public void Bind(EveUnityPlayableWorldClientHost host)
|
||||
public void Bind(EveUnityPlayableWorldClientHost host, IEveUnityGameObjectAssetProvider? assetProvider = null)
|
||||
{
|
||||
if (host == null) throw new ArgumentNullException(nameof(host));
|
||||
if (ReferenceEquals(_host, host)) return;
|
||||
if (ReferenceEquals(_host, host) && ReferenceEquals(_assetProvider, assetProvider)) return;
|
||||
Unbind();
|
||||
_host = host;
|
||||
_assetProvider = assetProvider;
|
||||
_host.ShotAvailable += Present;
|
||||
}
|
||||
|
||||
|
|
@ -33,6 +35,7 @@ namespace GameCult.Eve.UnityScene
|
|||
{
|
||||
if (_host != null) _host.ShotAvailable -= Present;
|
||||
_host = null;
|
||||
_assetProvider = null;
|
||||
}
|
||||
|
||||
public void Present(EveUnityShotReceipt receipt)
|
||||
|
|
@ -52,6 +55,15 @@ namespace GameCult.Eve.UnityScene
|
|||
var origin = Vector(receipt.Origin);
|
||||
line.SetPosition(0, origin);
|
||||
var endpoint = Vector(receipt.Endpoint);
|
||||
var effectPrefab = ResolveEffectPrefab(receipt);
|
||||
if (effectPrefab != null)
|
||||
{
|
||||
var effect = Instantiate(effectPrefab, shot.transform);
|
||||
effect.transform.position = origin;
|
||||
var direction = endpoint - origin;
|
||||
if (direction.sqrMagnitude > 0.0001f) effect.transform.rotation = Quaternion.LookRotation(direction.normalized);
|
||||
}
|
||||
var impactPrefab = ResolveRolePrefab("effect.impact." + receipt.ImpactKind);
|
||||
var travels = string.Equals(receipt.PresentationKind, "bolt", StringComparison.Ordinal) ||
|
||||
string.Equals(receipt.PresentationKind, "guided", StringComparison.Ordinal);
|
||||
line.SetPosition(1, travels ? origin : endpoint);
|
||||
|
|
@ -62,7 +74,8 @@ namespace GameCult.Eve.UnityScene
|
|||
endpoint,
|
||||
Time.unscaledTime,
|
||||
Math.Max(minimumDurationSeconds, (float)receipt.DurationSeconds),
|
||||
travels));
|
||||
travels,
|
||||
impactPrefab));
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
|
@ -74,6 +87,15 @@ namespace GameCult.Eve.UnityScene
|
|||
var progress = Mathf.Clamp01((now - trajectory.StartedAt) / trajectory.Duration);
|
||||
if (trajectory.Travels)
|
||||
trajectory.Line.SetPosition(1, Vector3.Lerp(trajectory.Origin, trajectory.Endpoint, progress));
|
||||
if (progress >= 1 && !trajectory.ImpactPresented)
|
||||
{
|
||||
trajectory.ImpactPresented = true;
|
||||
if (trajectory.ImpactPrefab != null)
|
||||
{
|
||||
var impact = Instantiate(trajectory.ImpactPrefab, transform);
|
||||
impact.transform.position = trajectory.Endpoint;
|
||||
}
|
||||
}
|
||||
if (progress < 1 || now < trajectory.StartedAt + trajectory.Duration + lingerSeconds) continue;
|
||||
Destroy(trajectory.Root);
|
||||
_active.RemoveAt(index);
|
||||
|
|
@ -89,6 +111,19 @@ namespace GameCult.Eve.UnityScene
|
|||
return _material;
|
||||
}
|
||||
|
||||
private GameObject? ResolveEffectPrefab(EveUnityShotReceipt receipt)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(receipt.ItemKey))
|
||||
{
|
||||
var itemPrefab = ResolveRolePrefab("effect.shot.item." + receipt.ItemKey);
|
||||
if (itemPrefab != null) return itemPrefab;
|
||||
}
|
||||
return ResolveRolePrefab("effect.shot." + receipt.PresentationKind);
|
||||
}
|
||||
|
||||
private GameObject? ResolveRolePrefab(string role) => _assetProvider?.ResolvePrefab(
|
||||
new EveUnityPlayableWorldAssetBinding("", role, "provider-asset-ref"));
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Unbind();
|
||||
|
|
@ -103,7 +138,7 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
private sealed class ActiveTrajectory
|
||||
{
|
||||
public ActiveTrajectory(GameObject root, LineRenderer line, Vector3 origin, Vector3 endpoint, float startedAt, float duration, bool travels)
|
||||
public ActiveTrajectory(GameObject root, LineRenderer line, Vector3 origin, Vector3 endpoint, float startedAt, float duration, bool travels, GameObject? impactPrefab)
|
||||
{
|
||||
Root = root;
|
||||
Line = line;
|
||||
|
|
@ -112,6 +147,7 @@ namespace GameCult.Eve.UnityScene
|
|||
StartedAt = startedAt;
|
||||
Duration = duration;
|
||||
Travels = travels;
|
||||
ImpactPrefab = impactPrefab;
|
||||
}
|
||||
|
||||
public GameObject Root { get; }
|
||||
|
|
@ -121,6 +157,8 @@ namespace GameCult.Eve.UnityScene
|
|||
public float StartedAt { get; }
|
||||
public float Duration { get; }
|
||||
public bool Travels { get; }
|
||||
public GameObject? ImpactPrefab { get; }
|
||||
public bool ImpactPresented { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,23 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(emitted[0].Endpoint.Z, Is.EqualTo(30));
|
||||
Assert.That(emitted[0].DurationSeconds, Is.EqualTo(0.25));
|
||||
Assert.That(emitted[0].PresentationKind, Is.EqualTo("test-laser"));
|
||||
Assert.That(emitted[0].ItemKey, Is.EqualTo("test-cannon"));
|
||||
Assert.That(emitted[0].ImpactKind, Is.EqualTo("shield"));
|
||||
Assert.That(emitted[0].PresentationIntensity, Is.EqualTo(9));
|
||||
Assert.That(presenter.Apply(next), Is.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ManifestResolvesSemanticEffectPresentationRoles()
|
||||
{
|
||||
var bolt = new EveUnityPlayableWorldAssetManifestEntry(
|
||||
"prefab.effect.shot.bolt", "effect.shot.bolt", "Prefabs/Lightning", "bolt");
|
||||
var manifest = new EveUnityPlayableWorldAssetManifest("manifest", new[] { bolt });
|
||||
|
||||
Assert.That(manifest.FindByPresentationRole("effect.shot.bolt"), Is.SameAs(bolt));
|
||||
Assert.That(manifest.FindByPresentationRole("effect.impact.shield"), Is.Null);
|
||||
}
|
||||
|
||||
private static EveUnitySceneProjection Projection(params EveUnitySceneNode[] shots) =>
|
||||
new EveUnitySceneProjection("provider", "surface", "world", "commands", "receipt", "provider", null,
|
||||
Node("root", "surface", Node("shots", "shot.receipt-stream", shots)));
|
||||
|
|
@ -55,6 +67,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
["endpoint"] = endpoint,
|
||||
["presentationDuration"] = "0.25",
|
||||
["presentationKind"] = "test-laser",
|
||||
["itemKey"] = "test-cannon",
|
||||
["impactKind"] = "shield",
|
||||
["presentationIntensity"] = "9"
|
||||
}, Empty(), Empty(), 0, 0, Array.Empty<EveUnitySceneEmbeddedDocumentSlot>(), null,
|
||||
|
|
|
|||
Reference in a new issue