From f33bc2cafbb6e850ff801fbe1225387e561d80f8 Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 14 Sep 2026 00:34:00 +0200 Subject: [PATCH] ball: support prefab-aware trough queues --- .../Assets/Art/Meshes/Ball.fbx | Bin 0 -> 83452 bytes .../Assets/Art/Meshes/Ball.fbx.meta | 114 +++++++++++ .../Resources/Prefabs/DefaultBall.prefab | 2 +- .../Inspectors/TroughInspector.cs | 49 ++++- .../VPT/BallManagerPlayModeTests.cs | 178 ++++++++++++++++++ .../VPT/BallManagerPlayModeTests.cs.meta | 2 + .../VPT/TroughTests.cs | 46 +++++ .../VPT/Ball/BallComponent.cs | 6 + .../VPT/Ball/BallManager.cs | 11 +- .../VPT/Trough/TroughApi.cs | 47 ++++- .../VPT/Trough/TroughComponent.cs | 6 +- 11 files changed, 443 insertions(+), 18 deletions(-) create mode 100644 VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx create mode 100644 VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs.meta diff --git a/VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx b/VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx new file mode 100644 index 0000000000000000000000000000000000000000..2608128b1c144c7997edf1755fde4c7205798721 GIT binary patch literal 83452 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�*3482MSXKTLx7c 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 F{a>{+q=Nte literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx.meta b/VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx.meta new file mode 100644 index 000000000..5761f426b --- /dev/null +++ b/VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx.meta @@ -0,0 +1,114 @@ +fileFormatVersion: 2 +guid: d78bc7cfea69c684c8c631e82dd9e206 +ModelImporter: + serializedVersion: 24501 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + searchTexturesGlobally: 0 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 1 + motionNodeName: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 1 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importPhysicalCameras: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 1 + generateMeshLods: 0 + meshLodGenerationFlags: 0 + maximumMeshLod: -1 + importUVs: -1 + importVertexColors: 1 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + strictVertexDataChecks: 0 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + calculateBlendshapeNormalsDeltaFromImportedNormals: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + importBlendShapeDeformPercent: 1 + remapMaterialsIfMaterialImportModeIsNone: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab b/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab index a5cdcea82..e4a874cb8 100644 --- a/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab +++ b/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab @@ -41,7 +41,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8289283333368007096} - m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} + m_Mesh: {fileID: 4983844940297228651, guid: d78bc7cfea69c684c8c631e82dd9e206, type: 3} --- !u!23 &5180081487853661404 MeshRenderer: m_ObjectHideFlags: 0 diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/TroughInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/TroughInspector.cs index 9677f45c7..78909b5f3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/TroughInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/TroughInspector.cs @@ -47,7 +47,8 @@ public class TroughInspector : MainInspector private SerializedProperty _playfieldEntrySwitchProperty; private SerializedProperty _playfieldExitKickerProperty; private SerializedProperty _ballCountProperty; - private SerializedProperty _ballProperty; + private SerializedProperty _legacyBallProperty; + private SerializedProperty _ballPrefabsProperty; private SerializedProperty _switchCountProperty; private SerializedProperty _jamSwitchProperty; private SerializedProperty _rollTimeProperty; @@ -61,8 +62,9 @@ protected override void OnEnable() _typeProperty = serializedObject.FindProperty(nameof(TroughComponent.Type)); _playfieldEntrySwitchProperty = serializedObject.FindProperty(nameof(TroughComponent._playfieldEntrySwitch)); _playfieldExitKickerProperty = serializedObject.FindProperty(nameof(TroughComponent.PlayfieldExitKicker)); - _ballProperty = serializedObject.FindProperty(nameof(TroughComponent.Ball)); + _legacyBallProperty = serializedObject.FindProperty(nameof(TroughComponent.Ball)); _ballCountProperty = serializedObject.FindProperty(nameof(TroughComponent.BallCount)); + _ballPrefabsProperty = serializedObject.FindProperty(nameof(TroughComponent.BallPrefabs)); _switchCountProperty = serializedObject.FindProperty(nameof(TroughComponent.SwitchCount)); _jamSwitchProperty = serializedObject.FindProperty(nameof(TroughComponent.JamSwitch)); _rollTimeProperty = serializedObject.FindProperty(nameof(TroughComponent.RollTime)); @@ -80,15 +82,12 @@ public override void OnInspectorGUI() DropDownProperty("Type", _typeProperty, TypeLabels, TypeValues); - PropertyField(_ballProperty, "Ball Prefab"); - if (MainComponent.Ball && !MainComponent.Ball.GetComponent()) { - EditorGUILayout.HelpBox("Ball prefab must contain a ball component.", MessageType.Error); - } - - if (MainComponent.Type != TroughType.ClassicSingleBall) { + if (_typeProperty.intValue != TroughType.ClassicSingleBall) { PropertyField(_ballCountProperty); } + DrawBallPrefabs(); + switch (MainComponent.Type) { case TroughType.ModernOpto: case TroughType.ModernMech: @@ -172,6 +171,40 @@ public override void OnInspectorGUI() } } + private void DrawBallPrefabs() + { + if (_typeProperty.hasMultipleDifferentValues || + _typeProperty.intValue != TroughType.ClassicSingleBall && _ballCountProperty.hasMultipleDifferentValues) { + EditorGUILayout.HelpBox("Edit ball prefabs separately when selected troughs have different types or ball counts.", MessageType.Info); + return; + } + + var ballCount = _typeProperty.intValue == TroughType.ClassicSingleBall ? 1 : _ballCountProperty.intValue; + var previousSize = _ballPrefabsProperty.arraySize; + if (previousSize == 0 && _legacyBallProperty.hasMultipleDifferentValues) { + EditorGUILayout.HelpBox("Edit legacy ball prefabs separately before migrating this selection.", MessageType.Info); + return; + } + var migrateLegacyPrefab = previousSize == 0 && !_legacyBallProperty.hasMultipleDifferentValues + ? _legacyBallProperty.objectReferenceValue + : null; + _ballPrefabsProperty.arraySize = ballCount; + + for (var i = previousSize; i < ballCount; i++) { + _ballPrefabsProperty.GetArrayElementAtIndex(i).objectReferenceValue = migrateLegacyPrefab; + } + + for (var i = 0; i < ballCount; i++) { + var ballPrefabProperty = _ballPrefabsProperty.GetArrayElementAtIndex(i); + PropertyField(ballPrefabProperty, $"Ball {i + 1} Prefab"); + if (ballPrefabProperty.objectReferenceValue is GameObject ballPrefab && !ballPrefab.GetComponent()) { + EditorGUILayout.HelpBox($"Ball {i + 1} prefab must contain a ball component on its root.", MessageType.Error); + } + } + + EditorGUILayout.HelpBox("Leave a slot empty to use the current render pipeline's default ball.", MessageType.None); + } + private static void DrawSwitch(string label, DeviceSwitch sw) { var labelPos = EditorGUILayout.GetControlRect(); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs new file mode 100644 index 000000000..0faafd756 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs @@ -0,0 +1,178 @@ +// Visual Pinball Engine +// Copyright (C) 2026 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +using System.Collections; +using System.Linq; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using UnityEngine.TestTools; + +namespace VisualPinball.Unity.Test +{ + public class BallManagerPlayModeTests + { + private const string BallMeshPath = "Packages/org.visualpinball.engine.unity/VisualPinball.Unity/Assets/Art/Meshes/Ball.fbx"; + private const string DefaultBallPath = "Packages/org.visualpinball.engine.unity/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab"; + + [UnityTearDown] + public IEnumerator LeavePlayModeAfterEachTest() + { + if (Application.isPlaying) { + yield return new ExitPlayMode(); + } + } + + [Test] + public void DefaultBallUsesCorrectlySizedUvMesh() + { + var uvMesh = AssetDatabase.LoadAllAssetsAtPath(BallMeshPath) + .OfType() + .Single(mesh => mesh.name == "Ball UV"); + var defaultBall = AssetDatabase.LoadAssetAtPath(DefaultBallPath); + + Assert.That(defaultBall, Is.Not.Null); + Assert.That(defaultBall.transform.localScale, Is.EqualTo(Vector3.one)); + Assert.That(defaultBall.GetComponent().sharedMesh, Is.SameAs(uvMesh)); + Assert.That(uvMesh.bounds.size.x, Is.EqualTo(Physics.ScaleToWorld(new Vector3(50f, 50f, 50f)).x).Within(0.000001f)); + } + + [UnityTest] + public IEnumerator ScalesRelativeToAuthoredPrefabAndTracksItsSource() + { + Assert.That(Application.isPlaying, Is.False); + yield return new EnterPlayMode(); + + var root = new GameObject("Ball Manager Play Mode Fixture"); + root.SetActive(false); + root.AddComponent(); + root.AddComponent(); + var player = root.AddComponent(); + var physicsEngine = root.AddComponent(); + + var playfieldObject = new GameObject("Playfield"); + playfieldObject.transform.SetParent(root.transform, false); + playfieldObject.AddComponent(); + + var prefabHolder = new GameObject("Prefab Holder"); + prefabHolder.transform.SetParent(root.transform, false); + prefabHolder.SetActive(false); + var ballPrefab = new GameObject("Large Authored Ball"); + ballPrefab.transform.SetParent(prefabHolder.transform, false); + ballPrefab.transform.localScale = Vector3.one * 2f; + ballPrefab.AddComponent().Radius = 50f; + + try { + root.SetActive(true); + yield return null; + yield return null; + + var ballId = player.BallManager.CreateBall(new DebugBallCreator(0f, 0f), 25f, 1f, ballPrefab); + Assert.That(physicsEngine.TryGetBall(ballId, out var ball), Is.True); + Assert.That(ball.transform.localScale, Is.EqualTo(Vector3.one)); + Assert.That(ball.SourcePrefab, Is.SameAs(ballPrefab)); + Assert.That(ball.Radius, Is.EqualTo(25f)); + } finally { + Object.DestroyImmediate(root); + } + + yield return new ExitPlayMode(); + } + + [UnityTest] + public IEnumerator TroughRequeuesThePrefabOfEachDrainedBall() + { + Assert.That(Application.isPlaying, Is.False); + yield return new EnterPlayMode(); + + var root = new GameObject("Trough Ball Queue Play Mode Fixture"); + root.SetActive(false); + root.AddComponent(); + root.AddComponent(); + var player = root.AddComponent(); + root.AddComponent(); + + var playfieldObject = new GameObject("Playfield"); + playfieldObject.transform.SetParent(root.transform, false); + playfieldObject.AddComponent(); + + var entryObject = new GameObject("Drain Trigger"); + entryObject.transform.SetParent(playfieldObject.transform, false); + var entry = entryObject.AddComponent(); + + var exitObject = new GameObject("Exit Kicker"); + exitObject.transform.SetParent(playfieldObject.transform, false); + var exit = exitObject.AddComponent(); + exit.Coils[0].Id = "exit"; + + var prefabHolder = new GameObject("Prefab Holder"); + prefabHolder.transform.SetParent(root.transform, false); + prefabHolder.SetActive(false); + var firstPrefab = CreateBallPrefab(prefabHolder.transform, "First Prefab"); + var secondPrefab = CreateBallPrefab(prefabHolder.transform, "Second Prefab"); + var thirdPrefab = CreateBallPrefab(prefabHolder.transform, "Third Prefab"); + var fourthPrefab = CreateBallPrefab(prefabHolder.transform, "Fourth Prefab"); + + var troughObject = new GameObject("Trough"); + troughObject.transform.SetParent(playfieldObject.transform, false); + var trough = troughObject.AddComponent(); + trough.Type = VisualPinball.Engine.VPT.TroughType.ModernMech; + trough.BallCount = 4; + trough.SwitchCount = 3; + trough.BallPrefabs = new[] { firstPrefab, secondPrefab, thirdPrefab, fourthPrefab }; + trough.PlayfieldEntrySwitch = entry; + trough.PlayfieldEntrySwitchItem = TriggerComponent.SwitchItem; + trough.PlayfieldExitKicker = exit; + trough.PlayfieldExitKickerItem = "exit"; + + try { + root.SetActive(true); + yield return null; + yield return null; + + Assert.That(trough.TroughApi.UncountedStackBalls, Is.EqualTo(1)); + Assert.That(trough.TroughApi.EjectBall(), Is.True); + trough.TroughApi.StackSwitch(0).SetSwitch(true); + Assert.That(trough.TroughApi.EjectBall(), Is.True); + var firstBall = playfieldObject.transform.Find("Ball 0").GetComponent(); + var secondBall = playfieldObject.transform.Find("Ball 1").GetComponent(); + Assert.That(firstBall.SourcePrefab, Is.SameAs(firstPrefab)); + Assert.That(secondBall.SourcePrefab, Is.SameAs(secondPrefab)); + + // Drain the second ejected ball first, then the first one. + ((IApiHittable)entry.TriggerApi).OnHit(secondBall.Id); + ((IApiHittable)entry.TriggerApi).OnHit(firstBall.Id); + + trough.TroughApi.StackSwitch(0).SetSwitch(true); + Assert.That(trough.TroughApi.EjectBall(), Is.True); + trough.TroughApi.StackSwitch(0).SetSwitch(true); + Assert.That(trough.TroughApi.EjectBall(), Is.True); + trough.TroughApi.StackSwitch(0).SetSwitch(true); + Assert.That(trough.TroughApi.EjectBall(), Is.True); + trough.TroughApi.StackSwitch(0).SetSwitch(true); + Assert.That(trough.TroughApi.EjectBall(), Is.True); + Assert.That(playfieldObject.transform.Find("Ball 2").GetComponent().SourcePrefab, Is.SameAs(thirdPrefab)); + Assert.That(playfieldObject.transform.Find("Ball 3").GetComponent().SourcePrefab, Is.SameAs(fourthPrefab)); + Assert.That(playfieldObject.transform.Find("Ball 4").GetComponent().SourcePrefab, Is.SameAs(secondPrefab)); + Assert.That(playfieldObject.transform.Find("Ball 5").GetComponent().SourcePrefab, Is.SameAs(firstPrefab)); + } finally { + Object.DestroyImmediate(root); + } + + yield return new ExitPlayMode(); + } + + private static GameObject CreateBallPrefab(Transform parent, string name) + { + var prefab = new GameObject(name); + prefab.transform.SetParent(parent, false); + prefab.AddComponent(); + return prefab; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs.meta new file mode 100644 index 000000000..b589cf31f --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/BallManagerPlayModeTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6ab5e78798ce838419ca4f83fba4fca8 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs index f8f6fffd5..f8d1c2a2c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs @@ -30,6 +30,52 @@ namespace VisualPinball.Unity.Test { public class TroughTests { + [Test] + public void ShouldPutDrainedBallPrefabsAtBackOfQueue() + { + var firstBall = new GameObject("First Ball"); + var secondBall = new GameObject("Second Ball"); + var thirdBall = new GameObject("Third Ball"); + try { + var queue = new BallPrefabQueue(new[] { firstBall, secondBall, thirdBall }, null, 3); + + queue.Peek().Should().BeSameAs(firstBall); + queue.Dequeue(); + queue.Peek().Should().BeSameAs(secondBall); + queue.Dequeue(); + + // The second ball drains before the first one. + queue.Enqueue(secondBall); + queue.Enqueue(firstBall); + + queue.Peek().Should().BeSameAs(thirdBall); + queue.Dequeue(); + queue.Peek().Should().BeSameAs(secondBall); + queue.Dequeue(); + queue.Peek().Should().BeSameAs(firstBall); + } finally { + Object.DestroyImmediate(firstBall); + Object.DestroyImmediate(secondBall); + Object.DestroyImmediate(thirdBall); + } + } + + [Test] + public void ShouldUseLegacyPrefabForEveryInitialBall() + { + var legacyBall = new GameObject("Legacy Ball"); + try { + var queue = new BallPrefabQueue(null, legacyBall, 3); + + for (var i = 0; i < 3; i++) { + queue.Peek().Should().BeSameAs(legacyBall); + queue.Dequeue(); + } + queue.Count.Should().Be(0); + } finally { + Object.DestroyImmediate(legacyBall); + } + } [Test] public void ShouldReturnCorrectSwitchesForModernOpto() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallComponent.cs index ca315f2c3..9ab722862 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallComponent.cs @@ -29,6 +29,12 @@ public class BallComponent : MonoBehaviour public float3 Velocity; public bool IsFrozen; + /// + /// The prefab this runtime ball was instantiated from. Troughs use this to put the + /// correct visual ball back into their prefab queue after a drain. + /// + internal GameObject SourcePrefab { get; set; } + internal BallState CreateState() { var pos = transform.localPosition.TranslateToVpx(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs index 3492b5da9..96cf037f4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs @@ -53,10 +53,19 @@ public int CreateBall(IBallCreationPosition ballCreator, float radius = 25f, flo var ballGo = Object.Instantiate(ballPrefab, _parent); var ballComp = ballGo.GetComponent(); + if (!ballComp) { + Object.DestroyImmediate(ballGo); + throw new InvalidOperationException($"Ball prefab '{ballPrefab.name}' must have a BallComponent on its root."); + } + if (ballComp.Radius <= 0f) { + Object.DestroyImmediate(ballGo); + throw new InvalidOperationException($"Ball prefab '{ballPrefab.name}' must have a radius greater than zero."); + } ballGo.name = $"Ball {NumBallsCreated++}"; - ballGo.transform.localScale = Physics.ScaleToWorld(new Vector3(radius, radius, radius) * 2f); + ballGo.transform.localScale *= radius / ballComp.Radius; ballGo.transform.localPosition = localPos.TranslateToWorld(); + ballComp.SourcePrefab = ballPrefab; ballComp.Radius = radius; ballComp.Mass = mass; ballComp.Velocity = ballCreator.GetBallCreationVelocity().ToUnityFloat3(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs index 453d3ba1c..14a2efa70 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs @@ -152,6 +152,7 @@ public class TroughApi : ItemApi, /// This does not include balls sitting in drain before being pushed into the trough. /// private int _countedStackBalls; + private BallPrefabQueue _ballPrefabs; /// /// The player will ask for switches to hook up to the gamelogic engine, @@ -233,6 +234,7 @@ void IApi.OnInit(BallManager ballManager) // fill up the ball stack var ballCount = MainComponent.Type == TroughType.ClassicSingleBall ? 1 : MainComponent.BallCount; + _ballPrefabs = new BallPrefabQueue(MainComponent.BallPrefabs, MainComponent.Ball, ballCount); for (var i = 0; i < ballCount; i++) { AddBall(); } @@ -250,7 +252,7 @@ private void AddBall() case TroughType.ModernOpto: case TroughType.ModernMech: case TroughType.TwoCoilsNSwitches: - if (_countedStackBalls < MainComponent.BallCount) { + if (_countedStackBalls < _stackSwitches.Length) { _stackSwitches[_countedStackBalls].SetSwitch(true); _countedStackBalls++; } else { @@ -297,12 +299,15 @@ private void OnDrainKickerHit(object sender, HitEventArgs args) private void OnEntry(object sender, SwitchEventArgs args) { if (args.IsEnabled) { - Logger.Info("Draining ball into trough."); - if (_drainSwitch is KickerApi kickerApi) { - kickerApi.DestroyBall(); - } else { - BallManager.DestroyBall(args.BallId); + if (!PhysicsEngine.TryGetBall(args.BallId, out var ballComponent)) { + Logger.Warn($"Cannot drain ball {args.BallId}: the ball no longer exists."); + return; } + + Logger.Info("Draining ball into trough."); + var ballPrefab = ballComponent.SourcePrefab; + BallManager.DestroyBall(args.BallId); + _ballPrefabs.Enqueue(ballPrefab); DrainBall(); } else { @@ -491,8 +496,13 @@ public bool EjectBall() Logger.Warn("Trough: Cannot spawn ball without an exit kicker."); return false; } + if (_ballPrefabs == null || _ballPrefabs.Count == 0) { + Logger.Error("Trough: Ball prefab queue is empty while the trough still contains a ball."); + return false; + } Logger.Info("Trough: Spawning new ball."); - _ejectKicker.CreateBall(MainComponent.Ball); + _ejectKicker.CreateBall(_ballPrefabs.Peek()); + _ballPrefabs.Dequeue(); _ejectCoil.OnCoil(true); // open the switch of the ejected ball immediately @@ -684,4 +694,27 @@ void IApi.OnDestroy() #endregion } + + internal sealed class BallPrefabQueue + { + private readonly Queue _prefabs = new Queue(); + + internal int Count => _prefabs.Count; + + internal BallPrefabQueue(IReadOnlyList configuredPrefabs, GameObject legacyPrefab, int ballCount) + { + var useLegacyPrefab = configuredPrefabs == null || configuredPrefabs.Count == 0; + for (var i = 0; i < ballCount; i++) { + _prefabs.Enqueue(useLegacyPrefab + ? legacyPrefab + : i < configuredPrefabs.Count ? configuredPrefabs[i] : null); + } + } + + internal GameObject Peek() => _prefabs.Peek(); + + internal void Dequeue() => _prefabs.Dequeue(); + + internal void Enqueue(GameObject prefab) => _prefabs.Enqueue(prefab); + } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughComponent.cs index 135957c0c..b41d75eb7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughComponent.cs @@ -62,13 +62,17 @@ public ITriggerComponent PlayfieldEntrySwitch public KickerComponent PlayfieldExitKicker; public string PlayfieldExitKickerItem = string.Empty; - [Tooltip("The prefab that will instantiated when ejecting a new ball.")] + [HideInInspector] + [Tooltip("Legacy ball prefab. Kept so existing tables can be migrated to Ball Prefabs.")] public GameObject Ball; [Range(1, 10)] [Tooltip("How many balls the trough holds when the game starts.")] public int BallCount = 6; + [Tooltip("The ball prefabs in ejection order. A null entry uses the current render pipeline's default ball.")] + public GameObject[] BallPrefabs = Array.Empty(); + [Range(1, 10)] [Tooltip("How many ball switches are available.")] public int SwitchCount = 6;