From b9b77b9d6a3ef93df3ebbccb21ecf772b319aefa Mon Sep 17 00:00:00 2001 From: Victor Chelaru Date: Sun, 26 Nov 2023 05:52:10 -0700 Subject: [PATCH] Made a number of fixes to support old FRB projects (pre GLUX version 8). --- .../TileShapeCollection.cs | 11 +- .../Glue/Glue/SaveClasses/GlueProjectSave.cs | 11 + .../CodeGeneration/SpriteCodeGenerator.cs | 6 +- .../CodeGeneration/StateCodeGenerator.cs | 18 + .../Player/platformervaluesstatic.csv | 12 +- .../Components/DefaultForms/Button.gucx | 1 + .../Components/DefaultForms/CheckBox.gucx | 1 + .../Components/DefaultForms/ColoredFrame.gucx | 3 +- .../Components/DefaultForms/ComboBox.gucx | 1 + .../Components/DefaultForms/Keyboard.gucx | 3 +- .../Components/DefaultForms/KeyboardKey.gucx | 1 + .../Components/DefaultForms/ListBox.gucx | 4 +- .../Components/DefaultForms/ListBoxItem.gucx | 1 + .../Components/DefaultForms/PasswordBox.gucx | 1 + .../Components/DefaultForms/RadioButton.gucx | 1 + .../Components/DefaultForms/ScrollBar.gucx | 1 + .../DefaultForms/ScrollBarThumb.gucx | 1 + .../Components/DefaultForms/ScrollViewer.gucx | 3 +- .../Components/DefaultForms/Slider.gucx | 1 + .../Components/DefaultForms/TextBox.gucx | 2 + .../Components/DefaultForms/ToggleButton.gucx | 1 + .../DefaultForms/TreeViewToggleButton.gucx | 1 + .../Components/IndividualJoinComponent.gucx | 55 +- .../GumProject/EventExport/gum_events.json | 73 +- .../Content/GumProject/GumProject.gumx | 11 + .../Screens/CharacterJoiningScreenGum.gusx | 1 + .../GumProject/Screens/GameScreenGum.gusx | 1 + .../Content/GumProject/Screens/Level1Gum.gusx | 1 + .../Content/GumProject/Standards/Circle.gutx | 17 + .../Standards/ColoredRectangle.gutx | 17 + .../GumProject/Standards/Component.gutx | 1 + .../GumProject/Standards/Container.gutx | 38 + .../GumProject/Standards/NineSlice.gutx | 23 + .../Content/GumProject/Standards/Polygon.gutx | 17 + .../GumProject/Standards/Rectangle.gutx | 17 + .../Content/GumProject/Standards/Sprite.gutx | 17 + .../Content/GumProject/Standards/Text.gutx | 46 + .../GlueSettings/CompilerSettings.json | 1 + .../TreeViewPlugin.settings.user.json | 1 + .../MultiplayerPlatformerDemo.csproj | 2 + .../MultiplayerPlatformerDemo.glux | 178 ++-- .../Setup/CameraSetup.cs | 556 +++++++----- ...leListVsTileShapeCollectionRelationship.cs | 87 +- ...idableVsTileShapeCollectionRelationship.cs | 101 ++- .../TileCollisions/TileShapeCollection.cs | 830 ++++++++++++------ .../TileEntities/TileEntityInstantiator.cs | 221 +++-- .../TileGraphics/AbstractMapLayer.cs | 59 ++ .../TileGraphics/ExternalTileset.cs | 66 +- .../TileGraphics/LayeredTileMap.cs | 159 +++- .../TileGraphics/LayeredTileMapAnimation.cs | 6 +- .../TileGraphics/MapDrawableBatch.cs | 572 +++++++++--- .../TileGraphics/MapLayer.cs | 8 - .../TileGraphics/MapTileset.cs | 194 ++-- .../TileGraphics/MapTilesetTile.cs | 24 +- .../TileGraphics/NamedValue.cs | 2 +- .../ReducedTileMapInfo.TiledMapSave.cs | 96 +- .../TileGraphics/ReducedTileMapInfo.cs | 28 +- .../TileGraphics/TileNodeNetworkCreator.cs | 125 ++- .../TileGraphics/TiledMapSave.Conversion.cs | 32 +- .../TiledMapSave.Serialization.cs | 250 ++++-- .../TiledMapToShapeCollectionConverter.cs | 18 +- 61 files changed, 2885 insertions(+), 1151 deletions(-) create mode 100644 Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/CompilerSettings.json create mode 100644 Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/TreeViewPlugin.settings.user.json diff --git a/Engines/FlatRedBallAddOns/FlatRedBall.TileCollisions/TileShapeCollection.cs b/Engines/FlatRedBallAddOns/FlatRedBall.TileCollisions/TileShapeCollection.cs index 1852d0a12..226aa7697 100644 --- a/Engines/FlatRedBallAddOns/FlatRedBall.TileCollisions/TileShapeCollection.cs +++ b/Engines/FlatRedBallAddOns/FlatRedBall.TileCollisions/TileShapeCollection.cs @@ -195,19 +195,26 @@ public Microsoft.Xna.Framework.Color Color ShapeCollection ICollidable.Collision => this.mShapes; +#if ICollidableHasItemsCollidedAgainst + + HashSet ICollidable.ItemsCollidedAgainst => this.mShapes.ItemsCollidedAgainst; HashSet ICollidable.LastFrameItemsCollidedAgainst => this.mShapes.LastFrameItemsCollidedAgainst; +#endif + +#if ICollidableHasObjectsCollidedAgainst + HashSet ICollidable.ObjectsCollidedAgainst => this.mShapes.ObjectsCollidedAgainst; HashSet ICollidable.LastFrameObjectsCollidedAgainst => this.mShapes.LastFrameObjectsCollidedAgainst; - +#endif public RepositionUpdateStyle RepositionUpdateStyle { get; set; } = RepositionUpdateStyle.Outward; - #endregion +#endregion public TileShapeCollection() { diff --git a/FRBDK/Glue/Glue/SaveClasses/GlueProjectSave.cs b/FRBDK/Glue/Glue/SaveClasses/GlueProjectSave.cs index 0f6cd6408..c13e41e6d 100644 --- a/FRBDK/Glue/Glue/SaveClasses/GlueProjectSave.cs +++ b/FRBDK/Glue/Glue/SaveClasses/GlueProjectSave.cs @@ -68,6 +68,13 @@ public enum GluxVersions // file version for supporting named subcollisions. ScreensHaveActivityEditMode = 8, SupportsNamedSubcollisions = 8, + + // This was added 5/5/2021, + TimeManagerHasDelaySeconds = 8, + + // Added 8/24/2021 + GumTextHasIsBold = 8, + GlueSavedToJson = 9, IEntityInFrb = 10, SeparateJsonFilesForElements = 11, @@ -100,6 +107,10 @@ public enum GluxVersions GeneratedCameraSetupFile = 26, ShapeCollectionHasMaxAxisAlignedRectanglesRadiusX = 27, AutoNameCollisionListsAsSingle = 28, + + // Added retroactively to address problem with project template: + GumHasIgnoredByParentSize = 29, + GumTextObjectsUpdateTextWith0ChildDepth = 29, HasFrameworkElementManager = 30, HasGumSkiaElements = 31, diff --git a/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/SpriteCodeGenerator.cs b/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/SpriteCodeGenerator.cs index f97c71bcc..ee5f6d959 100644 --- a/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/SpriteCodeGenerator.cs +++ b/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/SpriteCodeGenerator.cs @@ -25,7 +25,11 @@ public void GenerateAdditionalMethods(StandardElementSave standardElementSave, I GenerateSetTextureCoordinatesFrom(classBodyBlock); GenerateSourceFileNameProperty(classBodyBlock); GenerateCurrentChainNameProperty(classBodyBlock); - GeneratePlayAnimationsAsync(classBodyBlock); + + if(GlueState.Self.CurrentGlueProject.FileVersion >= (int)GluxVersions.TimeManagerHasDelaySeconds) + { + GeneratePlayAnimationsAsync(classBodyBlock); + } } } diff --git a/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/StateCodeGenerator.cs b/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/StateCodeGenerator.cs index 223c7e106..6fef28098 100644 --- a/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/StateCodeGenerator.cs +++ b/FRBDK/Glue/GumPlugin/GumPlugin/CodeGeneration/StateCodeGenerator.cs @@ -132,6 +132,24 @@ public void RefreshVariableNamesToSkipBasedOnGlueVersion() Skip("LineHeightMultiplier"); } + if ( version >= (int) GluxVersions.GumHasIgnoredByParentSize) + { + Skip("IgnoredByParentSize"); + } + else + { + Skip("IgnoredByParentSize"); + } + + if( version >= (int) GluxVersions.GumTextHasIsBold) + { + Skip("IsBold"); + } + else + { + Skip("IsBold"); + } + return; void Include(string variable) diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/Entities/Player/platformervaluesstatic.csv b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/Entities/Player/platformervaluesstatic.csv index 383a30e9a..502065df6 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/Entities/Player/platformervaluesstatic.csv +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/Entities/Player/platformervaluesstatic.csv @@ -1,6 +1,6 @@ -"Name (System.String, required)",MaxSpeedX (System.Single),AccelerationTimeX (System.Single),DecelerationTimeX (System.Single),Gravity (System.Single),MaxFallSpeed (System.Single),JumpVelocity (System.Single),JumpApplyLength (System.Single),JumpApplyByButtonHold (System.Boolean),UsesAcceleration (System.Boolean),CanFallThroughCloudPlatforms (System.Boolean),CloudFallThroughDistance (System.Single),IsUsingCustomDeceleration (System.Boolean),CustomDecelerationValue (System.Single),MoveSameSpeedOnSlopes (System.Boolean),UphillFullSpeedSlope (System.Decimal),UphillStopSpeedSlope (System.Decimal),DownhillFullSpeedSlope (System.Decimal),DownhillMaxSpeedSlope (System.Decimal),DownhillMaxSpeedBoostPercentage (System.Decimal) -Ground,100,0.25,0.15,900,500,230,0.2,True,True,True,16,False,0,True,0,60,0,60,50 -Air,100,0.7,0.7,900,260,0,0,False,True,True,16,False,0,True,0,0,0,0,0 -Running,150,0.3,0.2,900,500,250,0.25,True,True,True,16,False,0,True,0,0,0,0,0 -RunningAir,150,0.7,0.7,900,260,0,0,False,True,True,16,False,0,True,0,0,0,0,0 -Ducking,0,0,0,900,500,230,0.2,True,True,True,16,True,200,True,0,0,0,0,0 \ No newline at end of file +"Name (System.String, required)",MaxSpeedX (System.Single),AccelerationTimeX (System.Single),DecelerationTimeX (System.Single),Gravity (System.Single),MaxFallSpeed (System.Single),JumpVelocity (System.Single),JumpApplyLength (System.Single),JumpApplyByButtonHold (System.Boolean),UsesAcceleration (System.Boolean),CanFallThroughCloudPlatforms (System.Boolean),CloudFallThroughDistance (System.Single),IsUsingCustomDeceleration (System.Boolean),CustomDecelerationValue (System.Single),MoveSameSpeedOnSlopes (System.Boolean),UphillFullSpeedSlope (System.Decimal),UphillStopSpeedSlope (System.Decimal),DownhillFullSpeedSlope (System.Decimal),DownhillMaxSpeedSlope (System.Decimal),DownhillMaxSpeedBoostPercentage (System.Decimal),CanClimb (System.Boolean),MaxClimbingSpeed (System.Single),InheritOrOverwriteAsInt (System.Int32) +Ground,100,0.25,0.15,900,500,230,0.2,True,True,True,16,False,0,True,0,60,0,60,50,False,0,0 +Air,100,0.7,0.7,900,260,0,0,False,True,True,16,False,0,True,0,0,0,0,0,False,0,0 +Running,150,0.3,0.2,900,500,250,0.25,True,True,True,16,False,0,True,0,0,0,0,0,False,0,0 +RunningAir,150,0.7,0.7,900,260,0,0,False,True,True,16,False,0,True,0,0,0,0,0,False,0,0 +Ducking,0,0,0,900,500,230,0.2,True,True,True,16,True,200,True,0,0,0,0,0,False,0,0 \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Button.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Button.gucx index 192769465..39f12608e 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Button.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Button.gucx @@ -264,6 +264,7 @@ ButtonCategory + false Enabled diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/CheckBox.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/CheckBox.gucx index a13686c7c..c8d53ef89 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/CheckBox.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/CheckBox.gucx @@ -277,6 +277,7 @@ CheckBoxCategory + false EnabledOn diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ColoredFrame.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ColoredFrame.gucx index 0050b2283..b65b94737 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ColoredFrame.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ColoredFrame.gucx @@ -26,7 +26,7 @@ true - ColorCategoryState + ColorCategory ColorCategoryState false @@ -213,6 +213,7 @@ ColorCategory + false Gray diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ComboBox.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ComboBox.gucx index 3f99496b1..ab00ed723 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ComboBox.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ComboBox.gucx @@ -289,6 +289,7 @@ ComboBoxCategory + false Enabled diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Keyboard.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Keyboard.gucx index 7155464fb..c6cfc2d89 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Keyboard.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Keyboard.gucx @@ -5,7 +5,7 @@ Default - CursorMoveCategoryState + CursorMoveCategory CursorMoveCategoryState false @@ -3832,6 +3832,7 @@ CursorMoveCategory + false LeftRightMoveSupported diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/KeyboardKey.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/KeyboardKey.gucx index 21ab55a02..8150a4ea2 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/KeyboardKey.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/KeyboardKey.gucx @@ -180,6 +180,7 @@ ButtonCategory + false Enabled diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBox.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBox.gucx index 3ce8b5dbf..f9a47a279 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBox.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBox.gucx @@ -291,7 +291,7 @@ false - ScrollBarVisibilityState + ScrollBarVisibility ScrollBarVisibilityState false @@ -357,6 +357,7 @@ ListBoxCategory + false Enabled @@ -425,6 +426,7 @@ ScrollBarVisibility + false NoScrollBar diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBoxItem.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBoxItem.gucx index e39231d02..c2e05e3de 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBoxItem.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ListBoxItem.gucx @@ -233,6 +233,7 @@ ListBoxItemCategory + false Enabled diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/PasswordBox.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/PasswordBox.gucx index 0b1b915b0..fdaee2ee3 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/PasswordBox.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/PasswordBox.gucx @@ -237,6 +237,7 @@ PasswordBoxCategory + false Enabled diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/RadioButton.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/RadioButton.gucx index f63d26857..c76f755ef 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/RadioButton.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/RadioButton.gucx @@ -272,6 +272,7 @@ RadioButtonCategory + false EnabledOn diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBar.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBar.gucx index 236082a90..e5314891e 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBar.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBar.gucx @@ -512,6 +512,7 @@ ScrollBarCategory + false BackgroundInstance diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBarThumb.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBarThumb.gucx index bbdf0b705..f64983596 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBarThumb.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollBarThumb.gucx @@ -96,6 +96,7 @@ ButtonCategory + false Enabled diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollViewer.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollViewer.gucx index 0517f244f..e7991db53 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollViewer.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ScrollViewer.gucx @@ -197,7 +197,7 @@ true - ScrollBarVisibilityState + ScrollBarVisibility ScrollBarVisibilityState false @@ -263,6 +263,7 @@ ScrollBarVisibility + false NoScrollBar diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Slider.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Slider.gucx index 90e820df7..694e83ae0 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Slider.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/Slider.gucx @@ -222,6 +222,7 @@ SliderCategory + false LineInstance diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TextBox.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TextBox.gucx index 20a8ed64e..46b91cd96 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TextBox.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TextBox.gucx @@ -274,6 +274,7 @@ TextBoxCategory + false Enabled @@ -289,6 +290,7 @@ LineModeCategory + false Single diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ToggleButton.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ToggleButton.gucx index 9d5ee9e00..f5430d839 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ToggleButton.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/ToggleButton.gucx @@ -361,6 +361,7 @@ ToggleCategory + false EnabledOn diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TreeViewToggleButton.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TreeViewToggleButton.gucx index d437d1d0a..93592fcde 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TreeViewToggleButton.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/DefaultForms/TreeViewToggleButton.gucx @@ -115,6 +115,7 @@ ToggleCategory + false EnabledOn diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/IndividualJoinComponent.gucx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/IndividualJoinComponent.gucx index eef01b876..5f50ad061 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/IndividualJoinComponent.gucx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Components/IndividualJoinComponent.gucx @@ -17,7 +17,7 @@ true - JoinCategoryState + JoinCategory JoinCategoryState false @@ -167,6 +167,7 @@ JoinCategory + false NotPluggedIn @@ -206,22 +207,9 @@ PluggedInNotJoined - - string - TextInstance.Text - Press A to join - Text - true - - - bool - RectangleInstance.Visible - true - true - int - RectangleInstance.Red + RectangleInstance.Blue 128 true @@ -233,28 +221,29 @@ int - RectangleInstance.Blue + RectangleInstance.Red 128 true - - - Joined - - string - TextInstance.Text - Player joined - true - bool RectangleInstance.Visible true true + + string + TextInstance.Text + Press A to join + Text + true + + + + Joined int - RectangleInstance.Red + RectangleInstance.Blue 255 Rendering true @@ -268,11 +257,23 @@ int - RectangleInstance.Blue + RectangleInstance.Red 255 Rendering true + + bool + RectangleInstance.Visible + true + true + + + string + TextInstance.Text + Player joined + true + diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/EventExport/gum_events.json b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/EventExport/gum_events.json index 3a10ed719..6172a3118 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/EventExport/gum_events.json +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/EventExport/gum_events.json @@ -1,76 +1,5 @@ { "UserEvents": { - "vchel": [ - { - "NewName": "IndividualJoinComponent", - "OldName": null, - "ElementType": "Components", - "EventType": 0, - "TimestampUtc": "2021-04-30T17:06:52.4561645Z" - }, - { - "NewName": "IndividualJoinComponent.TextInstance", - "OldName": null, - "ElementType": "Components", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:07:52.0813455Z" - }, - { - "NewName": " CharacterJoiningScreenGum.ContainerInstance", - "OldName": null, - "ElementType": "Screens", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:15:03.7448715Z" - }, - { - "NewName": " CharacterJoiningScreenGum.IndividualJoinComponentInstance", - "OldName": null, - "ElementType": "Screens", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:15:15.6216151Z" - }, - { - "NewName": " CharacterJoiningScreenGum.IndividualJoinComponentInstance1", - "OldName": null, - "ElementType": "Screens", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:15:43.2295857Z" - }, - { - "NewName": " CharacterJoiningScreenGum.IndividualJoinComponentInstance2", - "OldName": null, - "ElementType": "Screens", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:15:43.4265857Z" - }, - { - "NewName": " CharacterJoiningScreenGum.IndividualJoinComponentInstance3", - "OldName": null, - "ElementType": "Screens", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:15:43.6465836Z" - }, - { - "NewName": "IndividualJoinComponent.RectangleInstance", - "OldName": null, - "ElementType": "Components", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:16:38.2937075Z" - }, - { - "NewName": " CharacterJoiningScreenGum.TextInstance", - "OldName": null, - "ElementType": "Screens", - "EventType": 5, - "TimestampUtc": "2021-04-30T17:18:54.3714057Z" - }, - { - "NewName": " CharacterJoiningScreenGum.InstructionTextInstance", - "OldName": " CharacterJoiningScreenGum.TextInstance", - "ElementType": "Screens", - "EventType": 7, - "TimestampUtc": "2021-04-30T17:19:27.3179863Z" - } - ] + "vchel": [] } } \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/GumProject.gumx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/GumProject.gumx index 4022bf2b2..f1daaf4ba 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/GumProject.gumx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/GumProject.gumx @@ -1,14 +1,21 @@  + 32-126,160-255 1 256 224 true + true + true + true false true false + ../ + true + 0 CharacterJoiningScreenGum Screen @@ -247,4 +254,8 @@ UserControlBehavior + + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/CharacterJoiningScreenGum.gusx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/CharacterJoiningScreenGum.gusx index e1b1dfa2d..6a817ae11 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/CharacterJoiningScreenGum.gusx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/CharacterJoiningScreenGum.gusx @@ -237,4 +237,5 @@ Text false + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/GameScreenGum.gusx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/GameScreenGum.gusx index d8f1dcfcc..b7b30e97c 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/GameScreenGum.gusx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/GameScreenGum.gusx @@ -10,4 +10,5 @@ true + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/Level1Gum.gusx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/Level1Gum.gusx index 650e5c54b..8651a4625 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/Level1Gum.gusx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Screens/Level1Gum.gusx @@ -10,4 +10,5 @@ true + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Circle.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Circle.gutx index 103d4945a..edb65bd2b 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Circle.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Circle.gutx @@ -51,6 +51,13 @@ true true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -60,6 +67,7 @@ string Parent + Parent true @@ -143,5 +151,14 @@ Position true + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/ColoredRectangle.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/ColoredRectangle.gutx index c67d9e88c..fb726bebf 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/ColoredRectangle.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/ColoredRectangle.gutx @@ -65,6 +65,13 @@ Dimensions true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -74,6 +81,7 @@ string Parent + Parent true @@ -158,5 +166,14 @@ Position true + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Component.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Component.gutx index c40ea7731..75ed9e0b7 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Component.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Component.gutx @@ -16,4 +16,5 @@ true + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Container.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Container.gutx index 4c3a3abb2..29c533dad 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Container.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Container.gutx @@ -3,6 +3,20 @@ Container Default + + int + AutoGridHorizontalCells + 4 + Children + true + + + int + AutoGridVerticalCells + 4 + Children + true + ChildrenLayout Children Layout @@ -64,6 +78,13 @@ Dimensions true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -73,6 +94,7 @@ string Parent + Parent true @@ -82,6 +104,13 @@ Flip and Rotation false + + float + StackSpacing + 0 + Children + true + bool Visible @@ -151,5 +180,14 @@ Position true + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/NineSlice.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/NineSlice.gutx index 8e5c57125..8e857d6ca 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/NineSlice.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/NineSlice.gutx @@ -24,6 +24,12 @@ Rendering true + + float? + CustomFrameTextureCoordinateWidth + Source + true + bool ExposeChildrenEvents @@ -65,6 +71,13 @@ Dimensions true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -74,6 +87,7 @@ string Parent + Parent true @@ -207,5 +221,14 @@ false + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Polygon.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Polygon.gutx index 08600d9c5..664872dfd 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Polygon.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Polygon.gutx @@ -30,6 +30,13 @@ Position true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -39,6 +46,7 @@ string Parent + Parent true @@ -123,5 +131,14 @@ + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Rectangle.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Rectangle.gutx index c89d47016..245b4d23c 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Rectangle.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Rectangle.gutx @@ -58,6 +58,13 @@ Dimensions true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -67,6 +74,7 @@ string Parent + Parent true @@ -151,5 +159,14 @@ Position true + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Sprite.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Sprite.gutx index e7c23235e..83295770e 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Sprite.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Sprite.gutx @@ -99,6 +99,13 @@ Dimensions true + + bool + IgnoredByParentSize + false + Parent + true + bool IsXamarinFormsControl @@ -108,6 +115,7 @@ string Parent + Parent true @@ -263,5 +271,14 @@ false + + string + VariableReferences + References + false + false + + + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Text.gutx b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Text.gutx index c8b5e7ffd..9b29b2e60 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Text.gutx +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Content/GumProject/Standards/Text.gutx @@ -100,6 +100,20 @@ Text true + + bool + IgnoredByParentSize + false + Parent + true + + + bool + IsBold + false + Font + true + bool IsItalic @@ -113,6 +127,13 @@ false true + + float + LineHeightMultiplier + 1 + Font + true + int? MaxLettersToShow @@ -129,6 +150,7 @@ string Parent + Parent true @@ -158,6 +180,20 @@ Text true + + TextOverflowHorizontalMode + TextOverflowHorizontalMode + 0 + Text + true + + + TextOverflowVerticalMode + TextOverflowVerticalMode + 0 + Text + true + bool UseCustomFont @@ -241,9 +277,18 @@ Position true + + string + VariableReferences + References + false + false + + ColorCategory + false Gray @@ -293,4 +338,5 @@ + \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/CompilerSettings.json b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/CompilerSettings.json new file mode 100644 index 000000000..9811bbf19 --- /dev/null +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/CompilerSettings.json @@ -0,0 +1 @@ +{"GenerateGlueControlManagerCode":false,"EmbedGameInGameTab":true,"RestartScreenOnLevelContentChange":true,"PortNumber":8117,"ShowScreenBoundsWhenViewingEntities":true,"RestartOnFailedCommands":false,"ShowGrid":true,"GridSize":32.0,"GridAlpha":0.15,"EnableSnapping":true,"SnapSize":8.0,"PolygonPointSnapSize":1.0,"SetBackgroundColor":false,"BackgroundRed":0,"BackgroundGreen":0,"BackgroundBlue":0,"ToolbarObjects":[]} \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/TreeViewPlugin.settings.user.json b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/TreeViewPlugin.settings.user.json new file mode 100644 index 000000000..34541c92b --- /dev/null +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/GlueSettings/TreeViewPlugin.settings.user.json @@ -0,0 +1 @@ +{"TreeNodeStates":[{"Text":"Entities","Children":[{"Text":"Player","Children":[{"Text":"Files","Children":[{"Text":"p2animations.achx","Children":[],"IsExpanded":false},{"Text":"p3animations.achx","Children":[],"IsExpanded":false},{"Text":"p4animations.achx","Children":[],"IsExpanded":false},{"Text":"PlatformerAnimations.achx","Children":[],"IsExpanded":false},{"Text":"PlatformerValuesStatic.csv","Children":[],"IsExpanded":false},{"Text":"TopDownValuesStatic.csv","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Objects","Children":[{"Text":"SpriteInstance","Children":[],"IsExpanded":false},{"Text":"AxisAlignedRectangleInstance","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Variables","Children":[{"Text":"X (float)","Children":[],"IsExpanded":false},{"Text":"Y (float)","Children":[],"IsExpanded":false},{"Text":"Z (float)","Children":[],"IsExpanded":false},{"Text":"GroundMovement (MultiplayerPlatformerDemo.DataTypes.PlatformerValues)","Children":[],"IsExpanded":false},{"Text":"AirMovement (MultiplayerPlatformerDemo.DataTypes.PlatformerValues)","Children":[],"IsExpanded":false},{"Text":"AfterDoubleJump (MultiplayerPlatformerDemo.DataTypes.PlatformerValues)","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"States","Children":[],"IsExpanded":false},{"Text":"Events","Children":[],"IsExpanded":false},{"Text":"Code","Children":[{"Text":"Player.cs","Children":[],"IsExpanded":false},{"Text":"PlayerFactory.Generated.cs","Children":[],"IsExpanded":false}],"IsExpanded":false}],"IsExpanded":false}],"IsExpanded":true},{"Text":"Screens","Children":[{"Text":"CharacterJoiningScreen","Children":[{"Text":"Files","Children":[{"Text":"CharacterJoiningScreenGum.gusx","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Objects","Children":[],"IsExpanded":false},{"Text":"Variables","Children":[],"IsExpanded":false},{"Text":"States","Children":[],"IsExpanded":false},{"Text":"Events","Children":[],"IsExpanded":false},{"Text":"Code","Children":[{"Text":"CharacterJoiningScreen.cs","Children":[],"IsExpanded":false}],"IsExpanded":false}],"IsExpanded":true},{"Text":"GameScreen","Children":[{"Text":"Files","Children":[{"Text":"GameScreenGum.gusx","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Objects","Children":[{"Text":"Layers","Children":[],"IsExpanded":false},{"Text":"Collision Relationships","Children":[{"Text":"PlayerListVsSolidCollision","Children":[],"IsExpanded":false},{"Text":"PlayerListVsCloudCollision","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Map","Children":[],"IsExpanded":false},{"Text":"SolidCollision","Children":[],"IsExpanded":false},{"Text":"CloudCollision","Children":[],"IsExpanded":false},{"Text":"PlayerList","Children":[],"IsExpanded":false},{"Text":"CameraControllingEntityInstance","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Variables","Children":[],"IsExpanded":false},{"Text":"States","Children":[],"IsExpanded":false},{"Text":"Events","Children":[],"IsExpanded":false},{"Text":"Code","Children":[{"Text":"GameScreen.cs","Children":[],"IsExpanded":false}],"IsExpanded":false}],"IsExpanded":false},{"Text":"Level1","Children":[{"Text":"Files","Children":[{"Text":"Level1Gum.gusx","Children":[],"IsExpanded":false},{"Text":"Level1Map.tmx","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Objects","Children":[{"Text":"Map","Children":[],"IsExpanded":false},{"Text":"SolidCollision","Children":[],"IsExpanded":false},{"Text":"CloudCollision","Children":[],"IsExpanded":false}],"IsExpanded":false},{"Text":"Variables","Children":[],"IsExpanded":false},{"Text":"States","Children":[],"IsExpanded":false},{"Text":"Events","Children":[],"IsExpanded":false},{"Text":"Code","Children":[{"Text":"Level1.cs","Children":[],"IsExpanded":false}],"IsExpanded":false}],"IsExpanded":false}],"IsExpanded":true},{"Text":"Global Content Files","Children":[{"Text":"GumProject.gumx","Children":[],"IsExpanded":false}],"IsExpanded":false}]} \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.csproj b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.csproj index e2de8f9c7..60c54227e 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.csproj +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.csproj @@ -215,6 +215,8 @@ + + diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.glux b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.glux index d42024b8f..ae5eac1a1 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.glux +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo.glux @@ -21,6 +21,7 @@ true MultiplayerPlatformerDemo.GumRuntimes.CharacterJoiningScreenGumRuntime + false false @@ -39,15 +40,14 @@ true MultiplayerPlatformerDemo.GumRuntimes.GameScreenGumRuntime + false false - - CreateEntitiesFromTiles - true - + Map + FlatRedBall.TileGraphics.LayeredTileMap AssociateWithFactory @@ -55,19 +55,24 @@ SourceType - FlatRedBallType + 2 + + bool + CreateEntitiesFromTiles + true + FlatRedBallType FlatRedBall.TileGraphics.LayeredTileMap - FlatRedBall.TileGraphics.LayeredTileMap - Map true true true false + SolidCollision + FlatRedBall.TileCollisions.TileShapeCollection AssociateWithFactory @@ -75,7 +80,7 @@ SourceType - FlatRedBallType + 2 CollisionCreationOptions @@ -124,14 +129,14 @@ FlatRedBallType FlatRedBall.TileCollisions.TileShapeCollection - FlatRedBall.TileCollisions.TileShapeCollection - SolidCollision true true true false + CloudCollision + FlatRedBall.TileCollisions.TileShapeCollection AssociateWithFactory @@ -139,7 +144,7 @@ SourceType - FlatRedBallType + 2 CollisionCreationOptions @@ -188,14 +193,14 @@ FlatRedBallType FlatRedBall.TileCollisions.TileShapeCollection - FlatRedBall.TileCollisions.TileShapeCollection - CloudCollision true true true false + PlayerList + FlatRedBall.Math.PositionedObjectList<T> AssociateWithFactory @@ -203,7 +208,7 @@ SourceType - FlatRedBallType + 2 SortAxis @@ -220,14 +225,14 @@ FlatRedBallType PositionedObjectList (Generic) - FlatRedBall.Math.PositionedObjectList<T> Entities\Player - PlayerList true true false + PlayerListVsSolidCollision + FlatRedBall.Math.Collision.DelegateListVsSingleRelationship<Entities.Player, FlatRedBall.TileCollisions.TileShapeCollection> AssociateWithFactory @@ -235,7 +240,7 @@ SourceType - FlatRedBallType + 2 IsAutoNameEnabled @@ -272,13 +277,13 @@ FlatRedBallType Collision Relationship - FlatRedBall.Math.Collision.DelegateListVsSingleRelationship<Entities.Player, FlatRedBall.TileCollisions.TileShapeCollection> - PlayerListVsSolidCollision true true false + PlayerListVsCloudCollision + FlatRedBall.Math.Collision.DelegateListVsSingleRelationship<Entities.Player, FlatRedBall.TileCollisions.TileShapeCollection> AssociateWithFactory @@ -286,7 +291,7 @@ SourceType - FlatRedBallType + 2 IsAutoNameEnabled @@ -323,22 +328,13 @@ FlatRedBallType Collision Relationship - FlatRedBall.Math.Collision.DelegateListVsSingleRelationship<Entities.Player, FlatRedBall.TileCollisions.TileShapeCollection> - PlayerListVsCloudCollision true true false - - Targets - PlayerList - - - string - Map - Map - + CameraControllingEntityInstance + FlatRedBall.Entities.CameraControllingEntity AssociateWithFactory @@ -346,13 +342,21 @@ SourceType - FlatRedBallType + 2 + + string + Map + Map + + + string + Targets + PlayerList + FlatRedBallType FlatRedBall.Entities.CameraControllingEntity - FlatRedBall.Entities.CameraControllingEntity - CameraControllingEntityInstance true true false @@ -373,21 +377,21 @@ true MultiplayerPlatformerDemo.GumRuntimes.Level1GumRuntime + false Screens/Level1/Level1Map.tmx true FlatRedBall.TileGraphics.LayeredTileMap + false false - - CreateEntitiesFromTiles - true - + Map + FlatRedBall.TileGraphics.LayeredTileMap AssociateWithFactory @@ -395,20 +399,25 @@ SourceType - File + 0 + + bool + CreateEntitiesFromTiles + true + File Screens/Level1/Level1Map.tmx Entire File (LayeredTileMap) - FlatRedBall.TileGraphics.LayeredTileMap - Map true true true false + SolidCollision + FlatRedBall.TileCollisions.TileShapeCollection AssociateWithFactory @@ -416,7 +425,7 @@ SourceType - FlatRedBallType + 2 CollisionCreationOptions @@ -433,14 +442,14 @@ FlatRedBallType FlatRedBall.TileCollisions.TileShapeCollection - FlatRedBall.TileCollisions.TileShapeCollection - SolidCollision true true true false + CloudCollision + FlatRedBall.TileCollisions.TileShapeCollection AssociateWithFactory @@ -448,7 +457,7 @@ SourceType - FlatRedBallType + 2 CollisionCreationOptions @@ -465,8 +474,6 @@ FlatRedBallType FlatRedBall.TileCollisions.TileShapeCollection - FlatRedBall.TileCollisions.TileShapeCollection - CloudCollision true true true @@ -492,12 +499,14 @@ true true + false Entities/Player/PlatformerAnimations.achx true FlatRedBall.Graphics.Animation.AnimationChainList + false Entities/Player/PlatformerValuesStatic.csv @@ -505,24 +514,28 @@ true true + false Entities/Player/p4animations.achx true FlatRedBall.Graphics.Animation.AnimationChainList + false Entities/Player/p2animations.achx true FlatRedBall.Graphics.Animation.AnimationChainList + false Entities/Player/p3animations.achx true FlatRedBall.Graphics.Animation.AnimationChainList + false false @@ -608,6 +621,18 @@ + SpriteInstance + FlatRedBall.Sprite + + + AssociateWithFactory + true + + + SourceType + 2 + + FlatRedBall.Graphics.Animation.AnimationChainList AnimationChains @@ -623,6 +648,15 @@ TextureScale 1 + FlatRedBallType + Sprite + true + true + false + + + AxisAlignedRectangleInstance + FlatRedBall.Math.Geometry.AxisAlignedRectangle AssociateWithFactory @@ -630,18 +664,9 @@ SourceType - FlatRedBallType + 2 - FlatRedBallType - Sprite - FlatRedBall.Sprite - SpriteInstance - true - true - false - - float Height @@ -662,20 +687,8 @@ Y 11 - - - AssociateWithFactory - true - - - SourceType - FlatRedBallType - - FlatRedBallType AxisAlignedRectangle - FlatRedBall.Math.Geometry.AxisAlignedRectangle - AxisAlignedRectangleInstance true true true @@ -685,29 +698,30 @@ Entities\Player + TopDownValues Entities/Player/TopDownValuesStatic.csv - TopDownValues true + PlatformerValues Entities/Player/PlatformerValuesStatic.csv - PlatformerValues true - false + false true true + GumProject/GumProject.gumx FileAdditionBehavior @@ -734,13 +748,14 @@ true - GumProject/GumProject.gumx true false true + false + false false @@ -755,6 +770,7 @@ + TileMapInfo @@ -766,29 +782,31 @@ EmbeddedAnimation - TileMapInfo true + TopDownValues Entities/Player/TopDownValuesStatic.csv - TopDownValues true + PlatformerValues Entities/Player/PlatformerValuesStatic.csv - PlatformerValues true true ../../ 6 + + + @@ -800,13 +818,15 @@ Custom - true true + true 256 224 - false + NoAspectRatio 16 9 + 0 + 0 true false false @@ -819,4 +839,6 @@ 1 + false + false \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Setup/CameraSetup.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Setup/CameraSetup.cs index 647d773af..dcf444e0d 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Setup/CameraSetup.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/Setup/CameraSetup.cs @@ -1,175 +1,245 @@ - // This is a generated file created by Glue. To change this file, edit the camera settings in Glue. - // To access the camera settings, push the camera icon. - using Camera = FlatRedBall.Camera; - namespace MultiplayerPlatformerDemo +// This is a generated file created by Glue. To change this file, edit the camera settings in Glue. +// To access the camera settings, push the camera icon. +using Camera = FlatRedBall.Camera; +namespace MultiplayerPlatformerDemo +{ + public class CameraSetupData { - public class CameraSetupData + public bool IsGenerateCameraDisplayCodeEnabled { get; set; } + public float Scale { get; set; } + public float ScaleGum { get; set; } + public bool Is2D { get; set; } + public int ResolutionWidth { get; set; } + public int ResolutionHeight { get; set; } + public decimal? AspectRatio { get; set; } + public decimal? AspectRatio2 { get; set; } + public bool AllowWindowResizing { get; set; } + public bool IsFullScreen { get; set; } + public ResizeBehavior ResizeBehavior { get; set; } + public ResizeBehavior ResizeBehaviorGum { get; set; } + public WidthOrHeight DominantInternalCoordinates { get; set; } + public Microsoft.Xna.Framework.Graphics.TextureFilter TextureFilter { get; set; } + + public decimal? EffectiveAspectRatio { - public float Scale { get; set; } - public float ScaleGum { get; set; } - public bool Is2D { get; set; } - public int ResolutionWidth { get; set; } - public int ResolutionHeight { get; set; } - public decimal? AspectRatio { get; set; } - public bool AllowWidowResizing { get; set; } - public bool IsFullScreen { get; set; } - public ResizeBehavior ResizeBehavior { get; set; } - public ResizeBehavior ResizeBehaviorGum { get; set; } - public WidthOrHeight DominantInternalCoordinates { get; set; } - public Microsoft.Xna.Framework.Graphics.TextureFilter TextureFilter { get; set; } - } - public enum ResizeBehavior - { - StretchVisibleArea, - IncreaseVisibleArea - } - public enum WidthOrHeight - { - Width, - Height - } - internal static class CameraSetup - { - static Microsoft.Xna.Framework.GraphicsDeviceManager graphicsDeviceManager; - public static CameraSetupData Data = new CameraSetupData - { - Scale = 500f, - ResolutionWidth = 256, - ResolutionHeight = 224, - Is2D = true, - IsFullScreen = false, - AllowWidowResizing = false, - TextureFilter = Microsoft.Xna.Framework.Graphics.TextureFilter.Point, - ResizeBehavior = ResizeBehavior.StretchVisibleArea, - ScaleGum = 100f, - ResizeBehaviorGum = ResizeBehavior.StretchVisibleArea, - DominantInternalCoordinates = WidthOrHeight.Height, - } - ; - internal static void ResetCamera (Camera cameraToReset = null) + get { - if (cameraToReset == null) + if(AspectRatio2 == null) { - cameraToReset = FlatRedBall.Camera.Main; + return AspectRatio; } - cameraToReset.Orthogonal = Data.Is2D; - if (Data.Is2D) + else if(AspectRatio == null) { - cameraToReset.OrthogonalHeight = Data.ResolutionHeight; - cameraToReset.OrthogonalWidth = Data.ResolutionWidth; - cameraToReset.FixAspectRatioYConstant(); + return AspectRatio2; } - else + else if(FlatRedBall.FlatRedBallServices.ClientHeight == 0) { - cameraToReset.UsePixelCoordinates3D(0); - var zoom = cameraToReset.DestinationRectangle.Height / (float)Data.ResolutionHeight; - cameraToReset.Z /= zoom; + // just in case: + return AspectRatio; } - if (Data.AspectRatio != null) + else { - SetAspectRatioTo(Data.AspectRatio.Value, Data.DominantInternalCoordinates, Data.ResolutionWidth, Data.ResolutionHeight); + // Neither AspectRatio nor 2 are null here + + var resolutionAspectRatio = FlatRedBall.FlatRedBallServices.ClientWidth / (decimal)FlatRedBall.FlatRedBallServices.ClientHeight; + + var minAspect = System.Math.Min(AspectRatio.Value, AspectRatio2.Value); + var maxAspect = System.Math.Max(AspectRatio.Value, AspectRatio2.Value); + + if(resolutionAspectRatio < minAspect) + { + return minAspect; + } + else if(resolutionAspectRatio > maxAspect) + { + return maxAspect; + } + else + { + // it's begween min and max, so return the resolution aspect ratio + return resolutionAspectRatio; + } } } - internal static void SetupCamera (Camera cameraToSetUp, Microsoft.Xna.Framework.GraphicsDeviceManager graphicsDeviceManager) + } + + } + public enum ResizeBehavior + { + StretchVisibleArea, + IncreaseVisibleArea + } + public enum WidthOrHeight + { + Width, + Height + } + internal static class CameraSetup + { + public static Microsoft.Xna.Framework.GraphicsDeviceManager GraphicsDeviceManager { get; private set; } + public static CameraSetupData Data = new CameraSetupData + { + Scale = 500f, + IsGenerateCameraDisplayCodeEnabled = true, + ResolutionWidth = 256, + ResolutionHeight = 224, + Is2D = true, + IsFullScreen = false, + AllowWindowResizing = false, + TextureFilter = Microsoft.Xna.Framework.Graphics.TextureFilter.Point, + ResizeBehavior = ResizeBehavior.StretchVisibleArea, + ScaleGum = 100f, + ResizeBehaviorGum = ResizeBehavior.StretchVisibleArea, + DominantInternalCoordinates = WidthOrHeight.Height, + } + ; + /// + /// Applies resolution and aspect ratio values to the FlatRedBall camera. If Gum is part of the project, + /// then the Gum resolution will be applied. Note that this does not call Layout on the contained Gum objects, + /// so this may need to be called explicitly if ResetCamera is called in custom code. + /// + internal static void ResetCamera (Camera cameraToReset = null) + { + if (cameraToReset == null) { - CameraSetup.graphicsDeviceManager = graphicsDeviceManager; - FlatRedBall.FlatRedBallServices.GraphicsOptions.TextureFilter = Data.TextureFilter; - ResetWindow(); - ResetCamera(cameraToSetUp); - ResetGumResolutionValues(); - FlatRedBall.FlatRedBallServices.GraphicsOptions.SizeOrOrientationChanged += HandleResolutionChange; + cameraToReset = FlatRedBall.Camera.Main; } - internal static void ResetWindow () + cameraToReset.Orthogonal = Data.Is2D; + if (Data.Is2D) { - #if WINDOWS || DESKTOP_GL - FlatRedBall.FlatRedBallServices.Game.Window.AllowUserResizing = Data.AllowWidowResizing; - if (Data.IsFullScreen) - { - #if DESKTOP_GL - graphicsDeviceManager.HardwareModeSwitch = false; - FlatRedBall.FlatRedBallServices.Game.Window.Position = new Microsoft.Xna.Framework.Point(0,0); - FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height, FlatRedBall.Graphics.WindowedFullscreenMode.FullscreenBorderless); - #elif WINDOWS - System.IntPtr hWnd = FlatRedBall.FlatRedBallServices.Game.Window.Handle; - var control = System.Windows.Forms.Control.FromHandle(hWnd); - var form = control.FindForm(); - form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; - form.WindowState = System.Windows.Forms.FormWindowState.Maximized; - #endif - } - else - { - var width = (int)(Data.ResolutionWidth * Data.Scale / 100.0f); - var height = (int)(Data.ResolutionHeight * Data.Scale / 100.0f); - // subtract to leave room for windows borders - var maxWidth = Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width - 6; - var maxHeight = Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height - 28; - width = System.Math.Min(width, maxWidth); - height = System.Math.Min(height, maxHeight); - if (FlatRedBall.FlatRedBallServices.Game.Window.Position.Y < 25) - { - FlatRedBall.FlatRedBallServices.Game.Window.Position = new Microsoft.Xna.Framework.Point(FlatRedBall.FlatRedBallServices.Game.Window.Position.X, 25); - } - FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(width, height); - } - #elif IOS || ANDROID - FlatRedBall.FlatRedBallServices.GraphicsOptions.SetFullScreen(FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth, FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight); - #elif UWP - if (Data.IsFullScreen) - { - FlatRedBall.FlatRedBallServices.GraphicsOptions.SetFullScreen(Data.ResolutionWidth, Data.ResolutionHeight); - } - else + cameraToReset.OrthogonalHeight = Data.ResolutionHeight; + cameraToReset.OrthogonalWidth = Data.ResolutionWidth; + cameraToReset.FixAspectRatioYConstant(); + } + else + { + cameraToReset.UsePixelCoordinates3D(0); + var zoom = cameraToReset.DestinationRectangle.Height / (float)Data.ResolutionHeight; + cameraToReset.Z /= zoom; + } + SetAspectRatioTo(Data.EffectiveAspectRatio, Data.DominantInternalCoordinates, Data.ResolutionWidth, Data.ResolutionHeight); + ResetGumResolutionValues(); + } + internal static void SetupCamera (Camera cameraToSetUp, Microsoft.Xna.Framework.GraphicsDeviceManager graphicsDeviceManager) + { + CameraSetup.GraphicsDeviceManager = graphicsDeviceManager; + FlatRedBall.FlatRedBallServices.GraphicsOptions.TextureFilter = Data.TextureFilter; + ResetWindow(); + ResetCamera(cameraToSetUp); + ResetGumResolutionValues(); + FlatRedBall.FlatRedBallServices.GraphicsOptions.SizeOrOrientationChanged += HandleResolutionChange; + } + internal static void ResetWindow () + { + #if WINDOWS || DESKTOP_GL + FlatRedBall.FlatRedBallServices.Game.Window.AllowUserResizing = Data.AllowWindowResizing; + if (Data.IsFullScreen) + { + #if DESKTOP_GL + #if DEBUG + if (GraphicsDeviceManager == null) { - FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(Data.ResolutionWidth, Data.ResolutionHeight); - var newWindowSize = new Windows.Foundation.Size(Data.ResolutionWidth, Data.ResolutionHeight); - Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().TryResizeView(newWindowSize); + throw new System.InvalidOperationException("ResetWindow cannot be called until SetupCamera is called first"); } #endif + GraphicsDeviceManager.HardwareModeSwitch = false; + FlatRedBall.FlatRedBallServices.Game.Window.Position = new Microsoft.Xna.Framework.Point(0,0); + FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height, FlatRedBall.Graphics.WindowedFullscreenMode.FullscreenBorderless); + #elif FNA + FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height, FlatRedBall.Graphics.WindowedFullscreenMode.FullscreenBorderless); + #elif WINDOWS + System.IntPtr hWnd = FlatRedBall.FlatRedBallServices.Game.Window.Handle; + var control = System.Windows.Forms.Control.FromHandle(hWnd); + var form = control.FindForm(); + form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + form.WindowState = System.Windows.Forms.FormWindowState.Maximized; + #endif } - private static void HandleResolutionChange (object sender, System.EventArgs args) + else { - if (Data.AspectRatio != null) - { - SetAspectRatioTo(Data.AspectRatio.Value, Data.DominantInternalCoordinates, Data.ResolutionWidth, Data.ResolutionHeight); - } - if (Data.Is2D && Data.ResizeBehavior == ResizeBehavior.IncreaseVisibleArea) + var width = (int)(Data.ResolutionWidth * Data.Scale / 100.0f); + var height = (int)(Data.ResolutionHeight * Data.Scale / 100.0f); + // subtract to leave room for windows borders + var maxWidth = Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width - 6; + var maxHeight = Microsoft.Xna.Framework.Graphics.GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height - 28; + width = System.Math.Min(width, maxWidth); + height = System.Math.Min(height, maxHeight); + #if MONOGAME + if (FlatRedBall.FlatRedBallServices.Game.Window.Position.Y < 25) { - FlatRedBall.Camera.Main.OrthogonalHeight = FlatRedBall.Camera.Main.DestinationRectangle.Height / (Data.Scale/ 100.0f); - FlatRedBall.Camera.Main.FixAspectRatioYConstant(); + FlatRedBall.FlatRedBallServices.Game.Window.Position = new Microsoft.Xna.Framework.Point(FlatRedBall.FlatRedBallServices.Game.Window.Position.X, 25); } - ResetGumResolutionValues(); + #endif + FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(width, height); } - public static void ResetGumResolutionValues () + #elif IOS || ANDROID + FlatRedBall.FlatRedBallServices.GraphicsOptions.SetFullScreen(FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth, FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight); + #elif UWP + if (Data.IsFullScreen) { - if (Data.ResizeBehaviorGum == ResizeBehavior.IncreaseVisibleArea) - { - global::RenderingLibrary.SystemManagers.Default.Renderer.Camera.Zoom = Data.Scale/100.0f; - Gum.Wireframe.GraphicalUiElement.CanvasWidth = Gum.Managers.ObjectFinder.Self.GumProjectSave.DefaultCanvasWidth; - Gum.Wireframe.GraphicalUiElement.CanvasHeight = Gum.Managers.ObjectFinder.Self.GumProjectSave.DefaultCanvasHeight; - } - else + FlatRedBall.FlatRedBallServices.GraphicsOptions.SetFullScreen(Data.ResolutionWidth, Data.ResolutionHeight); + } + else + { + FlatRedBall.FlatRedBallServices.GraphicsOptions.SetResolution(Data.ResolutionWidth, Data.ResolutionHeight); + var newWindowSize = new Windows.Foundation.Size(Data.ResolutionWidth, Data.ResolutionHeight); + Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().TryResizeView(newWindowSize); + } + #endif + } + private static void HandleResolutionChange (object sender, System.EventArgs args) + { + SetAspectRatioTo(Data.EffectiveAspectRatio, Data.DominantInternalCoordinates, Data.ResolutionWidth, Data.ResolutionHeight); + if (Data.Is2D && Data.ResizeBehavior == ResizeBehavior.IncreaseVisibleArea) + { + FlatRedBall.Camera.Main.OrthogonalHeight = FlatRedBall.Camera.Main.DestinationRectangle.Height / (Data.Scale/ 100.0f); + FlatRedBall.Camera.Main.FixAspectRatioYConstant(); + } + ResetGumResolutionValues(); + } + /// + /// Sets the Gum GraphicalUiElement's CanvasWidth and CanvasHeight as well as all Layer Zoom values. + /// + public static void ResetGumResolutionValues () + { + if (Data.ResizeBehaviorGum == ResizeBehavior.IncreaseVisibleArea) + { + global::RenderingLibrary.SystemManagers.Default.Renderer.Camera.Zoom = Data.Scale/100.0f; + Gum.Wireframe.GraphicalUiElement.CanvasWidth = FlatRedBall.Camera.Main.DestinationRectangle.Width; + Gum.Wireframe.GraphicalUiElement.CanvasHeight = FlatRedBall.Camera.Main.DestinationRectangle.Height; + } + else + { + Gum.Wireframe.GraphicalUiElement.CanvasHeight = Data.ResolutionHeight / (Data.ScaleGum/100.0f); + if (Data.EffectiveAspectRatio != null) { - Gum.Wireframe.GraphicalUiElement.CanvasHeight = Data.ResolutionHeight / (Data.ScaleGum/100.0f); - if (Data.AspectRatio != null) + + + if(Data.DominantInternalCoordinates == WidthOrHeight.Height) + { + Gum.Wireframe.GraphicalUiElement.CanvasHeight = Data.ResolutionHeight / (Data.ScaleGum / 100.0f); + Gum.Wireframe.GraphicalUiElement.CanvasWidth = FlatRedBall.Math.MathFunctions.RoundToInt(Gum.Wireframe.GraphicalUiElement.CanvasHeight * (double)Data.EffectiveAspectRatio.Value); + } + else { - + Gum.Wireframe.GraphicalUiElement.CanvasWidth = Data.ResolutionWidth / (Data.ScaleGum/100.0f); + Gum.Wireframe.GraphicalUiElement.CanvasHeight = FlatRedBall.Math.MathFunctions.RoundToInt(Gum.Wireframe.GraphicalUiElement.CanvasHeight / (double)Data.EffectiveAspectRatio.Value); + } - Gum.Wireframe.GraphicalUiElement.CanvasWidth = Data.ResolutionWidth / (Data.ScaleGum/100.0f); var resolutionAspectRatio = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth / (decimal)FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; int destinationRectangleWidth; int destinationRectangleHeight; int x = 0; int y = 0; - if (Data.AspectRatio.Value > resolutionAspectRatio) + if (Data.EffectiveAspectRatio.Value > resolutionAspectRatio) { destinationRectangleWidth = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth; - destinationRectangleHeight = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleWidth / (float)Data.AspectRatio.Value); + destinationRectangleHeight = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleWidth / (float)Data.EffectiveAspectRatio.Value); } else { destinationRectangleHeight = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; - destinationRectangleWidth = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleHeight * (float)Data.AspectRatio.Value); + destinationRectangleWidth = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleHeight * (float)Data.EffectiveAspectRatio.Value); } var canvasHeight = Gum.Wireframe.GraphicalUiElement.CanvasHeight; @@ -188,10 +258,10 @@ public static void ResetGumResolutionValues () } - } - else - { - + } + else + { + // since a fixed aspect ratio isn't specified, adjust the width according to the // current game aspect ratio and the canvas height @@ -215,100 +285,146 @@ public static void ResetGumResolutionValues () } } - } } } - private static void SetAspectRatioTo (decimal aspectRatio, WidthOrHeight dominantInternalCoordinates, int desiredWidth, int desiredHeight) + } + private static void SetAspectRatioTo (decimal? aspectRatio, WidthOrHeight dominantInternalCoordinates, int desiredWidth, int desiredHeight) + { + var resolutionAspectRatio = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth / (decimal)FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; + int destinationRectangleWidth; + int destinationRectangleHeight; + int x = 0; + int y = 0; + if (aspectRatio == null) + { + destinationRectangleWidth = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth; + destinationRectangleHeight = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; + } + else if (aspectRatio > resolutionAspectRatio) + { + destinationRectangleWidth = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth; + destinationRectangleHeight = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleWidth / (float)aspectRatio); + y = (FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight - destinationRectangleHeight) / 2; + } + else { - var resolutionAspectRatio = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth / (decimal)FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; - int destinationRectangleWidth; - int destinationRectangleHeight; - int x = 0; - int y = 0; - if (aspectRatio > resolutionAspectRatio) + destinationRectangleHeight = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; + destinationRectangleWidth = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleHeight * (float)aspectRatio); + x = (FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth - destinationRectangleWidth) / 2; + } + foreach (var camera in FlatRedBall.SpriteManager.Cameras) + { + int currentX = x; + int currentY = y; + int currentWidth = destinationRectangleWidth; + int currentHeight = destinationRectangleHeight; + switch(camera.CurrentSplitScreenViewport) { - destinationRectangleWidth = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth; - destinationRectangleHeight = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleWidth / (float)aspectRatio); - y = (FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight - destinationRectangleHeight) / 2; + case Camera.SplitScreenViewport.TopLeft: + currentWidth /= 2; + currentHeight /= 2; + break; + case Camera.SplitScreenViewport.TopRight: + currentX = x + destinationRectangleWidth / 2; + currentWidth /= 2; + currentHeight /= 2; + break; + case Camera.SplitScreenViewport.BottomLeft: + currentY = y + destinationRectangleHeight / 2; + currentWidth /= 2; + currentHeight /= 2; + break; + case Camera.SplitScreenViewport.BottomRight: + currentX = x + destinationRectangleWidth / 2; + currentY = y + destinationRectangleHeight / 2; + currentWidth /= 2; + currentHeight /= 2; + break; + case Camera.SplitScreenViewport.TopHalf: + currentHeight /= 2; + break; + case Camera.SplitScreenViewport.BottomHalf: + currentY = y + destinationRectangleHeight / 2; + currentHeight /= 2; + break; + case Camera.SplitScreenViewport.LeftHalf: + currentWidth /= 2; + break; + case Camera.SplitScreenViewport.RightHalf: + currentX = x + destinationRectangleWidth / 2; + currentWidth /= 2; + break; } - else + camera.DestinationRectangle = new Microsoft.Xna.Framework.Rectangle(currentX, currentY, currentWidth, currentHeight); + if (dominantInternalCoordinates == WidthOrHeight.Height) { - destinationRectangleHeight = FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionHeight; - destinationRectangleWidth = FlatRedBall.Math.MathFunctions.RoundToInt(destinationRectangleHeight * (float)aspectRatio); - x = (FlatRedBall.FlatRedBallServices.GraphicsOptions.ResolutionWidth - destinationRectangleWidth) / 2; + camera.OrthogonalHeight = desiredHeight; + camera.FixAspectRatioYConstant(); } - foreach (var camera in FlatRedBall.SpriteManager.Cameras) + else { - int currentX = x; - int currentY = y; - int currentWidth = destinationRectangleWidth; - int currentHeight = destinationRectangleHeight; - switch(camera.CurrentSplitScreenViewport) - { - case Camera.SplitScreenViewport.TopLeft: - currentWidth /= 2; - currentHeight /= 2; - break; - case Camera.SplitScreenViewport.TopRight: - currentX = x + destinationRectangleWidth / 2; - currentWidth /= 2; - currentHeight /= 2; - break; - case Camera.SplitScreenViewport.BottomLeft: - currentY = y + destinationRectangleHeight / 2; - currentWidth /= 2; - currentHeight /= 2; - break; - case Camera.SplitScreenViewport.BottomRight: - currentX = x + destinationRectangleWidth / 2; - currentY = y + destinationRectangleHeight / 2; - currentWidth /= 2; - currentHeight /= 2; - break; - case Camera.SplitScreenViewport.TopHalf: - currentHeight /= 2; - break; - case Camera.SplitScreenViewport.BottomHalf: - currentY = y + destinationRectangleHeight / 2; - currentHeight /= 2; - break; - case Camera.SplitScreenViewport.LeftHalf: - currentWidth /= 2; - break; - case Camera.SplitScreenViewport.RightHalf: - currentX = x + destinationRectangleWidth / 2; - currentWidth /= 2; - break; - } - camera.DestinationRectangle = new Microsoft.Xna.Framework.Rectangle(currentX, currentY, currentWidth, currentHeight); - if (dominantInternalCoordinates == WidthOrHeight.Height) - { - camera.OrthogonalHeight = desiredHeight; - camera.FixAspectRatioYConstant(); - } - else - { - camera.OrthogonalWidth = desiredWidth; - camera.FixAspectRatioXConstant(); - } + camera.OrthogonalWidth = desiredWidth; + camera.FixAspectRatioXConstant(); } } - + } + #if WINDOWS - internal static readonly System.IntPtr HWND_TOPMOST = new System.IntPtr(-1); - internal static readonly System.IntPtr HWND_NOTOPMOST = new System.IntPtr(-2); - internal static readonly System.IntPtr HWND_TOP = new System.IntPtr(0); - internal static readonly System.IntPtr HWND_BOTTOM = new System.IntPtr(1); + internal static readonly System.IntPtr HWND_TOPMOST = new System.IntPtr(-1); + internal static readonly System.IntPtr HWND_NOTOPMOST = new System.IntPtr(-2); + internal static readonly System.IntPtr HWND_TOP = new System.IntPtr(0); + internal static readonly System.IntPtr HWND_BOTTOM = new System.IntPtr(1); - [System.Flags] - internal enum SetWindowPosFlags : uint - { - IgnoreMove = 0x0002, - IgnoreResize = 0x0001, - } + [System.Flags] + internal enum SetWindowPosFlags : uint + { + IgnoreMove = 0x0002, + IgnoreResize = 0x0001, + } + + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] + public struct RECT + + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + [System.Runtime.InteropServices.DllImport("user32.dll")] + [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)] + private static extern bool GetWindowRect(System.IntPtr hWnd, out RECT lpRect); + - [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] - internal static extern bool SetWindowPos(System.IntPtr hWnd, System.IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); + [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] + internal static extern bool SetWindowPos(System.IntPtr hWnd, System.IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); + + public static Microsoft.Xna.Framework.Rectangle GetWindowRectangle() + { + var hWnd = FlatRedBall.FlatRedBallServices.Game.Window.Handle; + + GetWindowRect(hWnd, out RECT rectInner); + + return new Microsoft.Xna.Framework.Rectangle( + rectInner.Left, + rectInner.Top, + rectInner.Right - rectInner.Left, + rectInner.Bottom - rectInner.Top); + + } + + public static void SetWindowPosition(int x, int y) + { + var hWnd = FlatRedBall.FlatRedBallServices.Game.Window.Handle; + SetWindowPos( + hWnd, + HWND_TOPMOST, + x, y, + 0, 0, //FlatRedBallServices.GraphicsOptions.ResolutionWidth, FlatRedBallServices.GraphicsOptions.ResolutionHeight, + SetWindowPosFlags.IgnoreResize + ); + } public static void SetWindowAlwaysOnTop() { @@ -347,5 +463,5 @@ public static void UnsetWindowAlwaysOnTop() #endif - } } +} diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableListVsTileShapeCollectionRelationship.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableListVsTileShapeCollectionRelationship.cs index e4cfaf25b..0051801cc 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableListVsTileShapeCollectionRelationship.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableListVsTileShapeCollectionRelationship.cs @@ -1,4 +1,14 @@ -using FlatRedBall.Math.Geometry; +#define PreVersion +#define HasFormsObject +#define AddedGeneratedGame1 +#define ListsHaveAssociateWithFactoryBool +#define GumGueHasGetAnimation +#define CsvInheritanceSupport +#define IPositionedSizedObjectInEngine +#define NugetPackageInCsproj + + +using FlatRedBall.Math.Geometry; using FlatRedBall.TileCollisions; using System; using System.Collections.Generic; @@ -14,11 +24,11 @@ public class CollidableListVsTileShapeCollectionRelationship : { CollidableVsTileShapeCollectionData data; - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionCircle = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionRectangle = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionPolygon = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionLine = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionCollidable = subCollisionFunc; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionCircle = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionRectangle = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionPolygon = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionLine = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionCollidable = subCollisionFunc; data.FirstSubObjectName = subObjectName; } public Action CollisionOccurred; @@ -70,33 +80,62 @@ public override bool DoCollisions() { var singleObject = list[i]; - var didCollide = false; - // todo - tile shape collections need to report their deep collision, they don't currently: - if (CollisionType == CollisionType.EventOnlyCollision) - { - didCollide = data.CollideAgainstConsiderSubCollisionEventOnly(singleObject, CollisionLimit); - } - else if (CollisionType == CollisionType.MoveCollision) - { - didCollide = data.CollideAgainstConsiderSubCollisionMove(singleObject); - } - else if (CollisionType == CollisionType.BounceCollision) - { - didCollide = data.CollideAgainstConsiderSubCollisionBounce(singleObject, bounceElasticity); - } - else - { - throw new NotImplementedException(); - } +#if CollisionRelationshipManualPhysics + bool didCollide = DoCollisionPhysicsInner(singleObject, ArePhysicsAppliedAutomatically == false); +#else + bool didCollide = DoCollisionPhysicsInner(singleObject, false); +#endif if (didCollide) { didCollisionOccur = true; CollisionOccurred?.Invoke(singleObject, data.TileShapeCollection); + +#if ICollidableHasItemsCollidedAgainst + singleObject.ItemsCollidedAgainst.Add(data.TileShapeCollection.Name); +#endif +#if ICollidableHasObjectsCollidedAgainst + singleObject.ObjectsCollidedAgainst.Add(data.TileShapeCollection); +#endif } } } return didCollisionOccur; } + + public bool DoCollisionPhysics(FirstCollidableT singleObject) + { + return DoCollisionPhysicsInner(singleObject, false); + } + + private bool DoCollisionPhysicsInner(FirstCollidableT singleObject, bool eventOnly) + { + bool didCollide; + // todo - tile shape collections need to report their deep collision, they don't currently: + if (CollisionType == CollisionType.EventOnlyCollision || eventOnly) + { + didCollide = data.CollideAgainstConsiderSubCollisionEventOnly(singleObject, CollisionLimit); + } + else if (CollisionType == CollisionType.MoveCollision) + { + didCollide = data.CollideAgainstConsiderSubCollisionMove(singleObject); + } + else if (CollisionType == CollisionType.BounceCollision) + { + didCollide = data.CollideAgainstConsiderSubCollisionBounce(singleObject, bounceElasticity); + } +#if CollisionRelationshipsSupportMoveSoft + else if (CollisionType == CollisionType.MoveSoftCollision) + { + throw new NotImplementedException("soft collision against tile shape collections is not currently supported"); + } +#endif + else + { + throw new NotImplementedException(); + } + + return didCollide; + } } } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableVsTileShapeCollectionRelationship.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableVsTileShapeCollectionRelationship.cs index 21b889bc6..22ee0a236 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableVsTileShapeCollectionRelationship.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/CollidableVsTileShapeCollectionRelationship.cs @@ -1,4 +1,15 @@ -using FlatRedBall; +#define PreVersion +#define HasFormsObject +#define AddedGeneratedGame1 +#define ListsHaveAssociateWithFactoryBool +#define GumGueHasGetAnimation +#define CsvInheritanceSupport +#define IPositionedSizedObjectInEngine +#define NugetPackageInCsproj + + + +using FlatRedBall; using FlatRedBall.Math.Collision; using FlatRedBall.Math.Geometry; using FlatRedBall.TileCollisions; @@ -22,6 +33,14 @@ class CollidableVsTileShapeCollectionData public Func firstSubCollisionLine; public Func firstSubCollisionCollidable; + /// + /// Stores the name of the first sub object name used in sub collision. If null, no object. Setting this does not + /// change the functionality of the collidable object, it is only used by the level editor to determine if a collision + /// relationship has changed. This should not be changed unless the sub collision has changed, and this usually is done + /// in generated code. + /// + public string FirstSubObjectName { get; set; } + public CollidableVsTileShapeCollectionData(TileShapeCollection tileShapeCollection) { if (tileShapeCollection == null) @@ -148,7 +167,11 @@ public bool CollideAgainstConsiderSubCollisionBounce(FirstCollidableT singleObje /// public static bool DoFirstCollisionLineVsShapeCollection(Line line, TileShapeCollection tileShapeCollection) { +#if ShapeManagerCollideAgainstClosest return tileShapeCollection.CollideAgainstClosest(line); +#else + return false; +#endif } } @@ -158,11 +181,11 @@ public class CollidableVsTileShapeCollectionRelationship : Col { CollidableVsTileShapeCollectionData data; - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionCircle = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionRectangle = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionPolygon = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionLine = subCollisionFunc; } - public void SetFirstSubCollision(Func subCollisionFunc) { data.firstSubCollisionCollidable = subCollisionFunc; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionCircle = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionRectangle = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionPolygon = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionLine = subCollisionFunc; data.FirstSubObjectName = subObjectName; } + public void SetFirstSubCollision(Func subCollisionFunc, string subObjectName = null) { data.firstSubCollisionCollidable = subCollisionFunc; data.FirstSubObjectName = subObjectName; } public Action CollisionOccurred; @@ -197,35 +220,65 @@ public override bool DoCollisions() { skippedFrames = 0; - var didCollide = false; - // todo - tile shape collections need to report their deep collision, they don't currently: - if (CollisionType == CollisionType.EventOnlyCollision) - { - didCollide = data.CollideAgainstConsiderSubCollisionEventOnly(singleObject, CollisionLimit); - } - else if (CollisionType == CollisionType.MoveCollision) - { - didCollide = data.CollideAgainstConsiderSubCollisionMove(singleObject); - } - else if (CollisionType == CollisionType.BounceCollision) - { - didCollide = data.CollideAgainstConsiderSubCollisionBounce(singleObject, bounceElasticity); - } - else - { - throw new NotImplementedException(); - } +#if CollisionRelationshipManualPhysics + bool didCollide = DoCollisionPhysicsInner(ArePhysicsAppliedAutomatically == false); +#else + bool didCollide = DoCollisionPhysicsInner(false); +#endif + if (didCollide) { CollisionOccurred?.Invoke(singleObject, data.TileShapeCollection); didCollisionOccur = true; + +#if ICollidableHasItemsCollidedAgainst + singleObject.ItemsCollidedAgainst.Add(data.TileShapeCollection.Name); +#endif +#if ICollidableHasObjectsCollidedAgainst + singleObject.ObjectsCollidedAgainst.Add(data.TileShapeCollection); +#endif } } } return didCollisionOccur; } + + public bool DoCollisionPhysics() + { + return DoCollisionPhysicsInner(false); + } + + private bool DoCollisionPhysicsInner(bool eventOnly) + { + var didCollide = false; + // todo - tile shape collections need to report their deep collision, they don't currently: + if (eventOnly || CollisionType == CollisionType.EventOnlyCollision) + { + didCollide = data.CollideAgainstConsiderSubCollisionEventOnly(singleObject, CollisionLimit); + } + else if (CollisionType == CollisionType.MoveCollision) + { + didCollide = data.CollideAgainstConsiderSubCollisionMove(singleObject); + } + else if (CollisionType == CollisionType.BounceCollision) + { + didCollide = data.CollideAgainstConsiderSubCollisionBounce(singleObject, bounceElasticity); + } +#if CollisionRelationshipsSupportMoveSoft + else if (CollisionType == CollisionType.MoveSoftCollision) + { + throw new NotImplementedException("soft collision against tile shape collections is not currently supported"); + } +#endif + else + { + throw new NotImplementedException(); + } + + return didCollide; + } } } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/TileShapeCollection.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/TileShapeCollection.cs index bd3421e16..eb1fe866b 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/TileShapeCollection.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileCollisions/TileShapeCollection.cs @@ -1,16 +1,47 @@ -using FlatRedBall.Math; +#define PreVersion +#define HasFormsObject +#define AddedGeneratedGame1 +#define ListsHaveAssociateWithFactoryBool +#define GumGueHasGetAnimation +#define CsvInheritanceSupport +#define IPositionedSizedObjectInEngine +#define NugetPackageInCsproj + + +using FlatRedBall.Math; using FlatRedBall.Math.Geometry; using FlatRedBall.TileGraphics; +using FlatRedBall.Utilities; +using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using AARect = FlatRedBall.Math.Geometry.AxisAlignedRectangle; - namespace FlatRedBall.TileCollisions { - public partial class TileShapeCollection + #region Enums + + public enum RepositionUpdateStyle + { + /// + /// No reposition direction changes will be performed. + /// + None, + /// + /// Reposition directions will be assigned to push objects outward + /// + Outward, + /// + /// Reposition directions will be assigned to push objects upward (for cloud collision) + /// + Upward, + } + + #endregion + + public partial class TileShapeCollection : INameable, ICollidable { #region Fields @@ -25,7 +56,8 @@ public partial class TileShapeCollection /// The bottommost edge of the map. This will correspond with the bottom edge of an AxisAlignedRectangle. /// public float BottomSeedY = 0; - float mGridSize; + float mGridSizeX; + float mGridSizeY; bool mVisible = true; @@ -54,7 +86,7 @@ public Axis SortAxis public float GridSize { - get { return mGridSize; } + get { return mGridSizeX; } set { #if DEBUG @@ -65,11 +97,41 @@ public float GridSize #endif - mGridSize = value; - mShapes.MaxAxisAlignedRectanglesScale = mGridSize; - mShapes.MaxPolygonRadius = mGridSize; + mGridSizeX = value; + mGridSizeY = value; +#if ShapeCollectionHasMaxAxisAlignedRectanglesRadiusX + mShapes.MaxAxisAlignedRectanglesRadiusX = mGridSizeX; + mShapes.MaxAxisAlignedRectanglesRadiusY = mGridSizeY; +#else + mShapes.MaxAxisAlignedRectanglesScale = mGridSizeX; +#endif + mShapes.MaxPolygonRadius = mGridSizeX; + } + } + +#if ShapeCollectionHasMaxAxisAlignedRectanglesRadiusX + public float GridSizeX + { + get => mGridSizeX; + set + { + mGridSizeX = value; + mShapes.MaxAxisAlignedRectanglesRadiusX = mGridSizeX; + mShapes.MaxPolygonRadius = mGridSizeX; + } + } + + public float GridSizeY + { + get => mGridSizeY; + set + { + mGridSizeY = value; + mShapes.MaxAxisAlignedRectanglesRadiusY = mGridSizeY; + mShapes.MaxPolygonRadius = mGridSizeY; } } +#endif public PositionedObjectList Rectangles { @@ -86,6 +148,11 @@ public PositionedObjectList Polygons public List LastCollisionPolygons => mShapes.LastCollisionPolygons; + + /// + /// Returns the rectangles which collided in the last successful collision. These can be checked in a collision event + /// to perform custom physics logic. + /// public List LastCollisionAxisAlignedRectangles => mShapes.LastCollisionAxisAlignedRectangles; public bool Visible @@ -134,7 +201,28 @@ public Microsoft.Xna.Framework.Color Color public bool AdjustRepositionDirectionsOnAddAndRemove { get; set; } = true; - #endregion + ShapeCollection ICollidable.Collision => this.mShapes; + +#if ICollidableHasItemsCollidedAgainst + + + HashSet ICollidable.ItemsCollidedAgainst => this.mShapes.ItemsCollidedAgainst; + + HashSet ICollidable.LastFrameItemsCollidedAgainst => this.mShapes.LastFrameItemsCollidedAgainst; + +#endif + +#if ICollidableHasObjectsCollidedAgainst + + HashSet ICollidable.ObjectsCollidedAgainst => this.mShapes.ObjectsCollidedAgainst; + + HashSet ICollidable.LastFrameObjectsCollidedAgainst => this.mShapes.LastFrameObjectsCollidedAgainst; +#endif + + public RepositionUpdateStyle RepositionUpdateStyle { get; set; } = RepositionUpdateStyle.Outward; + + +#endregion public TileShapeCollection() { @@ -143,9 +231,23 @@ public TileShapeCollection() } - public void AddToLayer(FlatRedBall.Graphics.Layer layer) + public void AddToLayer(FlatRedBall.Graphics.Layer layer, bool makeAutomaticallyUpdated = false) { + // Note - the makeAutomaticallyUpdated method has been added in July 2021 + // This is a necessary addition to make addition of TileshapeCollections much + // faster than before. Unfortunately this is not a generated file but a copied + // file so it cannot use the Glue project version to optionally make this call + // Therefore, older projects may experience compile errors here. To solve this you + // can do one of the following: + // 1. Update the FRB libraries that your project references to get this latest method (recommended) + // 2. Downgrade to a version of Glue prior to July 2021 which will not include this method + // 3. Re-compile your own version of the plugin for Glue and modify this code + // 4. Remove this parameter by hand whenever this file is re-generated. This is painful! +#if SupportsNamedSubcollisions + this.mShapes.AddToManagers(layer, makeAutomaticallyUpdated); +#else this.mShapes.AddToManagers(layer); +#endif } public void AttachTo(PositionedObject newParent, bool changeRelative = true) @@ -153,6 +255,13 @@ public void AttachTo(PositionedObject newParent, bool changeRelative = true) mShapes.AttachTo(newParent, changeRelative); } + public TileShapeCollection Clone() + { + var toReturn = (TileShapeCollection)this.MemberwiseClone(); + toReturn.mShapes = this.mShapes.Clone(); + return toReturn; + } + public void CopyAbsoluteToRelative() { mShapes.CopyAbsoluteToRelative(); @@ -194,267 +303,109 @@ public bool CollideAgainstSolid(Line movableObject) return toReturn; } + /// + /// Returns whether this instance collides against the argument AxisAlignedRectangle. + /// + /// The AxisAlignedRectangle to test collision against. + /// Whether collision has occurred. public bool CollideAgainst(AxisAlignedRectangle rectangle) { return mShapes.CollideAgainst(rectangle, true, mSortAxis); } + /// + /// Returns whether this instance collides against the argument Circle. + /// + /// The Circle to test collision against. + /// Whether collision has occurred. public bool CollideAgainst(Circle circle) { return mShapes.CollideAgainst(circle, true, mSortAxis); } + /// + /// Returns whether this instance collides against the argument Polygon. + /// + /// The Polygon to test collision against. + /// Whether collision has occurred. public bool CollideAgainst(Polygon polygon) { return mShapes.CollideAgainst(polygon, true, mSortAxis); } + /// + /// Returns whether this instance collides against the argument Line. + /// + /// The Line to test collision against. + /// Whether collision has occurred. public bool CollideAgainst(Line line) { return mShapes.CollideAgainst(line, true, mSortAxis); } - +#if ShapeManagerCollideAgainstClosest public bool CollideAgainstClosest(Line line) { - line.LastCollisionPoint = new Point(double.NaN, double.NaN); - - Segment a = line.AsSegment(); + return mShapes.CollideAgainstClosest(line, SortAxis, GridSize); + } +#endif - if (SortAxis == Axis.X) - { - var leftmost = (float)System.Math.Min(line.AbsolutePoint1.X, line.AbsolutePoint2.X); - var rightmost = (float)System.Math.Max(line.AbsolutePoint1.X, line.AbsolutePoint2.X); + /// + /// Returns whether this intance collides against the argument ICollidable. + /// + /// The ICollidable to test collision against. + /// Whether collision has occurred. + public bool CollideAgainst(ICollidable collidable) + { + return mShapes.CollideAgainst(collidable.Collision, true, mSortAxis); + } - float clampedPosition = line.Position.X; + public bool CollideAgainstMove(Circle circle) + { + return mShapes.CollideAgainstMove(circle, 1, 0); + } - bool isPositionOnEnd = false; - if (clampedPosition <= leftmost) - { - clampedPosition = leftmost; - isPositionOnEnd = true; - } - else if (clampedPosition >= rightmost) - { - clampedPosition = rightmost; - isPositionOnEnd = true; - } + public bool CollideAgainstMove(ICollidable collidable) + { + return mShapes.CollideAgainstMove(collidable.Collision, 1, 0); + } - // only support rectangles for now (maybe forever) - var rectangles = Rectangles; + public bool CollideAgainstSolid(ICollidable collidable) + { + return mShapes.CollideAgainstBounce(collidable.Collision, true, mSortAxis, 1, 0, 0); + } - var firstIndex = rectangles.GetFirstAfter(leftmost - GridSize, Axis.X, 0, rectangles.Count); - var lastIndex = rectangles.GetFirstAfter(rightmost + GridSize, Axis.X, firstIndex, rectangles.Count); +#if IStackableInEngine + public bool CollideAgainstSolid(T item) where T : PositionedObject, ICollidable, IStackable + { + if (this.CollideAgainst(item)) + { + var collidedTileRetangles = this.LastCollisionAxisAlignedRectangles; - if (isPositionOnEnd) + for (int i = 0; i < collidedTileRetangles.Count; i++) { - FlatRedBall.Math.Geometry.AxisAlignedRectangle collidedRectangle = null; - Point? intersectionPoint = null; - if (clampedPosition < rightmost) - { - - // start at the beginning of the list, go up - for (int i = firstIndex; i < lastIndex; i++) - { - var rectangle = Rectangles[i]; - - if (collidedRectangle != null) - { - if (rectangle.X > collidedRectangle.X) - { - break; - } - - if (rectangle.Y > collidedRectangle.Y && collidedRectangle.Y > line.Position.Y) - { - break; - } - if (rectangle.Y < collidedRectangle.Y && collidedRectangle.Y < line.Position.Y) - { - break; - } - } - - - Point tl = new Point( - rectangle.Position.X - rectangle.ScaleX, - rectangle.Position.Y + rectangle.ScaleY); - Point tr = new Point( - rectangle.Position.X + rectangle.ScaleX, - rectangle.Position.Y + rectangle.ScaleY); - Point bl = new Point( - rectangle.Position.X - rectangle.ScaleX, - rectangle.Position.Y - rectangle.ScaleY); - Point br = new Point( - rectangle.Position.X + rectangle.ScaleX, - rectangle.Position.Y - rectangle.ScaleY); - - Point tempPoint; - - // left gets priority - // left - var intersects = a.Intersects(new Segment(tl, bl), out tempPoint); - - if (rectangle.Y > line.Y) - { - // bottom gets priority over top - if (!intersects) - { - // bottom - intersects = a.Intersects(new Segment(bl, br), out tempPoint); - } - if (!intersects) - { - // top - intersects = a.Intersects(new Segment(tl, tr), out tempPoint); - } - } - else - { - // top gets priority over top - if (!intersects) - { - // top - intersects = a.Intersects(new Segment(tl, tr), out tempPoint); - } - if (!intersects) - { - // bottom - intersects = a.Intersects(new Segment(bl, br), out tempPoint); - } - } - if (!intersects) - { - // right - intersects = a.Intersects(new Segment(tr, br), out tempPoint); - } + var tileRect = collidedTileRetangles[i]; - if (intersects) - { - intersectionPoint = tempPoint; - collidedRectangle = rectangle; - } - } - } - else - { - // start at the end of the list, go down - for (int i = lastIndex - 1; i >= firstIndex; i--) - { - var rectangle = Rectangles[i]; - - if (collidedRectangle != null) - { - if (rectangle.X < collidedRectangle.X) - { - break; - } - - if (rectangle.Y > collidedRectangle.Y && collidedRectangle.Y > line.Position.Y) - { - break; - } - if (rectangle.Y < collidedRectangle.Y && collidedRectangle.Y < line.Position.Y) - { - break; - } - } + var itemPositionBefore = item.Position; + item.CollideAgainstBounce(tileRect, 0, 1, 0); - Point tl = new Point( - rectangle.Position.X - rectangle.ScaleX, - rectangle.Position.Y + rectangle.ScaleY); - Point tr = new Point( - rectangle.Position.X + rectangle.ScaleX, - rectangle.Position.Y + rectangle.ScaleY); - Point bl = new Point( - rectangle.Position.X - rectangle.ScaleX, - rectangle.Position.Y - rectangle.ScaleY); - Point br = new Point( - rectangle.Position.X + rectangle.ScaleX, - rectangle.Position.Y - rectangle.ScaleY); - - Point tempPoint; - - // right gets priority - // right - var intersects = a.Intersects(new Segment(tr, br), out tempPoint); - - if (rectangle.Y > line.Y) - { - // bottom gets priority over top - if (!intersects) - { - // bottom - intersects = a.Intersects(new Segment(bl, br), out tempPoint); - } - if (!intersects) - { - // top - intersects = a.Intersects(new Segment(tl, tr), out tempPoint); - } - } - else - { - // top gets priority over top - if (!intersects) - { - // top - intersects = a.Intersects(new Segment(tl, tr), out tempPoint); - } - if (!intersects) - { - // bottom - intersects = a.Intersects(new Segment(bl, br), out tempPoint); - } - } - if (!intersects) - { - // left - intersects = a.Intersects(new Segment(tl, bl), out tempPoint); - } + var positionAfter = item.Position; - if (intersects) - { - intersectionPoint = tempPoint; - collidedRectangle = rectangle; - } - } - } + var change = positionAfter - itemPositionBefore; - if (collidedRectangle != null) + if (change.X != 0 || change.Y != 0) { - line.LastCollisionPoint = intersectionPoint ?? new Point(double.NaN, double.NaN); - + item.LockVectorsTemp.Add(change.Normalized()); } - return collidedRectangle != null; } - else - { - throw new NotImplementedException("The argument line's position is not on either endpoint. This is a requirement for this type of collision."); - } - } - else if (SortAxis == Axis.Y) - { - throw new NotImplementedException("Bug Vic to do Y. Currently just X is done"); + + return true; } return false; } - - public bool CollideAgainst(ICollidable collidable) - { - return mShapes.CollideAgainst(collidable.Collision, true, mSortAxis); - } - - public bool CollideAgainstSolid(ICollidable collidable) - { - bool toReturn = false; - - toReturn = mShapes.CollideAgainstBounce(collidable.Collision, true, mSortAxis, 1, 0, 0); - - return toReturn; - } +#endif public bool CollideAgainstBounce(ICollidable collidable, float elasticity) { @@ -492,6 +443,12 @@ public AxisAlignedRectangle GetTileAt(float x, float y) return GetRectangleAtPosition(x, y); } + /// + /// Returns the AxisAlignedRectangle at the argument worldX and worldY position. If no rectangle is located at this position, null is returned. + /// + /// The world X coordinate + /// The world Y coordinate + /// The AxisAlignedRectangle at the location, or null if none is found. public AxisAlignedRectangle GetRectangleAtPosition(float worldX, float worldY) { float middleOfTileX = MathFunctions.RoundFloat(worldX, GridSize, LeftSeedX + GridSize / 2.0f); @@ -508,7 +465,7 @@ public AxisAlignedRectangle GetRectangleAtPosition(float worldX, float worldY) int endExclusive = mShapes.AxisAlignedRectangles.GetFirstAfter(keyValueAfter, mSortAxis, 0, mShapes.AxisAlignedRectangles.Count); - AxisAlignedRectangle toReturn = GetRectangleAtPosition(worldX, worldY, startInclusive, endExclusive); + AxisAlignedRectangle toReturn = GetRectangleAtPosition(middleOfTileX, middleOfTileY, startInclusive, endExclusive); return toReturn; } @@ -525,11 +482,11 @@ public Polygon GetPolygonAtPosition(float worldX, float worldY) float keyValueAfter = keyValue + halfGridSize; int startInclusive = mShapes.Polygons.GetFirstAfter(keyValueBefore, mSortAxis, - 0, mShapes.AxisAlignedRectangles.Count); + 0, mShapes.Polygons.Count); int endExclusive = mShapes.Polygons.GetFirstAfter(keyValueAfter, mSortAxis, - 0, mShapes.AxisAlignedRectangles.Count); + 0, mShapes.Polygons.Count); var left = middleOfTileX - halfGridSize; var right = middleOfTileX + halfGridSize; @@ -581,6 +538,7 @@ private AxisAlignedRectangle GetRectangleAtPosition(float x, float y, int startI AxisAlignedRectangle toReturn = null; for (int i = startInclusive; i < endExclusive; i++) { + var rect = mShapes.AxisAlignedRectangles[i]; if (mShapes.AxisAlignedRectangles[i].IsPointInside(x, y)) { toReturn = mShapes.AxisAlignedRectangles[i]; @@ -623,34 +581,46 @@ public void AddCollisionAtWorld(float x, float y) } } - public void InsertRectangle(AARect rectangle) + public void InsertRectangle(AARect rectangle, int forcedIndex = -1) { float roundedX = rectangle.Left; float roundedY = rectangle.Bottom; - float keyValue = GetCoordinateValueForPartitioning(roundedX, roundedY); + int index; + if (forcedIndex > -1) + { + index = forcedIndex; + } + else + { + float keyValue = GetCoordinateValueForPartitioning(roundedX, roundedY); - int index = mShapes.AxisAlignedRectangles.GetFirstAfter(keyValue, mSortAxis, - 0, mShapes.AxisAlignedRectangles.Count); + index = mShapes.AxisAlignedRectangles.GetFirstAfter(keyValue, mSortAxis, + 0, mShapes.AxisAlignedRectangles.Count); + } mShapes.AxisAlignedRectangles.Insert(index, rectangle); - if(AdjustRepositionDirectionsOnAddAndRemove) + if (AdjustRepositionDirectionsOnAddAndRemove) { - var directions = UpdateRepositionForNeighborsAndGetThisRepositionDirection(rectangle); + var directions = UpdateRepositionDirections(rectangle); rectangle.RepositionDirections = directions; } } + /// + /// Adds all shapes from the argument TileShapeCollection into this TileShapeCollection and updates RepositionDirections on all contained AxisAlignedRectangles + /// + /// The source from which to copy the shapes. public void InsertShapes(TileShapeCollection source) { - foreach(var rectangle in source.Rectangles) + foreach (var rectangle in source.Rectangles) { this.InsertRectangle(rectangle); } - if(source.Polygons.Count > 0) + if (source.Polygons.Count > 0) { throw new InvalidOperationException("Inserting does not currently support TileShapeCollections with polygons"); } @@ -658,9 +628,9 @@ public void InsertShapes(TileShapeCollection source) public void InsertCollidables(IList collidables) where T : FlatRedBall.Math.Geometry.ICollidable { - foreach(var collidable in collidables) + foreach (var collidable in collidables) { - foreach(var rectangle in collidable.Collision.AxisAlignedRectangles) + foreach (var rectangle in collidable.Collision.AxisAlignedRectangles) { rectangle.ForceUpdateDependencies(); InsertRectangle(rectangle); @@ -683,7 +653,7 @@ public void RemoveRectangle(AARect existing) { ShapeManager.Remove(existing); - if(AdjustRepositionDirectionsOnAddAndRemove) + if (AdjustRepositionDirectionsOnAddAndRemove) { float keyValue = GetCoordinateValueForPartitioning(existing.X, existing.Y); @@ -800,7 +770,121 @@ public static void UpdateLShapedPassNeighbors(AARect center, AARect left, AARect down != null && left != null && downLeft == null; } - private RepositionDirections UpdateRepositionForNeighborsAndGetThisRepositionDirection(PositionedObject positionedObject) + private RepositionDirections GetRepositionDirection(PositionedObject positionedObject, bool[] array, float collectionLeft, float collectionBottom, int numberTilesWide, out bool repositionHalfSize) + { + var worldX = positionedObject.Position.X; + var worldY = positionedObject.Position.Y; + + var xIndex = MathFunctions.RoundToInt(System.Math.Floor((worldX - collectionLeft) / mGridSizeX)); + var yIndex = MathFunctions.RoundToInt(System.Math.Floor((worldY - collectionBottom) / mGridSizeY)); + + bool ValueAt(int xIndexInner, int yIndexInner) + { + var absoluteIndex = xIndexInner + yIndexInner * numberTilesWide; + + return xIndexInner >= 0 && xIndexInner < numberTilesWide && absoluteIndex < array.Length && absoluteIndex > -1 && array[absoluteIndex]; + } + + RepositionDirections directions = RepositionDirections.All; + + bool left = ValueAt(xIndex - 1, yIndex); + bool right = ValueAt(xIndex + 1, yIndex); + bool up = ValueAt(xIndex, yIndex + 1); + var down = ValueAt(xIndex, yIndex - 1); + + bool upLeft = ValueAt(xIndex - 1, yIndex + 1); + bool upRight = ValueAt(xIndex + 1, yIndex + 1); + + + bool downLeft = ValueAt(xIndex - 1, yIndex - 1); + bool downRight = ValueAt(xIndex + 1, yIndex - 1); + + if (left) + { + directions -= RepositionDirections.Left; + } + else + { + //var polygon = GetPolygonAtPosition(leftOfX, middleY, polygonsBeforeIndex, polygonsAfterIndex); + + //if (polygon != null) + //{ + // directions -= RepositionDirections.Left; + // if ((polygon.RepositionDirections & RepositionDirections.Right) == RepositionDirections.Right) + // { + // polygon.RepositionDirections -= RepositionDirections.Right; + // } + //} + } + + if (right) + { + directions -= RepositionDirections.Right; + } + else + { + //var polygon = GetPolygonAtPosition(rightOfX, middleY, polygonsBeforeIndex, polygonsAfterIndex); + + //if (polygon != null) + //{ + // directions -= RepositionDirections.Right; + // if ((polygon.RepositionDirections & RepositionDirections.Left) == RepositionDirections.Left) + // { + // polygon.RepositionDirections -= RepositionDirections.Left; + // } + //} + } + + + if (up) + { + directions -= RepositionDirections.Up; + } + else + { + //var polygon = GetPolygonAtPosition(middleX, aboveY, polygonsBeforeIndex, polygonsAfterIndex); + + //if (polygon != null) + //{ + // directions -= RepositionDirections.Up; + + // if ((polygon.RepositionDirections & RepositionDirections.Down) == RepositionDirections.Down) + // { + // polygon.RepositionDirections -= RepositionDirections.Down; + // } + //} + } + + if (down) + { + directions -= RepositionDirections.Down; + } + else + { + //var polygon = GetPolygonAtPosition(middleX, belowY, polygonsBeforeIndex, polygonsAfterIndex); + + //if (polygon != null) + //{ + // directions -= RepositionDirections.Down; + + // if ((polygon.RepositionDirections & RepositionDirections.Up) == RepositionDirections.Up) + // { + // polygon.RepositionDirections -= RepositionDirections.Up; + // } + //} + } + + // do the L-shaped: + repositionHalfSize = + (left && up && !upLeft) || + (up && right && !upRight) || + (right && down && !downRight) || + (down && left && !downLeft); + + return directions; + } + + private RepositionDirections UpdateRepositionDirections(PositionedObject positionedObject, bool updateNeighbors = true, bool[] array = null, int numberTilesWide = 0) { // Let's see what is surrounding this rectangle and update it and the surrounding rects appropriately float keyValue = GetCoordinateValueForPartitioning(positionedObject.Position.X, positionedObject.Position.Y); @@ -835,7 +919,7 @@ private RepositionDirections UpdateRepositionForNeighborsAndGetThisRepositionDir void UpdateLShaped(AARect center) { - if(center != null) + if (center != null) { var left = GetRectangleAtPosition(center.X - GridSize, center.Y); var upLeft = GetRectangleAtPosition(center.X - GridSize, center.Y + GridSize); @@ -850,15 +934,22 @@ void UpdateLShaped(AARect center) } } - UpdateLShapedPassNeighbors(positionedObject as AARect, rectangleLeftOf, rectangleUpLeft, rectangleAbove, rectangleUpRight, rectangleRightOf, rectangleDownRight, rectangleBelow, rectangleDownLeft); - UpdateLShaped(rectangleLeftOf); - UpdateLShaped(rectangleUpLeft); - UpdateLShaped(rectangleAbove); - UpdateLShaped(rectangleUpRight); - UpdateLShaped(rectangleRightOf); - UpdateLShaped(rectangleDownRight); - UpdateLShaped(rectangleBelow); - UpdateLShaped(rectangleDownLeft); + if (positionedObject is AARect asAaRect) + { + UpdateLShapedPassNeighbors(asAaRect, rectangleLeftOf, rectangleUpLeft, rectangleAbove, rectangleUpRight, rectangleRightOf, rectangleDownRight, rectangleBelow, rectangleDownLeft); + } + + if (updateNeighbors) + { + UpdateLShaped(rectangleLeftOf); + UpdateLShaped(rectangleUpLeft); + UpdateLShaped(rectangleAbove); + UpdateLShaped(rectangleUpRight); + UpdateLShaped(rectangleRightOf); + UpdateLShaped(rectangleDownRight); + UpdateLShaped(rectangleBelow); + UpdateLShaped(rectangleDownLeft); + } RepositionDirections directions = RepositionDirections.All; if (rectangleLeftOf != null) @@ -960,11 +1051,13 @@ void UpdateLShaped(AARect center) public void RemoveFromManagersOneWay() { - this.mShapes.MakeOneWay(); - this.mShapes.RemoveFromManagers(); - this.mShapes.MakeTwoWay(); + this.mShapes.RemoveFromManagers(clearThis: false); } + /// + /// Removes all shapes from managers (makes them invisible) and clears this TileShapeCollection. + /// + /// public void RemoveFromManagers() { this.mShapes.RemoveFromManagers(); @@ -1002,26 +1095,54 @@ public void SetColor(Microsoft.Xna.Framework.Color color) } + public void Shift(Microsoft.Xna.Framework.Vector3 shiftVector) + { + mShapes.Shift(shiftVector); + } + + /// + /// Updates the reposition directions for all contained shapes to prevent snagging. This can be called after performing add or remove operations on this ShapeCollection. + /// By default this does not need to be called when calling InsertRectangle or AddRectangle - reposition direcitons will be adjusted automatically when these methods are called + /// if AdjustRepositionDirectionsOnAddAndRemove is true. + /// + /// + /// This method adjusts the reposition directions to point "outward" if the shape is on the outside. If a shape is fully enclosed, it + /// has no reposition direction assigned. + /// public void RefreshAllRepositionDirections() { - var count = this.mShapes.AxisAlignedRectangles.Count; - for (int i = 0; i < count; i++) + if (this.RepositionUpdateStyle == RepositionUpdateStyle.Upward) { - var rectangle = this.mShapes.AxisAlignedRectangles[i]; + UpdateShapesForCloudCollision(); + } + else if (this.RepositionUpdateStyle == RepositionUpdateStyle.Outward) + { + var bytes = GetCollisionByteArray(out float left, out float bottom, out int numberTilesWide); - var directions = UpdateRepositionForNeighborsAndGetThisRepositionDirection(rectangle); + var count = this.mShapes.AxisAlignedRectangles.Count; + for (int i = 0; i < count; i++) + { + var rectangle = this.mShapes.AxisAlignedRectangles[i]; - rectangle.RepositionDirections = directions; - } + var directions = // UpdateRepositionDirections(rectangle, false, bytes, numberTilesWide); + GetRepositionDirection(rectangle, bytes, left, bottom, numberTilesWide, out bool repositionHalfSize); + rectangle.RepositionDirections = directions; + rectangle.RepositionHalfSize = repositionHalfSize; + } - count = this.mShapes.Polygons.Count; - for (int i = 0; i < count; i++) - { - var polygon = this.mShapes.Polygons[i]; + count = this.mShapes.Polygons.Count; + for (int i = 0; i < count; i++) + { + var polygon = this.mShapes.Polygons[i]; - var directions = UpdateRepositionForNeighborsAndGetThisRepositionDirection(polygon); + var directions = UpdateRepositionDirections(polygon, false); - polygon.RepositionDirections = directions; + polygon.RepositionDirections = directions; + } + } + else + { + // do nothing... } } @@ -1069,6 +1190,7 @@ RepositionDirections RepBelow(AxisAlignedRectangle rectangle) while (rectanglesWithNoneReposition.Count > 0) { + var startedRectanglesCount = rectanglesWithNoneReposition.Count; rectanglesProcessedThisRound.Clear(); // see if any @@ -1124,17 +1246,136 @@ RepositionDirections RepBelow(AxisAlignedRectangle rectangle) } } } + + // This can get stuck. If we still have rectangles but nothing was + if (startedRectanglesCount == rectanglesWithNoneReposition.Count) + { + break; // somehow some rectangle is left without a reposition... + } } } + public void UpdateShapesForCloudCollision() + { + var count = this.mShapes.AxisAlignedRectangles.Count; + for (int i = 0; i < count; i++) + { + var rectangle = this.mShapes.AxisAlignedRectangles[i]; + + rectangle.RepositionHalfSize = true; + + rectangle.RepositionDirections = RepositionDirections.Up; + } + } + public override string ToString() { return Name; } - } + bool[] GetCollisionByteArray(out float leftEdge, out float bottomEdge, out int numberTilesWide) + { + bool[] toReturn; + + leftEdge = 0; + bottomEdge = 0; + + if (mShapes.AxisAlignedRectangles.Count == 0) + { + numberTilesWide = 0; + toReturn = new bool[0]; + } + else + { + var rectangles = mShapes.AxisAlignedRectangles; + var first = rectangles[0]; + var minCenterX = first.X; + var maxCenterX = first.X; + + var minLeft = first.X - first.Width / 2.0f; + var maxRight = first.X + first.Width / 2.0f; + + var minCenterY = first.Y; + var maxCenterY = first.Y; + + var minBottom = first.Y - first.Height / 2.0f; + var maxTop = first.Y + first.Height / 2.0f; + + for (int i = 1; i < mShapes.AxisAlignedRectangles.Count; i++) + { + var rect = mShapes.AxisAlignedRectangles[i]; + + if (rect.X < minCenterX) + { + minCenterX = rect.X; + } + if (rect.X > maxCenterX) + { + maxCenterX = rect.X; + } + + if (rect.Y < minCenterY) + { + minCenterY = rect.Y; + } + if (rect.Y > maxCenterY) + { + maxCenterY = rect.Y; + } + + + var left = rect.X - rect.Width / 2.0f; + var right = rect.X + rect.Width / 2.0f; + + var top = rect.Y + rect.Height / 2.0f; + var bottom = rect.Y - rect.Height / 2.0f; + + if (left < minLeft) minLeft = left; + if (right > maxRight) maxRight = right; + + if (bottom < minBottom) minBottom = bottom; + if (top > maxTop) maxTop = top; + } + + // now we know the mins and maxes + + + //if(BottomSeedY / mGridSizeY != 0 || LeftSeedX/mGridSizeX != 0) + //{ + // throw new Exception("Due to recent changes in tile shape collection generation, seed code has not yet been supported. If you need this, file an issue on github or explain it in the FlatRedBall Discord"); + //} + + leftEdge = (int)(minCenterX / mGridSizeX) * mGridSizeX; + + bottomEdge = (float)(System.Math.Floor(minBottom / mGridSizeY) * mGridSizeY); + + var numberOfXTiles = MathFunctions.RoundToInt(System.Math.Ceiling((maxRight - leftEdge) / mGridSizeX)); + var numberOfYTiles = MathFunctions.RoundToInt(System.Math.Ceiling((maxTop - bottomEdge) / mGridSizeY)); + + + numberTilesWide = numberOfXTiles; + + var numberOfTiles = numberOfXTiles * numberOfYTiles; + + toReturn = new bool[numberOfTiles]; + + for (int i = 0; i < mShapes.AxisAlignedRectangles.Count; i++) + { + var rect = mShapes.AxisAlignedRectangles[i]; + + var xIndex = MathFunctions.RoundToInt(System.Math.Floor((rect.Position.X - leftEdge) / mGridSizeX)); + var yIndex = MathFunctions.RoundToInt(System.Math.Floor((rect.Position.Y - bottomEdge) / mGridSizeY)); + + var index = xIndex + yIndex * numberOfXTiles; + + toReturn[index] = true; + } + } + return toReturn; + } + } public static class TileShapeCollectionLayeredTileMapExtensions { @@ -1208,6 +1449,9 @@ public static void AddCollisionFrom(this TileShapeCollection tileShapeCollection var properties = layeredTileMap.TileProperties; + var wasAdjusting = tileShapeCollection.AdjustRepositionDirectionsOnAddAndRemove; + tileShapeCollection.AdjustRepositionDirectionsOnAddAndRemove = false; + foreach (var kvp in properties) { string name = kvp.Key; @@ -1233,6 +1477,10 @@ public static void AddCollisionFrom(this TileShapeCollection tileShapeCollection { var indexList = dictionary[name]; + var GridSize = tileShapeCollection.GridSize; + var LeftSeedX = tileShapeCollection.LeftSeedX; + var BottomSeedY = tileShapeCollection.BottomSeedY; + foreach (var index in indexList) { float left; @@ -1241,23 +1489,51 @@ public static void AddCollisionFrom(this TileShapeCollection tileShapeCollection var centerX = left + dimensionHalf; var centerY = bottom + dimensionHalf; + + // this performs a slower add because of all the checks internal. We can speed things + // up by inlining and removing. Specifically, we won't do contains checks, and we'll + // assume that everything is already ordered so we don't have to fetch indexes: + tileShapeCollection.AddCollisionAtWorld(centerX, centerY); + //float roundedX = MathFunctions.RoundFloat(centerX - GridSize / 2.0f, GridSize, LeftSeedX); + //float roundedY = MathFunctions.RoundFloat(centerY - GridSize / 2.0f, GridSize, BottomSeedY); + + //AxisAlignedRectangle newAar = new AxisAlignedRectangle(); + //newAar.Width = GridSize; + //newAar.Height = GridSize; + //newAar.Left = roundedX; + //newAar.Bottom = roundedY; + + //if (tileShapeCollection.Visible) + //{ + // newAar.Visible = true; + //} + + //tileShapeCollection.InsertRectangle(newAar, tileShapeCollection.Rectangles.Count); + + } - if(removeTilesOnAdd) + if (removeTilesOnAdd) { indexesToRemove.AddRange(indexList); } } - if(removeTilesOnAdd && indexesToRemove.Count > 0) + if (removeTilesOnAdd && indexesToRemove.Count > 0) { layer.RemoveQuads(indexesToRemove); } } } } + + tileShapeCollection.AdjustRepositionDirectionsOnAddAndRemove = wasAdjusting; + if (wasAdjusting) + { + tileShapeCollection.RefreshAllRepositionDirections(); + } } public static void AddMergedCollisionFrom(this TileShapeCollection tileShapeCollection, LayeredTileMap layeredTileMap, @@ -1312,13 +1588,13 @@ public static void AddMergedCollisionFromTilesWithProperty(this TileShapeCollect layeredTileMap, (list) => list.Any(item => item.Name == propertyName), removeTilesOnAdd); } - public static void AddCollisionFromTilesWithType(this TileShapeCollection tileShapeCollection, + public static void AddCollisionFromTilesWithType(this TileShapeCollection tileShapeCollection, LayeredTileMap layeredTileMap, string type, bool removeTilesOnAdd = false) { - if(layeredTileMap != null) + if (layeredTileMap != null) { tileShapeCollection.AddCollisionFrom( - layeredTileMap, + layeredTileMap, (list) => list.Any(item => item.Name == "Type" && (item.Value as string) == type), removeTilesOnAdd); } @@ -1733,6 +2009,4 @@ public static void RemoveCollisionFromTilesWithType(this TileShapeCollection til removeTilesOnAdd); } } - - } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileEntities/TileEntityInstantiator.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileEntities/TileEntityInstantiator.cs index 257956f53..5ef938557 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileEntities/TileEntityInstantiator.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileEntities/TileEntityInstantiator.cs @@ -1,3 +1,13 @@ +#define PreVersion +#define HasFormsObject +#define AddedGeneratedGame1 +#define ListsHaveAssociateWithFactoryBool +#define GumGueHasGetAnimation +#define CsvInheritanceSupport +#define IPositionedSizedObjectInEngine +#define NugetPackageInCsproj + + using MultiplayerPlatformerDemo.DataTypes; using FlatRedBall.TileGraphics; using System; @@ -27,8 +37,29 @@ public class Settings public bool RemoveTileObjectsAfterEntityCreation = true; } + static Type[] typesInThisAssembly; + static Type[] TypesInThisAssembly + { + get + { + if (typesInThisAssembly == null) + { +#if WINDOWS_8 || UWP + var assembly = typeof(TileEntityInstantiator).GetTypeInfo().Assembly; + typesInThisAssembly = assembly.DefinedTypes.Select(item=>item.AsType()).ToArray(); +#else + var assembly = Assembly.GetExecutingAssembly(); + typesInThisAssembly = assembly.GetTypes(); +#endif + } + + return typesInThisAssembly; + } + } public static Settings CurrentSettings { get; set; } = new Settings(); + public static Func CreationFunction; + /// /// A dictionary that stores all available values for a given type. /// @@ -62,7 +93,7 @@ public static void CreateEntitiesFrom(MapDrawableBatch mapLayer, LayeredTileMap CreateEntitiesFrom(entitiesToRemove, mapLayer, layeredTileMap.TileProperties, layeredTileMap.WidthPerTile ?? 16); - if(CurrentSettings.RemoveTileObjectsAfterEntityCreation) + if (CurrentSettings.RemoveTileObjectsAfterEntityCreation) { foreach (var entityToRemove in entitiesToRemove) { @@ -75,7 +106,7 @@ public static void CreateEntitiesFrom(MapDrawableBatch mapLayer, LayeredTileMap public static void CreateEntitiesFrom(LayeredTileMap layeredTileMap, InstantiationRestrictions restrictions = null) { - if(layeredTileMap != null) + if (layeredTileMap != null) { var entitiesToRemove = new List(); @@ -83,7 +114,7 @@ public static void CreateEntitiesFrom(LayeredTileMap layeredTileMap, Instantiati { CreateEntitiesFrom(entitiesToRemove, layer, layeredTileMap.TileProperties, layeredTileMap.WidthPerTile ?? 16, restrictions); } - if(CurrentSettings.RemoveTileObjectsAfterEntityCreation) + if (CurrentSettings.RemoveTileObjectsAfterEntityCreation) { foreach (var entityToRemove in entitiesToRemove) { @@ -121,18 +152,15 @@ private static void CreateEntitiesFromCircles(LayeredTileMap layeredTileMap, Sha { shouldCreate = restrictions.InclusiveList.Contains(entityType); } - if(shouldCreate) + if (shouldCreate) { - IEntityFactory factory = GetFactory(entityType); - - if(factory != null) + PositionedObject entity = CreateEntity(entityType); + if (entity != null) { - var entity = factory.CreateNew(null) as PositionedObject; - entity.Name = circle.Name; ApplyPropertiesTo(entity, properties, circle.Position); - if(CurrentSettings.RemoveTileObjectsAfterEntityCreation) + if (CurrentSettings.RemoveTileObjectsAfterEntityCreation) { shapeCollection.Circles.Remove(circle); } @@ -168,15 +196,13 @@ private static void CreateEntitiesFromRectangles(LayeredTileMap layeredTileMap, } if (shouldCreate) { - IEntityFactory factory = GetFactory(entityType); - if(factory != null) + PositionedObject entity = CreateEntity(entityType); + if (entity != null) { - var entity = factory.CreateNew(null) as PositionedObject; - entity.Name = rectangle.Name; ApplyPropertiesTo(entity, properties, rectangle.Position); - if(CurrentSettings.RemoveTileObjectsAfterEntityCreation) + if (CurrentSettings.RemoveTileObjectsAfterEntityCreation) { shapeCollection.AxisAlignedRectangles.Remove(rectangle); } @@ -212,11 +238,9 @@ private static void CreateEntitiesFromPolygons(LayeredTileMap layeredTileMap, Ma } if (shouldCreate) { - IEntityFactory factory = GetFactory(entityType); - if(factory != null) + PositionedObject entity = CreateEntity(entityType); + if (entity != null) { - var entity = factory.CreateNew(null) as PositionedObject; - entity.Name = polygon.Name; ApplyPropertiesTo(entity, properties, polygon.Position); @@ -230,6 +254,7 @@ private static void CreateEntitiesFromPolygons(LayeredTileMap layeredTileMap, Ma var entityCollision = (entity as Math.Geometry.ICollidable).Collision; entityCollision.Polygons.Add(polygon); polygon.AttachTo(entity, false); + polygon.RelativeRotationZ = polygon.RotationZ; } } } @@ -237,7 +262,23 @@ private static void CreateEntitiesFromPolygons(LayeredTileMap layeredTileMap, Ma } } - private static void CreateEntitiesFrom(List entitiesToRemove, MapDrawableBatch layer, Dictionary> propertiesDictionary, + private static PositionedObject CreateEntity(string entityType) + { + PositionedObject entity = null; + IEntityFactory factory = GetFactory(entityType); + if (factory != null) + { + entity = factory.CreateNew((FlatRedBall.Graphics.Layer)null) as PositionedObject; + } + else if (CreationFunction != null) + { + entity = CreationFunction(entityType); + } + + return entity; + } + + private static void CreateEntitiesFrom(List entitiesToRemove, MapDrawableBatch layer, Dictionary> propertiesDictionary, float tileSize, InstantiationRestrictions restrictions = null) { @@ -271,21 +312,14 @@ private static void CreateEntitiesFrom(List entitiesToRemove, MapDrawabl { IEntityFactory factory = GetFactory(entityType); - if (factory == null) + if (factory == null && CreationFunction == null) { - bool isEntity = typesInThisAssembly.Any(item => item.Name.Contains($".Entities.") && item.Name.EndsWith(entityType)); - - if (isEntity) - { - string message = - $"The factory for entity {entityType} could not be found. To create instances of this entity, " + - "set its 'CreatedByOtherEntities' property to true in Glue."; - throw new Exception(message); - } + // do nothing? } else { - entitiesToRemove.Add(entityType); + var createdEntityOfThisType = false; + var indexList = dictionary[tileName]; foreach (var tileIndex in indexList) @@ -300,14 +334,49 @@ private static void CreateEntitiesFrom(List entitiesToRemove, MapDrawabl shouldCreate = bounds.IsPointInside(x, y); } - if(shouldCreate) + if (shouldCreate) { - var entity = factory.CreateNew(flatRedBallLayer) as PositionedObject; - - ApplyPropertiesTo(entity, layer, tileIndex, propertyList); + PositionedObject entity = null; + if (factory != null) + { + entity = factory.CreateNew(flatRedBallLayer) as PositionedObject; + } + else if (CreationFunction != null) + { + entity = CreationFunction(entityType); + // todo - need to support moving to layer + } + + if (entity != null) + { + ApplyPropertiesTo(entity, layer, tileIndex, propertyList); + createdEntityOfThisType = true; + +#if ITiledTileMetadataInFrb + if(entity is FlatRedBall.Entities.ITiledTileMetadata asEntity) + { + float tx, ty; + layer.GetTextureCoordiantesForOrderedTile(tileIndex, out tx, out ty); + var ttm = new FlatRedBall.Entities.TiledTileMetadata() + { + LeftTextureCoordinate = tx, + TopTextureCoordinate = ty, + RightTextureCoordinate = tx + (tileSize / layer.Texture.Width), + BottomTextureCoordinate = ty + (tileSize / layer.Texture.Height) + }; + + ttm.RotationZ = layer.GetRotationZForOrderedTile(tileIndex); + + asEntity.SetTileMetadata(ttm); + } +#endif + } } } - + if (createdEntityOfThisType) + { + entitiesToRemove.Add(entityType); + } } } } @@ -364,7 +433,7 @@ private static void ApplyPropertiesTo(PositionedObject entity, List // If name is EntityToCreate, skip it: string propertyName = property.Name; - bool shouldSet = propertyName != "EntityToCreate" && + bool shouldSet = propertyName != "EntityToCreate" && propertyName != "Type"; if (shouldSet) @@ -386,14 +455,14 @@ private static void ApplyPropertiesTo(PositionedObject entity, List valueToSet = ConvertValueAccordingToType(valueToSet, propertyName, propertyType, entityType); try { - switch(propertyName) + switch (propertyName) { case "X": - if(valueToSet is float) + if (valueToSet is float) { entity.X += (float)valueToSet; } - else if(valueToSet is int) + else if (valueToSet is int) { entity.X += (int)valueToSet; } @@ -434,7 +503,7 @@ private static void ApplyPropertiesTo(PositionedObject entity, List $"Check the property type in your TMX and make sure it matches the type on the entity."; throw new Exception(message, e); } - catch (Exception e) + catch (Exception) { // Since this code indiscriminately tries to set properties, it may set properties which don't // actually exist. Therefore, we tolerate failures. @@ -487,12 +556,12 @@ private static string GetFriendlyNameForType(string type) { case "System.String": return "string"; case "System.Single": return "float"; + case "System.Decimal": return "decimal"; } return type; } - private static object ConvertValueAccordingToType(object valueToSet, string valueName, string valueType, Type entityType) { if (valueType == "bool") @@ -513,6 +582,15 @@ private static object ConvertValueAccordingToType(object valueToSet, string valu valueToSet = floatValue; } } + else if (valueType == "decimal") + { + decimal decimalValue; + + if (decimal.TryParse((string)valueToSet, System.Globalization.NumberStyles.Float, System.Globalization.NumberFormatInfo.InvariantInfo, out decimalValue)) + { + valueToSet = decimalValue; + } + } else if (valueType == "int") { int intValue; @@ -526,7 +604,7 @@ private static object ConvertValueAccordingToType(object valueToSet, string valu { // Since it's part of the class, it uses the "+" separator var enumTypeName = entityType.FullName + "+VariableState"; - var enumType = typesInThisAssembly.FirstOrDefault(item => item.FullName == enumTypeName); + var enumType = TypesInThisAssembly.FirstOrDefault(item => item.FullName == enumTypeName); valueToSet = Enum.Parse(enumType, (string)valueToSet); } @@ -554,7 +632,7 @@ private static object ConvertValueAccordingToType(object valueToSet, string valu // it in allDictionaries to make future calls faster else if (valueType != null && valueType.Contains("+")) { - var stateType = typesInThisAssembly.FirstOrDefault(item => item.FullName == valueType); + var stateType = TypesInThisAssembly.FirstOrDefault(item => item.FullName == valueType); if (stateType != null) { @@ -599,58 +677,7 @@ private static void AssignCustomPropertyTo(PositionedObject entity, NamedValue p throw new NotImplementedException(); } - - static Type[] typesInThisAssembly; - public static IEntityFactory GetFactory(string entityType) - { - if (typesInThisAssembly == null) - { -#if WINDOWS_8 || UWP - var assembly = typeof(TileEntityInstantiator).GetTypeInfo().Assembly; - typesInThisAssembly = assembly.DefinedTypes.Select(item=>item.AsType()).ToArray(); - -#else - var assembly = Assembly.GetExecutingAssembly(); - typesInThisAssembly = assembly.GetTypes(); -#endif - } - - -#if WINDOWS_8 || UWP - var filteredTypes = - typesInThisAssembly.Where(t => t.GetInterfaces().Contains(typeof(IEntityFactory)) - && t.GetConstructors().Any(c=>c.GetParameters().Count() == 0)); -#else - var filteredTypes = - typesInThisAssembly.Where(t => t.GetInterfaces().Contains(typeof(IEntityFactory)) - && t.GetConstructor(Type.EmptyTypes) != null); -#endif - - var factories = filteredTypes - .Select( - t => - { -#if WINDOWS_8 || UWP - var propertyInfo = t.GetProperty("Self"); -#else - var propertyInfo = t.GetProperty("Self"); -#endif - var value = propertyInfo.GetValue(null, null); - return value as IEntityFactory; - }).ToList(); - - - var factory = factories.FirstOrDefault(item => - { - var type = item.GetType(); - var methodInfo = type.GetMethod("CreateNew", new[] { typeof(Layer), typeof(float), typeof(float) }); - var returntypeString = methodInfo.ReturnType.Name; - - return entityType == returntypeString || - entityType.EndsWith("\\" + returntypeString) || - entityType.EndsWith("/" + returntypeString); - }); - return factory; - } + public static IEntityFactory GetFactory(string entityType) => FactoryManager.Get(entityType); } + } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/AbstractMapLayer.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/AbstractMapLayer.cs index d24853937..fccd355c1 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/AbstractMapLayer.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/AbstractMapLayer.cs @@ -11,9 +11,26 @@ namespace TMXGlueLib [XmlInclude(typeof (mapObjectgroup))] public abstract class AbstractMapLayer { + private float _offsetX; + private float _offsetY; + [XmlAttribute("name")] public string Name { get; set; } + [XmlAttribute("id")] + public int Id { get; set; } + + + [XmlAttribute("tintcolor")] + public string TintColor { get; set; } = "#ffffff"; + + + [XmlAttribute("opacity")] + public float Opacity + { + get; set; + } = 1.0f; + private int? visibleField; [XmlAttribute("visible")] public int visible @@ -29,5 +46,47 @@ public int visible } public bool IsVisible => visibleField == null || visibleField == 1; + + private float? parallaxxField; + [XmlAttribute("parallaxx")] + public float ParallaxX + { + get + { + return this.parallaxxField.HasValue ? this.parallaxxField.Value : 1f; + } + set + { + this.parallaxxField = value; + } + } + + private float? parallaxyField; + [XmlAttribute("parallaxy")] + public float ParallaxY + { + get + { + return this.parallaxyField.HasValue ? this.parallaxyField.Value : 1f; + } + set + { + this.parallaxyField = value; + } + } + + [XmlAttribute("offsetx")] + public float OffsetX + { + get { return _offsetX; } + set { _offsetX = value; } + } + + [XmlAttribute("offsety")] + public float OffsetY + { + get { return _offsetY; } + set { _offsetY = value; } + } } } \ No newline at end of file diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ExternalTileset.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ExternalTileset.cs index 1da1d590f..c543bd9b2 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ExternalTileset.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ExternalTileset.cs @@ -109,6 +109,7 @@ public int spacing { } } + /// [System.Xml.Serialization.XmlAttributeAttribute()] public int margin { @@ -119,16 +120,77 @@ public int margin { this.marginField = value; } } + + + [XmlAttribute("version")] + public string Version { get; set; } + + [XmlAttribute("tiledversion")] + public string TiledVersion { get; set; } + + [XmlAttribute("tilecount")] + public int TileCount { get; set; } + + [XmlAttribute("columns")] + public int Columns { get; set; } + + + public List wangsets { get; set; } = new List(); + } -[System.Xml.Serialization.XmlRootAttribute(ElementName = "tileset", Namespace = "", IsNullable = false)] -public class ExternalTileSet : Tileset +public class wangset +{ + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlAttribute("type")] + public string Type { get; set; } + + [XmlAttribute("tile")] + public int Tile + { + get; set; + } + + [XmlElement("wangcolor")] + public List WangColors { get; set; } = new List(); + + [XmlElement("wangtile")] + public List WangTiles { get; set; } = new List(); +} + +public class wangcolor { + [XmlAttribute] + public string name { get; set; } + [XmlAttribute] + public string color { get; set; } + [XmlAttribute] + public int tile { get; set; } + [XmlAttribute] + public double probability { get; set; } } +public class wangtile +{ + [XmlAttribute] + public int tileid { get; set; } + + [XmlAttribute] + public string wangid { get; set; } + +} + +[System.Xml.Serialization.XmlRootAttribute(ElementName = "tileset", Namespace = "", IsNullable = false)] +public class ExternalTileSet : Tileset +{ +} + + /// [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)] diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMap.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMap.cs index c608d869f..2dcf199a1 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMap.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMap.cs @@ -1,4 +1,15 @@ -using System; +#define PreVersion +#define HasFormsObject +#define AddedGeneratedGame1 +#define ListsHaveAssociateWithFactoryBool +#define GumGueHasGetAnimation +#define CsvInheritanceSupport +#define IPositionedSizedObjectInEngine +#define NugetPackageInCsproj + + + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -18,17 +29,20 @@ namespace FlatRedBall.TileGraphics { - public partial class LayeredTileMap : PositionedObject, IVisible, FlatRedBall.Math.IPositionedSizedObject + public partial class LayeredTileMap : PositionedObject, IVisible +#if IPositionedSizedObjectInEngine + , FlatRedBall.Math.IPositionedSizedObject +#endif { - #region Fields +#region Fields FlatRedBall.Math.PositionedObjectList mMapLists = new FlatRedBall.Math.PositionedObjectList(); float mRenderingScale = 1; float mZSplit = 1; - #endregion +#endregion - #region Properties +#region Properties public float Left { @@ -44,8 +58,23 @@ public float Top public int? NumberTilesWide { get; private set; } public int? NumberTilesTall { get; private set; } - public float? WidthPerTile { get; private set; } - public float? HeightPerTile { get; private set; } + + /// + /// The width in world units for each tile in the map. + /// + /// + /// Normally this property is set when the LayeredTileMap + /// is loaded from a TMX file. + public float? WidthPerTile { get; set; } + + /// + /// The height in world units for each tile in the map. + /// + /// + /// Normally this property is set when the LayeredTileMap + /// is loaded from a TMX file. + public float? HeightPerTile { get; set; } + public Dictionary> TileProperties { get; @@ -169,6 +198,7 @@ public float Height public LayeredTileMapAnimation Animation { get; set; } public List MapProperties { get; set; } + public List Tilesets { get; private set; } IVisible IVisible.Parent @@ -211,7 +241,7 @@ public bool IgnoresParentVisibility } - #endregion +#endregion public IEnumerable TileNamesWith(string propertyName) { @@ -358,6 +388,15 @@ public static LayeredTileMap FromTiledMapSave(string fileName, string contentMan return FromTiledMapSave(fileName, contentManager, tms); } + // from https://stackoverflow.com/questions/321370/how-can-i-convert-a-hex-string-to-a-byte-array + static byte[] StringToByteArray(string hex) + { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + public static LayeredTileMap FromTiledMapSave(string tiledMapSaveFile, string contentManager, TiledMapSave tms) { @@ -383,9 +422,17 @@ public static LayeredTileMap FromTiledMapSave(string tiledMapSaveFile, string co AddShapeCollections(toReturn, tms); - foreach (var layer in tms.MapLayers) + // February 28, 2023 + // Tiled allows multiple layers with the same name. + // We can't do a foreach and find matching by name. + // Instead, we will loop through by index. + //foreach (var layer in tms.MapLayers) + //{ + // var matchingLayer = toReturn.MapLayers.FirstOrDefault(item => item.Name == layer.Name); + for (int i = 0; i < tms.MapLayers.Count; i++) { - var matchingLayer = toReturn.MapLayers.FirstOrDefault(item => item.Name == layer.Name); + AbstractMapLayer layer = tms.MapLayers[i]; + var matchingLayer = toReturn.MapLayers[i]; if (matchingLayer != null) @@ -405,6 +452,16 @@ public static LayeredTileMap FromTiledMapSave(string tiledMapSaveFile, string co matchingLayer.Visible = mapLayer.visible == 1; matchingLayer.Alpha = mapLayer.Opacity; + + if (!string.IsNullOrWhiteSpace(mapLayer.TintColor)) + { + var removedHash = mapLayer.TintColor.Substring(1); + var bytes = StringToByteArray(removedHash); + + matchingLayer.Red = (bytes[0])/255.0f; + matchingLayer.Green = (bytes[1]) / 255.0f; + matchingLayer.Blue = (bytes[2]) / 255.0f; + } } else if (layer is mapObjectgroup objectLayer) { @@ -575,6 +632,8 @@ public static LayeredTileMap FromTiledMapSave(string tiledMapSaveFile, string co { Name = propertySave.name, Value = propertySave.value, Type = propertySave.Type }) .ToList(); + // copy the original tilesets over + toReturn.Tilesets = tms.Tilesets.ToList(); return toReturn; } @@ -678,18 +737,30 @@ private static void AddTileShapeCollectionForLayer(TMXGlueLib.MapLayer layer, Di var tilesetTileGid = tilesetTile.id + tileset.Firstgid; foreach (var tilesetObject in tilesetTile.Objects.@object) { - var name = layer.Name; + const bool applyVisibility = false; + TiledMapToShapeCollectionConverter.ConvertTiledObjectToFrbShape(tilesetObject, applyVisibility, out polygon, out rectangle, out circle); - TiledMapToShapeCollectionConverter.ConvertTiledObjectToFrbShape(tilesetObject, out polygon, out rectangle, out circle); - if (rectangle != null) + var hasShape = polygon != null || rectangle != null || circle != null; + + + TileCollisions.TileShapeCollection collection = null; + if (hasShape) { var collectionName = layer.Name; - if (tilesetObject.Type != null && separateOnTileType) + if (!string.IsNullOrWhiteSpace(tilesetObject.Type)) + { + collectionName = tilesetObject.Type; + } + else if (!string.IsNullOrWhiteSpace(tilesetTile.Type)) { - collectionName += "_" + tilesetObject.Type; + collectionName = tilesetTile.Type; } - var collection = GetOrAddTileShapeCollection(collectionName, collisionDictionary); + collection = GetOrAddTileShapeCollection(collectionName, collisionDictionary); collection.GridSize = tileDimension; + } + + if (rectangle != null) + { rectangle.Z = z; if (sortOnY) { @@ -714,13 +785,6 @@ private static void AddTileShapeCollectionForLayer(TMXGlueLib.MapLayer layer, Di } else if (polygon != null) { - var collectionName = layer.Name; - if (tilesetObject.Type != null && separateOnTileType) - { - collectionName += "_" + tilesetObject.Type; - } - var collection = GetOrAddTileShapeCollection(collectionName, collisionDictionary); - collection.GridSize = tileDimension; // For tile polygons we want them to be centered on the tile. // To do this, we shift all points by its position: @@ -812,7 +876,7 @@ private static void ApplyFlip(uint idWithFlip, Polygon cloned) } } - private static Polygon AddPolygonCloneAtXY(MapLayer layer, float tileDimension, Polygon polygon, List tiles, long tilesetTileGid, int index, + private static Polygon AddPolygonCloneAtXY(MapLayer layer, float tileDimension, Polygon polygon, uint[] tiles, long tilesetTileGid, int index, TileCollisions.TileShapeCollection collectionForThisName) { int xIndex = index % layer.width; @@ -828,7 +892,7 @@ private static Polygon AddPolygonCloneAtXY(MapLayer layer, float tileDimension, return cloned; } - private static void AddRectangleCloneAtXY(MapLayer layer, float tileDimension, AxisAlignedRectangle rectangle, List tiles, long tilesetTileGid, int x, int y, + private static void AddRectangleCloneAtXY(MapLayer layer, float tileDimension, AxisAlignedRectangle rectangle, uint[] tiles, long tilesetTileGid, int x, int y, TileCollisions.TileShapeCollection collectionForThisName) { @@ -836,9 +900,10 @@ private static void AddRectangleCloneAtXY(MapLayer layer, float tileDimension, A var strippedId = tiles[i] & 0x0fffffff; + TiledMapSave.GetFlipBoolsFromGid(tiles[i], out bool flipHorizontally, out bool flipVertically, out bool flipDiagonally); + if (strippedId == tilesetTileGid) { - float xIndex = i % layer.width; // intentional int division float yIndex = i / layer.width; @@ -850,11 +915,37 @@ private static void AddRectangleCloneAtXY(MapLayer layer, float tileDimension, A //cloned.Y = -(yIndex + .5f) * tileDimension; // Actually use the X and Y to get the top left, then use the actual rectangle's X and Y values so that // its offset applies: - cloned.X = rectangle.X + (xIndex) * tileDimension; - cloned.Y = rectangle.Y - (yIndex) * tileDimension; - collectionForThisName.Rectangles.Add(cloned); + var rectX = rectangle.X; + var rectY = rectangle.Y; + var width = rectangle.Width; + var height = rectangle.Height; + + + // diagonal flipping first + if (flipDiagonally) + { + rectX = -rectangle.Y; + rectY = -rectangle.X; + width = rectangle.Height; + height = rectangle.Width; + } + if (flipHorizontally) + { + rectX = tileDimension - rectX; + } + if (flipVertically) + { + rectY = -tileDimension - rectY; + } + + cloned.X = rectX + (xIndex) * tileDimension; + cloned.Y = rectY - (yIndex) * tileDimension; + cloned.Width = width; + cloned.Height = height; + + collectionForThisName.Rectangles.Add(cloned); } } @@ -1000,6 +1091,12 @@ public LayeredTileMap Clone() toReturn.ShapeCollections.Add(shapeCollection.Clone()); } + toReturn.Collisions = new List(this.Collisions.Count); + foreach (var collision in this.Collisions) + { + toReturn.Collisions.Add(collision.Clone()); + } + return toReturn; } @@ -1042,12 +1139,12 @@ public void Destroy() for (int i = 0; i < this.Collisions.Count; i++) { - this.Collisions[i].RemoveFromManagers(); + this.Collisions[i].RemoveFromManagersOneWay(); } for (int i = 0; i < this.ShapeCollections.Count; i++) { - this.ShapeCollections[i].RemoveFromManagers(); + this.ShapeCollections[i].RemoveFromManagers(false); } SpriteManager.RemovePositionedObject(this); diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMapAnimation.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMapAnimation.cs index b5a3f803c..931ce338b 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMapAnimation.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/LayeredTileMapAnimation.cs @@ -43,16 +43,18 @@ private void ReactToChangedAnimationFrame(string spriteName, AnimationChainConta { AnimationFrame animationFrame = animationChainContainer.CurrentFrame; Microsoft.Xna.Framework.Vector4 textureValues = new Microsoft.Xna.Framework.Vector4(); - foreach (var mapLayer in layeredTileMap.MapLayers) + for (int i = 0; i < layeredTileMap.MapLayers.Count; i++) { + MapDrawableBatch mapLayer = layeredTileMap.MapLayers[i]; var nameDictionary = mapLayer.NamedTileOrderedIndexes; if (nameDictionary.ContainsKey(spriteName)) { var indexes = nameDictionary[spriteName]; - foreach (int value in indexes) + for (int i1 = 0; i1 < indexes.Count; i1++) { + int value = indexes[i1]; textureValues.X = animationFrame.LeftCoordinate; textureValues.Y = animationFrame.RightCoordinate; textureValues.Z = animationFrame.TopCoordinate; diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapDrawableBatch.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapDrawableBatch.cs index 8ce7100a9..96af09ec2 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapDrawableBatch.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapDrawableBatch.cs @@ -1,3 +1,13 @@ +#define PreVersion +#define HasFormsObject +#define AddedGeneratedGame1 +#define ListsHaveAssociateWithFactoryBool +#define GumGueHasGetAnimation +#define CsvInheritanceSupport +#define IPositionedSizedObjectInEngine +#define NugetPackageInCsproj + + using System; using System.Collections.Generic; using System.Linq; @@ -13,15 +23,12 @@ using FlatRedBall.Debugging; using FlatRedBall.Math; using TMXGlueLib.DataTypes; - -#if TILEMAPS_ALPHA_AND_COLOR using VertexType = Microsoft.Xna.Framework.Graphics.VertexPositionTexture; -#else -using VertexType = Microsoft.Xna.Framework.Graphics.VertexPositionTexture; -#endif namespace FlatRedBall.TileGraphics { + #region Enums + public enum SortAxis { None, @@ -29,16 +36,24 @@ public enum SortAxis Y } + #endregion + public class MapDrawableBatch : PositionedObject, IVisible, IDrawableBatch { #region Fields protected Tileset mTileset; - #region XML Docs +#if RendererHasExternalEffectManager /// - /// The effect used to draw. Shared by all instances for performance reasons + /// Use the custom shader instead of MG's default. This enables using + /// color operations and linearization for gamma correction. + /// + public static bool UseCustomEffect { get; set; } +#endif + + /// + /// The effect used to draw. Shared by all instances for performance reasons /// - #endregion private static BasicEffect mBasicEffect; private static AlphaTestEffect mAlphaTestEffect; @@ -67,10 +82,41 @@ public class MapDrawableBatch : PositionedObject, IVisible, IDrawableBatch private int mCurrentNumberOfTiles = 0; - public float Red = 1; - public float Green = 1; - public float Blue = 1; - public float Alpha = 1; + float mRed = 1; + float mGreen = 1; + float mBlue = 1; + float mAlpha = 1; + + public float Red + { + get { return mRed; } + set { mRed = value; } + } + + public float Green + { + get { return mGreen; } + set { mGreen = value; } + } + + public float Blue + { + get { return mBlue; } + set { mBlue = value; } + } + + public float Alpha + { + get { return mAlpha; } + set { mAlpha = value; } + } + + ColorOperation mColorOperation = ColorOperation.Modulate; + public ColorOperation ColorOperation + { + get { return mColorOperation; } + set { mColorOperation = value; } + } private SortAxis mSortAxis; @@ -100,13 +146,11 @@ public SortAxis SortAxis } } - #region XML Docs /// - /// Here we tell the engine if we want this batch - /// updated every frame. Since we have no updating to - /// do though, we will set this to false + /// Whether this batch + /// updated every frame. Since MapDrawableBatches do not + /// have any built-in update, this value defaults to false. /// - #endregion public bool UpdateEveryFrame { get { return true; } @@ -152,13 +196,7 @@ public int QuadCount } } - public VertexPositionTexture[] Vertices - { - get - { - return mVertices; - } - } + public VertexType[] Vertices => mVertices; public Texture2D Texture { @@ -200,8 +238,8 @@ public Texture2D Texture /// public float ParallaxMultiplierX { - get { return -_parallaxMultiplierX + 1; } - set { _parallaxMultiplierX = 1 - value; } + get => -_parallaxMultiplierX + 1; + set => _parallaxMultiplierX = 1 - value; } private float _parallaxMultiplierY; @@ -221,7 +259,6 @@ public float ParallaxMultiplierY public TextureFilter? TextureFilter { get; set; } = null; - #endregion #region Constructor / Initialization @@ -229,7 +266,6 @@ public float ParallaxMultiplierY // this exists purely for Clone public MapDrawableBatch() { - } public MapDrawableBatch(int numberOfTiles, Texture2D texture) @@ -242,16 +278,19 @@ public MapDrawableBatch(int numberOfTiles, Texture2D texture) InternalInitialize(); mTexture = texture; - mVertices = new VertexPositionTexture[4 * numberOfTiles]; + mVertices = new VertexType[4 * numberOfTiles]; FlipFlagArray = new byte[numberOfTiles]; mIndices = new int[6 * numberOfTiles]; } - #region XML Docs /// - /// Create and initialize all assets + /// Create a new MapDrawableBatch with vertices to hold numberOfTiles. This creates a new Tileset which + /// stores the argument texture which can be used to add or paint tiles. /// - #endregion + /// + /// Although maps typically have fixed dimensions, this is not required. Tiles can be added anywhere so + /// no dimension parameters are required. + /// public MapDrawableBatch(int numberOfTiles, int textureTileDimensionWidth, int textureTileDimensionHeight, Texture2D texture) : base() { @@ -263,7 +302,7 @@ public MapDrawableBatch(int numberOfTiles, int textureTileDimensionWidth, int te InternalInitialize(); mTexture = texture; - mVertices = new VertexPositionTexture[4 * numberOfTiles]; + mVertices = new VertexType[4 * numberOfTiles]; FlipFlagArray = new byte[numberOfTiles]; mIndices = new int[6 * numberOfTiles]; @@ -345,8 +384,6 @@ void InternalInitialize() } RenderingScale = 1; - - } #endregion @@ -544,13 +581,19 @@ internal static MapDrawableBatch FromReducedLayer(TMXGlueLib.DataTypes.ReducedLa // pad before doing any rotations/flipping const bool pad = true; + float amountToAddX = .0000001f; + float amountToAddY = .0000001f; + if (texture != null) + { + amountToAddX = .037f / texture.Width; + amountToAddY = .037f / texture.Height; + } if (pad) { - const float amountToAdd = .0000001f; - textureValues.X += amountToAdd; // Left - textureValues.Y -= amountToAdd; // Right - textureValues.Z += amountToAdd; // Top - textureValues.W -= amountToAdd; // Bottom + textureValues.X += amountToAddX; // Left + textureValues.Y -= amountToAddX; // Right + textureValues.Z += amountToAddY; // Top + textureValues.W -= amountToAddY; // Bottom } if ((quad.FlipFlags & TMXGlueLib.DataTypes.ReducedQuadInfo.FlippedHorizontallyFlag) == TMXGlueLib.DataTypes.ReducedQuadInfo.FlippedHorizontallyFlag) @@ -609,6 +652,9 @@ internal static MapDrawableBatch FromReducedLayer(TMXGlueLib.DataTypes.ReducedLa toReturn.RegisterName(quad.Name, tileIndex); } + toReturn.ParallaxMultiplierX = reducedLayerInfo.ParallaxMultiplierX; + toReturn.ParallaxMultiplierY = reducedLayerInfo.ParallaxMultiplierY; + return toReturn; } @@ -871,6 +917,81 @@ public void GetTextureCoordiantesForOrderedTile(int orderedTileIndex, out float textureY = vector.Y; } + public float GetRotationZForOrderedTile(int orderedTileIndex) + { + // The order is: + // 3 2 + // + // 0 1 + + // So that means + // 3 2 + // ^ + // | + // Y + // | + // 0 ---X---> 1 + + // We can't use positions because for rotated tiles the positions stay the same, but the + // texture coordiantes do not. So we should use the texture coordiantes. + // A tile's texture coordiantes may look like this: + // (0, 0) (1, 0) + // + // + // + // + // (0, 1) (1, 1) + // + + // The X Axis is set by finding the pair of texture coordinates where + // the Y value is the same, and the X value is increasing + // There may be a more elegant way to do this (mathematically) + // but I'm not sure what that is so we'll just brute force it: + var startIndex = orderedTileIndex * 4; + + var bottomLeft = mVertices[startIndex]; + var bottomRight = mVertices[startIndex + 1]; + var topRight = mVertices[startIndex + 2]; + var topLeft = mVertices[startIndex + 3]; + + Vector3 xAxis = Vector3.UnitX; + + if (bottomLeft.TextureCoordinate.Y == bottomRight.TextureCoordinate.Y) + { + if (bottomRight.TextureCoordinate.X > bottomLeft.TextureCoordinate.X) + { + xAxis = bottomRight.Position - bottomLeft.Position; + } + else + { + xAxis = bottomLeft.Position - bottomRight.Position; + } + } + else + { + // use top right and bottom right + if (topRight.TextureCoordinate.Y == bottomRight.TextureCoordinate.Y) + { + if (topRight.TextureCoordinate.X > bottomRight.TextureCoordinate.X) + { + xAxis = topRight.Position - bottomRight.Position; + } + else + { + xAxis = bottomRight.Position - topRight.Position; + } + } + } + + var rotationZ = (float)System.Math.Atan2(xAxis.Y, xAxis.X); + if (rotationZ < 0) + { + rotationZ += MathHelper.TwoPi; + } + + return rotationZ; + } + public void GetBottomLeftWorldCoordinateForOrderedTile(int orderedTileIndex, out float x, out float y) { // The order is: @@ -885,6 +1006,64 @@ public void GetBottomLeftWorldCoordinateForOrderedTile(int orderedTileIndex, out y = vector.Y; } + /// + /// Returns the quad index at the argument worldX and worldY. Returns null if no quad is found at this index. + /// + /// The absolute world X position. + /// The absolute world Y position. + /// The index found, or null if one isn't found. + public int? GetQuadIndex(float worldX, float worldY) + { + if (mVertices.Length == 0) + { + return null; + } + + var firstVertIndex = 0; + + var lastVertIndexExclusive = mVertices.Length; + + float tileWidth = mVertices[1].Position.X - mVertices[0].Position.X; + + if (mSortAxis == SortAxis.X) + { + firstVertIndex = GetFirstAfterX(mVertices, worldX - tileWidth); + lastVertIndexExclusive = GetFirstAfterX(mVertices, worldX + tileWidth); + } + else if (mSortAxis == SortAxis.Y) + { + firstVertIndex = GetFirstAfterY(mVertices, worldY - tileWidth); + lastVertIndexExclusive = GetFirstAfterY(mVertices, worldY + tileWidth); + } + + for (int i = firstVertIndex; i < lastVertIndexExclusive; i += 4) + { + // Coords are + // 3 2 + // + // 0 1 + + if (mVertices[i + 0].Position.X <= worldX && mVertices[i + 0].Position.Y <= worldY && + mVertices[i + 1].Position.X >= worldX && mVertices[i + 1].Position.Y <= worldY && + mVertices[i + 2].Position.X >= worldX && mVertices[i + 2].Position.Y >= worldY && + mVertices[i + 3].Position.X <= worldX && mVertices[i + 3].Position.Y >= worldY) + { + return i / 4; + } + } + + + return null; + } + + /// + /// Most pixel distortion problems can be solved by snapping the camera position + /// to the pixel using MathFunctions.RoundFloat or using the Camera Controlling Entity. + /// For stubborn distortions only happening on Tiled maps you can use this. It won't + /// do anything unless set to something different to zero. + /// + public static float TileVertexOffset { get; set; } + /// /// Adds a tile to the tile map /// @@ -902,19 +1081,18 @@ public int AddTile(Vector3 bottomLeftPosition, Vector2 dimensions, Vector4 textu int currentIndex = mCurrentNumberOfTiles * 6; // 6 indices per tile (there are mVertices.Length/4 tiles) - float xOffset = bottomLeftPosition.X; - float yOffset = bottomLeftPosition.Y; + float xOffset = bottomLeftPosition.X + TileVertexOffset; + float yOffset = bottomLeftPosition.Y + TileVertexOffset; float zOffset = bottomLeftPosition.Z; - float width = dimensions.X; - float height = dimensions.Y; - + float width = dimensions.X - (TileVertexOffset * 2f); + float height = dimensions.Y - (TileVertexOffset * 2f); // create vertices - mVertices[currentVertex + 0] = new VertexPositionTexture(new Vector3(xOffset + 0f, yOffset + 0f, zOffset), new Vector2(texture.X, texture.W)); - mVertices[currentVertex + 1] = new VertexPositionTexture(new Vector3(xOffset + width, yOffset + 0f, zOffset), new Vector2(texture.Y, texture.W)); - mVertices[currentVertex + 2] = new VertexPositionTexture(new Vector3(xOffset + width, yOffset + height, zOffset), new Vector2(texture.Y, texture.Z)); - mVertices[currentVertex + 3] = new VertexPositionTexture(new Vector3(xOffset + 0f, yOffset + height, zOffset), new Vector2(texture.X, texture.Z)); + mVertices[currentVertex + 0] = new VertexType(new Vector3(xOffset + 0f, yOffset + 0f, zOffset), new Vector2(texture.X, texture.W)); + mVertices[currentVertex + 1] = new VertexType(new Vector3(xOffset + width, yOffset + 0f, zOffset), new Vector2(texture.Y, texture.W)); + mVertices[currentVertex + 2] = new VertexType(new Vector3(xOffset + width, yOffset + height, zOffset), new Vector2(texture.Y, texture.Z)); + mVertices[currentVertex + 3] = new VertexType(new Vector3(xOffset + 0f, yOffset + height, zOffset), new Vector2(texture.X, texture.Z)); // create indices mIndices[currentIndex + 0] = currentVertex + 0; @@ -970,6 +1148,9 @@ public void Draw(Camera camera) //////////////////End Early Out///////////////// + AdjustOffsetAndParallax(camera); + + ForceUpdateDependencies(); int firstVertIndex; int lastVertIndex; @@ -1002,7 +1183,7 @@ public void Draw(Camera camera) // It could use DrawIndexedPrimitives instead for much faster performance, // but to do that we'd have to keep VB's around and make sure to re-create them // whenever the graphics device is lost. - FlatRedBallServices.GraphicsDevice.DrawUserIndexedPrimitives( + FlatRedBallServices.GraphicsDevice.DrawUserIndexedPrimitives( PrimitiveType.TriangleList, mVertices, firstVertIndex, @@ -1039,7 +1220,7 @@ private Effect PrepareRenderingStates(Camera camera, out TextureAddressMode oldT #else FlatRedBall.Graphics.Renderer.BlendOperation = BlendOperation.Regular; #endif - Effect effectTouse = null; + Effect effectToUse = null; if (ZBuffered) { @@ -1049,22 +1230,53 @@ private Effect PrepareRenderingStates(Camera camera, out TextureAddressMode oldT mAlphaTestEffect.World = Matrix.CreateScale(RenderingScale) * base.TransformationMatrix; mAlphaTestEffect.Texture = mTexture; - effectTouse = mAlphaTestEffect; + effectToUse = mAlphaTestEffect; } else { - camera.SetDeviceViewAndProjection(mBasicEffect, false); +#if RendererHasExternalEffectManager && MONOGAME_381 + if (UseCustomEffect) + { + var effectManager = Renderer.ExternalEffectManager; + + var world = Matrix.CreateScale(RenderingScale) * base.TransformationMatrix; + var view = camera.GetLookAtMatrix(false); + var projection = camera.GetProjectionMatrix(); + var worldView = Matrix.Identity; + var worldViewProj = Matrix.Identity; + + Matrix.Multiply(ref world, ref view, out worldView); + Matrix.Multiply(ref worldView, ref projection, out worldViewProj); + + effectManager.ParameterViewProj.SetValue(worldViewProj); + effectManager.ParameterCurrentTexture.SetValue(mTexture); + + var color = CustomEffectManager.ProcessColorForColorOperation(mColorOperation, new Vector4(mRed, mGreen, mBlue, mAlpha)); + effectManager.ParameterColorModifier.SetValue(color); - mBasicEffect.World = Matrix.CreateScale(RenderingScale) * base.TransformationMatrix; - mBasicEffect.Texture = mTexture; + effectToUse = effectManager.Effect; - mBasicEffect.DiffuseColor = new Vector3(Red, Green, Blue); - mBasicEffect.Alpha = Alpha; + var effectTechnique = effectManager.GetColorModifierTechniqueFromColorOperation(mColorOperation); + + if (effectToUse.CurrentTechnique != effectTechnique) + effectToUse.CurrentTechnique = effectTechnique; + } + else +#endif + { + camera.SetDeviceViewAndProjection(mBasicEffect, false); + + mBasicEffect.World = Matrix.CreateScale(RenderingScale) * base.TransformationMatrix; + mBasicEffect.Texture = mTexture; + + mBasicEffect.DiffuseColor = new Vector3(Red, Green, Blue); + mBasicEffect.Alpha = Alpha; #if TILEMAPS_ALPHA_AND_COLOR mBasicEffect.VertexColorEnabled = true; #endif - effectTouse = mBasicEffect; + effectToUse = mBasicEffect; + } } @@ -1076,8 +1288,8 @@ private Effect PrepareRenderingStates(Camera camera, out TextureAddressMode oldT // on non-power-of-two textures. oldTextureAddressMode = Renderer.TextureAddressMode; Renderer.TextureAddressMode = TextureAddressMode.Clamp; - - return effectTouse; + + return effectToUse; } private void GetRenderingIndexValues(Camera camera, out int firstVertIndex, out int lastVertIndex, out int indexStart, out int numberOfTriangles) @@ -1121,7 +1333,7 @@ private void GetRenderingIndexValues(Camera camera, out int firstVertIndex, out numberOfTriangles = (indexEndExclusive - indexStart) / 3; } - public static int GetFirstAfterX(VertexPositionTexture[] list, float xGreaterThan) + public static int GetFirstAfterX(VertexType[] list, float xGreaterThan) { int min = 0; int originalMax = list.Length / 4; @@ -1180,7 +1392,7 @@ public static int GetFirstAfterX(VertexPositionTexture[] list, float xGreaterTha } } - public static int GetFirstAfterY(VertexPositionTexture[] list, float yGreaterThan) + public static int GetFirstAfterY(VertexType[] list, float yGreaterThan) { int min = 0; int originalMax = list.Length / 4; @@ -1237,22 +1449,71 @@ public static int GetFirstAfterY(VertexPositionTexture[] list, float yGreaterTha return list.Length; } } - #region XML Docs - /// - /// Here we update our batch - but this batch doesn't - /// need to be updated - /// - #endregion - public void Update() + + string GetTileNameAt(float worldX, float worldY) { - float leftView = Camera.Main.AbsoluteLeftXEdgeAt(0); - float topView = Camera.Main.AbsoluteTopYEdgeAt(0); + var quadIndex = GetQuadIndex(worldX, worldY); - float cameraOffsetX = leftView - CameraOriginX; - float cameraOffsetY = topView - CameraOriginY; + if (quadIndex != null) + { + // look in all the dictionaries to find which names exist at this index + var foundKeyValuePair = NamedTileOrderedIndexes.FirstOrDefault(item => item.Value.Contains(quadIndex.Value)); + + return foundKeyValuePair.Key; + } + return null; + } + + TMXGlueLib.mapTilesetTile GetTilesetTileAt(float worldX, float worldY, LayeredTileMap map) + { + TMXGlueLib.mapTilesetTile tilesetTile = null; + + var quadIndex = GetQuadIndex(worldX, worldY); + + if (quadIndex != null) + { + // look in all the dictionaries to find which names exist at this index + var foundKeyValuePair = NamedTileOrderedIndexes.FirstOrDefault(item => item.Value.Contains(quadIndex.Value)); + + if (foundKeyValuePair.Key != null) + { + var name = foundKeyValuePair.Key; + + foreach (var tileset in map.Tilesets) + { + tilesetTile = GetTilesetTile(tileset, name); + + if (tilesetTile != null) + { + break; + } + } + } + } + + return tilesetTile; + } + + private TMXGlueLib.mapTilesetTile GetTilesetTile(TMXGlueLib.Tileset tileset, string name) + { + foreach (var kvp in tileset.TileDictionary) + { + var candidate = kvp.Value; + var nameProperty = candidate.properties.FirstOrDefault(item => item.StrippedNameLower == "name"); + + if (nameProperty?.value == name) + { + return candidate; + } + } + + return null; + } - this.RelativeX = cameraOffsetX * _parallaxMultiplierX; - this.RelativeY = cameraOffsetY * _parallaxMultiplierY; + public void Update() + { + var camera = Camera.Main; + AdjustOffsetAndParallax(camera); this.TimedActivity(TimeManager.SecondDifference, TimeManager.SecondDifferenceSquaredDividedByTwo, TimeManager.LastSecondDifference); @@ -1265,6 +1526,37 @@ public void Update() this.UpdateDependencies(TimeManager.CurrentTime); } + private void AdjustOffsetAndParallax(Camera camera) + { + float leftView = NativeCameraWidth != null ? camera.X - NativeCameraWidth.Value / 2f : camera.AbsoluteLeftXEdgeAt(0); + float topView = NativeCameraHeight != null ? camera.Y + NativeCameraHeight.Value / 2.0f : camera.AbsoluteTopYEdgeAt(0); + + float cameraOffsetX = leftView - CameraOriginX; + float cameraOffsetY = topView - CameraOriginY; + + if (camera.Orthogonal) + { + var zoom = camera.DestinationRectangle.Height / camera.OrthogonalHeight; + + var pixelRoundingValue = 1 / zoom; + pixelRoundingValue = System.Math.Min(1, pixelRoundingValue); + + this.RelativeX = MathFunctions.RoundFloat(cameraOffsetX * _parallaxMultiplierX, pixelRoundingValue); + this.RelativeY = MathFunctions.RoundFloat(cameraOffsetY * _parallaxMultiplierY, pixelRoundingValue); + + + } + + else + { + this.RelativeX = cameraOffsetX * _parallaxMultiplierX; + this.RelativeY = cameraOffsetY * _parallaxMultiplierY; + } + } + + public static float? NativeCameraWidth; + public static float? NativeCameraHeight; + // TODO: I would like to somehow make this a property on the LayeredTileMap, but right now it is easier to put them here public float CameraOriginY { get; set; } public float CameraOriginX { get; set; } @@ -1321,16 +1613,40 @@ public void Destroy() public void MergeOntoThis(IEnumerable mapDrawableBatches) { + int quadsOnThis = QuadCount; + + // If this is empty, then this will inherit the first MDB's texture + + if (quadsOnThis == 0 && this.Texture == null) + { + var firstWithNonNullTexture = mapDrawableBatches.FirstOrDefault(item => item.Texture != null); + this.Texture = firstWithNonNullTexture?.Texture; + } + + +#if DEBUG + var thisTexture = this.Texture; + foreach (var mdb in mapDrawableBatches) + { + if(mdb.Texture != thisTexture && mdb.QuadCount > 0) + { + string thisTextureName = thisTexture?.Name ?? ""; + string otherTexture = mdb.Texture?.Name ?? ""; + + throw new InvalidOperationException($"The MapDrawableBatch {mdb.Name} has the texture {otherTexture} which is different than this layer's texture {thisTextureName}"); + } + } +#endif int quadsToAdd = 0; - int quadsOnThis = QuadCount; foreach (var mdb in mapDrawableBatches) { quadsToAdd += mdb.QuadCount; } + int totalNumberOfVerts = 4 * (this.QuadCount + quadsToAdd); int totalNumberOfIndexes = 6 * (this.QuadCount + quadsToAdd); @@ -1452,9 +1768,9 @@ private void MergeSortedX(IEnumerable mapDrawableBatches, int newIndexes[destinationIndexIndex + 5] = destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex + 5]; - if (invertedDictionaries[layerIndexToCopyFrom].ContainsKey(sourceVertIndex/4)) + if (invertedDictionaries[layerIndexToCopyFrom].ContainsKey(sourceVertIndex / 4)) { - var newName = invertedDictionaries[layerIndexToCopyFrom][sourceVertIndex/4]; + var newName = invertedDictionaries[layerIndexToCopyFrom][sourceVertIndex / 4]; if (newNameIndexDictionary.ContainsKey(newName) == false) { @@ -1500,8 +1816,11 @@ private void MergeSortedY(IEnumerable mapDrawableBatches, int mCurrentNumberOfTiles = totalNumberOfVerts / 4; + int newFlagFlipArraySize = 0; foreach (var layer in layers) { + newFlagFlipArraySize += layer.FlipFlagArray.Length; + var invertedLayerDictionary = new Dictionary(); foreach (var kvp in layer.NamedTileOrderedIndexes) @@ -1515,12 +1834,13 @@ private void MergeSortedY(IEnumerable mapDrawableBatches, int invertedDictionaries.Add(invertedLayerDictionary); } + var newFlipFlagArray = new byte[newFlagFlipArraySize]; while (true) { float smallestY = float.PositiveInfinity; //int smallestIndex = -1; - int toCopyFrom = -1; + int layerIndexToCopyFrom = -1; for (int layerIndex = 0; layerIndex < currentVertIndex.Length; layerIndex++) { @@ -1531,60 +1851,67 @@ private void MergeSortedY(IEnumerable mapDrawableBatches, int if (vertY < smallestY) { smallestY = vertY; - toCopyFrom = layerIndex; + layerIndexToCopyFrom = layerIndex; //smallestIndex = currentVertIndex[layerIndex]; } } } - if (toCopyFrom == -1) + if (layerIndexToCopyFrom == -1) { break; } else { - var sourceVertIndex = currentVertIndex[toCopyFrom]; + var layerToCopyFrom = layers[layerIndexToCopyFrom]; + var sourceVertIndex = currentVertIndex[layerIndexToCopyFrom]; var sourceIndexIndex = (sourceVertIndex / 4) * 6; + var sourceFlipIndex = (sourceVertIndex / 4); + + var destinationFlipIndex = destinationVertIndex / 4; + + newFlipFlagArray[destinationFlipIndex] = layerToCopyFrom.FlipFlagArray[sourceFlipIndex]; - newVerts[destinationVertIndex] = layers[toCopyFrom].mVertices[sourceVertIndex]; - newVerts[destinationVertIndex + 1] = layers[toCopyFrom].mVertices[sourceVertIndex + 1]; - newVerts[destinationVertIndex + 2] = layers[toCopyFrom].mVertices[sourceVertIndex + 2]; - newVerts[destinationVertIndex + 3] = layers[toCopyFrom].mVertices[sourceVertIndex + 3]; + newVerts[destinationVertIndex] = layerToCopyFrom.mVertices[sourceVertIndex]; + newVerts[destinationVertIndex + 1] = layerToCopyFrom.mVertices[sourceVertIndex + 1]; + newVerts[destinationVertIndex + 2] = layerToCopyFrom.mVertices[sourceVertIndex + 2]; + newVerts[destinationVertIndex + 3] = layerToCopyFrom.mVertices[sourceVertIndex + 3]; - var firstVert = layers[toCopyFrom].mIndices[sourceIndexIndex]; + var firstVert = layerToCopyFrom.mIndices[sourceIndexIndex]; newIndexes[destinationIndexIndex] = - destinationVertIndex - firstVert + layers[toCopyFrom].mIndices[sourceIndexIndex]; + destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex]; newIndexes[destinationIndexIndex + 1] = - destinationVertIndex - firstVert + layers[toCopyFrom].mIndices[sourceIndexIndex + 1]; + destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex + 1]; newIndexes[destinationIndexIndex + 2] = - destinationVertIndex - firstVert + layers[toCopyFrom].mIndices[sourceIndexIndex + 2]; + destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex + 2]; newIndexes[destinationIndexIndex + 3] = - destinationVertIndex - firstVert + layers[toCopyFrom].mIndices[sourceIndexIndex + 3]; + destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex + 3]; newIndexes[destinationIndexIndex + 4] = - destinationVertIndex - firstVert + layers[toCopyFrom].mIndices[sourceIndexIndex + 4]; + destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex + 4]; newIndexes[destinationIndexIndex + 5] = - destinationVertIndex - firstVert + layers[toCopyFrom].mIndices[sourceIndexIndex + 5]; + destinationVertIndex - firstVert + layerToCopyFrom.mIndices[sourceIndexIndex + 5]; - if (invertedDictionaries[toCopyFrom].ContainsKey(sourceVertIndex/4)) + if (invertedDictionaries[layerIndexToCopyFrom].ContainsKey(sourceVertIndex / 4)) { - var newName = invertedDictionaries[toCopyFrom][sourceVertIndex/4]; + var newName = invertedDictionaries[layerIndexToCopyFrom][sourceVertIndex / 4]; if (newNameIndexDictionary.ContainsKey(newName) == false) { newNameIndexDictionary[newName] = new List(); } - newNameIndexDictionary[newName].Add(destinationVertIndex/4); + newNameIndexDictionary[newName].Add(destinationVertIndex / 4); } destinationVertIndex += 4; destinationIndexIndex += 6; - currentVertIndex[toCopyFrom] += 4; + currentVertIndex[layerIndexToCopyFrom] += 4; } } this.mNamedTileOrderedIndexes = newNameIndexDictionary; + this.FlipFlagArray = newFlipFlagArray; this.mVertices = newVerts; this.mIndices = newIndexes; @@ -1652,7 +1979,50 @@ private void MergeUnsorted(IEnumerable mapDrawableBatches, int } } + public void SortQuadsOnAxis(SortAxis sortAxis) + { + this.SortAxis = sortAxis; + + List quads = new List(); + + for (int i = 0; i < Vertices.Count(); i += 4) + { + var quad = new Quad(); + quad.Vertices[0] = Vertices[i + 0]; + quad.Vertices[1] = Vertices[i + 1]; + quad.Vertices[2] = Vertices[i + 2]; + quad.Vertices[3] = Vertices[i + 3]; + + quads.Add(quad); + } + + Quad[] sortedQuads = null; + if (sortAxis == SortAxis.X) + { + sortedQuads = quads.OrderBy(item => item.Position.X).ToArray(); + } + else if (sortAxis == SortAxis.Y) + { + sortedQuads = quads.OrderBy(item => item.Position.Y).ToArray(); + } + else + { + throw new ArgumentException($"Invalid sort axis: {sortAxis}"); + } + + for (int i = 0; i < sortedQuads.Length; i++) + { + Vertices[i * 4 + 0] = sortedQuads[i].Vertices[0]; + Vertices[i * 4 + 1] = sortedQuads[i].Vertices[1]; + Vertices[i * 4 + 2] = sortedQuads[i].Vertices[2]; + Vertices[i * 4 + 3] = sortedQuads[i].Vertices[3]; + } + } + /// + /// Removes quads from the TileMap using the argument QuadIndexes + /// + /// The indexes of the quads public void RemoveQuads(IEnumerable quadIndexes) { var vertList = mVertices.ToList(); @@ -1719,8 +2089,14 @@ public void RemoveQuads(IEnumerable quadIndexes) #endregion } + #region Additional Classes + public class Quad + { + public Vector3 Position => Vertices[0].Position; + public VertexType[] Vertices = new VertexType[4]; + } public static class MapDrawableBatchExtensionMethods { @@ -1728,5 +2104,5 @@ public static class MapDrawableBatchExtensionMethods } - + #endregion } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapLayer.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapLayer.cs index 74e3a2c8e..3088ce6bc 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapLayer.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapLayer.cs @@ -30,8 +30,6 @@ public List properties private mapLayerData[] dataField; - private string nameField; - private int widthField; private int heightField; @@ -120,12 +118,6 @@ public int height } } - [XmlAttribute("opacity")] - public float Opacity - { - get; set; - } = 1.0f; - [XmlIgnore] public TiledMapSave.LayerVisibleBehavior VisibleBehavior = TiledMapSave.LayerVisibleBehavior.Ignore; diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTileset.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTileset.cs index d1b01d33f..4d74deb4a 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTileset.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTileset.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using FlatRedBall.IO; using System.IO; +using System.Linq; namespace TMXGlueLib { @@ -14,10 +15,48 @@ namespace TMXGlueLib public class Tileset // ReSharper restore InconsistentNaming { + #region Fields/Properties + + /// + [XmlElement("image")] + public TilesetImage[] Images + { + get + { + return this._imageField; + } + set + { + if (this._imageField == null || this._imageField.Length == 0) + { + this._imageField = value; + } + } + } + public bool ShouldSerializeImage() + { + return string.IsNullOrEmpty(this.Source); + } + + + private TilesetImage[] _imageField; private mapTilesetTileOffset[] _tileOffsetField; + [XmlAttribute("version")] + public string Version { get; set; } + + [XmlAttribute("tiledversion")] + public string TiledVersion { get; set; } + + [XmlAttribute("tilecount")] + public int TileCount { get; set; } + + + [XmlAttribute("columns")] + public int Columns { get; set; } + private string _sourceField; [XmlIgnore] @@ -66,59 +105,6 @@ public bool IsShared } } - private void LoadValuesFromSource() - { - if (!string.IsNullOrEmpty(this._sourceField)) - { - _sourceField = _sourceField.Replace("/", "\\"); - - tileset xts = null; - - try - { - - xts = FileManager.XmlDeserialize(_sourceField); - } - catch (FileNotFoundException) - { - string fileAttemptedToLoad = _sourceField; - if (FileManager.IsRelative(_sourceField)) - { - fileAttemptedToLoad = FileManager.RelativeDirectory + _sourceField; - } - - string message = "Could not find the shared tsx file \n" + fileAttemptedToLoad + - "\nIf this is a relative file name, then the loader will use " + - "the FileManager's RelativeDirectory to make the file absolute. Therefore, be sure to set the FileManger's RelativeDirectory to the file represented by " + - "this fileset before setting this property if setting this property manually."; - - - throw new FileNotFoundException(message); - } - - if (xts.image != null) - { - - Images = new TilesetImage[xts.image.Length]; - - Parallel.For(0, xts.image.Length, count => - { - this.Images[count] = new TilesetImage - { - Source = xts.image[count].source, - height = xts.image[count].height != 0 ? xts.image[count].height : xts.tileheight, - width = xts.image[count].width != 0 ? xts.image[count].width : xts.tilewidth - }; - }); - } - this.Name = xts.name; - this.Margin = xts.margin; - this.Spacing = xts.spacing; - this.Tileheight = xts.tileheight; - this.Tilewidth = xts.tilewidth; - this.Tiles = xts.tile; - } - } /// [XmlElement("tileoffset")] @@ -138,30 +124,6 @@ public mapTilesetTileOffset[] Tileoffset } - /// - [XmlElement("image")] - public TilesetImage[] Images - { - get - { - return this._imageField; - } - set - { - if (this._imageField == null || this._imageField.Length == 0) - { - this._imageField = value; - } - } - } - - - public bool ShouldSerializeImage() - { - return string.IsNullOrEmpty(this.Source); - } - - [XmlArray("terraintypes")] public List terraintypes = new List(); @@ -202,6 +164,10 @@ public void RefreshTileDictionary() private IDictionary _tileDictionaryField; + /// + /// Dictionary containing the tiles where the key is the tile.id. This makes searching + /// for tiles by id faster than a .Where call. + /// [XmlIgnore] public IDictionary TileDictionary { @@ -326,6 +292,78 @@ public bool ShouldSerializeMargin() return string.IsNullOrEmpty(this.Source); } + public List wangsets { get; set; } = new List(); + + + #endregion + + private void LoadValuesFromSource() + { + if (!string.IsNullOrEmpty(this._sourceField)) + { + _sourceField = _sourceField.Replace("/", "\\"); + + tileset xts = null; + + if(_sourceField.EndsWith(".json")) + { + throw new System.InvalidOperationException( + $"Could not load tileset {_sourceField} because it uses the .json format which is currently not supported. Try saving your tileset as tsx instead of json."); + } + + try + { + + xts = FileManager.XmlDeserialize(_sourceField); + } + catch (FileNotFoundException) + { + string fileAttemptedToLoad = _sourceField; + if (FileManager.IsRelative(_sourceField)) + { + fileAttemptedToLoad = FileManager.RemoveDotDotSlash( FileManager.RelativeDirectory + _sourceField); + } + + string message = "Could not find the shared tsx file \n" + fileAttemptedToLoad + + "\nIf this is a relative file name, then the loader will use " + + "the FileManager's RelativeDirectory to make the file absolute. Therefore, be sure to set the FileManger's RelativeDirectory to the file represented by " + + "this fileset before setting this property if setting this property manually."; + + + throw new FileNotFoundException(message); + } + + if (xts.image != null) + { + + Images = new TilesetImage[xts.image.Length]; + + Parallel.For(0, xts.image.Length, count => + { + this.Images[count] = new TilesetImage + { + Source = xts.image[count].source, + height = xts.image[count].height != 0 ? xts.image[count].height : xts.tileheight, + width = xts.image[count].width != 0 ? xts.image[count].width : xts.tilewidth + }; + }); + } + this.Name = xts.name; + this.Margin = xts.margin; + this.Spacing = xts.spacing; + this.Tileheight = xts.tileheight; + this.Tilewidth = xts.tilewidth; + this.Tiles = xts.tile; + + this.Version = xts.Version; + this.TiledVersion = xts.TiledVersion; + this.Columns = xts.Columns; + this.TileCount = xts.TileCount; + + this.wangsets = xts.wangsets; + } + } + public override string ToString() { string toReturn = this.Name; @@ -340,6 +378,8 @@ public override string ToString() } } + + public class mapTilesetTerrain { public string name { get; set; } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTilesetTile.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTilesetTile.cs index 42ff1bfde..a314bcae4 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTilesetTile.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/MapTilesetTile.cs @@ -41,6 +41,9 @@ public List properties } } + public bool ShouldSerializeproperties() => mProperties?.Count > 0; + + // Vic asks - shouldn't this be a uint? /// [XmlAttribute()] public int id @@ -52,6 +55,15 @@ public int id [XmlAttribute("type")] public string Type { get; set; } + + + [XmlAttribute("class")] + public string Class + { + get => Type; + set => Type = value; + } + [XmlElement("animation")] public TileAnimation Animation { @@ -62,7 +74,12 @@ public TileAnimation Animation [XmlElement("objectgroup")] public mapObjectgroup Objects { get; set; } - + [XmlAttribute("probability")] + public double Probability + { + get; + set; + } = 1; public mapTilesetTile() { @@ -73,6 +90,11 @@ public override string ToString() { string toReturn = id.ToString(); + if(!string.IsNullOrEmpty(Type)) + { + toReturn += $" {Type} "; + } + if(PropertyDictionary.Count != 0) { toReturn += " ("; diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/NamedValue.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/NamedValue.cs index 285280059..09116ee01 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/NamedValue.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/NamedValue.cs @@ -11,7 +11,7 @@ public struct NamedValue public string Type; public object Value; - public static NamedValue Empty = new NamedValue(); + public static NamedValue Empty => new NamedValue(); public override string ToString() diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.TiledMapSave.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.TiledMapSave.cs index 309817872..c5bfd4bff 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.TiledMapSave.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.TiledMapSave.cs @@ -91,16 +91,39 @@ public static ReducedTileMapInfo FromTiledMapSave(TiledMapSave tiledMapSave, flo } + static List GetAllMapLayers(TiledMapSave tiledMapSave) + { + var layers = new List(); + layers.AddRange(tiledMapSave.MapLayers); + + foreach (var group in tiledMapSave.Group) + { + GetAllMapLayers(group, layers); + } + + return layers; + } + + static void GetAllMapLayers(LayerGroup layerGroup, List layers) + { + layers.AddRange(layerGroup.MapLayers); + foreach (var group in layerGroup.Group) + { + GetAllMapLayers(group, layers); + } + } + private static void CreateFromTiledMapSave(TiledMapSave tiledMapSave, string tmxDirectory, FileReferenceType referenceType, ReducedTileMapInfo reducedTileMapInfo) { ReducedLayerInfo reducedLayerInfo = null; - for (int i = 0; i < tiledMapSave.MapLayers.Count; i++) + var allLayers = GetAllMapLayers(tiledMapSave); + for (int i = 0; i < allLayers.Count; i++) { string directory = tmxDirectory; - var tiledLayer = tiledMapSave.MapLayers[i]; + var tiledLayer = allLayers[i]; string texture = null; @@ -117,7 +140,7 @@ private static void CreateFromTiledMapSave(TiledMapSave tiledMapSave, string tmx firstGid = mapLayer.data[0].tiles.FirstOrDefault(item => item != 0); } } - else + else if (tiledLayer is mapObjectgroup) { var objectLayer = tiledLayer as mapObjectgroup; @@ -157,9 +180,17 @@ private static void CreateFromTiledMapSave(TiledMapSave tiledMapSave, string tmx } } } + else if (tiledLayer is MapImageLayer mapImageLayer) + { + if (!string.IsNullOrEmpty(mapImageLayer.ImageObject?.Source)) + { + directory = tmxDirectory; + texture = FlatRedBall.IO.FileManager.RemoveDotDotSlash(directory + mapImageLayer.ImageObject.Source); + } + } - int tileWidth = FlatRedBall.Math.MathFunctions.RoundToInt(tiledMapSave.tilewidth); - int tileHeight = FlatRedBall.Math.MathFunctions.RoundToInt(tiledMapSave.tileheight); + int tileWidth = tiledMapSave.tilewidth; + int tileHeight = tiledMapSave.tileheight; reducedLayerInfo = new ReducedLayerInfo { @@ -168,6 +199,8 @@ private static void CreateFromTiledMapSave(TiledMapSave tiledMapSave, string tmx Name = tiledLayer.Name, TileWidth = tileWidth, TileHeight = tileHeight, + ParallaxMultiplierX = tiledLayer.ParallaxX, + ParallaxMultiplierY = tiledLayer.ParallaxY, }; reducedTileMapInfo.Layers.Add(reducedLayerInfo); @@ -184,7 +217,12 @@ private static void CreateFromTiledMapSave(TiledMapSave tiledMapSave, string tmx else if (tiledLayer is mapObjectgroup) { - AddObjectLayerTiles(reducedLayerInfo, tiledLayer, tileSet, firstGid, tileWidth, tileHeight); + AddObjectLayerTiles(reducedLayerInfo, tiledLayer, tileSet, tileWidth, tileHeight); + } + + else if (tiledLayer is MapImageLayer mapImageLayer) + { + AddImageLayerTiles(reducedLayerInfo, mapImageLayer, tileWidth, tileHeight); } } } @@ -193,7 +231,7 @@ private static void CreateFromTiledMapSave(TiledMapSave tiledMapSave, string tmx private static void AddTileLayerTiles(TiledMapSave tiledMapSave, ReducedLayerInfo reducedLayerInfo, int i, AbstractMapLayer tiledLayer, Tileset tileSet, int tileWidth, int tileHeight) { var asMapLayer = tiledLayer as MapLayer; - var count = asMapLayer.data[0].tiles.Count; + var count = asMapLayer.data[0].tiles.Length; for (int dataId = 0; dataId < count; dataId++) { var dataAtIndex = asMapLayer.data[0].tiles[dataId]; @@ -265,7 +303,7 @@ private static void AddTileLayerTiles(TiledMapSave tiledMapSave, ReducedLayerInf } } - private static void AddObjectLayerTiles(ReducedLayerInfo reducedLayerInfo, AbstractMapLayer tiledLayer, Tileset tileSet, uint? gid, int tileWidth, int tileHeight) + private static void AddObjectLayerTiles(ReducedLayerInfo reducedLayerInfo, AbstractMapLayer tiledLayer, Tileset tileSet, int tileWidth, int tileHeight) { var asMapLayer = tiledLayer as mapObjectgroup; @@ -289,15 +327,15 @@ private static void AddObjectLayerTiles(ReducedLayerInfo reducedLayerInfo, Abstr quad.RotationDegrees = (float)objectInstance.Rotation; - quad.FlipFlags = (byte)(gid.Value & 0xf0000000 >> 7); + quad.FlipFlags = (byte)(objectInstance.gid & 0xf0000000 >> 7); - var valueWithoutFlip = gid.Value & 0x0fffffff; + var valueWithoutFlip = objectInstance.gid & 0x0fffffff; int leftPixelCoord; int topPixelCoord; int rightPixelCoord; int bottomPixelCoord; - TiledMapSave.GetPixelCoordinatesFromGid(gid.Value, tileSet, + TiledMapSave.GetPixelCoordinatesFromGid((uint)valueWithoutFlip, tileSet, out leftPixelCoord, out topPixelCoord, out rightPixelCoord, out bottomPixelCoord); quad.LeftTexturePixel = (ushort)Math.Min(leftPixelCoord, rightPixelCoord); @@ -316,6 +354,42 @@ private static void AddObjectLayerTiles(ReducedLayerInfo reducedLayerInfo, Abstr } } + private static void AddImageLayerTiles(ReducedLayerInfo reducedLayerInfo, MapImageLayer mapImageLayer, int tileWidth, int tileHeight) + { + ///////////////////////Early Out///////////////////////// + if (mapImageLayer.ImageObject == null) + { + return; + } + ///////////////////////End Early Out///////////////////////// + + ReducedQuadInfo quad = new DataTypes.ReducedQuadInfo(); + + quad.LeftQuadCoordinate = 0; + quad.BottomQuadCoordinate = (float)-mapImageLayer.ImageObject.Height; + + quad.OverridingWidth = mapImageLayer.ImageObject.Width; + quad.OverridingHeight = mapImageLayer.ImageObject.Height; + + quad.RotationDegrees = (float)0; + + // todo... + quad.FlipFlags = 0; + + int leftPixelCoord = 0; + int topPixelCoord = 0; + int rightPixelCoord = (int)mapImageLayer.ImageObject.Width; + int bottomPixelCoord = (int)mapImageLayer.ImageObject.Height; + + quad.LeftTexturePixel = (ushort)Math.Min(leftPixelCoord, rightPixelCoord); + quad.TopTexturePixel = (ushort)Math.Min(topPixelCoord, bottomPixelCoord); + + quad.Name = mapImageLayer.Name; + + reducedLayerInfo?.Quads.Add(quad); + + } + private static void CreateFromSpriteEditorScene(TiledMapSave tiledMapSave, float scale, float zOffset, FileReferenceType referenceType, ReducedTileMapInfo toReturn) { var ses = tiledMapSave.ToSceneSave(scale, referenceType); diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.cs index e1f4b9b51..2a61b4377 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/ReducedTileMapInfo.cs @@ -105,6 +105,13 @@ public class ReducedLayerInfo // Version 2: public int TextureId; + // Version 3: + public float ParallaxMultiplierX = 1f; + public float ParallaxMultiplierY = 1f; + + public float CameraOffsetX = 0f; + public float CameraOffsetY = 0f; + public static ReducedLayerInfo ReadFrom(BinaryReader reader, int version) { @@ -128,6 +135,15 @@ public static ReducedLayerInfo ReadFrom(BinaryReader reader, int version) toReturn.TextureId = reader.ReadInt32(); } + if (version >= 3) + { + toReturn.ParallaxMultiplierX = reader.ReadSingle(); + toReturn.ParallaxMultiplierY = reader.ReadSingle(); + + toReturn.CameraOffsetX = reader.ReadSingle(); + toReturn.CameraOffsetY = reader.ReadSingle(); + } + return toReturn; } @@ -151,6 +167,15 @@ public void WriteTo(BinaryWriter writer, int version) { writer.Write(TextureId); } + + if (version >= 3) + { + writer.Write(ParallaxMultiplierX); + writer.Write(ParallaxMultiplierY); + + writer.Write(CameraOffsetX); + writer.Write(CameraOffsetY); + } } public override string ToString() @@ -181,7 +206,8 @@ public partial class ReducedTileMapInfo // Added: // int NumberCellsWide; // int NumberCellsTall; - public int VersionNumber = 2; + // Version 3 added parallax + public int VersionNumber = 3; public int NumberCellsWide; public int NumberCellsTall; diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TileNodeNetworkCreator.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TileNodeNetworkCreator.cs index 3e4271424..8856cbf3e 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TileNodeNetworkCreator.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TileNodeNetworkCreator.cs @@ -6,6 +6,7 @@ using FlatRedBall.TileGraphics; using FlatRedBall.Math; using TMXGlueLib.DataTypes; +using Microsoft.Xna.Framework; namespace FlatRedBall.AI.Pathfinding { @@ -18,11 +19,93 @@ public static TileNodeNetwork CreateFrom(LayeredTileMap layeredTileMap, Directio FillFromPredicate(nodeNetwork, layeredTileMap, predicate); - nodeNetwork.Visible = true; - return nodeNetwork; } + public static void FillAllExceptFromPredicate(this TileNodeNetwork nodeNetwork, LayeredTileMap layeredTileMap, Func, bool> predicate, int removalTileRadius = 0) + { + var dimensionHalf = layeredTileMap.WidthPerTile.Value / 2.0f; + + var properties = layeredTileMap.TileProperties; + + nodeNetwork.FillCompletely(); + + foreach (var kvp in properties) + { + string name = kvp.Key; + var namedValues = kvp.Value; + + if (predicate(namedValues)) + { + foreach (var layer in layeredTileMap.MapLayers) + { + var dictionary = layer.NamedTileOrderedIndexes; + + if (dictionary.ContainsKey(name)) + { + var indexList = dictionary[name]; + + foreach (var index in indexList) + { + float left; + float bottom; + layer.GetBottomLeftWorldCoordinateForOrderedTile(index, out left, out bottom); + + Vector3 positionToRemove = new Vector3(); + + positionToRemove.X = left + dimensionHalf; + positionToRemove.Y = bottom + dimensionHalf; + + nodeNetwork.RemoveAndUnlinkNode(ref positionToRemove); + + for (int radius = 1; radius <= removalTileRadius; radius++) + { + RemoveTilesAtRadius(radius, positionToRemove); + } + } + } + } + } + + + void RemoveTilesAtRadius(int radius, Vector3 positionToRemove) + { + var currentPosition = positionToRemove + new Vector3(-radius * layeredTileMap.WidthPerTile.Value, radius * layeredTileMap.WidthPerTile.Value, 0); + + for (int i = 0; i < radius * 2; i++) + { + currentPosition.X += layeredTileMap.WidthPerTile.Value; + var copy = currentPosition; + nodeNetwork.RemoveAndUnlinkNode(ref copy); + } + + for (int i = 0; i < radius * 2; i++) + { + currentPosition.Y -= layeredTileMap.WidthPerTile.Value; + var copy = currentPosition; + + nodeNetwork.RemoveAndUnlinkNode(ref copy); + } + + for (int i = 0; i < radius * 2; i++) + { + currentPosition.X -= layeredTileMap.WidthPerTile.Value; + var copy = currentPosition; + + nodeNetwork.RemoveAndUnlinkNode(ref copy); + } + + for (int i = 0; i < radius * 2; i++) + { + currentPosition.Y += layeredTileMap.WidthPerTile.Value; + var copy = currentPosition; + + nodeNetwork.RemoveAndUnlinkNode(ref copy); + } + } + } + } + public static void FillFromPredicate(this TileNodeNetwork nodeNetwork, LayeredTileMap layeredTileMap, Func, bool> predicate) { var dimensionHalf = layeredTileMap.WidthPerTile.Value / 2.0f; @@ -135,6 +218,44 @@ bool CreateFromTypesPredicate(List list) return CreateFrom(layeredTileMap, directionalType, CreateFromTypesPredicate); } + public static TileNodeNetwork CreateFromTilesWithoutTypes(LayeredTileMap layeredTileMap, DirectionalType directionalType, params string[] types) => + CreateFromTilesWithoutTypes(layeredTileMap, directionalType, (ICollection)types); + + public static TileNodeNetwork CreateFromTilesWithoutTypes(LayeredTileMap layeredTileMap, DirectionalType directionalType, int removalTileRadius, params string[] types) => + CreateFromTilesWithoutTypes(layeredTileMap, directionalType, (ICollection)types, removalTileRadius); + + public static TileNodeNetwork CreateFromTilesWithoutTypes(LayeredTileMap layeredTileMap, DirectionalType directionalType, ICollection types, int removalTileRadius = 0) + { + bool CreateFromTypesPredicate(List list) + { + var toReturn = false; + + foreach (var namedValue in list) + { + if (namedValue.Name == "Type") + { + var valueAsString = namedValue.Value as string; + + if (!string.IsNullOrEmpty(valueAsString) && types.Contains(valueAsString)) + { + toReturn = true; + break; + } + } + } + + return toReturn; + } + + //return CreateFrom(layeredTileMap, directionalType, CreateFromTypesPredicate); + + TileNodeNetwork nodeNetwork = CreateTileNodeNetwork(layeredTileMap, directionalType); + + FillAllExceptFromPredicate(nodeNetwork, layeredTileMap, CreateFromTypesPredicate, removalTileRadius); + + return nodeNetwork; + } + public static void FillFromTypes(this TileNodeNetwork tileNodeNetwork, LayeredTileMap layeredTileMap, DirectionalType directionalType, ICollection types) { bool CreateFromTypesPredicate(List list) diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Conversion.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Conversion.cs index fbd8cf255..0069fab68 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Conversion.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Conversion.cs @@ -109,9 +109,12 @@ public void MoveTypeToProperties() var properties = tileset.TileDictionary[item.gid.Value - tileset.Firstgid]; if (!string.IsNullOrEmpty(properties.Type)) { - - item.properties.Add(new property { name = "Type", Type = "string", value = properties.Type }); - item.PropertyDictionary["Type"] = properties.Type; + if(item.PropertyDictionary.ContainsKey("Type")) { + //If it already has a Type, it's overridden in tiled so we don't want the base tileset Type + } else { + item.properties.Add(new property { name = "Type", Type = "string", value = properties.Type }); + item.PropertyDictionary["Type"] = properties.Type; + } } } @@ -682,7 +685,7 @@ public NodeNetwork ToNodeNetwork(bool linkHorizontally, bool linkVertically, boo MapLayer mLayer = mapLayer; int mLayerCount = layercount; - Parallel.For(0, mapLayer.data[0].tiles.Count, count => + Parallel.For(0, mapLayer.data[0].tiles.Length, count => { uint gid = mLayer.data[0].tiles[count]; @@ -693,14 +696,14 @@ public NodeNetwork ToNodeNetwork(bool linkHorizontally, bool linkVertically, boo //int tileWidth = requireTile ? tileSet.tilewidth : tilewidth; //int tileHeight = requireTile ? tileSet.tileheight : tileheight; - int x = count % this.Width; - int y = count / this.Width; + int x = (int)count % this.Width; + int y = (int)count / this.Width; float nodex; float nodey; float nodez; - CalculateWorldCoordinates(mLayerCount, count, tilewidth, tileheight, mLayer.width, out nodex, out nodey, out nodez); + CalculateWorldCoordinates(mLayerCount, (int)count, tilewidth, tileheight, mLayer.width, out nodex, out nodey, out nodez); node.X = nodex; node.Y = nodey; @@ -855,7 +858,7 @@ public SceneSave ToSceneSave(float scale, FileReferenceType referenceType = File MapLayer mLayer = mapLayer; int mLayerCount = layercount; - for (int i = 0; i < mapLayer.data[0].tiles.Count; i++) + for (int i = 0; i < mapLayer.data[0].tiles.Length; i++) { uint gid = mLayer.data[0].tiles[i]; if (gid > 0) @@ -1111,7 +1114,7 @@ private SpriteSave CreateSpriteSaveFromMapTileset(float scale, int layercount, M private static bool IsName(string key) { - return property.GetStrippedName(key).ToLower() == "name"; + return String.Equals(property.GetStrippedName(key), "name", StringComparison.OrdinalIgnoreCase); } public void CalculateWorldCoordinates(int layerIndex, int tileIndex, int tileWidth, int tileHeight, int layerWidth, out float x, out float y, out float z) @@ -1362,8 +1365,15 @@ public static TiledMapSave FromFile(string fileName) try { - tms = FileManager.XmlDeserialize(fileName); - tms.FileName = fileName; + if(fileName?.EndsWith(".json") == true) + { + throw new InvalidOperationException("Could not load TMX file with .json extension"); + } + else + { + tms = FileManager.XmlDeserialize(fileName); + tms.FileName = fileName; + } } finally { diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Serialization.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Serialization.cs index 8616ed9d1..fd6f4aa1c 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Serialization.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapSave.Serialization.cs @@ -8,9 +8,6 @@ using System.Linq; using System.Xml.Schema; -// -// This source code was auto-generated by xsd, Version=2.0.50727.3038. -// namespace TMXGlueLib { #region TiledMapSave Class @@ -19,15 +16,16 @@ namespace TMXGlueLib [XmlRoot(ElementName = "map", Namespace = "", IsNullable = false)] public partial class TiledMapSave { + #region Enums public enum LayerVisibleBehavior { Ignore, Match, Skip }; - #region Fields + #endregion + private IDictionary propertyDictionaryField = null; List mProperties = new List(); - #endregion [XmlIgnore] public string FileName @@ -71,7 +69,7 @@ public static IDictionary BuildPropertyDictionaryConcurrently(IE }); return propertyDictionary; } - + public List properties { get { return mProperties; } @@ -95,12 +93,15 @@ public List Tilesets set; } - + [XmlElement("layer", typeof(MapLayer))] [XmlElement("imagelayer", typeof(MapImageLayer))] [XmlElement("objectgroup", typeof(mapObjectgroup))] public List MapLayers { get; set; } + [XmlElement("group")] + public List Group { get; set; } + /// @@ -166,6 +167,12 @@ public int tileheight set; } + [XmlAttribute("infinite")] + public int Infinite + { + get; set; + } + public TiledMapSave() { MapLayers = new List(); @@ -176,11 +183,11 @@ public List GetReferencedFiles() { List referencedFiles = new List(); - if(this.Tilesets != null) + if (this.Tilesets != null) { foreach (var tileset in this.Tilesets) { - if(!string.IsNullOrEmpty(tileset.Source )) + if (!string.IsNullOrEmpty(tileset.Source)) { referencedFiles.Add(tileset.Source); } @@ -189,7 +196,7 @@ public List GetReferencedFiles() var image = tileset.Images[0]; string fileName = image.Source; - + // keep it relative referencedFiles.Add(fileName); @@ -198,7 +205,7 @@ public List GetReferencedFiles() } } - foreach(var layer in this.Layers) + foreach (var layer in this.Layers) { // Vic says: This wasn't doing anything, do we need it? //foreach(var dataItem in layer.data) @@ -207,15 +214,15 @@ public List GetReferencedFiles() //} } - foreach(var objectLayer in this.objectgroup) + foreach (var objectLayer in this.objectgroup) { - if(objectLayer.@object != null) + if (objectLayer.@object != null) { - foreach(var item in objectLayer.@object) + foreach (var item in objectLayer.@object) { - foreach(var property in item.properties) + foreach (var property in item.properties) { - if(property.Type == "file" && !string.IsNullOrWhiteSpace(property.value)) + if (property.Type == "file" && !string.IsNullOrWhiteSpace(property.value)) { referencedFiles.Add(property.value); } @@ -231,6 +238,30 @@ public List GetReferencedFiles() #endregion + #region LayerGroup +#if !UWP + [Serializable] +#endif + [XmlType(AnonymousType = true)] + public class LayerGroup + { + [XmlAttribute("id")] + public int Id { get; set; } + + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlElement("layer", typeof(MapLayer))] + [XmlElement("imagelayer", typeof(MapImageLayer))] + [XmlElement("objectgroup", typeof(mapObjectgroup))] + public List MapLayers { get; set; } + + [XmlElement("group")] + public List Group { get; set; } + } + + #endregion + #region property Class public partial class property { @@ -294,7 +325,7 @@ public static string GetPropertyName(string name) int open = name.IndexOf('('); int close = name.IndexOf(')'); - int afterOpen = open+1; + int afterOpen = open + 1; return name.Substring(afterOpen, close - afterOpen); @@ -315,6 +346,8 @@ public override string ToString() } #endregion + #region MapImageLayer + #if !UWP [Serializable] #endif @@ -322,9 +355,6 @@ public partial class MapImageLayer : AbstractMapLayer { private MapImageLayerImage imageField; - private float _offsetX; - private float _offsetY; - List mProperties = new List(); public List properties @@ -376,22 +406,11 @@ public float Opacity set; } = 1.0f; - [XmlAttribute("offsetx")] - public float OffsetX - { - get { return _offsetX; } - set { _offsetX = value; } - } - - [XmlAttribute("offsety")] - public float OffsetY - { - get { return _offsetY; } - set { _offsetY = value; } - } - } + #endregion + + #region MapImageLayerImage public partial class MapImageLayerImage { private string _source; @@ -412,6 +431,9 @@ public string Source public float Height { get; set; } } + #endregion + + #region TilesetImage /// [XmlType(AnonymousType = true)] @@ -489,6 +511,10 @@ public int height } } + #endregion + + #region mapTilesetTileOffset + /// [XmlType(AnonymousType = true)] public partial class mapTilesetTileOffset @@ -527,7 +553,9 @@ public int y } } + #endregion + #region mapLayerDataTile public partial class mapLayerDataTile { @@ -535,6 +563,8 @@ public partial class mapLayerDataTile public string gid { get; set; } } + #endregion + /// [XmlType(AnonymousType = true)] public partial class mapLayerData @@ -550,14 +580,8 @@ public partial class mapLayerData [XmlAttribute()] public string encoding { - get - { - return this.encodingField; - } - set - { - this.encodingField = value; - } + get => encodingField; + set => encodingField = value; } /// @@ -581,14 +605,8 @@ public string compression [XmlText()] public string Value { - get - { - return this.valueField; - } - set - { - this.valueField = value; - } + get => valueField; + set => valueField = value; } @@ -596,9 +614,9 @@ public string Value /// Represents the index that this tile is displaying from the source tile map. This is 1-based. 0 means no tile. /// This can span multiple tilesets. /// - private List _ids = null; - - + private uint[] _ids = null; + + /// /// Represents all of the tiles in this layer. A tile with index 0 means there is no tile there. Non-zero values /// mean that the value is painted. Painted values are global IDs of tiles. Index 0 is the top-left tile. Increasing @@ -606,7 +624,7 @@ public string Value /// next row. /// [XmlIgnore] - public List tiles + public uint[] tiles { get { @@ -619,7 +637,7 @@ public List tiles { if (encodingField != null && encodingField != "csv") { - _ids = new List(length); + _ids = new uint[length]; // get a stream to the decoded Base64 text var trimmedValue = Value.Trim(); @@ -633,10 +651,10 @@ public List tiles case "zlib": #if SUPPORTS_ZLIB data = new Ionic.Zlib.ZlibStream(data, Ionic.Zlib.CompressionMode.Decompress, false); + break; #else throw new NotImplementedException("Does not support zlib"); #endif - break; case null: // Not compressed. Data is already decoded. break; @@ -649,18 +667,30 @@ public List tiles { using (BinaryReader reader = new BinaryReader(data)) { - _ids = new List(); - for (int i = 0; i < length; i++) - { - _ids.Add(reader.ReadUInt32()); - } + var byteArray = reader.ReadBytes(4 * length); + Buffer.BlockCopy(byteArray, 0, _ids, 0, length * 4); } + + + //using (BinaryReader reader = new BinaryReader(data)) + //{ + // var list = new List(); + // for (int i = 0; i < length; i++) + // { + // list.Add(reader.ReadUInt32()); + // } + // _ids = list.ToArray(); + //} + + + } } else if (encodingField == "csv") { + string[] idStrs = Value.Split(",\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - _ids = idStrs.AsParallel().Select(id => + _ids = idStrs.AsParallel().Select(id => { uint gid; if (!uint.TryParse(id, out gid)) @@ -668,7 +698,7 @@ public List tiles gid = 0; } return gid; - }).ToList(); + }).ToArray(); } else if (encodingField == null) { @@ -680,7 +710,7 @@ public List tiles gid = 0; } return gid; - }).ToList(); + }).ToArray(); } } @@ -690,6 +720,55 @@ public List tiles } public int length { get; set; } + + public void SetTileData(uint[] newData, string encoding, string compression) + { + this.encoding = encoding; + this.compression = compression; + + if (encodingField != "csv") + { + if (compression == "gzip") + { + { + string convertedString = + CompressGzip(newData); + + this.Value = "\n " + convertedString + "\n"; + + } + } + else + { + throw new NotImplementedException(); + } + } + else + { + throw new NotImplementedException($"Encoding {encodingField} not supported"); + } + } + + private static string CompressGzip(uint[] newData) + { + var memoryStream = new MemoryStream(); + var gzipStream = new GZipStream(memoryStream, CompressionLevel.Optimal); + var writer = new BinaryWriter(gzipStream); + for (int i = 0; i < newData.Length; i++) + { + writer.Write(newData[i]); + } + + writer.Flush(); + gzipStream.Flush(); + gzipStream.Close(); + + var memoryBytes = memoryStream.ToArray(); + + return Convert.ToBase64String(memoryBytes); + } + + } #if !UWP @@ -699,6 +778,15 @@ public partial class mapObjectgroup : AbstractMapLayer { private mapObjectgroupObject[] objectField; + public bool ShouldSerializetintcolor() => false; + public bool ShouldSerializeopacity() => false; + public bool ShouldSerializeparallaxx() => false; + public bool ShouldSerializeparallaxy() => false; + public bool ShouldSerializeoffsetx() => false; + public bool ShouldSerializeoffsety() => false; + + [XmlAttribute("draworder")] + public string DrawOrder { get; set; } List mProperties = new List(); @@ -711,6 +799,8 @@ public List properties } } + public bool ShouldSerializeproperties() => properties?.Count > 0; + private IDictionary propertyDictionaryField = null; [XmlIgnore] @@ -752,6 +842,9 @@ public override string ToString() /// public partial class mapObjectgroupObject { + [XmlAttribute("visible")] + public int Visible { get; set; } = 1; + private mapObjectgroupObjectEllipse ellipseField = null; private mapObjectgroupObjectPolygon[] polygonField; @@ -785,6 +878,12 @@ public string Type get; set; } + [XmlAttribute(AttributeName = "class")] + public string Class { + get { return Type; } + set { Type = value; } + } + [XmlIgnore] public uint? gid { get; set; } @@ -829,6 +928,8 @@ public List properties mProperties = value; } } + public bool ShouldSerializeproperties() => properties?.Count > 0; + [XmlElement("ellipse", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)] public mapObjectgroupObjectEllipse ellipse @@ -839,7 +940,7 @@ public mapObjectgroupObjectEllipse ellipse } set { - ellipseField = value; + ellipseField = value; } } @@ -906,6 +1007,8 @@ public double y /// [XmlAttribute()] public float height { get; set; } + public bool ShouldSerializeheight() => height != 0; + [XmlAttribute("rotation")] public double Rotation @@ -913,18 +1016,23 @@ public double Rotation get; set; } + public bool ShouldSerializeRotation() => Rotation != 0; + } [XmlType(AnonymousType = true)] public partial class mapObjectgroupObjectEllipse { - + [XmlAttribute("visible")] + public int Visible { get; set; } = 1; } /// [XmlType(AnonymousType = true)] public partial class mapObjectgroupObjectPolygon { + [XmlAttribute("visible")] + public int Visible { get; set; } = 1; private string pointsField; @@ -943,12 +1051,14 @@ public string points } } -#region mapObjectgroupObjectPolyline + #region mapObjectgroupObjectPolyline /// [XmlType(AnonymousType = true)] public partial class mapObjectgroupObjectPolyline { + [XmlAttribute("visible")] + public int Visible { get; set; } = 1; private string pointsField; @@ -967,9 +1077,9 @@ public string points } } -#endregion + #endregion -#region NewDataSet Class + #region NewDataSet Class /// [XmlType(AnonymousType = true)] [XmlRoot(Namespace = "", IsNullable = false)] @@ -993,6 +1103,6 @@ public TiledMapSave[] Items } } -#endregion + #endregion } diff --git a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapToShapeCollectionConverter.cs b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapToShapeCollectionConverter.cs index 579078160..75fb9de06 100644 --- a/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapToShapeCollectionConverter.cs +++ b/Samples/Platformer/MultiplayerPlatformerDemo/MultiplayerPlatformerDemo/TileGraphics/TiledMapToShapeCollectionConverter.cs @@ -57,7 +57,7 @@ public static void AddShapeToShapeCollection(mapObjectgroupObject @object, Shape AxisAlignedRectangle rectangle; Circle circle; - ConvertTiledObjectToFrbShape(@object, out polygon, out rectangle, out circle); + ConvertTiledObjectToFrbShape(@object, true, out polygon, out rectangle, out circle); if (polygon != null) { @@ -73,7 +73,7 @@ public static void AddShapeToShapeCollection(mapObjectgroupObject @object, Shape } } - public static void ConvertTiledObjectToFrbShape(mapObjectgroupObject @object, out Polygon polygon, out AxisAlignedRectangle rectangle, out Circle circle) + public static void ConvertTiledObjectToFrbShape(mapObjectgroupObject @object, bool applyVisibility, out Polygon polygon, out AxisAlignedRectangle rectangle, out Circle circle) { polygon = null; rectangle = null; @@ -85,6 +85,10 @@ public static void ConvertTiledObjectToFrbShape(mapObjectgroupObject @object, ou // TODO: Make this a rectangle object polygon = ConvertTmxObjectToFrbPolygon(@object.Name, @object.x, @object.y, @object.Rotation, tiledPolygon.points, true); + if (applyVisibility) + { + polygon.Visible = tiledPolygon.Visible == 1; + } } } @@ -94,6 +98,7 @@ public static void ConvertTiledObjectToFrbShape(mapObjectgroupObject @object, ou { polygon = ConvertTmxObjectToFrbPolygon(@object.Name, @object.x, @object.y, @object.Rotation, polyline.points, false); + polygon.Visible = polyline.Visible == 1; } } @@ -109,6 +114,10 @@ public static void ConvertTiledObjectToFrbShape(mapObjectgroupObject @object, ou ScaleX = @object.width / 2, ScaleY = @object.height / 2, }; + if (applyVisibility) + { + rectangle.Visible = @object.Visible == 1; + } } else if (@object.ellipse != null && @object.width == @object.height) @@ -120,7 +129,10 @@ public static void ConvertTiledObjectToFrbShape(mapObjectgroupObject @object, ou Y = (float)-@object.y - (@object.height / 2), Radius = @object.width / 2 }; - + if (applyVisibility) + { + circle.Visible = @object.Visible == 1; + } } else {