From 1727a672c3bf880f946df721920cb200c7f76aa4 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 5 Apr 2025 22:53:19 +0200 Subject: [PATCH 01/11] switch: Add first version of animation component. --- .../VPT/Trigger/SwitchAnimationComponent.cs | 177 ++++++++++++++++++ .../Trigger/SwitchAnimationComponent.cs.meta | 3 + 2 files changed, 180 insertions(+) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs new file mode 100644 index 000000000..6586f1161 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs @@ -0,0 +1,177 @@ +// Visual Pinball Engine +// Copyright (C) 2023 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System.Collections; +using System.Linq; +using NLog; +using Unity.Mathematics; +using UnityEngine; +using VisualPinball.Engine.VPT.Trigger; +using Logger = NLog.Logger; + +namespace VisualPinball.Unity +{ + //[PackAs("SwitchAnimation")] + [AddComponentMenu("Pinball/Animation/Switch Animation")] + public class SwitchAnimationComponent : AnimationComponent//, IPackable + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + #region Data + + public float StartAngle { + get => transform.localEulerAngles.x > 180 ? transform.localEulerAngles.x - 360 : transform.localEulerAngles.x; + set => transform.SetLocalXRotation(math.radians(value)); + } + + [Range(-180f, 180f)] + [Tooltip("Angle of the switch bracket in end position (closed)")] + public float EndAngle; + + public AnimationCurve BackAnimationCurve = AnimationCurve.EaseInOut(0, 0, 1f, 1); + public float BackAnimationDurationSeconds = 0.3f; + + private float _startAngle; + private float _currentAngle; + private bool _ballInside; + private int _ballId; + private float _yEnter; + private float _yExit; + private Coroutine _animateBackCoroutine; + + private TriggerComponent _triggerComp; + private PhysicsEngine _physicsEngine; + + #endregion + + private void Start() + { + _startAngle = StartAngle; + _currentAngle = _startAngle; + _triggerComp = GetComponentInParent(); + if (!_triggerComp) { + Logger.Warn($"{name}: No Trigger Component found in parent. Animation will not work."); + return; + } + _physicsEngine = _triggerComp.GetComponentInParent(); + if (!_physicsEngine) { + Logger.Warn($"{name}: No Physics Engine found in parent. Animation will not work."); + return; + } + + _yEnter = _triggerComp.DragPoints[0].Center.Y; + _yExit = _triggerComp.DragPoints[1].Center.Y; + + _triggerComp.TriggerApi.Hit += OnHit; + _triggerComp.TriggerApi.UnHit += UnHit; + } + + + private void OnHit(object sender, HitEventArgs e) + { + if (_ballInside) { + // ignore other balls + return; + } + if (_animateBackCoroutine != null) { + StopCoroutine(_animateBackCoroutine); + _animateBackCoroutine = null; + } + + _ballInside = true; + _ballId = e.BallId; + Debug.Log("----------- OnHit(" + e + ")"); + } + + private void Update() + { + if (!_ballInside) { + // nothing to animate + return; + } + + var ballTransform = _physicsEngine.GetTransform(_ballId); + var ballLocalToWorld = (float4x4)ballTransform.localToWorldMatrix; + var transformWithinParent = ballLocalToWorld.GetLocalToPlayfieldMatrixInVpx(_triggerComp.transform.worldToLocalMatrix); + var localVpxPos = transformWithinParent.MultiplyPoint(ballTransform.position); + + var yPos = math.unlerp(_yEnter, _yExit, localVpxPos.y - _yEnter); // yPos is between 0 and 1, depending where localVpxPos.y is + _currentAngle = math.clamp(math.lerp(_startAngle, EndAngle, yPos), _startAngle, EndAngle); + + transform.SetLocalXRotation(math.radians(_currentAngle)); + } + + private void UnHit(object sender, HitEventArgs e) + { + if (e.BallId != _ballId) { + // ignore other balls + return; + } + _ballId = 0; + _ballInside = false; + Debug.Log("----------- UnHit(" + e + ")"); + + if (_animateBackCoroutine != null) { + StopCoroutine(_animateBackCoroutine); + } + _animateBackCoroutine = StartCoroutine(AnimateBack()); + } + + private IEnumerator AnimateBack() + { + // rotate from _currentAngle to _startAngle + var from = _currentAngle; + var to = _startAngle; + var d = to - from; + + var t = 0f; + while (t < BackAnimationDurationSeconds) { + var f = BackAnimationCurve.Evaluate(t / BackAnimationDurationSeconds); + _currentAngle = from + f * d; + transform.SetLocalXRotation(math.radians(_currentAngle)); + t += Time.deltaTime; + yield return null; // wait one frame + } + + // finally, snap to the curve's final value + transform.SetLocalXRotation(math.radians(to)); + _animateBackCoroutine = null; + } + + private void OnDestroy() + { + if (_triggerComp) { + _triggerComp.TriggerApi.Hit -= OnHit; + _triggerComp.TriggerApi.UnHit -= UnHit; + } + } + + // #region Packaging + // + // public byte[] Pack() => TriggerAnimationPackable.Pack(this); + // + // public byte[] PackReferences(Transform root, PackagedRefs refs, PackagedFiles files) => null; + // + // public void Unpack(byte[] bytes) => TriggerAnimationPackable.Unpack(bytes, this); + // + // public void UnpackReferences(byte[] data, Transform root, PackagedRefs refs, PackagedFiles files) { } + // + // #endregion + + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs.meta new file mode 100644 index 000000000..ac4cefb6a --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8df7b558b1f640809ed31dc5e5ff9ca2 +timeCreated: 1744981170 \ No newline at end of file From a8fea19251ca87449f3eb7a5fbaba73cd5782626 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 6 Apr 2025 22:53:37 +0200 Subject: [PATCH 02/11] switch: Add gizmo. --- .../VPT/Trigger/SwitchAnimationComponent.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs index 6586f1161..6f078874a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs @@ -173,5 +173,36 @@ private void OnDestroy() // // #endregion +#if UNITY_EDITOR + private void OnDrawGizmosSelected() + { + var triggerComp = GetComponentInParent(); + var collComp = GetComponentInParent(); + if (!triggerComp || triggerComp.DragPoints == null || triggerComp.DragPoints.Length < 2 || !collComp) { + return; + } + + var dp0 = triggerComp.DragPoints[0].Center.ToUnityVector3(); + var dp1 = triggerComp.DragPoints[1].Center.ToUnityVector3(); + var dist = dp0.y - dp1.y; + var height = collComp.HitHeight; + + var entryRect = new[] { + dp0, + new Vector3(dp0.x, dp0.y, dp0.z + height), + new Vector3(dp0.x, dp0.y - dist, dp0.z + height), + new Vector3(dp0.x, dp0.y - dist, dp0.z), + }; + + Debug.Log(string.Join(" - ", entryRect.Select(v => v.ToString()))); + + Gizmos.matrix = triggerComp.transform.GetLocalToPlayfieldMatrixInVpx(); + Gizmos.color = Color.red; + Gizmos.DrawLineStrip(entryRect, true); + foreach (var v in entryRect) { + Gizmos.DrawSphere(v, 1f); + } + } +#endif } } From 197c80f9c33c21bedf39057180194d473f34e03c Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 13 Apr 2025 23:32:49 +0200 Subject: [PATCH 03/11] switch: Add better gizmo. --- .../VisualPinball.Unity/Common/Handles2.cs | 40 +++++++++++++++ .../Common/Handles2.cs.meta | 3 ++ .../VisualPinball.Unity/Physics/Physics.cs | 9 ++++ .../VPT/Trigger/SwitchAnimationComponent.cs | 49 ++++++++++++++----- 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs b/VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs new file mode 100644 index 000000000..f4650ad30 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs @@ -0,0 +1,40 @@ +// Visual Pinball Engine +// Copyright (C) 2023 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#if UNITY_EDITOR + +using UnityEditor; +using UnityEngine; + +namespace VisualPinball.Unity +{ + public static class Handles2 + { + public static void DrawArrow(Vector3 from, Vector3 to, float width = 1f, float arrowHeadLength = 0.025f, float arrowHeadAngle = 20.0f, bool bothSides = false) + { + var direction = to - from; + var right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1); + var left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1); + Handles.DrawAAPolyLine(width, from, to); + Handles.DrawAAPolyLine(width, to + right * arrowHeadLength, to, to + left * arrowHeadLength); + if (bothSides) { + Handles.DrawAAPolyLine(width, from - right * arrowHeadLength, from, from - left * arrowHeadLength); + } + } + } +} + +#endif diff --git a/VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs.meta new file mode 100644 index 000000000..eedbc83ea --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c4984fa3344e4254a63ce13e77799178 +timeCreated: 1745010982 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Physics.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Physics.cs index ab689024e..4f80fa1d9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Physics.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Physics.cs @@ -86,6 +86,15 @@ public static class Physics public static float4x4 GetLocalToPlayfieldMatrixInVpx(this float4x4 localToWorld, float4x4 worldToPlayfield) => math.mul(math.mul(WorldToVpx, math.mul(worldToPlayfield, localToWorld)), VpxToWorld); + public static float4x4 GetLocalToPlayfieldMatrixInVpx(this Transform itemTransform) + { + var playfieldComp = itemTransform.GetComponentInParent(); + var playfieldToWorld = playfieldComp ? (float4x4)playfieldComp.transform.localToWorldMatrix : float4x4.identity; + var worldToPlayfield = playfieldComp ? (float4x4)playfieldComp.transform.worldToLocalMatrix : float4x4.identity; + var localToPlayfieldMatrixInVpx = GetLocalToPlayfieldMatrixInVpx(itemTransform.localToWorldMatrix, worldToPlayfield); + return math.mul(math.mul(playfieldToWorld, VpxToWorld), localToPlayfieldMatrixInVpx); + } + #endregion #region Translation diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs index 6f078874a..151683b04 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs @@ -16,14 +16,17 @@ // ReSharper disable InconsistentNaming +#if UNITY_EDITOR +using UnityEditor; +#endif using System.Collections; -using System.Linq; using NLog; using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.VPT.Trigger; using Logger = NLog.Logger; + namespace VisualPinball.Unity { //[PackAs("SwitchAnimation")] @@ -174,34 +177,54 @@ private void OnDestroy() // #endregion #if UNITY_EDITOR + private void OnDrawGizmosSelected() { + var triggerComp = GetComponentInParent(); var collComp = GetComponentInParent(); - if (!triggerComp || triggerComp.DragPoints == null || triggerComp.DragPoints.Length < 2 || !collComp) { + if (!triggerComp || triggerComp.DragPoints is not { Length: 4 } || !collComp) { return; } var dp0 = triggerComp.DragPoints[0].Center.ToUnityVector3(); var dp1 = triggerComp.DragPoints[1].Center.ToUnityVector3(); - var dist = dp0.y - dp1.y; - var height = collComp.HitHeight; + var dp3 = triggerComp.DragPoints[3].Center.ToUnityVector3(); + + var dx = dp3.x - dp0.x; + var h = collComp.HitHeight; var entryRect = new[] { dp0, - new Vector3(dp0.x, dp0.y, dp0.z + height), - new Vector3(dp0.x, dp0.y - dist, dp0.z + height), - new Vector3(dp0.x, dp0.y - dist, dp0.z), + new Vector3(dp0.x, dp0.y, dp0.z + h), + new Vector3(dp0.x + dx, dp0.y, dp0.z + h), + new Vector3(dp0.x + dx, dp0.y, dp0.z), + dp0 + }; + + var exitRect = new[] { + dp1, + new Vector3(dp1.x, dp1.y, dp1.z + h), + new Vector3(dp1.x + dx, dp1.y, dp1.z + h), + new Vector3(dp1.x + dx, dp1.y, dp1.z), + dp1 }; - Debug.Log(string.Join(" - ", entryRect.Select(v => v.ToString()))); + Handles.matrix = triggerComp.transform.GetLocalToPlayfieldMatrixInVpx(); + Handles.color = Color.gray; - Gizmos.matrix = triggerComp.transform.GetLocalToPlayfieldMatrixInVpx(); - Gizmos.color = Color.red; - Gizmos.DrawLineStrip(entryRect, true); - foreach (var v in entryRect) { - Gizmos.DrawSphere(v, 1f); + for (var i = 0; i < 4; i++) { + Handles2.DrawArrow(entryRect[i], exitRect[i], 3, 10); } + Handles.DrawAAPolyLine(5, entryRect); + Handles.DrawAAPolyLine(5, exitRect); + + // Gizmos.matrix = triggerComp.transform.GetLocalToPlayfieldMatrixInVpx(); + // Gizmos.color = Color.red; + // Gizmos.DrawLineStrip(entryRect, true); + // foreach (var v in entryRect) { + // Gizmos.DrawSphere(v, 1f); + // } } #endif } From a66bdfb387e351360b40ca61c18cf2a40ec669e5 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 18 Apr 2025 00:21:29 +0200 Subject: [PATCH 04/11] switch: Add animation curve for forward animation. --- .../VPT/Trigger/SwitchAnimationComponent.cs | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs index 151683b04..fb72b45df 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs @@ -46,8 +46,9 @@ public float StartAngle { [Tooltip("Angle of the switch bracket in end position (closed)")] public float EndAngle; - public AnimationCurve BackAnimationCurve = AnimationCurve.EaseInOut(0, 0, 1f, 1); - public float BackAnimationDurationSeconds = 0.3f; + public AnimationCurve ForwardsAnimationCurve = AnimationCurve.EaseInOut(0, 0, 1f, 1); + public AnimationCurve BackwardsAnimationCurve = AnimationCurve.EaseInOut(0, 0, 1f, 1); + public float BackwardsAnimationDurationSeconds = 0.3f; private float _startAngle; private float _currentAngle; @@ -55,7 +56,7 @@ public float StartAngle { private int _ballId; private float _yEnter; private float _yExit; - private Coroutine _animateBackCoroutine; + private Coroutine _animateBackwardsCoroutine; private TriggerComponent _triggerComp; private PhysicsEngine _physicsEngine; @@ -84,21 +85,19 @@ private void Start() _triggerComp.TriggerApi.UnHit += UnHit; } - private void OnHit(object sender, HitEventArgs e) { if (_ballInside) { // ignore other balls return; } - if (_animateBackCoroutine != null) { - StopCoroutine(_animateBackCoroutine); - _animateBackCoroutine = null; + if (_animateBackwardsCoroutine != null) { + StopCoroutine(_animateBackwardsCoroutine); + _animateBackwardsCoroutine = null; } _ballInside = true; _ballId = e.BallId; - Debug.Log("----------- OnHit(" + e + ")"); } private void Update() @@ -113,10 +112,10 @@ private void Update() var transformWithinParent = ballLocalToWorld.GetLocalToPlayfieldMatrixInVpx(_triggerComp.transform.worldToLocalMatrix); var localVpxPos = transformWithinParent.MultiplyPoint(ballTransform.position); - var yPos = math.unlerp(_yEnter, _yExit, localVpxPos.y - _yEnter); // yPos is between 0 and 1, depending where localVpxPos.y is + var yPos = ForwardsAnimationCurve.Evaluate(math.unlerp(_yEnter, _yExit, localVpxPos.y)); // yPos is between 0 and 1, depending on where localVpxPos.y is _currentAngle = math.clamp(math.lerp(_startAngle, EndAngle, yPos), _startAngle, EndAngle); - transform.SetLocalXRotation(math.radians(_currentAngle)); + transform.SetLocalXRotation(math.sin(math.radians(_currentAngle))); } private void UnHit(object sender, HitEventArgs e) @@ -127,15 +126,14 @@ private void UnHit(object sender, HitEventArgs e) } _ballId = 0; _ballInside = false; - Debug.Log("----------- UnHit(" + e + ")"); - if (_animateBackCoroutine != null) { - StopCoroutine(_animateBackCoroutine); + if (_animateBackwardsCoroutine != null) { + StopCoroutine(_animateBackwardsCoroutine); } - _animateBackCoroutine = StartCoroutine(AnimateBack()); + _animateBackwardsCoroutine = StartCoroutine(AnimateBackwards()); } - private IEnumerator AnimateBack() + private IEnumerator AnimateBackwards() { // rotate from _currentAngle to _startAngle var from = _currentAngle; @@ -143,8 +141,8 @@ private IEnumerator AnimateBack() var d = to - from; var t = 0f; - while (t < BackAnimationDurationSeconds) { - var f = BackAnimationCurve.Evaluate(t / BackAnimationDurationSeconds); + while (t < BackwardsAnimationDurationSeconds) { + var f = BackwardsAnimationCurve.Evaluate(t / BackwardsAnimationDurationSeconds); _currentAngle = from + f * d; transform.SetLocalXRotation(math.radians(_currentAngle)); t += Time.deltaTime; @@ -153,7 +151,7 @@ private IEnumerator AnimateBack() // finally, snap to the curve's final value transform.SetLocalXRotation(math.radians(to)); - _animateBackCoroutine = null; + _animateBackwardsCoroutine = null; } private void OnDestroy() @@ -218,13 +216,6 @@ private void OnDrawGizmosSelected() } Handles.DrawAAPolyLine(5, entryRect); Handles.DrawAAPolyLine(5, exitRect); - - // Gizmos.matrix = triggerComp.transform.GetLocalToPlayfieldMatrixInVpx(); - // Gizmos.color = Color.red; - // Gizmos.DrawLineStrip(entryRect, true); - // foreach (var v in entryRect) { - // Gizmos.DrawSphere(v, 1f); - // } } #endif } From fe2dfefc8bf6ac2dde1e17a9f260f599d5eda182 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 19 Apr 2025 00:56:37 +0200 Subject: [PATCH 05/11] trigger: Add drag points when adding component to empty game object. --- .../VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs index 2696d2e6a..d72e7e4ec 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs @@ -70,7 +70,7 @@ public float Rotation { } [SerializeField] - private DragPointData[] _dragPoints; + private DragPointData[] _dragPoints = { new(-50f, -50f), new(-50f, 50f), new(50f, 50f), new(50f, -50f) }; public DragPointData[] DragPoints { get => _dragPoints; set => _dragPoints = value; } #endregion From 25d28cba5bc47b1cec306d31db57a96924034394 Mon Sep 17 00:00:00 2001 From: BilboX Date: Mon, 27 Mar 2023 16:45:03 -1000 Subject: [PATCH 06/11] Debug shot component --- .../VisualPinball.Unity.Editor/Utils/Icons.cs | 2 + .../Game/DebugShotComponent.cs | 267 ++++++++++++++++++ .../Game/DebugShotComponent.cs.meta | 11 + 3 files changed, 280 insertions(+) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index a6592068d..516fc6a3f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -291,6 +291,7 @@ internal class IconLookup : IIconLookup public Texture2D Lookup(T mb, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) where T : class { switch (mb) { + case DebugShotComponent _: return Icons.BallRoller(size, color); case BallComponent _: return Icons.Ball(size, color); case BallRollerComponent _: return Icons.BallRoller(size, color); case BumperComponent _: return Icons.Bumper(size, color); @@ -330,6 +331,7 @@ public Texture2D Lookup(T mb, IconSize size = IconSize.Large, IconColor color public void DisableGizmoIcons() { + Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs new file mode 100644 index 000000000..efb1e2f0c --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs @@ -0,0 +1,267 @@ +// Visual Pinball Engine +// Copyright (C) 2023 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using VisualPinball.Engine.Common; +using UnityEngine.InputSystem; +using VisualPinball.Unity; +using Unity.Mathematics; +using Unity.Entities; +using Unity.Collections; + +namespace VisualPinball.Unity +{ + public class DebugShotComponent : MonoBehaviour + { + private EntityManager _entityManager; + private EntityQuery _ballEntityQuery; + + bool _activated = false; //!< DebugShot mode activation flag + bool _mouseDown = false; //!< mouse button down flag + + private PlayfieldComponent _playfield; + private Matrix4x4 _ltw; + private Matrix4x4 _wtl; + + private Plane _playfieldPlane; + + public float ForceMultiplier = 0.2F; + + + private void Awake() + { + _playfield = GameObject.FindObjectOfType(); + _ltw = Physics.VpxToWorld; + _wtl = Physics.WorldToVpx; + + _entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; + _ballEntityQuery = _entityManager.CreateEntityQuery(typeof(BallData)); + } + + void Start() + { + { + CreateShot("Current"); + } + + //for (int i = 1; i <= 12; i++) + //{ + // CreateShot("F" + i); + //} + + SetVisible(false); + } + + void SetVisible(bool b, GameObject o = null) + { + if (o == null) + o = gameObject; + + if (o.GetComponent() != null) + o.GetComponent().enabled = b; + + for (int i = 0; i < o.transform.childCount; i++) + SetVisible(b, o.transform.GetChild(i).gameObject); + } + + void CreateShot(string name) + { + // If already loaded, to not modify + if (transform.Find(name) != null) + return; + + // Father object + GameObject shot = GameObject.CreatePrimitive(PrimitiveType.Plane); + Destroy(shot.GetComponent()); + Destroy(shot.GetComponent()); + shot.name = name; + shot.transform.parent = transform; + + // Shot start + GameObject point1 = GameObject.CreatePrimitive(PrimitiveType.Sphere); + Destroy(point1.GetComponent()); + point1.name = "Start"; + point1.transform.parent = shot.transform; + point1.transform.localScale = new Vector3(1f, 1f, 1f) * Physics.ScaleToWorld(PhysicsConstants.PhysSkin*2);// * Globals.g_Scale; + // Shot direction end + GameObject point2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); + Destroy(point2.GetComponent()); + point2.name = "DirectionEnd"; + point2.transform.parent = shot.transform; + point2.transform.localScale = new Vector3(1f, 1f, 1f) * Physics.ScaleToWorld(PhysicsConstants.PhysSkin*2);// * Globals.g_Scale; + + GameObject line = GameObject.CreatePrimitive(PrimitiveType.Cylinder); + Destroy(line.GetComponent()); + line.name = "Direction"; + line.transform.parent = shot.transform; + line.transform.localScale = new Vector3(0.005f, 1f, 0.005f); + } + + void SetShotStart(string name, Vector3 p) + { + transform.Find(name).position = p; // Sets the fathers position (start is the reference) + } + + void SetShotEnd(string name, Vector3 p) + { + transform.Find(name + "/DirectionEnd").position = p; // Sets the direction's end position + + // Set the shooting line + Vector3 ps = transform.Find(name).position; + Vector3 pe = p; + Transform dir = transform.Find(name + "/Direction"); + float length = (pe - ps).magnitude; + dir.position = (pe + ps) * 0.5f; + dir.localScale = new Vector3(0.005f, length * 0.5f, 0.005f);// *Globals.g_Scale; + dir.LookAt(pe); + dir.Rotate(90.0f, 0.0f, 0.0f); + + } + + // Raycast from mouse pos + bool RayCastMouse(ref Vector3 pos) + { + Camera cam = null; + cam = Camera.main; + if (cam == null) + return false; + + var mouseOnScreenPos = Mouse.current.position.ReadValue(); + Ray ray = cam.ScreenPointToRay(mouseOnScreenPos); + + var p = _playfield.transform.position + _playfield.transform.up * Physics.ScaleToWorld(_playfield.TableHeight); + _playfieldPlane.SetNormalAndPosition(_playfield.transform.up, p); // Need update as it is rotated for display at run + + + if (_playfieldPlane.Raycast(ray, out var enter)) + { + var playfieldPosWorld = ray.GetPoint(enter); + + pos = playfieldPosWorld + _playfieldPlane.normal * Physics.ScaleToWorld(PhysicsConstants.PhysSkin); + // todo check playfield bounds + return true; + } + + return false; + } + + float m_elasped = 0f; //!< elapsed time since last click + void Update() + { + if (Camera.main == null)// || _playfield == null) + return; + + if (Mouse.current.middleButton.wasPressedThisFrame || _mouseDown) + { + Vector3 pos = new Vector3(); // hit point + if (!_mouseDown) // just clicked (set start) + { + m_elasped = 0f; + if (RayCastMouse(ref pos)) + SetShotStart("Current", pos); + + if (!_activated) + SetVisible(true, transform.Find("Current").gameObject); + } + else + { + m_elasped += Time.deltaTime; + if (RayCastMouse(ref pos)) + { + SetShotEnd("Current", pos); + // SetShotForce ("Current",50f);//1f/m_elasped); + } + } + _mouseDown = true; + } + + if (Mouse.current.middleButton.wasReleasedThisFrame) + { + if (_mouseDown == true) // just released + { + LaunchShot("Current"); + if (!_activated) + SetVisible(false, transform.Find("Current").gameObject); + } + _mouseDown = false; + } + + if (Keyboard.current.spaceKey.wasPressedThisFrame) + LaunchShot("Current"); + } + + public static float SignedAngle(Vector3 from, Vector3 to, Vector3 normal) + { + // angle in [0,180] + float angle = Vector3.Angle(from, to); + float sign = Mathf.Sign(Vector3.Dot(normal, Vector3.Cross(from, to))); + return angle * sign; + } + + Entity FindNearest(float2 physicPos) + { + Entity ret = Entity.Null; + var ballEntities = _ballEntityQuery.ToEntityArray(Allocator.Temp); + var nearestDistance = float.PositiveInfinity; + BallData nearestBall = default; + foreach (var ballEntity in ballEntities) + { + var ballData = _entityManager.GetComponentData(ballEntity); + if (ballData.IsFrozen) + { + continue; + } + var distance = math.distance(physicPos, ballData.Position.xy); + if (distance < nearestDistance) + { + nearestDistance = distance; + nearestBall = ballData; + ret = ballEntity; + } + } + + return ret; + + } + + void LaunchShot(string name) + { + var ballManager = GameObject.FindObjectOfType()?.BallManager; // Need to update as it is not created at start (TODO: change script order but not important as one shot) + if (ballManager == null) + return; + Vector3 ps = _wtl.MultiplyPoint(transform.Find(name).position); + Vector3 pe = _wtl.MultiplyPoint(transform.Find(name + "/DirectionEnd").position); + + var dir = pe - ps; + float mag = dir.magnitude; // To reuse magnitude for force + + float angle = mag>Mathf.Epsilon ? Vector3.SignedAngle(Vector3.up, dir/mag, Vector3.forward) + 180F : 0F; + + if (!Keyboard.current.leftCtrlKey.isPressed) + { + var nearest = FindNearest(new float2(ps.x, ps.y)); + if (nearest != Entity.Null) + ballManager.DestroyEntity(nearest); + } + ballManager.CreateBall(new DebugBallCreator(ps.x, ps.y, PhysicsConstants.PhysSkin, angle, mag * ForceMultiplier)); + + } + + } + +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs.meta new file mode 100644 index 000000000..b8ce274f7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: edcb711c769cb9046b46430fa4447c1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From aa9b91a476ba58b08e0a75228c9240e1136ceb19 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 19 Apr 2025 14:35:50 +0200 Subject: [PATCH 07/11] debug: Move some logic from ball roller component in to base class and ball manager. --- .../Game/BallDebugComponent.cs | 58 +++++++++++++++ .../Game/BallDebugComponent.cs.meta | 3 + .../Game/BallRollerComponent.cs | 73 +------------------ .../VPT/Ball/BallManager.cs | 25 +++++++ 4 files changed, 90 insertions(+), 69 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs new file mode 100644 index 000000000..f22782993 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs @@ -0,0 +1,58 @@ +using Unity.Mathematics; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace VisualPinball.Unity +{ + public abstract class BallDebugComponent : MonoBehaviour + { + protected PhysicsEngine _physicsEngine; + protected PlayfieldComponent _playfield; + protected Player _player; + protected Matrix4x4 _ltw; + protected Matrix4x4 _wtl; + + protected Plane _playfieldPlane; + + protected int _ballId = 0; + + private void Awake() + { + _playfield = GetComponentInChildren(); + _player = GetComponentInChildren(); + + _ltw = Physics.VpxToWorld; + _wtl = Physics.WorldToVpx; + + var p1 = _ltw.MultiplyPoint(new Vector3(-100f, 100f, 0)); + var p2 = _ltw.MultiplyPoint(new Vector3(100f, 100f, 0)); + var p3 = _ltw.MultiplyPoint(new Vector3(100f, -100f, 0)); + _playfieldPlane.Set3Points(p1, p2, p3); + _physicsEngine = GetComponentInChildren(); + } + + + protected bool GetCursorPositionOnPlayfield(out float2 position) + { + if (!Camera.main) { + position = float2.zero; + return false; + } + + var mouseOnScreenPos = Mouse.current.position.ReadValue(); + var ray = Camera.main.ScreenPointToRay(mouseOnScreenPos); + + if (_playfieldPlane.Raycast(ray, out var enter)) { + var playfieldPosWorld = _playfield.transform.localToWorldMatrix.inverse.MultiplyPoint(ray.GetPoint(enter)); + var playfieldPosLocal = _wtl.MultiplyPoint(playfieldPosWorld); + + position = new float2(playfieldPosLocal.x, playfieldPosLocal.y); + + // todo check playfield bounds + return true; + } + position = float2.zero; + return false; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs.meta new file mode 100644 index 000000000..fca50ad78 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dfe9af57cee842449d63397ffed037bc +timeCreated: 1745065405 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs index 2a0066186..002de0604 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs @@ -21,7 +21,7 @@ namespace VisualPinball.Unity { [PackAs("BallRoller")] - public class BallRollerComponent : MonoBehaviour, IPackable + public class BallRollerComponent : BallDebugComponent, IPackable { #region Packaging @@ -35,60 +35,18 @@ public void UnpackReferences(byte[] bytes, Transform root, PackagedRefs refs, Pa #endregion - private PhysicsEngine _physicsEngine; - private PlayfieldComponent _playfield; - private Matrix4x4 _ltw; - private Matrix4x4 _wtl; - - private Plane _playfieldPlane; - - private int _ballId = 0; - - private void Awake() - { - _playfield = GetComponentInChildren(); - - _ltw = Physics.VpxToWorld; - _wtl = Physics.WorldToVpx; - - var p1 = _ltw.MultiplyPoint(new Vector3(-100f, 100f, 0)); - var p2 = _ltw.MultiplyPoint(new Vector3(100f, 100f, 0)); - var p3 = _ltw.MultiplyPoint(new Vector3(100f, -100f, 0)); - _playfieldPlane.Set3Points(p1, p2, p3); - _physicsEngine = GetComponentInChildren(); - } - private void Update() { - if (Camera.main == null || _playfield == null) { + if (!Camera.main || !_playfield || !_player) { return; } // find nearest ball if (Mouse.current.middleButton.wasPressedThisFrame) { if (GetCursorPositionOnPlayfield(out var mousePosition)) { - var nearestDistance = float.PositiveInfinity; - BallState nearestBall = default; - var ballFound = false; - - using (var enumerator = _physicsEngine.Balls.GetEnumerator()) { - while (enumerator.MoveNext()) { - var ball = enumerator.Current.Value; - - if (ball.IsFrozen) { - continue; - } - var distance = math.distance(mousePosition, ball.Position.xy); - if (distance < nearestDistance) { - nearestDistance = distance; - nearestBall = ball; - ballFound = true; - _ballId = ball.Id; - } - } - } - if (ballFound) { + if (_player.BallManager.FindNearest(mousePosition, out var nearestBall)) { + _ballId = nearestBall.Id; UpdateBall(ref nearestBall, mousePosition); } } @@ -112,28 +70,5 @@ private void UpdateBall(ref BallState ballState, float2 position) ballState.ManualControl = true; ballState.ManualPosition = position; } - - private bool GetCursorPositionOnPlayfield(out float2 position) - { - if (Camera.main == null) { - position = float2.zero; - return false; - } - - var mouseOnScreenPos = Mouse.current.position.ReadValue(); - var ray = Camera.main.ScreenPointToRay(mouseOnScreenPos); - - if (_playfieldPlane.Raycast(ray, out var enter)) { - var playfieldPosWorld = _playfield.transform.localToWorldMatrix.inverse.MultiplyPoint(ray.GetPoint(enter)); - var playfieldPosLocal = _wtl.MultiplyPoint(playfieldPosWorld); - - position = new float2(playfieldPosLocal.x, playfieldPosLocal.y); - - // todo check playfield bounds - return true; - } - position = float2.zero; - return false; - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs index 9d25dd573..e12a128a3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs @@ -15,6 +15,8 @@ // along with this program. If not, see . using System; +using Unity.Collections; +using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.Game; using Object = UnityEngine.Object; @@ -73,5 +75,28 @@ public void DestroyBall(int ballId) // destroy game object Object.DestroyImmediate(ballTransform.gameObject); } + + public bool FindNearest(float2 fromPosition, out BallState nearestBall) + { + var nearestDistance = float.PositiveInfinity; + nearestBall = default; + var ballFound = false; + + using var enumerator = _physicsEngine.Balls.GetEnumerator(); + while (enumerator.MoveNext()) { + var ball = enumerator.Current.Value; + + if (ball.IsFrozen) { + continue; + } + var distance = math.distance(fromPosition, ball.Position.xy); + if (distance < nearestDistance) { + nearestDistance = distance; + nearestBall = ball; + ballFound = true; + } + } + return ballFound; + } } } From 91e82b9c54c5912252c3cfa4548e563f19033774 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 19 Apr 2025 15:24:43 +0200 Subject: [PATCH 08/11] debug: Rename and merge debug shot component. --- .../VisualPinball.Unity.Editor/Utils/Icons.cs | 4 +- .../Game/BallDebugComponent.cs | 18 +- .../Game/BallRollerComponent.cs | 10 +- .../Game/BallShotComponent.cs | 178 ++++++++++++ ...nent.cs.meta => BallShotComponent.cs.meta} | 0 .../Game/DebugShotComponent.cs | 267 ------------------ 6 files changed, 193 insertions(+), 284 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs rename VisualPinball.Unity/VisualPinball.Unity/Game/{DebugShotComponent.cs.meta => BallShotComponent.cs.meta} (100%) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index 516fc6a3f..1655ca5af 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -291,7 +291,7 @@ internal class IconLookup : IIconLookup public Texture2D Lookup(T mb, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) where T : class { switch (mb) { - case DebugShotComponent _: return Icons.BallRoller(size, color); + case BallShotComponent _: return Icons.BallRoller(size, color); case BallComponent _: return Icons.Ball(size, color); case BallRollerComponent _: return Icons.BallRoller(size, color); case BumperComponent _: return Icons.Bumper(size, color); @@ -331,7 +331,7 @@ public Texture2D Lookup(T mb, IconSize size = IconSize.Large, IconColor color public void DisableGizmoIcons() { - Icons.DisableGizmo(); + Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs index f22782993..f0ae24471 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs @@ -18,8 +18,9 @@ public abstract class BallDebugComponent : MonoBehaviour private void Awake() { - _playfield = GetComponentInChildren(); - _player = GetComponentInChildren(); + _physicsEngine = GetComponentInParent(); + _playfield = GetComponentInParent(); + _player = GetComponentInParent(); _ltw = Physics.VpxToWorld; _wtl = Physics.WorldToVpx; @@ -28,14 +29,14 @@ private void Awake() var p2 = _ltw.MultiplyPoint(new Vector3(100f, 100f, 0)); var p3 = _ltw.MultiplyPoint(new Vector3(100f, -100f, 0)); _playfieldPlane.Set3Points(p1, p2, p3); - _physicsEngine = GetComponentInChildren(); } - protected bool GetCursorPositionOnPlayfield(out float2 position) + protected bool GetCursorPositionOnPlayfield(out float3 vpxPos, out float3 worldPos) { + vpxPos = float3.zero; + worldPos = float3.zero; if (!Camera.main) { - position = float2.zero; return false; } @@ -43,15 +44,12 @@ protected bool GetCursorPositionOnPlayfield(out float2 position) var ray = Camera.main.ScreenPointToRay(mouseOnScreenPos); if (_playfieldPlane.Raycast(ray, out var enter)) { - var playfieldPosWorld = _playfield.transform.localToWorldMatrix.inverse.MultiplyPoint(ray.GetPoint(enter)); - var playfieldPosLocal = _wtl.MultiplyPoint(playfieldPosWorld); - - position = new float2(playfieldPosLocal.x, playfieldPosLocal.y); + worldPos = _playfield.transform.localToWorldMatrix.inverse.MultiplyPoint(ray.GetPoint(enter)); + vpxPos = _wtl.MultiplyPoint(worldPos); // todo check playfield bounds return true; } - position = float2.zero; return false; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs index 002de0604..d8e2b99c5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs @@ -43,18 +43,18 @@ private void Update() // find nearest ball if (Mouse.current.middleButton.wasPressedThisFrame) { - if (GetCursorPositionOnPlayfield(out var mousePosition)) { + if (GetCursorPositionOnPlayfield(out var mousePosition, out var _)) { - if (_player.BallManager.FindNearest(mousePosition, out var nearestBall)) { + if (_player.BallManager.FindNearest(mousePosition.xy, out var nearestBall)) { _ballId = nearestBall.Id; - UpdateBall(ref nearestBall, mousePosition); + UpdateBall(ref nearestBall, mousePosition.xy); } } } else if (Mouse.current.middleButton.isPressed && _ballId != 0) { - if (GetCursorPositionOnPlayfield(out var mousePosition)) { + if (GetCursorPositionOnPlayfield(out var mousePosition, out var _)) { ref var ball = ref _physicsEngine.BallState(_ballId); - UpdateBall(ref ball, mousePosition); + UpdateBall(ref ball, mousePosition.xy); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs new file mode 100644 index 000000000..41a77879d --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs @@ -0,0 +1,178 @@ +// Visual Pinball Engine +// Copyright (C) 2023 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using Unity.Mathematics; +using UnityEngine; +using UnityEngine.InputSystem; +using VisualPinball.Engine.Common; + +namespace VisualPinball.Unity +{ + public class BallShotComponent : BallDebugComponent + { + bool _activated = false; //!< DebugShot mode activation flag + bool _mouseDown = false; //!< mouse button down flag + + public float ForceMultiplier = 0.2F; + + float m_elasped = 0f; //!< elapsed time since last click + + + private void Start() + { + CreateShotGizmos("Current"); + + //for (int i = 1; i <= 12; i++) + //{ + // CreateShot("F" + i); + //} + + SetVisible(false); + } + + private void SetVisible(bool b, GameObject o = null) + { + if (o == null) { + o = gameObject; + } + + if (o.GetComponent() != null) { + o.GetComponent().enabled = b; + } + + for (var i = 0; i < o.transform.childCount; i++) { + SetVisible(b, o.transform.GetChild(i).gameObject); + } + } + + private void CreateShotGizmos(string name) + { + // If already loaded, to not modify + if (transform.Find(name) != null) { + return; + } + + // Father object + var shot = GameObject.CreatePrimitive(PrimitiveType.Plane); + Destroy(shot.GetComponent()); + Destroy(shot.GetComponent()); + shot.name = name; + shot.transform.parent = transform; + + // Shot start + var point1 = GameObject.CreatePrimitive(PrimitiveType.Sphere); + Destroy(point1.GetComponent()); + point1.name = "Start"; + point1.transform.parent = shot.transform; + point1.transform.localScale = new Vector3(1f, 1f, 1f) * 0.027f;// * Globals.g_Scale; + // Shot direction end + var point2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); + Destroy(point2.GetComponent()); + point2.name = "DirectionEnd"; + point2.transform.parent = shot.transform; + point2.transform.localScale = new Vector3(1f, 1f, 1f) * 0.027f;// * Globals.g_Scale; + + var line = GameObject.CreatePrimitive(PrimitiveType.Cylinder); + Destroy(line.GetComponent()); + line.name = "Direction"; + line.transform.parent = shot.transform; + line.transform.localScale = new Vector3(0.005f, 1f, 0.005f); + } + + private void SetShotStart(string n, Vector3 p) + { + Debug.Log("--------- SetShotStart " + n + " " + p); + transform.Find(n).position = p; // Sets the fathers position (start is the reference) + } + + private void SetShotEnd(string n, Vector3 p) + { + transform.Find(n + "/DirectionEnd").position = p; // Sets the direction's end position + + // Set the shooting line + var ps = transform.Find(n).position; + var pe = p; + var dir = transform.Find(n + "/Direction"); + var length = (pe - ps).magnitude; + dir.position = (pe + ps) * 0.5f; + dir.localScale = new Vector3(0.005f, length * 0.5f, 0.005f);// *Globals.g_Scale; + dir.LookAt(pe); + dir.Rotate(90.0f, 0.0f, 0.0f); + } + + private void Update() + { + if (!Camera.main || !_playfield || !_player) { + return; + } + + if (Mouse.current.middleButton.wasPressedThisFrame || _mouseDown) { + if (!_mouseDown) { // just clicked (set start) + m_elasped = 0f; + if (GetCursorPositionOnPlayfield(out var vpxPos, out var worldPos)) { + SetShotStart("Current", worldPos); + } + + if (!_activated) { + SetVisible(true, transform.Find("Current").gameObject); + } + + } else { + m_elasped += Time.deltaTime; + if (GetCursorPositionOnPlayfield(out var pos, out var worldPos)) { + SetShotEnd("Current", worldPos); + // SetShotForce ("Current",50f);//1f/m_elasped); + } + } + _mouseDown = true; + } + + if (Mouse.current.middleButton.wasReleasedThisFrame) { + if (_mouseDown) { // just released + LaunchShot("Current"); + if (!_activated) { + SetVisible(false, transform.Find("Current").gameObject); + } + } + _mouseDown = false; + } + + if (Keyboard.current.spaceKey.wasPressedThisFrame) { + LaunchShot("Current"); + } + } + + private void LaunchShot(string n) + { + var ps = _wtl.MultiplyPoint(transform.Find(n).position); + var pe = _wtl.MultiplyPoint(transform.Find(n + "/DirectionEnd").position); + + var dir = pe - ps; + var mag = dir.magnitude; // To reuse magnitude for force + + var angle = mag > Mathf.Epsilon + ? Vector3.SignedAngle(Vector3.up, dir/mag, Vector3.forward) + 180F + : 0F; + + if (!Keyboard.current.leftCtrlKey.isPressed) { + if (_player.BallManager.FindNearest(new float2(ps.x, ps.y), out var nearestBall)) { + _player.BallManager.DestroyBall(nearestBall.Id); + } + } + _player.BallManager.CreateBall(new DebugBallCreator(ps.x, ps.y, PhysicsConstants.PhysSkin, angle, mag * ForceMultiplier)); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs.meta similarity index 100% rename from VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs deleted file mode 100644 index efb1e2f0c..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugShotComponent.cs +++ /dev/null @@ -1,267 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 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. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using UnityEngine; -using System.Collections; -using System.Collections.Generic; -using VisualPinball.Engine.Common; -using UnityEngine.InputSystem; -using VisualPinball.Unity; -using Unity.Mathematics; -using Unity.Entities; -using Unity.Collections; - -namespace VisualPinball.Unity -{ - public class DebugShotComponent : MonoBehaviour - { - private EntityManager _entityManager; - private EntityQuery _ballEntityQuery; - - bool _activated = false; //!< DebugShot mode activation flag - bool _mouseDown = false; //!< mouse button down flag - - private PlayfieldComponent _playfield; - private Matrix4x4 _ltw; - private Matrix4x4 _wtl; - - private Plane _playfieldPlane; - - public float ForceMultiplier = 0.2F; - - - private void Awake() - { - _playfield = GameObject.FindObjectOfType(); - _ltw = Physics.VpxToWorld; - _wtl = Physics.WorldToVpx; - - _entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; - _ballEntityQuery = _entityManager.CreateEntityQuery(typeof(BallData)); - } - - void Start() - { - { - CreateShot("Current"); - } - - //for (int i = 1; i <= 12; i++) - //{ - // CreateShot("F" + i); - //} - - SetVisible(false); - } - - void SetVisible(bool b, GameObject o = null) - { - if (o == null) - o = gameObject; - - if (o.GetComponent() != null) - o.GetComponent().enabled = b; - - for (int i = 0; i < o.transform.childCount; i++) - SetVisible(b, o.transform.GetChild(i).gameObject); - } - - void CreateShot(string name) - { - // If already loaded, to not modify - if (transform.Find(name) != null) - return; - - // Father object - GameObject shot = GameObject.CreatePrimitive(PrimitiveType.Plane); - Destroy(shot.GetComponent()); - Destroy(shot.GetComponent()); - shot.name = name; - shot.transform.parent = transform; - - // Shot start - GameObject point1 = GameObject.CreatePrimitive(PrimitiveType.Sphere); - Destroy(point1.GetComponent()); - point1.name = "Start"; - point1.transform.parent = shot.transform; - point1.transform.localScale = new Vector3(1f, 1f, 1f) * Physics.ScaleToWorld(PhysicsConstants.PhysSkin*2);// * Globals.g_Scale; - // Shot direction end - GameObject point2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); - Destroy(point2.GetComponent()); - point2.name = "DirectionEnd"; - point2.transform.parent = shot.transform; - point2.transform.localScale = new Vector3(1f, 1f, 1f) * Physics.ScaleToWorld(PhysicsConstants.PhysSkin*2);// * Globals.g_Scale; - - GameObject line = GameObject.CreatePrimitive(PrimitiveType.Cylinder); - Destroy(line.GetComponent()); - line.name = "Direction"; - line.transform.parent = shot.transform; - line.transform.localScale = new Vector3(0.005f, 1f, 0.005f); - } - - void SetShotStart(string name, Vector3 p) - { - transform.Find(name).position = p; // Sets the fathers position (start is the reference) - } - - void SetShotEnd(string name, Vector3 p) - { - transform.Find(name + "/DirectionEnd").position = p; // Sets the direction's end position - - // Set the shooting line - Vector3 ps = transform.Find(name).position; - Vector3 pe = p; - Transform dir = transform.Find(name + "/Direction"); - float length = (pe - ps).magnitude; - dir.position = (pe + ps) * 0.5f; - dir.localScale = new Vector3(0.005f, length * 0.5f, 0.005f);// *Globals.g_Scale; - dir.LookAt(pe); - dir.Rotate(90.0f, 0.0f, 0.0f); - - } - - // Raycast from mouse pos - bool RayCastMouse(ref Vector3 pos) - { - Camera cam = null; - cam = Camera.main; - if (cam == null) - return false; - - var mouseOnScreenPos = Mouse.current.position.ReadValue(); - Ray ray = cam.ScreenPointToRay(mouseOnScreenPos); - - var p = _playfield.transform.position + _playfield.transform.up * Physics.ScaleToWorld(_playfield.TableHeight); - _playfieldPlane.SetNormalAndPosition(_playfield.transform.up, p); // Need update as it is rotated for display at run - - - if (_playfieldPlane.Raycast(ray, out var enter)) - { - var playfieldPosWorld = ray.GetPoint(enter); - - pos = playfieldPosWorld + _playfieldPlane.normal * Physics.ScaleToWorld(PhysicsConstants.PhysSkin); - // todo check playfield bounds - return true; - } - - return false; - } - - float m_elasped = 0f; //!< elapsed time since last click - void Update() - { - if (Camera.main == null)// || _playfield == null) - return; - - if (Mouse.current.middleButton.wasPressedThisFrame || _mouseDown) - { - Vector3 pos = new Vector3(); // hit point - if (!_mouseDown) // just clicked (set start) - { - m_elasped = 0f; - if (RayCastMouse(ref pos)) - SetShotStart("Current", pos); - - if (!_activated) - SetVisible(true, transform.Find("Current").gameObject); - } - else - { - m_elasped += Time.deltaTime; - if (RayCastMouse(ref pos)) - { - SetShotEnd("Current", pos); - // SetShotForce ("Current",50f);//1f/m_elasped); - } - } - _mouseDown = true; - } - - if (Mouse.current.middleButton.wasReleasedThisFrame) - { - if (_mouseDown == true) // just released - { - LaunchShot("Current"); - if (!_activated) - SetVisible(false, transform.Find("Current").gameObject); - } - _mouseDown = false; - } - - if (Keyboard.current.spaceKey.wasPressedThisFrame) - LaunchShot("Current"); - } - - public static float SignedAngle(Vector3 from, Vector3 to, Vector3 normal) - { - // angle in [0,180] - float angle = Vector3.Angle(from, to); - float sign = Mathf.Sign(Vector3.Dot(normal, Vector3.Cross(from, to))); - return angle * sign; - } - - Entity FindNearest(float2 physicPos) - { - Entity ret = Entity.Null; - var ballEntities = _ballEntityQuery.ToEntityArray(Allocator.Temp); - var nearestDistance = float.PositiveInfinity; - BallData nearestBall = default; - foreach (var ballEntity in ballEntities) - { - var ballData = _entityManager.GetComponentData(ballEntity); - if (ballData.IsFrozen) - { - continue; - } - var distance = math.distance(physicPos, ballData.Position.xy); - if (distance < nearestDistance) - { - nearestDistance = distance; - nearestBall = ballData; - ret = ballEntity; - } - } - - return ret; - - } - - void LaunchShot(string name) - { - var ballManager = GameObject.FindObjectOfType()?.BallManager; // Need to update as it is not created at start (TODO: change script order but not important as one shot) - if (ballManager == null) - return; - Vector3 ps = _wtl.MultiplyPoint(transform.Find(name).position); - Vector3 pe = _wtl.MultiplyPoint(transform.Find(name + "/DirectionEnd").position); - - var dir = pe - ps; - float mag = dir.magnitude; // To reuse magnitude for force - - float angle = mag>Mathf.Epsilon ? Vector3.SignedAngle(Vector3.up, dir/mag, Vector3.forward) + 180F : 0F; - - if (!Keyboard.current.leftCtrlKey.isPressed) - { - var nearest = FindNearest(new float2(ps.x, ps.y)); - if (nearest != Entity.Null) - ballManager.DestroyEntity(nearest); - } - ballManager.CreateBall(new DebugBallCreator(ps.x, ps.y, PhysicsConstants.PhysSkin, angle, mag * ForceMultiplier)); - - } - - } - -} From 3ae99996de90aae6af42edc08262674c43564c0e Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 19 Apr 2025 22:38:17 +0200 Subject: [PATCH 09/11] style: Minor refactoring. --- .../Game/BallShotComponent.cs | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs index 41a77879d..38982259e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs @@ -30,10 +30,14 @@ public class BallShotComponent : BallDebugComponent float m_elasped = 0f; //!< elapsed time since last click + private const string NameParent = "Current"; + private const string NameStart = "StartGizmo"; + private const string NameEnd = "EndGizmo"; + private const string NameDirection = "DirectionGizmo"; private void Start() { - CreateShotGizmos("Current"); + CreateShotGizmos(NameParent); //for (int i = 1; i <= 12; i++) //{ @@ -75,19 +79,20 @@ private void CreateShotGizmos(string name) // Shot start var point1 = GameObject.CreatePrimitive(PrimitiveType.Sphere); Destroy(point1.GetComponent()); - point1.name = "Start"; + point1.name = NameStart; point1.transform.parent = shot.transform; point1.transform.localScale = new Vector3(1f, 1f, 1f) * 0.027f;// * Globals.g_Scale; - // Shot direction end + + // Shot direction end var point2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); Destroy(point2.GetComponent()); - point2.name = "DirectionEnd"; + point2.name = NameEnd; point2.transform.parent = shot.transform; point2.transform.localScale = new Vector3(1f, 1f, 1f) * 0.027f;// * Globals.g_Scale; var line = GameObject.CreatePrimitive(PrimitiveType.Cylinder); Destroy(line.GetComponent()); - line.name = "Direction"; + line.name = NameDirection; line.transform.parent = shot.transform; line.transform.localScale = new Vector3(0.005f, 1f, 0.005f); } @@ -95,19 +100,19 @@ private void CreateShotGizmos(string name) private void SetShotStart(string n, Vector3 p) { Debug.Log("--------- SetShotStart " + n + " " + p); - transform.Find(n).position = p; // Sets the fathers position (start is the reference) + transform.Find(n).localPosition = p; // Sets the fathers position (start is the reference) } private void SetShotEnd(string n, Vector3 p) { - transform.Find(n + "/DirectionEnd").position = p; // Sets the direction's end position + transform.Find($"{n}/{NameEnd}").localPosition = p; // Sets the direction's end position // Set the shooting line - var ps = transform.Find(n).position; + var ps = transform.Find(n).localPosition; var pe = p; - var dir = transform.Find(n + "/Direction"); + var dir = transform.Find($"{n}/{NameDirection}"); var length = (pe - ps).magnitude; - dir.position = (pe + ps) * 0.5f; + dir.localPosition = (pe + ps) * 0.5f; dir.localScale = new Vector3(0.005f, length * 0.5f, 0.005f);// *Globals.g_Scale; dir.LookAt(pe); dir.Rotate(90.0f, 0.0f, 0.0f); @@ -123,18 +128,18 @@ private void Update() if (!_mouseDown) { // just clicked (set start) m_elasped = 0f; if (GetCursorPositionOnPlayfield(out var vpxPos, out var worldPos)) { - SetShotStart("Current", worldPos); + SetShotStart(NameParent, worldPos); } if (!_activated) { - SetVisible(true, transform.Find("Current").gameObject); + SetVisible(true, transform.Find(NameParent).gameObject); } } else { m_elasped += Time.deltaTime; if (GetCursorPositionOnPlayfield(out var pos, out var worldPos)) { - SetShotEnd("Current", worldPos); - // SetShotForce ("Current",50f);//1f/m_elasped); + SetShotEnd(NameParent, worldPos); + // SetShotForce (NameParent,50f);//1f/m_elasped); } } _mouseDown = true; @@ -142,23 +147,23 @@ private void Update() if (Mouse.current.middleButton.wasReleasedThisFrame) { if (_mouseDown) { // just released - LaunchShot("Current"); + LaunchShot(NameParent); if (!_activated) { - SetVisible(false, transform.Find("Current").gameObject); + SetVisible(false, transform.Find(NameParent).gameObject); } } _mouseDown = false; } if (Keyboard.current.spaceKey.wasPressedThisFrame) { - LaunchShot("Current"); + LaunchShot(NameParent); } } private void LaunchShot(string n) { - var ps = _wtl.MultiplyPoint(transform.Find(n).position); - var pe = _wtl.MultiplyPoint(transform.Find(n + "/DirectionEnd").position); + var ps = _wtl.MultiplyPoint(transform.Find(n).localPosition); + var pe = _wtl.MultiplyPoint(transform.Find($"{n}/{NameEnd}").localPosition); var dir = pe - ps; var mag = dir.magnitude; // To reuse magnitude for force From 8cecb0b27facc11a32ee0e03c5629af8af501f5d Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 19 Apr 2025 23:02:44 +0200 Subject: [PATCH 10/11] debug: Fix debug shot gizmos and shoot direction. --- .../Game/BallShotComponent.cs | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs index 38982259e..103a6c965 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallShotComponent.cs @@ -24,12 +24,13 @@ namespace VisualPinball.Unity public class BallShotComponent : BallDebugComponent { bool _activated = false; //!< DebugShot mode activation flag - bool _mouseDown = false; //!< mouse button down flag + bool _mouseDown; //!< mouse button down flag public float ForceMultiplier = 0.2F; float m_elasped = 0f; //!< elapsed time since last click + private const float GizmoHeight = 0.025f; private const string NameParent = "Current"; private const string NameStart = "StartGizmo"; private const string NameEnd = "EndGizmo"; @@ -62,59 +63,59 @@ private void SetVisible(bool b, GameObject o = null) } } - private void CreateShotGizmos(string name) + private void CreateShotGizmos(string parent) { // If already loaded, to not modify - if (transform.Find(name) != null) { + if (transform.Find(parent) != null) { return; } // Father object - var shot = GameObject.CreatePrimitive(PrimitiveType.Plane); - Destroy(shot.GetComponent()); - Destroy(shot.GetComponent()); - shot.name = name; - shot.transform.parent = transform; + var plane = GameObject.CreatePrimitive(PrimitiveType.Plane); + Destroy(plane.GetComponent()); + Destroy(plane.GetComponent()); + plane.name = parent; + plane.transform.SetParent(transform, false); // Shot start var point1 = GameObject.CreatePrimitive(PrimitiveType.Sphere); Destroy(point1.GetComponent()); point1.name = NameStart; - point1.transform.parent = shot.transform; - point1.transform.localScale = new Vector3(1f, 1f, 1f) * 0.027f;// * Globals.g_Scale; + point1.transform.parent = plane.transform; + point1.transform.localScale = Vector3.one * 0.027f; // ball size = 27mm // Shot direction end var point2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); Destroy(point2.GetComponent()); point2.name = NameEnd; - point2.transform.parent = shot.transform; - point2.transform.localScale = new Vector3(1f, 1f, 1f) * 0.027f;// * Globals.g_Scale; + point2.transform.parent = plane.transform; + point2.transform.localScale = Vector3.one * 0.027f; var line = GameObject.CreatePrimitive(PrimitiveType.Cylinder); Destroy(line.GetComponent()); line.name = NameDirection; - line.transform.parent = shot.transform; + line.transform.parent = plane.transform; line.transform.localScale = new Vector3(0.005f, 1f, 0.005f); } - private void SetShotStart(string n, Vector3 p) + private void SetShotStart(string parent, float3 p) { - Debug.Log("--------- SetShotStart " + n + " " + p); - transform.Find(n).localPosition = p; // Sets the fathers position (start is the reference) + transform.Find($"{parent}/{NameStart}").localPosition = new float3(p.x, GizmoHeight, p.z); // Sets the fathers position (start is the reference) } - private void SetShotEnd(string n, Vector3 p) + private void SetShotEnd(string parent, float3 p) { - transform.Find($"{n}/{NameEnd}").localPosition = p; // Sets the direction's end position + p = new float3(p.x, GizmoHeight, p.z); + transform.Find($"{parent}/{NameEnd}").localPosition = p; // Sets the direction's end position // Set the shooting line - var ps = transform.Find(n).localPosition; - var pe = p; - var dir = transform.Find($"{n}/{NameDirection}"); + var ps = transform.Find($"{parent}/{NameStart}").localPosition; + var pe = (Vector3)p; + var dir = transform.Find($"{parent}/{NameDirection}"); var length = (pe - ps).magnitude; dir.localPosition = (pe + ps) * 0.5f; dir.localScale = new Vector3(0.005f, length * 0.5f, 0.005f);// *Globals.g_Scale; - dir.LookAt(pe); + dir.LookAt(_playfield.transform.localToWorldMatrix.MultiplyPoint(pe)); dir.Rotate(90.0f, 0.0f, 0.0f); } @@ -162,7 +163,7 @@ private void Update() private void LaunchShot(string n) { - var ps = _wtl.MultiplyPoint(transform.Find(n).localPosition); + var ps = _wtl.MultiplyPoint(transform.Find($"{n}/{NameStart}").localPosition); var pe = _wtl.MultiplyPoint(transform.Find($"{n}/{NameEnd}").localPosition); var dir = pe - ps; From 275bbcfaf51046978fef23e3d06c8fae076400ef Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 20 Apr 2025 12:46:10 +0200 Subject: [PATCH 11/11] switch: Remove math.sin from previous test. --- .../VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs index fb72b45df..df769c3a1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/SwitchAnimationComponent.cs @@ -115,7 +115,7 @@ private void Update() var yPos = ForwardsAnimationCurve.Evaluate(math.unlerp(_yEnter, _yExit, localVpxPos.y)); // yPos is between 0 and 1, depending on where localVpxPos.y is _currentAngle = math.clamp(math.lerp(_startAngle, EndAngle, yPos), _startAngle, EndAngle); - transform.SetLocalXRotation(math.sin(math.radians(_currentAngle))); + transform.SetLocalXRotation(math.radians(_currentAngle)); } private void UnHit(object sender, HitEventArgs e)