diff --git a/articles/tutorials/advanced/01_publish_to_android/index.md b/articles/tutorials/advanced/01_publish_to_android/index.md deleted file mode 100644 index e69de29b..00000000 diff --git a/articles/tutorials/advanced/02_publish_to_ios/index.md b/articles/tutorials/advanced/02_publish_to_ios/index.md deleted file mode 100644 index e69de29b..00000000 diff --git a/articles/tutorials/advanced/MobileDeployment/01_getting_started/index.md b/articles/tutorials/advanced/MobileDeployment/01_getting_started/index.md new file mode 100644 index 00000000..a33a4d39 --- /dev/null +++ b/articles/tutorials/advanced/MobileDeployment/01_getting_started/index.md @@ -0,0 +1,54 @@ +--- +title: "01: Setting Up MonoGame for Android and iOS Development" +description: "Get started with MonoGame mobile development by setting up your development environment, tools, and SDKs for Android and iOS platforms." +--- + +# Getting Started and Overview + +This tutorial extends the **MonoGame 2D Dungeon Slime tutorial** to mobile platforms. If you haven't completed the desktop tutorial yet, we recommend finishing it first as we'll be building directly on that foundation. + +## What You'll Learn + +By the end of this mobile tutorial series, you'll have: +- Ported the **Dungeon Slime** game to run on Android and iOS +- Implemented touch controls to replace mouse and keyboard input +- Set up cross-platform project architecture for code sharing +- Learned debugging techniques for mobile development +- Published your game to both Google Play and the App Store +- Created automated deployment workflows + +# Development Requirements + +## Android Development + +**Platform Support:** Android development can be accomplished on either Windows PC or Mac. However, ARM64 variants of Windows do not run the Android simulator well. + +**Required Tools:** +- **Android Device Manager** - Used to set up simulators (accessible through Visual Studio) +- **Android SDK Manager** - Used to install the SDK platforms + +## iOS Development + +**Platform Requirement:** iOS development requires a Mac, whether you develop entirely on it or use the **Pair to Mac** option with Windows Visual Studio. + +Since **Visual Studio for Mac** has been deprecated, [JetBrains Rider](https://www.jetbrains.com/rider/) is the currently recommended IDE for Mac development. Visual Studio Code is another viable option too. + +**Additional Requirements:** +- **Xcode** - Required for iOS development and deployment +- **Apple Developer Account** - Required for physical device deployment and App Store publishing ([enrollment link](https://developer.apple.com/programs/enroll/)) + +# Modern .NET Project Features + +This tutorial utilizes modern .NET project management features to streamline cross-platform development: + +## Central Package Management + +We'll be using [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management) to maintain consistent package versions across all platform projects. This ensures your shared code library and platform-specific projects use identical dependency versions. + +## SLNX Solution Format + +The sample projects use the new [SLNX solution format](https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/) for improved cross-platform development experience and better integration with modern .NET tooling. + +# Next Steps + +In the next chapter, we'll convert the existing Windows-only Dungeon Slime project to support multiple platforms and explore the cross-platform project architecture. diff --git a/articles/tutorials/advanced/MobileDeployment/02_crossplatform/images/crossplatform_projects.png b/articles/tutorials/advanced/MobileDeployment/02_crossplatform/images/crossplatform_projects.png new file mode 100644 index 00000000..494b6114 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/02_crossplatform/images/crossplatform_projects.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/02_crossplatform/index.md b/articles/tutorials/advanced/MobileDeployment/02_crossplatform/index.md new file mode 100644 index 00000000..898ce9a2 --- /dev/null +++ b/articles/tutorials/advanced/MobileDeployment/02_crossplatform/index.md @@ -0,0 +1,103 @@ +--- +title: "02: Setting Up Cross-Platform Projects" +description: "Learn how to convert a Windows-only MonoGame project to support iOS and Android platforms, creating a unified codebase for multi-platform deployment." +--- + +# What You'll Learn + +In this chapter you will: +- Convert a Windows-only MonoGame project to support multiple platforms +- Understand the project structure for multi-platform games +- Set up shared code architecture for cross-platform development + +# Prerequisites + +Before starting this chapter, ensure you have: + +- Read the [Building 2D Games](../building_2d_games) tutorial + +- **For iOS Development:** + - An App Store Developer Account + - **Certificates** and **Provisioning Profiles** configured and installed. + - **Mac with Xcode 16 installed** - installed through the App Store. +- **For Android Development:** + - Android SDK and tools installed - through the Visual Studio Installer or through Jetbrains Rider. + +# Converting the Dungeon Slime Project + +The Dungeon Slime game from the 2D tutorial serves as our practical example for cross-platform conversion. This approach can be applied to any MonoGame project you want to deploy across multiple platforms. + +The key principle is **code sharing** of the game logic - we'll extract the game logic into a common library that all platform-specific projects can refer to and use. + +# Cross-Platform Project Structure + +When converted to support multiple platforms, your solution structure will look like this: + +![Cross Platform Projects](images/crossplatform_projects.png) + +This architecture separates concerns cleanly: +- **Shared game logic** in a common library +- **Platform-specific shells** that handle deployment and platform requirements +- **Consistent naming** for easy project management + +## DungeonSlime.Common - The Shared Library + +The common project contains all your game logic and uses multi-targeting to support different platforms: + +```xml +net8.0;net8.0-ios;net8.0-android +``` + +## Platform-Specific Package References + +Each target framework pulls in the appropriate MonoGame package: + +For **iOS**: +```xml + + + +``` + +For **Android**: + +```xml + + + +``` + +For **Windows**: + +```xml + + + +``` + +## Platform-Specific Projects + +The individual platform projects become lightweight shells that: + +- Reference the _common_ library +- Handle platform-specific initialization +- Manage deployment settings and assets +- Contain minimal platform-specific code + +## Project Structure + +- **DungeonSlime.Windows** - Windows desktop version +- **DungeonSlime.Android** - Android mobile version +- **DungeonSlime.iOS** - iOS mobile version + +The platform projects no longer contain the main Game class - this has been moved to the common library for sharing. + +## Third-Party Library Considerations + +When using external libraries like **Gum**, ensure they support cross-platform development: + +[https://github.com/vchelaru/Gum](https://github.com/vchelaru/Gum) + +```xml + +``` diff --git a/articles/tutorials/advanced/MobileDeployment/03_touch/index.md b/articles/tutorials/advanced/MobileDeployment/03_touch/index.md new file mode 100644 index 00000000..221761ef --- /dev/null +++ b/articles/tutorials/advanced/MobileDeployment/03_touch/index.md @@ -0,0 +1,174 @@ +--- +title: "03: Touch Gesture Handling" +description: "Learn the fundamentals of touch gesture recognition in MonoGame - registration, detection, and processing." +--- + +Before proceeding with the cross-platform conversion, let's review how to handle touch input in MonoGame. The concepts discussed here apply to any MonoGame project. For our Dungeon Slime demo, touch input is managed by the Gum library, but understanding the fundamentals is still important. + +## Gesture Registration + +Before your application can respond to touch input, you must explicitly register for the gesture types you want to detect. + +This is done during initialization: + +```csharp +protected override void Initialize() +{ + TouchPanel.EnabledGestures = GestureType.Tap | + GestureType.DoubleTap | + GestureType.Hold | + GestureType.Flick | + GestureType.FreeDrag | + GestureType.HorizontalDrag | + GestureType.VerticalDrag | + GestureType.Pinch | + GestureType.DragComplete | + GestureType.PinchComplete; + + base.Initialize(); +} +``` + +### Available Gesture Types + +| Gesture Type | Description | Common Use Cases | +|--------------|-------------|------------------| +| `Tap` | Quick touch and release | Button presses, object selection | +| `DoubleTap` | Two quick taps in succession | Zoom to fit, special actions | +| `Hold` | Touch and hold for extended time | Context menus, charged actions | +| `Flick` | Quick swipe motion | Throwing objects, page navigation | +| `FreeDrag` | Continuous dragging motion | Moving objects, drawing | +| `HorizontalDrag` | Dragging constrained to horizontal | Sliders, horizontal scrolling | +| `VerticalDrag` | Dragging constrained to vertical | Vertical scrolling, pull-to-refresh | +| `Pinch` | Two-finger pinch/spread motion | Zoom in/out, scaling | +| `DragComplete` | End of any drag operation | Finalize object placement | +| `PinchComplete` | End of pinch operation | Finalize zoom level | + +> [!NOTE] +> Only register for gestures you actually need. Each enabled gesture type has a small performance cost. + +## Gesture Detection Loop + +Touch gestures are processed using a polling approach in your `Update` method. Use `TouchPanel.IsGestureAvailable` to check if gestures are waiting to be processed: + +```csharp +protected override void Update(GameTime gameTime) +{ + // Process all available gestures each frame + while (TouchPanel.IsGestureAvailable) + { + GestureSample gesture = TouchPanel.ReadGesture(); + ProcessGesture(gesture); + } + + base.Update(gameTime); +} +``` + +### Why Use a Loop? + +Multiple gestures can occur between frames, especially during complex touch interactions. The `while` loop ensures you process all queued gestures rather than missing any: + +- **Single `if` statement** - Might miss gestures if multiple occur +- **`while` loop** - Processes all queued gestures until none remain + +## Reading and Processing Gestures + +Each gesture is read using `TouchPanel.ReadGesture()`, which returns a `GestureSample` containing the gesture details: + +```csharp +void ProcessGesture(GestureSample gesture) +{ + switch (gesture.GestureType) + { + case GestureType.Tap: + HandleTap(gesture.Position); + break; + + case GestureType.Flick: + HandleFlick(gesture.Position, gesture.Delta); + break; + + case GestureType.Pinch: + HandlePinch(gesture.Position, gesture.Position2); + break; + + // Handle other gesture types... + } +} +``` + +### GestureSample Properties + +Each `GestureSample` provides different data depending on the gesture type: + +- **`GestureType`** - The type of gesture detected +- **`Position`** - Primary touch point location (screen coordinates) +- **`Position2`** - Secondary touch point (used for pinch gestures) +- **`Delta`** - Movement vector for drag and flick gestures +- **`Timestamp`** - When the gesture occurred + +### Example: Processing Different Gesture Data + +```csharp +switch (gesture.GestureType) +{ + case GestureType.Tap: + // Use: Position + Vector2 tapLocation = gesture.Position; + break; + + case GestureType.Flick: + // Use: Position (start), Delta (direction/speed) + Vector2 flickStart = gesture.Position; + Vector2 flickDirection = gesture.Delta; + break; + + case GestureType.Pinch: + // Use: Position (finger 1), Position2 (finger 2) + Vector2 finger1 = gesture.Position; + Vector2 finger2 = gesture.Position2; + break; +} +``` + +## Complete Example + +Here's a minimal working example demonstrating all three concepts: + +```csharp +public class TouchGame : Game +{ + protected override void Initialize() + { + // 1. Register for gestures + TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Flick; + base.Initialize(); + } + + protected override void Update(GameTime gameTime) + { + // 2. Check for available gestures + while (TouchPanel.IsGestureAvailable) + { + // 3. Read and process each gesture + GestureSample gesture = TouchPanel.ReadGesture(); + + switch (gesture.GestureType) + { + case GestureType.Tap: + Console.WriteLine($"Tap at {gesture.Position}"); + break; + + case GestureType.Flick: + Console.WriteLine($"Flick from {gesture.Position} with delta {gesture.Delta}"); + break; + } + } + + base.Update(gameTime); + } +} +``` + +This foundation enables you to respond to touch input across iOS and Android platforms using MonoGame's cross-platform gesture system. diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_device.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_device.png new file mode 100644 index 00000000..20f23237 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_device.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_device2.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_device2.png new file mode 100644 index 00000000..24891caa Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_device2.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_target.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_target.png new file mode 100644 index 00000000..e1f2f338 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/android_target.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_android_orientation.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_android_orientation.png new file mode 100644 index 00000000..65bd66cf Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_android_orientation.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_ipad_simulator.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_ipad_simulator.png new file mode 100644 index 00000000..1b134c23 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_ipad_simulator.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_iphone_simulator.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_iphone_simulator.png new file mode 100644 index 00000000..bc4f226a Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/game_iphone_simulator.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_debugreleasebuilds.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_debugreleasebuilds.png new file mode 100644 index 00000000..caf1a227 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_debugreleasebuilds.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_debugrun.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_debugrun.png new file mode 100644 index 00000000..6034bdfd Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_debugrun.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_devices.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_devices.png new file mode 100644 index 00000000..79fc89c1 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/ios_rider_devices.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/xcode_certificates.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/xcode_certificates.png new file mode 100644 index 00000000..8e894709 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/xcode_certificates.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/xcode_signing.png b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/xcode_signing.png new file mode 100644 index 00000000..6fe15b7e Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/images/xcode_signing.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/index.md b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/index.md new file mode 100644 index 00000000..3c33ce52 --- /dev/null +++ b/articles/tutorials/advanced/MobileDeployment/04_debugging_locally/index.md @@ -0,0 +1,33 @@ + +# Android + +![](images/game_android_target.png) + +![](images/game_android_device.png) + +![](images/game_android_device2.png) + +![](images/game_android_orientation.png) + +# iOS Signing + +XCode --> Settings + +![](images/xcode_certificates.png) + +![](images/xcode_signing.png) + +``` + + iPhone Distribution + + + + iPhone Developer + +``` + +# Debugging + +![](images/game_ios_simulator.png) + diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive.png new file mode 100644 index 00000000..1a7d8607 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive_distribute.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive_distribute.png new file mode 100644 index 00000000..30c0c509 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive_distribute.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive_signing.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive_signing.png new file mode 100644 index 00000000..28e0dff4 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_archive_signing.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_import_keystore.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_import_keystore.png new file mode 100644 index 00000000..279791b9 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_import_keystore.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_package_name.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_package_name.png new file mode 100644 index 00000000..82450b98 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_package_name.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_release.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_release.png new file mode 100644 index 00000000..2a2da6f3 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/android/android_release.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon20x20@2x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon20x20@2x.png new file mode 100644 index 00000000..0d36c4be Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon20x20@2x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon20x20@3x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon20x20@3x.png new file mode 100644 index 00000000..13e81e5b Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon20x20@3x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon29x29@2x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon29x29@2x.png new file mode 100644 index 00000000..826942c9 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon29x29@2x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon29x29@3x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon29x29@3x.png new file mode 100644 index 00000000..eef69b7e Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon29x29@3x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon40x40@2x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon40x40@2x.png new file mode 100644 index 00000000..76bf8b6c Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon40x40@2x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon40x40@3x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon40x40@3x.png new file mode 100644 index 00000000..09da383e Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon40x40@3x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon60x60@2x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon60x60@2x.png new file mode 100644 index 00000000..2a6eba37 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon60x60@2x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon60x60@3x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon60x60@3x.png new file mode 100644 index 00000000..fd8c4a04 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon60x60@3x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon76x76@2x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon76x76@2x.png new file mode 100644 index 00000000..b3ef476c Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon76x76@2x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon83.5x83.5@2x.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon83.5x83.5@2x.png new file mode 100644 index 00000000..cb03f365 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appicon83.5x83.5@2x.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appiconItunesArtwork.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appiconItunesArtwork.png new file mode 100644 index 00000000..3f789e7e Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/icons/appiconItunesArtwork.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build-assign-testers.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build-assign-testers.png new file mode 100644 index 00000000..54072691 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build-assign-testers.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build-encryption.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build-encryption.png new file mode 100644 index 00000000..01fbdcb8 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build-encryption.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build.png new file mode 100644 index 00000000..0cf50e82 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/testflight-build.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter-upload1.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter-upload1.png new file mode 100644 index 00000000..c1fc8432 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter-upload1.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter-upload2.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter-upload2.png new file mode 100644 index 00000000..9e8ebbf9 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter-upload2.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter.png b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter.png new file mode 100644 index 00000000..1de96af6 Binary files /dev/null and b/articles/tutorials/advanced/MobileDeployment/05_publishing/images/ios/transporter.png differ diff --git a/articles/tutorials/advanced/MobileDeployment/05_publishing/index.md b/articles/tutorials/advanced/MobileDeployment/05_publishing/index.md new file mode 100644 index 00000000..823c4884 --- /dev/null +++ b/articles/tutorials/advanced/MobileDeployment/05_publishing/index.md @@ -0,0 +1,419 @@ +--- +title: "Chapter 05: Publishing to iOS App Store and Google Play Store" +description: "Learn how to publish your MonoGame mobile application to both the iOS App Store and Google Play Store with step-by-step guidance and real-world insights." +--- + +# Publishing Overview + +This chapter covers the complete publishing process for both major mobile app stores. You'll learn the requirements, workflows, and common pitfalls for getting your MonoGame app successfully published on iOS and Android platforms. + +## Prerequisites + +Before publishing, ensure you have: +- Completed development and testing of your cross-platform MonoGame app +- **For iOS:** Active Apple Developer Program membership ($99/year) +- **For Android:** Google Play Developer account ($25 one-time fee) +- App icons, screenshots, and store assets prepared +- Final build configurations tested on physical devices + +--- + +# iOS App Store Publishing + +## Pre-Publishing Preparation + +### Final iOS Build Configuration + +Ensure your iOS project is properly configured for App Store submission: + +#### Project Properties +```xml +12.2 +com.companyname.gamename +AppIcon +``` + +#### Code Signing for Release + +Two entitlements - one for debug and one for publication. + +```xml + + iPhone Distribution + EntitlementsProduction.plist + +``` + +#### Production Entitlements + +Verify your `EntitlementsProduction.plist` has debugging disabled: +```xml +get-task-allow + +``` + +### Required Assets + +#### App Icons + +Ensure your `Assets.xcassets/AppIcon.appiconset` contains all required sizes: + +- **iPhone**: 40x40, 60x60, 87x87, 120x120, 180x180 +- **iPad**: 40x40, 58x58, 80x80, 152x152, 167x167 +- **App Store**: 1024x1024 + +|Asset|Image| +|-|-| +|40x40|![40x40](images/icons/appicon20x20@2x.png)| +|58x58|![58x58](images/icons/appicon29x29@2x.png)| +|60x60|![60x60](images/icons/appicon20x20@3x.png)| +|80x80|![80x80](images/icons/appicon40x40@2x.png)| +|87x87|![87x87](images/icons/appicon29x29@3x.png)| +|120x120|![120x120](images/icons/appicon40x40@3x.png)| +|120x120|![120x120](images/icons/appicon60x60@2x.png)| +|152x152|![152x152](images/icons/appicon76x76@2x.png)| +|83.5x83.5|![167x167](images/icons/appicon83.5x83.5@2x.png)| +|180x180|![180x180](images/icons/appicon60x60@3x.png)| +|Artwork 1024x1024|![1024x1024](images/icons/appiconItunesArtwork.png)| + +#### Info.plist Configuration + +Verify essential properties are set: + +The bundle identifier: + +From the csproj to match the one in the info.plist file too. + +> [!NOTE] +> Change the bundle identifier to match what was set up during the provisioning process. + +IOS CSProj: + +```xml +com.monogame.dungeonslime +``` + +Info.Plist setting: + +```xml + CFBundleIdentifier + com.monogame.dungeonslime +``` + +The game name to appear on the device: + +```xml +CFBundleDisplayName +DungeonSlime +``` + +Versioning of game: + +``` +CFBundleVersion +11 +CFBundleShortVersionString +1.10 +``` + +Typical settings for a game: + +``` +UIRequiresFullScreen + +UIStatusBarHidden + +``` + +### Asset Compilation Target + +Ensure your iOS project includes the asset compilation target: + +```xml + + + + +``` + +## App Store Connect Setup + +### Creating Your App Record + +1. **Log into App Store Connect** at [appstoreconnect.apple.com](https://appstoreconnect.apple.com) +2. **Create New App:** + - Platform: iOS + - Name: Your app's marketing name + - Primary Language: English (or your primary market) + - Bundle ID: Must match your project's `BundleIdentifier` + - SKU: Unique identifier for your records + +### App Information + +#### Required Information + +- **App Name:** Display name in the App Store +- **Subtitle:** Brief description +- **Category:** Primary and secondary categories +- **Content Rights:** Whether you own or have licensed all content + +#### App Store Listing + +- **Description:** Detailed app description +- **Keywords:** Search terms (comma-separated) +- **Marketing URL:** Your app's website +- **Privacy Policy URL:** Required for most apps + +### Screenshots and Media + +#### Required Screenshots + +You need screenshots for each device type you support: + +- **iPhone 6.7"** (iPhone 14 Pro Max): 1290 x 2796 pixels +- **iPhone 6.5"** (iPhone 11 Pro Max): 1242 x 2688 pixels +- **iPhone 5.5"** (iPhone 8 Plus): 1242 x 2208 pixels +- **iPad Pro (6th gen)**: 2048 x 2732 pixels +- **iPad Pro (2nd gen)**: 2048 x 2732 pixels + +#### App Preview Videos (Optional) + +- 30 seconds maximum +- Same dimensions as screenshots +- Shows actual app gameplay + +## Building and Uploading + +### Archive Build Process + +#### Using Command Line + +The creation of an IPA file can be achieved on the terminal. Currently, Rider does **not** support this process. + +```sh +dotnet clean +rm -rf bin/ obj/ +dotnet publish -c Release -f net8.0-ios -r ios-arm64 -p:ArchiveOnBuild=true +``` + +### Upload Method + +#### Transporter + +- Standalone upload tool from Apple + +![Transporter](images/ios/transporter.png) + +- Useful for automated workflows + +![Transporter Upload Step 1](images/ios/transporter-upload1.png) + +![Transporter Upload Step 2](images/ios/transporter-upload2.png) + +- Requires pre-signed IPA file + +### Build Processing + +After upload: + +1. **Processing Time:** 10-60 minutes typically +2. **Build Appears:** In App Store Connect under "Activity" +3. **Status Check:** Wait for "Ready to Submit" status +4. **Build Selection:** Choose the build for your app version + +![Transporter](images/ios/testflight-build.png) + +![Transporter](images/ios/testflight-build-encryption.png) + +![Transporter](images/ios/testflight-build-assign-testers.png) + +## App Review Submission + +### Pre-Submission Checklist + +- [ ] All required screenshots uploaded +- [ ] App description and metadata complete +- [ ] Privacy policy URL provided (if required) +- [ ] Content rating completed +- [ ] Pricing and availability set +- [ ] Build selected and tested +- [ ] Export compliance information provided + +### Review Information + +#### App Review Contact Information + +- First Name, Last Name +- Phone Number +- Email Address + +#### Demo Account (If Required) + +If your app requires login: +- Demo Username +- Demo Password +- Additional instructions for reviewers + +#### Notes for Review + +Additional information to help reviewers: +- Special instructions +- Feature explanations +- Known limitations + +### Submission Process + +1. **Select Build:** Choose your uploaded build +2. **Review Summary:** Check all information is correct +3. **Submit for Review:** Click "Submit for Review" +4. **Status Tracking:** Monitor in App Store Connect + +## Review Timeline and Common Issues + +### Typical Timeline + +- **Review Time:** 24-48 hours +- **Processing:** Additional time for first-time developers +- **Holiday Delays:** Longer during Apple holidays, so plan accordingly. + +--- + +# Google Play Store Publishing + +## Pre-Publishing Preparation + +### Final Android Build Configuration + +Ensure your Android project is properly configured for Google Play submission: + +#### Project Properties + +```xml +23 +com.companyname.DungeonSlime.Android +1 +1.0 +``` + +#### Activity Configuration + +Verify your main activity is properly configured: + +```csharp +[Activity( + Label = "@string/app_name", + MainLauncher = true, + Icon = "@drawable/icon", + AlwaysRetainTaskState = true, + LaunchMode = LaunchMode.SingleInstance, + ScreenOrientation = ScreenOrientation.SensorLandscape, + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | + ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize +)] +``` + +### Required Assets + +#### App Icons + +Ensure you have app icons for all density buckets: + +- **mdpi:** 48x48 px +- **hdpi:** 72x72 px +- **xhdpi:** 96x96 px +- **xxhdpi:** 144x144 px +- **xxxhdpi:** 192x192 px + +#### Feature Graphic + +- **Size:** 1024 x 500 px +- **Format:** PNG or JPEG +- **Purpose:** Featured in Google Play Store + +#### Screenshots + +Required for each supported device type: + +- **Phone:** Minimum 320px, maximum 3840px +- **7-inch Tablet:** Same requirements +- **10-inch Tablet:** Same requirements +- **Android TV:** 1920 x 1080 px +- **Wear OS:** 384 x 384 px + +### Release Build Configuration + +#### Signing Configuration + +#### Creating a Keystore +```bash +# Generate new keystore (first time only) +keytool -genkey -v -keystore your-app.keystore -alias your-key-alias -keyalg RSA -keysize 2048 -validity 10000 + +# Verify keystore +keytool -list -v -keystore your-app.keystore +``` + +**Important:** Keep your keystore file secure and backed up. Lost keystores cannot be recovered and prevent app updates. + +## Google Play Console Setup + +### Creating Your Developer Account + +**Register at** [play.google.com/console](https://play.google.com/console) + +### Creating Your App + +1. **Create App** in Google Play Console + +2. **App Details:** + - App Name: Your app's display name + - Default Language: Primary market language + - App or Game: Select "Game" for MonoGame apps + - Free or Paid: Choose pricing model + +3. **Declarations:** + - App Content: Age rating and content + - Target Audience: Age groups + - News App: Usually "No" for games + +## Building Release APK/AAB + +### Android App Bundle (Recommended) + +Google Play prefers AAB format for optimized delivery: + +```bash +# Build AAB using .NET CLI +dotnet publish -f net8.0-android -c Release + +# Or using MSBuild +msbuild YourApp.Android.csproj /p:Configuration=Release /p:AndroidPackageFormat=aab +``` + +### APK Build (Alternative) + +```bash +# Build APK +dotnet publish -f net8.0-android -c Release /p:AndroidPackageFormat=apk +``` + +![Visual Studio Archive](images/android/android_release.png) + +### Verification Steps + +Before upload, verify your build: + +```bash +# Check APK contents +aapt dump badging your-app.apk + +# Verify signing +jarsigner -verify -verbose -certs your-app.apk + +# Test installation +adb install your-app.apk +``` + +# Conclusion + +This completes the comprehensive guide to publishing your MonoGame application on both major mobile app stores. Following these workflows will help ensure successful publication and ongoing maintenance of your mobile game.