From e5a43cffcddeaf6a492d8583408baa4172bd5796 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 15 Jul 2026 04:18:13 +0100 Subject: [PATCH] Prefer provider-authored shot effects --- .../Runtime/EveUnityShotTrajectoryRenderer.cs | 34 +++++--- .../EveUnityShotTrajectoryRendererTests.cs | 85 +++++++++++++++++++ ...veUnityShotTrajectoryRendererTests.cs.meta | 11 +++ .../org.gamecult.eve.unity-scene/package.json | 2 +- 4 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs create mode 100644 packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs.meta diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs index ecee9ca..2fc1532 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityShotTrajectoryRenderer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; #nullable enable @@ -20,6 +21,7 @@ namespace GameCult.Eve.UnityScene private Material? _material; public int ActiveTrajectoryCount => _active.Count; + public int ActiveFallbackTrajectoryCount => _active.Count(trajectory => trajectory.Line != null); public void Bind(EveUnityPlayableWorldClientHost host, IEveUnityGameObjectAssetProvider? assetProvider = null) { @@ -43,19 +45,10 @@ namespace GameCult.Eve.UnityScene if (receipt == null) throw new ArgumentNullException(nameof(receipt)); var shot = new GameObject("Eve shot " + receipt.ShotId); shot.transform.SetParent(transform, false); - var line = shot.AddComponent(); - line.useWorldSpace = true; - line.positionCount = 2; - var intensityScale = Mathf.Clamp(Mathf.Sqrt((float)receipt.PresentationIntensity), 0.5f, 4f); - line.startWidth = Math.Max(0.01f, width * intensityScale); - line.endWidth = Math.Max(0.01f, width * 0.5f * intensityScale); - line.material = SharedMaterial(); - line.startColor = line.endColor = receipt.Hit ? hitColor : missColor; - var origin = Vector(receipt.Origin); - line.SetPosition(0, origin); var endpoint = Vector(receipt.Endpoint); var effectPrefab = ResolveEffectPrefab(receipt); + LineRenderer? line = null; if (effectPrefab != null) { var effect = Instantiate(effectPrefab, shot.transform); @@ -71,10 +64,23 @@ namespace GameCult.Eve.UnityScene lightning.StartAnimation(); } } + else + { + line = shot.AddComponent(); + line.useWorldSpace = true; + line.positionCount = 2; + var intensityScale = Mathf.Clamp(Mathf.Sqrt((float)receipt.PresentationIntensity), 0.5f, 4f); + line.startWidth = Math.Max(0.01f, width * intensityScale); + line.endWidth = Math.Max(0.01f, width * 0.5f * intensityScale); + line.material = SharedMaterial(); + line.startColor = line.endColor = receipt.Hit ? hitColor : missColor; + line.SetPosition(0, origin); + } 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); + if (line != null) + line.SetPosition(1, travels ? origin : endpoint); _active.Add(new ActiveTrajectory( shot, line, @@ -93,7 +99,7 @@ namespace GameCult.Eve.UnityScene { var trajectory = _active[index]; var progress = Mathf.Clamp01((now - trajectory.StartedAt) / trajectory.Duration); - if (trajectory.Travels) + if (trajectory.Travels && trajectory.Line != null) trajectory.Line.SetPosition(1, Vector3.Lerp(trajectory.Origin, trajectory.Endpoint, progress)); if (progress >= 1 && !trajectory.ImpactPresented) { @@ -146,7 +152,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, GameObject? impactPrefab) + public ActiveTrajectory(GameObject root, LineRenderer? line, Vector3 origin, Vector3 endpoint, float startedAt, float duration, bool travels, GameObject? impactPrefab) { Root = root; Line = line; @@ -159,7 +165,7 @@ namespace GameCult.Eve.UnityScene } public GameObject Root { get; } - public LineRenderer Line { get; } + public LineRenderer? Line { get; } public Vector3 Origin { get; } public Vector3 Endpoint { get; } public float StartedAt { get; } diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs new file mode 100644 index 0000000..8dff40b --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; + +#nullable enable + +namespace GameCult.Eve.UnityScene.Tests +{ + public sealed class EveUnityShotTrajectoryRendererTests + { + [Test] + public void ProviderShotEffectReplacesGenericFallbackLine() + { + var root = new GameObject("shot-renderer-provider-test"); + var prefab = new GameObject("provider-bolt"); + try + { + var host = root.AddComponent(); + var renderer = root.AddComponent(); + renderer.Bind(host, new BoltProvider(prefab)); + + renderer.Present(Shot("provider-shot")); + + Assert.That(renderer.ActiveTrajectoryCount, Is.EqualTo(1)); + Assert.That(renderer.ActiveFallbackTrajectoryCount, Is.Zero); + Assert.That(root.GetComponentsInChildren(), Is.Empty); + Assert.That(root.transform.GetChild(0).Find("provider-bolt(Clone)"), Is.Not.Null); + } + finally + { + UnityEngine.Object.DestroyImmediate(root); + UnityEngine.Object.DestroyImmediate(prefab); + } + } + + [Test] + public void MissingProviderShotEffectUsesGenericFallbackLine() + { + var root = new GameObject("shot-renderer-fallback-test"); + try + { + var host = root.AddComponent(); + var renderer = root.AddComponent(); + renderer.Bind(host, new BoltProvider(null)); + + renderer.Present(Shot("fallback-shot")); + + Assert.That(renderer.ActiveTrajectoryCount, Is.EqualTo(1)); + Assert.That(renderer.ActiveFallbackTrajectoryCount, Is.EqualTo(1)); + Assert.That(root.GetComponentsInChildren(), Has.Length.EqualTo(1)); + } + finally + { + UnityEngine.Object.DestroyImmediate(root); + } + } + + private static EveUnityShotReceipt Shot(string id) => + new EveUnityShotReceipt(new EveUnitySceneNode(id, "shot.receipt", "scene-node", + new Dictionary(StringComparer.Ordinal) + { + ["shotId"] = id, + ["origin"] = "0,0", + ["endpoint"] = "10,0", + ["presentationDuration"] = "0.2", + ["presentationKind"] = "bolt", + ["impactKind"] = "armor", + ["presentationIntensity"] = "85", + ["hit"] = "true" + }, Empty(), Empty(), 0, 0, Array.Empty(), null, + Array.Empty())); + + private static IReadOnlyDictionary Empty() => + new Dictionary(StringComparer.Ordinal); + + private sealed class BoltProvider : IEveUnityGameObjectAssetProvider + { + private readonly GameObject? _prefab; + public BoltProvider(GameObject? prefab) { _prefab = prefab; } + public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset) => + asset.EntityKind == "effect.shot.bolt" ? _prefab : null; + } + } +} diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs.meta b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs.meta new file mode 100644 index 0000000..9cf0c04 --- /dev/null +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityShotTrajectoryRendererTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1d80f8e29164e23ad367feefb67c49e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-scene/package.json b/packages/org.gamecult.eve.unity-scene/package.json index b2c1856..14285f7 100644 --- a/packages/org.gamecult.eve.unity-scene/package.json +++ b/packages/org.gamecult.eve.unity-scene/package.json @@ -1,7 +1,7 @@ { "name": "org.gamecult.eve.unity-scene", "displayName": "Eve Unity Scene Lowering", - "version": "0.3.19", + "version": "0.3.20", "unity": "2021.3", "description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.", "dependencies": {