From 78df3c7f4d04a53321aa5a8c4028db2dccc57c28 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 27 Oct 2021 00:26:54 +0200 Subject: [PATCH 01/29] fix: Error when ball roller pressed when no ball. --- .../VisualPinball.Unity.Editor/Utils/Icons.cs | 30 +++++++++---------- .../Game/BallRollerComponent.cs | 7 +++-- .../VPT/Teleporter/TeleporterApi.cs | 18 ++++++++--- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index 505b7a28f..4900c4104 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -37,11 +37,11 @@ public IconVariant(string name, IconSize size, IconColor color) } private const string BallRollerName = "ball_roller"; - private const string BumperName = "bumper"; private const string BoltName = "bolt"; + private const string BumperName = "bumper"; private const string CoilName = "coil"; - private const string DropTargetName = "drop_target"; private const string DropTargetBankName = "drop_target_bank"; + private const string DropTargetName = "drop_target"; private const string FlasherName = "light_flasher"; private const string FlipperName = "flipper"; private const string GateName = "gate"; @@ -51,25 +51,25 @@ public IconVariant(string name, IconSize size, IconColor color) private const string LightGroupName = "light_group"; private const string LightName = "light"; private const string PlayfieldName = "playfield"; - private const string PlungerName = "plunger"; private const string PlugName = "plug"; + private const string PlungerName = "plunger"; private const string PrimitiveName = "primitive"; private const string RampName = "ramp"; private const string RubberName = "rubber"; + private const string SlingshotName = "slingshot"; private const string SpinnerName = "spinner"; private const string SurfaceName = "surface"; - private const string SlingshotName = "slingshot"; + private const string SwitchNcName = "switch_nc"; + private const string SwitchNoName = "switch_no"; private const string TableName = "table"; private const string TeleporterName = "teleporter"; private const string TriggerName = "trigger"; private const string TroughName = "trough"; - private const string SwitchNcName = "switch_nc"; - private const string SwitchNoName = "switch_no"; private static readonly string[] Names = { - BallRollerName, BumperName, BoltName, CoilName, DropTargetName, DropTargetBankName, FlasherName, FlipperName, HitTargetName, GateName, KeyName, - KickerName, LightGroupName, LightName, PlayfieldName, PlungerName, PlugName, PrimitiveName, RampName, RubberName, SpinnerName, SurfaceName, - TableName, TeleporterName, TriggerName, TroughName, SlingshotName, SwitchNcName, SwitchNoName + BallRollerName, BoltName, BumperName, CoilName, DropTargetBankName, DropTargetName, FlasherName, FlipperName, GateName, HitTargetName, KeyName, + KickerName, LightGroupName, LightName, PlayfieldName, PlugName, PlungerName, PrimitiveName, RampName, RubberName, SlingshotName, SpinnerName, + SurfaceName, SwitchNcName, SwitchNoName, TableName, TeleporterName, TriggerName, TroughName, }; private readonly Dictionary _icons = new Dictionary(); @@ -101,33 +101,33 @@ private Icons() } public static Texture2D BallRoller(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(BallRollerName, size, color); + public static Texture2D Bolt(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(BoltName, size, color); public static Texture2D Bumper(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(BumperName, size, color); + public static Texture2D Coil(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(CoilName, size, color); public static Texture2D DropTarget(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(DropTargetName, size, color); public static Texture2D DropTargetBank(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(DropTargetBankName, size, color); public static Texture2D Flasher(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(FlasherName, size, color); public static Texture2D Flipper(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(FlipperName, size, color); public static Texture2D Gate(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(GateName, size, color); public static Texture2D HitTarget(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(HitTargetName, size, color); + public static Texture2D Key(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KeyName, size, color); public static Texture2D Kicker(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KickerName, size, color); public static Texture2D Light(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightName, size, color); public static Texture2D LightGroup(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightGroupName, size, color); public static Texture2D Playfield(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlayfieldName, size, color); - public static Texture2D Plunger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlungerName, size, color); public static Texture2D Plug(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlugName, size, color); + public static Texture2D Plunger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlungerName, size, color); public static Texture2D Primitive(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PrimitiveName, size, color); public static Texture2D Ramp(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RampName, size, color); public static Texture2D Rubber(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RubberName, size, color); + public static Texture2D Slingshot(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SlingshotName, size, color); public static Texture2D Spinner(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SpinnerName, size, color); public static Texture2D Surface(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SurfaceName, 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 Table(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(TableName, size, color); public static Texture2D Teleporter(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(TeleporterName, 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); - public static Texture2D Bolt(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(BoltName, size, color); public static Texture2D ByComponent(T mb, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) where T : class diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs index ae7a1d475..78edc4bf6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/BallRollerComponent.cs @@ -31,7 +31,7 @@ public class BallRollerComponent : MonoBehaviour private Plane _playfieldPlane; private EntityManager _entityManager; - private Entity _ballEntity; + private Entity _ballEntity = Entity.Null; private EntityQuery _ballEntityQuery; private void Awake() @@ -84,17 +84,18 @@ private void Update() } } - } else if (Mouse.current.middleButton.isPressed) { + } else if (Mouse.current.middleButton.isPressed && _ballEntity != Entity.Null) { if (GetCursorPositionOnPlayfield(out var mousePosition)) { var ballData = _entityManager.GetComponentData(_ballEntity); UpdateBall(ref ballData, mousePosition); } } - if (Mouse.current.middleButton.wasReleasedThisFrame) { + if (Mouse.current.middleButton.wasReleasedThisFrame && _ballEntity != Entity.Null) { var ballData = _entityManager.GetComponentData(_ballEntity); ballData.ManualControl = false; _entityManager.SetComponentData(_ballEntity, ballData); + _ballEntity = Entity.Null; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterApi.cs index c56dd538a..65b5f86a1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterApi.cs @@ -66,11 +66,16 @@ private void OnTeleport() var ballInSrc = _fromKicker.HasBall(); var ballInDst = _toKicker.HasBall(); - if (!ballInSrc || ballInDst) { + if (!ballInSrc && !ballInDst || ballInSrc && ballInDst) { // duh, do nothing. return; } + if (ballInDst) { + Eject(); + return; + } + var ballData = _fromKicker.GetBallData(); _fromKicker.DestroyBall(); _toKicker.CreateSizedBallWithMass(ballData.Radius, ballData.Mass); @@ -80,15 +85,20 @@ private void OnTeleport() return; } + Eject(); + } + + private void Eject() + { if (_component.EjectDelay > 0) { - _simulationSystemGroup.ScheduleAction((int)math.round(_component.EjectDelay * 1000f), Eject); + _simulationSystemGroup.ScheduleAction((int)math.round(_component.EjectDelay * 1000f), TriggerEjectCoil); } else { - Eject(); + TriggerEjectCoil(); } } - private void Eject() + private void TriggerEjectCoil() { if (!string.IsNullOrEmpty(_component.ToKickerItem)) { var kickerCoil = (_toKicker as IApiCoilDevice).Coil(_component.ToKickerItem); From 73e20008b9927ac1ed6ae698d2c0d800160747f4 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 27 Oct 2021 00:40:56 +0200 Subject: [PATCH 02/29] teleporter: Disable destination kicker on start. --- .../VPT/Teleporter/TeleporterComponent.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterComponent.cs index 706681aa5..1fc2648c8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Teleporter/TeleporterComponent.cs @@ -74,12 +74,16 @@ public class TeleporterComponent : MonoBehaviour, ICoilDeviceComponent private void Awake() { var player = GetComponentInParent(); - if (player == null) - { - Logger.Error($"Cannot find player for cannon {name}."); + if (player == null) { + Logger.Error($"Cannot find player for teleporter {name}."); return; } + // disable destination kicker collider + if (ToKicker != null) { + ToKicker.GetComponent().enabled = false; + } + player.RegisterTeleporter(this); } From 5bc1507676ce4b38227e355192a0cf8b9b02c9aa Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 27 Oct 2021 00:49:01 +0200 Subject: [PATCH 03/29] ramp: Make collidable. --- .../VisualPinball.Unity/VPT/Ramp/RampApi.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs index 396b852e5..7733890bb 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs @@ -21,13 +21,18 @@ namespace VisualPinball.Unity { - public class RampApi : CollidableApi, IApi + public class RampApi : CollidableApi, IApi, IApiHittable { /// /// Event emitted when the table is started. /// public event EventHandler Init; + /// + /// Event emitted when the ball hits the ramp. + /// + public event EventHandler Hit; + internal RampApi(GameObject go, Entity entity, Entity parentEntity, Player player) : base(go, entity, parentEntity, player) { } @@ -40,6 +45,11 @@ void IApi.OnInit(BallManager ballManager) Init?.Invoke(this, EventArgs.Empty); } + public void OnHit(Entity ballEntity, bool isUnHit = false) + { + Hit?.Invoke(this, new HitEventArgs(ballEntity)); + } + void IApi.OnDestroy() { } From c69cfd16d8cc5c9b773634b6d979798813976ccf Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 27 Oct 2021 23:31:20 +0200 Subject: [PATCH 04/29] mechs: Rename and generalize cannon. --- .../Materials/Dot Matrix Display (SRP).mat | 34 ++-- .../Materials/Segment Display (SRP).mat | 49 +++-- .../Import/VpxPrefab.cs | 2 +- .../VPT/MainInspector.cs | 2 +- .../Matcher/TablePatcher.cs | 17 ++ .../Patcher/Tables/Terminator2.cs | 23 ++- .../VisualPinball.Unity/Game/DeviceSwitch.cs | 2 + .../Game/Engine/DefaultGamelogicEngine.cs | 29 ++- .../VisualPinball.Unity/Game/Player.cs | 4 +- .../VisualPinball.Unity/Game/SwitchHandler.cs | 6 +- .../VPT/Kicker/KickerApi.cs | 8 + .../VisualPinball.Unity/VPT/MainComponent.cs | 3 + .../VPT/MainRenderableComponent.cs | 7 +- .../VisualPinball.Unity/VPT/Mech/CannonApi.cs | 178 ------------------ .../VPT/Mech/StepRotatorApi.cs | 175 +++++++++++++++++ ...nnonApi.cs.meta => StepRotatorApi.cs.meta} | 2 +- ...onComponent.cs => StepRotatorComponent.cs} | 71 +++++-- ...t.cs.meta => StepRotatorComponent.cs.meta} | 2 +- .../VisualPinball.Unity/VPT/SubComponent.cs | 3 + .../VisualPinball.Unity/VPT/Table/TableApi.cs | 22 +-- 20 files changed, 391 insertions(+), 248 deletions(-) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{CannonApi.cs.meta => StepRotatorApi.cs.meta} (83%) rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{CannonComponent.cs => StepRotatorComponent.cs} (60%) rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{CannonComponent.cs.meta => StepRotatorComponent.cs.meta} (83%) diff --git a/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat b/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat index 46195e0a4..cf9b91a22 100644 --- a/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat +++ b/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat @@ -1,18 +1,21 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &-7048375842616513859 -MonoBehaviour: - m_ObjectHideFlags: 11 - 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: da692e001514ec24dbc4cca1949ff7e8, type: 3} - m_Name: - m_EditorClassIdentifier: - version: 11 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: da692e001514ec24dbc4cca1949ff7e8, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 12 + hdPluginSubTargetMaterialVersions: + m_Keys: [] + m_Values: --- !u!21 &2100000 Material: serializedVersion: 6 @@ -146,6 +149,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - Vector1_3ebae42385484fdab87c4c0493f74e5a: 1.49 - Vector1_48868e3f644c4f8d9dc2d14907d07c46: 7 @@ -170,6 +174,7 @@ Material: - _Anisotropy: 0 - _BlendMode: 0 - _CoatMask: 0 + - _ConservativeDepthOffsetEnable: 0 - _CullMode: 2 - _CullModeForward: 2 - _Cutoff: 0.5 @@ -183,6 +188,7 @@ Material: - _DisplacementLockTilingScale: 1 - _DisplacementMode: 0 - _DoubleSidedEnable: 0 + - _DoubleSidedGIMode: 0 - _DoubleSidedNormalMode: 1 - _DstBlend: 0 - _EmissiveColorMode: 1 @@ -265,9 +271,11 @@ Material: - _ZTestGBuffer: 4 - _ZTestTransparent: 4 - _ZWrite: 1 - - __Padding: 0.2 + - __Emission: 1 - __NumChars: 2 - __NumSegments: 15 + - __Padding: 0.2 + - __Roundness: 0.35 - __SegmentType: 0 - __SegmentWeight: 0.05 - __SkewAngle: 0 diff --git a/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat b/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat index 0e4f80a80..86cec9d19 100644 --- a/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat +++ b/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat @@ -1,18 +1,21 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &-7048375842616513859 -MonoBehaviour: - m_ObjectHideFlags: 11 - 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: da692e001514ec24dbc4cca1949ff7e8, type: 3} - m_Name: - m_EditorClassIdentifier: - version: 11 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: da692e001514ec24dbc4cca1949ff7e8, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 12 + hdPluginSubTargetMaterialVersions: + m_Keys: [] + m_Values: --- !u!21 &2100000 Material: serializedVersion: 6 @@ -102,6 +105,10 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + - _SegmentDisplayCustomFunction_a94cb738cee9426e8bf076adaa3b6811_SegmentData_2: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} - _SpecularColorMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} @@ -126,6 +133,10 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + - __Data: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} - unity_Lightmaps: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} @@ -138,6 +149,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - Vector1_48868e3f644c4f8d9dc2d14907d07c46: 7 - Vector1_78a66e5d29d14bd2954daf3fb60bb4aa: 0 @@ -161,6 +173,7 @@ Material: - _Anisotropy: 0 - _BlendMode: 0 - _CoatMask: 0 + - _ConservativeDepthOffsetEnable: 0 - _CullMode: 2 - _CullModeForward: 2 - _Cutoff: 0.5 @@ -174,6 +187,7 @@ Material: - _DisplacementLockTilingScale: 1 - _DisplacementMode: 0 - _DoubleSidedEnable: 0 + - _DoubleSidedGIMode: 0 - _DoubleSidedNormalMode: 1 - _DstBlend: 0 - _EmissiveColorMode: 1 @@ -256,6 +270,13 @@ Material: - _ZTestGBuffer: 4 - _ZTestTransparent: 4 - _ZWrite: 1 + - __HorizontalMiddle: 0 + - __NumChars: 2 + - __NumSegments: 14 + - __SegmentWeight: 0.05 + - __SeparatorEveryThree: 0 + - __SeparatorType: 2 + - __SkewAngle: 0 m_Colors: - Color_754dade2513142578632eb55adb8f59b: {r: 1, g: 0.36865607, b: 0, a: 0} - Color_a1cf9b5e0df34a8c907984367d220f54: {r: 0.25471696, g: 0.25471696, b: 0.25471696, a: 0} @@ -276,4 +297,8 @@ Material: - _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0} - _UVMappingMask: {r: 1, g: 0, b: 0, a: 0} - _UVMappingMaskEmissive: {r: 1, g: 0, b: 0, a: 0} + - __LitColor: {r: 1, g: 0.39999998, b: 0, a: 0} + - __Padding: {r: 0.3, g: 0.5, b: 0, a: 0} + - __SeparatorPos: {r: 1.4, g: 0, b: 0, a: 0} + - __UnlitColor: {r: 0.25471696, g: 0.25471696, b: 0.25471696, a: 0} m_BuildTextureStacks: [] diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxPrefab.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxPrefab.cs index 43562da17..60c84067c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxPrefab.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Import/VpxPrefab.cs @@ -67,7 +67,7 @@ public void SetReferencedData(Table table, IMaterialProvider materialProvider, I public void UpdateTransforms() { - if (_mainComponent is IMainRenderableComponent renderComponent) { + if (_mainComponent && _mainComponent is IMainRenderableComponent renderComponent) { renderComponent.UpdateTransforms(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/MainInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/MainInspector.cs index 96036e902..592ca0da9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/MainInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/MainInspector.cs @@ -62,7 +62,7 @@ protected void InvalidParentError() protected void UpdateSurfaceReferences(Transform obj) { var surfaceComponent = obj.gameObject.GetComponent(); - if (surfaceComponent != null && (TMainComponent)surfaceComponent.Surface == MainComponent) { + if (surfaceComponent != null && surfaceComponent.Surface == MainComponent) { surfaceComponent.OnSurfaceUpdated(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs index 274e99ea5..1ced500fe 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs @@ -92,6 +92,23 @@ protected static GameObject GetOrCreateGameObject(GameObject parentGo, string na return CreateEmptyGameObject(parentGo, name); } + /// + /// Set a new parent for the given child while keeping the position and rotation. + /// + /// + /// + protected static void Reparent(GameObject child, GameObject parent) + { + var rot = child.transform.rotation; + var pos = child.transform.position; + + // re-parent the child + child.transform.SetParent(parent.transform, false); + + child.transform.rotation = rot; + child.transform.position = pos; + } + #endregion #region Element Helpers diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs index 9ba2e4548..e5ce17c38 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs @@ -367,7 +367,7 @@ public void SkullKicker(KickerComponent kickerComponent) public void Teleporter(KickerComponent kickerComponent) { kickerComponent.Coils[0].Name = "Teleporter Out"; - kickerComponent.Coils[0].Speed = 15; + kickerComponent.Coils[0].Speed = 3; kickerComponent.Coils[0].Angle = 72; } @@ -389,7 +389,7 @@ public void LeftLock(KickerComponent kickerComponent) #endregion - #region Drop Target Banks + #region Mechs [NameMatch("sw77")] public void CreateDropTargetBank(GameObject dropTargetGo, DropTargetComponent dropTargetComponent) @@ -399,6 +399,25 @@ public void CreateDropTargetBank(GameObject dropTargetGo, DropTargetComponent dr dropTargetBank.DropTargets = new[] { dropTargetComponent }; } + [NameMatch("T2_Gun")] + public void SetupCannon(GameObject primitiveGo) + { + var playfieldGo = primitiveGo.GetComponentInParent().gameObject; + var mechsParent = GetOrCreateGameObject(playfieldGo, "Mechs"); + var kickerGo = playfieldGo.transform.Find("Kickers/sw31").gameObject; + Reparent(primitiveGo, mechsParent); + Reparent(kickerGo, primitiveGo); + + var cannon = primitiveGo.AddComponent(); + cannon.NumSteps = 240; + cannon.Marks = new[] { + new StepRotatorMark("Gun Mark", "gun_mark_switch", 0, 5), + new StepRotatorMark("Gun Home", "gun_home_switch", 98, 105) + }; + + // todo remove convertoentity component + } + #endregion #region Materials diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceSwitch.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceSwitch.cs index 5d9a36739..7d9d35118 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceSwitch.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceSwitch.cs @@ -41,6 +41,8 @@ public class DeviceSwitch : IApiSwitch /// public bool IsSwitchClosed => _switchDefault == SwitchDefault.NormallyClosed ? !IsSwitchEnabled : IsSwitchEnabled; + public string Id => _switchHandler.Name; + /// /// Indicates whether the switch is currently opened or closed. /// diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs index 08e31e031..2f58b56a0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs @@ -67,6 +67,10 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine private const string SwCreateBall = "s_create_ball"; private const string SwRedBumper = "s_red_bumper"; + private const string SwCannon = "s_cannon"; + private const string SwMotorStart = "s_motor_start"; + private const string SwMotorEnd = "s_motor_end"; + public GamelogicEngineSwitch[] AvailableSwitches => _availableSwitches.ToArray(); private readonly List _availableSwitches = new List { new GamelogicEngineSwitch(SwLeftFlipper) { Description = "Left Flipper (Button)", InputActionHint = InputConstants.ActionLeftFlipper }, @@ -78,7 +82,11 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine new GamelogicEngineSwitch(SwTrough4) { Description = "Trough 4", DeviceHint = "^Trough\\s*\\d?", DeviceItemHint = "4"}, new GamelogicEngineSwitch(SwTrough4) { Description = "Trough 4", DeviceHint = "^Trough\\s*\\d?", DeviceItemHint = "4"}, new GamelogicEngineSwitch(SwCreateBall) { Description = "Create Debug Ball", InputActionHint = InputConstants.ActionCreateBall, InputMapHint = InputConstants.MapDebug }, - new GamelogicEngineSwitch(SwRedBumper) { Description = "Red Bumper", DeviceHint = "^Bumper1$" } + new GamelogicEngineSwitch(SwRedBumper) { Description = "Red Bumper", DeviceHint = "^Bumper1$" }, + + new GamelogicEngineSwitch(SwCannon) { Description = "Cannon" }, + new GamelogicEngineSwitch(SwMotorStart) { Description = "Motor Start" }, + new GamelogicEngineSwitch(SwMotorEnd) { Description = "Motor End" } }; private const string CoilLeftFlipperMain = "c_flipper_left_main"; @@ -87,6 +95,7 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine private const string CoilRightFlipperHold = "c_flipper_right_hold"; private const string CoilTroughEntry = "c_trough_entry"; private const string CoilTroughEject = "c_trough_eject"; + private const string CoilMotorStart = "c_motor_start"; public GamelogicEngineCoil[] AvailableCoils => _availableCoils.ToArray(); private readonly List _availableCoils = new List { @@ -96,6 +105,7 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine new GamelogicEngineCoil(CoilRightFlipperHold) { Description = "Right Flipper (Hold)", DeviceHint = "^(RightFlipper|RFlipper|FlipperRight|FlipperR)$", DeviceItemHint = FlipperComponent.HoldCoilItem }, new GamelogicEngineCoil(CoilTroughEject) { Description = "Trough Eject", DeviceHint = "^Trough\\s*\\d?", DeviceItemHint = TroughComponent.EjectCoilId}, new GamelogicEngineCoil(CoilTroughEntry) { Description = "Trough Entry", DeviceHint = "^Trough\\s*\\d?", DeviceItemHint = TroughComponent.EntryCoilId}, + new GamelogicEngineCoil(CoilMotorStart) { Description = "Cannon Motor" }, }; public GamelogicEngineWire[] AvailableWires { get; } = { @@ -256,6 +266,23 @@ public void Switch(string id, bool isClosed) } break; } + + case SwCannon: { + SetCoil(CoilMotorStart, true); + break; + } + + case SwMotorStart: { + if (isClosed) { + SetCoil(CoilMotorStart, false); + } + break; + } + + case SwMotorEnd: { + + break; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 68cd10352..80e6b6e15 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -239,9 +239,9 @@ public void RegisterLampGroup(LightGroupComponent component) Register(new LightGroupApi(component.Lights.Select(l => l.GetApi(this)).ToArray()), component); } - public void RegisterCannonComponent(CannonComponent component) + public void RegisterStepRotator(StepRotatorComponent component) { - Register(new CannonApi(component.gameObject, this), component); + Register(new StepRotatorApi(component.gameObject, this), component); } public void RegisterDropTargetBankComponent(DropTargetBankComponent component) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs index 564ade289..3bc557b11 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs @@ -22,7 +22,7 @@ public class SwitchHandler /// public bool IsEnabled; - private readonly string _name; + public readonly string Name; private readonly Player _player; private IGamelogicEngine Engine => _player.GamelogicEngine; @@ -43,7 +43,7 @@ public class SwitchHandler public SwitchHandler(string name, Player player, bool isEnabled = false) { - _name = name; + Name = name; _player = player; IsEnabled = isEnabled; } @@ -163,7 +163,7 @@ internal void ScheduleSwitch(bool enabled, int delay, Action onSwitched) // handle own status SimulationSystemGroup.ScheduleAction(delay, () => { - Debug.Log($"Setting scheduled switch {_name} to {enabled}."); + Debug.Log($"Setting scheduled switch {Name} to {enabled}."); IsEnabled = enabled; #if UNITY_EDITOR diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index 8fed6e36e..304e985fc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -119,6 +119,14 @@ public bool HasBall() return kickerCollisionData.HasBall; } + internal Entity BallEntity { + get { + var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; + var kickerCollisionData = entityManager.GetComponentData(Entity); + return kickerCollisionData.BallEntity; + } + } + internal BallData GetBallData() { var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/MainComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/MainComponent.cs index c5eecb062..95b3fe9ac 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/MainComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/MainComponent.cs @@ -95,6 +95,9 @@ protected Entity ParentEntity { public bool IsCorrectlyParented { get { + if (!ValidParents.Any()) { + return true; + } var parentComponent = ParentComponent; return parentComponent == null || ValidParents.Any(validParent => parentComponent.GetType() == validParent); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/MainRenderableComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/MainRenderableComponent.cs index 81a94b31c..7df39f3ab 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/MainRenderableComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/MainRenderableComponent.cs @@ -90,9 +90,10 @@ protected void Convert(Entity entity, EntityManager dstManager) { Entity = entity; var parentComponent = ParentComponent; - if (parentComponent != null && !(parentComponent is TableComponent)) { - ParentEntity = parentComponent.Entity; - } + // todo remove the parenting stuff + // if (parentComponent != null && !(parentComponent is TableComponent)) { + // ParentEntity = parentComponent.Entity; + // } } protected float SurfaceHeight(ISurfaceComponent surface, Vector2 position) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs deleted file mode 100644 index c5841be08..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2021 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using Logger = NLog.Logger; -using NLog; -using UnityEngine; - -namespace VisualPinball.Unity -{ - public class CannonApi : IApi, IApiSwitchDevice, IApiCoilDevice - { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public const int Length = 240; - - private enum Direction - { - Forward = 0, - Reverse = 1 - } - - private readonly CannonComponent _cannonComponent; - private Player _player; - - public DeviceCoil GunMotorCoil; - public DeviceSwitch GunHomeSwitch; - public DeviceSwitch GunMarkSwitch; - - public event EventHandler Init; - - private bool _enabled; - private float _position; - private Direction _direction; - - internal CannonApi(GameObject go, Player player) - { - _cannonComponent = go.GetComponentInChildren(); - _player = player; - } - - IApiCoil IApiCoilDevice.Coil(string deviceItem) => Coil(deviceItem); - - private IApiCoil Coil(string deviceItem) - { - return deviceItem switch - { - CannonComponent.GunMotorCoilItem => GunMotorCoil, - _ => throw new ArgumentException($"Unknown coil \"{deviceItem}\". Valid name is: \"{CannonComponent.GunMotorCoilItem}\".") - }; - } - - IApiSwitch IApiSwitchDevice.Switch(string deviceItem) - { - return deviceItem switch - { - CannonComponent.GunHomeSwitchItem => GunHomeSwitch, - CannonComponent.GunMarkSwitchItem => GunMarkSwitch, - _ => throw new ArgumentException($"Unknown switch \"{deviceItem}\". Valid names are: [ \"{CannonComponent.GunHomeSwitchItem}\", \"{CannonComponent.GunMarkSwitchItem}\" ].") - }; - } - - void IApi.OnInit(BallManager ballManager) - { - _enabled = false; - _position = 0; - _direction = Direction.Forward; - - GunMotorCoil = new DeviceCoil(OnGunMotorCoilEnabled, OnGunMotorCoilDisabled); - - GunHomeSwitch = new DeviceSwitch(CannonComponent.GunHomeSwitchItem, false, SwitchDefault.NormallyOpen, _player); - GunHomeSwitch.SetSwitch(true); - - GunMarkSwitch = new DeviceSwitch(CannonComponent.GunMarkSwitchItem, false, SwitchDefault.NormallyOpen, _player); - GunMarkSwitch.SetSwitch(false); - - _player.OnUpdate += OnUpdate; - - Init?.Invoke(this, EventArgs.Empty); - } - - private void OnGunMotorCoilEnabled() - { - _enabled = true; - - Logger.Info("OnGunMotorCoilEnabled - starting rotation"); - } - - private void OnGunMotorCoilDisabled() - { - _enabled = false; - - Logger.Info("OnGunMotorCoilDisabled - stopping rotation"); - } - - private void OnUpdate(object sender, EventArgs eventArgs) - { - if (!_enabled) - { - return; - } - - float speed = (Length * 2 / 6.5f) * Time.deltaTime; - - if (_direction == Direction.Forward) - { - _position += speed; - - if (_position >= Length) - { - _position = Length - (_position - Length); - - _direction = Direction.Reverse; - } - } - else - { - _position -= speed; - - if (_position <= 0) - { - _position = -_position; - - _direction = Direction.Forward; - } - } - - if (_position >= 0 && _position <= 5) - { - if (!GunHomeSwitch.IsSwitchEnabled) - { - GunHomeSwitch.SetSwitch(true); - } - } - else if (GunHomeSwitch.IsSwitchEnabled) - { - GunHomeSwitch.SetSwitch(false); - } - - if (_position >= 98 && _position <= 105) - { - if (!GunMarkSwitch.IsSwitchEnabled) - { - GunMarkSwitch.SetSwitch(true); - } - } - else if (GunMarkSwitch.IsSwitchEnabled) - { - GunMarkSwitch.SetSwitch(false); - } - - _cannonComponent.UpdateRotation(_position / Length); - - Logger.Debug($"{_cannonComponent.name} position={_position}"); - } - - void IApi.OnDestroy() - { - _player.OnUpdate -= OnUpdate; - - Logger.Info($"Destroying {_cannonComponent.name}"); - } - } -} - diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs new file mode 100644 index 000000000..badf32a18 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs @@ -0,0 +1,175 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using Unity.Entities; +using UnityEngine; +using Logger = NLog.Logger; + +namespace VisualPinball.Unity +{ + public class StepRotatorApi : IApi, IApiSwitchDevice, IApiCoilDevice + { + public event EventHandler Init; + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private enum Direction + { + Forward = 0, + Reverse = 1 + } + + private readonly Player _player; + private readonly StepRotatorComponent _component; + + public DeviceCoil MotorCoil; + private Dictionary _switches; + private Dictionary _marks; + + + private bool _enabled; + private float _currentStep; + private Direction _direction; + private KickerApi[] _kickers; + private (KickerApi kicker, Entity ballEntity)[] _ballEntities; + + internal StepRotatorApi(GameObject go, Player player) + { + _component = go.GetComponentInChildren(); + _player = player; + } + + + void IApi.OnInit(BallManager ballManager) + { + _enabled = false; + _currentStep = 0; + _direction = Direction.Forward; + + MotorCoil = new DeviceCoil(OnMotorCoilEnabled, OnMotorCoilDisabled); + + _marks = _component.Marks.ToDictionary(m => m.SwitchId, m => m); + _switches = _component.Marks.ToDictionary(m => m.SwitchId, m => new DeviceSwitch(m.SwitchId, false, SwitchDefault.NormallyOpen, _player)); + var i = 0; + foreach (var sw in _switches.Values) { + sw.SetSwitch(i == 0); + i++; + } + + _player.OnUpdate += OnUpdate; + _kickers = _component.Kickers.Select(k => _player.TableApi.Kicker(k)).ToArray(); + + Init?.Invoke(this, EventArgs.Empty); + } + + IApiCoil IApiCoilDevice.Coil(string deviceItem) => Coil(deviceItem); + + private IApiCoil Coil(string deviceItem) + { + return deviceItem switch + { + StepRotatorComponent.MotorCoilItem => MotorCoil, + _ => throw new ArgumentException($"Unknown coil \"{deviceItem}\". Valid name is: \"{StepRotatorComponent.MotorCoilItem}\".") + }; + } + + IApiSwitch IApiSwitchDevice.Switch(string deviceItem) + { + if (!_switches.ContainsKey(deviceItem)) { + throw new ArgumentException($"Unknown switch \"{deviceItem}\". Valid names are: [ \"{string.Join("\", ", _switches.Keys)}\" ]."); + } + return _switches[deviceItem]; + } + + + private void OnMotorCoilEnabled() + { + _enabled = true; + _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => (k, k.BallEntity)).ToArray(); + + Logger.Info("OnGunMotorCoilEnabled - starting rotation"); + } + + private void OnMotorCoilDisabled() + { + _enabled = false; + Logger.Info("OnGunMotorCoilDisabled - stopping rotation"); + } + + private void OnUpdate(object sender, EventArgs eventArgs) + { + if (!_enabled) + { + return; + } + + var numSteps = _component.NumSteps; + float speed = (numSteps * 2 / 6.5f) * Time.deltaTime; + + + // determine position + if (_direction == Direction.Forward) + { + _currentStep += speed; + + if (_currentStep >= numSteps) + { + _currentStep = numSteps - (_currentStep - numSteps); + _direction = Direction.Reverse; + } + } + else + { + _currentStep -= speed; + + if (_currentStep <= 0) + { + _currentStep = -_currentStep; + _direction = Direction.Forward; + } + } + + // check if any mark hit + foreach (var mark in _marks.Values) { + var sw = _switches[mark.SwitchId]; + if (_currentStep >= mark.StepBeginning && _currentStep <= mark.StepEnd) { + if (!sw.IsSwitchEnabled) { + sw.SetSwitch(true); + } + + } else if (sw.IsSwitchEnabled) { + sw.SetSwitch(false); + } + } + + _component.UpdateRotation(_currentStep / numSteps); + + Logger.Debug($"{_component.name} position={_currentStep}"); + } + + void IApi.OnDestroy() + { + _player.OnUpdate -= OnUpdate; + + Logger.Info($"Destroying {_component.name}"); + } + } +} + diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs.meta index 70bfb520b..f1b874007 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonApi.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 22e0181ba659a436ab6bb57d9744d241 +guid: 04cafd0542d86b34f81db185737d7c9c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs similarity index 60% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonComponent.cs rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index 4b37ce6b7..599059db6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -14,7 +14,11 @@ // 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.Generic; +using System.Linq; using UnityEngine; using VisualPinball.Engine.Game.Engines; using Logger = NLog.Logger; @@ -22,31 +26,36 @@ namespace VisualPinball.Unity { - [AddComponentMenu("Visual Pinball/Game Item/Cannon")] - public class CannonComponent : MonoBehaviour, ISwitchDeviceComponent, ICoilDeviceComponent + [AddComponentMenu("Visual Pinball/Game Item/Step Rotator")] + public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoilDeviceComponent { + #region Data + + public int NumSteps; + public StepRotatorMark[] Marks; + + #endregion + + #region Constants + + public const string MotorCoilItem = "motor_coil"; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public const string GunMotorCoilItem = "gun_motor_coil"; + #endregion - public IEnumerable AvailableCoils => new[] { - new GamelogicEngineCoil(GunMotorCoilItem) { - Description = "Gun Motor" - } - }; + internal KickerComponent[] Kickers; - public const string GunMarkSwitchItem = "gun_mark_switch"; - public const string GunHomeSwitchItem = "gun_home_switch"; + #region Wiring - public IEnumerable AvailableSwitches => new[] { - new GamelogicEngineSwitch(GunMarkSwitchItem) { - Description = "Gun Mark" - }, - new GamelogicEngineSwitch(GunHomeSwitchItem) { - Description = "Gun Home" + public IEnumerable AvailableCoils => new[] { + new GamelogicEngineCoil(MotorCoilItem) { + Description = "Motor" } }; + public IEnumerable AvailableSwitches => Marks.Select(m => m.Switch); + public SwitchDefault SwitchDefault => SwitchDefault.NormallyOpen; IEnumerable IDeviceComponent.AvailableDeviceItems => AvailableCoils; @@ -54,6 +63,10 @@ public class CannonComponent : MonoBehaviour, ISwitchDeviceComponent, ICoilDevic IEnumerable IWireableComponent.AvailableWireDestinations => AvailableCoils; IEnumerable IDeviceComponent.AvailableDeviceItems => AvailableCoils; + #endregion + + #region Runtime + private void Awake() { var player = GetComponentInParent(); @@ -63,7 +76,9 @@ private void Awake() return; } - player.RegisterCannonComponent(this); + Kickers = GetComponentsInChildren(); + + player.RegisterStepRotator(this); } public void UpdateRotation(float y) @@ -73,5 +88,27 @@ public void UpdateRotation(float y) transform.rotation = rotation; } + + #endregion } + + [Serializable] + public class StepRotatorMark + { + public string Description; + public string SwitchId; + public int StepBeginning; + public int StepEnd; + + public GamelogicEngineSwitch Switch => new(SwitchId) { Description = Description }; + + public StepRotatorMark(string description, string switchId, int stepBeginning, int stepEnd) + { + Description = description; + SwitchId = switchId; + StepBeginning = stepBeginning; + StepEnd = stepEnd; + } + } + } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonComponent.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs.meta index 384ef875c..edc658c3b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/CannonComponent.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3f853fba4c41e47bfb470ad3b384e3c9 +guid: dba3d4e2fa10d2a49ba3576412cb755c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/SubComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/SubComponent.cs index b98850367..f7c16e78a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/SubComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/SubComponent.cs @@ -39,6 +39,9 @@ public abstract class SubComponent : ItemComponent public bool IsCorrectlyParented { get { + if (!ValidParents.Any()) { + return true; + } var parentComponent = ParentComponent; return parentComponent == null || ValidParents.Any(validParent => parentComponent.GetType() == validParent); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs index 8a075d197..c6f3a7340 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs @@ -27,8 +27,6 @@ public class TableApi : IApi #region Dictionaries private readonly Dictionary _bumpersByName = new Dictionary(); - private readonly Dictionary _cannonsByName = new Dictionary(); - private readonly Dictionary _mechsByName = new Dictionary(); private readonly Dictionary _dropTargetsByName = new Dictionary(); private readonly Dictionary _dropTargetBanksByName = new Dictionary(); private readonly Dictionary _flippersByName = new Dictionary(); @@ -42,6 +40,7 @@ public class TableApi : IApi private readonly Dictionary _rampsByName = new Dictionary(); private readonly Dictionary _rubbersByName = new Dictionary(); private readonly Dictionary _spinnersByName = new Dictionary(); + private readonly Dictionary _stepRotatorsByName = new Dictionary(); private readonly Dictionary _surfacesByName = new Dictionary(); private readonly Dictionary _teleportersByName = new Dictionary(); private readonly Dictionary _triggersByName = new Dictionary(); @@ -49,8 +48,6 @@ public class TableApi : IApi private readonly Dictionary _bumpersByComponent = new Dictionary(); - private readonly Dictionary _cannonsByComponent = new Dictionary(); - private readonly Dictionary _mechsByComponent = new Dictionary(); private readonly Dictionary _dropTargetsByComponent = new Dictionary(); private readonly Dictionary _dropTargetBanksByComponent = new Dictionary(); private readonly Dictionary _flippersByComponent = new Dictionary(); @@ -64,6 +61,7 @@ public class TableApi : IApi private readonly Dictionary _rampsByComponent = new Dictionary(); private readonly Dictionary _rubbersByComponent = new Dictionary(); private readonly Dictionary _spinnersByComponent = new Dictionary(); + private readonly Dictionary _stepRotatorsByComponent = new Dictionary(); private readonly Dictionary _surfacesByComponent = new Dictionary(); private readonly Dictionary _teleportersByComponent = new Dictionary(); private readonly Dictionary _triggersByComponent = new Dictionary(); @@ -199,12 +197,12 @@ public TableApi(Player player) public TroughApi Trough(MonoBehaviour component) => Get(component); /// - /// Returns a cannon by name. + /// Returns a step rotator by name. /// - /// Name of the cannon - /// Cannon or `null` if no cannon with that name exists. - public CannonApi Cannon(string name) => Get(name); - public CannonApi Cannon(MonoBehaviour component) => Get(component); + /// Name of the step rotator + /// Step rotator or `null` if no step rotator with that name exists. + public StepRotatorApi StepRotator(string name) => Get(name); + public StepRotatorApi StepRotator(MonoBehaviour component) => Get(component); /// /// Returns a drop target by name. @@ -246,8 +244,6 @@ internal void Register(MonoBehaviour component, T api) where T : IApi private Dictionary GetNameDictionary(Type t) where T : IApi { if (t == typeof(BumperApi)) return _bumpersByName as Dictionary; - if (t == typeof(CannonApi)) return _cannonsByName as Dictionary; - if (t == typeof(CannonApi)) return _mechsByName as Dictionary; if (t == typeof(DropTargetApi)) return _dropTargetsByName as Dictionary; if (t == typeof(DropTargetBankApi)) return _dropTargetBanksByName as Dictionary; if (t == typeof(FlipperApi)) return _flippersByName as Dictionary; @@ -262,6 +258,7 @@ private Dictionary GetNameDictionary(Type t) where T : IApi if (t == typeof(RampApi)) return _rampsByName as Dictionary; if (t == typeof(RubberApi)) return _rubbersByName as Dictionary; if (t == typeof(SpinnerApi)) return _spinnersByName as Dictionary; + if (t == typeof(StepRotatorApi)) return _stepRotatorsByName as Dictionary; if (t == typeof(SurfaceApi)) return _surfacesByName as Dictionary; if (t == typeof(TeleporterApi)) return _teleportersByName as Dictionary; if (t == typeof(TriggerApi)) return _triggersByName as Dictionary; @@ -272,8 +269,6 @@ private Dictionary GetNameDictionary(Type t) where T : IApi private Dictionary GetComponentDictionary(Type t) where T : IApi { if (t == typeof(BumperApi)) return _bumpersByComponent as Dictionary; - if (t == typeof(CannonApi)) return _cannonsByComponent as Dictionary; - if (t == typeof(CannonApi)) return _mechsByComponent as Dictionary; if (t == typeof(DropTargetApi)) return _dropTargetsByComponent as Dictionary; if (t == typeof(DropTargetBankApi)) return _dropTargetBanksByComponent as Dictionary; if (t == typeof(FlipperApi)) return _flippersByComponent as Dictionary; @@ -288,6 +283,7 @@ private Dictionary GetComponentDictionary(Type t) where T : if (t == typeof(RampApi)) return _rampsByComponent as Dictionary; if (t == typeof(RubberApi)) return _rubbersByComponent as Dictionary; if (t == typeof(SpinnerApi)) return _spinnersByComponent as Dictionary; + if (t == typeof(StepRotatorApi)) return _stepRotatorsByComponent as Dictionary; if (t == typeof(SurfaceApi)) return _surfacesByComponent as Dictionary; if (t == typeof(TeleporterApi)) return _teleportersByComponent as Dictionary; if (t == typeof(TriggerApi)) return _triggersByComponent as Dictionary; From 05329f91eb697b25fd49802468cf2dc21fff50b1 Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 28 Oct 2021 00:25:25 +0200 Subject: [PATCH 05/29] mechs: Add property drawer for rotator mark. --- .../VisualPinball.Unity.Editor/VPT/Mech.meta | 8 +++ .../VPT/Mech/StepRotatorMarkPropertyDrawer.cs | 65 +++++++++++++++++++ .../StepRotatorMarkPropertyDrawer.cs.meta | 11 ++++ .../VPT/Kicker/KickerApi.cs | 17 ++--- .../VPT/Mech/StepRotatorComponent.cs | 7 ++ 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech.meta new file mode 100644 index 000000000..8e79e0947 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7af66892ce9cbfc4bb8a176ac3979d07 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs new file mode 100644 index 000000000..89010e79b --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs @@ -0,0 +1,65 @@ +// 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 +{ + [CustomPropertyDrawer(typeof(StepRotatorMark))] + public class StepRotatorMarkPropertyDrawer : PropertyDrawer + { + private const float Padding = 2f; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => (EditorGUIUtility.singleLineHeight + Padding) * 3f; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + + position.height = EditorGUIUtility.singleLineHeight; + + // save indent level + var indent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + + // description and switch id + EditorGUI.PropertyField(position, property.FindPropertyRelative(nameof(StepRotatorMark.Description)), new GUIContent("Title")); + position.y += EditorGUIUtility.singleLineHeight + Padding; + EditorGUI.PropertyField(position, property.FindPropertyRelative(nameof(StepRotatorMark.SwitchId))); + + // step line + position.y += EditorGUIUtility.singleLineHeight + Padding; + position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Between")); + + var width = (position.width - 40) / 2f - 10f; + var fromRect = new Rect(position.x, position.y, width, position.height); + var toRect = new Rect(position.x + width + 20f, position.y, width, position.height); + var midRect = new Rect(position.x + width + 6f, position.y, width, position.height); + var rightRect = new Rect(position.x + position.width - 40 + 4, position.y, 40, position.height); + + EditorGUI.PropertyField(fromRect, property.FindPropertyRelative(nameof(StepRotatorMark.StepBeginning)), GUIContent.none); + EditorGUI.LabelField(midRect, new GUIContent("-")); + EditorGUI.PropertyField(toRect, property.FindPropertyRelative(nameof(StepRotatorMark.StepEnd)), GUIContent.none); + EditorGUI.LabelField(rightRect, new GUIContent("steps")); + + // set indent back to what it was + EditorGUI.indentLevel = indent; + + EditorGUI.EndProperty(); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs.meta new file mode 100644 index 000000000..9c0971866 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/StepRotatorMarkPropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94401e36468dcb343b369d6683fcc34a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index 304e985fc..dd3cb7015 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -119,14 +119,6 @@ public bool HasBall() return kickerCollisionData.HasBall; } - internal Entity BallEntity { - get { - var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; - var kickerCollisionData = entityManager.GetComponentData(Entity); - return kickerCollisionData.BallEntity; - } - } - internal BallData GetBallData() { var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; @@ -136,6 +128,15 @@ internal BallData GetBallData() : default; } + internal Entity BallEntity + { + get { + var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; + var kickerCollisionData = entityManager.GetComponentData(Entity); + return kickerCollisionData.BallEntity; + } + } + #region Wiring public bool IsSwitchEnabled => SwitchHandler.IsEnabled; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index 599059db6..c06c01da8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -31,7 +31,14 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil { #region Data + [Range(0f, 360f)] + [Tooltip("Angle in degrees the object rotates until it changes rotation and goes back. It's the angle that corresponds to the number of steps below.")] + public float TotalRotationDegrees = 65; + + [Min(0)] public int NumSteps; + + [Tooltip("On each mark, the switch changes are transmitted to the gamelogic engine.")] public StepRotatorMark[] Marks; #endregion From 92000adafa67dad9c7cf0acd6cabd54d80a2060b Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 28 Oct 2021 23:14:07 +0200 Subject: [PATCH 06/29] rotator: Move ball with object. --- .../VPT/Kicker/KickerApi.cs | 2 ++ .../VPT/Kicker/KickerCollider.cs | 8 +++-- .../VPT/Kicker/KickerColliderComponent.cs | 3 ++ .../VPT/Kicker/KickerComponent.cs | 1 + .../VPT/Kicker/KickerStaticData.cs | 1 + .../VPT/Mech/StepRotatorApi.cs | 36 +++++++++++++++---- .../VPT/Mech/StepRotatorComponent.cs | 16 +++++---- 7 files changed, 52 insertions(+), 15 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index dd3cb7015..50aba9941 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -48,6 +48,8 @@ public class KickerApi : CollidableApi public event EventHandler Switch; + internal float3 Position => new(MainComponent.Position.x, MainComponent.Position.y, MainComponent.PositionZ); + private readonly Dictionary _coils = new Dictionary(); public KickerApi(GameObject go, Entity entity, Entity parentEntity, Player player) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerCollider.cs index 1ad9e875e..93d5b822c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerCollider.cs @@ -100,9 +100,11 @@ public static void Collide(ref BallData ball, ref NativeQueue.Paralle // Only mess with variables if ball was not kicked during event ball.Velocity = float3.zero; ball.AngularMomentum = float3.zero; - var posZ = staticData.FallThrough - ? staticData.ZLow - ball.Radius - 5.0f - : staticData.ZLow + ball.Radius; + var posZ = !staticData.FallIn + ? staticData.ZLow + ball.Radius * 2 + : staticData.FallThrough + ? staticData.ZLow - ball.Radius - 5.0f + : staticData.ZLow + ball.Radius; ball.Position = new float3(staticData.Center.x, staticData.Center.y, posZ); } else { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs index 9a0e7b41e..dc6a15349 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs @@ -43,6 +43,9 @@ public class KickerColliderComponent : ColliderComponent World.DefaultGameObjectInjectionWorld.EntityManager; + private enum Direction { @@ -48,7 +51,7 @@ private enum Direction private float _currentStep; private Direction _direction; private KickerApi[] _kickers; - private (KickerApi kicker, Entity ballEntity)[] _ballEntities; + private (KickerApi kicker, float distance, Entity ballEntity)[] _ballEntities; internal StepRotatorApi(GameObject go, Player player) { @@ -83,8 +86,7 @@ void IApi.OnInit(BallManager ballManager) private IApiCoil Coil(string deviceItem) { - return deviceItem switch - { + return deviceItem switch { StepRotatorComponent.MotorCoilItem => MotorCoil, _ => throw new ArgumentException($"Unknown coil \"{deviceItem}\". Valid name is: \"{StepRotatorComponent.MotorCoilItem}\".") }; @@ -98,11 +100,15 @@ IApiSwitch IApiSwitchDevice.Switch(string deviceItem) return _switches[deviceItem]; } - private void OnMotorCoilEnabled() { + var pos = _component.transform.localPosition; _enabled = true; - _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => (k, k.BallEntity)).ToArray(); + _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => ( + k, + math.sqrt(math.pow(pos.x - k.Position.x, 2) + math.pow(pos.y - k.Position.y, 2)), + k.BallEntity) + ).ToArray(); Logger.Info("OnGunMotorCoilEnabled - starting rotation"); } @@ -159,7 +165,25 @@ private void OnUpdate(object sender, EventArgs eventArgs) } } - _component.UpdateRotation(_currentStep / numSteps); + var value = _currentStep / numSteps; + _component.UpdateRotation(value); + + var currentPos = value * _component.TotalRotationDegrees; + + var pos = _component.transform.localPosition; + foreach (var (kicker, distance, ballEntity) in _ballEntities) { + var ballData = EntityManager.GetComponentData(ballEntity); + ballData.Position = new float3( + pos.x - distance * math.sin(currentPos * math.PI / 180f), + pos.y - distance * math.cos(currentPos * math.PI / 180f), + kicker.Position.z + ballData.Radius * 2f + ); + ballData.Velocity = float3.zero; + ballData.AngularMomentum = float3.zero; + ballData.AngularVelocity = float3.zero; + + EntityManager.SetComponentData(ballEntity, ballData); + } Logger.Debug($"{_component.name} position={_currentStep}"); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index c06c01da8..7832b4e91 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -19,10 +19,11 @@ using System; using System.Collections.Generic; using System.Linq; +using NLog; using UnityEngine; +using UnityEngine.UIElements; using VisualPinball.Engine.Game.Engines; using Logger = NLog.Logger; -using NLog; namespace VisualPinball.Unity { @@ -52,6 +53,7 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil #endregion internal KickerComponent[] Kickers; + private PrimitiveComponent _primitiveComponent; #region Wiring @@ -83,17 +85,19 @@ private void Awake() return; } + _primitiveComponent = GetComponent(); Kickers = GetComponentsInChildren(); + foreach (var kicker in GetComponentsInChildren()) { + kicker.FallIn = false; + } player.RegisterStepRotator(this); } - public void UpdateRotation(float y) + public void UpdateRotation(float value) { - var rotation = transform.rotation; - rotation.y = -(y * 0.65f); - - transform.rotation = rotation; + _primitiveComponent.ObjectRotation.z = -value * TotalRotationDegrees; + _primitiveComponent.UpdateTransforms(); } #endregion From 35059c63b5f60b15ebf081763d4dcb2678f5033c Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 28 Oct 2021 23:34:59 +0200 Subject: [PATCH 07/29] rotator: Make kicker coil angle changeable during gameplay. --- .../VisualPinball.Unity/Game/DeviceCoil.cs | 12 +++++----- .../VPT/Kicker/KickerApi.cs | 24 +++++++++++++++++-- .../VPT/Mech/StepRotatorApi.cs | 7 +++--- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceCoil.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceCoil.cs index 25646c10c..e1d6c75b1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceCoil.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/DeviceCoil.cs @@ -22,22 +22,22 @@ public class DeviceCoil: IApiCoil { public bool IsEnabled; - private readonly Action _onEnable; - private readonly Action _onDisable; + protected Action OnEnable; + protected Action OnDisable; public DeviceCoil(Action onEnable = null, Action onDisable = null) { - _onEnable = onEnable; - _onDisable = onDisable; + OnEnable = onEnable; + OnDisable = onDisable; } public void OnCoil(bool enabled) { IsEnabled = enabled; if (enabled) { - _onEnable?.Invoke(); + OnEnable?.Invoke(); } else { - _onDisable?.Invoke(); + OnDisable?.Invoke(); } #if UNITY_EDITOR UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index 50aba9941..99a2709ac 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -50,13 +50,15 @@ public class KickerApi : CollidableApi new(MainComponent.Position.x, MainComponent.Position.y, MainComponent.PositionZ); - private readonly Dictionary _coils = new Dictionary(); + public KickerDeviceCoil KickerCoil => _coils.Values.FirstOrDefault(); + + private readonly Dictionary _coils = new Dictionary(); public KickerApi(GameObject go, Entity entity, Entity parentEntity, Player player) : base(go, entity, parentEntity, player) { foreach (var coil in MainComponent.Coils) { - _coils[coil.Id] = new DeviceCoil(() => Kick(coil.Angle, coil.Speed, coil.Inclination)); + _coils[coil.Id] = new KickerDeviceCoil(coil, this); } } @@ -156,6 +158,8 @@ void IApiSwitch.DestroyBall(Entity ballEntity) IApiWireDest IApiWireDeviceDest.Wire(string deviceItem) => Coil(deviceItem); + public IApiCoil Coil() => _coils.Values.FirstOrDefault(); + private IApiCoil Coil(string deviceItem) { if (_coils.ContainsKey(deviceItem)) { @@ -278,4 +282,20 @@ void IApiHittable.OnHit(Entity ballEntity, bool isUnHit) #endregion } + + public class KickerDeviceCoil : DeviceCoil + { + public readonly KickerCoil KickerCoil; + private readonly KickerApi _kickerApi; + + public KickerDeviceCoil(KickerCoil kickerCoil, KickerApi api) + { + KickerCoil = kickerCoil; + _kickerApi = api; + OnEnable = Kick; + } + + private void Kick() => _kickerApi.Kick(KickerCoil.Angle, KickerCoil.Speed, KickerCoil.Inclination); + + } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs index 01e0cc4ff..2ff5f445d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs @@ -168,19 +168,20 @@ private void OnUpdate(object sender, EventArgs eventArgs) var value = _currentStep / numSteps; _component.UpdateRotation(value); - var currentPos = value * _component.TotalRotationDegrees; + var currentAngle = value * _component.TotalRotationDegrees; var pos = _component.transform.localPosition; foreach (var (kicker, distance, ballEntity) in _ballEntities) { var ballData = EntityManager.GetComponentData(ballEntity); ballData.Position = new float3( - pos.x - distance * math.sin(currentPos * math.PI / 180f), - pos.y - distance * math.cos(currentPos * math.PI / 180f), + pos.x - distance * math.sin(currentAngle * math.PI / 180f), + pos.y - distance * math.cos(currentAngle * math.PI / 180f), kicker.Position.z + ballData.Radius * 2f ); ballData.Velocity = float3.zero; ballData.AngularMomentum = float3.zero; ballData.AngularVelocity = float3.zero; + kicker.KickerCoil.KickerCoil.Angle = currentAngle; EntityManager.SetComponentData(ballEntity, ballData); } From 8d0445da3680b9e7046bb1d3e427a67d1ef9787d Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 00:10:05 +0200 Subject: [PATCH 08/29] rotator: Make most of it generic. --- .../Patcher/Tables/Terminator2.cs | 16 +++---- .../VPT/IRotatableComponent.cs | 36 ++++++++++++++ .../VPT/IRotatableComponent.cs.meta | 11 +++++ .../VPT/Kicker/KickerApi.cs | 8 ++-- .../VPT/Kicker/KickerComponent.cs | 38 ++++++++++++++- .../VPT/Mech/StepRotatorApi.cs | 19 +++++--- .../VPT/Mech/StepRotatorComponent.cs | 48 +++++++++++++++---- .../VPT/Primitive/PrimitiveComponent.cs | 29 ++++++++++- 8 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs index e5ce17c38..02ddf703c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs @@ -400,22 +400,22 @@ public void CreateDropTargetBank(GameObject dropTargetGo, DropTargetComponent dr } [NameMatch("T2_Gun")] - public void SetupCannon(GameObject primitiveGo) + public void SetupCannon(GameObject primitiveGo, PrimitiveComponent cannonComp) { var playfieldGo = primitiveGo.GetComponentInParent().gameObject; var mechsParent = GetOrCreateGameObject(playfieldGo, "Mechs"); - var kickerGo = playfieldGo.transform.Find("Kickers/sw31").gameObject; - Reparent(primitiveGo, mechsParent); - Reparent(kickerGo, primitiveGo); - var cannon = primitiveGo.AddComponent(); - cannon.NumSteps = 240; - cannon.Marks = new[] { + var rotatorGo = CreateEmptyGameObject(mechsParent, "Cannon"); + var rotatorComp = rotatorGo.AddComponent(); + rotatorComp.Target = cannonComp; + rotatorComp.NumSteps = 240; + rotatorComp.Marks = new[] { new StepRotatorMark("Gun Mark", "gun_mark_switch", 0, 5), new StepRotatorMark("Gun Home", "gun_home_switch", 98, 105) }; - // todo remove convertoentity component + var kickerGo = playfieldGo.transform.Find("Kickers/sw31").gameObject; + } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs new file mode 100644 index 000000000..37c6ae560 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs @@ -0,0 +1,36 @@ +// 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 System.Collections.Generic; +using Unity.Mathematics; +using VisualPinball.Engine.Game.Engines; + +namespace VisualPinball.Unity +{ + /// + /// A component that can be rotated along the Z-axis + /// + public interface IRotatableComponent + { + /// + /// Sets the rotation + /// + float RotateZ { set; } + + float2 RotatedPosition { set; get; } + } + +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs.meta new file mode 100644 index 000000000..81f576e12 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/IRotatableComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28e93b51aff5c2a459a72b49fba40f82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index 99a2709ac..9268fdf36 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -285,17 +285,17 @@ void IApiHittable.OnHit(Entity ballEntity, bool isUnHit) public class KickerDeviceCoil : DeviceCoil { - public readonly KickerCoil KickerCoil; + public readonly KickerCoil Coil; private readonly KickerApi _kickerApi; - public KickerDeviceCoil(KickerCoil kickerCoil, KickerApi api) + public KickerDeviceCoil(KickerCoil coil, KickerApi api) { - KickerCoil = kickerCoil; + Coil = coil; _kickerApi = api; OnEnable = Kick; } - private void Kick() => _kickerApi.Kick(KickerCoil.Angle, KickerCoil.Speed, KickerCoil.Inclination); + private void Kick() => _kickerApi.Kick(Coil.Angle, Coil.Speed, Coil.Inclination); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs index c97bca0e1..3f4fbc9e5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs @@ -38,7 +38,8 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Kicker")] public class KickerComponent : MainRenderableComponent, - ICoilDeviceComponent, ITriggerComponent, IBallCreationPosition, IOnSurfaceComponent, IConvertGameObjectToEntity, ISerializationCallbackReceiver + ICoilDeviceComponent, ITriggerComponent, IBallCreationPosition, IOnSurfaceComponent, + IRotatableComponent, IConvertGameObjectToEntity, ISerializationCallbackReceiver { #region Data @@ -131,6 +132,26 @@ public override void UpdateTransforms() }; } + private float _originalRotationZ; + private float _originalKickerAngle; + private KickerApi _kickerApi; + + public float RotateZ { + set { + Orientation = _originalRotationZ + value; + _kickerApi.KickerCoil.Coil.Angle = _originalKickerAngle + value; + } + } + + public float2 RotatedPosition { + get => new(Position.x, Position.y); + set { + Position.x = value.x; + Position.y = value.y; + UpdateTransforms(); + } + } + #endregion #region Conversion @@ -281,6 +302,21 @@ public void OnAfterDeserialize() #endregion + #region Runtime + + private void Awake() + { + _originalRotationZ = Orientation; + } + + private void Start() + { + _kickerApi = GetComponentInParent().TableApi.Kicker(this); + _originalKickerAngle = _kickerApi.KickerCoil.Coil.Angle; + } + + #endregion + #region IBallCreationPosition public Vertex3D GetBallCreationPosition() => new Vertex3D(Position.x, Position.y, PositionZ); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs index 2ff5f445d..82992b5f3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs @@ -51,8 +51,10 @@ private enum Direction private float _currentStep; private Direction _direction; private KickerApi[] _kickers; + private (KickerApi kicker, float distance, Entity ballEntity)[] _ballEntities; + internal StepRotatorApi(GameObject go, Player player) { _component = go.GetComponentInChildren(); @@ -77,7 +79,10 @@ void IApi.OnInit(BallManager ballManager) } _player.OnUpdate += OnUpdate; - _kickers = _component.Kickers.Select(k => _player.TableApi.Kicker(k)).ToArray(); + + _kickers = _component.Kickers + .Select(k => _player.TableApi.Kicker(k)) + .ToArray(); Init?.Invoke(this, EventArgs.Empty); } @@ -102,7 +107,7 @@ IApiSwitch IApiSwitchDevice.Switch(string deviceItem) private void OnMotorCoilEnabled() { - var pos = _component.transform.localPosition; + var pos = _component.Target.RotatedPosition; _enabled = true; _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => ( k, @@ -166,11 +171,13 @@ private void OnUpdate(object sender, EventArgs eventArgs) } var value = _currentStep / numSteps; + + // rotate target _component.UpdateRotation(value); + // rotate ball(s) var currentAngle = value * _component.TotalRotationDegrees; - - var pos = _component.transform.localPosition; + var pos = _component.Target.RotatedPosition; foreach (var (kicker, distance, ballEntity) in _ballEntities) { var ballData = EntityManager.GetComponentData(ballEntity); ballData.Position = new float3( @@ -181,12 +188,10 @@ private void OnUpdate(object sender, EventArgs eventArgs) ballData.Velocity = float3.zero; ballData.AngularMomentum = float3.zero; ballData.AngularVelocity = float3.zero; - kicker.KickerCoil.KickerCoil.Angle = currentAngle; + kicker.KickerCoil.Coil.Angle = currentAngle; EntityManager.SetComponentData(ballEntity, ballData); } - - Logger.Debug($"{_component.name} position={_currentStep}"); } void IApi.OnDestroy() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index 7832b4e91..57b0d482d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -20,8 +20,8 @@ using System.Collections.Generic; using System.Linq; using NLog; +using Unity.Mathematics; using UnityEngine; -using UnityEngine.UIElements; using VisualPinball.Engine.Game.Engines; using Logger = NLog.Logger; @@ -32,6 +32,12 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil { #region Data + public IRotatableComponent Target { get => _target as IRotatableComponent; set => _target = value as MonoBehaviour; } + [SerializeField] + [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] + [Tooltip("The target that will rotate.")] + public MonoBehaviour _target; + [Range(0f, 360f)] [Tooltip("Angle in degrees the object rotates until it changes rotation and goes back. It's the angle that corresponds to the number of steps below.")] public float TotalRotationDegrees = 65; @@ -42,6 +48,17 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil [Tooltip("On each mark, the switch changes are transmitted to the gamelogic engine.")] public StepRotatorMark[] Marks; + [SerializeField] + [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] + [Tooltip("Other objects at will rotate around the target.")] + public MonoBehaviour[] _rotateWith; + + #endregion + + #region Access + + internal IEnumerable Kickers => _rotateWith.OfType(); + #endregion #region Constants @@ -52,9 +69,6 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil #endregion - internal KickerComponent[] Kickers; - private PrimitiveComponent _primitiveComponent; - #region Wiring public IEnumerable AvailableCoils => new[] { @@ -76,6 +90,8 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil #region Runtime + private Dictionary _rotatingObjectDistances = new(); + private void Awake() { var player = GetComponentInParent(); @@ -85,19 +101,35 @@ private void Awake() return; } - _primitiveComponent = GetComponent(); - Kickers = GetComponentsInChildren(); foreach (var kicker in GetComponentsInChildren()) { kicker.FallIn = false; } + var pos = Target.RotatedPosition; + _rotatingObjectDistances = _rotateWith + .OfType() + .ToDictionary( + r => r, + r => math.sqrt(math.pow(pos.x - r.RotatedPosition.x, 2) + math.pow(pos.y - r.RotatedPosition.y, 2)) + ); + player.RegisterStepRotator(this); } public void UpdateRotation(float value) { - _primitiveComponent.ObjectRotation.z = -value * TotalRotationDegrees; - _primitiveComponent.UpdateTransforms(); + var angleDeg = -value * TotalRotationDegrees; + + Target.RotateZ = angleDeg; + + var pos = Target.RotatedPosition; + foreach (var obj in _rotatingObjectDistances.Keys) { + obj.RotateZ = angleDeg; + obj.RotatedPosition = new float2( + pos.x - _rotatingObjectDistances[obj] * math.sin(angleDeg * math.PI / 180f), + pos.y - _rotatingObjectDistances[obj] * math.cos(angleDeg * math.PI / 180f) + ); + } } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs index e161dee67..54cc13503 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs @@ -25,6 +25,7 @@ using System.Collections.Generic; using System.Linq; using Unity.Entities; +using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.Math; using VisualPinball.Engine.VPT; @@ -36,7 +37,8 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Primitive")] - public class PrimitiveComponent : MainRenderableComponent, IMeshGenerator, IConvertGameObjectToEntity + public class PrimitiveComponent : MainRenderableComponent, IMeshGenerator, + IRotatableComponent, IConvertGameObjectToEntity { #region Data @@ -81,6 +83,22 @@ public override void UpdateTransforms() transform.SetFromMatrix(GetTransformationMatrix().ToUnityMatrix()); } + public float _originalRotateZ; + public float RotateZ { + set { + ObjectRotation.z = _originalRotateZ + value; + UpdateTransforms(); + } + } + public float2 RotatedPosition { + get => new(Position.x, Position.y); + set { + Position.x = value.x; + Position.y = value.y; + UpdateTransforms(); + } + } + #endregion #region Conversion @@ -214,6 +232,15 @@ public override PrimitiveData CopyDataTo(PrimitiveData data, string[] materialNa #endregion + #region Runtime + + private void Awake() + { + _originalRotateZ = ObjectRotation.z; + } + + #endregion + #region IMeshGenerator public Mesh GetMesh() => GetDefaultMesh(); From 43ec223496e7d8b8dc939bf7f4cd155bcb29a1bd Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 00:23:30 +0200 Subject: [PATCH 09/29] rotator: Make flipper rotatable. --- .../VPT/Flipper/FlipperApi.cs | 9 +++++ .../VPT/Flipper/FlipperComponent.cs | 37 ++++++++++++++++++- .../VPT/Mech/StepRotatorComponent.cs | 15 ++++---- .../VPT/Ramp/RampComponent.cs | 2 +- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs index 5d41e5b89..b0bbb4780 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs @@ -107,6 +107,15 @@ public void RotateToStart() EngineProvider.Get().FlipperRotateToStart(Entity); } + internal float StartAngle + { + set { + var staticData = EntityManager.GetComponentData(Entity); + staticData.AngleStart = value; + EntityManager.SetComponentData(Entity, staticData); + } + } + #region Coil Handling private DeviceCoil _mainCoil; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 6ea2aef75..609964e63 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -41,7 +41,8 @@ namespace VisualPinball.Unity [AddComponentMenu("Visual Pinball/Game Item/Flipper")] [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/flippers.html")] public class FlipperComponent : MainRenderableComponent, - IFlipperData, ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, IConvertGameObjectToEntity + IFlipperData, ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, + IRotatableComponent, IConvertGameObjectToEntity { #region Data @@ -177,6 +178,26 @@ public override void UpdateTransforms() t.localEulerAngles = new Vector3(0, 0, _startAngle); } + private FlipperApi _flipperApi; + public float _originalRotateZ; + + public float RotateZ { + set { + _startAngle = _originalRotateZ + value; + _flipperApi.StartAngle = _originalRotateZ + value; + UpdateTransforms(); + } + } + + public float2 RotatedPosition { + get => new(Position.x, Position.y); + set { + Position.x = value.x; + Position.y = value.y; + UpdateTransforms(); + } + } + #endregion #region Conversion @@ -545,6 +566,20 @@ public TriggerData CreateCorrectionTriggerData() #endregion + #region Runtime + + private void Awake() + { + _originalRotateZ = _startAngle; + } + + private void Start() + { + _flipperApi = GetComponentInParent().TableApi.Flipper(this); + } + + #endregion + #region DOTS Data private FlipperStaticData GetMaterialData(FlipperColliderComponent colliderComponent) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index 57b0d482d..af6a80aea 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -52,6 +52,7 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] [Tooltip("Other objects at will rotate around the target.")] public MonoBehaviour[] _rotateWith; + public IRotatableComponent[] RotateWith => _rotateWith.OfType().ToArray(); #endregion @@ -106,12 +107,10 @@ private void Awake() } var pos = Target.RotatedPosition; - _rotatingObjectDistances = _rotateWith - .OfType() - .ToDictionary( - r => r, - r => math.sqrt(math.pow(pos.x - r.RotatedPosition.x, 2) + math.pow(pos.y - r.RotatedPosition.y, 2)) - ); + _rotatingObjectDistances = RotateWith.ToDictionary( + r => r, + r => math.sqrt(math.pow(pos.x - r.RotatedPosition.x, 2) + math.pow(pos.y - r.RotatedPosition.y, 2)) + ); player.RegisterStepRotator(this); } @@ -126,8 +125,8 @@ public void UpdateRotation(float value) foreach (var obj in _rotatingObjectDistances.Keys) { obj.RotateZ = angleDeg; obj.RotatedPosition = new float2( - pos.x - _rotatingObjectDistances[obj] * math.sin(angleDeg * math.PI / 180f), - pos.y - _rotatingObjectDistances[obj] * math.cos(angleDeg * math.PI / 180f) + pos.x - _rotatingObjectDistances[obj] * math.sin(math.radians(angleDeg)), + pos.y - _rotatingObjectDistances[obj] * math.cos(math.radians(angleDeg)) ); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampComponent.cs index 7c9b24b76..a0972e988 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampComponent.cs @@ -399,7 +399,7 @@ protected static void WalkChildren(IEnumerable node, Action action) protected void UpdateSurfaceReferences(Transform obj) { var surfaceComponent = obj.gameObject.GetComponent(); - if (surfaceComponent != null && (RampComponent)surfaceComponent.Surface == this) { + if (surfaceComponent != null && surfaceComponent.Surface == this) { surfaceComponent.OnSurfaceUpdated(); } } From 1ebb80ff6e4939a9d89c2f718d17ad8c962abf1e Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 22:48:54 +0200 Subject: [PATCH 10/29] rotator: Fix rotation for referenced objects. --- .../VPT/Mech/StepRotatorComponent.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index af6a80aea..5c8d451d7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -91,7 +91,7 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil #region Runtime - private Dictionary _rotatingObjectDistances = new(); + private Dictionary _rotatingObjectDistances = new(); private void Awake() { @@ -109,7 +109,10 @@ private void Awake() var pos = Target.RotatedPosition; _rotatingObjectDistances = RotateWith.ToDictionary( r => r, - r => math.sqrt(math.pow(pos.x - r.RotatedPosition.x, 2) + math.pow(pos.y - r.RotatedPosition.y, 2)) + r => ( + math.distance(pos, r.RotatedPosition), + math.sign(pos.x - r.RotatedPosition.x) * Vector2.Angle(r.RotatedPosition - pos, new float2(0f, -1f)) + ) ); player.RegisterStepRotator(this); @@ -117,16 +120,17 @@ private void Awake() public void UpdateRotation(float value) { - var angleDeg = -value * TotalRotationDegrees; + var angleDeg = value * TotalRotationDegrees; - Target.RotateZ = angleDeg; + Target.RotateZ = -angleDeg; var pos = Target.RotatedPosition; foreach (var obj in _rotatingObjectDistances.Keys) { - obj.RotateZ = angleDeg; + var (distance, angle) = _rotatingObjectDistances[obj]; + obj.RotateZ = -angleDeg; obj.RotatedPosition = new float2( - pos.x - _rotatingObjectDistances[obj] * math.sin(math.radians(angleDeg)), - pos.y - _rotatingObjectDistances[obj] * math.cos(math.radians(angleDeg)) + pos.x -distance * math.sin(math.radians(angleDeg + angle)), + pos.y -distance * math.cos(math.radians(angleDeg + angle)) ); } } From 48632f03e418a4c82cfbfd99183113e891bcb394 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 23:01:20 +0200 Subject: [PATCH 11/29] rotator: Fix ball rotation. --- .../VisualPinball.Unity/VPT/Kicker/KickerComponent.cs | 2 +- .../VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs index 3f4fbc9e5..e43f5649c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs @@ -58,7 +58,7 @@ public class KickerComponent : MainRenderableComponent, [Tooltip("On which surface the kicker is attached to. Updates Z-translation.")] public MonoBehaviour _surface; - public List Coils = new List { + public List Coils = new() { new KickerCoil { Name = "Default Coil" } }; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs index 82992b5f3..92c6af855 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs @@ -52,7 +52,7 @@ private enum Direction private Direction _direction; private KickerApi[] _kickers; - private (KickerApi kicker, float distance, Entity ballEntity)[] _ballEntities; + private (KickerApi kicker, float distance, float angle, Entity ballEntity)[] _ballEntities; internal StepRotatorApi(GameObject go, Player player) @@ -111,7 +111,8 @@ private void OnMotorCoilEnabled() _enabled = true; _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => ( k, - math.sqrt(math.pow(pos.x - k.Position.x, 2) + math.pow(pos.y - k.Position.y, 2)), + math.distance(pos, k.Position.xy), + math.sign(pos.x - k.Position.x) * Vector2.Angle(k.Position.xy - pos, new float2(0f, -1f)), k.BallEntity) ).ToArray(); @@ -178,11 +179,11 @@ private void OnUpdate(object sender, EventArgs eventArgs) // rotate ball(s) var currentAngle = value * _component.TotalRotationDegrees; var pos = _component.Target.RotatedPosition; - foreach (var (kicker, distance, ballEntity) in _ballEntities) { + foreach (var (kicker, distance, angle, ballEntity) in _ballEntities) { var ballData = EntityManager.GetComponentData(ballEntity); ballData.Position = new float3( - pos.x - distance * math.sin(currentAngle * math.PI / 180f), - pos.y - distance * math.cos(currentAngle * math.PI / 180f), + pos.x -distance * math.sin(math.radians(currentAngle + angle)), + pos.y -distance * math.cos(math.radians(currentAngle + angle)), kicker.Position.z + ballData.Radius * 2f ); ballData.Velocity = float3.zero; From 267a11679f396a16ed0005dd796d8bf91f477f07 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 23:10:46 +0200 Subject: [PATCH 12/29] rotator: Fix kicker angle. --- .../VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs index 92c6af855..1958b4459 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs @@ -189,7 +189,6 @@ private void OnUpdate(object sender, EventArgs eventArgs) ballData.Velocity = float3.zero; ballData.AngularMomentum = float3.zero; ballData.AngularVelocity = float3.zero; - kicker.KickerCoil.Coil.Angle = currentAngle; EntityManager.SetComponentData(ballEntity, ballData); } From bfe0e2a68bdaa3f2735e509e2b7153f897486616 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 23:21:27 +0200 Subject: [PATCH 13/29] fix: Remove obsolete angle and speed from kicker component. --- .../VPT/Kicker/KickerDataTests.cs | 5 ----- VisualPinball.Engine/VPT/Kicker/KickerData.cs | 10 ---------- .../VPT/Kicker/KickerColliderInspector.cs | 8 -------- .../VisualPinball.Unity/VPT/Kicker/KickerApi.cs | 5 ----- .../VPT/Kicker/KickerColliderComponent.cs | 8 -------- .../VisualPinball.Unity/VPT/Kicker/KickerComponent.cs | 4 ---- 6 files changed, 40 deletions(-) diff --git a/VisualPinball.Engine.Test/VPT/Kicker/KickerDataTests.cs b/VisualPinball.Engine.Test/VPT/Kicker/KickerDataTests.cs index ac999b7ac..ba3eccee6 100644 --- a/VisualPinball.Engine.Test/VPT/Kicker/KickerDataTests.cs +++ b/VisualPinball.Engine.Test/VPT/Kicker/KickerDataTests.cs @@ -59,11 +59,6 @@ public static void ValidateKickerData(KickerData data) data.Radius.Should().Be(25.98f); data.Scatter.Should().Be(4.98f); data.Surface.Should().Be(""); - - #if !WRITE_VP106 && !WRITE_VP107 - data.Angle.Should().Be(65.5f); - data.Speed.Should().Be(5.8f); - #endif } } } diff --git a/VisualPinball.Engine/VPT/Kicker/KickerData.cs b/VisualPinball.Engine/VPT/Kicker/KickerData.cs index 3ae94ca6d..ea9e8658e 100644 --- a/VisualPinball.Engine/VPT/Kicker/KickerData.cs +++ b/VisualPinball.Engine/VPT/Kicker/KickerData.cs @@ -82,16 +82,6 @@ public class KickerData : ItemData [BiffInt("TMIN", Pos = 4)] public int TimerInterval; - // ----------------- - // new fields by VPE - // ----------------- - - [BiffFloat("ANGL", Pos = 16, SkipHash = true, IsVpeEnhancement = true)] - public float Angle = 90f; - - [BiffFloat("SPED", Pos = 17, SkipHash = true, IsVpeEnhancement = true)] - public float Speed = 3f; - public KickerData() : base(StoragePrefix.GameItem) { } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerColliderInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerColliderInspector.cs index f05a93495..dc2f67a66 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerColliderInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Kicker/KickerColliderInspector.cs @@ -29,8 +29,6 @@ public class KickerColliderInspector : ColliderInspector KickXYZ(Entity, ColliderComponent.EjectAngle, ColliderComponent.EjectSpeed, 0, 0, 0, 0)); - } - public void Kick(float angle, float speed, float inclination = 0) { SimulationSystemGroup.QueueAfterBallCreation(() => KickXYZ(Entity, angle, speed, inclination, 0, 0, 0)); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs index dc6a15349..dd62a50fc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerColliderComponent.cs @@ -49,14 +49,6 @@ public class KickerColliderComponent : ColliderComponent SetData(KickerData data) colliderComponent.HitHeight = data.HitHeight; colliderComponent.FallThrough = data.FallThrough; colliderComponent.LegacyMode = data.LegacyMode; - colliderComponent.EjectAngle = data.Angle; - colliderComponent.EjectSpeed = data.Speed; updatedComponents.Add(colliderComponent); } @@ -264,8 +262,6 @@ public override KickerData CopyDataTo(KickerData data, string[] materialNames, s data.HitHeight = colliderComponent.HitHeight; data.FallThrough = colliderComponent.FallThrough; data.LegacyMode = colliderComponent.LegacyMode; - data.Angle = colliderComponent.EjectAngle; - data.Speed = colliderComponent.EjectSpeed; } else { data.IsEnabled = false; From 9734407437009280026da98fb83ac5628ff3960f Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 29 Oct 2021 23:43:55 +0200 Subject: [PATCH 14/29] ci: Bump Unity to 2021.2.0f1 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e65f9b2ed..c5e22d5a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ jobs: - uses: game-ci/unity-test-runner@main id: test with: - unityVersion: '2020.3.13f1' + unityVersion: '2021.2.0f1' projectPath: VisualPinball.Unity/VisualPinball.Unity.Test/TestProject~ artifactsPath: VisualPinball.Unity/VisualPinball.Unity.Test/TestProject~/artifacts testMode: all From 8b4b85feaf50c37b8577fec77e9a028098ab298e Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 30 Oct 2021 00:39:32 +0200 Subject: [PATCH 15/29] rotator: Don't control ball when kicked out. --- .../VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs | 5 ++++- .../VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs | 7 +++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs index 1958b4459..0354537c5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs @@ -180,11 +180,14 @@ private void OnUpdate(object sender, EventArgs eventArgs) var currentAngle = value * _component.TotalRotationDegrees; var pos = _component.Target.RotatedPosition; foreach (var (kicker, distance, angle, ballEntity) in _ballEntities) { + if (!kicker.HasBall()) { + return; + } var ballData = EntityManager.GetComponentData(ballEntity); ballData.Position = new float3( pos.x -distance * math.sin(math.radians(currentAngle + angle)), pos.y -distance * math.cos(math.radians(currentAngle + angle)), - kicker.Position.z + ballData.Radius * 2f + ballData.Position.z ); ballData.Velocity = float3.zero; ballData.AngularMomentum = float3.zero; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs index 5c8d451d7..ada627d17 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs @@ -96,14 +96,13 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil private void Awake() { var player = GetComponentInParent(); - if (player == null) - { + if (player == null) { Logger.Error($"Cannot find player for cannon {name}."); return; } - foreach (var kicker in GetComponentsInChildren()) { - kicker.FallIn = false; + foreach (var kicker in Kickers) { + kicker.GetComponent().FallIn = false; } var pos = Target.RotatedPosition; From 6b8012c21f14de00c91e8f4471738b778b951de5 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 30 Oct 2021 21:10:11 +0200 Subject: [PATCH 16/29] rotator: Split mech and tranformations. --- .../Patcher/Tables/Terminator2.cs | 15 +- .../VisualPinball.Unity/Game/Player.cs | 4 +- .../VPT/Mech/RotatorComponent.cs | 134 ++++++++++++++++++ ...orApi.cs.meta => RotatorComponent.cs.meta} | 2 +- ...tepRotatorApi.cs => StepRotatorMechApi.cs} | 71 ++-------- ...ent.cs.meta => StepRotatorMechApi.cs.meta} | 2 +- ...mponent.cs => StepRotatorMechComponent.cs} | 54 +------ .../VPT/Mech/StepRotatorMechComponent.cs.meta | 11 ++ .../VisualPinball.Unity/VPT/Table/TableApi.cs | 12 +- 9 files changed, 185 insertions(+), 120 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{StepRotatorApi.cs.meta => RotatorComponent.cs.meta} (83%) rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{StepRotatorApi.cs => StepRotatorMechApi.cs} (61%) rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{StepRotatorComponent.cs.meta => StepRotatorMechApi.cs.meta} (83%) rename VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/{StepRotatorComponent.cs => StepRotatorMechComponent.cs} (60%) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs index 02ddf703c..cd9e84441 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs @@ -406,16 +406,19 @@ public void SetupCannon(GameObject primitiveGo, PrimitiveComponent cannonComp) var mechsParent = GetOrCreateGameObject(playfieldGo, "Mechs"); var rotatorGo = CreateEmptyGameObject(mechsParent, "Cannon"); - var rotatorComp = rotatorGo.AddComponent(); - rotatorComp.Target = cannonComp; - rotatorComp.NumSteps = 240; - rotatorComp.Marks = new[] { + var rotatorComp = rotatorGo.AddComponent(); + var mechComp = rotatorGo.AddComponent(); + mechComp.Target = rotatorComp; + mechComp.NumSteps = 240; + mechComp.Marks = new[] { new StepRotatorMark("Gun Mark", "gun_mark_switch", 0, 5), new StepRotatorMark("Gun Home", "gun_home_switch", 98, 105) }; - var kickerGo = playfieldGo.transform.Find("Kickers/sw31").gameObject; - + rotatorComp.Target = cannonComp; + rotatorComp.RotateWith = new IRotatableComponent[] { + playfieldGo.transform.Find("Kickers/sw31").GetComponent(), + }; } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 80e6b6e15..a009db5ff 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -239,9 +239,9 @@ public void RegisterLampGroup(LightGroupComponent component) Register(new LightGroupApi(component.Lights.Select(l => l.GetApi(this)).ToArray()), component); } - public void RegisterStepRotator(StepRotatorComponent component) + public void RegisterStepRotator(StepRotatorMechComponent component) { - Register(new StepRotatorApi(component.gameObject, this), component); + Register(new StepRotatorMechApi(component.gameObject, this), component); } public void RegisterDropTargetBankComponent(DropTargetBankComponent component) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs new file mode 100644 index 000000000..143af97e6 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs @@ -0,0 +1,134 @@ +// 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.Collections.Generic; +using System.Linq; +using Unity.Entities; +using Unity.Mathematics; +using UnityEngine; + +namespace VisualPinball.Unity +{ + [AddComponentMenu("Visual Pinball/Game Item/Rotator")] + public class RotatorComponent : MonoBehaviour + { + #region Data + + public IRotatableComponent Target { get => _target as IRotatableComponent; set => _target = value as MonoBehaviour; } + [SerializeField] + [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] + [Tooltip("The target that will rotate.")] + public MonoBehaviour _target; + + [SerializeField] + [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] + [Tooltip("Other objects at will rotate around the target.")] + public MonoBehaviour[] _rotateWith; + public IRotatableComponent[] RotateWith { + get => _rotateWith.OfType().ToArray(); + set => _rotateWith = value.OfType().ToArray(); + } + + #endregion + + #region Access + + internal IEnumerable Kickers => _rotateWith.OfType(); + + #endregion + + #region Runtime + + private Player _player; + private KickerApi[] _kickers; + private (KickerApi kicker, float distance, float angle, Entity ballEntity)[] _ballEntities; + + private Dictionary _rotatingObjectDistances = new(); + private static EntityManager EntityManager => World.DefaultGameObjectInjectionWorld.EntityManager; + + private void Awake() + { + _player = GetComponentInParent(); + + var pos = Target.RotatedPosition; + _rotatingObjectDistances = RotateWith.ToDictionary( + r => r, + r => ( + math.distance(pos, r.RotatedPosition), + math.sign(pos.x - r.RotatedPosition.x) * Vector2.Angle(r.RotatedPosition - pos, new float2(0f, -1f)) + ) + ); + } + + private void Start() + { + _kickers = Kickers + .Select(k => _player.TableApi.Kicker(k)) + .ToArray(); + } + + public void StartRotating() + { + var pos = Target.RotatedPosition; + _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => ( + k, + math.distance(pos, k.Position.xy), + math.sign(pos.x - k.Position.x) * Vector2.Angle(k.Position.xy - pos, new float2(0f, -1f)), + k.BallEntity) + ).ToArray(); + } + + public void UpdateRotation(float angleDeg) + { + // rotate target + Target.RotateZ = -angleDeg; + var pos = Target.RotatedPosition; + + // rotate objects + foreach (var obj in _rotatingObjectDistances.Keys) { + var (distance, angle) = _rotatingObjectDistances[obj]; + obj.RotateZ = -angleDeg; + obj.RotatedPosition = new float2( + pos.x -distance * math.sin(math.radians(angleDeg + angle)), + pos.y -distance * math.cos(math.radians(angleDeg + angle)) + ); + } + + // rotate ball(s) in kicker(s) + foreach (var (kicker, distance, angle, ballEntity) in _ballEntities) { + if (!kicker.HasBall()) { + return; + } + var ballData = EntityManager.GetComponentData(ballEntity); + ballData.Position = new float3( + pos.x -distance * math.sin(math.radians(angleDeg + angle)), + pos.y -distance * math.cos(math.radians(angleDeg + angle)), + ballData.Position.z + ); + ballData.Velocity = float3.zero; + ballData.AngularMomentum = float3.zero; + ballData.AngularVelocity = float3.zero; + + EntityManager.SetComponentData(ballEntity, ballData); + } + } + + #endregion + } + +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta index f1b874007..fea40c617 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 04cafd0542d86b34f81db185737d7c9c +guid: 815586529ac0612409629e7eba366f76 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs similarity index 61% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs index 0354537c5..5a3ad7df7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs @@ -18,20 +18,16 @@ using System.Collections.Generic; using System.Linq; using NLog; -using Unity.Entities; -using Unity.Mathematics; using UnityEngine; using Logger = NLog.Logger; namespace VisualPinball.Unity { - public class StepRotatorApi : IApi, IApiSwitchDevice, IApiCoilDevice + public class StepRotatorMechApi : IApi, IApiSwitchDevice, IApiCoilDevice { public event EventHandler Init; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private static EntityManager EntityManager => World.DefaultGameObjectInjectionWorld.EntityManager; - private enum Direction { @@ -40,24 +36,19 @@ private enum Direction } private readonly Player _player; - private readonly StepRotatorComponent _component; - - public DeviceCoil MotorCoil; - private Dictionary _switches; - private Dictionary _marks; - + private readonly StepRotatorMechComponent _component; + private DeviceCoil _motorCoil; private bool _enabled; private float _currentStep; private Direction _direction; - private KickerApi[] _kickers; - - private (KickerApi kicker, float distance, float angle, Entity ballEntity)[] _ballEntities; + private Dictionary _switches; + private Dictionary _marks; - internal StepRotatorApi(GameObject go, Player player) + internal StepRotatorMechApi(GameObject go, Player player) { - _component = go.GetComponentInChildren(); + _component = go.GetComponentInChildren(); _player = player; } @@ -68,7 +59,7 @@ void IApi.OnInit(BallManager ballManager) _currentStep = 0; _direction = Direction.Forward; - MotorCoil = new DeviceCoil(OnMotorCoilEnabled, OnMotorCoilDisabled); + _motorCoil = new DeviceCoil(OnMotorCoilEnabled, OnMotorCoilDisabled); _marks = _component.Marks.ToDictionary(m => m.SwitchId, m => m); _switches = _component.Marks.ToDictionary(m => m.SwitchId, m => new DeviceSwitch(m.SwitchId, false, SwitchDefault.NormallyOpen, _player)); @@ -80,10 +71,6 @@ void IApi.OnInit(BallManager ballManager) _player.OnUpdate += OnUpdate; - _kickers = _component.Kickers - .Select(k => _player.TableApi.Kicker(k)) - .ToArray(); - Init?.Invoke(this, EventArgs.Empty); } @@ -92,8 +79,8 @@ void IApi.OnInit(BallManager ballManager) private IApiCoil Coil(string deviceItem) { return deviceItem switch { - StepRotatorComponent.MotorCoilItem => MotorCoil, - _ => throw new ArgumentException($"Unknown coil \"{deviceItem}\". Valid name is: \"{StepRotatorComponent.MotorCoilItem}\".") + StepRotatorMechComponent.MotorCoilItem => _motorCoil, + _ => throw new ArgumentException($"Unknown coil \"{deviceItem}\". Valid name is: \"{StepRotatorMechComponent.MotorCoilItem}\".") }; } @@ -107,15 +94,8 @@ IApiSwitch IApiSwitchDevice.Switch(string deviceItem) private void OnMotorCoilEnabled() { - var pos = _component.Target.RotatedPosition; + _component.Target.StartRotating(); _enabled = true; - _ballEntities = _kickers.Where(k => k.HasBall()).Select(k => ( - k, - math.distance(pos, k.Position.xy), - math.sign(pos.x - k.Position.x) * Vector2.Angle(k.Position.xy - pos, new float2(0f, -1f)), - k.BallEntity) - ).ToArray(); - Logger.Info("OnGunMotorCoilEnabled - starting rotation"); } @@ -127,14 +107,13 @@ private void OnMotorCoilDisabled() private void OnUpdate(object sender, EventArgs eventArgs) { - if (!_enabled) + if (!_enabled || _component.NumSteps == 0) { return; } var numSteps = _component.NumSteps; - float speed = (numSteps * 2 / 6.5f) * Time.deltaTime; - + var speed = numSteps * 2 / 6.5f * Time.deltaTime; // determine position if (_direction == Direction.Forward) @@ -171,30 +150,8 @@ private void OnUpdate(object sender, EventArgs eventArgs) } } - var value = _currentStep / numSteps; - // rotate target - _component.UpdateRotation(value); - - // rotate ball(s) - var currentAngle = value * _component.TotalRotationDegrees; - var pos = _component.Target.RotatedPosition; - foreach (var (kicker, distance, angle, ballEntity) in _ballEntities) { - if (!kicker.HasBall()) { - return; - } - var ballData = EntityManager.GetComponentData(ballEntity); - ballData.Position = new float3( - pos.x -distance * math.sin(math.radians(currentAngle + angle)), - pos.y -distance * math.cos(math.radians(currentAngle + angle)), - ballData.Position.z - ); - ballData.Velocity = float3.zero; - ballData.AngularMomentum = float3.zero; - ballData.AngularVelocity = float3.zero; - - EntityManager.SetComponentData(ballEntity, ballData); - } + _component.UpdateRotation(_currentStep / numSteps); } void IApi.OnDestroy() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs.meta index edc658c3b..0a56babc8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dba3d4e2fa10d2a49ba3576412cb755c +guid: f40159dfaad83b941ae9e60688743475 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs similarity index 60% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs rename to VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs index ada627d17..9ec85b3da 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs @@ -27,16 +27,13 @@ namespace VisualPinball.Unity { - [AddComponentMenu("Visual Pinball/Game Item/Step Rotator")] - public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoilDeviceComponent + [AddComponentMenu("Visual Pinball/Mechs/Step Rotator")] + public class StepRotatorMechComponent : MonoBehaviour, ISwitchDeviceComponent, ICoilDeviceComponent { #region Data - public IRotatableComponent Target { get => _target as IRotatableComponent; set => _target = value as MonoBehaviour; } - [SerializeField] - [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] - [Tooltip("The target that will rotate.")] - public MonoBehaviour _target; + [Tooltip("The target to rotate.")] + public RotatorComponent Target; [Range(0f, 360f)] [Tooltip("Angle in degrees the object rotates until it changes rotation and goes back. It's the angle that corresponds to the number of steps below.")] @@ -48,18 +45,6 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil [Tooltip("On each mark, the switch changes are transmitted to the gamelogic engine.")] public StepRotatorMark[] Marks; - [SerializeField] - [TypeRestriction(typeof(IRotatableComponent), PickerLabel = "Rotatable Objects")] - [Tooltip("Other objects at will rotate around the target.")] - public MonoBehaviour[] _rotateWith; - public IRotatableComponent[] RotateWith => _rotateWith.OfType().ToArray(); - - #endregion - - #region Access - - internal IEnumerable Kickers => _rotateWith.OfType(); - #endregion #region Constants @@ -91,46 +76,21 @@ public class StepRotatorComponent : MonoBehaviour, ISwitchDeviceComponent, ICoil #region Runtime - private Dictionary _rotatingObjectDistances = new(); - private void Awake() { var player = GetComponentInParent(); if (player == null) { - Logger.Error($"Cannot find player for cannon {name}."); + Logger.Error($"Cannot find player for step rotator {name}."); return; } - foreach (var kicker in Kickers) { - kicker.GetComponent().FallIn = false; - } - - var pos = Target.RotatedPosition; - _rotatingObjectDistances = RotateWith.ToDictionary( - r => r, - r => ( - math.distance(pos, r.RotatedPosition), - math.sign(pos.x - r.RotatedPosition.x) * Vector2.Angle(r.RotatedPosition - pos, new float2(0f, -1f)) - ) - ); - player.RegisterStepRotator(this); } public void UpdateRotation(float value) { - var angleDeg = value * TotalRotationDegrees; - - Target.RotateZ = -angleDeg; - - var pos = Target.RotatedPosition; - foreach (var obj in _rotatingObjectDistances.Keys) { - var (distance, angle) = _rotatingObjectDistances[obj]; - obj.RotateZ = -angleDeg; - obj.RotatedPosition = new float2( - pos.x -distance * math.sin(math.radians(angleDeg + angle)), - pos.y -distance * math.cos(math.radians(angleDeg + angle)) - ); + if (Target != null) { + Target.UpdateRotation(value * TotalRotationDegrees); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta new file mode 100644 index 000000000..c479ca6a5 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 924da895b2d85c6478716801dcc07b7c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs index c6f3a7340..b7d30327f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableApi.cs @@ -40,7 +40,7 @@ public class TableApi : IApi private readonly Dictionary _rampsByName = new Dictionary(); private readonly Dictionary _rubbersByName = new Dictionary(); private readonly Dictionary _spinnersByName = new Dictionary(); - private readonly Dictionary _stepRotatorsByName = new Dictionary(); + private readonly Dictionary _stepRotatorsByName = new Dictionary(); private readonly Dictionary _surfacesByName = new Dictionary(); private readonly Dictionary _teleportersByName = new Dictionary(); private readonly Dictionary _triggersByName = new Dictionary(); @@ -61,7 +61,7 @@ public class TableApi : IApi private readonly Dictionary _rampsByComponent = new Dictionary(); private readonly Dictionary _rubbersByComponent = new Dictionary(); private readonly Dictionary _spinnersByComponent = new Dictionary(); - private readonly Dictionary _stepRotatorsByComponent = new Dictionary(); + private readonly Dictionary _stepRotatorsByComponent = new Dictionary(); private readonly Dictionary _surfacesByComponent = new Dictionary(); private readonly Dictionary _teleportersByComponent = new Dictionary(); private readonly Dictionary _triggersByComponent = new Dictionary(); @@ -201,8 +201,8 @@ public TableApi(Player player) /// /// Name of the step rotator /// Step rotator or `null` if no step rotator with that name exists. - public StepRotatorApi StepRotator(string name) => Get(name); - public StepRotatorApi StepRotator(MonoBehaviour component) => Get(component); + public StepRotatorMechApi StepRotator(string name) => Get(name); + public StepRotatorMechApi StepRotator(MonoBehaviour component) => Get(component); /// /// Returns a drop target by name. @@ -258,7 +258,7 @@ private Dictionary GetNameDictionary(Type t) where T : IApi if (t == typeof(RampApi)) return _rampsByName as Dictionary; if (t == typeof(RubberApi)) return _rubbersByName as Dictionary; if (t == typeof(SpinnerApi)) return _spinnersByName as Dictionary; - if (t == typeof(StepRotatorApi)) return _stepRotatorsByName as Dictionary; + if (t == typeof(StepRotatorMechApi)) return _stepRotatorsByName as Dictionary; if (t == typeof(SurfaceApi)) return _surfacesByName as Dictionary; if (t == typeof(TeleporterApi)) return _teleportersByName as Dictionary; if (t == typeof(TriggerApi)) return _triggersByName as Dictionary; @@ -283,7 +283,7 @@ private Dictionary GetComponentDictionary(Type t) where T : if (t == typeof(RampApi)) return _rampsByComponent as Dictionary; if (t == typeof(RubberApi)) return _rubbersByComponent as Dictionary; if (t == typeof(SpinnerApi)) return _spinnersByComponent as Dictionary; - if (t == typeof(StepRotatorApi)) return _stepRotatorsByComponent as Dictionary; + if (t == typeof(StepRotatorMechApi)) return _stepRotatorsByComponent as Dictionary; if (t == typeof(SurfaceApi)) return _surfacesByComponent as Dictionary; if (t == typeof(TeleporterApi)) return _teleportersByComponent as Dictionary; if (t == typeof(TriggerApi)) return _triggersByComponent as Dictionary; From 753aa1e1b34834cbbba43a6cf4bacda130e4d235 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 30 Oct 2021 21:41:19 +0200 Subject: [PATCH 17/29] rotator: Only update when motor is running. --- .../VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs index 5a3ad7df7..afe055e29 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechApi.cs @@ -69,8 +69,6 @@ void IApi.OnInit(BallManager ballManager) i++; } - _player.OnUpdate += OnUpdate; - Init?.Invoke(this, EventArgs.Empty); } @@ -94,6 +92,7 @@ IApiSwitch IApiSwitchDevice.Switch(string deviceItem) private void OnMotorCoilEnabled() { + _player.OnUpdate += OnUpdate; _component.Target.StartRotating(); _enabled = true; Logger.Info("OnGunMotorCoilEnabled - starting rotation"); @@ -103,6 +102,7 @@ private void OnMotorCoilDisabled() { _enabled = false; Logger.Info("OnGunMotorCoilDisabled - stopping rotation"); + _player.OnUpdate -= OnUpdate; } private void OnUpdate(object sender, EventArgs eventArgs) @@ -156,8 +156,6 @@ private void OnUpdate(object sender, EventArgs eventArgs) void IApi.OnDestroy() { - _player.OnUpdate -= OnUpdate; - Logger.Info($"Destroying {_component.name}"); } } From 0dcffeb2a4fbf8089cf8ad887bd60c56a2154e0e Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 30 Oct 2021 21:54:28 +0200 Subject: [PATCH 18/29] doc: Spelling (thanks benlogan and @DanLShane). --- .../creators-guide/manual/mechanisms/light-groups.md | 2 +- .../creators-guide/manual/mechanisms/teleporters.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md index c8d57803d..9dffbf031 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/light-groups.md @@ -16,7 +16,7 @@ A light group is a component you can add to any GameObject. It's recommended to To create a new light group, select the GameObject you want to add your light group to, and in the inspector click on *Add Component* and choose *Visual Pinball -> Game Item -> Light Group*. -Then use the list control to add and remove lights. There are a few button that make this easier. +Then use the list control to add and remove lights. There are a few buttons that make this easier. ### Add Children diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/teleporters.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/teleporters.md index c8263319e..77397bffe 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/teleporters.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/teleporters.md @@ -6,7 +6,7 @@ description: VPE can teleport the ball from one kicker to another. # Teleporters -Sometimes, it's easier to teleport the ball from one place to another instead of setting up the physical environment to simulate the actual movement. VPE provides a simple component that destroys a ball at kicker A and creates a new one at kicker B. +Sometimes it's easier to teleport the ball from one place to another instead of setting up the physical environment to simulate the actual movement. VPE provides a simple component that destroys a ball at kicker A and creates a new one at kicker B. > [!NOTE] > Please note that you shouldn't be using teleporters when the ball is visible, because it breaks the natural flow of the ball and looks choppy. @@ -26,7 +26,7 @@ In order to create a new teleporter, select the GameObject you want to add it to Once the ball is created at the destination kicker, it can be ejected immediately or after a delay (see next section), or it can stay in the kicker. -If disabled, this option makes the destination kicker keep the ball until it's explicitly ejected trough the kicker's coil. +If disabled, this option makes the destination kicker keep the ball until it's explicitly ejected through the kicker's coil. ### Wait Before Eject From 3e359734a885ec4c51f5a2867d80c21150bc6b1d Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 30 Oct 2021 22:21:13 +0200 Subject: [PATCH 19/29] rotator: Add icons. --- .../Assets/Editor/Icons/large_blue/mech.png | Bin 0 -> 11636 bytes .../Editor/Icons/large_blue/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/large_blue/rotator.png | Bin 0 -> 11374 bytes .../Editor/Icons/large_blue/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/large_gray/mech.png | Bin 0 -> 11695 bytes .../Editor/Icons/large_gray/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/large_gray/rotator.png | Bin 0 -> 11419 bytes .../Editor/Icons/large_gray/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/large_green/mech.png | Bin 0 -> 11674 bytes .../Editor/Icons/large_green/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/large_green/rotator.png | Bin 0 -> 11394 bytes .../Editor/Icons/large_green/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/large_orange/mech.png | Bin 0 -> 11679 bytes .../Editor/Icons/large_orange/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/large_orange/rotator.png | Bin 0 -> 11435 bytes .../Icons/large_orange/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/small_blue/mech.png | Bin 0 -> 884 bytes .../Editor/Icons/small_blue/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/small_blue/rotator.png | Bin 0 -> 754 bytes .../Editor/Icons/small_blue/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/small_gray/mech.png | Bin 0 -> 890 bytes .../Editor/Icons/small_gray/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/small_gray/rotator.png | Bin 0 -> 796 bytes .../Editor/Icons/small_gray/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/small_green/mech.png | Bin 0 -> 887 bytes .../Editor/Icons/small_green/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/small_green/rotator.png | Bin 0 -> 752 bytes .../Editor/Icons/small_green/rotator.png.meta | 122 ++++++++++++++++++ .../Assets/Editor/Icons/small_orange/mech.png | Bin 0 -> 891 bytes .../Editor/Icons/small_orange/mech.png.meta | 122 ++++++++++++++++++ .../Editor/Icons/small_orange/rotator.png | Bin 0 -> 755 bytes .../Icons/small_orange/rotator.png.meta | 122 ++++++++++++++++++ .../VisualPinball.Unity.Editor/Utils/Icons.cs | 6 +- .../VPT/Mech/RotatorComponent.cs.meta | 2 +- .../VPT/Mech/StepRotatorMechComponent.cs.meta | 2 +- 35 files changed, 1959 insertions(+), 3 deletions(-) create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_blue/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_blue/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_gray/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_gray/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_gray/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_gray/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_green/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_green/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_orange/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/large_orange/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_blue/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_blue/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_gray/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_gray/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_green/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_green/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_green/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_green/rotator.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_orange/mech.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_orange/mech.png.meta create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png.meta diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png new file mode 100644 index 0000000000000000000000000000000000000000..7fbba49f9c95e3b0a5889045f986cf244fa17894 GIT binary patch literal 11636 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 J%XEm5{|l~ER0jY6 literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png.meta new file mode 100644 index 000000000..5d155a34d --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: f69f70377ba5a2849b6315198ec065d9 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + 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 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + 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: 0 + 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..fed47cee4085e1c98e7fdb8438156096c4cf5ec3 GIT binary patch literal 11374 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 literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/mech.png.meta new file mode 100644 index 000000000..35e556c25 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 58e68a2b3dc2a2843a6f96ced8716558 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + 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 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + 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: 0 + 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..6b44d324e6929fc87b6a9ca93e734c7945c1a407 GIT binary patch literal 11419 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¹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 AcmMzZ literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/mech.png.meta new file mode 100644 index 000000000..4b2f8b061 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: fe51d69592bb5b148900bd5218081d7f +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + 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 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + 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: 0 + 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..f93aeaf07749ffa5f3dc52e818adcedff8686e24 GIT binary patch literal 11394 zcmdtI`9GB3`#64PvB%g)BHFB3D-~rX`@YMbr95p#$(Ar9gdwym2?nwuFp0Dy%5MFL)K z_-7@imj(auT{LsM41k;D_8(%+Ve=IL^1$5alvB*3xsefP8JB+UG3}7~aSde9-W&yF zkSPA^1)3{~atv9_o4HqW_@Z7H-wq?6G|JvIF8TjA zfVyaLJ-)FM89(d22Hr_e2{et;^ftd`^I3g4T7V<0sij;SQJvWGCf^_v-YoTQ;RyGp zDjMb*=X*M!mQ3>6p5Kj7-qyP}Us55*LUAMz_i4#}m)<&xUBar$b*e&~;TPy6+TMfg zI&yTB7v;^V9yBmh;@^7xNdt1fdXv`w`u4@0gm;oVT<2~)(-AO`>S_j(EkZ%MZJqMUR&gm=sr36eMb z*8EDw$g7Q3A4jxz7|CCJCh~0t4czM;R%nr4>#tV_ibb%kfEVU}cR-3J{tX&D6u!)g z@5VW;Y>+Z|k&0*J-Q|I1&@%P>@gGOaIvell@!K4fKi7c<3X1ORRD0%yfwS1HE2wr6 zZYvQ@WhA(*<^6TkxSKH7)WK^L$(1XLAU>xC65N<`eTrOO2!&Ii(BhJ|H*Es?9-U)# zFN7=W(FwB~Xz*M#gC$hEp=?D?8XH^zM?}CggRcv6LCcN}6}i$?cXQ}`KwNcgo}3{< zZnd=zJsXS{E_6i#YI-X{O0JG!f9Mp^4+b&(9ngwKkoEz)=6f;ekt z+;k>vMfJVUPTn(an5B}<%YjAmXJM!tS2!BWP4~Y%p~Y5vyI6_6yngL;S;niqyq?&_ zgH}0!GUn$LJv?T7l8lYZXuK}r8@|!)F3*&jL7~mK4*<<)AJ>*?M(=Shi#_SYw>wZg zH>`FJQ|sO@BOj-v3j--5!-`D}I{%=hYTXxM+*KY%cWWoDsNm#vc~JExU1P~(bR!{j z#kpll=->y+P$%PMwRrSn4fSoeeg!>%eQ~c>V$Y!RSimDwK5#-s zJ?3~N+nRCMoxL`_{B$Zp_O$-DzSI$vtorVKV9~j{YoW}-yQpc(T_z9fze^+mqgt?= z`5t+y#RQCLsDy3MsOHI`PG^NxGq^FTUQoD52#T%0sE7mvhf7}|ojSvxt?f#u`}umM z&{lklSR^24TxJtq%}Ura&7UvH`&bZZd>{^%d>fU#87JoF=X80W`@owQ+{idu{e?6? z8@;sCxco5haN8Xs*K0vBAT7DPb^mU2klV0n_vvet?7MMgEL!=|ey$oz5a{H;z82!< zaAWYuMW^mNzpgq4T@VG*hrPdMsq8HAH2!_mph)-9*G2mSJpB3d3;FL<$Bvtsfk3kw zrEFEH2g4@6qf3wXWrPmdb9sd1W4ojfg(4DaYnwig3(Cs_<0;lJ1Qs_30HP3~rFi8` z3?n*U`8HM{EL)&J@?xOfJ~T1C!l#6+Y87NN{N|DqbD=~QDONwg643OQ5Qh@JGg9cb zA#|AxEJM%k3FqV=?S2hSdtD&r%&cyy*P4z#sqrmiCe@Z-Xu6Ki=8g~rEpdjud2*{# zoWLxYBHkSqWV$K(Jd6MerDOE4S8|W-Vvdge;f((DMRdN$qltXV4a_0$9U&yOq{)DO5qk<9N`Z=#4}+ZcZi|G=4D`1se0m6F#p7hbQ>e`)31K#`2AFc4;5qEyk}E3_ z)ij2QV)q(@XJ)=sS{&}&>9O~xvIJkH0lW0v&CQaWvPefM@x7zFSV>8bu}fwK_ycGn zzj#yy&03sL#yYi!zC!tOO_GPx6E8RZhPUeCGIiGY6csXHq%O5BoHL)~delxyx%Ll; zzo>EwtH3zHPH0tt(V{VCZY?2c!&7*^L>!V6Cp(am9Q^d{``(|R}}Dz2>9zOb0|_fG#fgliQ!Bt z+T>06O}p<$5S{c)poij@2jYc935bL{T$m%gcp^((;SfUpjqA$&%TzKQBdEL|-JFHW z`1DDdn8}}e+mT&lpYnpQN=b7cLjHCxs_A=7(tSGShUz)trQmIUI77>ju|oX4d~1Vc zLzRB3`$Mu_|t!y2y{TgBT$#2&%I*Bdmzz%{2tFZ<(Mvf& zSAEE*JDez=tPq(>r)$mxNN)|z^UNMx?jI+>y6weM&obok@l!{0m`K)LLQ_@l+8c`frLG}Pewp&6&y023hp-$5}) z#J(GGvg`2Snu4 zVOc5O_OBldCT}-1<|6jbfi}$7CZ#ouL`QIC($@x;{+kc*;XCynzY^U2X(|M;0=Jkd8(60oSSmb?fXVAszy7Wm;N z9Y_0P-%mbgeYGu7n|}L7dViZPAVVDYxB-mrwHlrgX+GyNwmOdi)#Gcfb&6C@$`azoZahQ z)Sdg)ym@Np#ev@!x97<&tfNO(4X6TFy%TR4-(co2MY7Ke41d?j0FJeuq>%}c$*r>C zwX#)=)1FdfsF;4mg+z~E3gD@9ihj1v)pUnWJ=x$bl!W<7pX0q;gTo&Y^8DRFPbO7E zdv`5cE3Y@L!JOUCjstq>cQ?wXjj$QrkqDidx#OUm|HrAsa`n|=vRrVXNvc--1Cl!B zdL2KwIq&e5aY8l7(&u_N#1pWEIzkG))PcASuSOl`tD;hg%7omoxl3HZlIwS$p4f%Q z`kajuEkA<3;0;je~sP= zc^HA{*KCWVls5u5B(WPukR$zkO^cr8ot zY^@{TrPFu{>X)fjyx{tsrTD%()BE5I>gKe=*SQlJc}x>*k6?KyTh(txZc;6-abT5c z0|Sb#?(O@H?{w_TlosqmOer)-Y5IqMO@MENzPO&aTp$c~e0C=y@zYx0$> zz`DiJpYVGdm;^5 zew;@>waRo8nC7OE*NcshQ1CCfKAr!hObj*X0p+A&ukZT^#krLp?E49lNTL@tF`&F3 zc_*eeF3OOHxNMYxF!dM|A%s3pBD_{(XYA!HU#AkxVf=HZPgXsv7`N041ZgSf%j?it ztKY12Se+8!Gzn5qsB4rQG^Srh9OP~em=eO@uX+}LG{RC*QoBWVsWZ;#UOa@7fJ$zQ zz6$1kelIwoB@I4GI|dBW^qOprRUTRiJn8SDw=qS4-7ChG-4s~i#VSG8l?dc?O34z{ zeJ|~T$*%$)mG0hZ*XJylGK*7ykIUy*_v)n2*CL~v%mVh8*=VIDT|6jB>~k6_(pD>TQP{YvR1w? zgzzqomWrHBr*h5DD`uXPS06lMYP`#|Aqs=@D$W6i2l|$%JJmqj9*nYN)Q;{S#|Oi4 z>{8ZQf~oCW)uXHu-TyA%g+UL#4|=^N{-OQV4ClA&7_{>Zs>``X>Y{crbah~3VlNsD zNflonwl#%9U{ANr*$;!|{mts9fZ36;fQN)F{~oNQe+2Q!9u>_7GeA2C!Y->e?r zLer~FG7KKAtXp|R`Uo(}8{gYK^ZEkTN7Mt_3%sxn>?UUH+*>Z4+6&e5NgptmmT7tV zR)1Umdlx~lT#QppHyIqNV>ogVyG@{7qf_?{yxA{nxaux7zkhHbnlT#tq?Y_POrO9x z!BTvn@n3=HTs49Gc`mkEbYSYDqGEOesZ<5b}jnQnl!P*p_1=a zETe#qvFbHE@}ZeSDU}2|BQ%(4J2**=<6fMzv6V~|%I%C=*cpP6c`0aIb=NiAX!uTl z@7F-s zc<*O_44j_k)6aNqdNX)@l$7;=m#-J`)X zoy|ZL8|L^ihUJSPk%c9ii8q@zcv--%{wYF!A|g~YzOlU#D4Red!U*}_ku5WJY=pwz zNS;E=L9S1-ASx76<$%lsrr?-ujnv|XVFu!Cusrq3hDSE%(8xHH+Y+*Dz~ITv`iqb^ zp_bcRj14Rybk9Lwqc?DW6-+onG?1Gk0}J%;!<1;uuzA5=;CaDOQ=sX4y(1e@s%i<+ zPKUpO4e1&bc7o-?MwltPz<0+eP!`&e?c>L(7a)(fy=70E*xt$<(9>rCrm!gWA&&Ad zs6J1|*9{Jq%Q2KTe*vJ?c*1(+-#sohW8ivqWkJ^)gn#}C8?q>bkYCo8^R>>%x8KQ6 znW!AceozIMGIp+?%${?5!4>m(?DEN-fF6g8-azG8&%>&G(Q+)CPrv>45JWd26R@Y* zUel#GjRx|yFl|1t`?FY z0E^RBAdO1I)OD*eN*X?TaVAq>T3f&@cQ%(LzM`vB?k__wp(|HCD2F0p9ef#o6ag+U zCzdBWjpK`8k6eFE=(F2^@$k_xm=4eF+<6KsY6KR|89apw2e^q&rYOumFY|#76%II2 z(x6P7C-i^;E{WcU4c!=G?VUlyl*%0PZz&U1Rp)@BT$%L>I7z~RUCP>^jlFs&luxDy z4p%^)N-e7E0k^9qp}1gxk9b337oh)b%tP)jaeB4PY= z#ZT$F-o)f5Oh~4nI=#m^};X?r$#vqLd24qI3UH2X#X{93cA zbUIBhm*q!!j>N+*L1oPTIxKAae_&USkO&KIa>!D4eECaMm-TtA!X_AlbdjxbX(}P; zM-Om*gpQ%bX>F?gaQikq#gp^Nf%5`CVCc-b7_DZuVWoh}vL zpA38ED2oh41qRrkw-tyHhc#or_6&Ug#(y9p6CDFxoar+4&qf4ks3QJXw6-j9@G{}%RL?uuw){}4Vn#boA+3V=3oQ=|!n#2;;#cF0h-Yy>{whxS z0ZWk{qSKW1M}hz^sBd`HX)5=89xdexuE?my#ayF>fDclc?yS$1TLQai^;JWr;;mHtpH-I z({oZFrwdIYC<1W>lf|REPTYmD>N47vvN$#82jTE0AIq^6_@hl}6D&ktAa`~zWj@>S zss71osjeFdfKMX4m?p*9?a^JK`A-O+IVGn@F=gLb|Ht*4{OYSae~^QZ>$ao_;iD@B#?)bQNvm?atTPZEeBjF=#5Rg%N!v_^ zYspGu#~=3^ZQC=#HV*=8;D>i}1^NjB@Xsl>yfY_sef>LW0a>HX1@K4`gzcQn_?d@% z{Kp=y)+gDqp{L9s;0VC16@$+}>+;6R!R1lrk${4yqH4iQxRxM9cW=5d)DU8KPL!GyqjPRKnR#i+2a?;7w{I zsGszRTNr>C2VW}miWo51_C-{Km*s)z1o$HU=H5w#$CI1L$NpleC6UMo7|yPUO2(Zf zlV+k8l;`c>ZUT|4RLg7+X8Eo6B{1;0u%-A9=2q z7t#rh_r6hkEg2h_O50qhefyS7+#0-@KFeR9)QsZ>B706R;@f(mROVR}$u z5hk!L7z^)g*ar<~Gc)JAl=3AD8=qu6+U>dO2PK_tNM<8SV+)Zh$+a5j}myrThxecu(Xs(os`@I2qBk?DOE?nyz7@tM*0wC6xtq$|u z{}h!>?q2A-z7JCGo`U8_FPBVjFYgE#Hz7ONeD`?wJ=)QH0$SKwh?G;3`=~<9M;si+2b{ECywc?9f#d>2z6TBrOu5k4x3vEY2g3hH1BJcrp9i+x zYA|du5AcI}xZqVV4}E}*$QL*%C>TGs6PAly;@xeBeb2jn;MMYo)6yDr63)0jmm$!A z*x?*;A2Ja=lecNNNl+g5qddi;;l7eYUN8frT=!{p2x|F-NGngwKzDYR%x}NqF6n>t zTu4>n@VP)Z9hSF6AD3AyY}0{V5quF$YeUHnr#VQmb1Pfu)GaEJ5pQh(7gDB=%lfcX z-hClB!bV&WdgpaO0MOz%?Bjit!6xEcNx7Z>hFmqGhPhFT#sXB_s38GPLfE7V_%XFM zJFS>j@5s0swM&5n%N45CnXfLgMzKwVm5EcW$M^d@fr~i={jK$sxX7X5srpwxOdxMb z)OYNg3L{h1^~k@0v$lTxKBikbcN>Cc$TvIWSie8TP9Cx&#}ot=F{AlhK7{@1FX;8d zI|7fi3rFE1#0b|hb9Y_Jiq)7pRQnD4rsV)saB26JZ79o;zdojgCaTuV9ymKBmg>Xr zW4%7HT7NufMspwqgMa6)!r9~89x>D%_uS@dlbWvhu5Lt3o7gfdZyJ+lmhcm0SGnz+ zr4v;C3Jk6~6ev=!&J)^M%L_lBtL~B=^ZuC7pcy(fn9?Yu?Zl z756kYyLqs#%4B_C-}<>;Y7P=V<>*Cj)tQLqoOJkN4M4vn*ShtDaL#jPG?xaaX(5Jb z8r$`(G8HVgvpE`Cm4_ziSp!1cNS&L<(fd_7)l3bl_4Q~Jnz+oo{BiBXnfvyA!j-J5f!Ifs4T?!Cz|=g@m*Gu zkTe%Dwd7Gjy(AcP6JN%Ju#YQ;u6USsthZE`MLDW-%gi2jLyRHPZMoxK3V2qRf@-zxb7H>0(#Ss!7MrI+=$oU1=+ehzwV*e8G(yVQ>a&LrWB z)#1h7w+xuMZJV5?9SarC+jKTWCa#vuA|r%&kil4Ld2Kggx?;S3 z+d74Gba72#cwWm3KXdCaBs8{`+B)`Syqrk}RS$Z-i2H_+iPBWR2{|$SSdn5#qTGf% zu4V2{HKuFlcu@XX-BOVRk5O&jP697uuYFUD6H#sL=sqXBk&M{4oGVY3R5O(|XQN`==gHY~1H>4D$RS2q5rjSn`hq1J5dXP#Evm1mw=1&XUF}1$KlrPpHFxn-# zVHUNWm&@CZs}ucaaI05)jj!*5bVN}fdv4;a35f2^S2H?|OKxD$db%#IU{OYF#~r1} z8FEEna73muDM5?1UCn4ANB4Vg#J&FaEqmMKUP+z|ZikzAon1>T(?^H7a=dCdP(Jgx z{_H}&^C5XSvnb%!7611HBW^ew+l3hP%0kD6x&XUtRxhC@{!X)><42~k`GmA?&U-tL z-CRW3xrO|AQka!R=ZF-qIQ@j_4523~8d0bGYby})hMkfIE7Y_%?|m*(3cr7BrY+!1 zG$YA=X$|YsN^iMUAWBHtsw1mlOg%1lFm)Pt?dtp&W&xzkN{?5y2(ATBIq@#u84O>u zypDbT>xi9gZ+eds>vPl*UVwrzy`P>Z=1upYn_*^g4k-SijNvN>=|z{LVD%q%furuuX8<*epJMyS)ZG8gpRC?-2OA1zeARo zNY&rExj^&Uc7a}5)&m1F6(+Vc4!vL~Ehya%kinVw1{1PQ!#la<{3p-z>9RU&~=avHp zSKJz>vbE~e!AI=!*49goCwf}RXX3Jf#QzV?87*KKcfxDV} zC`EFb*Y0j`93(d*vzRa8M|MaYlkGaL0j@g^ayZWLw@2Hp;_JjbSFDe*=6Pjzj$+CU zmL8QLZH2JHC-xw`ntiuK18$*qk!blp+&S;ENzmabm^nrH#8GELWuh`7#=WS5eg7ac zPgs`7m+r)=rc_Qv5*!{RC5>HT=q;Hu6uIC9m8so}smBh1`E+{%p{jE|rA3DX!LhHH za|~}ohVaERlsEX(mwpbI%T#;qr4pXNy>9rcXP14^`akZj7uV|7)kdhl^1o>BPA!6W z)rW_>mhUMf+&=z`rl+Sq<|dz>JA+eZum5p&c`@y#ZW@m;H zyHbFoo&UR=7jF4?haS9S%ZQgN-sj^^{G3K3CwN2IDFo}IT1)G2`+o@R5+Qem`Hj9i zM{B=#isyzVU-vxuRUxxLg>#`B5eQe0^weXTx}R4}al0lPMXCg?Ypj-S97)iI&k?jiP8J6Rv>J;ecv6IDn`-sCSAeYV;eY5|=R-UsgTdzhM0b4HB>e*&L^z zs(X2JJWEo@hiIKD2qLchZ&zU|FEaBLy_K-5o%L{GG8&%JuxG~qwRPi86l>y^GDykT zoWDHDjS&UR=N4t?)A=T*lxL|osK}q3=rJ$&sP&|)I!tK<(bW0GfvRbhYs8t!aN~_6f>`T(G3B3v&Dnh7 z+3UL`WnX zt;R0~?j0ePRPdrEf0gx!$-aobJ0H67u&F`ug8@!Q0aVrUMWIf6wb4y|3;pa@FbY5D zJp^aEpu(|D@Nt$c5L6a(}ItluuY2HF_WR zd$Q9LmjsXa^tMIupBUF^#glK3IYi6n4^|L_QwS;o8@tb@;F91OBhMGF=gZJ7VY7Yj z-1$&-SRtjlnRn^g<=IOMm#@kd!$(**2g%t++#J(o;V;S-jkym+ofMIG5lE=O;f~rf zQ_M~F0L{jRQ*#YBgA&_^P2^n#xxQsjKgoxJ$BR?$an2rG+Q)|=?mr#swAro7QH)ki z9CM7b(v~tRB8bR+Ay;l>rEA5(!%%UW=i;_!tw?jle8Z}mb^e;nh=o7(4(svl>uW{X zE{!KDx(2u zKd+_;KaMI2lpf0COG3|H$Y`~#hR4)|qj$)-U$XgqaK2a?7T$C(RI1=(9^0R<{AvjE zn?`CdNKJTQm(xOAnb4N;NqAt)ao_d`-1{R7PgH&Kf$~A@TK=)O*$?@Em5CLm`SMrI zeQ!xm!V(J1g+Z>27lJ#Y{`EszV1c;^$gR2iidc(0Pv2{@DY>fUh6LZz4si+1%8#jr z!q@pN=&K2XKpQI&8D7*2@vX~h3GVwq*J0m6bCUk{oFDVUJI%$94-?J*O|EE2#50os z@D>r^z3)3#GElBk|K%DD>U*Tmz6(G|+HD?MTdFLVzkAlw<~70yD+6IG0(=DEDqNq1 zyrx&q88QxvEg91zVRiNc3OKEcuC&^j>G$&oS*JDCmltT>ay48G9ye5S?SzNf#(AM% zGWt7g0WsNcx)5@W+HvM^oF`+%q6iXCIio5kDgp|D$c~m9c8eF1lfx9djx^k$x3H%& zw!-*N{#68cEu#|NpdZe}vaMv=4ox!LK~M0bT{>#x_Q;41BKre-s|-ApigX literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png.meta new file mode 100644 index 000000000..d4e1ebfb1 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/rotator.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 27156fa6734746944be8b19026ac83db +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + 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 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + 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: 0 + 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8d01dd7fde6212d09efc4b6c19262d191291c9 GIT binary patch literal 11679 zcmch7cRba9^#AJ)?iDghR@_U-CNmlL3Xz#nA#y7#BV=aXYc!~=$|x%#WRuWvHSCa5 zq`J3Z3)%DD-^=Iwdpv%B{{H*^;f>dRopWC2JkLltVQI{{OK2AWfb+PCp$z~?_$Lxz zS>dmxuumKC7n{F{Lm&VS`*wa1D>j>z0N}xKLw&oj?7!b#Y=k@0+y5=a(*leYKsZak zuynVa@=N4zJ^{j)xPJz^!d29TUGk?$C7c4M`aKbRUmOv8*o=>1w`Go>bhxq*N#HT0 zWlc6I?ke9J?d-XuG&?)HAAssn=Gqk$AaNn!3F5HuBVY(WRQ%5QSb5+_`3C$@^wIFk zAr3zz?*Emc`59aNAFej{BxeU!{b>nTUNr7u%vI$Cwu};JT^ZepQ)#$eAZYsC_wOQA z0=Ea<7^bWNnyS@Sm|$k`(65mfsT5^SOv~ekVsd5P(O#fh7YT%(bVp8PNAQ0Tjxcx% zK*JkR;PVS#`z-hqorVDNL_n1~YrN8Mo?Qauvm(Ilj^bBCClG+5zytd2Zq@xAlI8uc z!@@{wq_R8P4S*pe0$6a0N7>4w0CgW0c*=gx(tzY?fNMDpYstf6u3dZVNaa= z|MU>|9OA02IVA<~UTC1kvn>ByanW8=8cD2&3y7~g3fWog+JApv(_TUX3MUV!wkcGg zj9bIQ?EI5-ZCG6)20=1FgYrk`LoCclV2A?&ragzx^{D{hLjjcF`krz&$X)~zq$j>$ znb(2*+-X0l##shgIRp^kiKmd=MK%r~j0YsXlXYH>NE9&;u2}HbQ=}V!e#qx1p5E04 zN&tA?0Fp`wD3jjjloj1KKS>LISf4g|f?wMn5gqe`XM=3|-7q{q{Z0&T~8yb%e zFSuF))a)|W69Q*50%t1%XC3w8ZHq5!zb=u*ZrNx4qQyXBVM6Bb!R{KX#AU}99y>6d670;kH8Tku-A9LlM#q(u8UNs zYkc_a;pj2Y$wF!F>B|WuWGo%Yy!N3^RFso)en-aF))nw^8zy?B2WwNSn8Io)xs$h< zM)T4mjU9=y34%3uSFfvl^!xQ%EA;g%04z=*?DG{DZLQt5Bq{K-K;8W{c0d%TKfV7GCLgFHysj&(JGzq0VE`!;>G=nS9rI z4JQ+^BuOMCrFOM!nPCxG*6kFiPF$9EoTeYV_jr0=lIlj+pp{k=60q|C(JzjEn)1&o zRlh%s`ug5B)HH(4VMK)W)q+}D*GH*t%jw8FiTi-gh3Gx`J!!pX81$S_$p>`>KB5wX z$uW^42QSzCxMvr1%)#2(cwUFSljqer3a#aW1_qhwb_8_LnH8-qU#}&^Q>^m|ev-q| z8h@oT!Z9xsvL^(LmKZL_=Y1}8mL>?ZAxcB5(fujIfIDnHZ*auAB1@lHI6Au9cdL&y zbXp)qch?(HSp_{T=lRMT0Nd^gCIaU3?sxdi)J_vj-0y$Xrx{jlahl7a#vig8U7T7J zdz<$ZEe5E%fe50z3*kW6-Y;&8_^iKqopjph#%0+yn>1X=(&A8ksa3`$33T;Apxk2z zQn-G0cd)*#u|qj#rFItA+PZI^d2ouQ_p75)#w+fT-08V4i&J(^5@-Q;DbSO>m>m3f zGw_t%8BOjqx7&4wA@l9Op4*`t9LrK=dz61EujtCjhO>ZU4!c0cF86nuwffZo>XaD5 z4Y^XR+HT|k^QU?1LU((4N$-KM-5@3l3kEnOKLtE-ws5U>Sbx|%uv(A;UFy$1<{+tdDj=kPEMeZhp3~L(^$U;p zJVYU!MPcUdQMiL9K0PJh(O7!Y@fEc(3seDWd2N^=FOK`$* z;?H&FAXD}=pXSlom4DJ(Qw(|IbXgRzblry{sq#DRMh~U9f|Ox)P7BKW6B`(g;|+WA zeMrqW=+;2N^6&~~DD|=&soe(j4~3E^_g1l2AD9IAIziL>RIdx^Js`rS3LMTFjJ!>^ zrr2A<`KEf{MqQTo8HCQpeZE1-IaKw*rfy1ls&H>~+jxy0z^m$XpjoAMQP2mv=)fOXfco;7`~4Bqsx07Z`2fS^s3f8Ti)3VR$$ zkbfIAdZ++wGy1iV;s#>=`Y@ng)mOeYK%g9yKMfj{vFZJPxu|=JQ52F4DE(c41AUoX zz)2wfZNLXE%GDkWXuqm&B3wlN@0|vmnD!bA1%MUWDj zd$xB^y^+T4wx7vdMdr&Osh7XqXZ77Zt>VE3hzFJs_C!6y7Nb=XH9Q(eMSN$1asw99 zp%80>C%WNGlxHkZHg;!jTI=qu&idHgEBITuMmZ zlnR0V7)ug_+G&_jCsi0v^8jX03yG+18lSfuVh6B`dLV>xKz)VuP*ry%l}m_Po{>(wHj^^O`t;IT z?$Vcq(aix`*OwQQ)IV~Es!G~|+LTaEQX4N3L$QI4@`t1qM+I$~_Tha;etCZmWW-3i*)yG@QqNuSB+dLMehVxUGGn;#qN`bGN z&Ka3}M^Gp>1*qk(Z22!=`oXat)hs37aLo&mZ?A@+I7FRzwL~WUyR-)7o8_pBc-UP# zYXxu@D+)3PjekBWRwVmgkjdw_W2bbW*+4DgrbE>JHpVGJw-T|;V#i`k7Z1j!7QK`v z${(3S;Wg)?QUv+q$jlyE9KOy5dNb6jVF@Ckia5(qaFV&K07hfaNQsD zKZK}h`}YLR>qQ=;#tw`Ad=aE1c~9U_-*#Gf0EJot3jKWk4dijw5ReotXAH23Vm2oD z>LQ8E#%wn1Es&zuHR5e@HU2gQn4e<KMc&^&eJDEZ89fH52DGCqPe65uO+VLj(b z3FYmtzeDjisvNAd%F!LiI=s7>t+z&_nCo4Mi9hf}bT~U4*tdZyW6|Hb5b*hA#(GoW ztN}VXtzg(;TRk)e1l*Jb@&-@y_eB35qz>3nwnVpY(XHz*oZ^xoLSqo`r7zbb&tfvk zOs6**Y3#Yp+<4^q_0tjtktE9g0k ziR5WjecN@Nk|#9BF>k!>`ivmVICTJxQWgelz-i75;&d$nPPf$2DezMjk1POKU_V*m z2W&Ie1Inw8yKj#V{7hyrrk(v)^?-*u=)llV?kGlP^G*Wwl2gc|YyxU*@}iYO+zQVzPGpTI*NV zi*DEoY5Bg-U_Ns0MR2DN1G*^dz0@9`8eN*fkE+(><_Qs{O|D{>Z(P6_w<>FnlgrMt4lpOXEP@)}t(8Q4GgMMV6~k(Jw;`McEt!R+T5d)h2zDAy4-B ztvl|;7F%3zd|GqHy)v4$zsKyy$8C4wCG1S^Ot@~h!uHQ!IACS3$D;i1A}E}VeZGz8 zQN@8jasMLF$;hCq!vu;41QPmupj7Dkkuo+SKay&|M^Eden0yi>*n^wgK=hw}!l$)i z!l1$q&WN`|v{~zL^zS~ZKq&u>s?pnDO$}{kAUqR6 z_1C#f-0sXbb8?h6Ea+Yj{*7P zdn5&FhXTb#RPTucViEuM|77Xy1L{>pWh|f%lER%CJw_I2eFS{9?@U3r=bRpZ+yBYl z+zZ>BAxxtlEk*%j2I=iXyCy*An&tzh%<5lv?4DEZO-Nn5K5R)@Fk`638Y7N21@v-G z@ZNB9{W!tDPS~VnbWifPXigpy1cggviz>3flDeF%B`aNU-RXsMfRL9CKlaj4=!&6hIJ2Kfo;B$zRdj@vfJXr6_Cn(JM)qXj+ zPOLX^9(xv}VYn$7s~StxF-HTLZD-pJOfJOuvI&7Sl{?EWa}8TnCMw(&;+zFXLys=& z)1AtY`xRzV?*V^{H#kyhvV(Pwccd=w>|;rWhI>sXx*%A5Xped--rtgY!gv9Z|2UrA zQzg|F;guB7Ne^!LS@X15&%@|mHP)8XjuU#I%^F20I)IR_{tMe?R z4h1sRQg12vV5Al7U9rhJVUl89(_pztS5DgnY$|`J6G)-08A#XPr(NKHv+b|+Yagmg zzVC12(TQU#Jqawu!&vbw(JLZ_yXcytrgfD{JX|NNFQxdH${snCz3vygK*r0kQjVkh z5T2{LhAvxKixWbYGoE@vC8e?Y>x7g-BtHtIo|weyrM0q(IS5`KZ`wmIt<8F;5`AptUQW%2=9H?vFXl>_FB`ZbTdZpq zw|Slq1kL!dMl>Q#H9*e{rhkh)_`tLJ9CxEprV4pX$wY0dZw$KH?Tl`%)vATO+q9)G z-5Vh{-d_{B!I(u)u08%aL@ReyA|+aP@lhr>3O-xJFiZyyp04lWE5gY8EqTeSjy@W; zDdIREZM=E!^_pe_BjlTovO$WJ-pg>&1Q z`uLKB_}Vx(RX5z{+5(}Dxd-W9)WdK(Uw!4~vEb0x92NY6H3;SWKPbw_r!w~ zh;^$L!v^_@n<-(sj}GgsM+r+0#rBoEPf5e-z;Pd%ILqpwP zIZSHo`|ZL(VAmdYTYciQre*u-uUrVj<8s4vb7T1)a^g}%6zxkhQ#?3}d8*M}ciB4D zrK(UU;K`aUgYP(Az15I~)P{fkeTl#JgWqG`33DU5QdZ4nFOqh&!UpYR5yQe{ke-Jn zi{dT_ZCxGv?c$MIzf13=&YG3L)*F?R2>;{?W2FOYW-6%;WRD`Nru~!g>}j9xn+UE( zhJ18;;4V}fd4s;WE!tY#;!zm1tx((kHJ)ZZd8fGhIsyNHc*>I$ z@2Z7X|MD6cllI7Ex4oC8Z zJ^E#V`J{Ao?!~>i-G5aDffHGbBtG5svVyN`HA*~&dS0M01rhe?bdz-%0>*z-HFbkG zyvr3AN6^tqv<^bDt3Tl@iS$f9F=}smCMTE;P~Ok7L2ZIJQfqhmvxzgo_cwco#g#LpL;;z>V2f=xtlDlf&?STLOWaSt4c50hNlz8Dw z!RyWAbNN^L4Nc3+VpDp6ZR1<^2M-Y^xS$RDV-WVm%(-o$HKMFByJ$n{(LjGN^V{WH zGmA;glbXIu=@Y4+D=9;w3X}*U$UHE<@GK|c>AZSC>R=bjoVUnuT|_6XsIXY?@w~Ql3)v~cm;GTC22GK4 zb)n|BV&<#`2-TaV+JgTsdMp>^@TnyB@c$Em(Gj#|QbZ4@eR=4t`Z_?%T zg0pb{l3v4Lw}^tOw+3&~&0~i`_M2k!m55*`j_yWsE6!9*NBI0RL(-;Hp7l=`oZe|K zs_*3`Hm?nTU8+=E^j={Jx(=i3YCYu#6V7(H=9Xh@5!KTtEZM4+@=-g(&#_Si&&Rro ztl5~puJ%rjLVSliN=hokI54elMGlcK%wNx(WyhoIVMtx-eBQZ0Bwmc6)5yV~>fn&Q zVRC=*AU|OU1`F~yzlTsZWl6Es% zj&%{({$r3cP5$s{wY;RjkvAp1Et-n!U_Exwti-rBTj*X!jV^b=-oS*MeN;BmeEYtw zL;7DluqhweKI$`<)5!1VTm-sW`1xdkXCX{RkgW?Iu#i+a7(>?8eK4-h<%-YSz45@u zW?NIP3`SCH5JX!|RmqCXYQ%{{tmOLqI#Y_8m{3pT-vz> z1({Fbd3=M%4x$Uo$vUdtbIq%72@cjq0hbeYf&0z3ltDkzUQBD>J9@I>njWI%bj8!Y zyUUN`7}4~+w^!tak)bGeVkjE zeX;MfcI40|<=>fC#i*^PTYb+CHGI4k<5RAEudJh@r{PhU%gh=keJU8Y2LW=@;x|nh zw@~YsRpXj3kZ8^j=E;Sqx-9c?3@u^GpZ@#st^s=dqam5+T;AI`R!R!gH<$lS9C4DY7&$9ptr1ynIMQI3p zGgY=9N_1GczV#=R-Se%cdIaCbNtpY4_qlxgx=QYM7re_GY%Y7h#M)Vq4nUFX3hoX5 zxsr?T*=(eJpm?j60L)*8e_-J;Y4^U^BnneeC!`CYt_7@OV?7dx7S zeHY%MMOlG&)@hx=RmT88Pat{6fp9)oK?(rUc;mRk8yFa>0sekeZ(uG4K%+6tcuhv` zbWc!c$<$oSI06FP$W~(_NZpXvM|9U0(_EzS;b=fjN^oKJB~H~&&t>ic+yo>by}24V zi+@X>D*-^Zf0Gb(VfE)b_#Wq z2n)=f#Hm$Q+QgmVbIxN`5*+)x32f-*Z->V%YYQCtHjcn!{yGLs+6`H^7BvVnk~+8k zcz;!!vO~7!E@y}~RXO`Dof(Ch&71|8v3jDoXqFB>E!J(T5{Q0>KhhHHSd&KkieDO}RM6_4k>Eho4YceNENZ(?EL-OZbM$w&6cEatsi zp$BK>buNI1wK$vs+~?{A3h{+K^Gkc*Y|{DtTi@SABbZLc3)$FqzopHC#jUg&Htrij z2x3NwduX?-(P*;p?n0q?ncm0dU8fy(O}+d>NR|Y6o8T;3t^1Q$rFE{2!);IHwA_!J zKc2s5kJb;(#ASp75{OG^Hwte34m}`A4vuSyC}DLnEt-^yZ9m?5FKFpz$b6_Bpo9&& z%|3sT6S#*|f5~+W=X^2LmiKU!z~N8Jf2=q5Pm88pK?D3{n_}JZH(Iit91PodO3v-k zu)ObB}@<``i`4K%hL`HCcNa?i))ol*L9Y0cYo z!^(+QO*kR?5lt2fsMs)0xzNJj-*4j*P3Wle*sH7a$)8&KM4UV6`v_Ro7lMioGz?N z*vu=~HIFd^An5)uZa#M#;mvt~{Hi|;$91pX6+XE*2)?_*r8ZIfp7($IS;4-br#P_L ztC42+MghK*yYlSW$GJ&ls;z9qa}0^`(nNSx0?zuq)nmDWd2xWju>g>Ams^$AM;IZl+uIU>yztQiD+e6?^UrMw*b7wU zzpb{g|FAj&waA#{;Eb!m(lO7rg$UDP5oH7bq5&%{g57g6Ss_#yDGCdE|J*Zv^)9Th z06MkN$O4%kwn#Yf|5w^TedJmh{?}gR`@H-C0H_C$MAgoqH+8pmPB$v0)Z_EuO2tnx z?KE?KLztDnl0NcccF@g|kHiAcg~-}Du{DeR`vu?Bgbsp2hmPBa6-kaY9$a2ww z)D6egJqvo}8O)4-bEC%2V;T2XFpvfEX+4(XA}3hFOnpnW^CuaUK+a7Rq|d%{I7*=3 zFvlA4N8IQv`N0N$6IHM~b!Wj7h%jpTYU>pau7|g1xOt-$jA2u@A*?UJ@=-8z<#Q}4 zb4*_t*t-WiZDHq0YjJotDLGk~LYh7a+?jvp&7_&{>IoF6xdpWb)PBDI79q0fcA)ABY45EoOu6L|4*tg#-4h?|j{yI=f%kLP!v`UJhfJ=1*;)pYWz z84FA(uTxvx=UqCuVjp4io|qZ1GJ8e37!+&VCJJh8|AF46J$~ma<{YdgX}iCyHXX#S z{Hm*>UY%+?4l7yiIJBpz{t&(bLB@#>izU!)z{5p18OM?KGT zBn&_t+3pApM+D$j>g{NcT4BKvGg-9qn(_3}SmLzs2{u3xgPvc%O8Oc63zSOSc~+noWO%oPs5xI%k;-DYl|$cKlv*u1(gN&aDQ-HIYB7; z`$~Bq9{*)Mw}zweEA;Ivl$4;BH=+1U2wnDz2s&$4wJMwym3-7^?_C;(@OxaNMJ?#E zt|lKKYGwVrow@u3rq=FpAuC)ICEOS=mc}(XQAk5W9c-?#@!^A2mE7>R(UwZh(%l{M zw2Z5m1)~IlGx;MMH&wUkt%uZBM zLIsX52iDu{?MR$*{MwDIe)Ms78r1H|mL4cjU;t$z*$vwIo0TKS_5dJh&kD>ZU;KhF zA?TbSFrhWx{J~tr0*d1J)(grX7Kd4=1QI*4=)%$N#9FC`v3d}eTz^j?HeYl0V?2QJ zy;02tOiow3=|S^yOCII)FA$Zs__F(R%g!p6u!?6=i+x=L9xvxutpk=Q6x+)XfomMs zV5{K=Lr&$7J^!cW#BDb~Qgx{-I09_|#2tR)--O4%c@Q>dueeyu54WuTnw?c#%45ry zgh{#Xqh}ZOVxC3{|9uo}JOdp9|K9bgn5{)Tl~x0TZ^7aJ`o!U#VGd#Y`X{UCYg9*V z&RW%0y60FP!`rX@H2EIiV16IFqtYPu%xmX3a1mYvbNfj zdF@Lw1XrAt!_?~vs|kx$%NjeKcushX*>kwH0HoF0qgu>E$7u~ovrs(_r*3uHOFS%O zOBrtSWuHzZnug0&c~cJ&@d%YS5bLpR{Yz~nt3ye=0duxIKX~ZLXQP69x2Ll=&3ezE zoEOKZ;+)GK@tFbQQ5Y{4PKsV$6R{{Xc$jy^S<&l~O-7T;qR!0tKN9t}-^v@V@Ru1i z1UzMI>smo`PYdf0?l5(cCE-h+LMB8!I^S1wVe=|0R0IiZ)nfDR?$=rdqtG^`jjkWX z9o14T$*pImyr8V`!V;yLz*%E6+Hlwr`es)28RIf6!ZQ`Og^3a^{dfZ!c(3cbWA0=j zS}r<}<>Em-rIl<3iC`@pf_;(Y(9RVel*mmHQC^#as!gI?F_6aG`WQRs@Aw(IOfN%B z51>ix91LwJE(s8(oeEFw+r9R|_pUgagyLW*Kx!o{K1QCOzWRzo3~(y1J&OkbFB=ca zsdSpSBLa0JF|rZY*=9(ZI(BL0!n_|F0uX~-lV4M~L4c$xna|9@u^OJn3$ny4>Bm1? zm2Q`r!1LRZK{2}krKIyZGa}m5-FNVk&@_}oL#xUo3}cO$)_sbrlUP_xwySo1n)o^H z90rkpU)*vaYH^@n9FBRK$#dVpAb;P^%wy}c^f$1e z!GX-?3yo|4Em*O5GA|t(GtV{>iK`nZ{MWjRq8tC}e_GikSAJm4w}J_;(e0lE?Qz9XLd#c`esj!~gK=Ab?~|ElVci$&_Jx}SB`K!4 z0cV2&3!t33p8?uneEt3Q+_8s@d6;Gk&LRi*Mq!sdLg@Bo!b7|f@CLypYr|F>(J%{V z>1IVaC6AhcVn-AiJqV!h+Uzqwchz4!f>kZNVyWqoKDHA1hR;O+N!<+zqBhWo_D|;< zoWth+7QCW(dvwX*E7C6+?s~xYAh=ZcNmOAtb4`8B!zosln!ZCp>u@ApE!M-{Z1MvU z`Cn80m7lU$+%LFIv-8f)@Y%hXutg{Tx*a8L-B7CH zXb0(9P_$L$^_wPp**Ab@j5=7G_lwfnq6=C@}N znT2Ybd1-ez9_sQbnN?>QNFc&Fn~=@_PCnkOz2 zQdOs<%!Z(%kj%iDG5YvbMsOD|C1meB$30UjxDJOE{hR0Lb;}~taLgSp+#j-Q|LJ%L zg!d13>ZMdYI}^>kkhT3lbi%c1G*jON2j?vo9#P)*YOeX~^11#YblV}O!Dm+R_V@*< z2G4L^gh!(ashY8VB#Q>#;`|C9GOQ+k{@)jV{`YH&g~q=zQP$5LwA=j0p}PaAV`OPq Jd6X3Ue*pYfe3t+K literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png.meta new file mode 100644 index 000000000..2a4e42b86 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 0a4f50b173346084492196eaccfd5bcd +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + 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 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + 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: 0 + 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..1559ee3e3829a115bbf1339450f4e2576403c204 GIT binary patch literal 11435 zcmds7`9G9l*S}`5@4J#E%935l&Q#Xyp=6Il*~^w~hP2qC4P_^!vX!M0Gh`H%u@tgL zwk$)o!OYz6?Rh@$Kk@u9pPBofYdPm!=bY=D?>R{}Rwf**2U!6C9A>75=Kw&$Kaqf$ z5&qhU{ILsvu>_l5yaB*P!Tk?n>)c)y0208=@RWUI?(*1}?V$_1Op{yq808=yTpf?8 zmB;fmj3%;(Ikoz|%7<*f5@vQD1;ZK)@(!~CGTVtKp8j*Vahd0|iuhPLX<=R0;c>H@ zb3eN{nf!5XvB|nXUmajv>7+ps@K*`|%xyFPFEU^<1b6``JPng)Zwdg8l?4Es!w3M5 z9r@oFv^y>&TIQo#K*{%Z25Mr9&?H6Ho#2JvWd*rR*#@@NYF_7&z}nI#CAaqU3~Q zQfpR2<|h9gyXAB1B@4)~;oq(L5`k50{u1lleGB_)9e3>&rsvq^(5EL@2;;}EjThA(aUL%}8{YD04R1P)a zg{0zc7d#wo|ACXhG@!v7zS~#>egEl_%L(8YFZgn&jg-iOyles_*i=7bpRXPdf!}ij z;U~mc`)u{+o(yUS@{p-x>e~0x~K13}|KFhvyGlT?fD(Guh zPgNUG4hGIhY9kXsCkr?tAhRtx@)>%umGSgdsV9BVr0Q;W`)>;ah1<7Rrf*l|ndE8O z-1~tB5AQwj9{CIX(xKnZkzJZIXNbFA(puVmT8Pz+g>gGdh>2CpGrN@qum`FZESs!V z)3Rum{JXDKO19iL%v%fD(8{lwc1CHx8f`L);87jA(A>`0de!`(+tLMre#5Utg#!!4 zDa^n|<8}NTCX+*XXIkwbtAyBCZ|-E$@w&a3O(n4n-2b z(!Fh0QcX3AC}cxb%j{kq?|4kGWh9+$MT3(kf7{}V4G;o}@hRDYJsciVNFefb{sp9v z+_HRu?=3&$mb9Q_7-}dR5S*kn|KjhH=7x?fYPWB%y2V&{BZ>lnA0Kf1<*7fC=6P}V z1>SE>{Fj$&+Yx+NYH&P9ZKHKq+&a-*zq{_XLD0-v5^JCW#rp zciWna>KQLFAqiHWv^mooLwJise$Es7id$)?2l2VYVPL=&a|%qdF*H(xJarx`#;|){ z&BG*}B0L7}@RH`a(@^v!^1U|ebGMlgc1Wrbn0!l&33nNCDQ_daVC6)Hv#dS^Ht#7% z!`*uFXjb}C^0yh|7qfY5Fb$qa4AHX%`k3MFB)I*czEon>@34Etme1cOVTes$aLi$- zndX$qFBQ&lNbp4I)_}cJ9va80IQ#a~X=6DdTS}UZtkt38F^*s2Ps`{qWkny*Wny6g z0!e~1wg{<5IkMF56XlV;0XhmEewFLUV^YArxR4k}{EpzL;dU)S(v**c$uc>0-4RbPTmTmKv!8dz+h0sSivtzhzn2kM{4Lu$pePntn2&?*9wt+qW z$_G0_{3$+3U}0EBoFMAe6b;F(I^Q7|aI5ej3FymFPsm}Zy8%)O97L=9X?D^oH&U7) zNoezSqFOH1+1BuQdUoPuWqLxqB0ja^0g5Re1P@I?_B)! znM)o|tyq{5x78SI3fWVji%ZKFsFyJ2c1W$f10LFiHrn8JXAK2EqV-Pq+oV?EG1#xF zyd7b{)=rlzATg)Zf_1PuPuWK_Fs3clDc zz4~56!PnO_b@8UGUS~#-yd=)tLF3qo^zmBKHopFuP?vx~lR*dRUv|EZtgQ6_xGeps znLfMXA*nWO^rxrh8#6f2`G?72?QT-1ESg$(BlxEFdyRKCNQ|^+(KjZdxNri3G2$XB zgUcYB;E}1sd+>%O;gO~kp&Q2zPyOOS#7}Vvd^v*FO)E(`@D!-Laoo_DO~w+%k3BSF zjy%95s4D3Pj32k==1y@v>AGx*Gm4ku1;)HLJT6F$PnJ^7%%~km{5Yibj%u?v=*eCN zI&SFx+ssQguvfaZfRhbl835m&nL&TfUCLp>qreFi%>|a^+DO;Kz50a;H4JaZx(<`QN${nL|!F| z4>?j-hD5JMdo-QzvL_JyQHN|^`!*lEx)IbQUQ_>|Q3>*cgNxA%A;Q|=leSm|1%H7W zuit*^lve=P2>}%egolJw16?_sQ)$~I($%Bb6QkPEjVmuh-jM?A5>Aj;O#gU$t|+G3 z(}y6Og2cLg^Qn$e-;)9C)4tCKpeiFQ0oUQgKhOOZ!3^|LP}q)l^Pd?E2!&I_p{lt%d6^OS;MU9C~OJ!6S7beTS&_$huw9N=+qUWaDGn>aWS+%XT88>inkIzk@P)&l@Ck;Rdq!ewV$`P;ZxVbGY4rHlsdef- z-lv=$n@~O75z4b~*JWc2xK8XUmJ-r>cN-_fzgRV?2Pc*!gdrMg&VzfEfp{6m)h+35 zhy2=7)y*WV6NKqL$7PfMSS%IcNEhOf65&~c7E4VB5hsU# z8nhI`a8_{p$#Y#1jF1P%2pVVK4@lG0c-u6UU4yz+u58@nTbiyob^k-4STpZvRQI8l zLOByikdAny(w2a6#R&vNAFz2t#3i}Sxp}41`C0@CJ)C?>%s&_$d(R^ztjf&a2nYX} z7PAxl!;K@q5Jzi*WQ` z&FI)r@GNl~^_=cm=KooIre-;)g3{PJx9O?Rhs5cZQk$=x-9$8_ai(E%v*G#emNBLrG;Vc;2y5~Ib_LnioTo<=JkRSpI^{>`fyMMmm~4MO6l zHm*Fx4H}ptnblL27cw8q(t{XdSl~g%FLRKJj4o=X;9l=?CpUIWZ}(t*`9YfdbKMNd zQ)^`1By~iedQE0K+m~ez)-&+gJap1tnsj|=Ot*y!= zb$cKrIB%NP)|8}YcCXyo`#tzmY)>b$%k@LA@=w*AuZh4TfW_)jK)LX^hUI#RS3KnN zg%e}f<4NdDrITIebOd3#+PU+l!?kzI;WSlz-=%w%J8eEg!<^)HckJnun3fLj8O+z2 zO2fO4?u-z$qegj$!g80RbqK?Rcdl{%b1p} zQRL!qonvF`@2qz_WCs!Hj+QHW5fd_zEz^clOO_*)3M9d!1X2&$FfUT1pC3l%XgW=Q zdpJTFi+Wv;Zz1c)?w+8e!0`uH($)~(buPTwk!5$TCVk_VM+9A-PWaKzS$0=YwQ+s7 z+-YybtQX-cSwwp_8MZZW=&DdN#d?iN`Y$CP1oJ>tdq2Fm9UCavj;N|7N;!aez0!_S zeUk$MQ2hsGz@?cs5hOlcUEZ*=socqrdFy0eu1vmgF-A~+7Rs5tIs6EU(yDsbzn&Hs zYeS89tJKQ&LY)ahxn#b?ydF-lQr{(SuAisIAC#qkG8Y8Jrbpf{T~aNmJU5!+2g%dH z#!+vk!p_%hJ$}w~k?vim*J=b;`pCPq=@SB2-04zIP;s9zj@IMN)qB{qHnJ+K7s5sC z1QjdNtC!w0$U64{?i<>JNp34eN|@wcY`+OGjm#q-j@H(gtU@)YCVO6@- zTra5}So3`xC_zYc%Y1>O5(g^cuUa^4mG6$O4x+2q=C|P8b4e8goULW`C}5uuW~*Z+SSh?s27qKSxqP4IFho z^HrV9P+Hl0J8g{TU*tOf+t>i;e=SAzSQN6YI@pHgDTDXCV0YbU&wY(a!sizn=g1xz zB+|@Xkm5HwgMIePm97l$kva1N;mf*$jM0a%3&!}t1G;u^LrW&QBKUyK-F@|b{8Q#b zHc9_VsA>1h?x_|8EDM{&QEgyZN%*vbDHIgP(Ah^kM4_< z1^&yY{^LX`8h4P3;d+(<=5)%=Yd!mA5;11$3d66;wVe(=Of;V{zivH*+ zQ2r{j58->viTwoyb)+tvw3U^N^~2xU#%J%J&o3*~=;c+}GylJus0%%OPM|<(_N|Z| zq#aYxdi-?>2f`q0{heP(i8$u8Ep;MhT41|eU%;l1A9MWo7)XZ6NQbYcl^;SVK>^^Y zI%&k1-X}v8mw3?dqS}V)Y(31aGE;4SRTOu{GX*g=!lCuLTG{! zr_ZK7M{mFzNSYI&ooCgQX?rd&`P%6+LvX9(0z!Y{xrbq5?jnz>!keDmQ zqBmTJyqdm>OB4-##n!{4^pfn5GUY#D?>v-=iP%9M;U_{L%QxYq{fbpd&1`pA3t-uF zp*kyq1k@0@{h?>cJ?oHyyxUvrzop^ zD7ZKEY?|TJ{`+}&r8BdJj4*3r;Itxl8p5^mx3tg2G@trm&24luAwx# zSzlH4Sc4B|*^DuLN(qZgoH|!vPsAU8szl(2Rg}>cN7C4pql2rjBTFW@(YV}pgpQXY zW9a^hQi71QAgbkA+hFuDScK1&a8q0vJzwD2NfUcUZcH%+j@fHIZCNpmu(f5cZi1Kf zGih;WC?*U2Esx+xIDhZ0q4M^5SZq?CrY0N$_bZtRzL6phjdM42$y)j#;I zsaJVBhY2Bl310B-8xhxJNlT@DHBZT>Yr;l&#NK8tXzD;pvxIN5D6XuWh}&0~+X9eP zZ9jPAj2^o9%}2bEB7IGH^OqeJ(PtV{VLg%T!mjy~6Z6(cXma?AxI|uC3fAuLQ-CC7 zxy-R;^q`ZmQgFg+ygKNo`($4lhRN7Pvlu0#r`MSZFM63z!rnQf+caW_tKuC{`DKLr zK2dHO8+lo^P=p3}yZ#vC4^xjf6boJI6U7Pl8ieA~_ZLZoHyO?(R9GWUa9N;W+hwu* z2s0|mdSXEi*R3aneh;hJ1EkXCLcewCAyIeO>Amk~2!_p2@`(Uc_MC{TN8?s3z&_LG zF0BPM!|57HE%LsJ1JAxy7{C|QiDQcNJjoBh;WYwCyOq%)UeY*nyK}b!3F66qF{(htUfNsAu zT)6nhFEbeX1qMmr+o;89*u;Hm9>1;zYX!VQfp=tyyEB!1|Jhr-55)5;6ah~-3&%gd zMRCS(g+=6mAhbOpORvmIDku$FW;k^*mt7XFekgouqE4(Fav5)Ok5espGEOI7?q%SH z3n5Q_r^N-V8N^LO1!qBI-X~9(*}~Hdg3y)Ih{Dd_2@>$5+CIY$$=F=W3{N`bnlR{l zT~p8zuCSQ8{C#A%hOKY=f;T&{8&ohS+v|PC`mClT846t;c}Ur_9r|2h6l zIg8t?1JSC5a1ACN*49V9-BewA)Z@u|ry$R3Ui?TvX!kx(4H{6SvydR@5SaQE$otXl z_!Xhay;?V6+$G}tzY`^&Ze#{+IGZH*Ld$tJec^~W^VJX0EIOR-JqVCpSSmt0t4z0i zxdZ7y(NAtC#YT$Bs7lKKU|}&Eu~U6iKWmUBe=X%UcI~wci(BIXKXaHGmBV(&=!bvC zYeoDwELagvK`;PZ(!KNl9Iiy{;D?GS8c9gPekyXe2?~bc3f*2$f{fRHBx^zFON)~` z|FwF`d2pvn{^lNZ-`*AXrytfj%x~5YU$KfT)!&<|X%lAeg;f?9U@RBYTINJOldSh| zc?%yD4m__-Dzx4>QL(S5m?GHeunpAc6XU8mv$Fymr?HI=n4w8Y(th$o$X z`jQ6CzlY%H?TxTXOO+WjK$+{6qb%HuU>4t-3DI@yyAM7< zBCi7Wve(T%;Q6u>r)58SiF~BowZ}`5PAb0crI~K39|%=_4!DQy2diq*cJi=XKd$q^ zF2JZYR{c7&sRK19RR|gj`vFOf`Som{0hH)CC_Q`d!1a*N=1eHWqOcbFh&;WPqD-{B z)2N5W^;2%#**Yw_EW8p8}0ty<^%Xa!T~8Q3|6KWr%ThhvR4*zVzCHZmlTykf8!5T78xL z@;TAq`QOy>jrvCY7C|{B=4*HUBALPA5OWg%PO1eno4QfGLUaD+-fGclhP4WbK~}V6 zH=vOsBC_a>@GR)rInH~#aB@&~zu@x@s+kvL9vPpwswz=aONsq?p;Zh{3r&dg1`c2- zk_DxTS@CQj4Jg3O$ z|C#|b2oR(DxmB*L1@VgA9~ha!H^EsTiBFL+rf<6wha`w538D1G|79>0a*8D4!ZX&V zvxx@Ug>szqtGuawcFpO{oCg_jd!@Mrje=mviGMvpo~k7mxZ!NnH4%Hpdb!MneQ4zB z8ze!Gw}72;P6*6@g01$qR7&3)S$dKQBy*{$|Cd3>K4&2epft#p+77x8k-Yg8gjv|LrSq-;;lUskiWt{C#GIWVT-stmsraLUJL{nA7W6jI0yw&M zDPg9AB??1rbFYce%3GY`rj>A@rkLjgSpJO>L?e%y-<-F8^lRhl%c$CrJZ>=X=mT4@ zud{?rvEw(YUz-WZa|3_mXD7~DBTr2b45*wInoOzyg*Fu_A?H)(ZBnNJV7J}x1t{lp zSH#0{7N}|YFQLWl6=iZl^qG^pwc_p zYd_Yxa-TQOHpkJ>>y(5|!gQhxT|H#l{pI;R={K(MwdXJK0aVQ8#HIDufj9ioba$D! zhEXmnb9Ii@n6ZP0fxql*&bj6Fr9YaV8G7pJcF=uok>4Ho`gYvf<;kq8$`Vl{C%!vf z&}Rc9W@DK_eED7p@rOzSX&MZcX`HG?y-LQ|i4)%qA~=;snmY_443Lt7#=P zf+U&#n$^1|l!s60r3bn@i!tC7m*IN(J!Olt)?>oVm#0Eg2cQqjEA0N~>PC9$*6^aF z;XsogIpf*L`BO(GU$y(VX{@C^Sl<%zXq#$zIKhj(v)q7}7qz~5b+}|RGWkO zPSYD60U}FA6BXQ(s>q#0MjrHCwh7+xHb35rl)nNlsud~y8Ka@2?mUjlR(sX|J24|GH69*eV*%!vh zbsp4PFVFRaTi@?kxjYXx6t))1KO*0*SC4YFgDxQ<8dbkd#`C)!2@UrU%)Wi~ps#il z5Ij7`p_oJms7#y5Lu68=BfBK(;p)oOLN!&gK}RNj#FE@JcL_-V#R5=VVpd=}Yq&d5 zNu1_s&~A*HfVv@O@JY)e#yfb<@>?%rLvr9`ss6u@NdwrcGIW76K-koOLHHFnwDQ-Z zkV~~t*z3~U0f+z-kftfnqwNj{rN&VK9!O6K)d6OgMXU#su*x}IbN5I7yA#YqKl&~i zR0z@3ouI4m)hlxI*yQ^>1mT0R5`Zxb}$gC+jdbpuNfJ% zEC28oj`!d~6|P&^=^DYyldyW_xyIkibCZ2-c*9W8H=MFHARPBrk2})Tg{*Ua4S!&P zyZ#yrU?CyMd5*6TCeONFsU*dIfO!i|R-L$CFbG26^sho2T)Y z2SDoO=WMsMmru0I8w3;X2~k74VOJX_ZBmF*$sgkxMCm5oN(e<3dLH11M9OJtaHYwN zaS@RSUN|xm@vxAU5FUoLVji#85wNBFeQdUd^*eao1umj_{DEKjAq&Nx@?-aC#|u#z zcgG%7ajwjxF$^#gH<*kSgR5}uhla9KkRpdcnc|}<=>5pNvmzHboB>}<6
e`I)(9;e~im9dMd|0LnWMQKUvz)nRX$i`{@R)z!lKv+nX&!5(^ zN!Uph@V{HC6nBxeX8gPt{+dF1%!z3kzO4oW9%JM{p2Ropm2q19Sb6$!X>j@2qdY|3 zN%WzaT{i?aWsmy#&+&34^ZXYs;a=WTAg=vq^Ej&t`qw_o!L*s4St7 zKUz+F~Fg9v5vb8{tt!*=pOj*f6PYQRW5`xW5lL> z*3Q7#r7iXAVZbkWerk>vncs2pR7B*bduqe8gCVWfr_i_rLuTQu1+AOTJ=w!0Oz4ZjDy7g4nou*p^J9Li<#PJw3ISTDKNeCm zp$b~TGPO8E2z$*|BPo26UlE%AW=u<)GTe5Az8Z&Fcy%`?%A-@Jg4%OyK5wXQ+97_s! z@Z(1QV3SS@}fji_M$;Ib*`^6 zj!ye0~k#ksQUNPtaRG{MR*iSnRLt9sfdvdKPhf07WZ)`@xj1aM3K-ReYp zZOiQ5P6{AgP{XHFRb9C?8~kwk{O{h8l_COzl@Yk0W1(j~ewl@$Q==T66*2;_j9aXV z)Jpf!x2=uLK=;L@T)ejs58B1xTgg<^cypT|alCYfRDuR#4)aR!dvHI}oiJp~;g-)4 zUhVE(nL|On|9p-jfxP@AYkyd7yEfu8`vnDUmpxmmIn>gUwj6tY=PfS+SI-~bvUBW1 z70&3^lY6~%x0uKJ!(x0fFQt1njlZH~@5(|-joYelLs4tH@^VR% z60eJq2y>MrzsuE;Drw}O>#fzBdvn^Xc$H2GG+-3ccp>Vbrns7s!;FR-ns#kn>(<>B zVt@4XVec8m9XSj#+WXS3;iEp|gPZu7_1ixmxpj(v*JrJl7TXzN!TvjzANqUi@FzZC zdhEQvxYeXvd{J2ImrDGxXhxYx``iyr|?iCS+6=`PIRvKhCBB!AqSqaZ{56z=p``>^2c0)H03e|SZB zsBtRy+r?26RWwNEZDQP#*X7Fp=-TcJ{TNI-#|-$dE?|l(#&@g^45vihU!E>EgahT2 zTYg)`@*a6WE|hKWe%`1_CmKjgl`(A{I{po#R6L(E;lm5RF}Z*f8&j&B)fo0U2R{Z> z$a_?RMa`7n7Nb~;On_0JvWR!pVW!+!6kZk|X|m=(gO_}E=%zMcZUot<6bv&1drRRL z4~wlg48U{r*d^E z$MlzEzu0MX1+mc`mV+2|jRB4Wj!uNk;|ZasqL%5@SEUJLSvSW=0O&dX<^HJRt#Yrb z6ziSHMdJn@618_GiG6o%YM$lDZj3QQU5E5HtN||3^KT(-UYn70Ouvc}UeSZSce?rs zR}wSGh-YRrFqEYnKHbK}H-B){Fodi>>bub zZFS+BffDUTK=hYf0QmUt9&DapIBxgtQUVMIz372fE&a^%&!Q|d+#(t&5e7Fswn?I{AqQqRqAZnMsUpLSvDbU)^0!wsR zOLQL^K5Abu7L3dm$K4HpJJ~22G6@k&d?0S+gE!fP?-gs=aioqe3t%5Uq4rjmy;CX|=~C8j6V>EnSC)0c-> zwcw=0s)6rHBg%^{e-xvrl*nKe%5kGOkvsT}U!uV^O*64dwP;l?dk?LZp(YmVla;u& zc}HqrylrupIc`)+Hl)r;TpxFBr`oJ z9IHy5gPlhs>fHE8MgPQ61b(>}2_c^>%i)+3bEw~d8J$TYvJq=M)JEcBwxW2b%x^hHyZTIj|Lq<7tjqX^7=V9re#UNjV8}oK0000< KMNUMnLSTZTbDU=Y literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/mech.png.meta new file mode 100644 index 000000000..45f15fffe --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 441f67dd65701854fb2e8d6e405c4683 +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 + ignoreMasterTextureLimit: 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..aaedaace4c7fe38d2ff2b78b6ae104e8c1b3ad46 GIT binary patch literal 754 zcmVDhJI6_Yr86SqozZ9{-Q9ld?e6UzH{0zN*C5eVP=102kS}eA zG(~v_U<^R+BLG8wdIhiqum+HuI9j*S^R8erMJX^7UiT+6-wM>ruTH3n!<##7Us0D8(|l zvf=_jdamE`$BHE%_)4rhHgU9U4eCH{2$-D%=oxcD;K8Y|ji9u^@|*$BfOQQpYMP;A zd2}8EatB(CHRjj@>Z+BS@4x>J>FB9;ZbtVHcAU1I?poj~(0=jpsV*B>i#{`&6TIa~ z!*7dFkfXK?NwsrwL=$=8}a<$a4T_Mn9;Dqg+K^uq5vj1qqdX_TsDX z4WU00(ig}R)&e`Z_;y+4XrFwwDeIz}I|p**X|fNhM$o!-w+nF~XKHnXTKk(p#b-=W zuAR^wg@t+FqzX0b{0LhXR;Ml=UWjR?weL7jz)EDkEN$X z?P+rB*a09nc6aeEP!_af2ex&A!ZC7Z<_aT`B+y7%nS9*Pr kdkOjcSEOsX4p0F902e5Hs~kk0P5=M^07*qoM6N<$g5%d#e*gdg literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png.meta new file mode 100644 index 000000000..341a90d30 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/rotator.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 46c9fd466e7b39a4990e68a253d79bbf +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 + ignoreMasterTextureLimit: 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png new file mode 100644 index 0000000000000000000000000000000000000000..4f935e0e0fb89b150483e16ee6e6358fb72e5f83 GIT binary patch literal 890 zcmV-=1BLvFP)TfYYF7;`5I81I$W12?cEL?pu5O-i&jx-EQd|aCsIiiReq4 z!D*I{iD-;tcjs3P*C~YPt ziJAIFM1Qc<+?Ej0$b0TH5zX+O{|q7;SjP1M>hT~XM7wM(>H)-&33mwB?3`_9w2CBe zxJI#vWTuD0v8vSB+j%si&W(Rm^iLf{;Fp^rgnY6rhhwAFzJ7~-_5_z%2&S=ul=S9LcNJKWU#(ixB7qb<`ePw>jG1}GVSNgkm=(En_A7VuG5AoN(=G+9h QhX4Qo07*qoM6N<$f`%odng9R* literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png.meta new file mode 100644 index 000000000..7c121975d --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 56418c7b162776c40bb1c0618abfbb82 +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 + ignoreMasterTextureLimit: 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..880ba2398db1fe825f13819dfca3958cf3dd26e2 GIT binary patch literal 796 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 RYtsM#002ovPDHLkV1m}~oooOA literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/mech.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/mech.png.meta new file mode 100644 index 000000000..0f0cb7d2a --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/mech.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: fb3697293112e424081c5debc2d519fb +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 + ignoreMasterTextureLimit: 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png new file mode 100644 index 0000000000000000000000000000000000000000..36c2986205b0fe8a2e867faea6b1251da8d4eaf8 GIT binary patch literal 755 zcmV zLRJu|1DO>_9Vqt@uqse6&Rb=$T2i9PwWz=Y5Cr(M^XYMBc6N2O+wE`(_UR%hKR`X$ zb=x7!G`s~60g&?uz>v3I0IUFP04#%~*tGHUrerG9P+%rJ?oNDd4Iq;vNQxKlpk*4y z0A>IN)~#nMXdvXrh{9j3=eF{GkQ7f|K>3O1mV&ul3zA}fg8q|W^5-@85=ooWC;K8Y|wV+gB)nvd1u(klB zx-WFBj?R5RigB!nV-M)7(yo4g`!Zyp$2z!~-QW4*wC(4vGA7zj-hQmA21d~*X7d>T z>g0^~7QP_ySJ^p}Q*1`xhoCIJM)sKJiOTSp@R=N*c#YoyeIJ7IY~;Qv(=bvG=MoA8 zlp6TJjP2AIZc>wfRgJsP`Z5hy{QL`5=cWvJQ>X>Jb5Qyp)=jM%R40$IwVH{%L7rl| zJh|ep7m(1Pls$Jm^Z1^Sk^`a=N>)5Zw+LzPB1Xpj_3m3iKZyvr0MRQ1`L ztHYlW`aL0Ci9BI1@M>pYE~^^tvd^Yuopo_%L9QK-?31b%v})e%!Ys&K&5lrWe?6$g zOqqro>*X#XCyv?9B+pv;y<^{6tLP2q_S>#OnW(veqz?n0S=A@51I^gb!k4N7s3sEQ zW2O|Owqk(bL+(z2Bry`sm5f^FnCq;*qX_7RGlwHaJX(-2T3uidiGd( z8ml$U(gd~#$gNpjya|*IZDhf=ZcsSJ(posy?h*Vy8xWS0esAemPW!z{X2JG<+tIdO lZ{(iCKK>Qy+AaeWz#qswd2&ZNXF>n~002ovPDHLkV1i)oVqgFO literal 0 HcmV?d00001 diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png.meta new file mode 100644 index 000000000..e55eee493 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/rotator.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 311ce6f9db0cc1148b42d67735584430 +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 + ignoreMasterTextureLimit: 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: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index 4900c4104..c74c9800c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -55,6 +55,7 @@ public IconVariant(string name, IconSize size, IconColor color) private const string PlungerName = "plunger"; private const string PrimitiveName = "primitive"; private const string RampName = "ramp"; + private const string RotatorName = "rotator"; private const string RubberName = "rubber"; private const string SlingshotName = "slingshot"; private const string SpinnerName = "spinner"; @@ -68,7 +69,7 @@ public IconVariant(string name, IconSize size, IconColor color) private static readonly string[] Names = { BallRollerName, BoltName, BumperName, CoilName, DropTargetBankName, DropTargetName, FlasherName, FlipperName, GateName, HitTargetName, KeyName, - KickerName, LightGroupName, LightName, PlayfieldName, PlugName, PlungerName, PrimitiveName, RampName, RubberName, SlingshotName, SpinnerName, + KickerName, LightGroupName, LightName, PlayfieldName, PlugName, PlungerName, PrimitiveName, RampName, RotatorName, RubberName, SlingshotName, SpinnerName, SurfaceName, SwitchNcName, SwitchNoName, TableName, TeleporterName, TriggerName, TroughName, }; @@ -119,6 +120,7 @@ private Icons() public static Texture2D Plunger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlungerName, size, color); public static Texture2D Primitive(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PrimitiveName, size, color); public static Texture2D Ramp(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RampName, size, color); + public static Texture2D Rotator(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RotatorName, size, color); public static Texture2D Rubber(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RubberName, size, color); public static Texture2D Slingshot(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SlingshotName, size, color); public static Texture2D Spinner(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SpinnerName, size, color); @@ -148,6 +150,7 @@ public static Texture2D ByComponent(T mb, IconSize size = IconSize.Large, Ico case PlayfieldComponent _: return Playfield(size, color); case PrimitiveComponent _: return Primitive(size, color); case RampComponent _: return Ramp(size, color); + case RotatorComponent _: return Rotator(size, color); case RubberComponent _: return Rubber(size, color); case SpinnerComponent _: return Spinner(size, color); case SlingshotComponent _: return Slingshot(size, color); @@ -204,6 +207,7 @@ public static void DisableGizmoIcons() DisableGizmo(); DisableGizmo(); DisableGizmo(); + DisableGizmo(); DisableGizmo(); DisableGizmo(); DisableGizmo(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta index fea40c617..1377eb924 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/RotatorComponent.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: 311ce6f9db0cc1148b42d67735584430, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta index c479ca6a5..cd0d80033 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/StepRotatorMechComponent.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: fb3697293112e424081c5debc2d519fb, type: 3} userData: assetBundleName: assetBundleVariant: From 32224322588065a9d4df339ccafb9a3aee3bf723 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 30 Oct 2021 23:15:01 +0200 Subject: [PATCH 20/29] t2: Update cannon config. --- .../VisualPinball.Unity.Editor/Utils/Icons.cs | 7 ++- .../Matcher/TablePatcher.cs | 44 ++++++++++++++----- .../Patcher/Tables/Terminator2.cs | 17 +++++++ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index c74c9800c..e783d0265 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -50,6 +50,7 @@ public IconVariant(string name, IconSize size, IconColor color) private const string KickerName = "kicker"; private const string LightGroupName = "light_group"; private const string LightName = "light"; + private const string MechName = "mech"; private const string PlayfieldName = "playfield"; private const string PlugName = "plug"; private const string PlungerName = "plunger"; @@ -69,8 +70,8 @@ public IconVariant(string name, IconSize size, IconColor color) private static readonly string[] Names = { BallRollerName, BoltName, BumperName, CoilName, DropTargetBankName, DropTargetName, FlasherName, FlipperName, GateName, HitTargetName, KeyName, - KickerName, LightGroupName, LightName, PlayfieldName, PlugName, PlungerName, PrimitiveName, RampName, RotatorName, RubberName, SlingshotName, SpinnerName, - SurfaceName, SwitchNcName, SwitchNoName, TableName, TeleporterName, TriggerName, TroughName, + KickerName, LightGroupName, LightName, MechName, PlayfieldName, PlugName, PlungerName, PrimitiveName, RampName, RotatorName, RubberName, + SlingshotName, SpinnerName, SurfaceName, SwitchNcName, SwitchNoName, TableName, TeleporterName, TriggerName, TroughName, }; private readonly Dictionary _icons = new Dictionary(); @@ -115,6 +116,7 @@ private Icons() public static Texture2D Kicker(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KickerName, size, color); public static Texture2D Light(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightName, size, color); public static Texture2D LightGroup(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightGroupName, size, color); + public static Texture2D Mech(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(MechName, size, color); public static Texture2D Playfield(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlayfieldName, size, color); public static Texture2D Plug(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlugName, size, color); public static Texture2D Plunger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlungerName, size, color); @@ -155,6 +157,7 @@ public static Texture2D ByComponent(T mb, IconSize size = IconSize.Large, Ico case SpinnerComponent _: return Spinner(size, color); case SlingshotComponent _: return Slingshot(size, color); case SurfaceComponent _: return Surface(size, color); + case StepRotatorMechComponent _: return Mech(size, color); case TeleporterComponent _: return Teleporter(size, color); case TriggerComponent _: return Trigger(size, color); case TroughComponent _: return Trough(size, color); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs index 1ced500fe..d9c2fa870 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Matcher/TablePatcher.cs @@ -456,6 +456,23 @@ protected static void LinkLights(GameObject go, params string[] lightNames) #region Mapping Helpers + /// + /// Links a coil device to an existing coil mapping. + /// + /// Table component for retrieving mappings. + /// The ID of the coil mapping that the coil device will be linked to + /// The coil device to be linked + /// If set, it's the device item, otherwise the first item of the device. + protected static void LinkCoil(TableComponent tableComponent, string coilId, ICoilDeviceComponent coilDevice, string deviceItem = null) + { + var coilMapping = tableComponent.MappingConfig.Coils.FirstOrDefault(cm => cm.Id == coilId); + if (coilMapping == null) { + return; + } + coilMapping.Device = coilDevice; + coilMapping.DeviceItem = deviceItem ?? coilDevice.AvailableCoils.First().Id; + } + /// /// Links a coil device to an existing coil mapping if it matches a given name. /// @@ -469,12 +486,24 @@ protected static void LinkCoil(TableComponent tableComponent, string elementName if (!string.Equals(coilDevice.gameObject.name, elementName, StringComparison.OrdinalIgnoreCase)) { return; } - var coilMapping = tableComponent.MappingConfig.Coils.FirstOrDefault(cm => cm.Id == coilId); - if (coilMapping == null) { + LinkCoil(tableComponent, coilId, coilDevice, deviceItem); + } + + /// + /// Links a switch device to an existing switch mapping. + /// + /// Table component for retrieving mappings. + /// The ID of the switch mapping that the switch device will be linked to + /// The switch device to be linked + /// Switch ID inside of the device item. If null, the first switch will be used. + protected static void LinkSwitch(TableComponent tableComponent, string switchId, ISwitchDeviceComponent switchDevice, string switchDeviceItem = null) + { + var switchMapping = tableComponent.MappingConfig.Switches.FirstOrDefault(sw => sw.Id == switchId); + if (switchMapping == null) { return; } - coilMapping.Device = coilDevice; - coilMapping.DeviceItem = deviceItem ?? coilDevice.AvailableCoils.First().Id; + switchMapping.Device = switchDevice; + switchMapping.DeviceItem = switchDeviceItem ?? switchDevice.AvailableSwitches.First().Id; } /// @@ -489,12 +518,7 @@ protected static void LinkSwitch(TableComponent tableComponent, string elementNa if (!string.Equals(switchDevice.gameObject.name, elementName, StringComparison.OrdinalIgnoreCase)) { return; } - var switchMapping = tableComponent.MappingConfig.Switches.FirstOrDefault(sw => sw.Id == switchId); - if (switchMapping == null) { - return; - } - switchMapping.Device = switchDevice; - switchMapping.DeviceItem = switchDevice.AvailableSwitches.First().Id; + LinkSwitch(tableComponent, switchId, switchDevice); } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs index cd9e84441..755f3bbb8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs @@ -111,6 +111,9 @@ private static void SetupMapping(GameObject tableGo) // left lock LinkCoil(tc, "sw51", "16", kicker); + + // cannon + LinkCoil(tc, "sw31", "02", kicker); } var plungers = tableGo.GetComponentsInChildren(); @@ -125,6 +128,20 @@ private static void SetupMapping(GameObject tableGo) // skull kicker LinkCoil(tc, "sw76", "01", teleporter, TeleporterComponent.CoilItem); } + + var bumpers = tableGo.GetComponentsInChildren(); + foreach (var bumper in bumpers) { + LinkSwitch(tc, "Bumper1", "41", bumper); + LinkSwitch(tc, "Bumper2", "43", bumper); + LinkSwitch(tc, "Bumper3", "42", bumper); + } + + // cannon + var cannonGo = tc.transform.Find("Playfield/Mechs/Cannon").gameObject; + var cannonMech = cannonGo.GetComponent(); + LinkSwitch(tc, "32", cannonMech, "gun_mark_switch"); + LinkSwitch(tc, "33", cannonMech, "gun_home_switch"); + LinkCoil(tc, "11", cannonMech); } private static void SetupDmd(GameObject tableGo) From c2c14e73af14bcd29fb4843c9d7b41919bb26a97 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 00:15:09 +0200 Subject: [PATCH 21/29] t2: Fix position of L58. --- .../VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs index 755f3bbb8..239086420 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs @@ -916,8 +916,8 @@ public void T2Eyes(GameObject go) [NameMatch("L55")] [NameMatch("L56")] [NameMatch("L57")] - [NameMatch("L58")] public void RightRedRect(GameObject go) => LightPos(go, 8.5f, -11.7f, -50f); + [NameMatch("L58")] public void L58Pos(GameObject go) => LightPos(go, -2.3f, 20f, -50f); [NameMatch("L61")] [NameMatch("L62")] From 67247e482c065af57a73238d88745bb22102d1f0 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 00:16:44 +0200 Subject: [PATCH 22/29] t2: Fix gun positions. --- .../VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs index 239086420..a015b0ab3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Terminator2.cs @@ -428,8 +428,8 @@ public void SetupCannon(GameObject primitiveGo, PrimitiveComponent cannonComp) mechComp.Target = rotatorComp; mechComp.NumSteps = 240; mechComp.Marks = new[] { - new StepRotatorMark("Gun Mark", "gun_mark_switch", 0, 5), - new StepRotatorMark("Gun Home", "gun_home_switch", 98, 105) + new StepRotatorMark("Gun Home", "gun_home_switch", 0, 5), + new StepRotatorMark("Gun Mark", "gun_mark_switch", 98, 105), }; rotatorComp.Target = cannonComp; From b6323d88a655ebc2ef32f6519f2c87de4344123f Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 00:41:52 +0200 Subject: [PATCH 23/29] debug: Add frame counter component. --- .../VisualPinball.Unity/Game/FpsCounter.cs | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs new file mode 100644 index 000000000..c1dbd612e --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs @@ -0,0 +1,159 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace VisualPinball.Unity +{ + /// + /// Pushes the Framerate value to a Text component. + /// + [AddComponentMenu("Visual Pinball/FPS Counter")] + public class FpsCounter : MonoBehaviour + { + [Header("// Sample Groups of Data ")] + [Tooltip("Instead of reporting a specific framerate, this will sample many frames and report the average.")] + public bool GroupSampling; + + [Tooltip("If Group Sampling is on, how many frames would you want to sample to report an average on?")] + [Range(0, 20)] + public int SampleSize = 10; + + [Header("// Config ")] + [Tooltip("The Text Component you want the result pushed to. If you're using TMP then you need to change this in the code to a TMP_Text component instead.")] + // use 'TMP_Text' instead of 'Text' if you want Text Mesh Pro support. + public Text TargetText; + + [Tooltip("How often (in frames) do you want to update the Text component?")] + [Range(1, 20)] + public int UpdateTextEvery = 10; + + [Tooltip("This will smooth out the results so they blend together between updates and are easier to read.")] + public bool Smoothed; + + [Tooltip("This sets how many numbers are buffered into memory as strings in order to obtain zero allocations at runtime.\n\nAlthough this is trivial in memory usage, realistically, there's no reason to be over 1000.")] + [Range(0, 1000)] + public int NumberBufferSize = 500; + + [Header("// System FPS (updates once/sec)")] + [Tooltip("Would you like to read the System Tick instead of calculating it in this script?\n\nTests show that differences are negligible, but the option remains available to you.")] + public bool UseSystemTick; + + [Header("// Color Config ")] + [Tooltip("Optionally change the color of the TargetText based on FPS performance.")] + public bool UseColors = true; + [Tooltip("If the framerate is above 'OkayBelow' it will be the 'Good' color.")] + public Color Good = Color.green; + [Tooltip("If the framerate is below 'OkayBelow' it will be the 'Okay' color.")] + public Color Okay = Color.yellow; + [Tooltip("If the framerate is below 'BadBelow' it will be the 'Bad' color.")] + public Color Bad = Color.red; + [Tooltip("Threshold for defining an 'okay' framerate. Below this value is considered okay, but not high enough to be good, and not low enough to be bad.")] + public int OkayBelow = 60; + [Tooltip("Threshold for defining an 'bad' framerate. Below this value is considered bad.")] + public int BadBelow = 30; + + public int Framerate { get; private set; } + + protected int[] FpsSamples; + protected int SampleIndex; + protected int TextUpdateIndex; + + private int m_sysLastSysTick; + private int m_sysLastFrameRate; + private int m_sysFrameRate; + private string m_localfps; + + private static string[] m_numbers; + + protected virtual void Reset() + { + GroupSampling = true; + SampleSize = 20; + UpdateTextEvery = 1; + Smoothed = true; + UseColors = true; + + Good = Color.green; + Okay = Color.yellow; + Bad = Color.red; + + OkayBelow = 60; + BadBelow = 30; + + UseSystemTick = false; + NumberBufferSize = 1000; + } + + protected virtual void Start() + { + m_numbers = new string[NumberBufferSize]; + for (var i = 0; i < NumberBufferSize; i++) m_numbers[i] = i.ToString(); + + FpsSamples = new int[SampleSize]; + for (var i = 0; i < FpsSamples.Length; i++) FpsSamples[i] = 1; + if (!TargetText) enabled = false; + } + + protected virtual void Update() + { + if (GroupSampling) Group(); + else SingleFrame(); + + m_localfps = m_numbers[Framerate]; + + SampleIndex = SampleIndex < SampleSize - 1 ? SampleIndex + 1 : 0; + TextUpdateIndex = TextUpdateIndex > UpdateTextEvery ? 0 : TextUpdateIndex + 1; + if (TextUpdateIndex == UpdateTextEvery) TargetText.text = m_localfps; + + if (!UseColors) return; + if (Framerate < BadBelow) { + TargetText.color = Bad; + return; + } + TargetText.color = Framerate < OkayBelow ? Okay : Good; + } + + protected virtual void SingleFrame() + { + Framerate = Mathf.Clamp(UseSystemTick + ? GetSystemFramerate() + : (int)(Smoothed + ? 1 / Time.smoothDeltaTime + : 1 / Time.deltaTime), + 0, + m_numbers.Length - 1); + } + + protected virtual void Group() + { + FpsSamples[SampleIndex] = Mathf.Clamp(UseSystemTick + ? GetSystemFramerate() + : (int)(Smoothed + ? 1 / Time.smoothDeltaTime + : 1 / Time.deltaTime), + 0, + m_numbers.Length - 1); + + Framerate = 0; + var loop = true; + var i = 0; + while (loop) { + if (i == SampleSize - 1) loop = false; + Framerate += FpsSamples[i]; + i++; + } + Framerate /= FpsSamples.Length; + } + + protected virtual int GetSystemFramerate() + { + if (Environment.TickCount - m_sysLastSysTick >= 1000) { + m_sysLastFrameRate = m_sysFrameRate; + m_sysFrameRate = 0; + m_sysLastSysTick = Environment.TickCount; + } + m_sysFrameRate++; + return m_sysLastFrameRate; + } + } +} From e8418ac249f1d83884527874f3ab1c69fd8b8009 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 15:57:23 +0100 Subject: [PATCH 24/29] rotator: Add inspector --- .../VPT/Mech/RotatorInspector.cs | 55 +++++++++++++++++++ .../VPT/Mech/RotatorInspector.cs.meta | 11 ++++ 2 files changed, 66 insertions(+) create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs new file mode 100644 index 000000000..df4a6bb95 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs @@ -0,0 +1,55 @@ +// 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 AssignmentInConditionalExpression + +using UnityEditor; +using UnityEngine; +using VisualPinball.Engine.VPT.Primitive; + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(RotatorComponent)), CanEditMultipleObjects] + public class RotatorInspector : ItemInspector + { + private SerializedProperty _targetProperty; + private SerializedProperty _rotateWithProperty; + + protected override MonoBehaviour UndoTarget => target as MonoBehaviour; + + protected override void OnEnable() + { + base.OnEnable(); + + _targetProperty = serializedObject.FindProperty(nameof(RotatorComponent._target)); + _rotateWithProperty = serializedObject.FindProperty(nameof(RotatorComponent._rotateWith)); + } + + public override void OnInspectorGUI() + { + BeginEditing(); + + OnPreInspectorGUI(); + + PropertyField(_targetProperty); + PropertyField(_rotateWithProperty); + + base.OnInspectorGUI(); + + EndEditing(); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs.meta new file mode 100644 index 000000000..392cc45f6 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Mech/RotatorInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1659b98b1b7e504f9b2731e3c474c36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 3e7421fcdaa89b4b5c9d5b29256db789360c508d Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 15:57:58 +0100 Subject: [PATCH 25/29] rotator: Add doc. --- .../manual/mechanisms/rotator-inspector.png | Bin 0 -> 10443 bytes .../manual/mechanisms/rotators.md | 33 ++++++++++++++++++ .../Documentation~/creators-guide/toc.yml | 2 ++ 3 files changed, 35 insertions(+) create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotator-inspector.png create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotators.md diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotator-inspector.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotator-inspector.png new file mode 100644 index 0000000000000000000000000000000000000000..70bf6df82733a69b522b4ed0136c3da710a4d05a GIT binary patch literal 10443 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 + +In order to create a rotator, add the *Rotator* component to a game object by clicking *Add Component* in the inspector, then choosing *Visual Pinball -> Game Item -> Rotator*. You can use any game object, although we recommend adding it to the target that you want to rotate. + +### Target + +Just adding the component to your target game object won't automatically rotate the target. You need to explicitly assign it in the **Target** field. + +### Rotate With + +Add other objects that rotate along with your target here. Currently, the following game items are supported: + +- **Primitives** - Apart from the position, the *object rotation* of the primitive is updated. Note that in case your primitive has collision enabled, the colliders will not rotate along with the rendered object. +- **Kickers** - Rotating kickers applies the new angle to the kicker coils, and if a kicker contains a ball, the ball is rotated along as well. +- **Flippers** - Rotating flippers updates the start angle of the flipper. + +## Usage + +When adding a rotator component to a game object, it provides a float input that other components can use to rotate the object. Currently, only the step rotator mech makes use of this input. However, the step rotator will probably be replaced by a more generic system soon, so we won't go into more details here. \ No newline at end of file diff --git a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml index be45f7606..632f8c142 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml +++ b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml @@ -53,3 +53,5 @@ href: manual/mechanisms/teleporters.md - name: Drop Target Banks href: manual/mechanisms/drop-target-banks.md + - name: Rotators + href: manual/mechanisms/rotators.md From a71fca607a3c9fd13255f2a3ae64d936a60cf528 Mon Sep 17 00:00:00 2001 From: Ryan Mercado Date: Sun, 31 Oct 2021 12:16:50 -0400 Subject: [PATCH 26/29] Update rotators.md Reword ambiguous "it" and use consistent formatting for the phrase "Rotator component" --- .../creators-guide/manual/mechanisms/rotators.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotators.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotators.md index 07ed4f7ea..eda42327f 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotators.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/mechanisms/rotators.md @@ -8,13 +8,13 @@ description: Rotating objects during gameplay Sometimes during gameplay, you might need to rotate objects in order to recreate certain mechanisms. Like Visual Pinball, VPE doesn't support dynamic colliders (yet), but visually, objects can still be rotated. -**Rotators** in VPE are components that allow you to easily rotate objects around the Z-axis. It works by choosing a *target* which rotates around its local origin (typically the position on the playfield). Additional objects can be linked to rotate around the same axis at the same time. +**Rotators** in VPE are components that allow you to easily rotate objects around the Z-axis. The **Rotator** component is applied to a *target*, which will then rotate around its local origin (typically the position on the playfield). Additional objects can be linked to rotate around the same axis at the same time. ## Setup Rotator Inspector -In order to create a rotator, add the *Rotator* component to a game object by clicking *Add Component* in the inspector, then choosing *Visual Pinball -> Game Item -> Rotator*. You can use any game object, although we recommend adding it to the target that you want to rotate. +In order to create a rotator, add the **Rotator** component to a game object by clicking *Add Component* in the inspector, then choosing *Visual Pinball -> Game Item -> Rotator*. You can use any game object, although we recommend adding it to the target that you want to rotate. ### Target @@ -30,4 +30,4 @@ Add other objects that rotate along with your target here. Currently, the follow ## Usage -When adding a rotator component to a game object, it provides a float input that other components can use to rotate the object. Currently, only the step rotator mech makes use of this input. However, the step rotator will probably be replaced by a more generic system soon, so we won't go into more details here. \ No newline at end of file +When adding a **Rotator** component to a game object, it provides a float input that other components can use to rotate the object. Currently, only the step rotator mech makes use of this input. However, the step rotator will probably be replaced by a more generic system soon, so we won't go into more details here. From 06a30b187c596fa98feb66890ff1500dfcaca6db Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 20:30:11 +0100 Subject: [PATCH 27/29] build: Remove FPS counter due to breaking the build. --- .../VisualPinball.Unity/Game/FpsCounter.cs | 159 ------------------ 1 file changed, 159 deletions(-) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs deleted file mode 100644 index c1dbd612e..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/FpsCounter.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.UI; - -namespace VisualPinball.Unity -{ - /// - /// Pushes the Framerate value to a Text component. - /// - [AddComponentMenu("Visual Pinball/FPS Counter")] - public class FpsCounter : MonoBehaviour - { - [Header("// Sample Groups of Data ")] - [Tooltip("Instead of reporting a specific framerate, this will sample many frames and report the average.")] - public bool GroupSampling; - - [Tooltip("If Group Sampling is on, how many frames would you want to sample to report an average on?")] - [Range(0, 20)] - public int SampleSize = 10; - - [Header("// Config ")] - [Tooltip("The Text Component you want the result pushed to. If you're using TMP then you need to change this in the code to a TMP_Text component instead.")] - // use 'TMP_Text' instead of 'Text' if you want Text Mesh Pro support. - public Text TargetText; - - [Tooltip("How often (in frames) do you want to update the Text component?")] - [Range(1, 20)] - public int UpdateTextEvery = 10; - - [Tooltip("This will smooth out the results so they blend together between updates and are easier to read.")] - public bool Smoothed; - - [Tooltip("This sets how many numbers are buffered into memory as strings in order to obtain zero allocations at runtime.\n\nAlthough this is trivial in memory usage, realistically, there's no reason to be over 1000.")] - [Range(0, 1000)] - public int NumberBufferSize = 500; - - [Header("// System FPS (updates once/sec)")] - [Tooltip("Would you like to read the System Tick instead of calculating it in this script?\n\nTests show that differences are negligible, but the option remains available to you.")] - public bool UseSystemTick; - - [Header("// Color Config ")] - [Tooltip("Optionally change the color of the TargetText based on FPS performance.")] - public bool UseColors = true; - [Tooltip("If the framerate is above 'OkayBelow' it will be the 'Good' color.")] - public Color Good = Color.green; - [Tooltip("If the framerate is below 'OkayBelow' it will be the 'Okay' color.")] - public Color Okay = Color.yellow; - [Tooltip("If the framerate is below 'BadBelow' it will be the 'Bad' color.")] - public Color Bad = Color.red; - [Tooltip("Threshold for defining an 'okay' framerate. Below this value is considered okay, but not high enough to be good, and not low enough to be bad.")] - public int OkayBelow = 60; - [Tooltip("Threshold for defining an 'bad' framerate. Below this value is considered bad.")] - public int BadBelow = 30; - - public int Framerate { get; private set; } - - protected int[] FpsSamples; - protected int SampleIndex; - protected int TextUpdateIndex; - - private int m_sysLastSysTick; - private int m_sysLastFrameRate; - private int m_sysFrameRate; - private string m_localfps; - - private static string[] m_numbers; - - protected virtual void Reset() - { - GroupSampling = true; - SampleSize = 20; - UpdateTextEvery = 1; - Smoothed = true; - UseColors = true; - - Good = Color.green; - Okay = Color.yellow; - Bad = Color.red; - - OkayBelow = 60; - BadBelow = 30; - - UseSystemTick = false; - NumberBufferSize = 1000; - } - - protected virtual void Start() - { - m_numbers = new string[NumberBufferSize]; - for (var i = 0; i < NumberBufferSize; i++) m_numbers[i] = i.ToString(); - - FpsSamples = new int[SampleSize]; - for (var i = 0; i < FpsSamples.Length; i++) FpsSamples[i] = 1; - if (!TargetText) enabled = false; - } - - protected virtual void Update() - { - if (GroupSampling) Group(); - else SingleFrame(); - - m_localfps = m_numbers[Framerate]; - - SampleIndex = SampleIndex < SampleSize - 1 ? SampleIndex + 1 : 0; - TextUpdateIndex = TextUpdateIndex > UpdateTextEvery ? 0 : TextUpdateIndex + 1; - if (TextUpdateIndex == UpdateTextEvery) TargetText.text = m_localfps; - - if (!UseColors) return; - if (Framerate < BadBelow) { - TargetText.color = Bad; - return; - } - TargetText.color = Framerate < OkayBelow ? Okay : Good; - } - - protected virtual void SingleFrame() - { - Framerate = Mathf.Clamp(UseSystemTick - ? GetSystemFramerate() - : (int)(Smoothed - ? 1 / Time.smoothDeltaTime - : 1 / Time.deltaTime), - 0, - m_numbers.Length - 1); - } - - protected virtual void Group() - { - FpsSamples[SampleIndex] = Mathf.Clamp(UseSystemTick - ? GetSystemFramerate() - : (int)(Smoothed - ? 1 / Time.smoothDeltaTime - : 1 / Time.deltaTime), - 0, - m_numbers.Length - 1); - - Framerate = 0; - var loop = true; - var i = 0; - while (loop) { - if (i == SampleSize - 1) loop = false; - Framerate += FpsSamples[i]; - i++; - } - Framerate /= FpsSamples.Length; - } - - protected virtual int GetSystemFramerate() - { - if (Environment.TickCount - m_sysLastSysTick >= 1000) { - m_sysLastFrameRate = m_sysFrameRate; - m_sysFrameRate = 0; - m_sysLastSysTick = Environment.TickCount; - } - m_sysFrameRate++; - return m_sysLastFrameRate; - } - } -} From 75015ea8a2c0c630e7f6c8f5c75bbf99c4820fa7 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 20:51:47 +0100 Subject: [PATCH 28/29] doc: Update changelog. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ff87577..076832ff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,10 @@ ## Unreleased -Built with Unity 2020.3. +Built with Unity 2021.2. ### Added +- A *Rotator* component ([#337](https://github.com/freezy/VisualPinball.Engine/pull/337), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/rotator.html)). - A *Teleporter* component ([#336](https://github.com/freezy/VisualPinball.Engine/pull/336), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/drop-target-banks.html)). - A *Drop Target Bank* component ([#333](https://github.com/freezy/VisualPinball.Engine/pull/333), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/teleporters.html)). - Editor: Enable manual trigger for coils, switches, lamps and wires during gameplay ([#332](https://github.com/freezy/VisualPinball.Engine/pull/332)) From 00b8a23b1e19910e0a5fce87b08d273449938db3 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 31 Oct 2021 20:53:02 +0100 Subject: [PATCH 29/29] doc: Fix links in changelog. --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 076832ff4..4d6f5e983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,9 @@ Built with Unity 2021.2. ### Added -- A *Rotator* component ([#337](https://github.com/freezy/VisualPinball.Engine/pull/337), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/rotator.html)). -- A *Teleporter* component ([#336](https://github.com/freezy/VisualPinball.Engine/pull/336), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/drop-target-banks.html)). -- A *Drop Target Bank* component ([#333](https://github.com/freezy/VisualPinball.Engine/pull/333), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/teleporters.html)). +- A *Rotator* component ([#337](https://github.com/freezy/VisualPinball.Engine/pull/337), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/rotators.html)). +- A *Teleporter* component ([#336](https://github.com/freezy/VisualPinball.Engine/pull/336), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/teleporters.html)). +- A *Drop Target Bank* component ([#333](https://github.com/freezy/VisualPinball.Engine/pull/333), [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/drop-target-banks.html)). - Editor: Enable manual trigger for coils, switches, lamps and wires during gameplay ([#332](https://github.com/freezy/VisualPinball.Engine/pull/332)) - Support for dynamic wires, also known as *Fast Flip* ([#330](https://github.com/freezy/VisualPinball.Engine/pull/330), [Documentation](https://docs.visualpinball.org/creators-guide/editor/wire-manager.html#dynamic)). - Component for light groups, allowing easy grouping of GI lamps. ([#330](https://github.com/freezy/VisualPinball.Engine/pull/330) [Documentation](https://docs.visualpinball.org/creators-guide/manual/mechanisms/light-groups.html)).