From a180b4091ca2430a54fa8be5cb717b843e326488 Mon Sep 17 00:00:00 2001 From: BilboX Date: Sat, 5 Jun 2021 21:42:19 -1000 Subject: [PATCH 01/23] add: Create nFozzy like correction objects: not sure FlipperInspector is the best place to do so... --- .../VPT/Flipper/FlipperInspector.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs index b39f0b944..33f5c30e7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs @@ -17,7 +17,12 @@ // ReSharper disable AssignmentInConditionalExpression using UnityEditor; +using UnityEngine; using VisualPinball.Engine.VPT.Flipper; +using VisualPinball.Engine.VPT.Trigger; +using Unity.Entities; +using Unity.Mathematics; +using VisualPinball.Engine.Math; namespace VisualPinball.Unity.Editor { @@ -88,6 +93,50 @@ public override void OnInspectorGUI() } EditorGUILayout.EndFoldoutHeaderGroup(); + if (GUILayout.Button("Setup nFozzy Corrections")) + { + var fa = target as FlipperAuthoring; + + // Get table reference + TableAuthoring table = fa.GetComponentInParent(); + if (fa != null && table != null) + { + + GameObject nFozzyCorrection = new GameObject(fa.name + "_nFozzy"); + nFozzyCorrection.transform.parent = fa.transform.parent; + nFozzyCorrection.transform.localScale = fa.transform.localScale; + nFozzyCorrection.transform.localPosition = fa.transform.localPosition; + nFozzyCorrection.transform.rotation = table.transform.rotation; + + TriggerData data = new TriggerData(fa.name + "_nFozzy", Data.Center.X, Data.Center.Y); + var poly = fa.GetEnclosingPolygon(23, 12); + data.DragPoints = new Engine.Math.DragPointData[poly.Count]; + data.IsLocked = true; + + for (int i = 0; i < poly.Count; i++) + { + // Poly points are expressed in flipper's frame: transpose to Table's frame as this is the basis uses for drag points + var p = table.transform.InverseTransformPoint(fa.transform.TransformPoint(poly[i])); + data.DragPoints[i] = new Engine.Math.DragPointData(p.x, p.y); + } + var triggerAuth = nFozzyCorrection.AddComponent(); + Trigger trigger = new Trigger(data); + triggerAuth.SetItem(trigger); + + var triggerColl = nFozzyCorrection.AddComponent(); + triggerColl.Data.HitHeight = 150F; // nFozzy's recommandation, but I think 50 should be ok + triggerColl.ItemDataChanged(); + + nFozzyCorrection.AddComponent(); + + // Register to Table + if (table.Table != null) + table.Table.Add(trigger); + + Undo.RegisterCreatedObjectUndo(nFozzyCorrection, "Create nFozzy's corrections object"); + } + } + base.OnInspectorGUI(); } } From 20e76c7bd9ba84717c1495a0bc673951df6dd4d7 Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 8 Jun 2021 00:36:34 +0200 Subject: [PATCH 02/23] nfp: Add DOTS structure. --- .../VPT/Flipper/FlipperInspector.cs | 44 ----------------- .../VPT/TransformInspector.cs | 18 +++---- .../VisualPinball.Unity/Game/Player.cs | 2 +- .../Collision/StaticCollisionSystem.cs | 47 +++++++++++++++--- .../VPT/Flipper/FlipperAuthoring.cs | 48 ++++++++++++++++++- .../VPT/Flipper/FlipperCorrection.cs | 31 ++++++++++++ .../VPT/Flipper/FlipperCorrection.cs.meta | 11 +++++ .../VPT/Flipper/FlipperCorrectionAuthoring.cs | 29 +++++++++++ .../FlipperCorrectionAuthoring.cs.meta | 11 +++++ .../VPT/Flipper/FlipperCorrectionData.cs | 25 ++++++++++ .../VPT/Flipper/FlipperCorrectionData.cs.meta | 11 +++++ .../VPT/Trigger/TriggerAuthoring.cs | 2 +- 12 files changed, 217 insertions(+), 62 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs index 33f5c30e7..7d32116b3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs @@ -93,50 +93,6 @@ public override void OnInspectorGUI() } EditorGUILayout.EndFoldoutHeaderGroup(); - if (GUILayout.Button("Setup nFozzy Corrections")) - { - var fa = target as FlipperAuthoring; - - // Get table reference - TableAuthoring table = fa.GetComponentInParent(); - if (fa != null && table != null) - { - - GameObject nFozzyCorrection = new GameObject(fa.name + "_nFozzy"); - nFozzyCorrection.transform.parent = fa.transform.parent; - nFozzyCorrection.transform.localScale = fa.transform.localScale; - nFozzyCorrection.transform.localPosition = fa.transform.localPosition; - nFozzyCorrection.transform.rotation = table.transform.rotation; - - TriggerData data = new TriggerData(fa.name + "_nFozzy", Data.Center.X, Data.Center.Y); - var poly = fa.GetEnclosingPolygon(23, 12); - data.DragPoints = new Engine.Math.DragPointData[poly.Count]; - data.IsLocked = true; - - for (int i = 0; i < poly.Count; i++) - { - // Poly points are expressed in flipper's frame: transpose to Table's frame as this is the basis uses for drag points - var p = table.transform.InverseTransformPoint(fa.transform.TransformPoint(poly[i])); - data.DragPoints[i] = new Engine.Math.DragPointData(p.x, p.y); - } - var triggerAuth = nFozzyCorrection.AddComponent(); - Trigger trigger = new Trigger(data); - triggerAuth.SetItem(trigger); - - var triggerColl = nFozzyCorrection.AddComponent(); - triggerColl.Data.HitHeight = 150F; // nFozzy's recommandation, but I think 50 should be ok - triggerColl.ItemDataChanged(); - - nFozzyCorrection.AddComponent(); - - // Register to Table - if (table.Table != null) - table.Table.Add(trigger); - - Undo.RegisterCreatedObjectUndo(nFozzyCorrection, "Create nFozzy's corrections object"); - } - } - base.OnInspectorGUI(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs index 6934525f4..493ff04b2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs @@ -110,15 +110,15 @@ protected virtual void OnDisable() Tools.hidden = false; } - public override void OnInspectorGUI() - { - if (_defaultEditor != null) { - _defaultEditor.OnInspectorGUI(); - return; - } - - GUILayout.Label("VPE item transforms driven by data on the component below."); - } + // public override void OnInspectorGUI() + // { + // if (_defaultEditor != null) { + // _defaultEditor.OnInspectorGUI(); + // return; + // } + // + // GUILayout.Label("VPE item transforms driven by data on the component below."); + // } private void RebuildMeshes() { diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 1ac4ed81f..762a8cfe3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -327,7 +327,7 @@ public void RegisterSpinner(Spinner spinner, Entity entity, Entity parentEntity, _switchPlayer.RegisterSwitch(spinner, spinnerApi); } - public void RegisterTrigger(Trigger trigger, Entity entity, Entity parentEntity, GameObject go) + public void RegisterTrigger(Trigger trigger, Entity entity, Entity parentEntity) { var triggerApi = new TriggerApi(trigger, entity, parentEntity, this); TableApi.Triggers[trigger.Name] = triggerApi; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs index 7b6bf8827..99b6b5c11 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs @@ -153,14 +153,31 @@ in spinnerStaticData break; case ColliderType.TriggerCircle: - case ColliderType.TriggerLine: - { - var triggerAnimationData = GetComponent(coll.Entity); + case ColliderType.TriggerLine: { + + var triggerAnimationData = HasComponent(coll.Entity) + ? GetComponent(coll.Entity) + : new TriggerAnimationData(); + TriggerCollider.Collide( ref ballData, ref events, ref collEvent, ref insideOfs, ref triggerAnimationData, in ballEntity, in coll ); - SetComponent(coll.Entity, triggerAnimationData); + + if (HasComponent(coll.Entity)) { + if (triggerAnimationData.UnHitEvent) { + var flipperCorrectionData = GetComponent(coll.Entity); + var flipperData = GetComponent(flipperCorrectionData.FlipperEntity); + FlipperCorrection.OnBallLeaveFlipper( + ref ballData, ref flipperCorrectionData, in flipperData + ); + SetComponent(coll.Entity, flipperCorrectionData); + SetComponent(flipperCorrectionData.FlipperEntity, flipperData); + } + + } else { + SetComponent(coll.Entity, triggerAnimationData); + } break; } case ColliderType.KickerCircle: @@ -197,12 +214,30 @@ in spinnerStaticData // trigger } else if (coll.Header.ItemType == ItemType.Trigger) { - var triggerAnimationData = GetComponent(coll.Entity); + + var triggerAnimationData = HasComponent(coll.Entity) + ? GetComponent(coll.Entity) + : new TriggerAnimationData(); + TriggerCollider.Collide( ref ballData, ref events, ref collEvent, ref insideOfs, ref triggerAnimationData, in ballEntity, in coll ); - SetComponent(coll.Entity, triggerAnimationData); + + if (HasComponent(coll.Entity)) { + if (triggerAnimationData.UnHitEvent) { + var flipperCorrectionData = GetComponent(coll.Entity); + var flipperData = GetComponent(flipperCorrectionData.FlipperEntity); + FlipperCorrection.OnBallLeaveFlipper( + ref ballData, ref flipperCorrectionData, in flipperData + ); + SetComponent(coll.Entity, flipperCorrectionData); + SetComponent(flipperCorrectionData.FlipperEntity, flipperData); + } + + } else { + SetComponent(coll.Entity, triggerAnimationData); + } } else { Collider.Collide(ref coll, ref ballData, ref events, in ballEntity, in collEvent, ref random); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index e4e26b596..2f44dcd2e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -23,11 +23,14 @@ using System; using System.Collections.Generic; using System.Linq; +using Mpf.Vpe; using Unity.Entities; using Unity.Mathematics; +using UnityEditor; using UnityEngine; using VisualPinball.Engine.Game; using VisualPinball.Engine.VPT.Flipper; +using VisualPinball.Engine.VPT.Trigger; namespace VisualPinball.Unity { @@ -68,8 +71,27 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio dstManager.AddComponentData(entity, GetHitData()); dstManager.AddComponentData(entity, new SolenoidStateData { Value = false }); + var player = transform.GetComponentInParent(); + + var correctionAuthoring = gameObject.GetComponent(); + if (correctionAuthoring) { + + // create trigger + var trigger = CreateCorrectionTrigger(); + var triggerEntity = dstManager.CreateEntity(typeof(TriggerStaticData)); + dstManager.AddComponentData(triggerEntity, new TriggerStaticData()); + + // todo create special registration method since we don't need all the api stuff. + player.RegisterTrigger(trigger, triggerEntity, Entity.Null); + + // add correction data + dstManager.AddComponentData(triggerEntity, new FlipperCorrectionData { + FlipperEntity = entity + }); + } + // register - transform.GetComponentInParent().RegisterFlipper(Item, entity, ParentEntity, gameObject); + player.RegisterFlipper(Item, entity, ParentEntity, gameObject); } public override void Restore() @@ -328,5 +350,29 @@ private FlipperHitData GetHitData() LastHitFace = false, }; } + + private Trigger CreateCorrectionTrigger() + { + // Get table reference + var ta = GetComponentInParent(); + if (ta != null) { + + var data = new TriggerData(name + "_nFozzy", Data.Center.X, Data.Center.Y); + var poly = GetEnclosingPolygon(23, 12); + data.DragPoints = new Engine.Math.DragPointData[poly.Count]; + data.IsLocked = true; + data.HitHeight = 150F; // nFozzy's recommandation, but I think 50 should be ok + + for (var i = 0; i < poly.Count; i++) { + + // Poly points are expressed in flipper's frame: transpose to Table's frame as this is the basis uses for drag points + var p = ta.transform.InverseTransformPoint(transform.TransformPoint(poly[i])); + data.DragPoints[i] = new Engine.Math.DragPointData(p.x, p.y); + } + + return new Trigger(data); + } + throw new InvalidOperationException("Cannot create correction trigger for flipper outside of the table hierarchy."); + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs new file mode 100644 index 000000000..3a68ab316 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -0,0 +1,31 @@ +// Visual Pinball Engine +// Copyright (C) 2021 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.Entities; +using Unity.Mathematics; +using Unity.Profiling; +using UnityEngine; + +namespace VisualPinball.Unity +{ + internal static class FlipperCorrection + { + public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionData flipperCorrectionData, in FlipperCorrectionData flipperData) + { + Debug.Log("Ball going out!"); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs.meta new file mode 100644 index 000000000..820300c04 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 226db937742254f4d81ac66d1750b968 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs new file mode 100644 index 000000000..7426514c7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs @@ -0,0 +1,29 @@ +// Visual Pinball Engine +// Copyright (C) 2021 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; + +namespace VisualPinball.Unity +{ + /// + /// Makes flippers flip better, A.K.A. nFozzy physics. + /// + public class FlipperCorrectionAuthoring : MonoBehaviour + { + public Vector2[] Polarities; + public Vector2[] Velocities; + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta new file mode 100644 index 000000000..d751a51f7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b9b7009cb3eada42944a8cf63ef9745 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs new file mode 100644 index 000000000..ae158d5d7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs @@ -0,0 +1,25 @@ +// Visual Pinball Engine +// Copyright (C) 2021 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.Entities; + +namespace VisualPinball.Unity +{ + public struct FlipperCorrectionData : IComponentData + { + public Entity FlipperEntity; + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs.meta new file mode 100644 index 000000000..27284722d --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8a65e0f46eb3494baddd8bdaef0219e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerAuthoring.cs index 84a187b92..6f8f10ae0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerAuthoring.cs @@ -63,7 +63,7 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio // register var trigger = GetComponent().Item; - transform.GetComponentInParent().RegisterTrigger(trigger, entity, ParentEntity, gameObject); + transform.GetComponentInParent().RegisterTrigger(trigger, entity, ParentEntity); } public override void Restore() From 7b5e6bcb6284eed4fe5b25fec287b91b8ccdea97 Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 8 Jun 2021 22:46:17 +0200 Subject: [PATCH 03/23] nfp: Fix drag point orientation and thus collision events. --- .../VPT/Flipper/FlipperInspector.cs | 33 +++++++++++++-- .../Collision/StaticCollisionSystem.cs | 42 ++++++++++++------- .../VisualPinball.Unity/VPT/Ball/BallData.cs | 1 + .../VPT/Flipper/FlipperAuthoring.cs | 4 +- .../VPT/Flipper/FlipperCorrection.cs | 5 ++- .../VPT/Trigger/TriggerCollider.cs | 22 ++-------- 6 files changed, 65 insertions(+), 42 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs index 7d32116b3..37943a99e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs @@ -16,13 +16,10 @@ // ReSharper disable AssignmentInConditionalExpression +using Unity.Entities; using UnityEditor; using UnityEngine; using VisualPinball.Engine.VPT.Flipper; -using VisualPinball.Engine.VPT.Trigger; -using Unity.Entities; -using Unity.Mathematics; -using VisualPinball.Engine.Math; namespace VisualPinball.Unity.Editor { @@ -93,6 +90,34 @@ public override void OnInspectorGUI() } EditorGUILayout.EndFoldoutHeaderGroup(); + if (GUILayout.Button("Setup nFozzy Corrections")) + { + // Get table reference + var table = ItemAuthoring.GetComponentInParent(); + if (table != null) { + + var nFozzyCorrection = new GameObject(ItemAuthoring.name + "_nFozzy"); + nFozzyCorrection.transform.parent = ItemAuthoring.transform.parent; + nFozzyCorrection.transform.localScale = ItemAuthoring.transform.localScale; + nFozzyCorrection.transform.localPosition = ItemAuthoring.transform.localPosition; + nFozzyCorrection.transform.rotation = table.transform.rotation; + + var trigger = ItemAuthoring.CreateCorrectionTrigger(); + var triggerAuth = nFozzyCorrection.AddComponent(); + triggerAuth.SetItem(trigger); + + var triggerColl = nFozzyCorrection.AddComponent(); + triggerColl.ItemDataChanged(); + + nFozzyCorrection.AddComponent(); + + // Register to Table + table.Table?.Add(trigger); + + Undo.RegisterCreatedObjectUndo(nFozzyCorrection, "Create nFozzy's corrections object"); + } + } + base.OnInspectorGUI(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs index 99b6b5c11..858ea434b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs @@ -20,7 +20,9 @@ using Unity.Collections; using Unity.Entities; using Unity.Profiling; +using UnityEngine; using VisualPinball.Engine.VPT; +using VisualPinball.Engine.VPT.Flipper; using Object = UnityEngine.Object; using Random = UnityEngine.Random; @@ -93,7 +95,7 @@ protected override void OnUpdate() fixed (Collider* collider = &coll) { switch (coll.Type) { - case ColliderType.Bumper: + case ColliderType.Bumper: { var bumperStaticData = GetComponent(coll.Entity); var ringData = GetComponent(bumperStaticData.RingEntity); var skirtData = GetComponent(bumperStaticData.SkirtEntity); @@ -102,21 +104,23 @@ protected override void OnUpdate() SetComponent(bumperStaticData.RingEntity, ringData); SetComponent(bumperStaticData.SkirtEntity, skirtData); break; + } - case ColliderType.Flipper: + case ColliderType.Flipper: { var flipperVelocityData = GetComponent(coll.Entity); var flipperMovementData = GetComponent(coll.Entity); var flipperMaterialData = GetComponent(coll.Entity); var flipperHitData = GetComponent(coll.Entity); - ((FlipperCollider*) collider)->Collide( + ((FlipperCollider*)collider)->Collide( ref ballData, ref collEvent, ref flipperMovementData, ref events, in ballEntity, in flipperMaterialData, in flipperVelocityData, in flipperHitData, timeMsec ); SetComponent(coll.Entity, flipperMovementData); break; + } - case ColliderType.Gate: + case ColliderType.Gate: { var gateStaticData = GetComponent(coll.Entity); var gateMovementData = GetComponent(gateStaticData.WireEntity); GateCollider.Collide( @@ -125,15 +129,17 @@ protected override void OnUpdate() ); SetComponent(gateStaticData.WireEntity, gateMovementData); break; + } - case ColliderType.LineSlingShot: + case ColliderType.LineSlingShot: { var slingshotData = GetComponent(coll.Entity); - ((LineSlingshotCollider*) collider)->Collide( + ((LineSlingshotCollider*)collider)->Collide( ref ballData, ref events, in ballEntity, in slingshotData, in collEvent, ref random); break; + } - case ColliderType.Plunger: + case ColliderType.Plunger: { var plungerMovementData = GetComponent(coll.Entity); var plungerStaticData = GetComponent(coll.Entity); PlungerCollider.Collide( @@ -141,8 +147,9 @@ protected override void OnUpdate() in plungerStaticData, ref random); SetComponent(coll.Entity, plungerMovementData); break; + } - case ColliderType.Spinner: + case ColliderType.Spinner: { var spinnerStaticData = GetComponent(coll.Entity); var spinnerMovementData = GetComponent(spinnerStaticData.PlateEntity); SpinnerCollider.Collide( @@ -151,6 +158,7 @@ in spinnerStaticData ); SetComponent(spinnerStaticData.PlateEntity, spinnerMovementData); break; + } case ColliderType.TriggerCircle: case ColliderType.TriggerLine: { @@ -166,13 +174,14 @@ in spinnerStaticData if (HasComponent(coll.Entity)) { if (triggerAnimationData.UnHitEvent) { + var flipperCorrectionData = GetComponent(coll.Entity); - var flipperData = GetComponent(flipperCorrectionData.FlipperEntity); + var flipperMovementData = GetComponent(flipperCorrectionData.FlipperEntity); + var flipperStaticData = GetComponent(flipperCorrectionData.FlipperEntity); FlipperCorrection.OnBallLeaveFlipper( - ref ballData, ref flipperCorrectionData, in flipperData + ref ballData, ref flipperCorrectionData, in flipperMovementData, in flipperStaticData ); SetComponent(coll.Entity, flipperCorrectionData); - SetComponent(flipperCorrectionData.FlipperEntity, flipperData); } } else { @@ -180,7 +189,8 @@ in spinnerStaticData } break; } - case ColliderType.KickerCircle: + + case ColliderType.KickerCircle: { var kickerCollisionData = GetComponent(coll.Entity); var kickerStaticData = GetComponent(coll.Entity); // ReSharper disable once ConditionIsAlwaysTrueOrFalse @@ -192,6 +202,7 @@ in spinnerStaticData ); SetComponent(coll.Entity, kickerCollisionData); break; + } case ColliderType.Line: case ColliderType.Line3D: @@ -226,13 +237,14 @@ in spinnerStaticData if (HasComponent(coll.Entity)) { if (triggerAnimationData.UnHitEvent) { + var flipperCorrectionData = GetComponent(coll.Entity); - var flipperData = GetComponent(flipperCorrectionData.FlipperEntity); + var flipperMovementData = GetComponent(flipperCorrectionData.FlipperEntity); + var flipperStaticData = GetComponent(flipperCorrectionData.FlipperEntity); FlipperCorrection.OnBallLeaveFlipper( - ref ballData, ref flipperCorrectionData, in flipperData + ref ballData, ref flipperCorrectionData, in flipperMovementData, in flipperStaticData ); SetComponent(coll.Entity, flipperCorrectionData); - SetComponent(flipperCorrectionData.FlipperEntity, flipperData); } } else { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs index 66a8f476f..de49f729c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs @@ -16,6 +16,7 @@ using Unity.Entities; using Unity.Mathematics; +using UnityEngine; namespace VisualPinball.Unity { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index 2f44dcd2e..0b2347d48 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -351,7 +351,7 @@ private FlipperHitData GetHitData() }; } - private Trigger CreateCorrectionTrigger() + public Trigger CreateCorrectionTrigger() { // Get table reference var ta = GetComponentInParent(); @@ -367,7 +367,7 @@ private Trigger CreateCorrectionTrigger() // Poly points are expressed in flipper's frame: transpose to Table's frame as this is the basis uses for drag points var p = ta.transform.InverseTransformPoint(transform.TransformPoint(poly[i])); - data.DragPoints[i] = new Engine.Math.DragPointData(p.x, p.y); + data.DragPoints[poly.Count - i - 1] = new Engine.Math.DragPointData(p.x, p.y); } return new Trigger(data); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index 3a68ab316..09460b74a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -18,14 +18,15 @@ using Unity.Mathematics; using Unity.Profiling; using UnityEngine; +using VisualPinball.Engine.VPT.Flipper; namespace VisualPinball.Unity { internal static class FlipperCorrection { - public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionData flipperCorrectionData, in FlipperCorrectionData flipperData) + public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionData flipperCorrectionData, in FlipperMovementData flipperMovementData, in FlipperStaticData flipperStaticData) { - Debug.Log("Ball going out!"); + Debug.Log("[CORR] Ball going out!"); } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerCollider.cs index e4e10b5cc..7476ef950 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerCollider.cs @@ -16,6 +16,7 @@ using Unity.Collections; using Unity.Entities; +using UnityEngine; using VisualPinball.Engine.Common; using VisualPinball.Engine.Game; @@ -27,35 +28,18 @@ public static void Collide(ref BallData ball, ref NativeQueue.Paralle ref CollisionEventData collEvent, ref DynamicBuffer insideOfs, ref TriggerAnimationData animationData, in Entity ballEntity, in Collider coll) { - Collide(ref ball, ref events, ref collEvent, ref insideOfs, ref animationData, in ballEntity, in coll, true); - } - - private static void Collide(ref BallData ball, ref NativeQueue.ParallelWriter events, - ref CollisionEventData collEvent, ref DynamicBuffer insideOfs, - ref TriggerAnimationData animationData, in Entity ballEntity, in Collider coll, bool animate) - { - // todo? - // if (!ball.isRealBall()) { - // return; - // } - var insideOf = BallData.IsInsideOf(in insideOfs, coll.Entity); if (collEvent.HitFlag == insideOf) { // Hit == NotAlreadyHit ball.Position += PhysicsConstants.StaticTime * ball.Velocity; // move ball slightly forward - if (!insideOf) { BallData.SetInsideOf(ref insideOfs, coll.Entity); - if (animate) { - animationData.HitEvent = true; - } + animationData.HitEvent = true; events.Enqueue(new EventData(EventId.HitEventsHit, coll.ParentEntity, ballEntity, true)); } else { BallData.SetOutsideOf(ref insideOfs, coll.Entity); - if (animate) { - animationData.UnHitEvent = true; - } + animationData.UnHitEvent = true; events.Enqueue(new EventData(EventId.HitEventsUnhit, coll.ParentEntity, ballEntity, true)); } From afd3e2a3202645b59810112406f714508de76d9a Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 8 Jun 2021 23:25:25 +0200 Subject: [PATCH 04/23] nfp: Add missing parameters to correction function. --- .../Collision/StaticCollisionSystem.cs | 20 ++++++------- .../Physics/Engine/DefaultPhysicsEngine.cs | 7 +++-- .../VPT/Flipper/FlipperAuthoring.cs | 26 ++++++++++++++--- .../VPT/Flipper/FlipperCorrection.cs | 16 +++++++---- .../VPT/Flipper/FlipperCorrectionBlob.cs | 28 +++++++++++++++++++ .../VPT/Flipper/FlipperCorrectionBlob.cs.meta | 11 ++++++++ .../VPT/Flipper/FlipperCorrectionData.cs | 2 +- .../VPT/Flipper/FlipperMovementData.cs | 1 + 8 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs index 858ea434b..5df0c0a0c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs @@ -20,9 +20,7 @@ using Unity.Collections; using Unity.Entities; using Unity.Profiling; -using UnityEngine; using VisualPinball.Engine.VPT; -using VisualPinball.Engine.VPT.Flipper; using Object = UnityEngine.Object; using Random = UnityEngine.Random; @@ -174,14 +172,13 @@ in spinnerStaticData if (HasComponent(coll.Entity)) { if (triggerAnimationData.UnHitEvent) { - var flipperCorrectionData = GetComponent(coll.Entity); - var flipperMovementData = GetComponent(flipperCorrectionData.FlipperEntity); - var flipperStaticData = GetComponent(flipperCorrectionData.FlipperEntity); + ref var flipperCorrectionBlob = ref flipperCorrectionData.Value.Value; + var flipperMovementData = GetComponent(flipperCorrectionBlob.FlipperEntity); + var flipperStaticData = GetComponent(flipperCorrectionBlob.FlipperEntity); FlipperCorrection.OnBallLeaveFlipper( - ref ballData, ref flipperCorrectionData, in flipperMovementData, in flipperStaticData + ref ballData, ref flipperCorrectionBlob, in flipperMovementData, in flipperStaticData, timeMsec ); - SetComponent(coll.Entity, flipperCorrectionData); } } else { @@ -237,14 +234,13 @@ in spinnerStaticData if (HasComponent(coll.Entity)) { if (triggerAnimationData.UnHitEvent) { - var flipperCorrectionData = GetComponent(coll.Entity); - var flipperMovementData = GetComponent(flipperCorrectionData.FlipperEntity); - var flipperStaticData = GetComponent(flipperCorrectionData.FlipperEntity); + ref var flipperCorrectionBlob = ref flipperCorrectionData.Value.Value; + var flipperMovementData = GetComponent(flipperCorrectionBlob.FlipperEntity); + var flipperStaticData = GetComponent(flipperCorrectionBlob.FlipperEntity); FlipperCorrection.OnBallLeaveFlipper( - ref ballData, ref flipperCorrectionData, in flipperMovementData, in flipperStaticData + ref ballData, ref flipperCorrectionBlob, in flipperMovementData, in flipperStaticData, timeMsec ); - SetComponent(coll.Entity, flipperCorrectionData); } } else { diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs index f5ae71362..4196901a5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs @@ -39,6 +39,8 @@ public class DefaultPhysicsEngine : IPhysicsEngine private readonly DebugFlipperSlider[] _flipperSliders = new DebugFlipperSlider[0]; private int _nextBallIdToNotifyDebugUI; + private VisualPinballSimulationSystemGroup _visualPinballSimulationSystemGroup; + public void Init(TableAuthoring tableAuthoring, BallManager ballManager) { _ballManager = ballManager; @@ -51,10 +53,10 @@ public void Init(TableAuthoring tableAuthoring, BallManager ballManager) _ballDataQuery = _entityManager.CreateEntityQuery(ComponentType.ReadOnly()); - var visualPinballSimulationSystemGroup = _entityManager.World.GetOrCreateSystem(); + _visualPinballSimulationSystemGroup = _entityManager.World.GetOrCreateSystem(); var simulateCycleSystemGroup = _entityManager.World.GetOrCreateSystem(); - visualPinballSimulationSystemGroup.Enabled = true; + _visualPinballSimulationSystemGroup.Enabled = true; simulateCycleSystemGroup.PhysicsEngine = this; // needed for flipper status update we don't do in all engines var transform = tableAuthoring.gameObject.transform; @@ -98,6 +100,7 @@ public void FlipperRotateToEnd(in Entity entity) { var mData = _entityManager.GetComponentData(entity); mData.EnableRotateEvent = 1; + mData.StartRotateToEndTime = _visualPinballSimulationSystemGroup.TimeMsec; _entityManager.SetComponentData(entity, mData); _entityManager.SetComponentData(entity, new SolenoidStateData { Value = true }); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index 0b2347d48..0c1daf1f4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Linq; using Mpf.Vpe; +using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using UnityEditor; @@ -84,10 +85,27 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio // todo create special registration method since we don't need all the api stuff. player.RegisterTrigger(trigger, triggerEntity, Entity.Null); - // add correction data - dstManager.AddComponentData(triggerEntity, new FlipperCorrectionData { - FlipperEntity = entity - }); + using (var builder = new BlobBuilder(Allocator.Temp)) { + + ref var root = ref builder.ConstructRoot(); + root.FlipperEntity = entity; + + var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.Polarities.Length); + for (var i = 0; i < correctionAuthoring.Polarities.Length; i++) { + polarities[i] = correctionAuthoring.Polarities[i]; + } + var velocities = builder.Allocate(ref root.Velocities, correctionAuthoring.Velocities.Length); + for (var i = 0; i < correctionAuthoring.Velocities.Length; i++) { + velocities[i] = correctionAuthoring.Velocities[i]; + } + + var blobAssetRef = builder.CreateBlobAssetReference(Allocator.Persistent); + + // add correction data + dstManager.AddComponentData(triggerEntity, new FlipperCorrectionData { + Value = blobAssetRef + }); + } } // register diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index 09460b74a..bb2188487 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -14,19 +14,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -using Unity.Entities; -using Unity.Mathematics; -using Unity.Profiling; using UnityEngine; -using VisualPinball.Engine.VPT.Flipper; namespace VisualPinball.Unity { internal static class FlipperCorrection { - public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionData flipperCorrectionData, in FlipperMovementData flipperMovementData, in FlipperStaticData flipperStaticData) + public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionBlob flipperCorrectionBlob, + in FlipperMovementData flipperMovementData, in FlipperStaticData flipperStaticData, uint timeMs) { - Debug.Log("[CORR] Ball going out!"); + ref var velocities = ref flipperCorrectionBlob.Velocities; + ref var polarities = ref flipperCorrectionBlob.Polarities; + var flipperAngleRad = flipperMovementData.Angle; + var flipperStrength = flipperStaticData.Strength; + var ballPosition = ballData.Position; + var ballVelocity = ballData.Velocity; + var timeSinceFlipperStartedRotatingToEndMs = timeMs - flipperMovementData.StartRotateToEndTime; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs new file mode 100644 index 000000000..6effc96e7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs @@ -0,0 +1,28 @@ +// Visual Pinball Engine +// Copyright (C) 2021 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.Entities; +using Unity.Mathematics; + +namespace VisualPinball.Unity +{ + public struct FlipperCorrectionBlob + { + public Entity FlipperEntity; + public BlobArray Polarities; + public BlobArray Velocities; + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs.meta new file mode 100644 index 000000000..692af5c11 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a385a32de1a35664899367786c93a093 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs index ae158d5d7..9c12dc204 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionData.cs @@ -20,6 +20,6 @@ namespace VisualPinball.Unity { public struct FlipperCorrectionData : IComponentData { - public Entity FlipperEntity; + public BlobAssetReference Value; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs index f5ce4278e..af93015da 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs @@ -27,6 +27,7 @@ internal struct FlipperMovementData : IComponentData public sbyte EnableRotateEvent; public quaternion BaseRotation; public uint LastHitTime; + public uint StartRotateToEndTime; public override string ToString() { From b539d1a6a2da4721f57a8dd2603ced5a99806390 Mon Sep 17 00:00:00 2001 From: BilboX Date: Fri, 11 Jun 2021 11:56:53 -1000 Subject: [PATCH 05/23] change: adding data to DOTS structures --- .../VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs | 1 + .../VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs | 1 + .../VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs | 1 + .../VisualPinball.Unity/VPT/Flipper/FlipperStaticData.cs | 2 ++ 4 files changed, 5 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs index 4196901a5..06aa2abee 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs @@ -101,6 +101,7 @@ public void FlipperRotateToEnd(in Entity entity) var mData = _entityManager.GetComponentData(entity); mData.EnableRotateEvent = 1; mData.StartRotateToEndTime = _visualPinballSimulationSystemGroup.TimeMsec; + mData.AngleAtRotateToEndTime = mData.Angle; _entityManager.SetComponentData(entity, mData); _entityManager.SetComponentData(entity, new SolenoidStateData { Value = true }); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs index 6effc96e7..ae8565431 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.cs @@ -24,5 +24,6 @@ public struct FlipperCorrectionBlob public Entity FlipperEntity; public BlobArray Polarities; public BlobArray Velocities; + public uint TimeDelayMs; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs index af93015da..e920b9719 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs @@ -28,6 +28,7 @@ internal struct FlipperMovementData : IComponentData public quaternion BaseRotation; public uint LastHitTime; public uint StartRotateToEndTime; + public float AngleAtRotateToEndTime; public override string ToString() { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperStaticData.cs index a1e044933..f2756608f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperStaticData.cs @@ -15,11 +15,13 @@ // along with this program. If not, see . using Unity.Entities; +using Unity.Mathematics; namespace VisualPinball.Unity { internal struct FlipperStaticData : IComponentData { + public float3 Position; public float Inertia; public float AngleStart; public float AngleEnd; From 33dd2d9b1143f4fc05501112995975af665616c1 Mon Sep 17 00:00:00 2001 From: BilboX Date: Fri, 11 Jun 2021 11:59:27 -1000 Subject: [PATCH 06/23] change: Changing correction data to animation curves, then fill in DOTS data --- VisualPinball.Unity/Assets/Curves.meta | 8 + .../Assets/Curves/CorrectionCurves.meta | 8 + .../Curves/CorrectionCurves/nFozzy.meta | 8 + .../nFozzy/90s and earlier - Polarities.asset | 155 ++++++++++++++++++ .../90s and earlier - Polarities.asset.meta | 8 + .../nFozzy/90s and earlier - Velocities.asset | 83 ++++++++++ .../90s and earlier - Velocities.asset.meta | 8 + .../VPT/Flipper/AnimationCurveAsset.cs | 41 +++++ .../VPT/Flipper/AnimationCurveAsset.cs.meta | 11 ++ .../VPT/Flipper/FlipperAuthoring.cs | 48 +++++- .../VPT/Flipper/FlipperCorrectionAuthoring.cs | 16 +- 11 files changed, 386 insertions(+), 8 deletions(-) create mode 100644 VisualPinball.Unity/Assets/Curves.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta diff --git a/VisualPinball.Unity/Assets/Curves.meta b/VisualPinball.Unity/Assets/Curves.meta new file mode 100644 index 000000000..f0f7f4e81 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60f702cb8973dff4ab8b328d150e3727 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves.meta new file mode 100644 index 000000000..a7560f0bf --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5037300f52b5e114995172820b8543bb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta new file mode 100644 index 000000000..8c2094d72 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ca4b74e1e89622f4f9c9f4dfb2586acf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset new file mode 100644 index 000000000..1eb5f01fe --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset @@ -0,0 +1,155 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} + m_Name: 90s and earlier - Polarities + m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: -110 + outSlope: -110 + tangentMode: 34 + weightedMode: 0 + inWeight: 0 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.05 + value: -5.5 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.4 + value: -5.5 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.6 + value: -5 + inSlope: 6.250005 + outSlope: 6.250005 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.65 + value: -4.5 + inSlope: 10.000004 + outSlope: 10.000004 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.7 + value: -4 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.75 + value: -3.5 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.8 + value: -3 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.85 + value: -2.5 + inSlope: 10.000004 + outSlope: 10.000004 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.9 + value: -2 + inSlope: 10.000004 + outSlope: 10.000004 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.95 + value: -1.5 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: -1 + inSlope: 10.000004 + outSlope: 10.000004 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1.05 + value: -0.5 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1.1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1.3 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset.meta new file mode 100644 index 000000000..2b893ad74 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 576f4daa08eb10240b2e42da0a7bc2e3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset new file mode 100644 index 000000000..e2a074282 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset @@ -0,0 +1,83 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} + m_Name: 90s and earlier - Velocities + m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.16 + value: 1.061 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.41 + value: 1.051 + inSlope: -0.080546916 + outSlope: -0.080546916 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.19800007 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.53 + value: 1 + inSlope: -0.30552328 + outSlope: -0.30552328 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.702 + value: 0.968 + inSlope: 0.0005121914 + outSlope: 0.0005121914 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.54385066 + - serializedVersion: 3 + time: 0.95 + value: 0.968 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1.03 + value: 0.945 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset.meta new file mode 100644 index 000000000..6ca0b8f83 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c793eb509ab1de49b7ed96dc9d97db2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs new file mode 100644 index 000000000..5eeec63ed --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs @@ -0,0 +1,41 @@ +// Visual Pinball Engine +// Copyright (C) 2021 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; + +/// +/// Simple class to encapsulate Curves as assets +/// + +namespace VisualPinball.Unity +{ + [CreateAssetMenu] + public class AnimationCurveAsset : ScriptableObject + { + public AnimationCurve curve = AnimationCurve.Linear(0, 0, 1, 1); + + public static implicit operator AnimationCurve(AnimationCurveAsset me) + { + return me.curve; + } + public static implicit operator AnimationCurveAsset(AnimationCurve curve) + { + AnimationCurveAsset asset = ScriptableObject.CreateInstance(); + asset.curve = curve; + return asset; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta new file mode 100644 index 000000000..e492ff085 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 927b6f60022ae314c9f37c8ffd39c67b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index 0c1daf1f4..f50889b14 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -89,14 +89,49 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio ref var root = ref builder.ConstructRoot(); root.FlipperEntity = entity; + root.TimeDelayMs = correctionAuthoring.TimeDelayMs; + // Discretize the curves + + var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.polaritiesCurveSlicingCount+1); + if (correctionAuthoring?.Polarities?.curve != null && correctionAuthoring.polaritiesCurveSlicingCount >= 0) + { + var curve = correctionAuthoring.Polarities.curve; + float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.polaritiesCurveSlicingCount; + int i = 0; + for (var t = curve[0].time; t <= curve[curve.length - 1].time; t += stepP) + { + polarities[i].x = t; + polarities[i++].y = curve.Evaluate(t); + } + } + else + { + for (int i = 0; i < correctionAuthoring.polaritiesCurveSlicingCount + 1; i++) + { + polarities[i].x = (float)i / (float)correctionAuthoring.polaritiesCurveSlicingCount; + polarities[i].y = 0F; + } + } - var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.Polarities.Length); - for (var i = 0; i < correctionAuthoring.Polarities.Length; i++) { - polarities[i] = correctionAuthoring.Polarities[i]; + var velocities = builder.Allocate(ref root.Velocities, correctionAuthoring.velocitiesCurveSlicingCount + 1); + if (correctionAuthoring?.Velocities?.curve != null && correctionAuthoring.velocitiesCurveSlicingCount >= 0) + { + var curve = correctionAuthoring.Velocities.curve; + float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.velocitiesCurveSlicingCount; + int i = 0; + for (var t = curve[0].time; t <= curve[curve.length - 1].time; t += stepP) + { + velocities[i].x = t; + velocities[i++].y = curve.Evaluate(t); + } } - var velocities = builder.Allocate(ref root.Velocities, correctionAuthoring.Velocities.Length); - for (var i = 0; i < correctionAuthoring.Velocities.Length; i++) { - velocities[i] = correctionAuthoring.Velocities[i]; + else + { + for (int i = 0; i < correctionAuthoring.velocitiesCurveSlicingCount + 1; i++) + { + velocities[i].x = (float)i / (float)correctionAuthoring.polaritiesCurveSlicingCount; + velocities[i].y = 1F; + } } var blobAssetRef = builder.CreateBlobAssetReference(Allocator.Persistent); @@ -312,6 +347,7 @@ private FlipperStaticData GetMaterialData() var inertia = (float) (1.0 / 3.0) * mass * (flipperRadius * flipperRadius); return new FlipperStaticData { + Position = new float3(Data.Center.X, Data.Center.Y, 0F), // TODO: surface height? Inertia = inertia, AngleStart = angleStart, AngleEnd = angleEnd, diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs index 7426514c7..0dfa9eab2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs @@ -23,7 +23,19 @@ namespace VisualPinball.Unity /// public class FlipperCorrectionAuthoring : MonoBehaviour { - public Vector2[] Polarities; - public Vector2[] Velocities; + public AnimationCurveAsset Polarities; + + [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] + [Min(1)] + public int polaritiesCurveSlicingCount = 256; + + public AnimationCurveAsset Velocities; + + [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] + [Min(1)] + public int velocitiesCurveSlicingCount = 256; + + [Tooltip("Time since flipper fire, in ms, after which the corrections are not applyied anymore.")] + public uint TimeDelayMs = 60; } } From 77f07d1c3b03b21a61c3cccbc6525ca0c5a56ccc Mon Sep 17 00:00:00 2001 From: BilboX Date: Fri, 11 Jun 2021 11:59:58 -1000 Subject: [PATCH 07/23] feature: implement nFozzy flipper corrections (polarities and velocities) --- .../VPT/Flipper/FlipperCorrection.cs | 159 +++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index bb2188487..c09436198 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -14,8 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using Unity.Burst; +using Unity.Mathematics; using UnityEngine; +using Unity.Entities; + namespace VisualPinball.Unity { internal static class FlipperCorrection @@ -23,14 +27,165 @@ internal static class FlipperCorrection public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionBlob flipperCorrectionBlob, in FlipperMovementData flipperMovementData, in FlipperStaticData flipperStaticData, uint timeMs) { + var timeSinceFlipperStartedRotatingToEndMs = timeMs - flipperMovementData.StartRotateToEndTime; + + // Time delay overrun test + if (timeSinceFlipperStartedRotatingToEndMs > flipperCorrectionBlob.TimeDelayMs) + return; + ref var velocities = ref flipperCorrectionBlob.Velocities; ref var polarities = ref flipperCorrectionBlob.Polarities; - var flipperAngleRad = flipperMovementData.Angle; + var angleCur = flipperMovementData.Angle; var flipperStrength = flipperStaticData.Strength; + var ballPosition = ballData.Position; var ballVelocity = ballData.Velocity; - var timeSinceFlipperStartedRotatingToEndMs = timeMs - flipperMovementData.StartRotateToEndTime; + var uncorrectedVel = ballVelocity; + if (ballVelocity.y > -8F) // ball going down + { + //Debug.Log("ball going down"); + return; + } + + + + var angleAtFire = flipperMovementData.AngleAtRotateToEndTime; + //var flipperBase = _hitCircleBase.Center; + //var feRadius = matData.EndRadius; + + //var angleMin = math.min(flipperStaticData.AngleStart, flipperStaticData.AngleEnd); + //var angleMax = math.max(flipperStaticData.AngleStart, flipperStaticData.AngleEnd); + var angleStart = flipperStaticData.AngleStart; + var angleEnd = flipperStaticData.AngleEnd; + + var flipPos = flipperStaticData.Position; + + var flipEnd = flipperStaticData.Position; + flipEnd.x += math.sin(angleCur) * flipperStaticData.FlipperRadius; + flipEnd.y += -math.cos(angleCur) * flipperStaticData.FlipperRadius; + + // Compute ball distance on Flipper (normalized from start to end) + //var dir = flipEnd - flipPos; + var ballPos = (ballPosition.x - flipPos.x) / (flipEnd.x - flipPos.x); + + // Safety coeffcient: has been disabled in all curves of nFozzy. Not using; + float Ycoef = 1F; + + //'Find balldata. BallPos = % on Flipper + //for x = 0 to uBound(Balls) + // if aBall.id = BallData(x).id AND not isempty(BallData(x).id) then + // idx = x + // BallPos = PSlope(BallData(x).x, FlipperStart, 0, FlipperEnd, 1) + // if ballpos > 0.65 then Ycoef = LinearEnvelope(BallData(x).Y, YcoefIn, YcoefOut) 'find safety coefficient 'ycoef' data + // end if + //Next + + // Normalized flipper course since fire (can be > 1 if rebounding...) + float partialFlipCoef = ((angleStart - angleAtFire) / (angleStart - angleEnd)); + partialFlipCoef = math.abs(partialFlipCoef - 1F); + + // Velocity correction + var velCoef = LinearEnvelopeEven(ballPos, ref velocities); + var velCoefInit = velCoef; + if (partialFlipCoef < 1) + { + velCoef = PSlope(partialFlipCoef, 0, 1, 1, velCoef); + } + ballVelocity *= velCoef; + + // Polarity Correction + bool isLeft = angleEnd < angleStart; // TODO: better if not classic flippers (trigonometry problems) + + float AddX = LinearEnvelopeEven(ballPos, ref polarities, 0F); + if(isLeft) { + AddX = -AddX; + } + ballVelocity.x += (AddX * Ycoef * partialFlipCoef); + + + + // Apply all corrections + ballData.Velocity = ballVelocity; + //ballData.IsFrozen = true; + +#if UNITY_EDITOR + Global.FlipperCorrectionDebug.flipPos = flipPos; + Global.FlipperCorrectionDebug.flipEnd = flipEnd; + Global.FlipperCorrectionDebug.endRadius = flipperStaticData.EndRadius; + Global.FlipperCorrectionDebug.outPos = ballPosition; + Global.FlipperCorrectionDebug.uncorrectedVel = uncorrectedVel; + Global.FlipperCorrectionDebug.outVel = ballVelocity; + Global.FlipperCorrectionDebug.ballPos = ballPos; + Global.FlipperCorrectionDebug.justOut = true; + DebugInfo("Normalized angle:"+ partialFlipCoef+" Velocity coef:"+velCoefInit+" => "+ velCoef+ " Polarity correction:"+ AddX+ " Time Since Power:"+timeSinceFlipperStartedRotatingToEndMs, true); +#endif + } + + [BurstDiscard] + public static void DebugInfo(string message = "", bool aditionnal = false) + { + if(message != "") + Debug.Log(" Debug : " + message + " "); + if (aditionnal) + { + Debug.Log(" Debug : OnBallLeaveFlipper. " + Global.FlipperCorrectionDebug.outPos + " " + Global.FlipperCorrectionDebug.outVel); + Debug.Log(" Debug : BallPos :" + Global.FlipperCorrectionDebug.ballPos + " "); + } + } + + // awefull linear interpolations : TODO: replace by AnimationCurve equivalent... + private static float PSlope(float x, float x1, float y1, float x2, float y2) //Set up line via two points, no clamping. Input X, output Y + { + float m = (y2 - y1) / (y2 - x1); + float b = y2 - m * y2; + return m * x + b; + } + + private static float LinearEnvelope(float xInput, ref BlobArray curve, float defaultValue = 1F) + { + if (curve.Length <= 0) + return defaultValue; + + if (xInput <= curve[0].x) //Clamp lower + return curve[0].y; + if (xInput >= curve[curve.Length - 1].x) //Clamp upper + return curve[curve.Length - 1].y; + + int L = -1; + for (int ii = 1; ii < curve.Length; ii++) //find active line + if (xInput <= curve[ii].x) + { + L = ii; + break; + } + + if (L < 0) + return defaultValue; + + if (xInput > curve[curve.Length - 1].x) // catch line overrun + L = curve.Length - 1; + + float y = PSlope(xInput, curve[L - 1].x, curve[L - 1].y, curve[L].x, curve[L - 1].y); + + return y; + } + + // if segments are even on x axis, no need to iterate: faster + private static float LinearEnvelopeEven(float xInput, ref BlobArray curve, float defaultValue = 1F) + { + if (curve.Length <= 0) + return defaultValue; + + if (xInput <= curve[0].x) //Clamp lower + return curve[0].y; + if (xInput >= curve[curve.Length - 1].x) //Clamp upper + return curve[curve.Length - 1].y; + + int L = (int)((xInput-curve[0].x) * (curve.Length-1) / (curve[curve.Length - 1].x - curve[0].x)); + + float y = PSlope(xInput, curve[L - 1].x, curve[L - 1].y, curve[L].x, curve[L - 1].y); + return y; } } } From eef56c82004cd367aa94d8d089d5fd46ad2bb74b Mon Sep 17 00:00:00 2001 From: BilboX Date: Fri, 11 Jun 2021 12:13:29 -1000 Subject: [PATCH 08/23] fix: old debug and cleaning code --- .../VPT/Flipper/FlipperCorrection.cs | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index c09436198..c14a7ca4d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -43,18 +43,11 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti var uncorrectedVel = ballVelocity; if (ballVelocity.y > -8F) // ball going down { - //Debug.Log("ball going down"); return; } - var angleAtFire = flipperMovementData.AngleAtRotateToEndTime; - //var flipperBase = _hitCircleBase.Center; - //var feRadius = matData.EndRadius; - - //var angleMin = math.min(flipperStaticData.AngleStart, flipperStaticData.AngleEnd); - //var angleMax = math.max(flipperStaticData.AngleStart, flipperStaticData.AngleEnd); var angleStart = flipperStaticData.AngleStart; var angleEnd = flipperStaticData.AngleEnd; @@ -70,15 +63,7 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti // Safety coeffcient: has been disabled in all curves of nFozzy. Not using; float Ycoef = 1F; - - //'Find balldata. BallPos = % on Flipper - //for x = 0 to uBound(Balls) - // if aBall.id = BallData(x).id AND not isempty(BallData(x).id) then - // idx = x - // BallPos = PSlope(BallData(x).x, FlipperStart, 0, FlipperEnd, 1) - // if ballpos > 0.65 then Ycoef = LinearEnvelope(BallData(x).Y, YcoefIn, YcoefOut) 'find safety coefficient 'ycoef' data - // end if - //Next + // Normalized flipper course since fire (can be > 1 if rebounding...) float partialFlipCoef = ((angleStart - angleAtFire) / (angleStart - angleEnd)); @@ -106,31 +91,6 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti // Apply all corrections ballData.Velocity = ballVelocity; - //ballData.IsFrozen = true; - -#if UNITY_EDITOR - Global.FlipperCorrectionDebug.flipPos = flipPos; - Global.FlipperCorrectionDebug.flipEnd = flipEnd; - Global.FlipperCorrectionDebug.endRadius = flipperStaticData.EndRadius; - Global.FlipperCorrectionDebug.outPos = ballPosition; - Global.FlipperCorrectionDebug.uncorrectedVel = uncorrectedVel; - Global.FlipperCorrectionDebug.outVel = ballVelocity; - Global.FlipperCorrectionDebug.ballPos = ballPos; - Global.FlipperCorrectionDebug.justOut = true; - DebugInfo("Normalized angle:"+ partialFlipCoef+" Velocity coef:"+velCoefInit+" => "+ velCoef+ " Polarity correction:"+ AddX+ " Time Since Power:"+timeSinceFlipperStartedRotatingToEndMs, true); -#endif - } - - [BurstDiscard] - public static void DebugInfo(string message = "", bool aditionnal = false) - { - if(message != "") - Debug.Log(" Debug : " + message + " "); - if (aditionnal) - { - Debug.Log(" Debug : OnBallLeaveFlipper. " + Global.FlipperCorrectionDebug.outPos + " " + Global.FlipperCorrectionDebug.outVel); - Debug.Log(" Debug : BallPos :" + Global.FlipperCorrectionDebug.ballPos + " "); - } } // awefull linear interpolations : TODO: replace by AnimationCurve equivalent... From 263ba0b90f23d05acebc211aaeea2579c7be1351 Mon Sep 17 00:00:00 2001 From: BilboX Date: Fri, 11 Jun 2021 16:56:05 -1000 Subject: [PATCH 09/23] fix: Despite the nFozz's vb code comment stating that we inverse for Left flippers (aka, polarity curve was defined for right one), it is the opposite. Fixed --- .../VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index c14a7ca4d..b2cea1425 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -82,7 +82,7 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti bool isLeft = angleEnd < angleStart; // TODO: better if not classic flippers (trigonometry problems) float AddX = LinearEnvelopeEven(ballPos, ref polarities, 0F); - if(isLeft) { + if(!isLeft) { AddX = -AddX; } ballVelocity.x += (AddX * Ycoef * partialFlipCoef); From cbd7dfcbfb9f903c644aaf11acd8e30d96bd50a0 Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 14 Jun 2021 22:00:56 +0200 Subject: [PATCH 10/23] nfp: Add remaining profiles. --- ...=> Early 90s and later - Polarities.asset} | 10 +- ...rly 90s and later - Polarities.asset.meta} | 0 ...=> Early 90s and later - Velocities.asset} | 2 +- ...rly 90s and later - Velocities.asset.meta} | 0 .../Late 80s to early 90s - Polarities.asset | 146 ++++++++++++++++++ ...e 80s to early 90s - Polarities.asset.meta | 8 + .../Late 80s to early 90s - Velocities.asset | 83 ++++++++++ ...e 80s to early 90s - Velocities.asset.meta | 8 + .../nFozzy/Mid 80s - Polarities.asset | 128 +++++++++++++++ .../nFozzy/Mid 80s - Polarities.asset.meta | 8 + .../nFozzy/Mid 80s - Velocities.asset | 83 ++++++++++ .../nFozzy/Mid 80s - Velocities.asset.meta | 8 + .../VPT/Flipper/AnimationCurveAsset.cs | 81 +++++----- .../VPT/Flipper/FlipperAuthoring.cs | 4 +- .../VPT/Flipper/FlipperCorrectionAuthoring.cs | 4 +- 15 files changed, 521 insertions(+), 52 deletions(-) rename VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/{90s and earlier - Polarities.asset => Early 90s and later - Polarities.asset} (96%) rename VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/{90s and earlier - Polarities.asset.meta => Early 90s and later - Polarities.asset.meta} (100%) rename VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/{90s and earlier - Velocities.asset => Early 90s and later - Velocities.asset} (97%) rename VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/{90s and earlier - Velocities.asset.meta => Early 90s and later - Velocities.asset.meta} (100%) create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset create mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset similarity index 96% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset rename to VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset index 1eb5f01fe..968892091 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: 90s and earlier - Polarities + m_Name: Early 90s and later - Polarities m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset curve: serializedVersion: 2 @@ -34,8 +34,8 @@ MonoBehaviour: inWeight: 0.33333334 outWeight: 0.33333334 - serializedVersion: 3 - time: 0.4 - value: -5.5 + time: 0.40836015 + value: -5.5259433 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -45,8 +45,8 @@ MonoBehaviour: - serializedVersion: 3 time: 0.6 value: -5 - inSlope: 6.250005 - outSlope: 6.250005 + inSlope: 6.3722224 + outSlope: 6.3722224 tangentMode: 34 weightedMode: 0 inWeight: 0.33333334 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset.meta similarity index 100% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Polarities.asset.meta rename to VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset.meta diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset similarity index 97% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset rename to VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset index e2a074282..bcd59a3a4 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: 90s and earlier - Velocities + m_Name: Early 90s and later - Velocities m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset curve: serializedVersion: 2 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset.meta similarity index 100% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/90s and earlier - Velocities.asset.meta rename to VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset.meta diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset new file mode 100644 index 000000000..1d9b7a8b3 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset @@ -0,0 +1,146 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} + m_Name: Late 80s to early 90s - Polarities + m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: -110 + outSlope: -110 + tangentMode: 34 + weightedMode: 0 + inWeight: 0 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.05 + value: -5.5 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.4 + value: -5 + inSlope: 1.5574826 + outSlope: 1.5574826 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.56028914 + - serializedVersion: 3 + time: 0.6 + value: -4.5 + inSlope: 4.3247 + outSlope: 4.3247 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.37700978 + outWeight: 1 + - serializedVersion: 3 + time: 0.65 + value: -4 + inSlope: 9.608398 + outSlope: 9.608398 + tangentMode: 0 + weightedMode: 0 + inWeight: 1 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.7 + value: -3.5 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.75 + value: -3 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.8 + value: -2.5 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.85 + value: -2 + inSlope: 10.000004 + outSlope: 10.000004 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.9 + value: -1.5 + inSlope: 10.000004 + outSlope: 10.000004 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.95 + value: -1 + inSlope: 9.999998 + outSlope: 9.999998 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: -0.5 + inSlope: 10.42706 + outSlope: 10.42706 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.49196112 + - serializedVersion: 3 + time: 1.1 + value: 0 + inSlope: 0.08466794 + outSlope: 0.08466794 + tangentMode: 0 + weightedMode: 0 + inWeight: 1 + outWeight: 0.57154316 + - serializedVersion: 3 + time: 1.3 + value: 0 + inSlope: 0.000007556099 + outSlope: 0.000007556099 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.49115735 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta new file mode 100644 index 000000000..b1d6a462b --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a3580132d20a29a4799131c9cd6ba27e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset new file mode 100644 index 000000000..35a41ee8e --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset @@ -0,0 +1,83 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} + m_Name: Late 80s to early 90s - Velocities + m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.16 + value: 1.06 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.41 + value: 1.05 + inSlope: -0.080546916 + outSlope: -0.080546916 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.19800007 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.53 + value: 1 + inSlope: -0.30135643 + outSlope: -0.30135643 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.702 + value: 0.968 + inSlope: 0.0005121914 + outSlope: 0.0005121914 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.54385066 + - serializedVersion: 3 + time: 0.95 + value: 0.968 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1.03 + value: 0.945 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta new file mode 100644 index 000000000..3fa0de9f4 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20f0505b91931ca44ad265ca55756c76 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset new file mode 100644 index 000000000..ba4e2dd15 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset @@ -0,0 +1,128 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} + m_Name: Mid 80s - Polarities + m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: -74 + outSlope: -74 + tangentMode: 34 + weightedMode: 0 + inWeight: 0 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.05 + value: -3.7 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.33 + value: -3.7 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.37 + value: -3.7 + inSlope: 0.023671044 + outSlope: 0.023671044 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.49242085 + - serializedVersion: 3 + time: 0.41 + value: -3.7 + inSlope: 0.027969535 + outSlope: 0.027969535 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.40237278 + - serializedVersion: 3 + time: 0.45 + value: -3.7 + inSlope: 0.030804683 + outSlope: 0.030804683 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.35316187 + - serializedVersion: 3 + time: 0.576 + value: -3.7 + inSlope: 0.029239332 + outSlope: 0.029239332 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.70652777 + outWeight: 1 + - serializedVersion: 3 + time: 0.66 + value: -2.3 + inSlope: 16.059673 + outSlope: 16.059673 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.36786118 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.743 + value: -1.5 + inSlope: 8.550622 + outSlope: 8.550622 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.81 + value: -1 + inSlope: 8.777506 + outSlope: 8.777506 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.7597609 + - serializedVersion: 3 + time: 0.88 + value: 0 + inSlope: 0.12312294 + outSlope: 0.12312294 + tangentMode: 0 + weightedMode: 0 + inWeight: 1 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1.3 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta new file mode 100644 index 000000000..4d95141c0 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c8facd826c96bae45967f87f131b731e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset new file mode 100644 index 000000000..cc53e20af --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset @@ -0,0 +1,83 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} + m_Name: Mid 80s - Velocities + m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.16 + value: 1.06 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.41 + value: 1.05 + inSlope: -0.080546916 + outSlope: -0.080546916 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.19800007 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.53 + value: 1 + inSlope: -0.30135643 + outSlope: -0.30135643 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.702 + value: 0.968 + inSlope: 0.0005121914 + outSlope: 0.0005121914 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.54385066 + - serializedVersion: 3 + time: 0.95 + value: 0.968 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1.03 + value: 0.945 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta new file mode 100644 index 000000000..0604ac0ca --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ee7f28be0b44bc4f81c8801d68038d0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs index 5eeec63ed..70e313ec6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs @@ -1,41 +1,40 @@ -// Visual Pinball Engine -// Copyright (C) 2021 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; - -/// -/// Simple class to encapsulate Curves as assets -/// - -namespace VisualPinball.Unity -{ - [CreateAssetMenu] - public class AnimationCurveAsset : ScriptableObject - { - public AnimationCurve curve = AnimationCurve.Linear(0, 0, 1, 1); - - public static implicit operator AnimationCurve(AnimationCurveAsset me) - { - return me.curve; - } - public static implicit operator AnimationCurveAsset(AnimationCurve curve) - { - AnimationCurveAsset asset = ScriptableObject.CreateInstance(); - asset.curve = curve; - return asset; - } - } -} +// Visual Pinball Engine +// Copyright (C) 2021 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; + +namespace VisualPinball.Unity +{ + /// + /// Simple class to encapsulate Curves as assets + /// + [CreateAssetMenu] + public class AnimationCurveAsset : ScriptableObject + { + public AnimationCurve curve = AnimationCurve.Linear(0, 0, 1, 1); + + public static implicit operator AnimationCurve(AnimationCurveAsset me) + { + return me.curve; + } + public static implicit operator AnimationCurveAsset(AnimationCurve curve) + { + AnimationCurveAsset asset = ScriptableObject.CreateInstance(); + asset.curve = curve; + return asset; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index f50889b14..c52ac7807 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -23,11 +23,9 @@ using System; using System.Collections.Generic; using System.Linq; -using Mpf.Vpe; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; -using UnityEditor; using UnityEngine; using VisualPinball.Engine.Game; using VisualPinball.Engine.VPT.Flipper; @@ -89,7 +87,7 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio ref var root = ref builder.ConstructRoot(); root.FlipperEntity = entity; - root.TimeDelayMs = correctionAuthoring.TimeDelayMs; + root.TimeDelayMs = correctionAuthoring.TimeThresholdMs; // Discretize the curves var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.polaritiesCurveSlicingCount+1); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs index 0dfa9eab2..0437dbbfa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs @@ -35,7 +35,7 @@ public class FlipperCorrectionAuthoring : MonoBehaviour [Min(1)] public int velocitiesCurveSlicingCount = 256; - [Tooltip("Time since flipper fire, in ms, after which the corrections are not applyied anymore.")] - public uint TimeDelayMs = 60; + [Tooltip("Time since flipper fire, in ms, after which the corrections are not applied anymore.")] + public uint TimeThresholdMs = 60; } } From 047a3cbe25bb449e27b00c5ea54e838ca9f6d4a6 Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 14 Jun 2021 22:04:03 +0200 Subject: [PATCH 11/23] nfp: Fix early 90s profile. --- .../Early 90s and later - Polarities.asset | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset index 968892091..742db3a52 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset +++ b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset @@ -27,15 +27,15 @@ MonoBehaviour: - serializedVersion: 3 time: 0.05 value: -5.5 - inSlope: 0 - outSlope: 0 + inSlope: -0.0012235044 + outSlope: -0.0012235044 tangentMode: 0 weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 + inWeight: 1 + outWeight: 0.2978914 - serializedVersion: 3 - time: 0.40836015 - value: -5.5259433 + time: 0.4 + value: -5.5 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -45,8 +45,8 @@ MonoBehaviour: - serializedVersion: 3 time: 0.6 value: -5 - inSlope: 6.3722224 - outSlope: 6.3722224 + inSlope: 6.250005 + outSlope: 6.250005 tangentMode: 34 weightedMode: 0 inWeight: 0.33333334 From 8c39f262c61524a943ffb4dffe51859cde2a0aea Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 14 Jun 2021 22:10:24 +0200 Subject: [PATCH 12/23] chore: Remove debug code. --- .../VPT/Flipper/FlipperInspector.cs | 28 ------------------- .../VPT/TransformInspector.cs | 18 ++++++------ .../VPT/Flipper/AnimationCurveAsset.cs | 2 +- 3 files changed, 10 insertions(+), 38 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs index 37943a99e..84b4c5d7c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs @@ -90,34 +90,6 @@ public override void OnInspectorGUI() } EditorGUILayout.EndFoldoutHeaderGroup(); - if (GUILayout.Button("Setup nFozzy Corrections")) - { - // Get table reference - var table = ItemAuthoring.GetComponentInParent(); - if (table != null) { - - var nFozzyCorrection = new GameObject(ItemAuthoring.name + "_nFozzy"); - nFozzyCorrection.transform.parent = ItemAuthoring.transform.parent; - nFozzyCorrection.transform.localScale = ItemAuthoring.transform.localScale; - nFozzyCorrection.transform.localPosition = ItemAuthoring.transform.localPosition; - nFozzyCorrection.transform.rotation = table.transform.rotation; - - var trigger = ItemAuthoring.CreateCorrectionTrigger(); - var triggerAuth = nFozzyCorrection.AddComponent(); - triggerAuth.SetItem(trigger); - - var triggerColl = nFozzyCorrection.AddComponent(); - triggerColl.ItemDataChanged(); - - nFozzyCorrection.AddComponent(); - - // Register to Table - table.Table?.Add(trigger); - - Undo.RegisterCreatedObjectUndo(nFozzyCorrection, "Create nFozzy's corrections object"); - } - } - base.OnInspectorGUI(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs index 493ff04b2..6934525f4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/TransformInspector.cs @@ -110,15 +110,15 @@ protected virtual void OnDisable() Tools.hidden = false; } - // public override void OnInspectorGUI() - // { - // if (_defaultEditor != null) { - // _defaultEditor.OnInspectorGUI(); - // return; - // } - // - // GUILayout.Label("VPE item transforms driven by data on the component below."); - // } + public override void OnInspectorGUI() + { + if (_defaultEditor != null) { + _defaultEditor.OnInspectorGUI(); + return; + } + + GUILayout.Label("VPE item transforms driven by data on the component below."); + } private void RebuildMeshes() { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs index 70e313ec6..b076b4e03 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs @@ -32,7 +32,7 @@ public static implicit operator AnimationCurve(AnimationCurveAsset me) } public static implicit operator AnimationCurveAsset(AnimationCurve curve) { - AnimationCurveAsset asset = ScriptableObject.CreateInstance(); + AnimationCurveAsset asset = CreateInstance(); asset.curve = curve; return asset; } From a1d8f0b7d1783acbf50cc2a9f7d2bd70e69d9b9d Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 14 Jun 2021 22:29:50 +0200 Subject: [PATCH 13/23] style: Minor fixes and name changes. --- .../Inspectors/DisplayInspector.cs | 1 - .../VPT/Flipper/FlipperInspector.cs | 2 -- .../Physics/Engine/DefaultPhysicsEngine.cs | 2 +- .../VisualPinball.Unity/VPT/Ball/BallData.cs | 1 - .../VPT/Flipper/FlipperAuthoring.cs | 22 +++++++++---------- .../VPT/Flipper/FlipperCorrection.cs | 13 +++++------ .../VPT/Flipper/FlipperCorrectionAuthoring.cs | 6 +++-- .../VPT/Flipper/FlipperMovementData.cs | 2 +- 8 files changed, 23 insertions(+), 26 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs index ca6ececea..61ecf268b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs @@ -18,7 +18,6 @@ using System.Linq; using UnityEditor; using UnityEngine; -using Object = System.Object; namespace VisualPinball.Unity.Editor { diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs index 84b4c5d7c..b39f0b944 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperInspector.cs @@ -16,9 +16,7 @@ // ReSharper disable AssignmentInConditionalExpression -using Unity.Entities; using UnityEditor; -using UnityEngine; using VisualPinball.Engine.VPT.Flipper; namespace VisualPinball.Unity.Editor diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs index 06aa2abee..d13f00d4d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs @@ -101,7 +101,7 @@ public void FlipperRotateToEnd(in Entity entity) var mData = _entityManager.GetComponentData(entity); mData.EnableRotateEvent = 1; mData.StartRotateToEndTime = _visualPinballSimulationSystemGroup.TimeMsec; - mData.AngleAtRotateToEndTime = mData.Angle; + mData.AngleAtRotateToEnd = mData.Angle; _entityManager.SetComponentData(entity, mData); _entityManager.SetComponentData(entity, new SolenoidStateData { Value = true }); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs index de49f729c..66a8f476f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs @@ -16,7 +16,6 @@ using Unity.Entities; using Unity.Mathematics; -using UnityEngine; namespace VisualPinball.Unity { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index c52ac7807..d1b1492c9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -88,13 +88,13 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio ref var root = ref builder.ConstructRoot(); root.FlipperEntity = entity; root.TimeDelayMs = correctionAuthoring.TimeThresholdMs; - // Discretize the curves - var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.polaritiesCurveSlicingCount+1); - if (correctionAuthoring?.Polarities?.curve != null && correctionAuthoring.polaritiesCurveSlicingCount >= 0) + // Discretize the curves + var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.PolaritiesCurveSlicingCount+1); + if (correctionAuthoring.Polarities && correctionAuthoring.Polarities.curve != null) { var curve = correctionAuthoring.Polarities.curve; - float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.polaritiesCurveSlicingCount; + float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.PolaritiesCurveSlicingCount; int i = 0; for (var t = curve[0].time; t <= curve[curve.length - 1].time; t += stepP) { @@ -104,18 +104,18 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio } else { - for (int i = 0; i < correctionAuthoring.polaritiesCurveSlicingCount + 1; i++) + for (int i = 0; i < correctionAuthoring.PolaritiesCurveSlicingCount + 1; i++) { - polarities[i].x = (float)i / (float)correctionAuthoring.polaritiesCurveSlicingCount; + polarities[i].x = (float)i / (float)correctionAuthoring.PolaritiesCurveSlicingCount; polarities[i].y = 0F; } } - var velocities = builder.Allocate(ref root.Velocities, correctionAuthoring.velocitiesCurveSlicingCount + 1); - if (correctionAuthoring?.Velocities?.curve != null && correctionAuthoring.velocitiesCurveSlicingCount >= 0) + var velocities = builder.Allocate(ref root.Velocities, correctionAuthoring.VelocitiesCurveSlicingCount + 1); + if (correctionAuthoring.Velocities && correctionAuthoring.Velocities.curve != null) { var curve = correctionAuthoring.Velocities.curve; - float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.velocitiesCurveSlicingCount; + float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.VelocitiesCurveSlicingCount; int i = 0; for (var t = curve[0].time; t <= curve[curve.length - 1].time; t += stepP) { @@ -125,9 +125,9 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio } else { - for (int i = 0; i < correctionAuthoring.velocitiesCurveSlicingCount + 1; i++) + for (int i = 0; i < correctionAuthoring.VelocitiesCurveSlicingCount + 1; i++) { - velocities[i].x = (float)i / (float)correctionAuthoring.polaritiesCurveSlicingCount; + velocities[i].x = (float)i / (float)correctionAuthoring.PolaritiesCurveSlicingCount; velocities[i].y = 1F; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index b2cea1425..576495c42 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -28,7 +28,7 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti in FlipperMovementData flipperMovementData, in FlipperStaticData flipperStaticData, uint timeMs) { var timeSinceFlipperStartedRotatingToEndMs = timeMs - flipperMovementData.StartRotateToEndTime; - + // Time delay overrun test if (timeSinceFlipperStartedRotatingToEndMs > flipperCorrectionBlob.TimeDelayMs) return; @@ -45,9 +45,9 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti { return; } - - var angleAtFire = flipperMovementData.AngleAtRotateToEndTime; + + var angleAtFire = flipperMovementData.AngleAtRotateToEnd; var angleStart = flipperStaticData.AngleStart; var angleEnd = flipperStaticData.AngleEnd; @@ -63,10 +63,9 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti // Safety coeffcient: has been disabled in all curves of nFozzy. Not using; float Ycoef = 1F; - // Normalized flipper course since fire (can be > 1 if rebounding...) - float partialFlipCoef = ((angleStart - angleAtFire) / (angleStart - angleEnd)); + float partialFlipCoef = (angleStart - angleAtFire) / (angleStart - angleEnd); partialFlipCoef = math.abs(partialFlipCoef - 1F); // Velocity correction @@ -85,7 +84,7 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti if(!isLeft) { AddX = -AddX; } - ballVelocity.x += (AddX * Ycoef * partialFlipCoef); + ballVelocity.x += AddX * Ycoef * partialFlipCoef; @@ -93,7 +92,7 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti ballData.Velocity = ballVelocity; } - // awefull linear interpolations : TODO: replace by AnimationCurve equivalent... + // awful linear interpolations : TODO: replace by AnimationCurve equivalent... private static float PSlope(float x, float x1, float y1, float x2, float y2) //Set up line via two points, no clamping. Input X, output Y { float m = (y2 - y1) / (y2 - x1); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs index 0437dbbfa..c699f4cfd 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// ReSharper disable InconsistentNaming + using UnityEngine; namespace VisualPinball.Unity @@ -27,13 +29,13 @@ public class FlipperCorrectionAuthoring : MonoBehaviour [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] [Min(1)] - public int polaritiesCurveSlicingCount = 256; + public int PolaritiesCurveSlicingCount = 256; public AnimationCurveAsset Velocities; [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] [Min(1)] - public int velocitiesCurveSlicingCount = 256; + public int VelocitiesCurveSlicingCount = 256; [Tooltip("Time since flipper fire, in ms, after which the corrections are not applied anymore.")] public uint TimeThresholdMs = 60; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs index e920b9719..4f6c0b3bf 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs @@ -28,7 +28,7 @@ internal struct FlipperMovementData : IComponentData public quaternion BaseRotation; public uint LastHitTime; public uint StartRotateToEndTime; - public float AngleAtRotateToEndTime; + public float AngleAtRotateToEnd; public override string ToString() { From c5c980d118259fc9012f53098914655f83d38b89 Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 14 Jun 2021 23:37:39 +0200 Subject: [PATCH 14/23] refactor: Put all correction params into ScriptableObject and link it to FlipperCollisionAuthoring instead of FlipperCorrectionAuthoring. --- .../Curves/CorrectionCurves/nFozzy.meta | 8 -- .../Early 90s and later - Velocities.asset | 83 ------------------- ...e 80s to early 90s - Polarities.asset.meta | 8 -- .../Late 80s to early 90s - Velocities.asset | 83 ------------------- .../nFozzy/Mid 80s - Polarities.asset.meta | 8 -- .../nFozzy/Mid 80s - Velocities.asset | 83 ------------------- .../nFozzy/Mid 80s - Velocities.asset.meta | 8 -- ...onCurves.meta => Flipper Corrections.meta} | 0 .../Early 90s and later.asset} | 80 +++++++++++++++++- .../Early 90s and later.asset.meta} | 2 +- .../Late 80s to early 90s.asset} | 80 +++++++++++++++++- .../Late 80s to early 90s.asset.meta} | 2 +- .../Mid 80s.asset} | 80 +++++++++++++++++- .../Mid 80s.asset.meta} | 2 +- .../VPT/Flipper/FlipperColliderInspector.cs | 1 + .../VPT/ItemInspector.cs | 10 +++ .../VPT/Flipper/AnimationCurveAsset.cs | 40 --------- .../VPT/Flipper/FlipperAuthoring.cs | 32 +++---- .../VPT/Flipper/FlipperColliderAuthoring.cs | 5 ++ .../VPT/Flipper/FlipperCorrection.cs | 1 + ...Authoring.cs => FlipperCorrectionAsset.cs} | 17 ++-- ...cs.meta => FlipperCorrectionAsset.cs.meta} | 2 +- .../FlipperCorrectionAuthoring.cs.meta | 11 --- 23 files changed, 274 insertions(+), 372 deletions(-) delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset delete mode 100644 VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves.meta => Flipper Corrections.meta} (100%) rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset => Flipper Corrections/Early 90s and later.asset} (66%) rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta => Flipper Corrections/Early 90s and later.asset.meta} (79%) rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset => Flipper Corrections/Late 80s to early 90s.asset} (65%) rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset.meta => Flipper Corrections/Late 80s to early 90s.asset.meta} (79%) rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves/nFozzy/Mid 80s - Polarities.asset => Flipper Corrections/Mid 80s.asset} (62%) rename VisualPinball.Unity/Assets/Curves/{CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset.meta => Flipper Corrections/Mid 80s.asset.meta} (79%) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs rename VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/{FlipperCorrectionAuthoring.cs => FlipperCorrectionAsset.cs} (76%) rename VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/{AnimationCurveAsset.cs.meta => FlipperCorrectionAsset.cs.meta} (83%) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta deleted file mode 100644 index 8c2094d72..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ca4b74e1e89622f4f9c9f4dfb2586acf -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset deleted file mode 100644 index bcd59a3a4..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset +++ /dev/null @@ -1,83 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: Early 90s and later - Velocities - m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 0.16 - value: 1.061 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 0.41 - value: 1.051 - inSlope: -0.080546916 - outSlope: -0.080546916 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.19800007 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 0.53 - value: 1 - inSlope: -0.30552328 - outSlope: -0.30552328 - tangentMode: 34 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 0.702 - value: 0.968 - inSlope: 0.0005121914 - outSlope: 0.0005121914 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.54385066 - - serializedVersion: 3 - time: 0.95 - value: 0.968 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 1.03 - value: 0.945 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta deleted file mode 100644 index b1d6a462b..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a3580132d20a29a4799131c9cd6ba27e -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset deleted file mode 100644 index 35a41ee8e..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset +++ /dev/null @@ -1,83 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: Late 80s to early 90s - Velocities - m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 0.16 - value: 1.06 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 0.41 - value: 1.05 - inSlope: -0.080546916 - outSlope: -0.080546916 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.19800007 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 0.53 - value: 1 - inSlope: -0.30135643 - outSlope: -0.30135643 - tangentMode: 34 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 0.702 - value: 0.968 - inSlope: 0.0005121914 - outSlope: 0.0005121914 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.54385066 - - serializedVersion: 3 - time: 0.95 - value: 0.968 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 1.03 - value: 0.945 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta deleted file mode 100644 index 4d95141c0..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c8facd826c96bae45967f87f131b731e -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset deleted file mode 100644 index cc53e20af..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset +++ /dev/null @@ -1,83 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: Mid 80s - Velocities - m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 0.16 - value: 1.06 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 0.41 - value: 1.05 - inSlope: -0.080546916 - outSlope: -0.080546916 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.19800007 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 0.53 - value: 1 - inSlope: -0.30135643 - outSlope: -0.30135643 - tangentMode: 34 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 0.702 - value: 0.968 - inSlope: 0.0005121914 - outSlope: 0.0005121914 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.54385066 - - serializedVersion: 3 - time: 0.95 - value: 0.968 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 1.03 - value: 0.945 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta deleted file mode 100644 index 0604ac0ca..000000000 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Velocities.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 3ee7f28be0b44bc4f81c8801d68038d0 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections.meta similarity index 100% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections.meta diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset similarity index 66% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset index 742db3a52..e77362d41 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset @@ -9,10 +9,10 @@ MonoBehaviour: m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: Early 90s and later - Polarities - m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset - curve: + m_Script: {fileID: 11500000, guid: 6685e224cafd61b45997dd5e687a3a13, type: 3} + m_Name: Early 90s and later + m_EditorClassIdentifier: + Polarities: serializedVersion: 2 m_Curve: - serializedVersion: 3 @@ -153,3 +153,75 @@ MonoBehaviour: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + PolaritiesCurveSlicingCount: 256 + Velocities: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.16 + value: 1.061 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.41 + value: 1.051 + inSlope: -0.080546916 + outSlope: -0.080546916 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.19800007 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.53 + value: 1 + inSlope: -0.30552328 + outSlope: -0.30552328 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.702 + value: 0.968 + inSlope: 0.0005121914 + outSlope: 0.0005121914 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.54385066 + - serializedVersion: 3 + time: 0.95 + value: 0.968 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1.03 + value: 0.945 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + VelocitiesCurveSlicingCount: 256 + TimeThresholdMs: 60 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset.meta similarity index 79% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset.meta index 3fa0de9f4..5dea9f5c3 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Velocities.asset.meta +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 20f0505b91931ca44ad265ca55756c76 +guid: 077fb32bbe758cf4b83a7b1344a53fd9 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset similarity index 65% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset index 1d9b7a8b3..642bec187 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Late 80s to early 90s - Polarities.asset +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset @@ -9,10 +9,10 @@ MonoBehaviour: m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: Late 80s to early 90s - Polarities - m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset - curve: + m_Script: {fileID: 11500000, guid: 6685e224cafd61b45997dd5e687a3a13, type: 3} + m_Name: Late 80s to early 90s + m_EditorClassIdentifier: + Polarities: serializedVersion: 2 m_Curve: - serializedVersion: 3 @@ -144,3 +144,75 @@ MonoBehaviour: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + PolaritiesCurveSlicingCount: 256 + Velocities: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.16 + value: 1.06 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.41 + value: 1.05 + inSlope: -0.080546916 + outSlope: -0.080546916 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.19800007 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.53 + value: 1 + inSlope: -0.30135643 + outSlope: -0.30135643 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.702 + value: 0.968 + inSlope: 0.0005121914 + outSlope: 0.0005121914 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.54385066 + - serializedVersion: 3 + time: 0.95 + value: 0.968 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1.03 + value: 0.945 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + VelocitiesCurveSlicingCount: 256 + TimeThresholdMs: 60 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset.meta similarity index 79% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset.meta index 6ca0b8f83..47a36cd6a 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Velocities.asset.meta +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9c793eb509ab1de49b7ed96dc9d97db2 +guid: f8799be363ab20a459dd185669ece8aa NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset similarity index 62% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset index ba4e2dd15..51d229f52 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Mid 80s - Polarities.asset +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset @@ -9,10 +9,10 @@ MonoBehaviour: m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 927b6f60022ae314c9f37c8ffd39c67b, type: 3} - m_Name: Mid 80s - Polarities - m_EditorClassIdentifier: VisualPinball.Unity::AnimationCurveAsset - curve: + m_Script: {fileID: 11500000, guid: 6685e224cafd61b45997dd5e687a3a13, type: 3} + m_Name: Mid 80s + m_EditorClassIdentifier: + Polarities: serializedVersion: 2 m_Curve: - serializedVersion: 3 @@ -126,3 +126,75 @@ MonoBehaviour: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + PolaritiesCurveSlicingCount: 256 + Velocities: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.16 + value: 1.06 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.41 + value: 1.05 + inSlope: -0.080546916 + outSlope: -0.080546916 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.19800007 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.53 + value: 1 + inSlope: -0.30135643 + outSlope: -0.30135643 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.702 + value: 0.968 + inSlope: 0.0005121914 + outSlope: 0.0005121914 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.54385066 + - serializedVersion: 3 + time: 0.95 + value: 0.968 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1.03 + value: 0.945 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + VelocitiesCurveSlicingCount: 256 + TimeThresholdMs: 60 diff --git a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset.meta similarity index 79% rename from VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset.meta index 2b893ad74..4b1a8e3ba 100644 --- a/VisualPinball.Unity/Assets/Curves/CorrectionCurves/nFozzy/Early 90s and later - Polarities.asset.meta +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 576f4daa08eb10240b2e42da0a7bc2e3 +guid: 56b227b098ffccd4d8d751fe45079373 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperColliderInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperColliderInspector.cs index e4393c9af..08bbec9bf 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperColliderInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Flipper/FlipperColliderInspector.cs @@ -40,6 +40,7 @@ public override void OnInspectorGUI() ItemDataField("Scatter Angle", ref Data.Scatter, false); ItemDataField("EOS Torque", ref Data.TorqueDamping, false); ItemDataField("EOS Torque Angle", ref Data.TorqueDampingAngle, false); + ItemDataField("Flipper Correction", ref ColliderAuthoring.FlipperCorrection, false, "Apply flipper corrections, a.k.a nFozzy flipper physics."); base.OnInspectorGUI(); } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs index 8ddb13275..24f9ae8c3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs @@ -236,6 +236,16 @@ protected void ItemDataField(string label, ref Engine.Math.Color field, bool dir } } + protected void ItemDataField(string label, ref T field, bool dirtyMesh = true, string tooltip = "") where T : ScriptableObject + { + EditorGUI.BeginChangeCheck(); + var val = EditorGUILayout.ObjectField(new GUIContent(label, tooltip), field, typeof(T), false) as T; + if (EditorGUI.EndChangeCheck()) { + FinishEdit(label, dirtyMesh); + field = val; + } + } + protected void ItemReferenceField(string label, string cacheKey, ref string field, bool dirtyMesh = true) where TItemAuthoring : ItemAuthoring where TData : ItemData where TItem : Item, IRenderable diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs deleted file mode 100644 index b076b4e03..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2021 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; - -namespace VisualPinball.Unity -{ - /// - /// Simple class to encapsulate Curves as assets - /// - [CreateAssetMenu] - public class AnimationCurveAsset : ScriptableObject - { - public AnimationCurve curve = AnimationCurve.Linear(0, 0, 1, 1); - - public static implicit operator AnimationCurve(AnimationCurveAsset me) - { - return me.curve; - } - public static implicit operator AnimationCurveAsset(AnimationCurve curve) - { - AnimationCurveAsset asset = CreateInstance(); - asset.curve = curve; - return asset; - } - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index d1b1492c9..3864950e9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -72,8 +72,10 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio var player = transform.GetComponentInParent(); - var correctionAuthoring = gameObject.GetComponent(); - if (correctionAuthoring) { + var colliderAuthoring = gameObject.GetComponent(); + if (colliderAuthoring && colliderAuthoring.FlipperCorrection) { + + var fc = colliderAuthoring.FlipperCorrection; // create trigger var trigger = CreateCorrectionTrigger(); @@ -87,14 +89,14 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio ref var root = ref builder.ConstructRoot(); root.FlipperEntity = entity; - root.TimeDelayMs = correctionAuthoring.TimeThresholdMs; + root.TimeDelayMs = fc.TimeThresholdMs; // Discretize the curves - var polarities = builder.Allocate(ref root.Polarities, correctionAuthoring.PolaritiesCurveSlicingCount+1); - if (correctionAuthoring.Polarities && correctionAuthoring.Polarities.curve != null) + var polarities = builder.Allocate(ref root.Polarities, fc.PolaritiesCurveSlicingCount + 1); + if (fc.Polarities != null) { - var curve = correctionAuthoring.Polarities.curve; - float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.PolaritiesCurveSlicingCount; + var curve = fc.Polarities; + float stepP = (curve[curve.length - 1].time - curve[0].time) / fc.PolaritiesCurveSlicingCount; int i = 0; for (var t = curve[0].time; t <= curve[curve.length - 1].time; t += stepP) { @@ -104,18 +106,18 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio } else { - for (int i = 0; i < correctionAuthoring.PolaritiesCurveSlicingCount + 1; i++) + for (int i = 0; i < fc.PolaritiesCurveSlicingCount + 1; i++) { - polarities[i].x = (float)i / (float)correctionAuthoring.PolaritiesCurveSlicingCount; + polarities[i].x = i / (float)fc.PolaritiesCurveSlicingCount; polarities[i].y = 0F; } } - var velocities = builder.Allocate(ref root.Velocities, correctionAuthoring.VelocitiesCurveSlicingCount + 1); - if (correctionAuthoring.Velocities && correctionAuthoring.Velocities.curve != null) + var velocities = builder.Allocate(ref root.Velocities, fc.VelocitiesCurveSlicingCount + 1); + if (fc.Velocities != null) { - var curve = correctionAuthoring.Velocities.curve; - float stepP = (curve[curve.length - 1].time - curve[0].time) / (float)correctionAuthoring.VelocitiesCurveSlicingCount; + var curve = fc.Velocities; + float stepP = (curve[curve.length - 1].time - curve[0].time) / fc.VelocitiesCurveSlicingCount; int i = 0; for (var t = curve[0].time; t <= curve[curve.length - 1].time; t += stepP) { @@ -125,9 +127,9 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio } else { - for (int i = 0; i < correctionAuthoring.VelocitiesCurveSlicingCount + 1; i++) + for (int i = 0; i < fc.VelocitiesCurveSlicingCount + 1; i++) { - velocities[i].x = (float)i / (float)correctionAuthoring.PolaritiesCurveSlicingCount; + velocities[i].x = i / (float)fc.PolaritiesCurveSlicingCount; velocities[i].y = 1F; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperColliderAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperColliderAuthoring.cs index 8c0950a54..5ef818cb1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperColliderAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperColliderAuthoring.cs @@ -29,5 +29,10 @@ public class FlipperColliderAuthoring : ItemColliderAuthoring ValidParents => ValidParentTypes; protected override IApiColliderGenerator InstantiateColliderApi(Player player, Entity entity, Entity parentEntity) => new FlipperApi(Item, entity, parentEntity, player); + + /// + /// If set, apply flipper correction (aka nFozzy) + /// + public FlipperCorrectionAsset FlipperCorrection; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index 576495c42..a444ad3c4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -27,6 +27,7 @@ internal static class FlipperCorrection public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrectionBlob flipperCorrectionBlob, in FlipperMovementData flipperMovementData, in FlipperStaticData flipperStaticData, uint timeMs) { + var timeSinceFlipperStartedRotatingToEndMs = timeMs - flipperMovementData.StartRotateToEndTime; // Time delay overrun test diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs similarity index 76% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs index c699f4cfd..7d0539da5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs @@ -1,4 +1,4 @@ -// Visual Pinball Engine +// Visual Pinball Engine // Copyright (C) 2021 freezy and VPE Team // // This program is free software: you can redistribute it and/or modify @@ -14,30 +14,29 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// ReSharper disable InconsistentNaming - using UnityEngine; namespace VisualPinball.Unity { /// - /// Makes flippers flip better, A.K.A. nFozzy physics. + /// Simple class to encapsulate Curves as assets /// - public class FlipperCorrectionAuthoring : MonoBehaviour + [CreateAssetMenu(menuName = "Visual Pinball/Flipper Correction", order = 101)] + public class FlipperCorrectionAsset : ScriptableObject { - public AnimationCurveAsset Polarities; - + public AnimationCurve Polarities = AnimationCurve.Linear(0, 0, 1, 1); [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] [Min(1)] public int PolaritiesCurveSlicingCount = 256; - public AnimationCurveAsset Velocities; - + public AnimationCurve Velocities = AnimationCurve.Linear(0, 0, 1, 1); [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] [Min(1)] public int VelocitiesCurveSlicingCount = 256; + [Tooltip("Time since flipper fire, in ms, after which the corrections are not applied anymore.")] public uint TimeThresholdMs = 60; + } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs.meta index e492ff085..3c6e93449 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/AnimationCurveAsset.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 927b6f60022ae314c9f37c8ffd39c67b +guid: 6685e224cafd61b45997dd5e687a3a13 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta deleted file mode 100644 index d751a51f7..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAuthoring.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5b9b7009cb3eada42944a8cf63ef9745 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From a0404610e6f36b821f9c3d9e1aedcd7fffe98043 Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 15 Jun 2021 22:32:27 +0200 Subject: [PATCH 15/23] doc: Update changelog. --- CHANGELOG.md | 1 + .../VPT/Flipper/FlipperCorrectionAsset.cs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48bdbe3e..d0cea509f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Built with [Unity 2020.2](https://github.com/freezy/VisualPinball.Engine/pull/255). ### Added +- Native support for nFozzy flipper physics ([#305](https://github.com/freezy/VisualPinball.Engine/pull/305)). - Automated camera clipping ([#304](https://github.com/freezy/VisualPinball.Engine/pull/304/files)). - DMD and segment display support ([Documentation](https://docs.visualpinball.org/creators-guide/manual/displays.html)). - Plugin: Mission Pinball Framework ([Documentation](https://docs.visualpinball.org/plugins/mpf/index.html)) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs index 7d0539da5..635a88739 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs @@ -19,9 +19,9 @@ namespace VisualPinball.Unity { /// - /// Simple class to encapsulate Curves as assets + /// An asset containing the flipper correction parameters (aka nFozzy). /// - [CreateAssetMenu(menuName = "Visual Pinball/Flipper Correction", order = 101)] + [CreateAssetMenu(fileName = "Flipper Correction", menuName = "Visual Pinball/Flipper Correction", order = 101)] public class FlipperCorrectionAsset : ScriptableObject { public AnimationCurve Polarities = AnimationCurve.Linear(0, 0, 1, 1); @@ -34,7 +34,6 @@ public class FlipperCorrectionAsset : ScriptableObject [Min(1)] public int VelocitiesCurveSlicingCount = 256; - [Tooltip("Time since flipper fire, in ms, after which the corrections are not applied anymore.")] public uint TimeThresholdMs = 60; From 42c670591890ad69b964b778921fbdac8628bb41 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 16 Jun 2021 00:24:16 +0200 Subject: [PATCH 16/23] doc: Document flipper physics parameters. --- .../mechanisms/flipper-collider-inspector.png | Bin 0 -> 28771 bytes .../manual/mechanisms/flippers.md | 74 +++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-collider-inspector.png diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-collider-inspector.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-collider-inspector.png new file mode 100644 index 0000000000000000000000000000000000000000..0f15c4b2e2c0ef04e5a2f4f768021e1fc3e72e74 GIT binary patch literal 28771 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 literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index df242232a..39d985c06 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -1,5 +1,75 @@ # Flipper -Flippers. They flip. +How flippers interact with the ball is what makes or breaks a pinball simulation. A few years ago, a community member named Mukuste spent time on improving Visual Pinball's flipper mechanics. The result was what has become known as the *physmod*, which provided a much more realistic simulation, and which was later merged into Visual Pinball's mainline. In his [own words](https://github.com/c-f-h/vpinball/wiki/VP10-Physics): -[You can program them too](xref:VisualPinball.Unity.FlipperApi)! \ No newline at end of file +> Flippers are now simulated as true dynamic rigid bodies which have forces from the solenoid, the return spring and the ball acting on them and accelerate accordingly. They will also properly bounce off their stoppers instead of just moving to maximum extension and then stopping. In practice, this means that flipper/ball interaction is now much more realistic and less binary. Post passes, light taps, cradle separations, drop and live catches are now all possible. Furthermore, the simulation of the friction of the flipper rubbers greatly improves aiming. + +Later, another member of the community named nFozzy managed to measure ball trajectory angles of real pinball machines, and developed a script where authors could provide a profile that would then slightly correct the ball trajectories during game play to match his measurements. This resulted in tables with the most realistic flipper behavior the community has produced yet. + +VPE's flipper behavior is identical to Visual Pinball's. However, VPE also provides native support for the nFozzy corrections. + +## Setup + +The easiest way to create a flipper is clicking on the flipper icon in the toolbar. This will instantiate the prefab and place it on the playfield. + +### Mesh + +VPE provides a procedurally generated flipper mesh. It consist of a *base mesh* (the plastic), and a *rubber mesh*. + +It's possible to provide a custom mesh for the flipper by replacing the game objects that generate the procedural meshes with others. However, the physics simulation will still use the original colliders, so make sure to adapt the parameters to match the custom flipper's dimensions. + +### Colliders + +Add display component + +Adding the collider component to the flipper makes it part of the physics simulation. Here you can tweak the various parameters. Most of the following is taken directly from [Mukuste's Wiki](https://github.com/c-f-h/vpinball/wiki/VP10-Physics#flipper-parameters). + +#### Mass + +This is the mass of the flipper (where 1 corresponds to standard ball mass, 80g). It basically describes how much the flipper interacts with the ball. A very heavy flipper will barely feel the impact of the ball and keep moving at almost the same velocity as it was before the hit. A very light flipper, on the other hand, will move much slower with the ball on it than it does without the ball, and will be deflected by the impact of the ball significantly. + +A good default value for this parameter is 1 - 1.25. + + +#### Strength + +This is the force (actually, torque) with which the solenoid accelerates the flipper. The higher this value, the faster the flipper will move. But be aware that this is directly linked to flipper mass: if the flipper is twice as heavy, it also needs twice the force to get it to move at the same speed. + +A reasonable range for this is 1000-3000, obviously depending a lot on the era of flippers being simulated and the desired speed of the game. + + +#### Elasticity and Elasticity Falloff + +This is basically the bounciness of the flipper rubber. Since real rubber is less bouncy when it is hit at a higher velocity, the Falloff parameter allows decreasing the elasticity for faster impacts. A value of 0 for falloff means no falloff, i.e., elasticity does not depend on velocity, and a value of 1.0 means that elasticity is halved at an impact velocity of 1 m/s. + +Good defaults for elasticity seem to be 0.8-0.9, with a falloff of 0.3. + +#### Friction + +This describes how much the rubber "grips" the ball. This value is very important for enabling center shots on the playfield with a moving ball, as well as backhands. In general it affects the aiming on all shots, but also makes a spinning ball deflect off the flipper in the proper direction. + +A good default seems to be 0.8. + +#### Return Strength Ratio + +This is the force of the return spring which pulls the flipper back down, relative to the solenoid force which pulls the flipper forward. For instance, at 0.10, the force of the return spring will be 1/10th of that of the solenoid. Due to how acceleration and velocity work, the time the flipper needs to return to its home position is about three times longer than that for the forward stroke in this example (square root of 10, to be precise). + +If you make this smaller, not only will the flipper return slower, but it will also pick up less speed if you briefly release the flipper and then press it again since it has less time to accelerate. A smaller value therefore makes it easier to do flipper tricks which involve light taps, such as cradle separations and flick passes. + +Try the range 0.07-0.10 to start with and experiment from there. + +#### Coil Ramp Up + +This simulates the fact that the magnetic field in the flipper solenoid takes a while to build up when the flipper button is pressed, and to fall off again when the button is released (also known as hysteresis). This means that the flipper will not have its full acceleration immediately as the coil needs some time to ramp up to the full magnetic field. + +At a value of 0, there is no ramp up, and the full acceleration takes effect immediately. At a nonzero value, this is the approximate time the solenoid needs to reach its full acceleration. For instance, if set to 3, the flipper coil will take around 30 ms to ramp up to full force. + +Gameplay-wise, the effect of this parameter is most strongly felt in situations where the flipper button is pressed only for a very short time, or released for a short time and then pressed again. In other words, it will make light taps much easier and therefore help with moves such as cradle separations and flick passes. Even tap passes can be achieved with the proper setting. + +Note that increasing this setting will decrease the speed of the flipper a bit and may need to be compensated with a higher Strength setting. Also, if this parameter is chosen too high, the flipper may feel sluggish and laggy. + +That's why per default, we recommend setting this value to 0. + +--- + +-> [API Reference](xref:VisualPinball.Unity.FlipperApi) \ No newline at end of file From e65e80adffad774ad53fce3e49d7a004092ce374 Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 17 Jun 2021 22:49:15 +0200 Subject: [PATCH 17/23] nfp: Rename profiles. --- .../{Mid 80s.asset => Late 70s to mid 80s.asset} | 4 ++-- .../{Mid 80s.asset.meta => Late 70s to mid 80s.asset.meta} | 0 ...Late 80s to early 90s.asset => Mid 80s to early 90s.asset} | 4 ++-- ...o early 90s.asset.meta => Mid 80s to early 90s.asset.meta} | 0 .../{Early 90s and later.asset => Mid 90s and later.asset} | 2 +- ... 90s and later.asset.meta => Mid 90s and later.asset.meta} | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename VisualPinball.Unity/Assets/Curves/Flipper Corrections/{Mid 80s.asset => Late 70s to mid 80s.asset} (98%) rename VisualPinball.Unity/Assets/Curves/Flipper Corrections/{Mid 80s.asset.meta => Late 70s to mid 80s.asset.meta} (100%) rename VisualPinball.Unity/Assets/Curves/Flipper Corrections/{Late 80s to early 90s.asset => Mid 80s to early 90s.asset} (98%) rename VisualPinball.Unity/Assets/Curves/Flipper Corrections/{Late 80s to early 90s.asset.meta => Mid 80s to early 90s.asset.meta} (100%) rename VisualPinball.Unity/Assets/Curves/Flipper Corrections/{Early 90s and later.asset => Mid 90s and later.asset} (99%) rename VisualPinball.Unity/Assets/Curves/Flipper Corrections/{Early 90s and later.asset.meta => Mid 90s and later.asset.meta} (100%) diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset similarity index 98% rename from VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset index 51d229f52..e83d3d0ec 100644 --- a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 6685e224cafd61b45997dd5e687a3a13, type: 3} - m_Name: Mid 80s + m_Name: Late 70s to mid 80s m_EditorClassIdentifier: Polarities: serializedVersion: 2 @@ -197,4 +197,4 @@ MonoBehaviour: m_PostInfinity: 2 m_RotationOrder: 4 VelocitiesCurveSlicingCount: 256 - TimeThresholdMs: 60 + TimeThresholdMs: 80 diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset.meta similarity index 100% rename from VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s.asset.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset.meta diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset similarity index 98% rename from VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset index 642bec187..a3c52396c 100644 --- a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 6685e224cafd61b45997dd5e687a3a13, type: 3} - m_Name: Late 80s to early 90s + m_Name: Mid 80s to early 90s m_EditorClassIdentifier: Polarities: serializedVersion: 2 @@ -215,4 +215,4 @@ MonoBehaviour: m_PostInfinity: 2 m_RotationOrder: 4 VelocitiesCurveSlicingCount: 256 - TimeThresholdMs: 60 + TimeThresholdMs: 80 diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset.meta similarity index 100% rename from VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 80s to early 90s.asset.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset.meta diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset similarity index 99% rename from VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset index e77362d41..0162ff046 100644 --- a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 6685e224cafd61b45997dd5e687a3a13, type: 3} - m_Name: Early 90s and later + m_Name: Mid 90s and later m_EditorClassIdentifier: Polarities: serializedVersion: 2 diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset.meta similarity index 100% rename from VisualPinball.Unity/Assets/Curves/Flipper Corrections/Early 90s and later.asset.meta rename to VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset.meta From d0e2cd673c84e7efa7fdf411155fff97449c34cb Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 17 Jun 2021 22:49:48 +0200 Subject: [PATCH 18/23] doc: Update flipper doc. --- .../mechanisms/flipper-correction-asset.png | Bin 0 -> 18974 bytes .../flipper-correction-polarities.png | Bin 0 -> 14359 bytes .../flipper-correction-velocities.png | Bin 0 -> 14391 bytes .../manual/mechanisms/flippers.md | 57 +++++++++++++++--- 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-asset.png create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-polarities.png create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-velocities.png diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-asset.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-asset.png new file mode 100644 index 0000000000000000000000000000000000000000..be91fb8c0d6a6a4fbf7462da06b684069c207394 GIT binary patch literal 18974 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 V{PY#x?x;8H_&)M0Tp8AS@-@>QqZ!j`S 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%0DPonTyU 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 literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-velocities.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-velocities.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0f4c84b03250db2a7a286c22922bc2a3f0f0ec GIT binary patch literal 14391 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 PYoGjM73qRU#xMRC*lrid literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index 39d985c06..6126b913b 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -18,11 +18,11 @@ VPE provides a procedurally generated flipper mesh. It consist of a *base mesh* It's possible to provide a custom mesh for the flipper by replacing the game objects that generate the procedural meshes with others. However, the physics simulation will still use the original colliders, so make sure to adapt the parameters to match the custom flipper's dimensions. -### Colliders +### Physics -Add display component +Flipper Collider -Adding the collider component to the flipper makes it part of the physics simulation. Here you can tweak the various parameters. Most of the following is taken directly from [Mukuste's Wiki](https://github.com/c-f-h/vpinball/wiki/VP10-Physics#flipper-parameters). +Adding the *Flipper Collider* component to the flipper makes it part of the physics simulation. Here you can tweak the various parameters. Most of the following is taken directly from [Mukuste's Wiki](https://github.com/c-f-h/vpinball/wiki/VP10-Physics#flipper-parameters). #### Mass @@ -42,13 +42,11 @@ A reasonable range for this is 1000-3000, obviously depending a lot on the era o This is basically the bounciness of the flipper rubber. Since real rubber is less bouncy when it is hit at a higher velocity, the Falloff parameter allows decreasing the elasticity for faster impacts. A value of 0 for falloff means no falloff, i.e., elasticity does not depend on velocity, and a value of 1.0 means that elasticity is halved at an impact velocity of 1 m/s. -Good defaults for elasticity seem to be 0.8-0.9, with a falloff of 0.3. #### Friction This describes how much the rubber "grips" the ball. This value is very important for enabling center shots on the playfield with a moving ball, as well as backhands. In general it affects the aiming on all shots, but also makes a spinning ball deflect off the flipper in the proper direction. -A good default seems to be 0.8. #### Return Strength Ratio @@ -56,8 +54,6 @@ This is the force of the return spring which pulls the flipper back down, relati If you make this smaller, not only will the flipper return slower, but it will also pick up less speed if you briefly release the flipper and then press it again since it has less time to accelerate. A smaller value therefore makes it easier to do flipper tricks which involve light taps, such as cradle separations and flick passes. -Try the range 0.07-0.10 to start with and experiment from there. - #### Coil Ramp Up This simulates the fact that the magnetic field in the flipper solenoid takes a while to build up when the flipper button is pressed, and to fall off again when the button is released (also known as hysteresis). This means that the flipper will not have its full acceleration immediately as the coil needs some time to ramp up to the full magnetic field. @@ -68,7 +64,52 @@ Gameplay-wise, the effect of this parameter is most strongly felt in situations Note that increasing this setting will decrease the speed of the flipper a bit and may need to be compensated with a higher Strength setting. Also, if this parameter is chosen too high, the flipper may feel sluggish and laggy. -That's why per default, we recommend setting this value to 0. +#### EOS Torque and Angle + +The "end of stroke" torque is the force that holds the flipper up once it reached the end position. The angle defines how many degrees before the end position that force is applied. + +#### Flipper Correction + +This is where you can set a profile for nFozzy's flipper physics. Profiles are files in your asset folder that you can create and modify. VPE ships with three profiles based on nFozzy's measurements that cover the solid state era of pinball machines. EM machines usually don't need flipper correction. + +Clicking on a flipper correction profile in your project window shows this in the inspector: + +Flipper Correction + +You see that it consists of two curves, one describing the corrected velocity, and one the corrected angle. Both curves are relative ball position on the flipper. Additionally, there is a threshold which defines after how many milliseconds since the flipper was fired, no corrections will be applied. + +You can tweak these curves by clicking on them in the inspector. However, you cannot edit VPE's default profiles directly, so you need to copy it to the table's asset folder first (and of course, assign the new copy to your flippers). + +Flipper Correction: Polarities + +Polarity Correction Curve + +Flipper Correction: Velocities + +Velocity Correction Curve + +When applying one of default profiles, you'll also need to adapt the flipper parameters in order to obtain realistic ball behavior. We've also added a column with good values for EM machines that don't need correction. + +| | Late 70s to mid 80s | Mid 80s to early 90s | Mid 90s and later | EMs | +|--------------------|---------------------|----------------------|-------------------|----------------| +| Mass | 1 | 1 | 1 | 1 | +| Strength | 1400-1600 (1500) | 2000-2600 | 3200-3300 (3250) | 500-1000 (750) | +| Elasticity | 0.88 | 0.88 | 0.88 | 0.88 | +| Elasticity Falloff | 0.15 | 0.15 | 0.15 | 0.15 | +| Fricition | 0.9 | 0.9 | 0.9 | 0.8-0.9 | +| Return Strength | 0.09 | 0.07 | 0.055 | 0.11 | +| Coil Ramp Up | 2.5 | 2.5 | 2.5 | 2.5 | +| Scatter Angle | 0 | 0 | 0 | 0 | +| EOS Torque | 0.3 | 0.275 | 0.275 | 0.3 | +| EOS Torque Angle | 4 | 6 | 6 | 4 | + + +## Common Gotchas + +### Flipper Length + +A common mistake is incorrect flipper length. A 3-inch flipper with rubbers will be about 3.125 inches long. This translates to about 147 VP units. Therefore, the flipper start radius + the flipper length + the flipper end radius should equal approximately 147 VP units. + --- From 0d3614324fcd69d45ff0e3d990b01fd3fd44d524 Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 17 Jun 2021 22:50:17 +0200 Subject: [PATCH 19/23] nfp: Hide number of slices in inspector. --- .../VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs | 2 -- .../VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs index a444ad3c4..fe2baaa1f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -37,7 +37,6 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti ref var velocities = ref flipperCorrectionBlob.Velocities; ref var polarities = ref flipperCorrectionBlob.Polarities; var angleCur = flipperMovementData.Angle; - var flipperStrength = flipperStaticData.Strength; var ballPosition = ballData.Position; var ballVelocity = ballData.Velocity; @@ -47,7 +46,6 @@ public static void OnBallLeaveFlipper(ref BallData ballData, ref FlipperCorrecti return; } - var angleAtFire = flipperMovementData.AngleAtRotateToEnd; var angleStart = flipperStaticData.AngleStart; var angleEnd = flipperStaticData.AngleEnd; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs index 635a88739..07fab288f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs @@ -25,11 +25,13 @@ namespace VisualPinball.Unity public class FlipperCorrectionAsset : ScriptableObject { public AnimationCurve Polarities = AnimationCurve.Linear(0, 0, 1, 1); + [HideInInspector] [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] [Min(1)] public int PolaritiesCurveSlicingCount = 256; public AnimationCurve Velocities = AnimationCurve.Linear(0, 0, 1, 1); + [HideInInspector] [Tooltip("The curve will be sliced in smaller straight lines. The bigger, the more precise, but at memory cost.")] [Min(1)] public int VelocitiesCurveSlicingCount = 256; From 31bf87c3cd654d5013bb23fc30cfe823302a44ba Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 17 Jun 2021 23:08:49 +0200 Subject: [PATCH 20/23] doc: Fix spelling. --- .../Documentation~/creators-guide/manual/mechanisms/flippers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index 6126b913b..8c7a8b471 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -96,7 +96,7 @@ When applying one of default profiles, you'll also need to adapt the flipper par | Strength | 1400-1600 (1500) | 2000-2600 | 3200-3300 (3250) | 500-1000 (750) | | Elasticity | 0.88 | 0.88 | 0.88 | 0.88 | | Elasticity Falloff | 0.15 | 0.15 | 0.15 | 0.15 | -| Fricition | 0.9 | 0.9 | 0.9 | 0.8-0.9 | +| Friction | 0.9 | 0.9 | 0.9 | 0.8-0.9 | | Return Strength | 0.09 | 0.07 | 0.055 | 0.11 | | Coil Ramp Up | 2.5 | 2.5 | 2.5 | 2.5 | | Scatter Angle | 0 | 0 | 0 | 0 | From 641523ca27fea5adba00a6f41fc5878cccdb5f18 Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 17 Jun 2021 23:34:14 +0200 Subject: [PATCH 21/23] doc: Rephrase. --- .../Documentation~/creators-guide/manual/mechanisms/flippers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index 8c7a8b471..98e7cf6dc 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -76,7 +76,7 @@ Clicking on a flipper correction profile in your project window shows this in th Flipper Correction -You see that it consists of two curves, one describing the corrected velocity, and one the corrected angle. Both curves are relative ball position on the flipper. Additionally, there is a threshold which defines after how many milliseconds since the flipper was fired, no corrections will be applied. +You see that it consists of two curves, one describing the corrected velocity magnitude, and one the corrected x-axis of the velocity. Both curves are relative ball position on the flipper, normalized to the flipper length. Additionally, there is a threshold which defines after how many milliseconds since the flipper was fired, no corrections will be applied. You can tweak these curves by clicking on them in the inspector. However, you cannot edit VPE's default profiles directly, so you need to copy it to the table's asset folder first (and of course, assign the new copy to your flippers). From 4b5099320af2e3712bfd6833983b154416b12f2c Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 17 Jun 2021 23:51:19 +0200 Subject: [PATCH 22/23] doc: Remove recommended values from prose. --- .../creators-guide/manual/mechanisms/flippers.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index 98e7cf6dc..7098d1385 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -28,15 +28,11 @@ Adding the *Flipper Collider* component to the flipper makes it part of the phys This is the mass of the flipper (where 1 corresponds to standard ball mass, 80g). It basically describes how much the flipper interacts with the ball. A very heavy flipper will barely feel the impact of the ball and keep moving at almost the same velocity as it was before the hit. A very light flipper, on the other hand, will move much slower with the ball on it than it does without the ball, and will be deflected by the impact of the ball significantly. -A good default value for this parameter is 1 - 1.25. - #### Strength This is the force (actually, torque) with which the solenoid accelerates the flipper. The higher this value, the faster the flipper will move. But be aware that this is directly linked to flipper mass: if the flipper is twice as heavy, it also needs twice the force to get it to move at the same speed. -A reasonable range for this is 1000-3000, obviously depending a lot on the era of flippers being simulated and the desired speed of the game. - #### Elasticity and Elasticity Falloff From 6bc2ebbd51e3b88f852751fd34b62d6bcae24f34 Mon Sep 17 00:00:00 2001 From: Eli Curtz Date: Thu, 17 Jun 2021 16:45:01 -0700 Subject: [PATCH 23/23] Update flippers.md Copy edit pass --- .../manual/mechanisms/flippers.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index 7098d1385..5ecd0009c 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -1,22 +1,22 @@ # Flipper -How flippers interact with the ball is what makes or breaks a pinball simulation. A few years ago, a community member named Mukuste spent time on improving Visual Pinball's flipper mechanics. The result was what has become known as the *physmod*, which provided a much more realistic simulation, and which was later merged into Visual Pinball's mainline. In his [own words](https://github.com/c-f-h/vpinball/wiki/VP10-Physics): +How flippers interact with the ball is what makes or breaks a pinball simulation. A few years ago, a community member named Mukuste spent time on improving Visual Pinball's flipper mechanics. The result was what has become known as the *physmod*, which provided a much more realistic simulation, and was later merged into Visual Pinball's main branch. In his [own words](https://github.com/c-f-h/vpinball/wiki/VP10-Physics): > Flippers are now simulated as true dynamic rigid bodies which have forces from the solenoid, the return spring and the ball acting on them and accelerate accordingly. They will also properly bounce off their stoppers instead of just moving to maximum extension and then stopping. In practice, this means that flipper/ball interaction is now much more realistic and less binary. Post passes, light taps, cradle separations, drop and live catches are now all possible. Furthermore, the simulation of the friction of the flipper rubbers greatly improves aiming. -Later, another member of the community named nFozzy managed to measure ball trajectory angles of real pinball machines, and developed a script where authors could provide a profile that would then slightly correct the ball trajectories during game play to match his measurements. This resulted in tables with the most realistic flipper behavior the community has produced yet. +Later, another member of the community named nFozzy managed to measure ball trajectory angles on real pinball machines, and developed a script where authors could provide a profile that would slightly adjust the ball trajectories during game play to match his measurements. This resulted in tables with the most realistic flipper behavior the community has produced yet. -VPE's flipper behavior is identical to Visual Pinball's. However, VPE also provides native support for the nFozzy corrections. +VPE's default flipper behavior is identical to Visual Pinball's. However, VPE also provides native support for the nFozzy adjustments. ## Setup -The easiest way to create a flipper is clicking on the flipper icon in the toolbar. This will instantiate the prefab and place it on the playfield. +The easiest way to create a flipper is clicking on the flipper icon in the toolbar. This will instantiate a flipper prefab and place it on the playfield. ### Mesh -VPE provides a procedurally generated flipper mesh. It consist of a *base mesh* (the plastic), and a *rubber mesh*. +VPE provides a procedurally generated flipper mesh. It consist of a *base mesh* (the plastic bat), and a *rubber mesh*. -It's possible to provide a custom mesh for the flipper by replacing the game objects that generate the procedural meshes with others. However, the physics simulation will still use the original colliders, so make sure to adapt the parameters to match the custom flipper's dimensions. +It's possible to provide a custom mesh for the flipper by replacing the game objects used to generate the procedural meshes. However, the physics simulation will still use the original colliders, so make sure to adapt the parameters to match the custom flipper's dimensions. ### Physics @@ -26,17 +26,17 @@ Adding the *Flipper Collider* component to the flipper makes it part of the phys #### Mass -This is the mass of the flipper (where 1 corresponds to standard ball mass, 80g). It basically describes how much the flipper interacts with the ball. A very heavy flipper will barely feel the impact of the ball and keep moving at almost the same velocity as it was before the hit. A very light flipper, on the other hand, will move much slower with the ball on it than it does without the ball, and will be deflected by the impact of the ball significantly. +This is the mass of the flipper (where 1 corresponds to standard ball mass, 80g). It basically describes how much the flipper interacts with the ball. A very heavy flipper will barely feel the impact of the ball and keep moving at almost the same velocity as it was before the contact. A very light flipper, on the other hand, will move more slowly with the ball on it than it does without, and will be significantly deflected by the impact of a ball. #### Strength -This is the force (actually, torque) with which the solenoid accelerates the flipper. The higher this value, the faster the flipper will move. But be aware that this is directly linked to flipper mass: if the flipper is twice as heavy, it also needs twice the force to get it to move at the same speed. +This is the force (actually torque) with which the solenoid accelerates the flipper. The higher this value, the faster the flipper will move. Be aware that this is directly linked to flipper mass; if the flipper is twice as heavy, it also needs twice the force to get it to accelerate at the same rate. #### Elasticity and Elasticity Falloff -This is basically the bounciness of the flipper rubber. Since real rubber is less bouncy when it is hit at a higher velocity, the Falloff parameter allows decreasing the elasticity for faster impacts. A value of 0 for falloff means no falloff, i.e., elasticity does not depend on velocity, and a value of 1.0 means that elasticity is halved at an impact velocity of 1 m/s. +This is basically the bounciness of the flipper rubber. Since real rubber is less bouncy when it is hit at a higher velocity, the Falloff parameter allows decreasing the elasticity for fast impacts. A value of 0 for falloff means no falloff, i.e. elasticity is independant of velocity, and a value of 1.0 means that elasticity is halved at an impact velocity of 1 m/s. #### Friction @@ -54,15 +54,15 @@ If you make this smaller, not only will the flipper return slower, but it will a This simulates the fact that the magnetic field in the flipper solenoid takes a while to build up when the flipper button is pressed, and to fall off again when the button is released (also known as hysteresis). This means that the flipper will not have its full acceleration immediately as the coil needs some time to ramp up to the full magnetic field. -At a value of 0, there is no ramp up, and the full acceleration takes effect immediately. At a nonzero value, this is the approximate time the solenoid needs to reach its full acceleration. For instance, if set to 3, the flipper coil will take around 30 ms to ramp up to full force. +At a value of 0, there is no ramp up, and the full acceleration takes effect immediately. At a nonzero value, this is the relative time the solenoid needs to reach its full acceleration. For instance, if set to 3, the flipper coil will take around 30 ms to ramp up to full force. Gameplay-wise, the effect of this parameter is most strongly felt in situations where the flipper button is pressed only for a very short time, or released for a short time and then pressed again. In other words, it will make light taps much easier and therefore help with moves such as cradle separations and flick passes. Even tap passes can be achieved with the proper setting. -Note that increasing this setting will decrease the speed of the flipper a bit and may need to be compensated with a higher Strength setting. Also, if this parameter is chosen too high, the flipper may feel sluggish and laggy. +Note that increasing this setting will decrease the speed of the flipper a bit and may need to be compensated with a higher Strength setting. Also, if this parameter is set too high, the flipper may feel sluggish and laggy. #### EOS Torque and Angle -The "end of stroke" torque is the force that holds the flipper up once it reached the end position. The angle defines how many degrees before the end position that force is applied. +The "end of stroke" torque is the force that holds the flipper up once it reached the end position. The angle defines how many degrees from the end position that force is applied. #### Flipper Correction @@ -72,9 +72,9 @@ Clicking on a flipper correction profile in your project window shows this in th Flipper Correction -You see that it consists of two curves, one describing the corrected velocity magnitude, and one the corrected x-axis of the velocity. Both curves are relative ball position on the flipper, normalized to the flipper length. Additionally, there is a threshold which defines after how many milliseconds since the flipper was fired, no corrections will be applied. +You can see that it consists of two curves, one describing the corrected velocity magnitude, and one the corrected x-axis of the velocity. Both curves are relative to ball position on the flipper, and normalized to the flipper length. Additionally, there is a threshold in milliseconds since the flipper was fired, after which no corrections will be applied. -You can tweak these curves by clicking on them in the inspector. However, you cannot edit VPE's default profiles directly, so you need to copy it to the table's asset folder first (and of course, assign the new copy to your flippers). +You can tweak these curves by clicking on them in the inspector. However you cannot edit VPE's default profiles directly, you must copy it to the table's asset folder first (and of course, assign the new copy to your flippers). Flipper Correction: Polarities @@ -84,7 +84,7 @@ You can tweak these curves by clicking on them in the inspector. However, you ca Velocity Correction Curve -When applying one of default profiles, you'll also need to adapt the flipper parameters in order to obtain realistic ball behavior. We've also added a column with good values for EM machines that don't need correction. +When applying one of default profiles, you should also adjust the flipper parameters in order to obtain realistic ball behavior. We've also added a column with good values for EM machines that don't need correction. | | Late 70s to mid 80s | Mid 80s to early 90s | Mid 90s and later | EMs | |--------------------|---------------------|----------------------|-------------------|----------------| @@ -109,4 +109,4 @@ A common mistake is incorrect flipper length. A 3-inch flipper with rubbers will --- --> [API Reference](xref:VisualPinball.Unity.FlipperApi) \ No newline at end of file +-> [API Reference](xref:VisualPinball.Unity.FlipperApi)