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/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/Flipper Corrections.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections.meta new file mode 100644 index 000000000..a7560f0bf --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5037300f52b5e114995172820b8543bb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset new file mode 100644 index 000000000..e83d3d0ec --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset @@ -0,0 +1,200 @@ +%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: 6685e224cafd61b45997dd5e687a3a13, type: 3} + m_Name: Late 70s to mid 80s + m_EditorClassIdentifier: + Polarities: + 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 + 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: 80 diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset.meta new file mode 100644 index 000000000..4b1a8e3ba --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Late 70s to mid 80s.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 56b227b098ffccd4d8d751fe45079373 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset new file mode 100644 index 000000000..a3c52396c --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset @@ -0,0 +1,218 @@ +%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: 6685e224cafd61b45997dd5e687a3a13, type: 3} + m_Name: Mid 80s to early 90s + m_EditorClassIdentifier: + Polarities: + 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 + 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: 80 diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset.meta new file mode 100644 index 000000000..47a36cd6a --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 80s to early 90s.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8799be363ab20a459dd185669ece8aa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset new file mode 100644 index 000000000..0162ff046 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset @@ -0,0 +1,227 @@ +%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: 6685e224cafd61b45997dd5e687a3a13, type: 3} + m_Name: Mid 90s and later + m_EditorClassIdentifier: + Polarities: + 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.0012235044 + outSlope: -0.0012235044 + tangentMode: 0 + weightedMode: 0 + inWeight: 1 + outWeight: 0.2978914 + - 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 + 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/Flipper Corrections/Mid 90s and later.asset.meta b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset.meta new file mode 100644 index 000000000..5dea9f5c3 --- /dev/null +++ b/VisualPinball.Unity/Assets/Curves/Flipper Corrections/Mid 90s and later.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 077fb32bbe758cf4b83a7b1344a53fd9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: 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 000000000..0f15c4b2e Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-collider-inspector.png differ 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 000000000..be91fb8c0 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-asset.png differ diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-polarities.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-polarities.png new file mode 100644 index 000000000..3b7f7b5b1 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-polarities.png differ 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 000000000..ed0f4c84b Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flipper-correction-velocities.png differ diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md index df242232a..5ecd0009c 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/flippers.md @@ -1,5 +1,112 @@ # 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 was later merged into Visual Pinball's main branch. 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 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 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 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 bat), and a *rubber mesh*. + +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 + +Flipper Collider + +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 + +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. 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 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 + +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. + + +#### 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. + +#### 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 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 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 from 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 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, you must 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 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 | +|--------------------|---------------------|----------------------|-------------------|----------------| +| 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 | +| 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 | +| 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. + + +--- + +-> [API Reference](xref:VisualPinball.Unity.FlipperApi) 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/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/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..5df0c0a0c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs @@ -93,7 +93,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 +102,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 +127,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 +145,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,19 +156,38 @@ in spinnerStaticData ); SetComponent(spinnerStaticData.PlateEntity, spinnerMovementData); 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); + ref var flipperCorrectionBlob = ref flipperCorrectionData.Value.Value; + var flipperMovementData = GetComponent(flipperCorrectionBlob.FlipperEntity); + var flipperStaticData = GetComponent(flipperCorrectionBlob.FlipperEntity); + FlipperCorrection.OnBallLeaveFlipper( + ref ballData, ref flipperCorrectionBlob, in flipperMovementData, in flipperStaticData, timeMsec + ); + } + + } else { + SetComponent(coll.Entity, triggerAnimationData); + } break; } - case ColliderType.KickerCircle: + + case ColliderType.KickerCircle: { var kickerCollisionData = GetComponent(coll.Entity); var kickerStaticData = GetComponent(coll.Entity); // ReSharper disable once ConditionIsAlwaysTrueOrFalse @@ -175,6 +199,7 @@ in spinnerStaticData ); SetComponent(coll.Entity, kickerCollisionData); break; + } case ColliderType.Line: case ColliderType.Line3D: @@ -197,12 +222,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); + ref var flipperCorrectionBlob = ref flipperCorrectionData.Value.Value; + var flipperMovementData = GetComponent(flipperCorrectionBlob.FlipperEntity); + var flipperStaticData = GetComponent(flipperCorrectionBlob.FlipperEntity); + FlipperCorrection.OnBallLeaveFlipper( + ref ballData, ref flipperCorrectionBlob, in flipperMovementData, in flipperStaticData, timeMsec + ); + } + + } 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/Physics/Engine/DefaultPhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs index f5ae71362..d13f00d4d 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,8 @@ public void FlipperRotateToEnd(in Entity entity) { var mData = _entityManager.GetComponentData(entity); mData.EnableRotateEvent = 1; + mData.StartRotateToEndTime = _visualPinballSimulationSystemGroup.TimeMsec; + mData.AngleAtRotateToEnd = mData.Angle; _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 e4e26b596..3864950e9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -23,11 +23,13 @@ using System; using System.Collections.Generic; using System.Linq; +using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.Game; using VisualPinball.Engine.VPT.Flipper; +using VisualPinball.Engine.VPT.Trigger; namespace VisualPinball.Unity { @@ -68,8 +70,81 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio dstManager.AddComponentData(entity, GetHitData()); dstManager.AddComponentData(entity, new SolenoidStateData { Value = false }); + var player = transform.GetComponentInParent(); + + var colliderAuthoring = gameObject.GetComponent(); + if (colliderAuthoring && colliderAuthoring.FlipperCorrection) { + + var fc = colliderAuthoring.FlipperCorrection; + + // 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); + + using (var builder = new BlobBuilder(Allocator.Temp)) { + + ref var root = ref builder.ConstructRoot(); + root.FlipperEntity = entity; + root.TimeDelayMs = fc.TimeThresholdMs; + + // Discretize the curves + var polarities = builder.Allocate(ref root.Polarities, fc.PolaritiesCurveSlicingCount + 1); + if (fc.Polarities != null) + { + 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) + { + polarities[i].x = t; + polarities[i++].y = curve.Evaluate(t); + } + } + else + { + for (int i = 0; i < fc.PolaritiesCurveSlicingCount + 1; i++) + { + polarities[i].x = i / (float)fc.PolaritiesCurveSlicingCount; + polarities[i].y = 0F; + } + } + + var velocities = builder.Allocate(ref root.Velocities, fc.VelocitiesCurveSlicingCount + 1); + if (fc.Velocities != null) + { + 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) + { + velocities[i].x = t; + velocities[i++].y = curve.Evaluate(t); + } + } + else + { + for (int i = 0; i < fc.VelocitiesCurveSlicingCount + 1; i++) + { + velocities[i].x = i / (float)fc.PolaritiesCurveSlicingCount; + velocities[i].y = 1F; + } + } + + var blobAssetRef = builder.CreateBlobAssetReference(Allocator.Persistent); + + // add correction data + dstManager.AddComponentData(triggerEntity, new FlipperCorrectionData { + Value = blobAssetRef + }); + } + } + // register - transform.GetComponentInParent().RegisterFlipper(Item, entity, ParentEntity, gameObject); + player.RegisterFlipper(Item, entity, ParentEntity, gameObject); } public override void Restore() @@ -272,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, @@ -328,5 +404,29 @@ private FlipperHitData GetHitData() LastHitFace = false, }; } + + public 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[poly.Count - i - 1] = 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/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 new file mode 100644 index 000000000..fe2baaa1f --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrection.cs @@ -0,0 +1,149 @@ +// 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.Burst; +using Unity.Mathematics; +using UnityEngine; + +using Unity.Entities; + +namespace VisualPinball.Unity +{ + 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 angleCur = flipperMovementData.Angle; + + var ballPosition = ballData.Position; + var ballVelocity = ballData.Velocity; + var uncorrectedVel = ballVelocity; + if (ballVelocity.y > -8F) // ball going down + { + return; + } + + var angleAtFire = flipperMovementData.AngleAtRotateToEnd; + 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; + + // 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; + } + + // 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); + 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; + } + } +} 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/FlipperCorrectionAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs new file mode 100644 index 000000000..07fab288f --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs @@ -0,0 +1,43 @@ +// 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 +{ + /// + /// An asset containing the flipper correction parameters (aka nFozzy). + /// + [CreateAssetMenu(fileName = "Flipper Correction", menuName = "Visual Pinball/Flipper Correction", order = 101)] + 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; + + [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/FlipperCorrectionAsset.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs.meta new file mode 100644 index 000000000..3c6e93449 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6685e224cafd61b45997dd5e687a3a13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: 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..ae8565431 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperCorrectionBlob.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 Unity.Entities; +using Unity.Mathematics; + +namespace VisualPinball.Unity +{ + public struct FlipperCorrectionBlob + { + public Entity FlipperEntity; + public BlobArray Polarities; + public BlobArray Velocities; + public uint TimeDelayMs; + } +} 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 new file mode 100644 index 000000000..9c12dc204 --- /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 BlobAssetReference Value; + } +} 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/Flipper/FlipperMovementData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs index f5ce4278e..4f6c0b3bf 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs @@ -27,6 +27,8 @@ internal struct FlipperMovementData : IComponentData public sbyte EnableRotateEvent; public quaternion BaseRotation; public uint LastHitTime; + public uint StartRotateToEndTime; + public float AngleAtRotateToEnd; 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; 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() 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)); }