1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-25 14:08:34 +01:00

Compare commits

...

84 commits

Author SHA1 Message Date
Ell
9b090c954f 6.0.0 2022-07-25 18:44:16 +02:00
Ell
064dc5607e resolved demo update todo 2022-07-25 18:42:52 +02:00
Ell
663d7148fe Revert "restore android workload when building"
This reverts commit 79f7206686.
2022-07-25 18:38:23 +02:00
Ell
79f7206686 restore android workload when building 2022-07-25 18:26:17 +02:00
Ell
711f60a97e Updated demos and templates to MonoGame 3.8.1 2022-07-25 18:23:16 +02:00
Ell
48dfa8f1ee Allow RandomExtensions to operate on any ICollection 2022-07-19 15:20:19 +02:00
Ell
e673ccea61 fixed a missing using directive in MLEM.FNA 2022-07-18 20:46:59 +02:00
Ell
ba1058748e Allow specifying multiple names for a DataTextureAtlas region 2022-07-18 20:41:19 +02:00
Ell
08e28cb95b Allow manually setting a RootElement as CanBeActive 2022-07-18 15:53:26 +02:00
Ell
288b8352af improved the README and some package descriptions slightly 2022-07-04 23:53:48 +02:00
Ell
d32bc0cbfb Added TryGetUpTime, GetUpTime, TryGetTimeSincePress and GetTimeSincePress to InputHandler 2022-06-29 15:57:41 +02:00
Ell
d58c5d8b33 fixed the new TextInput not updating correctly if the Font is set late 2022-06-29 14:34:13 +02:00
Ell
6f05263980 ensure that SimpleEndCode doesn't end formatting codes with shorter names 2022-06-26 17:18:38 +02:00
Ell
6e2c2b3730 Improved the way terminating formatting codes work by introducing SimpleEndCode 2022-06-26 15:08:11 +02:00
Ell
92018eea1e Made the base package's description more descriptive
This reverts commit 2973bd98e2.
This reverts commit 04c6bb5ff8.
2022-06-26 14:25:19 +02:00
Ell
04c6bb5ff8 neaten up the MLEM.Input description 2022-06-25 23:50:25 +02:00
Ell
2973bd98e2 moved MLEM's Input namespace into its own package 2022-06-25 23:46:06 +02:00
Ell
87b575b5c3 bump major version and cleaned up readme 2022-06-24 21:39:53 +02:00
Ell
61055148ef some fixes and doc changes to make MLEM.FNA more in line with MLEM 2022-06-24 21:33:24 +02:00
Ell
334918103f link to the mlem search directly for baget 2022-06-24 17:20:15 +02:00
Ell
42a87471fc fixed UiSystem AutoScaleReferenceSize default value 2022-06-24 15:21:08 +02:00
Ell
9c370f75e8 switch non-core dependencies over to core 2022-06-24 15:04:01 +02:00
Ell
059e26781b fixed submodules not being restored on ci 2022-06-24 14:11:43 +02:00
Ell
5fcdda80dc fixed multiline text inputs not working on FNA 2022-06-24 14:10:24 +02:00
Ell
5d7d238630 added MLEM.FNA 2022-06-24 14:01:26 +02:00
Ell
aff61508c4 Added TextInput class, which is an isolated version of MLEM.Ui's TextField logic 2022-06-19 18:17:46 +02:00
Ell
282a398f3b fixed the text formatting demo being too dark for drop shadows to be visible 2022-06-17 23:17:45 +02:00
Ell
6b08e892f7 added a proper text formatting demo 2022-06-17 21:47:40 +02:00
Ell
aabb1ed5df added .editorconfig 2022-06-17 18:23:47 +02:00
Ell
1795acb30e Added GenericInput support for Buttons.None 2022-06-15 11:44:28 +02:00
Ell
59af00c89a Code cleanup, and marked AStar.InfiniteCost as obsolete 2022-06-15 11:38:11 +02:00
Ell
d0ece92550 return the ElementType along with each Element in Parse 2022-06-14 00:08:05 +02:00
Ell
01fb5288ff finished up UiMarkdownParser 2022-06-14 00:04:57 +02:00
Ell
7b1da2f1a7 fixed image base path not being applied properly 2022-06-13 23:59:56 +02:00
Ell
f53305ce42 Added UiMarkdownParser 2022-06-13 23:52:10 +02:00
Ell
d03116a49a Allow using multiple textures in a StaticSpriteBatch 2022-06-08 11:05:18 +02:00
Ell
7d9633d989 Fixed StaticSpriteBatch not resetting its texture when all items are removed 2022-06-07 11:57:25 +02:00
Ell
42993f1a0b Added LayerPositionF to MLEM.Extended 2022-06-06 23:50:13 +02:00
Ell
0a93fb7da7 Allow using a StaticSpriteBatch to draw an IndividualTiledMapRenderer 2022-06-06 22:56:48 +02:00
Ell
144062fa64 cleaned up sandbox texture atlas 2022-05-29 15:05:23 +02:00
Ell
16053d9d04 further improve runtime texture packer performance by caching the first possible position for a request of a given size 2022-05-28 21:21:25 +02:00
Ell
61770d59b1 further improve RuntimeTexturePacker performance by only checking against already packed textures 2022-05-27 11:41:21 +02:00
Ell
93a82bcf36 cache UniformTextureAtlas texture straight away when checking for transparency 2022-05-27 11:19:29 +02:00
Ell
fdf04a7e77 allow ignoring transparent regions when packing a UniformTextureAtlas 2022-05-27 11:16:16 +02:00
Ell
cb496f613f Improve RuntimeTexturePacker performance by checking against packed textures in the same order as packing 2022-05-27 11:02:33 +02:00
Ell
951f4babd5 Fixed gamepad auto-nav angle being incorrect for some elements 2022-05-26 11:39:55 +02:00
Ell
b9f2de8290 Made RuntimeTexturePacker padding be per request and improve performance by caching texture data 2022-05-25 13:18:25 +02:00
Ell
f0f1d7f8ed added runtime texture region padding and other improvements 2022-05-25 12:37:51 +02:00
Ell
fcca5300ae Fixed elements' OnDeselected events not being raised when CanBeSelected is set to false while selected 2022-05-21 20:42:54 +02:00
Ell
bd9d3f970b Added RandomPitchModifier and GetRandomPitch to SoundEffectInfo 2022-05-20 16:59:28 +02:00
Ell
161d44dbe0 Added an Enum constructor to GenericInput 2022-05-18 21:45:38 +02:00
Ell
30bcdc1710 Allow comparing Keybind and Combination based on the amount of modifiers they have 2022-05-18 18:50:00 +02:00
Ell
15b873a8ad fixed xml doc recursive reference 2022-05-18 16:01:24 +02:00
Ell
6dc4011ef5 Added optional isKeybindAllowed parameter to KeybindButton 2022-05-18 15:54:29 +02:00
Ell
8968e6025d Added IsPressConsumed and IsAnyPressedAvailable to InputHandler 2022-05-17 18:20:31 +02:00
Ell
03accff6ae modify AutoNavGroup behavior to disallow new selections 2022-05-17 16:06:22 +02:00
Ell
5ba550619d Added AndThen to Easings 2022-05-10 21:32:28 +02:00
Ell
7ebbe49786 Added ReverseInput and ReverseOutput to Easings 2022-05-10 20:56:14 +02:00
Ell
61439aa521 made the MLEM.Data description more descriptive 2022-05-10 16:06:45 +02:00
Ell
874be1fd6e Fixed SoundEffectReader incorrectly claiming it could read ogg and mp3 files 2022-05-10 15:58:47 +02:00
Ell
acd15fea14 improved Ui and Data package descriptions 2022-05-10 15:42:20 +02:00
Ell
47b58b1942 Premultiply textures when using RawContentManager 2022-05-07 21:39:36 +02:00
Ell
16b9e26969 Fixed elements sometimes staying hidden when they shouldn't in scrolling panels 2022-05-04 13:54:15 +02:00
Ell
98118e540a Allow manually hiding a paragraph without its text overriding the hidden state 2022-05-04 13:22:24 +02:00
Ell
58b716aabb Don't query a paragraph's text callback in the constructor 2022-05-03 20:26:39 +02:00
Ell
63d2353694 Improved ElementHelper.AddTooltip overloads 2022-05-03 20:10:26 +02:00
Ell
15a57d8db9 Turned Tooltip paragraph styling into style properties 2022-05-03 19:35:44 +02:00
Ell
5a1b31e8a3 Allow adding dropdown and tooltip elements at a specified index 2022-05-03 19:07:53 +02:00
Ell
435042e1f5 Allow Tooltip to manage more than one paragraph and make it easier to add new lines 2022-05-03 18:58:18 +02:00
Ell
4c24284a3f updated templates 2022-04-30 13:14:44 +02:00
Ell
bc0f9d5c0c consume other UI inputs too 2022-04-30 12:26:40 +02:00
Ell
610527374e Make use of the new consuming variants in InputHandler and Keybind to consume UiControls inputs 2022-04-30 12:14:08 +02:00
Ell
4a88cca8bf also added consuming variants of IsPressed to Keybind 2022-04-30 11:38:05 +02:00
Ell
8adee49e55 fixed scroll bars not working with the new InvertPressBehavior 2022-04-30 11:31:40 +02:00
Ell
46c77d2444 Added InputHandler.InvertPressBehavior 2022-04-29 15:34:04 +02:00
Ell
6393d879d9 added SpriteBatchContext 2022-04-25 15:25:58 +02:00
Ell
c78bafd000 Ensure that Element.IsMouseOver is always accurate by making it an auto-property 2022-04-15 14:18:55 +02:00
Ell
783da33107 Fixed elements not being deselected when removed through RemoveChild 2022-04-15 14:16:38 +02:00
Ell
be26a2ebc2 made the new AutoNavGroup also work for gamepad controls 2022-04-14 18:01:30 +02:00
Ell
45afd9ac79 Added Element.AutoNavGroup which allows forming groups for auto-navigation 2022-04-14 17:54:25 +02:00
Ell
ad29b46df3 Fixed radio buttons not unchecking all other radio buttons with the same root element 2022-04-14 17:45:01 +02:00
Ell
f445f59078 Added consuming variants of IsPressed methods to InputHandler 2022-04-11 10:33:41 +02:00
Ell
902391d278 Fixed auto-nav tooltip displaying on the selected element even when not in auto-nav mode 2022-04-09 22:00:21 +02:00
Ell
62d2b28ec0 bump version 2022-04-08 14:50:14 +02:00
225 changed files with 4910 additions and 2353 deletions

113
.editorconfig Normal file
View file

@ -0,0 +1,113 @@
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
# Microsoft .NET properties
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_object_initializers = false
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = false
csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion
csharp_space_after_cast = true
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_naming_rule.private_constants_rule.severity = warning
dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style
dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols
dotnet_naming_rule.private_instance_fields_rule.severity = warning
dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style
dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols
dotnet_naming_rule.private_static_fields_rule.severity = warning
dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style
dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols
dotnet_naming_rule.private_static_readonly_rule.severity = warning
dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style
dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private
dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field
dotnet_naming_symbols.private_constants_symbols.required_modifiers = const
dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private
dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field
dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private
dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field
dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static, readonly
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = true:suggestion
dotnet_style_qualification_for_field = true:suggestion
dotnet_style_qualification_for_method = true:suggestion
dotnet_style_qualification_for_property = true:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# ReSharper properties
resharper_blank_lines_after_block_statements = 0
resharper_blank_lines_around_auto_property = 0
resharper_blank_lines_around_local_method = 0
resharper_blank_lines_around_property = 0
resharper_blank_lines_inside_type = 1
resharper_braces_for_dowhile = required_for_multiline
resharper_braces_for_fixed = required_for_multiline
resharper_braces_for_for = required_for_multiline
resharper_braces_for_foreach = required_for_multiline
resharper_braces_for_ifelse = required_for_multiline
resharper_braces_for_lock = required_for_multiline
resharper_braces_for_using = required_for_multiline
resharper_braces_for_while = required_for_multiline
resharper_braces_redundant = false
resharper_csharp_blank_lines_around_field = 0
resharper_csharp_blank_lines_around_single_line_invocable = 1
resharper_csharp_empty_block_style = together
resharper_csharp_keep_blank_lines_in_code = 1
resharper_csharp_keep_blank_lines_in_declarations = 1
resharper_csharp_max_line_length = unset
resharper_csharp_stick_comment = false
resharper_csharp_wrap_before_ternary_opsigns = false
resharper_csharp_wrap_multiple_declaration_style = wrap_if_long
resharper_csharp_wrap_multiple_type_parameter_constraints_style = wrap_if_long
resharper_csharp_wrap_ternary_expr_style = wrap_if_long
resharper_force_attribute_style = join
resharper_indent_nested_fixed_stmt = true
resharper_indent_nested_foreach_stmt = true
resharper_indent_nested_for_stmt = true
resharper_indent_nested_lock_stmt = true
resharper_indent_nested_usings_stmt = true
resharper_indent_nested_while_stmt = true
resharper_indent_preprocessor_if = usual_indent
resharper_indent_preprocessor_other = usual_indent
resharper_keep_existing_declaration_parens_arrangement = false
resharper_keep_existing_embedded_arrangement = false
resharper_keep_existing_expr_member_arrangement = false
resharper_keep_existing_property_patterns_arrangement = false
resharper_keep_existing_switch_expression_arrangement = false
resharper_parentheses_redundancy_style = remove
resharper_place_attribute_on_same_line = false
resharper_place_expr_accessor_on_single_line = true
resharper_place_expr_method_on_single_line = true
resharper_place_expr_property_on_single_line = true
resharper_place_simple_accessor_on_single_line = false
resharper_place_simple_anonymousmethod_on_single_line = false
resharper_place_simple_embedded_statement_on_same_line = false
resharper_space_around_arrow_op = true
resharper_space_within_empty_braces = false
resharper_space_within_single_line_array_initializer_braces = false
resharper_static_members_qualify_members = field, property, event, method
resharper_wrap_for_stmt_header_style = wrap_if_long
resharper_wrap_object_and_collection_initializer_style = wrap_if_long
resharper_xmldoc_attribute_indent = align_by_first_attribute
resharper_xmldoc_attribute_style = on_single_line
resharper_xmldoc_pi_attribute_style = on_single_line

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "FNA"]
path = FNA
url = https://github.com/FNA-XNA/FNA
[submodule "FontStashSharp"]
path = FontStashSharp
url = https://github.com/FontStashSharp/FontStashSharp

View file

@ -2,11 +2,98 @@
MLEM tries to adhere to [semantic versioning](https://semver.org/). Breaking changes are written in **bold**.
Jump to version:
- [6.0.0](#600)
- [5.3.0](#530)
- [5.2.0](#520)
- [5.1.0](#510)
- [5.0.0](#500)
## 6.0.0
### MLEM
Additions
- Added consuming variants of IsPressed methods to InputHandler and Keybind
- Added SpriteBatchContext struct and extensions
- Added InputHandler.InvertPressBehavior
- Added ReverseInput, ReverseOutput and AndThen to Easings
- Added an Enum constructor to GenericInput
- Added RandomPitchModifier and GetRandomPitch to SoundEffectInfo
- Added TextInput class, which is an isolated version of MLEM.Ui's TextField logic
- Added MLEM.FNA, which is fully compatible with FNA
- Added TryGetUpTime, GetUpTime, TryGetTimeSincePress and GetTimeSincePress to InputHandler
Improvements
- Allow comparing Keybind and Combination based on the amount of modifiers they have
- Allow using multiple textures in a StaticSpriteBatch
- Added GenericInput support for Buttons.None
- Improved the way terminating formatting codes work by introducing SimpleEndCode
- Allow RandomExtensions to operate on any ICollection
Removals
- Marked AStar.InfiniteCost as obsolete
### MLEM.Ui
Additions
- Added Element.AutoNavGroup which allows forming groups for auto-navigation
- Added UiMarkdownParser
- Added MLEM.Ui.FNA, which is fully compatible with FNA
Improvements
- Ensure that Element.IsMouseOver is always accurate by making it an auto-property
- Started using SpriteBatchContext for Draw and DrawTransformed methods
- Make use of the new consuming variants in InputHandler and Keybind to consume UiControls inputs
- Allow Tooltip to manage more than one paragraph and make it easier to add new lines
- Allow adding dropdown elements at a specified index
- Turned Tooltip paragraph styling into style properties
- Improved ElementHelper.AddTooltip overloads
- Don't query a paragraph's text callback in the constructor
- Allow manually hiding a paragraph without its text overriding the hidden state
- Added optional isKeybindAllowed parameter to KeybindButton
- Allow manually setting a RootElement as CanBeActive
Fixes
- Fixed auto-nav tooltip displaying on the selected element even when not in auto-nav mode
- Fixed radio buttons not unchecking all other radio buttons with the same root element
- Fixed elements not being deselected when removed through RemoveChild
- Fixed elements sometimes staying hidden when they shouldn't in scrolling panels
- Fixed elements' OnDeselected events not being raised when CanBeSelected is set to false while selected
- Fixed gamepad auto-nav angle being incorrect for some elements
Removals
- Marked old Draw and DrawTransformed overloads as obsolete in favor of SpriteBatchContext ones
- Marked Tooltip.Paragraph as obsolete in favor of new Paragraphs collection
### MLEM.Extended
Additions
- Added LayerPositionF
- Added MLEM.Extended.FNA, which is fully compatible with FNA
Improvements
- Allow using a StaticSpriteBatch to render an IndividualTiledMapRenderer
### MLEM.Data
Additions
- Added the ability to add padding to RuntimeTexturePacker texture regions
- Added the ability to pack UniformTextureAtlas and DataTextureAtlas using RuntimeTexturePacker
- Added MLEM.Data.FNA, which is fully compatible with FNA
Improvements
- Premultiply textures when using RawContentManager
- Allow enumerating all region names of a DataTextureAtlas
- Cache RuntimeTexturePacker texture data while packing to improve performance
- Greatly improved RuntimeTexturePacker performance
- Allow specifying multiple names for a DataTextureAtlas region
Fixes
- Fixed SoundEffectReader incorrectly claiming it could read ogg and mp3 files
### MLEM.Startup
Additions
- Added MLEM.Startup.FNA, which is fully compatible with FNA
### MLEM.Templates
Improvements
- Updated to MonoGame 3.8.1
## 5.3.0
### MLEM
Additions
@ -287,4 +374,4 @@ Additions
Fixes
- Fixed some number parsing not using the invariant culture
- Fixed RawContentManager crashing with dynamic assemblies present
- Fixed RawContentManager crashing with dynamic assemblies present

View file

@ -0,0 +1,36 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-mgcb": {
"version": "3.8.1.263",
"commands": [
"mgcb"
]
},
"dotnet-mgcb-editor": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor"
]
},
"dotnet-mgcb-editor-linux": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-linux"
]
},
"dotnet-mgcb-editor-windows": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-windows"
]
},
"dotnet-mgcb-editor-mac": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-mac"
]
}
}
}

View file

@ -1,55 +1,55 @@
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Net;
using Android.OS;
using Android.Views;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MLEM.Extensions;
using MLEM.Misc;
using static Android.Views.SystemUiFlags;
using Uri = Android.Net.Uri;
namespace Demos.Android {
[Activity(
Label = "@string/app_name",
MainLauncher = true,
Icon = "@drawable/icon",
AlwaysRetainTaskState = true,
LaunchMode = LaunchMode.SingleInstance,
ScreenOrientation = ScreenOrientation.UserLandscape,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
)]
public class Activity1 : AndroidGameActivity {
namespace Demos.Android;
private GameImpl game;
private View view;
[Activity(
Label = "@string/app_name",
MainLauncher = true,
Icon = "@drawable/icon",
AlwaysRetainTaskState = true,
LaunchMode = LaunchMode.SingleInstance,
ScreenOrientation = ScreenOrientation.UserLandscape,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
)]
public class Activity1 : AndroidGameActivity {
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
// render under notches
if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
private GameImpl game;
private View view;
MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
this.game = new GameImpl();
// reset MlemGame width and height to use device's aspect ratio
this.game.GraphicsDeviceManager.ResetWidthAndHeight(this.game.Window);
// disable mouse handling for android to make emulator behavior more coherent
this.game.OnLoadContent += game => game.InputHandler.HandleMouse = false;
// set the game to fullscreen to cause the status bar to be hidden
this.game.GraphicsDeviceManager.IsFullScreen = true;
this.view = this.game.Services.GetService(typeof(View)) as View;
this.SetContentView(this.view);
this.game.Run();
}
public override void OnWindowFocusChanged(bool hasFocus) {
base.OnWindowFocusChanged(hasFocus);
// hide the status bar
if (hasFocus)
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility) (ImmersiveSticky | LayoutStable | LayoutHideNavigation | LayoutFullscreen | HideNavigation | Fullscreen);
}
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
// render under notches
if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
this.game = new GameImpl();
// reset MlemGame width and height to use device's aspect ratio
this.game.GraphicsDeviceManager.ResetWidthAndHeight(this.game.Window);
// disable mouse handling for android to make emulator behavior more coherent
this.game.OnLoadContent += game => game.InputHandler.HandleMouse = false;
// set the game to fullscreen to cause the status bar to be hidden
this.game.GraphicsDeviceManager.IsFullScreen = true;
this.view = this.game.Services.GetService(typeof(View)) as View;
this.SetContentView(this.view);
this.game.Run();
}
}
public override void OnWindowFocusChanged(bool hasFocus) {
base.OnWindowFocusChanged(hasFocus);
// hide the status bar
if (hasFocus) {
#pragma warning disable CS0618
// TODO this is deprecated, find out how to replace it
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility) (SystemUiFlags.ImmersiveSticky | SystemUiFlags.LayoutStable | SystemUiFlags.LayoutHideNavigation | SystemUiFlags.LayoutFullscreen | SystemUiFlags.HideNavigation | SystemUiFlags.Fullscreen);
#pragma warning restore CS0618
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.ellpeck.mlem.demos.android" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="31" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application android:label="MLEM Android Demos" />
</manifest>

View file

@ -1,104 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{410C0262-131C-4D0E-910D-D01B4F7143E0}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Demos.Android</RootNamespace>
<AssemblyName>Demos.Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidStoreUncompressedFileExtensions>.m4a</AndroidStoreUncompressedFileExtensions>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
<DefineConstants>DEBUG;TRACE;ANDROID</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
<DefineConstants>TRACE;ANDROID</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<MandroidI18n />
<AndroidPackageFormat>aab</AndroidPackageFormat>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>31</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<ApplicationId>de.ellpeck.mlem.demos.android</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="Mono.Android" />
</ItemGroup>
<ItemGroup>
<Compile Include="Activity1.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Drawable\Icon.png" />
<AndroidResource Include="Resources\Values\Strings.xml" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.263" />
<PackageReference Include="MonoGame.Framework.Android" Version="3.8.1.263" />
<ProjectReference Include="..\Demos\Demos.csproj" />
</ItemGroup>
<ItemGroup>
<MonoGameContentReference Include="..\Demos\Content\Content.mgcb" />
<None Include="..\Demos\Content\*\**" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Coroutine" Version="2.1.3" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.0.1641" />
<PackageReference Include="MonoGame.Framework.Android" Version="3.8.0.1641" />
<PackageReference Include="TextCopy" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Demos\Demos.csproj">
<Project>{1bc4682b-aa14-4937-b5c7-707e20fe88ff}</Project>
<Name>Demos</Name>
</ProjectReference>
<ProjectReference Include="..\MLEM.Startup\MLEM.Startup.csproj">
<Project>{997f4739-7bec-4621-b9ca-68deb2d74412}</Project>
<Name>MLEM.Startup</Name>
</ProjectReference>
<ProjectReference Include="..\MLEM.Ui\MLEM.Ui.csproj">
<Project>{6f00629a-8b87-4264-8896-19983285e32f}</Project>
<Name>MLEM.Ui</Name>
</ProjectReference>
<ProjectReference Include="..\MLEM\MLEM.csproj">
<Project>{1d6ab762-43c4-4775-8924-707c7ec3f142}</Project>
<Name>MLEM</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.ellpeck.mlem.demos.android" android:installLocation="auto" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
<application android:label="MLEM Android Demos" android:resizeableActivity="true" />
</manifest>

View file

@ -1,15 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MLEM Android Demos")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MLEM Android Demos")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

View file

@ -1,76 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Demos.Android.Resource", IsApplication=true)]
namespace Demos.Android
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F010000
public const int Icon = 2130771968;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class String
{
// aapt resource value: 0x7F020000
public const int app_name = 2130837504;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
}
}
#pragma warning restore 1591

View file

@ -0,0 +1,36 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-mgcb": {
"version": "3.8.1.263",
"commands": [
"mgcb"
]
},
"dotnet-mgcb-editor": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor"
]
},
"dotnet-mgcb-editor-linux": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-linux"
]
},
"dotnet-mgcb-editor-windows": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-windows"
]
},
"dotnet-mgcb-editor-mac": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-mac"
]
}
}
}

View file

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ApplicationIcon>Icon.ico</ApplicationIcon>
<AssemblyName>MLEM Desktop Demos</AssemblyName>
<RootNamespace>Demos.DesktopGL</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
<!-- We still use the MG content builder for ease of compatibility between the MG and FNA demo projects -->
<MonoGamePlatform>DesktopGL</MonoGamePlatform>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Demos\Demos.FNA.csproj" />
<ProjectReference Include="..\MLEM.Startup\MLEM.Startup.FNA.csproj" />
<ProjectReference Include="..\MLEM.Ui\MLEM.Ui.FNA.csproj" />
<ProjectReference Include="..\MLEM\MLEM.FNA.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.0.1641" />
<ProjectReference Include="..\FNA\FNA.Core.csproj" />
</ItemGroup>
<ItemGroup>
<MonoGameContentReference Include="..\Demos\Content\Content.mgcb" />
<Content Include="..\Demos\Content\*\**" />
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Icon.bmp" />
<Content Include="FnaNative/**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>%(Filename)%(Extension)</Link>
</Content>
</ItemGroup>
</Project>

View file

@ -2,28 +2,32 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<ApplicationIcon>Icon.ico</ApplicationIcon>
<AssemblyName>MLEM Desktop Demos</AssemblyName>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.263" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.263" />
<ProjectReference Include="..\Demos\Demos.csproj" />
<ProjectReference Include="..\MLEM.Startup\MLEM.Startup.csproj" />
<ProjectReference Include="..\MLEM.Ui\MLEM.Ui.csproj" />
<ProjectReference Include="..\MLEM\MLEM.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.0.1641" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641" />
</ItemGroup>
<ItemGroup>
<MonoGameContentReference Include="..\Demos\Content\Content.mgcb" />
<Content Include="..\Demos\Content\*\**" />
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Icon.bmp" />
</ItemGroup>
</Project>
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
<Message Text="Restoring dotnet tools" Importance="High" />
<Exec Command="dotnet tool restore" />
</Target>
</Project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,14 +1,20 @@
using Microsoft.Xna.Framework;
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MLEM.Misc;
namespace Demos.DesktopGL {
public static class Program {
public static void Main() {
#if FNA
MlemPlatform.Current = new MlemPlatform.DesktopFna(a => TextInputEXT.TextInput += a);
#else
MlemPlatform.Current = new MlemPlatform.DesktopGl<TextInputEventArgs>((w, c) => w.TextInput += c);
#endif
using var game = new GameImpl();
game.Run();
}
}
}
}

View file

@ -27,7 +27,7 @@ namespace Demos {
// using it having wrong coordinates and/or sizes
// the regions that are part of the atlas are then referenced by region coordinates rather than texture coordinates
// (as seen below)
var atlas = new UniformTextureAtlas(LoadContent<Texture2D>("Textures/Anim"), 4, 4);
var atlas = new UniformTextureAtlas(Demo.LoadContent<Texture2D>("Textures/Anim"), 4, 4);
// create the four animations by supplying the time per frame, and the four regions used
// note that you don't need to use a texture atlas for this, you can also simply supply the texture and the regions manually here
@ -90,7 +90,7 @@ namespace Demos {
public override void DoDraw(GameTime gameTime) {
this.GraphicsDevice.Clear(Color.CornflowerBlue);
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10));
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(10));
// draw the group's current region
// if not using a group, just draw the animation's CurrentRegion here
this.SpriteBatch.Draw(this.group.CurrentRegion, new Vector2(10, 10), Color.White);
@ -104,4 +104,4 @@ namespace Demos {
}
}
}
}

View file

@ -20,7 +20,7 @@ namespace Demos {
public override void LoadContent() {
base.LoadContent();
// The layout of the texture is important for auto tiling to work correctly, and is explained in the XML docs for the methods used
this.texture = LoadContent<Texture2D>("Textures/AutoTiling");
this.texture = Demo.LoadContent<Texture2D>("Textures/AutoTiling");
// in this example, a simple string array is used for layout purposes. As the AutoTiling method allows any kind of
// comparison, the actual implementation can vary (for instance, based on a more in-depth tile map)
@ -37,7 +37,7 @@ namespace Demos {
this.GraphicsDevice.Clear(Color.Black);
// drawing the auto tiles
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10));
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(10));
for (var x = 0; x < 6; x++) {
for (var y = 0; y < 5; y++) {
// don't draw non-grass tiles ( )
@ -59,12 +59,12 @@ namespace Demos {
}
// the texture region supplied to the AutoTiling method should only encompass the first filler tile's location and size
AutoTiling.DrawAutoTile(this.SpriteBatch, new Vector2(x + 1, y + 1) * TileSize, new TextureRegion(this.texture, 0, 0, TileSize, TileSize), ConnectsTo, Color.White);
AutoTiling.DrawAutoTile(this.SpriteBatch, new Vector2(x + 1, y + 1) * AutoTilingDemo.TileSize, new TextureRegion(this.texture, 0, 0, AutoTilingDemo.TileSize, AutoTilingDemo.TileSize), ConnectsTo, Color.White);
// when drawing extended auto-tiles, the same rules apply, but the source texture layout is different
var background = new TextureRegion(this.texture, 0, TileSize * 2, TileSize, TileSize);
var overlay = new TextureRegion(this.texture, background.Area.OffsetCopy(new Point(TileSize, 0)));
AutoTiling.DrawExtendedAutoTile(this.SpriteBatch, new Vector2(x + 8, y + 1) * TileSize, background, overlay, ConnectsTo, Color.White, Color.White);
var background = new TextureRegion(this.texture, 0, AutoTilingDemo.TileSize * 2, AutoTilingDemo.TileSize, AutoTilingDemo.TileSize);
var overlay = new TextureRegion(this.texture, background.Area.OffsetCopy(new Point(AutoTilingDemo.TileSize, 0)));
AutoTiling.DrawExtendedAutoTile(this.SpriteBatch, new Vector2(x + 8, y + 1) * AutoTilingDemo.TileSize, background, overlay, ConnectsTo, Color.White, Color.White);
}
}
this.SpriteBatch.End();
@ -73,4 +73,4 @@ namespace Demos {
}
}
}
}

View file

@ -13,6 +13,13 @@
#---------------------------------- Content ---------------------------------#
#begin Fonts/MonospacedFont.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:Fonts/MonospacedFont.spritefont
#begin Fonts/TestFont.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
@ -34,12 +41,8 @@
/processorParam:TextureFormat=Compressed
/build:Fonts/TestFontItalic.spritefont
#begin Fonts/MonospacedFont.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:Fonts/MonospacedFont.spritefont
#begin Markdown.md
/copy:Markdown.md
#begin Textures/Anim.png
/importer:TextureImporter

View file

@ -6,55 +6,55 @@ of the font in your game, and to change the characters which are available to dr
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>JetBrainsMono-Regular.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>JetBrainsMono-Regular.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

View file

@ -6,55 +6,55 @@ of the font in your game, and to change the characters which are available to dr
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>CAYETANO.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>CAYETANO.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

View file

@ -6,55 +6,55 @@ of the font in your game, and to change the characters which are available to dr
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>CAYTANOB</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>CAYTANOB</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

View file

@ -6,55 +6,55 @@ of the font in your game, and to change the characters which are available to dr
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>CAYETANI</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>CAYETANI</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

23
Demos/Content/Markdown.md Normal file
View file

@ -0,0 +1,23 @@
# H1
## H2
### H3
#### H4
##### H5
###### H6
Italics with *asterisks* or _underscores_.
Bold with **asterisks** or __underscores__.
Strikethrough with ~~two tildes~~.
[I'm an inline-style link](https://www.google.com)
<http://www.example.com>
![](https://raw.githubusercontent.com/Ellpeck/MLEM/main/Media/Logo.png)
Some `inline code` right here
```js
function codeBlock() {
}
```

View file

@ -32,4 +32,4 @@ namespace Demos {
}
}
}
}

21
Demos/Demos.FNA.csproj Normal file
View file

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Demos</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MLEM.Startup\MLEM.Startup.FNA.csproj" />
<ProjectReference Include="..\MLEM.Ui\MLEM.Ui.FNA.csproj" />
<ProjectReference Include="..\MLEM\MLEM.FNA.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FNA\FNA.Core.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
</Project>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
@ -11,7 +11,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641">
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.263">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

View file

@ -13,7 +13,7 @@ namespace Demos {
private static readonly FieldInfo[] EasingFields = typeof(Easings)
.GetFields(BindingFlags.Public | BindingFlags.Static).ToArray();
private static readonly Easings.Easing[] Easings = EasingFields
private static readonly Easings.Easing[] Easings = EasingsDemo.EasingFields
.Select(f => (Easings.Easing) f.GetValue(null)).ToArray();
private Group group;
private int current;
@ -27,11 +27,11 @@ namespace Demos {
this.group = new Group(Anchor.TopCenter, Vector2.One) {CanBeMoused = false};
this.group.AddChild(new Button(Anchor.AutoCenter, new Vector2(30, 10), "Next") {
OnPressed = e => {
this.current = (this.current + 1) % Easings.Length;
this.current = (this.current + 1) % EasingsDemo.Easings.Length;
this.progress = 0;
}
});
this.group.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => EasingFields[this.current].Name, true));
this.group.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => EasingsDemo.EasingFields[this.current].Name, true));
this.UiRoot.AddChild(this.group);
}
@ -43,11 +43,11 @@ namespace Demos {
this.GraphicsDevice.Clear(Color.CornflowerBlue);
base.DoDraw(time);
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp);
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null);
var view = this.GraphicsDevice.Viewport;
// graph the easing function
var graphEase = Easings[this.current].ScaleInput(0, view.Width).ScaleOutput(-view.Height / 3, view.Height / 3);
var graphEase = EasingsDemo.Easings[this.current].ScaleInput(0, view.Width).ScaleOutput(-view.Height / 3, view.Height / 3);
for (var x = 0; x < view.Width; x++) {
var area = new RectangleF(x - 2, view.Height / 2 - graphEase(x) - 2, 4, 4);
this.SpriteBatch.Draw(this.SpriteBatch.GetBlankTexture(), area, Color.Green);
@ -55,7 +55,7 @@ namespace Demos {
// draw a little dot to show what it would look like
this.progress = (this.progress + (float) time.ElapsedGameTime.TotalSeconds / 2) % 1;
var dotEase = Easings[this.current].AndReverse().ScaleOutput(0, view.Height / 4);
var dotEase = EasingsDemo.Easings[this.current].AndReverse().ScaleOutput(0, view.Height / 4);
var pos = new RectangleF(view.Width / 2 - 4, view.Height - 20 - dotEase(this.progress), 8, 8);
this.SpriteBatch.Draw(this.SpriteBatch.GetBlankTexture(), pos, Color.Red);
@ -63,4 +63,4 @@ namespace Demos {
}
}
}
}

View file

@ -9,7 +9,9 @@ using MLEM.Textures;
using MLEM.Ui;
using MLEM.Ui.Elements;
using MLEM.Ui.Style;
#if !FNA
using MonoGame.Framework.Utilities;
#endif
namespace Demos {
public class GameImpl : MlemGame {
@ -23,11 +25,12 @@ namespace Demos {
private TimeSpan secondCounter;
static GameImpl() {
Demos.Add("Ui", ("An in-depth demonstration of the MLEM.Ui package and its abilities", game => new UiDemo(game)));
Demos.Add("Easings", ("An example of MLEM's Easings class, an adaptation of easings.net", game => new EasingsDemo(game)));
Demos.Add("Pathfinding", ("An example of MLEM's A* pathfinding implementation in 2D", game => new PathfindingDemo(game)));
Demos.Add("Animation and Texture Atlas", ("An example of UniformTextureAtlases, SpriteAnimations and SpriteAnimationGroups", game => new AnimationDemo(game)));
Demos.Add("Auto Tiling", ("A demonstration of the AutoTiling class that MLEM provides", game => new AutoTilingDemo(game)));
GameImpl.Demos.Add("Ui", ("An in-depth demonstration of the MLEM.Ui package and its abilities", game => new UiDemo(game)));
GameImpl.Demos.Add("Text Formatting", ("A demonstration of MLEM's text formatting system", game => new TextFormattingDemo(game)));
GameImpl.Demos.Add("Easings", ("An example of MLEM's Easings class, an adaptation of easings.net", game => new EasingsDemo(game)));
GameImpl.Demos.Add("Pathfinding", ("An example of MLEM's A* pathfinding implementation in 2D", game => new PathfindingDemo(game)));
GameImpl.Demos.Add("Animation and Texture Atlas", ("An example of UniformTextureAtlases, SpriteAnimations and SpriteAnimationGroups", game => new AnimationDemo(game)));
GameImpl.Demos.Add("Auto Tiling", ("A demonstration of the AutoTiling class that MLEM provides", game => new AutoTilingDemo(game)));
}
public GameImpl() {
@ -45,13 +48,6 @@ namespace Demos {
}
protected override void LoadContent() {
// TODO remove with MonoGame 3.8.1 https://github.com/MonoGame/MonoGame/issues/7298
if (PlatformInfo.MonoGamePlatform == MonoGamePlatform.DesktopGL) {
this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
this.GraphicsDeviceManager.ApplyChanges();
}
base.LoadContent();
this.UiSystem.AutoScaleReferenceSize = new Point(1280, 720);
this.UiSystem.AutoScaleWithScreen = true;
@ -74,7 +70,7 @@ namespace Demos {
selection.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Select the demo you want to see below using your mouse, touch input, your keyboard or a controller. Check the demos' <l https://github.com/Ellpeck/MLEM/tree/main/Demos>source code</l> for more in-depth explanations of their functionality or the <l https://mlem.ellpeck.de/>website</l> for tutorials and API documentation."));
selection.AddChild(new VerticalSpace(5));
foreach (var demo in Demos) {
foreach (var demo in GameImpl.Demos) {
selection.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 10), demo.Key, demo.Value.Item1) {
OnPressed = e => {
selection.IsHidden = true;
@ -92,9 +88,9 @@ namespace Demos {
}
protected override UiStyle InitializeDefaultUiStyle(SpriteBatch batch) {
var tex = LoadContent<Texture2D>("Textures/Test");
var tex = MlemGame.LoadContent<Texture2D>("Textures/Test");
return new UntexturedStyle(this.SpriteBatch) {
Font = new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/TestFont")),
Font = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("Fonts/TestFont")),
TextScale = 0.1F,
PanelTexture = new NinePatch(new TextureRegion(tex, 0, 8, 24, 24), 8),
ButtonTexture = new NinePatch(new TextureRegion(tex, 24, 8, 16, 16), 4),
@ -128,4 +124,4 @@ namespace Demos {
}
}
}
}

View file

@ -35,14 +35,14 @@ namespace Demos {
// Create a cost function, which determines how expensive (or difficult) it should be to move from a given position
// to the next, adjacent position. In our case, the only restriction should be walls and out-of-bounds positions, which
// both have a cost of AStar2.InfiniteCost, meaning they are completely unwalkable.
// both have a cost of AStar2.InfiniteCost, meaning they are completely unwalkable.
// If your game contains harder-to-move-on areas like, say, a muddy pit, you can return a higher cost value for those
// locations. If you want to scale your cost function differently, you can specify a different default cost in your
// locations. If you want to scale your cost function differently, you can specify a different default cost in your
// pathfinder's constructor
float Cost(Point pos, Point nextPos) {
if (nextPos.X < 0 || nextPos.Y < 0 || nextPos.X >= 50 || nextPos.Y >= 50)
return AStar2.InfiniteCost;
return this.world[nextPos.X, nextPos.Y] ? 1 : AStar2.InfiniteCost;
return float.PositiveInfinity;
return this.world[nextPos.X, nextPos.Y] ? 1 : float.PositiveInfinity;
}
// Actually initialize the pathfinder with the cost function, as well as specify if moving diagonally between tiles should be
@ -73,7 +73,7 @@ namespace Demos {
public override void DoDraw(GameTime gameTime) {
this.GraphicsDevice.Clear(Color.White);
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(14));
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(14));
var tex = this.SpriteBatch.GetBlankTexture();
// draw the world with simple shapes
for (var x = 0; x < 50; x++) {
@ -87,9 +87,9 @@ namespace Demos {
// in a real game, you'd obviously make your characters walk along the path instead of drawing it
if (this.path != null) {
for (var i = 1; i < this.path.Count; i++) {
var (firstX, firstY) = this.path[i - 1];
var (secondX, secondY) = this.path[i];
this.SpriteBatch.Draw(tex, RectangleF.FromCorners(new Vector2(firstX + 0.25F, firstY + 0.25F), new Vector2(secondX + 0.75F, secondY + 0.75F)), Color.Blue);
var first = this.path[i - 1];
var second = this.path[i];
this.SpriteBatch.Draw(tex, RectangleF.FromCorners(new Vector2(first.X + 0.25F, first.Y + 0.25F), new Vector2(second.X + 0.75F, second.Y + 0.75F)), Color.Blue);
}
}
this.SpriteBatch.End();
@ -102,4 +102,4 @@ namespace Demos {
}
}
}
}

View file

@ -0,0 +1,81 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Font;
using MLEM.Formatting;
using MLEM.Formatting.Codes;
using MLEM.Startup;
using MLEM.Textures;
namespace Demos {
public class TextFormattingDemo : Demo {
private const string Text =
"MLEM's text formatting system allows for various <b>formatting codes</b> to be applied in the middle of a string. Here's a demonstration of some of them.\n\n" +
"You can write in <b>bold</i>, <i>italics</i>, <u>with an underline</u>, <st>strikethrough</st>, with a <s #000000 4>drop shadow</s> whose <s #ff0000 4>color</s> and <s #000000 10>offset</s> you can modify in each application of the code, or with various types of <b>combined <c Pink>formatting</c> codes</b>.\n\n" +
"You can apply <c CornflowerBlue>custom</c> <c Yellow>colors</c> to text, including all default <c Orange>MonoGame colors</c> and <c #aabb00>inline custom colors</c>.\n\n" +
"You can also use animations like <a wobbly>a wobbly one</a>, as well as create custom ones using the <a wobbly>Code class</a>.\n\n" +
"You can also display <i grass> icons in your text!\n\n" +
"Additionally, the text formatter has various methods for interacting with the text, like custom behaviors when hovering over certain parts, and more.";
private const float Scale = 0.5F;
private const float Width = 0.9F;
private TextFormatter formatter;
private TokenizedString tokenizedText;
private GenericFont font;
public TextFormattingDemo(MlemGame game) : base(game) {}
public override void LoadContent() {
this.Game.Window.ClientSizeChanged += this.OnResize;
// creating a new text formatter as well as a generic font to draw with
this.formatter = new TextFormatter();
// GenericFont and its subtypes are wrappers around various font classes, including SpriteFont, MonoGame.Extended's BitmapFont and FontStashSharp
// supplying a bold and italic version of the font here allows for the bold and italic formatting codes to be used
this.font = new GenericSpriteFont(
Demo.LoadContent<SpriteFont>("Fonts/TestFont"),
Demo.LoadContent<SpriteFont>("Fonts/TestFontBold"),
Demo.LoadContent<SpriteFont>("Fonts/TestFontItalic"));
// adding the image code used in the example to it
var testTexture = Demo.LoadContent<Texture2D>("Textures/Test");
this.formatter.AddImage("grass", new TextureRegion(testTexture, 0, 0, 8, 8));
// tokenizing our text and splitting it to fit the screen
// we specify our text alignment here too, so that all data is cached correctly for display
this.tokenizedText = this.formatter.Tokenize(this.font, TextFormattingDemo.Text, TextAlignment.Center);
this.tokenizedText.Split(this.font, this.GraphicsDevice.Viewport.Width * TextFormattingDemo.Width, TextFormattingDemo.Scale, TextAlignment.Center);
}
public override void DoDraw(GameTime time) {
this.GraphicsDevice.Clear(Color.DarkSlateGray);
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null);
// we draw the tokenized text in the center of the screen
// since the text is already center-aligned, we only need to align it on the y axis here
var size = this.tokenizedText.Measure(this.font) * TextFormattingDemo.Scale;
var pos = new Vector2(this.GraphicsDevice.Viewport.Width / 2, (this.GraphicsDevice.Viewport.Height - size.Y) / 2);
this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, TextFormattingDemo.Scale, 0);
this.SpriteBatch.End();
}
public override void Update(GameTime time) {
// update our tokenized string to animate the animation codes
this.tokenizedText.Update(time);
}
public override void Clear() {
base.Clear();
this.Game.Window.ClientSizeChanged -= this.OnResize;
}
private void OnResize(object sender, EventArgs e) {
// re-split our text if the window resizes, since it depends on the window size
// this doesn't require re-tokenization of the text, since TokenizedString also stores the un-split version
this.tokenizedText.Split(this.font, this.GraphicsDevice.Viewport.Width * TextFormattingDemo.Width, TextFormattingDemo.Scale, TextAlignment.Center);
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Coroutine;
using Microsoft.Xna.Framework;
@ -7,13 +8,13 @@ using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MLEM.Font;
using MLEM.Formatting;
using MLEM.Formatting.Codes;
using MLEM.Input;
using MLEM.Misc;
using MLEM.Startup;
using MLEM.Textures;
using MLEM.Ui;
using MLEM.Ui.Elements;
using MLEM.Ui.Parsers;
using MLEM.Ui.Style;
namespace Demos {
@ -26,7 +27,7 @@ namespace Demos {
public UiDemo(MlemGame game) : base(game) {}
public override void LoadContent() {
this.testTexture = LoadContent<Texture2D>("Textures/Test");
this.testTexture = Demo.LoadContent<Texture2D>("Textures/Test");
this.testPatch = new NinePatch(new TextureRegion(this.testTexture, 0, 8, 24, 24), 8);
base.LoadContent();
@ -36,7 +37,10 @@ namespace Demos {
// when using a SpriteFont, use GenericSpriteFont. When using a MonoGame.Extended BitmapFont, use GenericBitmapFont.
// Wrapping fonts like this allows for both types to be usable within MLEM.Ui easily
// Supplying a bold and an italic version is optional
Font = new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/TestFont"), LoadContent<SpriteFont>("Fonts/TestFontBold"), LoadContent<SpriteFont>("Fonts/TestFontItalic")),
Font = new GenericSpriteFont(
Demo.LoadContent<SpriteFont>("Fonts/TestFont"),
Demo.LoadContent<SpriteFont>("Fonts/TestFontBold"),
Demo.LoadContent<SpriteFont>("Fonts/TestFontItalic")),
TextScale = 0.1F,
PanelTexture = this.testPatch,
ButtonTexture = new NinePatch(new TextureRegion(this.testTexture, 24, 8, 16, 16), 4),
@ -47,7 +51,7 @@ namespace Demos {
CheckboxCheckmark = new TextureRegion(this.testTexture, 24, 0, 8, 8),
RadioTexture = new NinePatch(new TextureRegion(this.testTexture, 16, 0, 8, 8), 3),
RadioCheckmark = new TextureRegion(this.testTexture, 32, 0, 8, 8),
AdditionalFonts = {{"Monospaced", new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/MonospacedFont"))}},
AdditionalFonts = {{"Monospaced", new GenericSpriteFont(Demo.LoadContent<SpriteFont>("Fonts/MonospacedFont"))}},
LinkColor = Color.CornflowerBlue
};
var untexturedStyle = new UntexturedStyle(this.SpriteBatch) {
@ -69,7 +73,7 @@ namespace Demos {
// add the root to the demos' ui
this.UiRoot.AddChild(this.root);
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of the MLEM Library for Extending MonoGame."));
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of the MLEM Library for Extending MonoGame and FNA."));
var image = this.root.AddChild(new Image(Anchor.AutoCenter, new Vector2(50, 50), new TextureRegion(this.testTexture, 0, 0, 8, 8)) {IsHidden = true, Padding = new Padding(3)});
// Setting the x or y coordinate of the size to 1 or a lower number causes the width or height to be a percentage of the parent's width or height
// (for example, setting the size's x to 0.75 would make the element's width be 0.75*parentWidth)
@ -88,12 +92,7 @@ namespace Demos {
this.root.AddChild(new VerticalSpace(3));
// a paragraph with formatting codes. To see them all or to add more, check the TextFormatting class
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Paragraphs can also contain <c Blue>formatting codes</c>, including colors and <i>text styles</i>. The names of all <c Orange>MonoGame Colors</c> can be used, as well as the codes <i>Italic</i>, <b>Bold</b>, <s>Drop Shadow'd</s> and <s><c Pink>mixed formatting</s></c>. You can also add additional fonts for things like\n<f Monospaced>void Code() {\n // Code\n}</f>\n<i>Even <c #ff611f82>inline custom colors</c> work!</i>"));
// adding some custom image formatting codes
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Additionally, you can create custom formatting codes that contain <i Grass> images and more!"));
this.UiSystem.TextFormatter.AddImage("Grass", image.Texture);
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Defining text animations as formatting codes is also possible, including <a wobbly>wobbly text</a> at <a wobbly 8 0.25>different intensities</a>. Of course, more animations can be added though."));
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Paragraphs can also contain <c Blue>formatting codes</c>, including colors and <i>text styles</i>. For more info, check out the <b>text formatting demo</b>!"));
this.root.AddChild(new VerticalSpace(3));
this.root.AddChild(new Paragraph(Anchor.AutoCenter, 1, "Multiline text input:", true));
@ -156,7 +155,7 @@ namespace Demos {
// Check the WobbleButton method for an explanation of how this button works
this.root.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, 10), "Wobble Me", "This button wobbles around visually when clicked, but this doesn't affect its actual size and positioning") {
OnPressed = element => CoroutineHandler.Start(WobbleButton(element)),
OnPressed = element => CoroutineHandler.Start(UiDemo.WobbleButton(element)),
PositionOffset = new Vector2(0, 1)
});
// Another button that shows animations!
@ -189,13 +188,13 @@ namespace Demos {
this.root.AddChild(new VerticalSpace(3));
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Progress bars!"));
var bar1 = this.root.AddChild(new ProgressBar(Anchor.AutoLeft, new Vector2(1, 8), Direction2.Right, 10) {PositionOffset = new Vector2(0, 1)});
CoroutineHandler.Start(WobbleProgressBar(bar1));
CoroutineHandler.Start(UiDemo.WobbleProgressBar(bar1));
var bar2 = this.root.AddChild(new ProgressBar(Anchor.AutoLeft, new Vector2(1, 8), Direction2.Left, 10) {PositionOffset = new Vector2(0, 1)});
CoroutineHandler.Start(WobbleProgressBar(bar2));
CoroutineHandler.Start(UiDemo.WobbleProgressBar(bar2));
var bar3 = this.root.AddChild(new ProgressBar(Anchor.AutoLeft, new Vector2(8, 30), Direction2.Down, 10) {PositionOffset = new Vector2(0, 1)});
CoroutineHandler.Start(WobbleProgressBar(bar3));
CoroutineHandler.Start(UiDemo.WobbleProgressBar(bar3));
var bar4 = this.root.AddChild(new ProgressBar(Anchor.AutoInline, new Vector2(8, 30), Direction2.Up, 10) {PositionOffset = new Vector2(1, 0)});
CoroutineHandler.Start(WobbleProgressBar(bar4));
CoroutineHandler.Start(UiDemo.WobbleProgressBar(bar4));
this.root.AddChild(new VerticalSpace(3));
var dropdown = this.root.AddChild(new Dropdown(Anchor.AutoLeft, new Vector2(1, 10), "Dropdown Menu"));
@ -223,6 +222,13 @@ namespace Demos {
alignPar.Alignment = alignment;
};
this.root.AddChild(new VerticalSpace(3));
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "MLEM.Ui also contains a simple Markdown parser, which can be useful for displaying things like changelogs in your game."));
this.root.AddChild(new VerticalSpace(3));
var parser = new UiMarkdownParser {GraphicsDevice = this.GraphicsDevice};
using (var reader = new StreamReader(TitleContainer.OpenStream("Content/Markdown.md")))
parser.ParseInto(reader.ReadToEnd(), this.root);
this.root.AddChild(new VerticalSpace(3));
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "The code for this demo contains some examples for how to query element data. This is the output of that:"));
@ -290,4 +296,4 @@ namespace Demos {
}
}
}
}

View file

@ -1,6 +1,6 @@
# MLEM.Ui
**MLEM.Ui** is a Ui framework for MonoGame that features elements with automatic positioning and sizing. It contains various ready-made element types like buttons, paragraphs, text fields and more, along with the ability to easily create custom controls. It supports **mouse**, **keyboard**, **gamepad** and **touch** input with little to no additional setup work required.
**MLEM.Ui** is a Ui framework for MonoGame and FNA that features elements with automatic positioning and sizing. It contains various ready-made element types like buttons, paragraphs, text fields and more, along with the ability to easily create custom controls. It supports **mouse**, **keyboard**, **gamepad** and **touch** input with little to no additional setup work required.
To see some of what MLEM.Ui can do, you can check out [the demo](https://github.com/Ellpeck/MLEM/blob/main/Demos/UiDemo.cs) as well.
@ -11,7 +11,7 @@ public UiSystem UiSystem;
protected override void LoadContent() {
// Load your other content here
// Initialize the Ui system
this.UiSystem = new UiSystem(this.Window, this.GraphicsDevice, new UntexturedStyle(this.SpriteBatch));
}
@ -24,26 +24,30 @@ protected override void Update(GameTime gameTime) {
protected override void Draw(GameTime gameTime) {
this.GraphicsDevice.Clear(Color.CornflowerBlue);
// Do your regular game drawing here
// Call Draw at the end to draw the Ui on top of your game
this.UiSystem.Draw(gameTime, this.SpriteBatch);
}
```
### Text Input
On desktop devices, MonoGame provides the `Window.TextInput` event that gets called automatically with the correct characters for the keys that you're pressing, even for non-American keyboards. However, this function doesn't exist on other devices. Similarly, MonoGame provides the `KeyboardInput` class for showing an on-screen keyboard on mobile devices and consoles, but not on desktop.
On desktop devices, MonoGame provides the `Window.TextInput` event that gets called automatically with the correct characters for the keys that you're pressing, even for non-American keyboards. However, this function doesn't exist on other devices. Similarly, MonoGame provides the `KeyboardInput` class for showing an on-screen keyboard on mobile devices and consoles, but not on desktop.
To make MLEM compatible with all devices without publishing a separate version for each MonoGame platform, you have to set up the `MlemPlatform` class based on the system you're using MLEM.Ui with. This has to be done *before* initializing your `UiSystem`.
DesktopGL:
DesktopGL and WindowsDX using MonoGame:
```cs
MlemPlatform.Current = new MlemPlatform.DesktopGl<TextInputEventArgs>((w, c) => w.TextInput += c);
```
Desktop using FNA:
```cs
MlemPlatform.Current = new MlemPlatform.DesktopFna(a => TextInputEXT.TextInput += a);
```
Mobile devices and consoles:
```cs
MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
```
If you're not using text input, you can just set the platform to a stub one like so:
If you're not using text input, you can leave the platform uninitialized or just set it to a stub one like so:
```cs
MlemPlatform.Current = new MlemPlatform.None();
```
@ -89,4 +93,4 @@ this.UiSystem.Add("InfoBox", box);
Note that, when setting the width and height of any element, there are some things to note:
- Each element has a `SetWidthBasedOnChildren` and a `SetHeightBasedOnChildren` property, which allow them to change their size automatically based on their content
- When specifying a width or height *lower than or equal to 1*, it is seen as a percentage based on the parent's size instead. For example, a paragraph with a width of `0.5F` inside of a panel width a width of `200` will be `100` units wide.
- When specifying a width *lower than 0*, it is seen as a percentage based on the element's height, and vice versa. For example, a panel with a width of `200` and a height of `-2` will be `400` units tall.
- When specifying a width *lower than 0*, it is seen as a percentage based on the element's height, and vice versa. For example, a panel with a width of `200` and a height of `-2` will be `400` units tall.

View file

@ -1,27 +1,27 @@
![The MLEM logo](https://raw.githubusercontent.com/Ellpeck/MLEM/release/Media/Banner.png)
**MLEM Library for Extending MonoGame** is an addition to the game framework [MonoGame](https://www.monogame.net/) that provides extension methods, quality of life improvements and additional features like a ui system and easy input handling.
**MLEM Library for Extending MonoGame and FNA** is a set of multipurpose libraries for the game frameworks [MonoGame](https://www.monogame.net/) and [FNA](https://fna-xna.github.io/) that provides abstractions, quality of life improvements and additional features like an extensive ui system and easy input handling.
# What next?
- Get it on [NuGet](https://www.nuget.org/packages?q=mlem)
- Get prerelease builds on [BaGet](https://nuget.ellpeck.de)
- Get prerelease builds on [BaGet](https://nuget.ellpeck.de/?q=mlem)
- See the source code on [GitHub](https://github.com/Ellpeck/MLEM)
- See tutorials and API documentation on [the website](https://mlem.ellpeck.de/)
- Check out [the demos](https://github.com/Ellpeck/MLEM/tree/release/Demos) on [Desktop](https://github.com/Ellpeck/MLEM/tree/release/Demos.DesktopGL) or [Android](https://github.com/Ellpeck/MLEM/tree/release/Demos.Android)
- See [the changelog](https://mlem.ellpeck.de/CHANGELOG.html) for information on updates
# Packages
- **MLEM** is the base package, which provides extension methods and additional features for MonoGame
- **MLEM.Ui** features a mouse, keyboard, gamepad and touch ready Ui system that features automatic anchoring, sizing and several ready-to-use element types
- **MLEM.Extended** ties in with MonoGame.Extended and other MonoGame libraries
- **MLEM.Data** provides simple loading and processing of textures and data
- **MLEM** is the base package, which provides various small addons and abstractions for MonoGame and FNA, including a text formatting system and simple input handling
- **MLEM.Ui** provides a mouse, keyboard, gamepad and touch ready Ui system that features automatic anchoring, sizing and several ready-to-use element types
- **MLEM.Extended** ties in with MonoGame.Extended and other MonoGame and FNA libraries
- **MLEM.Data** provides simple loading and processing of textures and other data, including the ability to load non-XNB content files easily
- **MLEM.Startup** combines MLEM with some other useful libraries into a quick Game startup class
- **MLEM.Templates** contains cross-platform project templates
# Made with MLEM
- [A Breath of Spring Air](https://ellpeck.itch.io/a-breath-of-spring-air), a short platformer ([Source](https://git.ellpeck.de/Ellpeck/GreatSpringGameJam))
- [Don't Wake Up](https://ellpeck.itch.io/dont-wake-up), a short puzzle game ([Source](https://github.com/Ellpeck/DontLetGo))
- [Pong clone](https://github.com/luanfagu/pong), a very simple pong clone ([Source](https://github.com/luanfagu/pong))
- [Pong Clone](https://github.com/luanfagu/pong), a very simple pong clone ([Source](https://github.com/luanfagu/pong))
- [Tiny Life](https://tinylifegame.com), an isometric life simulation game ([Modding API](https://github.com/Ellpeck/TinyLifeExampleMod))
If you created a game with the help of MLEM, you can get it added to this list by submitting it on the [issue tracker](https://github.com/Ellpeck/MLEM/issues). If its source is public, other people will be able to use your project as an example, too!
@ -38,9 +38,9 @@ MLEM's [text formatting system](https://mlem.ellpeck.de/articles/text_formatting
![An image showing text with various colors and other formatting](https://raw.githubusercontent.com/Ellpeck/MLEM/release/Media/Formatting.png)
# Friends of MLEM
There are several other libraries and tools that work well in combination with MonoGame and MLEM. Here are some of them:
There are several other libraries and tools that work well in combination with MonoGame, FNA and MLEM. Here are some of them:
- [Contentless](https://github.com/Ellpeck/Contentless), a tool that removes the need to add assets to the MonoGame Content Pipeline manually
- [GameBundle](https://github.com/Ellpeck/GameBundle), a tool that packages MonoGame and other .NET Core applications into several distributable formats
- [GameBundle](https://github.com/Ellpeck/GameBundle), a tool that packages MonoGame and other .NET applications into several distributable formats
- [MonoGame.Extended](https://github.com/craftworkgames/MonoGame.Extended), a package that also provides several additional features for MonoGame
- [Coroutine](https://github.com/Ellpeck/Coroutine), a package that implements Unity-style coroutines for any project
- [Illumilib](https://github.com/Ellpeck/Illumilib), a simple keyboard and mouse lighting library with support for Razer, Logitech and Corsair devices

1
FNA Submodule

@ -0,0 +1 @@
Subproject commit 102990f514f1e5bfac07d33f7c33e2e712946da4

1
FontStashSharp Submodule

@ -0,0 +1 @@
Subproject commit f0774130cad6cec0b790a58bc7c811a186443fb3

5
Jenkinsfile vendored
View file

@ -1,6 +1,11 @@
pipeline {
agent any
stages {
stage('Submodules') {
steps {
sh 'git submodule update --init --recursive --force'
}
}
stage('Cake Build') {
steps {
sh 'dotnet tool restore'

View file

@ -23,4 +23,4 @@ namespace MLEM.Data.Content {
}
}
}
}

View file

@ -7,19 +7,22 @@ using Microsoft.Xna.Framework.Graphics;
namespace MLEM.Data.Content {
/// <summary>
/// Represents a version of <see cref="ContentManager"/> that doesn't load content binary <c>xnb</c> files, but rather as their regular formats.
/// Represents a version of <see cref="ContentManager"/> that doesn't load content binary <c>xnb</c> files, but rather as their regular formats.
/// </summary>
public class RawContentManager : ContentManager, IGameComponent {
private static List<RawContentReader> readers;
private readonly List<IDisposable> disposableAssets = new List<IDisposable>();
/// <summary>
/// The graphics device that this content manager uses
/// </summary>
public readonly GraphicsDevice GraphicsDevice;
private readonly List<IDisposable> disposableAssets = new List<IDisposable>();
#if FNA
private Dictionary<string, object> LoadedAssets { get; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
#endif
/// <summary>
/// Creates a new content manager with an optionally specified root directory.
/// </summary>
@ -50,15 +53,19 @@ namespace MLEM.Data.Content {
/// <param name="originalAssetName">The original name of the asset.</param>
/// <param name="currentAsset">The current asset instance.</param>
/// <typeparam name="T">The asset's type.</typeparam>
protected override void ReloadAsset<T>(string originalAssetName, T currentAsset) {
protected
#if !FNA
override
#endif
void ReloadAsset<T>(string originalAssetName, T currentAsset) {
this.Read(originalAssetName, currentAsset);
}
private T Read<T>(string assetName, T existing) {
var triedFiles = new List<string>();
if (readers == null)
readers = CollectContentReaders();
foreach (var reader in readers) {
if (RawContentManager.readers == null)
RawContentManager.readers = RawContentManager.CollectContentReaders();
foreach (var reader in RawContentManager.readers) {
if (!reader.CanRead(typeof(T)))
continue;
foreach (var ext in reader.GetFileExtensions()) {
@ -125,4 +132,4 @@ namespace MLEM.Data.Content {
}
}
}
}

View file

@ -58,4 +58,4 @@ namespace MLEM.Data.Content {
protected abstract T Read(RawContentManager manager, string assetPath, Stream stream, T existing);
}
}
}

View file

@ -17,4 +17,4 @@ namespace MLEM.Data.Content {
}
}
}
}

View file

@ -12,8 +12,8 @@ namespace MLEM.Data.Content {
/// <inheritdoc />
public override string[] GetFileExtensions() {
return new[] {"ogg", "wav", "mp3"};
return new[] {"wav"};
}
}
}
}

View file

@ -1,5 +1,7 @@
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
namespace MLEM.Data.Content {
/// <inheritdoc />
@ -7,11 +9,26 @@ namespace MLEM.Data.Content {
/// <inheritdoc />
protected override Texture2D Read(RawContentManager manager, string assetPath, Stream stream, Texture2D existing) {
#if !FNA
if (existing != null) {
existing.Reload(stream);
return existing;
} else {
return Texture2D.FromStream(manager.GraphicsDevice, stream);
} else
#endif
{
// premultiply the texture's color to be in line with the pipeline's texture reader
using (var texture = Texture2D.FromStream(manager.GraphicsDevice, stream)) {
var ret = new Texture2D(manager.GraphicsDevice, texture.Width, texture.Height);
using (var textureData = texture.GetTextureData()) {
using (var retData = ret.GetTextureData()) {
for (var x = 0; x < ret.Width; x++) {
for (var y = 0; y < ret.Height; y++)
retData[x, y] = Color.FromNonPremultiplied(textureData[x, y].ToVector4());
}
}
}
return ret;
}
}
}
@ -21,4 +38,4 @@ namespace MLEM.Data.Content {
}
}
}
}

View file

@ -22,4 +22,4 @@ namespace MLEM.Data.Content {
}
}
}
}

View file

@ -21,7 +21,7 @@ namespace MLEM.Data {
/// <param name="content">The content manager to add the json serializer to</param>
/// <param name="serializer">The json serializer to add</param>
public static void SetJsonSerializer(this ContentManager content, JsonSerializer serializer) {
Serializers[content] = serializer;
ContentExtensions.Serializers[content] = serializer;
}
/// <summary>
@ -31,7 +31,7 @@ namespace MLEM.Data {
/// <param name="content">The content manager whose serializer to get</param>
/// <returns>The content manager's serializer</returns>
public static JsonSerializer GetJsonSerializer(this ContentManager content) {
if (!Serializers.TryGetValue(content, out var serializer)) {
if (!ContentExtensions.Serializers.TryGetValue(content, out var serializer)) {
serializer = JsonConverters.AddAll(new JsonSerializer());
content.SetJsonSerializer(serializer);
}
@ -44,7 +44,7 @@ namespace MLEM.Data {
/// <param name="content">The content manager to add the converter to</param>
/// <param name="converter">The converter to add</param>
public static void AddJsonConverter(this ContentManager content, JsonConverter converter) {
var serializer = GetJsonSerializer(content);
var serializer = content.GetJsonSerializer();
serializer.Converters.Add(converter);
}
@ -60,7 +60,7 @@ namespace MLEM.Data {
public static T LoadJson<T>(this ContentManager content, string name, string[] extensions = null, JsonSerializer serializer = null) {
var triedFiles = new List<string>();
var serializerToUse = serializer ?? content.GetJsonSerializer();
foreach (var extension in extensions ?? JsonExtensions) {
foreach (var extension in extensions ?? ContentExtensions.JsonExtensions) {
var file = Path.Combine(content.RootDirectory, name + extension);
triedFiles.Add(file);
try {
@ -74,4 +74,4 @@ namespace MLEM.Data {
}
}
}
}

View file

@ -23,8 +23,8 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the object to copy</typeparam>
/// <returns>A shallow copy of the object</returns>
[Obsolete("CopyExtensions has major flaws and insufficient speed compared to other libraries specifically designed for copying objects.")]
public static T Copy<T>(this T obj, BindingFlags flags = DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
var copy = (T) Construct(typeof(T), flags);
public static T Copy<T>(this T obj, BindingFlags flags = CopyExtensions.DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
var copy = (T) CopyExtensions.Construct(typeof(T), flags);
obj.CopyInto(copy, flags, fieldInclusion);
return copy;
}
@ -39,8 +39,8 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the object to copy</typeparam>
/// <returns>A deep copy of the object</returns>
[Obsolete("CopyExtensions has major flaws and insufficient speed compared to other libraries specifically designed for copying objects.")]
public static T DeepCopy<T>(this T obj, BindingFlags flags = DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
var copy = (T) Construct(typeof(T), flags);
public static T DeepCopy<T>(this T obj, BindingFlags flags = CopyExtensions.DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
var copy = (T) CopyExtensions.Construct(typeof(T), flags);
obj.DeepCopyInto(copy, flags, fieldInclusion);
return copy;
}
@ -54,7 +54,7 @@ namespace MLEM.Data {
/// <param name="fieldInclusion">A predicate that determines whether or not the given field should be copied. If null, all fields will be copied.</param>
/// <typeparam name="T">The type of the object to copy</typeparam>
[Obsolete("CopyExtensions has major flaws and insufficient speed compared to other libraries specifically designed for copying objects.")]
public static void CopyInto<T>(this T obj, T otherObj, BindingFlags flags = DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
public static void CopyInto<T>(this T obj, T otherObj, BindingFlags flags = CopyExtensions.DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
foreach (var field in typeof(T).GetFields(flags)) {
if (fieldInclusion == null || fieldInclusion(field))
field.SetValue(otherObj, field.GetValue(obj));
@ -71,7 +71,7 @@ namespace MLEM.Data {
/// <param name="fieldInclusion">A predicate that determines whether or not the given field should be copied. If null, all fields will be copied.</param>
/// <typeparam name="T">The type of the object to copy</typeparam>
[Obsolete("CopyExtensions has major flaws and insufficient speed compared to other libraries specifically designed for copying objects.")]
public static void DeepCopyInto<T>(this T obj, T otherObj, BindingFlags flags = DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
public static void DeepCopyInto<T>(this T obj, T otherObj, BindingFlags flags = CopyExtensions.DefaultFlags, Predicate<FieldInfo> fieldInclusion = null) {
foreach (var field in obj.GetType().GetFields(flags)) {
if (fieldInclusion != null && !fieldInclusion(field))
continue;
@ -83,7 +83,7 @@ namespace MLEM.Data {
var otherVal = field.GetValue(otherObj);
// if the object we want to copy into doesn't have a value yet, we create one
if (otherVal == null) {
otherVal = Construct(field.FieldType, flags);
otherVal = CopyExtensions.Construct(field.FieldType, flags);
field.SetValue(otherObj, otherVal);
}
val.DeepCopyInto(otherVal, flags);
@ -92,7 +92,7 @@ namespace MLEM.Data {
}
private static object Construct(Type t, BindingFlags flags) {
if (!ConstructorCache.TryGetValue(t, out var constructor)) {
if (!CopyExtensions.ConstructorCache.TryGetValue(t, out var constructor)) {
var constructors = t.GetConstructors(flags);
// find a contructor with the correct attribute
constructor = constructors.FirstOrDefault(c => c.GetCustomAttribute<CopyConstructorAttribute>() != null);
@ -104,7 +104,7 @@ namespace MLEM.Data {
constructor = constructors.FirstOrDefault();
if (constructor == null)
throw new NullReferenceException($"Type {t} does not have a constructor with the required visibility");
ConstructorCache.Add(t, constructor);
CopyExtensions.ConstructorCache.Add(t, constructor);
}
return constructor.Invoke(new object[constructor.GetParameters().Length]);
}
@ -116,4 +116,4 @@ namespace MLEM.Data {
/// </summary>
[AttributeUsage(AttributeTargets.Constructor), Obsolete("CopyExtensions has major flaws and insufficient speed compared to other libraries specifically designed for copying objects.")]
public class CopyConstructorAttribute : Attribute {}
}
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@ -5,12 +6,13 @@ using System.Text.RegularExpressions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MLEM.Textures;
namespace MLEM.Data {
/// <summary>
/// <para>
/// This class represents an atlas of <see cref="TextureRegion"/> objects which are loaded from a special texture atlas file.
/// This class represents an atlas of <see cref="TextureRegion"/> objects which are loaded from a special texture atlas file.
/// To load a data texture atlas, you can use <see cref="DataTextureAtlasExtensions.LoadTextureAtlas"/>.
/// </para>
/// <para>
@ -50,9 +52,13 @@ namespace MLEM.Data {
}
}
/// <summary>
/// Returns an enumerable of all of the <see cref="TextureRegion"/>s in this atlas.
/// Returns an enumerable of all of the <see cref="TextureRegion"/> values in this atlas.
/// </summary>
public IEnumerable<TextureRegion> Regions => this.regions.Values;
/// <summary>
/// Returns an enumerable of all of the <see cref="TextureRegion"/> names in this atlas.
/// </summary>
public IEnumerable<string> RegionNames => this.regions.Keys;
private readonly Dictionary<string, TextureRegion> regions = new Dictionary<string, TextureRegion>();
@ -79,9 +85,8 @@ namespace MLEM.Data {
}
var atlas = new DataTextureAtlas(texture);
// parse each texture region: "<name> loc <u> <v> <w> <h> [piv <px> <py>] [off <ox> <oy>]"
// parse each texture region: "<names> loc <u> <v> <w> <h> [piv <px> <py>] [off <ox> <oy>]"
foreach (Match match in Regex.Matches(text, @"(.+)\W+loc\W+([0-9]+)\W+([0-9]+)\W+([0-9]+)\W+([0-9]+)\W*(?:piv\W+([0-9.]+)\W+([0-9.]+))?\W*(?:off\W+([0-9.]+)\W+([0-9.]+))?")) {
var name = match.Groups[1].Value.Trim();
// offset
var off = !match.Groups[8].Success ? Vector2.Zero : new Vector2(
float.Parse(match.Groups[8].Value, CultureInfo.InvariantCulture),
@ -91,18 +96,23 @@ namespace MLEM.Data {
var loc = new Rectangle(
int.Parse(match.Groups[2].Value), int.Parse(match.Groups[3].Value),
int.Parse(match.Groups[4].Value), int.Parse(match.Groups[5].Value));
loc.Offset(off);
loc.Offset(off.ToPoint());
// pivot
var piv = !match.Groups[6].Success ? Vector2.Zero : off + new Vector2(
float.Parse(match.Groups[6].Value, CultureInfo.InvariantCulture) - (pivotRelative ? 0 : loc.X),
float.Parse(match.Groups[7].Value, CultureInfo.InvariantCulture) - (pivotRelative ? 0 : loc.Y));
var region = new TextureRegion(texture, loc) {
PivotPixels = piv,
Name = name
};
atlas.regions.Add(name, region);
foreach (var name in Regex.Split(match.Groups[1].Value, @"\W")) {
var trimmed = name.Trim();
if (trimmed.Length <= 0)
continue;
var region = new TextureRegion(texture, loc) {
PivotPixels = piv,
Name = trimmed
};
atlas.regions.Add(trimmed, region);
}
}
return atlas;
@ -129,4 +139,4 @@ namespace MLEM.Data {
}
}
}
}

View file

@ -60,7 +60,7 @@ namespace MLEM.Data {
if (this.allFlagsCache == null)
this.allFlagsCache = new Dictionary<DynamicEnum, bool>();
if (!this.allFlagsCache.TryGetValue(flags, out var ret)) {
ret = (GetValue(this) & GetValue(flags)) == GetValue(flags);
ret = (DynamicEnum.GetValue(this) & DynamicEnum.GetValue(flags)) == DynamicEnum.GetValue(flags);
this.allFlagsCache.Add(flags, ret);
}
return ret;
@ -76,7 +76,7 @@ namespace MLEM.Data {
if (this.anyFlagsCache == null)
this.anyFlagsCache = new Dictionary<DynamicEnum, bool>();
if (!this.anyFlagsCache.TryGetValue(flags, out var ret)) {
ret = (GetValue(this) & GetValue(flags)) != 0;
ret = (DynamicEnum.GetValue(this) & DynamicEnum.GetValue(flags)) != 0;
this.anyFlagsCache.Add(flags, ret);
}
return ret;
@ -87,13 +87,13 @@ namespace MLEM.Data {
public override string ToString() {
if (this.name == null) {
var included = new List<DynamicEnum>();
if (GetValue(this) != 0) {
foreach (var v in GetValues(this.GetType())) {
if (this.HasFlag(v) && GetValue(v) != 0)
if (DynamicEnum.GetValue(this) != 0) {
foreach (var v in DynamicEnum.GetValues(this.GetType())) {
if (this.HasFlag(v) && DynamicEnum.GetValue(v) != 0)
included.Add(v);
}
}
this.name = included.Count > 0 ? string.Join(" | ", included) : GetValue(this).ToString();
this.name = included.Count > 0 ? string.Join(" | ", included) : DynamicEnum.GetValue(this).ToString();
}
return this.name;
}
@ -107,7 +107,7 @@ namespace MLEM.Data {
/// <returns>The newly created enum value</returns>
/// <exception cref="ArgumentException">Thrown if the name or value passed are already present</exception>
public static T Add<T>(string name, BigInteger value) where T : DynamicEnum {
var storage = GetStorage(typeof(T));
var storage = DynamicEnum.GetStorage(typeof(T));
// cached parsed values and names might be incomplete with new values
storage.ClearCaches();
@ -119,7 +119,7 @@ namespace MLEM.Data {
throw new ArgumentException($"Duplicate name {name}", nameof(name));
}
var ret = Construct(typeof(T), name, value);
var ret = DynamicEnum.Construct(typeof(T), name, value);
storage.Values.Add(value, ret);
return (T) ret;
}
@ -134,9 +134,9 @@ namespace MLEM.Data {
/// <returns>The newly created enum value</returns>
public static T AddValue<T>(string name) where T : DynamicEnum {
BigInteger value = 0;
while (GetStorage(typeof(T)).Values.ContainsKey(value))
while (DynamicEnum.GetStorage(typeof(T)).Values.ContainsKey(value))
value++;
return Add<T>(name, value);
return DynamicEnum.Add<T>(name, value);
}
/// <summary>
@ -149,9 +149,9 @@ namespace MLEM.Data {
/// <returns>The newly created enum value</returns>
public static T AddFlag<T>(string name) where T : DynamicEnum {
BigInteger value = 1;
while (GetStorage(typeof(T)).Values.ContainsKey(value))
while (DynamicEnum.GetStorage(typeof(T)).Values.ContainsKey(value))
value <<= 1;
return Add<T>(name, value);
return DynamicEnum.Add<T>(name, value);
}
/// <summary>
@ -161,7 +161,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type whose values to get</typeparam>
/// <returns>The defined values for the given type</returns>
public static IEnumerable<T> GetValues<T>() where T : DynamicEnum {
return GetValues(typeof(T)).Cast<T>();
return DynamicEnum.GetValues(typeof(T)).Cast<T>();
}
/// <summary>
@ -171,7 +171,7 @@ namespace MLEM.Data {
/// <param name="type">The type whose values to get</param>
/// <returns>The defined values for the given type</returns>
public static IEnumerable<DynamicEnum> GetValues(Type type) {
return GetStorage(type).Values.Values;
return DynamicEnum.GetStorage(type).Values.Values;
}
/// <summary>
@ -182,9 +182,9 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise OR (|) combination</returns>
public static T Or<T>(T left, T right) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).OrCache;
var cache = DynamicEnum.GetStorage(typeof(T)).OrCache;
if (!cache.TryGetValue((left, right), out var ret)) {
ret = GetEnumValue<T>(GetValue(left) | GetValue(right));
ret = DynamicEnum.GetEnumValue<T>(DynamicEnum.GetValue(left) | DynamicEnum.GetValue(right));
cache.Add((left, right), ret);
}
return (T) ret;
@ -198,9 +198,9 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise AND (&amp;) combination</returns>
public static T And<T>(T left, T right) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).AndCache;
var cache = DynamicEnum.GetStorage(typeof(T)).AndCache;
if (!cache.TryGetValue((left, right), out var ret)) {
ret = GetEnumValue<T>(GetValue(left) & GetValue(right));
ret = DynamicEnum.GetEnumValue<T>(DynamicEnum.GetValue(left) & DynamicEnum.GetValue(right));
cache.Add((left, right), ret);
}
return (T) ret;
@ -214,9 +214,9 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise XOR (^) combination</returns>
public static T Xor<T>(T left, T right) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).XorCache;
var cache = DynamicEnum.GetStorage(typeof(T)).XorCache;
if (!cache.TryGetValue((left, right), out var ret)) {
ret = GetEnumValue<T>(GetValue(left) ^ GetValue(right));
ret = DynamicEnum.GetEnumValue<T>(DynamicEnum.GetValue(left) ^ DynamicEnum.GetValue(right));
cache.Add((left, right), ret);
}
return (T) ret;
@ -229,9 +229,9 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise NEG (~) value</returns>
public static T Neg<T>(T value) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).NegCache;
var cache = DynamicEnum.GetStorage(typeof(T)).NegCache;
if (!cache.TryGetValue(value, out var ret)) {
ret = GetEnumValue<T>(~GetValue(value));
ret = DynamicEnum.GetEnumValue<T>(~DynamicEnum.GetValue(value));
cache.Add(value, ret);
}
return (T) ret;
@ -253,7 +253,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type that the returned dynamic enum should have</typeparam>
/// <returns>The defined or combined dynamic enum value</returns>
public static T GetEnumValue<T>(BigInteger value) where T : DynamicEnum {
return (T) GetEnumValue(typeof(T), value);
return (T) DynamicEnum.GetEnumValue(typeof(T), value);
}
/// <summary>
@ -263,7 +263,7 @@ namespace MLEM.Data {
/// <param name="value">The value whose dynamic enum value to get</param>
/// <returns>The defined or combined dynamic enum value</returns>
public static DynamicEnum GetEnumValue(Type type, BigInteger value) {
var storage = GetStorage(type);
var storage = DynamicEnum.GetStorage(type);
// get the defined value if it exists
if (storage.Values.TryGetValue(value, out var defined))
@ -271,7 +271,7 @@ namespace MLEM.Data {
// otherwise, cache the combined value
if (!storage.FlagCache.TryGetValue(value, out var combined)) {
combined = Construct(type, null, value);
combined = DynamicEnum.Construct(type, null, value);
storage.FlagCache.Add(value, combined);
}
return combined;
@ -286,7 +286,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the dynamic enum value to parse</typeparam>
/// <returns>The parsed enum value, or null if parsing fails</returns>
public static T Parse<T>(string strg) where T : DynamicEnum {
return (T) Parse(typeof(T), strg);
return (T) DynamicEnum.Parse(typeof(T), strg);
}
/// <summary>
@ -297,28 +297,28 @@ namespace MLEM.Data {
/// <param name="strg">The string to parse into a dynamic enum value</param>
/// <returns>The parsed enum value, or null if parsing fails</returns>
public static DynamicEnum Parse(Type type, string strg) {
var cache = GetStorage(type).ParseCache;
var cache = DynamicEnum.GetStorage(type).ParseCache;
if (!cache.TryGetValue(strg, out var cached)) {
BigInteger? accum = null;
foreach (var val in strg.Split('|')) {
foreach (var defined in GetValues(type)) {
foreach (var defined in DynamicEnum.GetValues(type)) {
if (defined.name == val.Trim()) {
accum = (accum ?? 0) | GetValue(defined);
accum = (accum ?? 0) | DynamicEnum.GetValue(defined);
break;
}
}
}
if (accum != null)
cached = GetEnumValue(type, accum.Value);
cached = DynamicEnum.GetEnumValue(type, accum.Value);
cache.Add(strg, cached);
}
return cached;
}
private static Storage GetStorage(Type type) {
if (!Storages.TryGetValue(type, out var storage)) {
if (!DynamicEnum.Storages.TryGetValue(type, out var storage)) {
storage = new Storage();
Storages.Add(type, storage);
DynamicEnum.Storages.Add(type, storage);
}
return storage;
}
@ -349,4 +349,4 @@ namespace MLEM.Data {
}
}
}
}

View file

@ -29,4 +29,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -27,4 +27,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -21,10 +21,10 @@ namespace MLEM.Data.Json {
/// <param name="serializer">The serializer to add the converters to</param>
/// <returns>The given serializer, for chaining</returns>
public static JsonSerializer AddAll(JsonSerializer serializer) {
foreach (var converter in Converters)
foreach (var converter in JsonConverters.Converters)
serializer.Converters.Add(converter);
return serializer;
}
}
}
}

View file

@ -42,4 +42,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -60,4 +60,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -30,4 +30,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -34,4 +34,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -34,4 +34,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -29,7 +29,7 @@ namespace MLEM.Data.Json {
/// </summary>
/// <param name="type">The type that the dictionary is declared in</param>
/// <param name="memberName">The name of the dictionary itself</param>
public StaticJsonConverter(Type type, string memberName) : this(GetEntries(type, memberName)) {}
public StaticJsonConverter(Type type, string memberName) : this(StaticJsonConverter<T>.GetEntries(type, memberName)) {}
/// <summary>Writes the JSON representation of the object.</summary>
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param>
@ -65,4 +65,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -30,4 +30,4 @@ namespace MLEM.Data.Json {
}
}
}
}

View file

@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<RootNamespace>MLEM.Data</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<PropertyGroup>
<Authors>Ellpeck</Authors>
<Description>Simple loading and processing of textures and other data for FNA, including the ability to load non-XNB content files easily</Description>
<PackageReleaseNotes>See the full changelog at https://mlem.ellpeck.de/CHANGELOG</PackageReleaseNotes>
<PackageTags>fna ellpeck mlem utility extensions data serialize</PackageTags>
<PackageProjectUrl>https://mlem.ellpeck.de/</PackageProjectUrl>
<RepositoryUrl>https://github.com/Ellpeck/MLEM</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MLEM\MLEM.FNA.csproj" />
<!--TODO remove lidgren support eventually (methods marked as obsolete since 5.2.0)-->
<PackageReference Include="Lidgren.Network" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<ProjectReference Include="..\FNA\FNA.Core.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="../Media/Logo.png" Pack="true" PackagePath="" />
<None Include="../Docs/index.md" Pack="true" PackagePath="README.md" />
</ItemGroup>
</Project>

View file

@ -3,11 +3,12 @@
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<PropertyGroup>
<Authors>Ellpeck</Authors>
<Description>Simple loading and processing of textures and data for MLEM Library for Extending MonoGame</Description>
<Description>Simple loading and processing of textures and other data for MonoGame, including the ability to load non-XNB content files easily</Description>
<PackageReleaseNotes>See the full changelog at https://mlem.ellpeck.de/CHANGELOG</PackageReleaseNotes>
<PackageTags>monogame ellpeck mlem utility extensions data serialize</PackageTags>
<PackageProjectUrl>https://mlem.ellpeck.de/</PackageProjectUrl>
@ -15,12 +16,11 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MLEM\MLEM.csproj" />
<!--TODO remove lidgren support eventually (methods marked as obsolete since 5.2.0)-->
<PackageReference Include="Lidgren.Network" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
@ -35,9 +35,9 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="../Media/Logo.png" Pack="true" PackagePath="" />
<None Include="../Docs/index.md" Pack="true" PackagePath="README.md" />
</ItemGroup>
</Project>
</Project>

View file

@ -100,4 +100,4 @@ namespace MLEM.Data {
}
}
}
}

View file

@ -117,4 +117,4 @@ namespace MLEM.Data {
}
}
}
}

View file

@ -6,6 +6,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MLEM.Textures;
using static MLEM.Extensions.TextureExtensions;
namespace MLEM.Data {
/// <summary>
@ -33,7 +34,10 @@ namespace MLEM.Data {
/// </summary>
public TimeSpan LastTotalTime => this.LastCalculationTime + this.LastPackTime;
private readonly List<Request> textures = new List<Request>();
private readonly List<Request> texturesToPack = new List<Request>();
private readonly List<Request> alreadyPackedTextures = new List<Request>();
private readonly Dictionary<Point, Point> firstPossiblePosForSizeCache = new Dictionary<Point, Point>();
private readonly Dictionary<Texture2D, TextureData> dataCache = new Dictionary<Texture2D, TextureData>();
private readonly bool autoIncreaseMaxWidth;
private readonly bool forcePowerOfTwo;
private readonly bool forceSquare;
@ -42,13 +46,13 @@ namespace MLEM.Data {
private int maxWidth;
/// <summary>
/// Creates a new runtime texture packer with the given settings
/// Creates a new runtime texture packer with the given settings.
/// </summary>
/// <param name="maxWidth">The maximum width that the packed texture can have. Defaults to 2048.</param>
/// <param name="autoIncreaseMaxWidth">Whether the maximum width should be increased if there is a texture to be packed that is wider than <see cref="maxWidth"/>. Defaults to false.</param>
/// <param name="forcePowerOfTwo">Whether the resulting <see cref="PackedTexture"/> should have a width and height that is a power of two</param>
/// <param name="forceSquare">Whether the resulting <see cref="PackedTexture"/> should be square regardless of required size</param>
/// <param name="disposeTextures">Whether the original textures submitted to this texture packer should be disposed after packing</param>
/// <param name="forcePowerOfTwo">Whether the resulting <see cref="PackedTexture"/> should have a width and height that is a power of two.</param>
/// <param name="forceSquare">Whether the resulting <see cref="PackedTexture"/> should be square regardless of required size.</param>
/// <param name="disposeTextures">Whether the original textures submitted to this texture packer should be disposed after packing.</param>
public RuntimeTexturePacker(int maxWidth = 2048, bool autoIncreaseMaxWidth = false, bool forcePowerOfTwo = false, bool forceSquare = false, bool disposeTextures = false) {
this.maxWidth = maxWidth;
this.autoIncreaseMaxWidth = autoIncreaseMaxWidth;
@ -58,37 +62,99 @@ namespace MLEM.Data {
}
/// <summary>
/// Adds a new texture to this texture packer to be packed.
/// Adds a new <see cref="UniformTextureAtlas"/> to this texture packer to be packed.
/// The passed <see cref="Action{T}"/> is invoked in <see cref="Pack"/> and provides the caller with the resulting dictionary of texture regions on the <see cref="PackedTexture"/>, mapped to their x and y positions on the original <see cref="UniformTextureAtlas"/>.
/// Note that the resulting data cannot be converted back into a <see cref="UniformTextureAtlas"/>, since the resulting texture regions might be scattered throughout the <see cref="PackedTexture"/>.
/// </summary>
/// <param name="atlas">The texture atlas to pack.</param>
/// <param name="result">The result callback which will receive the resulting texture regions.</param>
/// <param name="padding">The padding that the texture should have around itself. This can be useful if texture bleeding issues occur due to texture coordinate rounding.</param>
/// <param name="padWithPixels">Whether the texture's padding should be filled with a copy of the texture's border, rather than transparent pixels. This value only has an effect if <paramref name="padding"/> is greater than 0.</param>
/// <param name="ignoreTransparent">Whether completely transparent texture regions in the <paramref name="atlas"/> should be ignored. If this is true, they will not be part of the <paramref name="result"/> collection either.</param>
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width.</exception>
public void Add(UniformTextureAtlas atlas, Action<Dictionary<Point, TextureRegion>> result, int padding = 0, bool padWithPixels = false, bool ignoreTransparent = false) {
var addedRegions = new List<TextureRegion>();
var resultRegions = new Dictionary<Point, TextureRegion>();
for (var x = 0; x < atlas.RegionAmountX; x++) {
for (var y = 0; y < atlas.RegionAmountY; y++) {
var pos = new Point(x, y);
var region = atlas[pos];
if (ignoreTransparent) {
if (this.IsTransparent(region))
continue;
}
this.Add(region, r => {
resultRegions.Add(pos, r);
if (resultRegions.Count >= addedRegions.Count)
result.Invoke(resultRegions);
}, padding, padWithPixels);
addedRegions.Add(region);
}
}
}
/// <summary>
/// Adds a new <see cref="DataTextureAtlas"/> to this texture packer to be packed.
/// The passed <see cref="Action{T}"/> is invoked in <see cref="Pack"/> and provides the caller with the resulting dictionary of texture regions on the <see cref="PackedTexture"/>, mapped to their name on the original <see cref="DataTextureAtlas"/>.
/// Note that the resulting data cannot be converted back into a <see cref="DataTextureAtlas"/>, since the resulting texture regions might be scattered throughout the <see cref="PackedTexture"/>.
/// </summary>
/// <param name="atlas">The texture atlas to pack.</param>
/// <param name="result">The result callback which will receive the resulting texture regions.</param>
/// <param name="padding">The padding that the texture should have around itself. This can be useful if texture bleeding issues occur due to texture coordinate rounding.</param>
/// <param name="padWithPixels">Whether the texture's padding should be filled with a copy of the texture's border, rather than transparent pixels. This value only has an effect if <paramref name="padding"/> is greater than 0.</param>
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width.</exception>
public void Add(DataTextureAtlas atlas, Action<Dictionary<string, TextureRegion>> result, int padding = 0, bool padWithPixels = false) {
var atlasRegions = atlas.RegionNames.ToArray();
var resultRegions = new Dictionary<string, TextureRegion>();
foreach (var region in atlasRegions) {
this.Add(atlas[region], r => {
resultRegions.Add(region, r);
if (resultRegions.Count >= atlasRegions.Length)
result.Invoke(resultRegions);
}, padding, padWithPixels);
}
}
/// <summary>
/// Adds a new <see cref="Texture2D"/> to this texture packer to be packed.
/// The passed <see cref="Action{T}"/> is invoked in <see cref="Pack"/> and provides the caller with the resulting texture region on the <see cref="PackedTexture"/>.
/// </summary>
/// <param name="texture">The texture to pack</param>
/// <param name="result">The result callback which will receive the resulting texture region</param>
public void Add(Texture2D texture, Action<TextureRegion> result) {
this.Add(new TextureRegion(texture), result);
/// <param name="texture">The texture to pack.</param>
/// <param name="result">The result callback which will receive the resulting texture region.</param>
/// <param name="padding">The padding that the texture should have around itself. This can be useful if texture bleeding issues occur due to texture coordinate rounding.</param>
/// <param name="padWithPixels">Whether the texture's padding should be filled with a copy of the texture's border, rather than transparent pixels. This value only has an effect if <paramref name="padding"/> is greater than 0.</param>
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width.</exception>
public void Add(Texture2D texture, Action<TextureRegion> result, int padding = 0, bool padWithPixels = false) {
this.Add(new TextureRegion(texture), result, padding, padWithPixels);
}
/// <summary>
/// Adds a new <see cref="TextureRegion"/> to this texture packer to be packed.
/// The passed <see cref="Action{T}"/> is invoked in <see cref="Pack"/> and provides the caller with the resulting texture region on the <see cref="PackedTexture"/>.
/// </summary>
/// <param name="texture">The texture to pack</param>
/// <param name="result">The result callback which will receive the resulting texture region</param>
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width</exception>
public void Add(TextureRegion texture, Action<TextureRegion> result) {
/// <param name="texture">The texture region to pack.</param>
/// <param name="result">The result callback which will receive the resulting texture region.</param>
/// <param name="padding">The padding that the texture should have around itself. This can be useful if texture bleeding issues occur due to texture coordinate rounding.</param>
/// <param name="padWithPixels">Whether the texture's padding should be filled with a copy of the texture's border, rather than transparent pixels. This value only has an effect if <paramref name="padding"/> is greater than 0.</param>
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width.</exception>
public void Add(TextureRegion texture, Action<TextureRegion> result, int padding = 0, bool padWithPixels = false) {
if (this.PackedTexture != null)
throw new InvalidOperationException("Cannot add texture to a texture packer that is already packed");
if (texture.Width > this.maxWidth) {
var paddedWidth = texture.Width + 2 * padding;
if (paddedWidth > this.maxWidth) {
if (this.autoIncreaseMaxWidth) {
this.maxWidth = texture.Width;
this.maxWidth = paddedWidth;
} else {
throw new InvalidOperationException($"Cannot add texture with width {texture.Width} to a texture packer with max width {this.maxWidth}");
}
}
this.textures.Add(new Request(texture, result));
this.texturesToPack.Add(new Request(texture, result, padding, padWithPixels));
}
/// <summary>
/// Packs all of the textures and texture regions added using <see cref="Add(Microsoft.Xna.Framework.Graphics.Texture2D,System.Action{MLEM.Textures.TextureRegion})"/> into one texture.
/// Packs all of the textures and texture regions added using <see cref="Add(Microsoft.Xna.Framework.Graphics.Texture2D,System.Action{MLEM.Textures.TextureRegion},int,bool)"/> into one texture.
/// The resulting texture will be stored in <see cref="PackedTexture"/>.
/// All of the result callbacks that were added will also be invoked.
/// </summary>
@ -99,20 +165,23 @@ namespace MLEM.Data {
throw new InvalidOperationException("Cannot pack a texture packer that is already packed");
// set pack areas for each request
// we pack larger textures first, so that smaller textures can fit in the gaps that larger ones leave
var stopwatch = Stopwatch.StartNew();
foreach (var request in this.textures.OrderByDescending(t => t.Texture.Width * t.Texture.Height)) {
var area = this.FindFreeArea(new Point(request.Texture.Width, request.Texture.Height));
request.PackedArea = area;
foreach (var request in this.texturesToPack.OrderByDescending(t => t.Texture.Width * t.Texture.Height)) {
request.PackedArea = this.FindFreeArea(request);
// if this is the first position that this request fit in, no other requests of the same size will find a position before it
this.firstPossiblePosForSizeCache[new Point(request.PackedArea.Width, request.PackedArea.Height)] = request.PackedArea.Location;
this.alreadyPackedTextures.Add(request);
}
stopwatch.Stop();
this.LastCalculationTime = stopwatch.Elapsed;
// figure out texture size and generate texture
var width = this.textures.Max(t => t.PackedArea.Right);
var height = this.textures.Max(t => t.PackedArea.Bottom);
var width = this.alreadyPackedTextures.Max(t => t.PackedArea.Right);
var height = this.alreadyPackedTextures.Max(t => t.PackedArea.Bottom);
if (this.forcePowerOfTwo) {
width = ToPowerOfTwo(width);
height = ToPowerOfTwo(height);
width = RuntimeTexturePacker.ToPowerOfTwo(width);
height = RuntimeTexturePacker.ToPowerOfTwo(height);
}
if (this.forceSquare)
width = height = Math.Max(width, height);
@ -121,20 +190,21 @@ namespace MLEM.Data {
// copy texture data onto the packed texture
stopwatch.Restart();
using (var data = this.PackedTexture.GetTextureData()) {
foreach (var request in this.textures)
CopyRegion(data, request);
foreach (var request in this.alreadyPackedTextures)
this.CopyRegion(data, request);
}
stopwatch.Stop();
this.LastPackTime = stopwatch.Elapsed;
// invoke callbacks
foreach (var request in this.textures) {
request.Result.Invoke(new TextureRegion(this.PackedTexture, request.PackedArea));
foreach (var request in this.alreadyPackedTextures) {
var packedArea = request.PackedArea.Shrink(new Point(request.Padding, request.Padding));
request.Result.Invoke(new TextureRegion(this.PackedTexture, packedArea));
if (this.disposeTextures)
request.Texture.Texture.Dispose();
}
this.textures.Clear();
this.ClearTempCollections();
}
/// <summary>
@ -143,9 +213,9 @@ namespace MLEM.Data {
public void Reset() {
this.PackedTexture?.Dispose();
this.PackedTexture = null;
this.textures.Clear();
this.LastCalculationTime = TimeSpan.Zero;
this.LastPackTime = TimeSpan.Zero;
this.ClearTempCollections();
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
@ -153,13 +223,17 @@ namespace MLEM.Data {
this.Reset();
}
private Rectangle FindFreeArea(Point size) {
var pos = new Point(0, 0);
private Rectangle FindFreeArea(Request request) {
var size = new Point(request.Texture.Width, request.Texture.Height);
size.X += request.Padding * 2;
size.Y += request.Padding * 2;
var pos = this.firstPossiblePosForSizeCache.TryGetValue(size, out var first) ? first : Point.Zero;
var lowestY = int.MaxValue;
while (true) {
var intersected = false;
var area = new Rectangle(pos, size);
foreach (var tex in this.textures) {
var area = new Rectangle(pos.X, pos.Y, size.X, size.Y);
foreach (var tex in this.alreadyPackedTextures) {
if (tex.PackedArea.Intersects(area)) {
pos.X = tex.PackedArea.Right;
// when we move down, we want to move down by the smallest intersecting texture's height
@ -179,18 +253,53 @@ namespace MLEM.Data {
}
}
private static void CopyRegion(TextureExtensions.TextureData destination, Request request) {
using (var data = request.Texture.Texture.GetTextureData()) {
for (var x = 0; x < request.Texture.Width; x++) {
for (var y = 0; y < request.Texture.Height; y++) {
var dest = request.PackedArea.Location + new Point(x, y);
var src = request.Texture.Position + new Point(x, y);
destination[dest] = data[src];
private void CopyRegion(TextureData destination, Request request) {
var data = this.GetCachedTextureData(request.Texture.Texture);
var location = request.PackedArea.Location + new Point(request.Padding, request.Padding);
for (var x = -request.Padding; x < request.Texture.Width + request.Padding; x++) {
for (var y = -request.Padding; y < request.Texture.Height + request.Padding; y++) {
Color srcColor;
if (!request.PadWithPixels && (x < 0 || y < 0 || x >= request.Texture.Width || y >= request.Texture.Height)) {
// if we're out of bounds and not padding with pixels, we make it transparent
srcColor = Color.Transparent;
} else {
// otherwise, we just use the closest pixel that is actually in bounds, causing the border pixels to be doubled up
var src = new Point((int) MathHelper.Clamp(x, 0, request.Texture.Width - 1), (int) MathHelper.Clamp(y, 0, request.Texture.Height - 1));
srcColor = data[request.Texture.Position + src];
}
destination[location + new Point(x, y)] = srcColor;
}
}
}
private TextureData GetCachedTextureData(Texture2D texture) {
// we cache texture data in case multiple requests use the same underlying texture
// this collection doesn't need to be disposed since we don't actually edit these textures
if (!this.dataCache.TryGetValue(texture, out var data)) {
data = texture.GetTextureData();
this.dataCache.Add(texture, data);
}
return data;
}
private bool IsTransparent(TextureRegion region) {
var data = this.GetCachedTextureData(region.Texture);
for (var rX = 0; rX < region.Width; rX++) {
for (var rY = 0; rY < region.Height; rY++) {
if (data[region.U + rX, region.V + rY] != Color.Transparent)
return false;
}
}
return true;
}
private void ClearTempCollections() {
this.texturesToPack.Clear();
this.alreadyPackedTextures.Clear();
this.firstPossiblePosForSizeCache.Clear();
this.dataCache.Clear();
}
private static int ToPowerOfTwo(int value) {
var ret = 1;
while (ret < value)
@ -202,14 +311,18 @@ namespace MLEM.Data {
public readonly TextureRegion Texture;
public readonly Action<TextureRegion> Result;
public readonly int Padding;
public readonly bool PadWithPixels;
public Rectangle PackedArea;
public Request(TextureRegion texture, Action<TextureRegion> result) {
public Request(TextureRegion texture, Action<TextureRegion> result, int padding, bool padWithPixels) {
this.Texture = texture;
this.Result = result;
this.Padding = padding;
this.PadWithPixels = padWithPixels;
}
}
}
}
}

View file

@ -41,4 +41,4 @@ namespace MLEM.Extended.Extensions {
}
}
}
}

View file

@ -40,4 +40,4 @@ namespace MLEM.Extended.Extensions {
}
}
}
}

View file

@ -62,4 +62,4 @@ namespace MLEM.Extended.Extensions {
}
}
}
}

View file

@ -45,4 +45,4 @@ namespace MLEM.Extended.Extensions {
}
}
}
}

View file

@ -43,4 +43,4 @@ namespace MLEM.Extended.Font {
}
}
}
}

View file

@ -43,4 +43,4 @@ namespace MLEM.Extended.Font {
}
}
}
}

View file

@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<RootNamespace>MLEM.Extended</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Authors>Ellpeck</Authors>
<Description>MLEM Library for Extending FNA extension that ties in with other FNA libraries</Description>
<PackageReleaseNotes>See the full changelog at https://mlem.ellpeck.de/CHANGELOG</PackageReleaseNotes>
<PackageTags>fna ellpeck mlem utility extensions extended</PackageTags>
<PackageProjectUrl>https://mlem.ellpeck.de/</PackageProjectUrl>
<RepositoryUrl>https://github.com/Ellpeck/MLEM</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MLEM\MLEM.FNA.csproj" />
<ProjectReference Include="..\FontStashSharp\src\XNA\FontStashSharp.FNA.Core.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
<ProjectReference Include="..\FNA\FNA.Core.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
<Compile Remove="Tiled/**" />
<Compile Remove="Extensions/**" />
<Compile Remove="Font/GenericBitmapFont.cs" />
</ItemGroup>
<ItemGroup>
<None Include="../Media/Logo.png" Pack="true" PackagePath="" />
<None Include="../Docs/index.md" Pack="true" PackagePath="README.md" />
</ItemGroup>
</Project>

View file

@ -26,7 +26,7 @@
<PackageReference Include="MonoGame.Extended.Tiled" Version="3.8.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FontStashSharp.MonoGame" Version="1.0.4">
<PackageReference Include="FontStashSharp.MonoGame" Version="1.1.6">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641">

View file

@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Cameras;
using MLEM.Extensions;
using MLEM.Graphics;
using MLEM.Misc;
using MonoGame.Extended.Tiled;
using RectangleF = MonoGame.Extended.RectangleF;
@ -92,6 +93,20 @@ namespace MLEM.Extended.Tiled {
}
}
/// <summary>
/// Adds this individual tiled map renderer to the given <see cref="StaticSpriteBatch"/>.
/// Optionally, a frustum can be supplied that determines which positions, in pixel space, are visible at this time. <see cref="Camera"/> provides <see cref="Camera.GetVisibleRectangle"/> for this purpose.
/// </summary>
/// <param name="batch">The static sprite batch to use for drawing.</param>
/// <param name="frustum">The area that is visible, in pixel space.</param>
/// <param name="addFunction">The add function to use, or null to use <see cref="DefaultAdd"/>.</param>
public void Add(StaticSpriteBatch batch, RectangleF? frustum = null, AddDelegate addFunction = null) {
for (var i = 0; i < this.map.TileLayers.Count; i++) {
if (this.map.TileLayers[i].IsVisible)
this.AddLayer(batch, i, frustum, addFunction);
}
}
/// <summary>
/// Draws the given layer of this individual tiled map renderer.
/// Optionally, a frustum can be supplied that determines which positions, in pixel space, are visible at this time. <see cref="Camera"/> provides <see cref="Camera.GetVisibleRectangle"/> for this purpose.
@ -101,12 +116,8 @@ namespace MLEM.Extended.Tiled {
/// <param name="frustum">The area that is visible, in pixel space.</param>
/// <param name="drawFunction">The draw function to use, or null to use <see cref="DefaultDraw"/></param>
public void DrawLayer(SpriteBatch batch, int layerIndex, RectangleF? frustum = null, DrawDelegate drawFunction = null) {
var draw = drawFunction ?? DefaultDraw;
var frust = frustum ?? new RectangleF(0, 0, float.MaxValue, float.MaxValue);
var minX = Math.Max(0, frust.Left / this.map.TileWidth).Floor();
var minY = Math.Max(0, frust.Top / this.map.TileHeight).Floor();
var maxX = Math.Min(this.map.Width, frust.Right / this.map.TileWidth).Ceil();
var maxY = Math.Min(this.map.Height, frust.Bottom / this.map.TileHeight).Ceil();
var draw = drawFunction ?? IndividualTiledMapRenderer.DefaultDraw;
var (minX, minY, maxX, maxY) = this.GetFrustum(frustum);
for (var x = minX; x < maxX; x++) {
for (var y = minY; y < maxY; y++) {
var info = this.drawInfos[layerIndex, x, y];
@ -116,6 +127,26 @@ namespace MLEM.Extended.Tiled {
}
}
/// <summary>
/// Adds the given layer of this individual tiled map renderer to the given <see cref="StaticSpriteBatch"/>.
/// Optionally, a frustum can be supplied that determines which positions, in pixel space, are visible at this time. <see cref="Camera"/> provides <see cref="Camera.GetVisibleRectangle"/> for this purpose.
/// </summary>
/// <param name="batch">The static sprite batch to use for drawing.</param>
/// <param name="layerIndex">The index of the layer in <see cref="TiledMap.TileLayers"/>.</param>
/// <param name="frustum">The area that is visible, in pixel space.</param>
/// <param name="addFunction">The add function to use, or null to use <see cref="DefaultAdd"/>.</param>
public void AddLayer(StaticSpriteBatch batch, int layerIndex, RectangleF? frustum = null, AddDelegate addFunction = null) {
var add = addFunction ?? IndividualTiledMapRenderer.DefaultAdd;
var (minX, minY, maxX, maxY) = this.GetFrustum(frustum);
for (var x = minX; x < maxX; x++) {
for (var y = minY; y < maxY; y++) {
var info = this.drawInfos[layerIndex, x, y];
if (info != null)
add(batch, info);
}
}
}
/// <summary>
/// Update all of the animated tiles in this individual tiled map renderer
/// </summary>
@ -125,8 +156,17 @@ namespace MLEM.Extended.Tiled {
animation.Update(time);
}
private (int MinX, int MinY, int MaxX, int MaxY) GetFrustum(RectangleF? frustum) {
var frust = frustum ?? new RectangleF(0, 0, float.MaxValue, float.MaxValue);
var minX = Math.Max(0, frust.Left / this.map.TileWidth).Floor();
var minY = Math.Max(0, frust.Top / this.map.TileHeight).Floor();
var maxX = Math.Min(this.map.Width, frust.Right / this.map.TileWidth).Ceil();
var maxY = Math.Min(this.map.Height, frust.Bottom / this.map.TileHeight).Ceil();
return (minX, minY, maxX, maxY);
}
/// <summary>
/// The default implementation of <see cref="DrawDelegate"/> that is used by <see cref="SetMap"/> if no custom draw function is passed
/// The default implementation of <see cref="DrawDelegate"/> that is used by <see cref="Draw"/> if no custom draw function is passed.
/// </summary>
/// <param name="batch">The sprite batch to use for drawing</param>
/// <param name="info">The <see cref="TileDrawInfo"/> to draw</param>
@ -137,6 +177,18 @@ namespace MLEM.Extended.Tiled {
batch.Draw(info.Tileset.Texture, drawPos, region, Color.White, 0, Vector2.Zero, 1, effects, info.Depth);
}
/// <summary>
/// The default implementation of <see cref="AddDelegate"/> that is used by <see cref="Add"/> if no custom add function is passed.
/// </summary>
/// <param name="batch">The static sprite batch to use for drawing.</param>
/// <param name="info">The <see cref="TileDrawInfo"/> to add.</param>
public static void DefaultAdd(StaticSpriteBatch batch, TileDrawInfo info) {
var region = info.Tileset.GetTextureRegion(info.TilesetTile);
var effects = info.Tile.GetSpriteEffects();
var drawPos = new Vector2(info.Position.X * info.Renderer.map.TileWidth, info.Position.Y * info.Renderer.map.TileHeight);
batch.Add(info.Tileset.Texture, drawPos, region, Color.White, 0, Vector2.Zero, 1, effects, info.Depth);
}
/// <summary>
/// A delegate method used for <see cref="IndividualTiledMapRenderer.depthFunction"/>.
/// The idea is to return a depth (between 0 and 1) for the given tile that determines where in the sprite batch it should be rendererd.
@ -155,6 +207,13 @@ namespace MLEM.Extended.Tiled {
/// <param name="info">The <see cref="TileDrawInfo"/> to draw</param>
public delegate void DrawDelegate(SpriteBatch batch, TileDrawInfo info);
/// <summary>
/// A delegate method used for adding an <see cref="IndividualTiledMapRenderer"/> to a <see cref="StaticSpriteBatch"/>.
/// </summary>
/// <param name="batch">The static sprite batch to use for drawing.</param>
/// <param name="info">The <see cref="TileDrawInfo"/> to add.</param>
public delegate void AddDelegate(StaticSpriteBatch batch, TileDrawInfo info);
/// <summary>
/// A tile draw info contains information about a tile at a given map location.
/// It caches a lot of data that is required for drawing a tile efficiently.
@ -198,4 +257,4 @@ namespace MLEM.Extended.Tiled {
}
}
}
}

View file

@ -4,7 +4,8 @@ using MonoGame.Extended.Tiled;
namespace MLEM.Extended.Tiled {
/// <summary>
/// A struct that represents a position on a <see cref="TiledMap"/> with multiple layers.
/// A struct that represents a position on a <see cref="TiledMap"/> with multiple layers, where the <see cref="X"/> and <see cref="Y"/> coordinates are 32-bit integer numbers.
/// See <see cref="LayerPositionF"/> for a floating point position.
/// </summary>
[DataContract]
public struct LayerPosition : IEquatable<LayerPosition> {
@ -53,8 +54,8 @@ namespace MLEM.Extended.Tiled {
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
public override int GetHashCode() {
var hashCode = this.Layer.GetHashCode();
hashCode = (hashCode * 397) ^ this.X;
hashCode = (hashCode * 397) ^ this.Y;
hashCode = hashCode * 397 ^ this.X;
hashCode = hashCode * 397 ^ this.Y;
return hashCode;
}
@ -104,7 +105,7 @@ namespace MLEM.Extended.Tiled {
/// <param name="right">The right position.</param>
/// <returns>The sum of the positions.</returns>
public static LayerPosition operator +(LayerPosition left, LayerPosition right) {
return Add(left, right);
return LayerPosition.Add(left, right);
}
/// <summary>
@ -114,8 +115,17 @@ namespace MLEM.Extended.Tiled {
/// <param name="right">The right position.</param>
/// <returns>The difference of the positions.</returns>
public static LayerPosition operator -(LayerPosition left, LayerPosition right) {
return Add(left, -right);
return LayerPosition.Add(left, -right);
}
/// <summary>
/// Implicitly converts a <see cref="LayerPosition"/> to a <see cref="LayerPositionF"/>.
/// </summary>
/// <param name="position">The position to convert.</param>
/// <returns>The converted position.</returns>
public static implicit operator LayerPositionF(LayerPosition position) {
return new LayerPositionF(position.Layer, position.X, position.Y);
}
}
}
}

View file

@ -0,0 +1,132 @@
using System;
using System.Runtime.Serialization;
using MonoGame.Extended.Tiled;
namespace MLEM.Extended.Tiled {
/// <summary>
/// A struct that represents a position on a <see cref="TiledMap"/> with multiple layers, where the <see cref="X"/> and <see cref="Y"/> coordinates are 32-bit floating point numbers.
/// See <see cref="LayerPosition"/> for an integer position.
/// </summary>
[DataContract]
public struct LayerPositionF : IEquatable<LayerPositionF> {
/// <summary>
/// The name of the layer that this position is on
/// </summary>
[DataMember]
public string Layer;
/// <summary>
/// The x coordinate of this position
/// </summary>
[DataMember]
public float X;
/// <summary>
/// The y coordinate of this position
/// </summary>
[DataMember]
public float Y;
/// <summary>
/// Creates a new layer position with the given settings
/// </summary>
/// <param name="layerName">The layer name</param>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
public LayerPositionF(string layerName, float x, float y) {
this.Layer = layerName;
this.X = x;
this.Y = y;
}
/// <inheritdoc cref="Equals(object)"/>
public bool Equals(LayerPositionF other) {
return this.Layer == other.Layer && this.X == other.X && this.Y == other.Y;
}
/// <summary>Indicates whether this instance and a specified object are equal.</summary>
/// <param name="obj">The object to compare with the current instance.</param>
/// <returns>true if <paramref name="obj">obj</paramref> and this instance are the same type and represent the same value; otherwise, false.</returns>
public override bool Equals(object obj) {
return obj is LayerPositionF other && this.Equals(other);
}
/// <summary>Returns the hash code for this instance.</summary>
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
public override int GetHashCode() {
var hashCode = this.Layer.GetHashCode();
hashCode = hashCode * 397 ^ this.X.GetHashCode();
hashCode = hashCode * 397 ^ this.Y.GetHashCode();
return hashCode;
}
/// <summary>Returns the fully qualified type name of this instance.</summary>
/// <returns>The fully qualified type name.</returns>
public override string ToString() {
return $"{nameof(this.Layer)}: {this.Layer}, {nameof(this.X)}: {this.X}, {nameof(this.Y)}: {this.Y}";
}
/// <summary>
/// Adds the given layer positions together, returning a new layer position with the sum of their coordinates.
/// If the two layer positions' <see cref="Layer"/> differ, an <see cref="ArgumentException"/> is thrown.
/// </summary>
/// <param name="left">The left position.</param>
/// <param name="right">The right position.</param>
/// <returns>The sum of the positions.</returns>
/// <exception cref="ArgumentException">Thrown if the two positions' <see cref="Layer"/> are not the same.</exception>
public static LayerPositionF Add(LayerPositionF left, LayerPositionF right) {
if (left.Layer != right.Layer)
throw new ArgumentException("Cannot add layer positions on different layers");
return new LayerPositionF(left.Layer, left.X + right.X, left.Y + right.Y);
}
/// <inheritdoc cref="Equals(LayerPositionF)"/>
public static bool operator ==(LayerPositionF left, LayerPositionF right) {
return left.Equals(right);
}
/// <inheritdoc cref="Equals(LayerPositionF)"/>
public static bool operator !=(LayerPositionF left, LayerPositionF right) {
return !left.Equals(right);
}
/// <summary>
/// Returns the negative of the given layer position.
/// </summary>
/// <param name="position">The position to negate.</param>
/// <returns>The negative position.</returns>
public static LayerPositionF operator -(LayerPositionF position) {
return new LayerPositionF(position.Layer, -position.X, -position.Y);
}
/// <summary>
/// Returns the sum of the two layer positions using <see cref="Add"/>.
/// </summary>
/// <param name="left">The left position.</param>
/// <param name="right">The right position.</param>
/// <returns>The sum of the positions.</returns>
public static LayerPositionF operator +(LayerPositionF left, LayerPositionF right) {
return LayerPositionF.Add(left, right);
}
/// <summary>
/// Subtracts the second from the first position using <see cref="Add"/>.
/// </summary>
/// <param name="left">The left position.</param>
/// <param name="right">The right position.</param>
/// <returns>The difference of the positions.</returns>
public static LayerPositionF operator -(LayerPositionF left, LayerPositionF right) {
return LayerPositionF.Add(left, -right);
}
/// <summary>
/// Implicitly converts a <see cref="LayerPositionF"/> to a <see cref="LayerPosition"/>.
/// The coordinates are typecast to 32-bit integers in the process.
/// </summary>
/// <param name="position">The position to convert.</param>
/// <returns>The converted position.</returns>
public static implicit operator LayerPosition(LayerPositionF position) {
return new LayerPosition(position.Layer, (int) position.X, (int) position.Y);
}
}
}

View file

@ -5,7 +5,6 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended;
using MonoGame.Extended.Tiled;
using static MonoGame.Extended.Tiled.TiledMapTileFlipFlags;
using ColorHelper = MLEM.Extensions.ColorHelper;
namespace MLEM.Extended.Tiled {
@ -144,9 +143,9 @@ namespace MLEM.Extended.Tiled {
public static TiledMapTilesetTile GetTilesetTile(this TiledMapTileset tileset, int localId, bool createStub = true) {
var tilesetTile = tileset.Tiles.FirstOrDefault(t => t.LocalTileIdentifier == localId);
if (tilesetTile == null && createStub) {
if (!StubTilesetTiles.TryGetValue(localId, out tilesetTile)) {
if (!TiledExtensions.StubTilesetTiles.TryGetValue(localId, out tilesetTile)) {
tilesetTile = new TiledMapTilesetTile(localId);
StubTilesetTiles.Add(localId, tilesetTile);
TiledExtensions.StubTilesetTiles.Add(localId, tilesetTile);
}
}
return tilesetTile;
@ -271,12 +270,12 @@ namespace MLEM.Extended.Tiled {
/// <param name="position">The position to add to the object's position</param>
/// <param name="flipFlags">The flipping of the tile that this object belongs to. If set, the returned area will be "flipped" in the tile's space so that it matches the flip flags.</param>
/// <returns>The area that the tile covers</returns>
public static RectangleF GetArea(this TiledMapObject obj, TiledMap map, Vector2? position = null, TiledMapTileFlipFlags flipFlags = None) {
public static RectangleF GetArea(this TiledMapObject obj, TiledMap map, Vector2? position = null, TiledMapTileFlipFlags flipFlags = TiledMapTileFlipFlags.None) {
var tileSize = map.GetTileSize();
var area = new RectangleF(obj.Position / tileSize, obj.Size / tileSize);
if (flipFlags.HasFlag(FlipHorizontally))
if (flipFlags.HasFlag(TiledMapTileFlipFlags.FlipHorizontally))
area.X = 1 - area.X - area.Width;
if (flipFlags.HasFlag(FlipVertically))
if (flipFlags.HasFlag(TiledMapTileFlipFlags.FlipVertically))
area.Y = 1 - area.Y - area.Height;
if (position != null)
area.Offset(position.Value);
@ -361,4 +360,4 @@ namespace MLEM.Extended.Tiled {
}
}
}
}

View file

@ -36,7 +36,7 @@ namespace MLEM.Extended.Tiled {
/// <param name="collisionFunction">The function used to collect the collision info of a tile, or null to use <see cref="DefaultCollectCollisions"/></param>
public void SetMap(TiledMap map, CollectCollisions collisionFunction = null) {
this.map = map;
this.collisionFunction = collisionFunction ?? DefaultCollectCollisions;
this.collisionFunction = collisionFunction ?? TiledMapCollisions.DefaultCollectCollisions;
this.collisionInfos = new TileCollisionInfo[map.Layers.Count, map.Width, map.Height];
for (var i = 0; i < map.TileLayers.Count; i++) {
for (var x = 0; x < map.Width; x++) {
@ -204,4 +204,4 @@ namespace MLEM.Extended.Tiled {
}
}
}
}

70
MLEM.FNA.sln Normal file
View file

@ -0,0 +1,70 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.FNA", "MLEM\MLEM.FNA.csproj", "{C2C88AE6-6274-4395-8B03-52AE898BA070}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Ui.FNA", "MLEM.Ui\MLEM.Ui.FNA.csproj", "{1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Startup.FNA", "MLEM.Startup\MLEM.Startup.FNA.csproj", "{FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Data.FNA", "MLEM.Data\MLEM.Data.FNA.csproj", "{6587BC91-0640-43FB-988A-4F545B8ACFC5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demos.FNA", "Demos\Demos.FNA.csproj", "{D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demos.DesktopGL.FNA", "Demos.DesktopGL\Demos.DesktopGL.FNA.csproj", "{AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.FNA", "Tests\Tests.FNA.csproj", "{C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Extended.FNA", "MLEM.Extended\MLEM.Extended.FNA.csproj", "{A5B22930-DF4B-4A62-93ED-A6549F7B666B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNA.Core", "FNA\FNA.Core.csproj", "{06459F72-CEAA-4B45-B2B1-708FC28D04F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FontStashSharp.FNA.Core", "FontStashSharp\src\XNA\FontStashSharp.FNA.Core.csproj", "{0B410591-3AED-4C82-A07A-516FF493709B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C2C88AE6-6274-4395-8B03-52AE898BA070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2C88AE6-6274-4395-8B03-52AE898BA070}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2C88AE6-6274-4395-8B03-52AE898BA070}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2C88AE6-6274-4395-8B03-52AE898BA070}.Release|Any CPU.Build.0 = Release|Any CPU
{1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Release|Any CPU.Build.0 = Release|Any CPU
{FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Release|Any CPU.Build.0 = Release|Any CPU
{6587BC91-0640-43FB-988A-4F545B8ACFC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6587BC91-0640-43FB-988A-4F545B8ACFC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6587BC91-0640-43FB-988A-4F545B8ACFC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6587BC91-0640-43FB-988A-4F545B8ACFC5}.Release|Any CPU.Build.0 = Release|Any CPU
{D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Release|Any CPU.Build.0 = Release|Any CPU
{AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Release|Any CPU.Build.0 = Release|Any CPU
{C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Release|Any CPU.Build.0 = Release|Any CPU
{A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Release|Any CPU.Build.0 = Release|Any CPU
{06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Debug|Any CPU.ActiveCfg = Debug|x64
{06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Debug|Any CPU.Build.0 = Debug|x64
{06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Release|Any CPU.ActiveCfg = Release|x64
{06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Release|Any CPU.Build.0 = Release|x64
{0B410591-3AED-4C82-A07A-516FF493709B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B410591-3AED-4C82-A07A-516FF493709B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B410591-3AED-4C82-A07A-516FF493709B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B410591-3AED-4C82-A07A-516FF493709B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -24,4 +24,4 @@ namespace MLEM.Startup {
public static readonly Event Draw = new Event();
}
}
}

View file

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<RootNamespace>MLEM.Startup</RootNamespace>
<DefineConstants>$(DefineConstants);FNA</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Authors>Ellpeck</Authors>
<Description>MLEM Library for Extending FNA combined with some other useful libraries into a quick Game startup class</Description>
<PackageReleaseNotes>See the full changelog at https://mlem.ellpeck.de/CHANGELOG</PackageReleaseNotes>
<PackageTags>fna ellpeck mlem utility extensions</PackageTags>
<PackageProjectUrl>https://mlem.ellpeck.de/</PackageProjectUrl>
<RepositoryUrl>https://github.com/Ellpeck/MLEM</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Coroutine" Version="2.1.3" />
<ProjectReference Include="..\MLEM.Ui\MLEM.Ui.FNA.csproj" />
<ProjectReference Include="..\MLEM\MLEM.FNA.csproj" />
<ProjectReference Include="..\FNA\FNA.Core.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="../Media/Logo.png" Pack="true" PackagePath="" />
<None Include="../Docs/index.md" Pack="true" PackagePath="README.md" />
</ItemGroup>
</Project>

View file

@ -18,7 +18,7 @@ namespace MLEM.Startup {
/// <summary>
/// The static game instance's input handler
/// </summary>
public static InputHandler Input => instance.InputHandler;
public static InputHandler Input => MlemGame.instance.InputHandler;
/// <summary>
/// This game's graphics device manager, initialized in the constructor
@ -64,12 +64,14 @@ namespace MLEM.Startup {
/// <param name="windowWidth">The default window width</param>
/// <param name="windowHeight">The default window height</param>
public MlemGame(int windowWidth = 1280, int windowHeight = 720) {
instance = this;
MlemGame.instance = this;
this.GraphicsDeviceManager = new GraphicsDeviceManager(this) {
PreferredBackBufferWidth = windowWidth,
PreferredBackBufferHeight = windowHeight,
#if !FNA
HardwareModeSwitch = false
#endif
};
this.Window.AllowUserResizing = true;
this.Content.RootDirectory = "Content";
@ -160,7 +162,7 @@ namespace MLEM.Startup {
/// <typeparam name="T">The type of content to load</typeparam>
/// <returns>The loaded content</returns>
public static T LoadContent<T>(string name) {
return instance.Content.Load<T>(name);
return MlemGame.instance.Content.Load<T>(name);
}
/// <summary>
@ -177,4 +179,4 @@ namespace MLEM.Startup {
public delegate void TimeCallback(MlemGame game, GameTime time);
}
}
}

View file

@ -0,0 +1,36 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-mgcb": {
"version": "3.8.1.263",
"commands": [
"mgcb"
]
},
"dotnet-mgcb-editor": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor"
]
},
"dotnet-mgcb-editor-linux": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-linux"
]
},
"dotnet-mgcb-editor-windows": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-windows"
]
},
"dotnet-mgcb-editor-mac": {
"version": "3.8.1.263",
"commands": [
"mgcb-editor-mac"
]
}
}
}

View file

@ -1,13 +1,13 @@
using MLEM.Startup;
namespace TemplateNamespace {
public class GameImpl : MlemGame {
namespace TemplateNamespace;
public static GameImpl Instance { get; private set; }
public class GameImpl : MlemGame {
public GameImpl() {
Instance = this;
}
public static GameImpl Instance { get; private set; }
public GameImpl() {
Instance = this;
}
}

View file

@ -1,14 +1,14 @@
using Microsoft.Xna.Framework;
using MLEM.Misc;
namespace TemplateNamespace {
public static class Program {
namespace TemplateNamespace;
public static void Main() {
MlemPlatform.Current = new MlemPlatform.DesktopGl<TextInputEventArgs>((w, c) => w.TextInput += c);
using var game = new GameImpl();
game.Run();
}
public static class Program {
public static void Main() {
MlemPlatform.Current = new MlemPlatform.DesktopGl<TextInputEventArgs>((w, c) => w.TextInput += c);
using var game = new GameImpl();
game.Run();
}
}

View file

@ -1,24 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<PublishReadyToRun>false</PublishReadyToRun>
<TieredCompilation>false</TieredCompilation>
<ApplicationIcon>Icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Contentless" Version="3.*" />
<PackageReference Include="MLEM.Startup" Version="5.*" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
<PackageReference Include="MLEM.Startup" Version="6.*" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.263" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.263" />
</ItemGroup>
<ItemGroup>
<MonoGameContentReference Include="Content\Content.mgcb" />
<Content Include="Content\*\**" />
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Icon.bmp" />
</ItemGroup>
</Project>
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
<Message Text="Restoring dotnet tools" Importance="High" />
<Exec Command="dotnet tool restore" />
</Target>
</Project>

View file

@ -1,13 +1,13 @@
using MLEM.Startup;
namespace TemplateNamespace {
public class GameImpl : MlemGame {
namespace TemplateNamespace;
public static GameImpl Instance { get; private set; }
public class GameImpl : MlemGame {
public GameImpl() {
Instance = this;
}
public static GameImpl Instance { get; private set; }
public GameImpl() {
Instance = this;
}
}
}

View file

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MLEM.Startup" Version="5.*" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*">
<PackageReference Include="MLEM.Startup" Version="6.*" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.263">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
</Project>

Some files were not shown because too many files have changed in this diff Show more