diff --git a/VisualPinball.Engine.Test/Math/VectorTests.cs b/VisualPinball.Engine.Test/Math/VectorTests.cs index 9c2f9bf50..e24ef006c 100644 --- a/VisualPinball.Engine.Test/Math/VectorTests.cs +++ b/VisualPinball.Engine.Test/Math/VectorTests.cs @@ -51,5 +51,15 @@ public void ShouldCorrectlyCrossVectors() .Should().BeEquivalentTo(new Vertex3D(-162.5f, -128.5f, 141.25f)); } + [Test] + public void ShouldCorrectlyLerpDragPoints() + { + var dp0 = new DragPointData(2f, 3f); + var dp1 = new DragPointData(8f, 12f); + + dp0.Lerp(dp1, 0).Center.Should().Be(new Vertex3D(2f, 3f, 0f)); + dp0.Lerp(dp1, 0.2f).Center.Should().Be(new Vertex3D(3.2f, 4.8f, 0f)); + dp0.Lerp(dp1, 1).Center.Should().Be(new Vertex3D(8f, 12f, 0f)); + } } } diff --git a/VisualPinball.Engine/Math/DragPointData.cs b/VisualPinball.Engine/Math/DragPointData.cs index 28f9f2aaa..da7089fc2 100644 --- a/VisualPinball.Engine/Math/DragPointData.cs +++ b/VisualPinball.Engine/Math/DragPointData.cs @@ -20,6 +20,7 @@ // ReSharper disable FieldCanBeMadeReadOnly.Global // ReSharper disable ConvertToConstant.Global // ReSharper disable CompareOfFloatsByEqualityOperator +// ReSharper disable InconsistentNaming #endregion using System; @@ -72,6 +73,20 @@ public override string ToString() return $"DragPoint({Center.X}/{Center.Y}/{Center.Z}, {(IsSmooth ? "S" : "")}{(IsSlingshot ? "SS" : "")}{(HasAutoTexture ? "A" : "")})"; } + public DragPointData Lerp(DragPointData dp, float pos) + { + return new DragPointData(Center + pos * (dp.Center - Center)) { + IsSmooth = dp.IsSmooth, + IsSlingshot = dp.IsSlingshot, + HasAutoTexture = dp.HasAutoTexture, + TextureCoord = dp.TextureCoord, + IsLocked = dp.IsLocked, + EditorLayer = dp.EditorLayer, + EditorLayerName = dp.EditorLayerName, + EditorLayerVisibility = EditorLayerVisibility + }; + } + #region BIFF static DragPointData() @@ -79,6 +94,12 @@ static DragPointData() Init(typeof(DragPointData), Attributes); } + public DragPointData(Vertex3D center) : base(null) + { + Center = center; + HasAutoTexture = true; + } + public DragPointData(float x, float y) : base(null) { Center = new Vertex3D(x, y, 0f); @@ -112,5 +133,6 @@ public override void Write(BinaryWriter writer, HashWriter hashWriter) private static readonly Dictionary> Attributes = new Dictionary>(); #endregion + } } diff --git a/VisualPinball.Engine/VPT/Surface/SurfaceData.cs b/VisualPinball.Engine/VPT/Surface/SurfaceData.cs index 0da64cdee..d584ae742 100644 --- a/VisualPinball.Engine/VPT/Surface/SurfaceData.cs +++ b/VisualPinball.Engine/VPT/Surface/SurfaceData.cs @@ -97,13 +97,13 @@ public class SurfaceData : ItemData, ISurfaceData public bool SlingshotAnimation = true; [BiffFloat("ELAS", Pos = 20)] - public float Elasticity; + public float Elasticity = 0.3f; [BiffFloat("ELFO", Pos = 21, WasAddedInVp107 = true)] public float ElasticityFalloff; [BiffFloat("WFCT", Pos = 22)] - public float Friction; + public float Friction = 0.3f; [BiffFloat("WSCT", Pos = 23)] public float Scatter; diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/slingshot.png new file mode 100644 index 000000000..25e7d9081 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/slingshot.png.meta new file mode 100644 index 000000000..90e64feb7 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: 1c560b6e1ccedad4d92bd76928042bd9 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/slingshot.png new file mode 100644 index 000000000..cc0c2abd1 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/slingshot.png.meta new file mode 100644 index 000000000..e2014c784 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: c61b00d7596aa4f44a57a28e43e0d345 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/large_green/slingshot.png new file mode 100644 index 000000000..81eade77a Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_green/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/slingshot.png.meta new file mode 100644 index 000000000..0e9b0a771 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: f62bc79d9eb93ea4393c9e0d21e86a9b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/slingshot.png new file mode 100644 index 000000000..20d774b5a Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/slingshot.png.meta new file mode 100644 index 000000000..b1b48872a --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: a159bc399cb055441820093655e8d7b8 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/slingshot.png new file mode 100644 index 000000000..2d3b10101 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/slingshot.png.meta new file mode 100644 index 000000000..d4af1e8e7 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: 88a354f67b50b854cb032f50a6134b33 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/slingshot.png new file mode 100644 index 000000000..95e68dd12 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/slingshot.png.meta new file mode 100644 index 000000000..e164ab15f --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: be8a139eef666cb4ab6a5ad8b7ba6e5f +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/small_green/slingshot.png new file mode 100644 index 000000000..64208eeaf Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_green/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_green/slingshot.png.meta new file mode 100644 index 000000000..e9562be94 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_green/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: 16dab5e18af7b3845becb889842a3e77 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/slingshot.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/slingshot.png new file mode 100644 index 000000000..5263152f7 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/slingshot.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/slingshot.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/slingshot.png.meta new file mode 100644 index 000000000..6a462b6e2 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/slingshot.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: a01c1f02db832b64bbc574fddbde82a1 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Slingshot.prefab b/VisualPinball.Unity/Assets/Resources/Prefabs/Slingshot.prefab new file mode 100644 index 000000000..6fa531177 --- /dev/null +++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Slingshot.prefab @@ -0,0 +1,136 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6110464775177654975 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1604346372501559456} + - component: {fileID: 8345816335357486725} + - component: {fileID: 4498014268656306279} + - component: {fileID: 1260714541426936682} + m_Layer: 0 + m_Name: Slingshot + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1604346372501559456 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6110464775177654975} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8345816335357486725 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6110464775177654975} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8840a3e9d44a5a443a20087784eae747, type: 3} + m_Name: + m_EditorClassIdentifier: + SlingshotSurface: {fileID: 0} + RubberOn: {fileID: 0} + RubberOff: {fileID: 0} + CoilArm: {fileID: 0} + CoilArmAngle: 0 + AnimationDuration: 70 + AnimationCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.5 + value: 1 + inSlope: 3.535 + outSlope: 0 + tangentMode: 0 + weightedMode: 3 + inWeight: 0.03333336 + outWeight: 0.5416666 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + _isLocked: 0 +--- !u!33 &4498014268656306279 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6110464775177654975} + m_Mesh: {fileID: 0} +--- !u!23 &1260714541426936682 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6110464775177654975} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 257 + m_RendererPriority: 0 + m_Materials: + - {fileID: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Slingshot.prefab.meta b/VisualPinball.Unity/Assets/Resources/Prefabs/Slingshot.prefab.meta new file mode 100644 index 000000000..8827ba025 --- /dev/null +++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Slingshot.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c557a5a88939f4f48ab972272730aa0b +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/Surface.prefab b/VisualPinball.Unity/Assets/Resources/Prefabs/Surface.prefab index 2caa6b145..c38bb28aa 100644 --- a/VisualPinball.Unity/Assets/Resources/Prefabs/Surface.prefab +++ b/VisualPinball.Unity/Assets/Resources/Prefabs/Surface.prefab @@ -44,7 +44,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ed6ad111d870e3b43a828b6086c08607, type: 3} m_Name: m_EditorClassIdentifier: - _meshCreated: 1 --- !u!33 &6530115296024031604 MeshFilter: m_ObjectHideFlags: 0 @@ -100,54 +99,13 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b8b3e83ae5f0a1e41a2dfdfaa251caf7, type: 3} m_Name: m_EditorClassIdentifier: - _data: - StoragePrefix: 0 - StorageIndex: 0 - IsLocked: 0 - EditorLayerName: - EditorLayerVisibility: 0 - Name: - HitEvent: 0 - IsDroppable: 0 - IsFlipbook: 0 - IsBottomSolid: 0 - IsCollidable: 0 - Threshold: 0 - Image: - SideImage: - SideMaterial: - TopMaterial: - PhysicsMaterial: - SlingShotMaterial: - HeightBottom: 0 - HeightTop: 0 - Inner: 0 - DisplayTexture: 0 - SlingshotForce: 0 - SlingshotThreshold: 0 - SlingshotAnimation: 0 - Elasticity: 0 - ElasticityFalloff: 0 - Friction: 0 - Scatter: 0 - IsTopBottomVisible: 0 - OverwritePhysics: 0 - DisableLightingTop: 0 - DisableLightingBelow: 0 - IsSideVisible: 0 - IsReflectionEnabled: 0 - DragPoints: [] - IsTimerEnabled: 0 - TimerInterval: 0 - Points: 0 - IsDisabled: 0 - IsDroppable: 0 - IsFlipbook: 0 - IsBottomSolid: 0 - HeightBottom: 0 + _isLocked: 0 + _editorLayer: 0 + _editorLayerName: + _editorLayerVisibility: 1 HeightTop: 50 - Inner: 1 - DragPoints: [] + HeightBottom: 0 + _dragPoints: [] --- !u!114 &7807490122124131058 MonoBehaviour: m_ObjectHideFlags: 0 @@ -161,18 +119,17 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: PhysicsMaterial: {fileID: 0} - ShowColliderMesh: 0 + ShowColliderMesh: 1 ShowAabbs: 0 HitEvent: 0 Threshold: 2 - SlingShotMaterial: {fileID: 0} - SlingshotForce: 80 + IsBottomSolid: 0 SlingshotThreshold: 0 - SlingshotAnimation: 1 + SlingshotForce: 80 OverwritePhysics: 1 - Elasticity: 0 + Elasticity: 0.3 ElasticityFalloff: 0 - Friction: 0 + Friction: 0.3 Scatter: 0 --- !u!114 &-3256406434667294810 MonoBehaviour: @@ -231,7 +188,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 75abfb7d4ccc1b14bb10b53cb3f60574, type: 3} m_Name: m_EditorClassIdentifier: - _meshCreated: 1 --- !u!33 &6035808683100513115 MeshFilter: m_ObjectHideFlags: 0 diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshot-curve.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshot-curve.png new file mode 100644 index 000000000..87f2da196 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshot-curve.png differ diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshot-inspector.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshot-inspector.png new file mode 100644 index 000000000..c24b57b4d Binary files /dev/null and b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshot-inspector.png differ diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshots.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshots.md new file mode 100644 index 000000000..415025a41 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/slingshots.md @@ -0,0 +1,108 @@ +# Slingshots + +Slingshots are most commonly located just above the flippers. They usually consist of two "blade" switches on the inner side of a triangular rubber. Between the switches there is a coil driven arm that propels the ball away from the slingshot when either switch closes. + +Visual Pinball doesn't have an explicit slingshot element. Instead, it relies on walls with a segment marked as *slingshot*, which generates an additional force being applied to the ball when the segment is hit. However, the rubber animation is up to the table script to implement. + +VPE does provide a slingshot component that implements the rubber animation during runtime. This allows for functional slingshots without any additional code. However this approach isn't ideal and will be replaced with a proper slingshot element in the future. + + +# Setup + +Slingshot Inspector + +### Slingshot Wall + +In order to trigger the animation, the slingshot component needs a reference to the wall which has one of the control points set to *slingshot*. + +### Rubbers + +VPE animates the control points of the rubber by interpolating between a start position to an end position and then back to the start over the duration of the animation. + +To set the start and end positions of the control points, we reference two rubber elements (which must have the same number of control points.) In the inspector, *Rubber Off* indicates the rubber representing the start position, and *Rubber On* the end position. + +> [!NOTE] +> In VPX, tables often come with three rubbers elements that are toggled in order to fake an animation. When using VPE's slingshot component, you can delete the rubber at mid position, since only the start and end rubbers are used. The interpolation is calculated in real time depending on the speed of the slingshot. + +### Coil Arm + +On physical machines, the rubber is moved by an arm attached to the coil. VPE can simulate the movement of that arm by rotating a primitive across the X-axis. In the *Coil Arm* field, a reference to the primive can be set, and the total angle of rotation under *Arm Angle*. + +Slingshot Animation Curve + +### Animation + +The animation has two parameters: The duration in milliseconds, and the curve. The curve represents the position in function of time and covers both directions of travel. This allows non-linear movement of the rubber. + +### Test + +With the test slider you can easily preview how the rubber and arm are animated. + +# Howto + +## Set Up a Slingshot from an Imported Table + +This howto uses the blank table, but other tables should be similar. Usually, slingshots consist of three rubbers for the animation, plus a wall for the physics. We need the following elements: + +1. The wall with a segment set to *slingshot* +2. The rubber at idle position +3. The rubber at *activated* position +4. Optionally, the coil arm that pushes the rubber + +Note that both rubbers *must* have the same number of drag points. This is because during the animation, the rubber is linearly interpolated between the two drag points positions, which isn't possible if the number differs. When converting a table from Visual Pinball, that means that you may need to add additional drag points to the rubber at idle position. + +### 1. Identify and clean up the elements + +Zoom in to the slingshot you want to set up. You'll probably want to temporily hide the plastic that covers up the rubbers and the wall. Find the rubber at idle position and at activated position. Delete the rubber in-between, we don't need that one. + +Note the names of those rubbers. Here it's `LSling` and `LSling1`. Also look for the the coil arm, which is called `Sling2`, as well as wall that acts as the physical slingshot, here `LeftSlingShot`. + +> [!Video https://www.youtube.com/embed/rL2uZyYXBHk] + + +### 2. Add additional control points if necessary + +Now, since `LSling1` is bent and thus contains three additional control points, we'll add the same points to `LSling`. + +We can now also can hide the meshes of the rubbers. + +> [!Video https://www.youtube.com/embed/Yie1Pby8iGs] + + +### 3. Add the slingshot component + +In the Toolbox, click on the *Slingshot* icon, which will create a new element in the scene. Rename it and link the elements we've identified in step 1: + +- *Slingshot Wall* links to `LeftSlingShot` +- *Rubber Off* links to `LSling` +- *Rubber On* links to `LSling1` + +The slingshot component is now able to create the mesh. The animation can be tested with the *Test* slider. + +> [!Video https://www.youtube.com/embed/421gesRScYo] + + +### 4. Setup the coil arm animation + +Since the rubber is pushed inside by the arm, the arm should be animated along with the rubber. This can be a bit fiddly, since the arm should be as close to the rubber as possible without clipping through it. + +Move the *Test* slider all to the right, and play with the *X-Rotation* of the coil arm. Once you're happy, copy the angle to the clipboard. Then, select the slingshot. In the inspector, set the following fields: + +- *Coil Arm* links to the primitive, in our case `Sling2`. +- *Arm Angle* is the angle when the coil is enabled. Paste the angle you've copied before. + +Now, when moving the *Test* slider, the arm should animate along with the rubbers. + +> [!Video https://www.youtube.com/embed/Q1jeJHRIziM] + + +### 5. Wrap-up and test + +Before final testing, there are two things left to do: + +1. Select the coil arm and disable the collider +2. Enable the plastic we hid in step 1. + +Then hit play and have a game! + +> [!Video https://www.youtube.com/embed/dNS4YPdRXTc] diff --git a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml index 0d948af46..f63844590 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml +++ b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml @@ -45,3 +45,5 @@ href: manual/mechanisms/troughs.md - name: Flippers href: manual/mechanisms/flippers.md + - name: Slingshots + href: manual/mechanisms/slingshots.md diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs index a36e084e5..845155435 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs @@ -20,6 +20,7 @@ using UnityEditor; using UnityEngine; using VisualPinball.Engine.Math; +using Color = UnityEngine.Color; using Object = UnityEngine.Object; namespace VisualPinball.Unity.Editor @@ -31,17 +32,17 @@ public class DragPointsHandler /// /// Component /// - public IMainRenderableComponent MainComponent { get; private set; } + public IMainRenderableComponent MainComponent { get; } /// /// Component item as IDragPointsEditable /// - public IDragPointsInspector DragPointInspector { get; private set; } + public IDragPointsInspector DragPointInspector { get; } /// /// Transform component of the game object /// - public Transform Transform { get; private set; } + public Transform Transform { get; } /// /// Control points storing & rendering @@ -55,7 +56,7 @@ public class DragPointsHandler /// /// Will handle all the rendering part and update some handler's variables about curve traveller /// - private readonly DragPointsSceneViewHandler _sceneViewHandler = null; + private readonly DragPointsSceneViewHandler _sceneViewHandler; /// /// Drag points selection @@ -72,7 +73,7 @@ public class DragPointsHandler /// public int CurveTravellerControlId { get; private set; } public Vector3 CurveTravellerPosition { get; set; } = Vector3.zero; - public bool CurveTravellerVisible { get; set; } = false; + public bool CurveTravellerVisible { get; set; } public int CurveTravellerControlPointIdx { get; set; } = -1; /// @@ -95,8 +96,8 @@ public DragPointsHandler(IMainRenderableComponent mainComponent, IDragPointsInsp _sceneViewHandler = new DragPointsSceneViewHandler(this){ CurveWidth = 10.0f, - CurveColor = UnityEngine.Color.blue, - CurveSlingShotColor = UnityEngine.Color.red, + CurveColor = Color.blue, + CurveSlingShotColor = Color.red, ControlPointsSizeRatio = 1.0f, CurveTravellerSizeRatio = 0.75f }; @@ -258,6 +259,10 @@ private void RebuildControlPoints() ControlPoints.Add(cp); } CurveTravellerControlId = GUIUtility.GetControlID(FocusType.Passive); + + // persist prefab changes + EditorUtility.SetDirty(MainComponent.gameObject); + PrefabUtility.RecordPrefabInstancePropertyModifications(MainComponent as Object); } /// diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs index 419c29695..146fb2b48 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxSceneConverter.cs @@ -556,18 +556,20 @@ private void CreateRootHierarchy(string tableName = null) _playfieldComponent.SetData(_sourceTable.Data); } - private GameObject GetGroupParent(IItem item) + private GameObject GetGroupParent(IItem item) => GetGroupParent(item.ItemGroupName); + + public GameObject GetGroupParent(string name) { // create group parent if not created (if null, attach it to the table directly). - if (!string.IsNullOrEmpty(item.ItemGroupName)) { - if (!_groupParents.ContainsKey(item.ItemGroupName)) { - var parent = new GameObject(item.ItemGroupName); + if (!string.IsNullOrEmpty(name)) { + if (!_groupParents.ContainsKey(name)) { + var parent = new GameObject(name); parent.transform.SetParent(_playfieldGo.transform, false); - _groupParents[item.ItemGroupName] = parent; + _groupParents[name] = parent; } } - var groupParent = !string.IsNullOrEmpty(item.ItemGroupName) - ? _groupParents[item.ItemGroupName] + var groupParent = !string.IsNullOrEmpty(name) + ? _groupParents[name] : _playfieldGo; return groupParent; diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Toolbox/ToolboxEditor.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Toolbox/ToolboxEditor.cs index 0e1db2409..93e96edb1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Toolbox/ToolboxEditor.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Toolbox/ToolboxEditor.cs @@ -167,6 +167,10 @@ private void OnGUI() CreateItem(Trough.GetDefault, "New Trough"); } + if (CreateButton("Slingshot", Icons.Slingshot(color: iconColor), iconSize, buttonStyle)) { + CreatePrefab("Slingshots", "Prefabs/Slingshot"); + } + GUILayout.EndHorizontal(); } @@ -196,5 +200,20 @@ private GameObject CreateRenderable(IItem item) TableComponent.TableContainer.Refresh(); return converter.InstantiateAndPersistPrefab(item).GameObject; } + + private void CreatePrefab(string groupName, string path) + { + var converter = new VpxSceneConverter(TableComponent); + TableComponent.TableContainer.Refresh(); + + var parentGo = converter.GetGroupParent(groupName); + + var prefab = Resources.Load(path); + var go = PrefabUtility.InstantiatePrefab(prefab) as GameObject; + if (go) { + go.transform.SetParent(parentGo.transform, false); + } + Selection.activeGameObject = go; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index 980fd8482..a23abd9e2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -54,6 +54,7 @@ public IconVariant(string name, IconSize size, IconColor color) private const string RubberName = "rubber"; private const string SpinnerName = "spinner"; private const string SurfaceName = "surface"; + private const string SlingshotName = "slingshot"; private const string TableName = "table"; private const string TriggerName = "trigger"; private const string TroughName = "trough"; @@ -63,7 +64,7 @@ public IconVariant(string name, IconSize size, IconColor color) private static readonly string[] Names = { BumperName, BoltName, CoilName, DropTargetName, FlipperName, HitTargetName, GateName, KeyName, KickerName, LightName, PlayfieldName, PlungerName, PlugName, PrimitiveName, RampName, RubberName, SpinnerName, SurfaceName, TableName, TriggerName, TroughName, - SwitchNcName, SwitchNoName + SlingshotName, SwitchNcName, SwitchNoName }; private readonly Dictionary _icons = new Dictionary(); @@ -76,7 +77,7 @@ public IconVariant(string name, IconSize size, IconColor color) private static readonly int MonoBehaviourClassID = 114; private static Icons _instance; - private static Icons Instance => _instance ?? (_instance = new Icons()); + private static Icons Instance => _instance ??= new Icons(); private Icons() { @@ -112,6 +113,7 @@ private Icons() public static Texture2D Table(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(TableName, size, color); public static Texture2D Trigger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(TriggerName, size, color); public static Texture2D Trough(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(TroughName, size, color); + public static Texture2D Slingshot(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SlingshotName, size, color); public static Texture2D Switch(bool isClosed, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(isClosed ? SwitchNcName : SwitchNoName, size, color); public static Texture2D Coil(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(CoilName, size, color); public static Texture2D Key(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KeyName, size, color); @@ -134,6 +136,7 @@ public static Texture2D ByComponent(T mb, IconSize size = IconSize.Large, Ico case RampComponent _: return Ramp(size, color); case RubberComponent _: return Rubber(size, color); case SpinnerComponent _: return Spinner(size, color); + case SlingshotComponent _: return Slingshot(size, color); case SurfaceComponent _: return Surface(size, color); case TriggerComponent _: return Trigger(size, color); case TroughComponent _: return Trough(size, color); @@ -186,6 +189,7 @@ public static void DisableGizmoIcons() DisableGizmo(); DisableGizmo(); DisableGizmo(); + DisableGizmo(); DisableGizmo(); DisableGizmo(); DisableGizmo(); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs index 9418e99b6..ff7debd3e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ItemInspector.cs @@ -37,6 +37,8 @@ public abstract class ItemInspector : UnityEditor.Editor private bool _transformsDirty; private bool _visibilityDirty; + private SerializedProperty _isLockedProperty; + #region Unity Events protected virtual void OnEnable() @@ -45,6 +47,8 @@ protected virtual void OnEnable() TableComponent = (target as MonoBehaviour)?.gameObject.GetComponentInParent(); PlayfieldComponent = (target as MonoBehaviour)?.gameObject.GetComponentInParent(); + + _isLockedProperty = serializedObject.FindProperty("_isLocked"); } protected virtual void OnDisable() @@ -232,16 +236,13 @@ protected void MeshDropdownProperty(string label, SerializedProperty meshProp, s protected void OnPreInspectorGUI() { - if (!(target is IMainRenderableComponent item)) { + if (!(target is IMainRenderableComponent)) { return; } EditorGUI.BeginChangeCheck(); - var newLock = EditorGUILayout.Toggle("IsLocked", item.IsLocked); - if (EditorGUI.EndChangeCheck()) - { - FinishEdit("IsLocked"); - item.IsLocked = newLock; + PropertyField(_isLockedProperty, "Locked"); + if (EditorGUI.EndChangeCheck()) { SceneView.RepaintAll(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Surface/SlingshotInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Surface/SlingshotInspector.cs new file mode 100644 index 000000000..9ee20146a --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Surface/SlingshotInspector.cs @@ -0,0 +1,88 @@ +// 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 UnityEditor; +using UnityEngine; + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(SlingshotComponent))] + public class SlingshotInspector : ItemInspector + { + private SlingshotComponent _slingshot; + + private SerializedProperty _surfaceProperty; + private SerializedProperty _rubberOffProperty; + private SerializedProperty _rubberOnProperty; + private SerializedProperty _animationDurationProperty; + private SerializedProperty _animationCurveProperty; + private SerializedProperty _coilArmProperty; + private SerializedProperty _coilArmAngleProperty; + + protected override MonoBehaviour UndoTarget => target as MonoBehaviour; + + protected override void OnEnable() + { + base.OnEnable(); + + _slingshot = target as SlingshotComponent; + + _surfaceProperty = serializedObject.FindProperty(nameof(SlingshotComponent.SlingshotSurface)); + _rubberOffProperty = serializedObject.FindProperty(nameof(SlingshotComponent.RubberOff)); + _rubberOnProperty = serializedObject.FindProperty(nameof(SlingshotComponent.RubberOn)); + _coilArmProperty = serializedObject.FindProperty(nameof(SlingshotComponent.CoilArm)); + _coilArmAngleProperty = serializedObject.FindProperty(nameof(SlingshotComponent.CoilArmAngle)); + _animationDurationProperty = serializedObject.FindProperty(nameof(SlingshotComponent.AnimationDuration)); + _animationCurveProperty = serializedObject.FindProperty(nameof(SlingshotComponent.AnimationCurve)); + } + + public override void OnInspectorGUI() + { + BeginEditing(); + + OnPreInspectorGUI(); + + PropertyField(_surfaceProperty, "Slingshot Wall"); + PropertyField(_rubberOffProperty, "Rubber Off", true); + PropertyField(_rubberOnProperty, "Rubber On", true); + + if (_slingshot.RubberOn && _slingshot.RubberOff && + _slingshot.RubberOn.DragPoints.Length != _slingshot.RubberOff.DragPoints.Length) { + EditorGUILayout.HelpBox($"In order to animate the rubber, the number of drag points of both rubbers must be equal. Here we have {_slingshot.RubberOn.DragPoints.Length} (on) and {_slingshot.RubberOff.DragPoints.Length} (off).", MessageType.Error); + } + + EditorGUILayout.Space(10f); + PropertyField(_coilArmProperty, "Coil Arm"); + PropertyField(_coilArmAngleProperty, "Arm Angle"); + + EditorGUILayout.Space(10f); + PropertyField(_animationDurationProperty, "Animation Duration"); + PropertyField(_animationCurveProperty, "Animation Curve"); + + EditorGUILayout.Space(10f); + EditorGUI.BeginChangeCheck(); + var pos = EditorGUILayout.Slider("Test", _slingshot.Position, 0f, 1f); + if (EditorGUI.EndChangeCheck()) { + _slingshot.Position = pos; + _slingshot.RebuildMeshes(); + } + + base.OnInspectorGUI(); + + EndEditing(); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Surface/SlingshotInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Surface/SlingshotInspector.cs.meta new file mode 100644 index 000000000..2ccd9e843 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Surface/SlingshotInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 083d1d50400ed5d428067a9dfb9adc3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugBallCreator.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugBallCreator.cs index ca999702e..dfe2759d8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/DebugBallCreator.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/DebugBallCreator.cs @@ -34,8 +34,8 @@ public DebugBallCreator(float x, float y, float playfieldHeight) _x = x; _y = y; _z = playfieldHeight; - _kickAngle = math.radians(180f);; - _kickForce = 20; + _kickAngle = 0; + _kickForce = 0; } public DebugBallCreator(float x, float y, float playfieldHeight, float kickAngle, float kickForce) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs index 0dae09132..9993e46b5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs @@ -229,7 +229,7 @@ public void Switch(string id, bool isClosed) case SwCreateBall: { if (isClosed) { - _ballManager.CreateBall(new DebugBallCreator(_playfieldComponent.Width / 2f, _playfieldComponent.Height / 2f, _playfieldComponent.TableHeight)); + _ballManager.CreateBall(new DebugBallCreator(630, _playfieldComponent.Height / 2f, _playfieldComponent.TableHeight)); } break; } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/LineSlingshotCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/LineSlingshotCollider.cs index bcc7ea8b7..ccfe19392 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/LineSlingshotCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/LineSlingshotCollider.cs @@ -30,10 +30,12 @@ internal struct LineSlingshotCollider : ICollider public readonly float2 V1; public readonly float2 V2; + public float2 Normal; - private float _length; public readonly float ZLow; public readonly float ZHigh; + private float _length; + private readonly float _force; public ColliderBounds Bounds => new ColliderBounds(_header.Entity, _header.Id, new Aabb( diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SlingshotComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SlingshotComponent.cs new file mode 100644 index 000000000..7670e4d91 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SlingshotComponent.cs @@ -0,0 +1,244 @@ +// 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 . + +// ReSharper disable InconsistentNaming + +using System; +using System.Collections; +using System.Collections.Generic; +using Unity.Entities; +using Unity.Mathematics; +using UnityEngine; +using VisualPinball.Engine.Math; +using VisualPinball.Engine.VPT.Rubber; + +namespace VisualPinball.Unity +{ + [AddComponentMenu("Visual Pinball/Game Item/Slingshot")] + public class SlingshotComponent : MonoBehaviour, IMeshComponent, IMainRenderableComponent, IRubberData + { + [Tooltip("Reference to the wall that acts as slingshot.")] + public SurfaceColliderComponent SlingshotSurface; + + [Tooltip("Reference to the rubber at \"enabled\" position (coil on).")] + public RubberComponent RubberOn; + + [Tooltip("Reference to the rubber at \"disabled\" position (coil off).")] + public RubberComponent RubberOff; + + [Tooltip("Reference to the arm attached to the coil. Rotates around X.")] + public PrimitiveComponent CoilArm; + + [Range(-180f, 180f)] + [Tooltip("Angle of the coil arm when on.")] + public float CoilArmAngle; + + [Min(0f)] + [Tooltip("Total duration of the animation in milliseconds.")] + public float AnimationDuration = 70f; + + [Tooltip("Animation curve. Starts at 0 and ends at 0.")] + public AnimationCurve AnimationCurve = new AnimationCurve( + new Keyframe(0, 0), + new Keyframe(0.5f, 1, 3.535f, 0f, 0.03333336f, 0.5416666f), + new Keyframe(1, 0) + ); + + [NonSerialized] public float Position; + [SerializeField] private bool _isLocked; + [NonSerialized] private readonly Dictionary _meshes = new Dictionary(); + [NonSerialized] private RubberMeshGenerator _meshGenerator; + private RubberMeshGenerator MeshGenerator => _meshGenerator ??= new RubberMeshGenerator(this); + + private const int MaxNumMeshCaches = 15; + + #region Runtime + + private void Start() + { + var player = GetComponentInParent(); + if (!player || player.TableApi == null || !SlingshotSurface) { + return; + } + var slingshotSurfaceApi = player.TableApi.Surface(SlingshotSurface.MainComponent); + slingshotSurfaceApi.Slingshot += OnSlingshot; + } + + private void OnDestroy() + { + var player = GetComponentInParent(); + if (player && player.TableApi != null && SlingshotSurface) { + var slingshotSurfaceApi = player.TableApi.Surface(SlingshotSurface.MainComponent); + slingshotSurfaceApi.Slingshot -= OnSlingshot; + } + + _meshes.Clear(); + } + + private void OnSlingshot(object sender, EventArgs e) => TriggerAnimation(); + + private void TriggerAnimation() + { + StopAllCoroutines(); + StartCoroutine(nameof(Animate)); + } + + private IEnumerator Animate() + { + var duration = AnimationDuration / 1000; + var journey = 0f; + while (journey <= duration) { + + journey += Time.deltaTime; + var curvePercent = AnimationCurve.Evaluate(journey / duration); + Position = math.clamp(curvePercent, 0f, 1f); + + RebuildMeshes(); + + yield return null; + } + } + + #endregion + + #region IRubberData + + public DragPointData[] DragPoints => DragPointsAt(Position); + public int Thickness => RubberOff.GetComponent()?.Thickness ?? 8; + public float Height => RubberOff.GetComponent()?.Height ?? 25f; + public float RotX => RubberOff.GetComponent()?.RotX ?? 0; + public float RotY => RubberOff.GetComponent()?.RotY ?? 0; + public float RotZ => RubberOff.GetComponent()?.RotZ ?? 0; + + #endregion + + #region IMeshComponent + + public IMainRenderableComponent MainRenderableComponent => this; + + public void RebuildMeshes() + { + var mf = GetComponent(); + var mr = GetComponent(); + if (!mf || !mr) { + Debug.LogWarning("Mesh filter or renderer not found."); + return; + } + + // mesh + var mesh = GetMesh(); + if (mesh != null) { + mf.sharedMesh = mesh; + } + + // material + if (RubberOff && !mr.sharedMaterial) { + var rubberMr = RubberOff.GetComponent(); + if (rubberMr) { + mr.sharedMaterial = rubberMr.sharedMaterial; + } + } + + if (CoilArm) { + CoilArm.Rotation.x = CoilArmAngle * Position; + CoilArm.UpdateTransforms(); + } + } + + private Mesh GetMesh() + { + var pos = (int)(Position * MaxNumMeshCaches); + if (Application.isPlaying && _meshes.ContainsKey(pos)) { + return _meshes[pos]; + } + + if (!RubberOff || DragPoints.Length < 3) { + return null; + } + + var pf = GetComponentInParent(); + var r0 = RubberOff.GetComponent(); + if (!r0 || !pf) { + return null; + } + + Debug.Log($"Generating new mesh at {pos}"); + + var mesh = MeshGenerator + .GetTransformedMesh(pf.PlayfieldHeight, r0.Height, pf.PlayfieldDetailLevel) + .ToUnityMesh(); + + _meshes[pos] = mesh; + + return mesh; + } + + #endregion + + #region IMainRenderableComponent + + public bool IsLocked { get => _isLocked; set => _isLocked = value; } + public bool CanBeTransformed => false; + public string ItemName => "Slingshot"; + public Entity Entity { get; set; } + + public void UpdateTransforms() { } + public void UpdateVisibility() { } + + public ItemDataTransformType EditorPositionType => ItemDataTransformType.None; + public Vector3 GetEditorPosition() => Vector3.zero; + public void SetEditorPosition(Vector3 pos) { } + + public ItemDataTransformType EditorRotationType => ItemDataTransformType.None; + public Vector3 GetEditorRotation() => Vector3.zero; + public void SetEditorRotation(Vector3 pos) { } + + public ItemDataTransformType EditorScaleType => ItemDataTransformType.None; + public Vector3 GetEditorScale() => Vector3.one; + public void SetEditorScale(Vector3 pos) { } + + #endregion + + private DragPointData[] DragPointsAt(float pos) + { + if (RubberOn == null || RubberOff == null) { + Debug.LogWarning("Rubber references not set."); + return Array.Empty(); + } + var r0 = RubberOff.GetComponent(); + var r1 = RubberOn.GetComponent(); + if (r0 == null || r1 == null || r0.DragPoints == null || r1.DragPoints == null) { + Debug.LogWarning("Rubber references not found or drag points not set."); + return Array.Empty(); + } + + var dp0 = r0.DragPoints; + var dp1 = r1.DragPoints; + + if (dp0.Length != dp1.Length) { + Debug.LogWarning($"Drag point number varies ({dp0.Length} vs {dp1.Length}.)."); + return Array.Empty(); + } + + var dp = new DragPointData[dp0.Length]; + for (var i = 0; i < dp.Length; i++) { + dp[i] = dp0[i].Lerp(dp1[i], pos); + } + + return dp; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SlingshotComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SlingshotComponent.cs.meta new file mode 100644 index 000000000..6ec07deb3 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SlingshotComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8840a3e9d44a5a443a20087784eae747 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: a01c1f02db832b64bbc574fddbde82a1, type: 3} + userData: + assetBundleName: + assetBundleVariant: