Click here to Skip to main content
Click here to Skip to main content

Port a 2D libgdx game to MonoGame

, 14 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Port your libgdx games to run in Windows Phone and Windows 8 platforms

Introduction

Libgdx is a very popular portable game engine. Developers write Java code and they can easily publish their games in Java desktop, HTML (using WebGL), Android and iOS (using RoboVM). However Windows Phone and Windows exports aren't supported. The main reason is that libgdx is an OpenGL framework. Libgdx developers have tried hard in order to create probably the most efficient OpenGL engine. This is one of libgdx's strongest points. However, Windows Phone and Windows platforms are based on DirectX. Although, there are libraries that support both OpenGL and DirectX (cocos2d-x), I believe that it is highly unlikely for libgdx to go this route.

This means that thousands of games developed with libgdx will difficulty found their way on Windows Phone and Windows platforms. This is a shame both for games, who won't find some really nice games to play and for developers, who will lose new opportunities.

In this article I will try to persuade libgdx developers that porting their games to MonoGame is far from difficult and time consuming. MonoGame is another cross-platform engine, which relies on C# language and can publish games to Android, iOS and of course Windows Phone and Windows platforms. Our aim of course will be to target Windows Phone and Windows, since other platforms are already covered by libgdx. To showcase, how easy the transition is, I am going to provide MonoGame versions of three libgdx open-source games.

MonoGame Framework

MonoGame is a cross-platform games development framework. It leverages C# programming language and supports all major platforms (for Android and iOS one needs to purchase a license). MonoGame provides an open source implementation of Microsoft's XNA framework. XNA was very popular for building Windows and XBox games, but Microsoft decided not to support Windows Phone 8 and Windows RT platforms. For these environments only native C++ development is available. MonoGame comes to fill in this gap and provide developers a way to build games with C# and the familiar XNA API.
Although hundreds of games are built with MonoGame, I would say that it is still not very mature, especially for Windows Phone 8. However, it is a very promising framework and is definitely worth getting involved with it. (While I was writing this, version 3.2 was announced. This was the first release after over one year.)

In this article I won't provide instructions for installing MonoGame and starting a new project. This information is already available elsewhere. A very informative article is posted here in Code Project. MonoGame documentation features a list of tutorials. Both free and paid resources are listed. From the free ones I have found RB Whitaker's Wiki tutorials to be considerably helpful. May I also recommend the "Windows 8 and Windows Phone 8 Game Development" book. (I have no affiliation with the publisher or the writer whatsoever.)

MonoGame for libgdx developers

MonoGame and libgdx are different frameworks. libgdx leverages Java language and uses Eclipse as an IDE. MonoGame uses C# and Visual Studio. (Free editions of Visual Studio are available. These are called Express Editions. If you want to develop both for Windows Phone 8 and Windows Store, you would need to download and install two different editions). A Windows 8 or 8.1 machine is needed for development. In order to use the Windows Phone 8 emulator, your machine must support Hyper-V technology. See here the full list of requirements.
If you are coming from a Java-Eclipse environment, I believe it would be very easy for you to migrate to C#-Visual Studio. Visual Studio is a very powerful and easy to use IDE. After the usually required getting used to time, you will feel very comfortable with it.
Java and C# are similar languages. They both have a C-language syntax and it is very easy for a Java developer to read and understand C# code. C# is a more modern language. I would say that C# features are a superset of Java features. However, you don't need to use these new features. You can start with the things that you already know from Java and adopt the new ones at your own pace. I strongly believe that a Java developer won't need more than one day before starting writing C# code.
There are many excellent C# books that will help you do the transition easier. Microsoft also offers a porting guide. This is an interesting read, but I am afraid that it doesn't offer much practical information.

Anyway, I suggest that you start writing C# code right away and search the internet, whenever you have a specific problem. You will be surprised how much Java knowledge you can re-use. At some point, you may want to read a book in order to build up your theoretical knowledge, but this isn't necessary for porting a game. In order to help you I have created a short Java-C# syntax comparison table. This table is of course not exhaustive, but with the sole aid of it, you will be able to port 90% or more of your Java code to C#. These are actually the notes I took while porting libgdx games to MonoGame.

Java - C# comparison

Packages - Namespaces
Java C#
package com.rengelbert.froggergdx;

import com.rengelbert.froggergdx.data.GameData;
import com.rengelbert.froggergdx.screens.Screen;

public class Game implements ApplicationListener {
}
using System;
using FruitCatcher.Text;
using FruitCatcher.Models;

namespace FruitCatcher
{
    public class FruitCatcherGame : ScreensGame
    {
    }
}
The equivalent to Java packages is namespaces. The syntax is different, but the IDE is your friend. When you create a class Visual Studio will add the namespace declaration and some common using statements. In Eclipse you can select "Source", "Organize Imports" in order to automatically add import statements and remove unnecessary ones. Visual Studio doesn't offer this functionality (there are plug-ins that do). The closest you can do is to right-click on a missing class at C# code and select "Resolve".
Build Path - References
Eclipse Visual Studio
In Java you add third-party libraries to the build path. In C# you add assemblies as references. You can add references to system libraries, dll files in the file system or to the output of library projects. However in Visual Studio there is a better way to add third-party libraries. You can use NuGet. With a graphical interface or a command prompt NuGet will allow you to easily integrate third-party libraries into your code. It won't only add all the necessary references, it will also make all code and configuration changes needed for you.
Extending classes. Interfaces
Java C#
public class Game implements ApplicationListener {}

public class MyGame extends BaseGame {}
public class Game : ApplicationListener

public class MyGame : BaseGame
C# uses the same syntax for extending a class and implementing an interface.
Types
Java C#
boolean

Integer.parseInt("12");

List<Integer> list = new ArrayList<integer>();
</integer>
bool

int.Parse("12");

List<int> list = new List<int>();
</int>

Instead of boolean, you need to use bool types in C#. Other primitive types in Java have the same names in C#. In C# however, there isn't the concept of primitive types. All types are objects. This makes your life easier, especially in collections.

final vs const and readonly
Java C#
public static final int GAME_STATE_PLAY = 0;
private final int ANIMATION_DURATION = 100;
public const int GAME_STATE_PLAY = 0;
private readonly int ANIMATION_DURATION = 100;

The final keyword is mapped to const or readonly in C#. This isn't an 1-1 relationship. You can read a full discussion here. For most cases the above two examples suffice.

super vs base
Java C#
public class MyExceptionClass extends Exception
{
  public MyExceptionClass(string msg, string extrainfo)
  {
    super(msg);
  }
}

public class MyExceptionClass : Exception
{
  public MyExceptionClass(string msg, string extrainfo)
                  : base(msg)
  {
  }
}


Instead of super you use base to access parent class methods. The constructor syntax is different.

getters and setters vs Properties
Java C#
class TimePeriod
{
    private double seconds;

    public double getHours() { 
        return seconds / 3600; 
    }
    
    public void setHours(double hours){
        seconds = hours * 3600;
    }
}
class TimePeriod
{
    private double seconds;

    public double Hours
    {
        get { return seconds / 3600; }
        set { seconds = value * 3600; }
    }
}

Java uses getters and setters for passing values between classes. C# uses Properties. You don't need to modify your code to use Properties, as getters and setters pattern works fine with C# as well. However, you absolutely need to understand the Properties concept, as it is heavily used by System libraries and MonoGame framework.

Annotations vs Attributes
Java C#
import com.google.gson.annotations.SerializedName;

public class PurchaseOrder
{
    @SerializedName("purchaseId")
    private int poId_value;
}
[DataContract]
public class PurchaseOrder
{
    private int poId_value;

    [DataMember]
    public int PurchaseOrderId
    {
        get { return poId_value; }
        set { poId_value = value; }
    }
}

The equivalent to Java Annotations is C# Attributes. The syntax is different, but the idea is the same. You annotate a class or a field to add a desired behavior. Another difference is the @Override annotation, which is replaced by override and virtual keywords in C#.

Code conventions
Java C#
public class A {
    public void methodName() {
    }
}

line.trim().length() 
list.size()
list.add(item)
public class A 
{
    public void MethodName()
    {
    }
}

line.Trim().Length
list.Count // Property
list.add(item)

Java and C# use different coding styles. C# encourages every curly brace to be in a new line. Also capitalization styles are slightly different. Perhaps the most important difference is that method names use Pascal case (first letter capitalized) in C#. You don't need to follow these conventions, while porting your code. However, this information will help you understand system and framework libraries.

Collections
Java C#
List<String> l = new ArrayList<string>();
Map<string,> m = new HashMap<string,>();

l.size()
l.add(item), l.remove(item)
l.get(i)
</string,>
List<String> l = new List<string>();
Dictionary<string,> m = new Dictionary<string,>();

l.Count  // Property
l.Add(item), l.Remove(item)
l[i]       // Operation overloading
</string,>

In C# we usually don't use interfaces for the definitions of collections. General interfaces (IEnumerable, ICollection) do exist however. A List maps to List and a Map to Dictionary in C#. Most methods have the same name in C#, with the only difference that the first letter is capitalized. This will make porting easier for you. C# supports operation overloading, so instead of get(i) to retrieve an item, we can use array syntax [i].

for-each loop
Java C#
for(String s: list) {
}
foreach(String s in list)
{
}

In C# you use foreach statement to implement a for-each loop.

enum types
Java C#
public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);

    private final double mass;   // in kilograms
    private final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    private double mass() { return mass; }
    private double radius() { return radius; }

    // universal gravitational constant  (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;

    double surfaceGravity() {
        return G * mass / (radius * radius);
    }
    double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println(
        "Usage: java Planet <earth_weight>");
            System.exit(-1);
        }
        double earthWeight = Double.parseDouble(args[0]);
        double mass = earthWeight/EARTH.surfaceGravity();
        for (Planet p : Planet.values())
           System.out.printf(
               "Your weight on %s is %f%n",
               p, p.surfaceWeight(mass));
    }
}
public enum Planet {
    MERCURY = 0,
    VENUS,
    EARTH,
    MARS,
    JUPITER,
    SATURN,
    URANUS,
    NEPTUNE
}

public static class PlanetExtensions {
    private static double [] masses = {
        3.303e+23, 4.869e+24, 5.976e+24, 6.421e+23,
        1.9e+27, 5.688e+26, 8.686e+25, 1.024e+26};
        
    private static double [] radii = {
        2.4397e6, 6.0518e6, 6.37814e6, 3.3972e6,
        7.1492e7, 6.0268e7, 2.5559e7, 2.4746e7};    

    public static double Mass(this Planet planet)
    {
        return masses[(int) planet];
    }
    
    public static double Radius(this Planet planet)
    {
        return radii[(int) planet];
    }    
}

Console.WriteLine(Planet.MARS.Mass().ToString());

enum types are perhaps the only language feature that Java offers more advanced functionality than C#. You can't have constructors or methods in an enum. Also only integer types are allowed. You can use extension methods to add the missing functionality.

As, I've said before the above list is far from complete. Other changes include that C# has no checked exceptions, offers struct and nullable types, supports closures and many functional programming features, like LINQ. You want need all these to get started and you can learn everything in a later phase.

Game loop

The libgdx engine implements the game loop with an ApplicationListener interface. On top of this, developers usually leverage a Game and Screens pattern. In MonoGame the game loop is implemented by overriding the Game class. The two implementations are compared below.

libgdx MonoGame
public class Drop 
      implements ApplicationListener {

   @Override
   public void create() {
      // load the images and sound effects 

      // start the playback of the 
      // background music

      // create resources
   }

   @Override
   public void render() {
      // update      

      // draw      

      // process user input
   }

   @Override
   public void dispose() {
      // dispose of all the native resources
   }

   @Override
   public void resize(int width, int height) {
   }

   @Override
   public void pause() {
   }

   @Override
   public void resume() {
   }
}
public class FroggerGame : Game

  public FroggerGame()
  {
    _graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";      
  }


  /// <summary>
  /// Allows the game to perform any initialization it 
  /// needs to before starting to run.
  /// This is where it can query for any required 
  /// services and load any non-graphic
  /// related content.  
  /// </summary>
  protected override void Initialize()
  {
    // TODO: Add your initialization logic here

    base.Initialize();
  }

  /// <summary>
  /// LoadContent will be called once per game and 
  /// is the place to load all of your content.
  /// </summary>
  protected override void LoadContent()
  {          

  }

  /// <summary>
  /// UnloadContent will be called once per game and 
  /// is the place to unload all content.
  /// </summary>
  protected override void UnloadContent()
  {
    // TODO: Unload any non ContentManager content here
  }

  /// <summary>
  /// Allows the game to run logic such as updating 
  /// the world, checking for collisions, gathering
  /// input, and playing audio.
  /// </summary>
  /// <param name="gameTime" />Provides a snapshot of 
  /// timing values.
  protected override void Update(GameTime gameTime)
  {
    base.Update(gameTime);      
  }

  /// <summary>
  /// This is called when the game should draw itself.
  /// </summary>
  /// <param name="gameTime" />Provides a snapshot of
  /// timing values.
  protected override void Draw(GameTime gameTime)
  {

  }
}

As you can see there are many similarities. There are methods that call code prior to the game start (Initialize). There are methods that run once a game and load all necessary content files (create - LoadContent). And there are methods that run once a frame (render - Update, Draw). MonoGame splits the render method in Update and Draw. The first one is for updating the game's state and the second one for drawing it. Most experienced developers do that in libgdx as well. However, MonoGame goes one step further by being able to call Update and Draw in different intervals. MonoGame will call Update 60 times per second (this is the default frame rate - FPS) and it will try to call Draw as many times as possible. This guarantees that the game state is consistent, even if the frame rate drops dramatically. In libgdx you would need to count the frame rate and skip the drawing part during a render, if the frame rate is too low.

Another obvious difference is that MonoGame doesn't support lifecycle events (pause, resume). MonoGame originates from XNA, which mainly targeted the desktop and console world. These lifecycle events haven't a meaning in these platforms and weren't introduced in MonoGame. There are however an important aspect of the mobile game development. I will show you later how to write native code for Windows Phone 8 in order to add them in your game.

Content Loading

The following table depicts how content is loaded in the two frameworks.

libgdx MonoGame
Texture dropImage;
Texture bucketImage;
Sound dropSound;
Music rainMusic;
BitmapFont font;

@Override
public void create() {
  dropImage = 
    new Texture(Gdx.files.internal("droplet.png"));
  bucketImage = 
    new Texture(Gdx.files.internal("bucket.png"));
  dropSound = 
    Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
  rainMusic = 
    Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
  font = new BitmapFont(
    Gdx.files.internal("fonts/poetsen.fnt"),
    Gdx.files.internal("fonts/poetsen.png"), 
    false);
}
private Texture2D background;
private SpriteFont font;
private Song music;
private SoundEffect jumpSound;

/// <summary>
/// LoadContent will be called once per game and is the 
/// place to load all of your content.
/// </summary>
protected override void LoadContent()
{
  background = 
    loadTexture(content, "data/background");
  font = 
    content.Load("data/Miramonte");
  jumpSound = 
    content.Load<SoundEffect>("data/jump");
  music = 
    content.Load<Song>("data/music");
}

The code is similar, however on contrary to libgdx, MonoGame can't load assets directly. Images, sounds and fonts need to be first processed and converted to a specific format. This is currently achieved through an XNA content project. For more details I suggest that you read this tutorial. The process is more complicated than it should be and it requires that you install Visual Express 2012 for Windows Phone (or the Windows Phone SDK, if you have the Professional Edition), even if you don't need to build for Windows Phone. Hopefully, MonoGame will evolve to fully support the Content Pipeline. (While I am writing this MonoGame 3.2 was released. One of the new features is: Tons of content pipeline improvements making it close to complete.)

2D Drawing

The main artifact for 2D drawing in both libgdx and MonoGame is SpriteBatch (libgdx, MonoGame). As you can see below the code is very similar. You call the begin method and then draw for each texture you want to render on the screen. In both versions you can have multiple begin-end loops with different parameters. In libgdx you use batch.setXXX methods to parameterize. In MonoGame you pass the parameters in batch.Begin(). (The parameters aren't one-to-one equivalent.)

libgdx MonoGame
SpriteBatch batch;
OrthographicCamera camera;

@Override
public void render() {
  Gdx.gl.glClearColor(0, 0, 0.2f, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

  camera.update();
  batch.setProjectionMatrix(camera.combined);

  batch.begin();
  batch.draw(bucketImage, bucket.x, bucket.y);
  for(Rectangle raindrop: raindrops) {
     batch.draw(dropImage, raindrop.x, raindrop.y);
  }
  batch.end();
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime" />Provides a snapshot 
/// of timing values.
protected override void Draw(GameTime gameTime)
{
  GraphicsDevice.Clear(Color.CornflowerBlue);
  
  batch.Begin();
  Vector position = new Vector2(0, 0);
  batch.Draw(bgTexture, position, Color.White);
  batch.End();
  
  base.Draw(gameTime);
}

The draw methods in both frameworks come in different flavors. Depending on what you want to achieve, you select one of them. It would be difficult to present all the different versions here. However, because these methods are the core of 2D drawing, I would at still like to compare the two most complete versions. The interfaces of these are presented below.

libgdx MonoGame
/** Draws a rectangle with the bottom left corner at x,y having the given width and height in pixels. The rectangle is offset by originX, originY relative to the origin. Scale specifies the scaling factor by which the rectangle should be scaled around originX, originY. Rotation specifies the angle of counter clockwise rotation of the rectangle around originX, originY. The portion of the {@link Texture} given by srcX, srcY and srcWidth, srcHeight is used. These coordinates and sizes are given in texels. FlipX and flipY specify whether the texture portion should be flipped horizontally or vertically.
* @param x the x-coordinate in screen space
* @param y the y-coordinate in screen space
* @param originX the x-coordinate of the scaling and rotation origin relative to the screen space coordinates
* @param originY the y-coordinate of the scaling and rotation origin relative to the screen space coordinates
* @param width the width in pixels
* @param height the height in pixels
* @param scaleX the scale of the rectangle around originX/originY in x
* @param scaleY the scale of the rectangle around originX/originY in y
* @param rotation the angle of counter clockwise rotation of the rectangle around originX/originY
* @param srcX the x-coordinate in texel space
* @param srcY the y-coordinate in texel space
* @param srcWidth the source with in texels
* @param srcHeight the source height in texels
* @param flipX whether to flip the sprite horizontally
* @param flipY whether to flip the sprite vertically */
public void draw (Texture texture, 
    float x, 
    float y, 
    float originX, 
    float originY, 
    float width, 
    float height, 
    float scaleX,
    float scaleY, 
    float rotation, 
    int srcX, 
    int srcY, 
    int srcWidth, 
    int srcHeight, 
    boolean flipX, 
    boolean flipY)

// A texel is a pixel from the texture.
// Overload for calling Draw() with named parameters
/// <summary>
/// This is a MonoGame Extension method for calling Draw() using named parameters. It is not available in the standard XNA Framework.
/// </summary>
/// <param name='texture'>
/// The Texture2D to draw. Required.
/// </param>
/// <param name='position'>
/// The position to draw at. If left empty, the method will draw at drawRectangle instead.
/// </param>
/// <param name='drawRectangle'>
/// The rectangle to draw at. If left empty, the method will draw at position instead.
/// </param>
/// <param name='sourceRectangle'>
/// The source rectangle of the texture. Default is null
/// </param>
/// <param name='origin'>
/// Origin of the texture. Default is Vector2.Zero
/// </param>
/// <param name='rotation'>
/// Rotation of the texture. Default is 0f
/// </param>
/// <param name='scale'>
/// The scale of the texture as a Vector2. Default is Vector2.One
/// </param>
/// <param name='color'>
/// Color of the texture. Default is Color.White
/// </param>
/// <param name='effect'>
/// SpriteEffect to draw with. Default is SpriteEffects.None
/// </param>
/// <param name='depth'>
/// Draw depth. Default is 0f.
/// </param>
public void Draw (Texture2D texture,
        Vector2? position = null,
        Rectangle? drawRectangle = null,
        Rectangle? sourceRectangle = null,
        Vector2? origin = null,
        float rotation = 0f,
        Vector2? scale = null,
        Color? color = null,
        SpriteEffects effect = SpriteEffects.None,
        float depth = 0f)

These two methods have similar names and you should be able to map one to another. MonoGame prefers to pack the parameters in Vector2 and Rectangle objects, whereas in libgdx we have separate parameters. In libgdx we do flipping through the flipX and flipY parameters. In MonoGame the SpriteEffects is used. In libgdx you achieve a color tinting effect by calling the SpriteBatch.setColor method prior to calling draw. In MonoGame the color is a parameter in the Draw method itself.

Note: The syntax Rectangle? denotes a nullable type. A Rectangle is a struct and as such a value type. This means that similar to an int for example, it should always have a value - null isn't allowed. On the contrary, you are allowed to set a null value to Rectangle? and to int?.

Although the 2D drawing samples look similar, there are actually important differences on how the two frameworks handle 2D drawing. The ones with the most practical implications are:

  • MonoGame doesn't support a TextureRegion. In libgdx you can create a TextureRegion (part of a bigger image) either directly or through a TextureAtlas. Also, special tooling is provided that helps you pack a lot of images into a single one and generate a positions and dimensions document that can be used by libgdx directly. The draw method can accept as input both a Texture and a TextureRegion. In MonoGame you need to do all of the above manually. You need to pass to the Draw method the sourceRectangle parameter, which defines what part of the image to draw. Porting of TextureRegion and TextureAtlas to MonoGame isn't difficult. I am going to provide code that will allow you to use the same packed image generated by libgdx tools.
  • MonoGame and libgdx use different coordination systems. In libgdx axis y starts from the bottom left and goes up. In MonoGame it starts from top left and goes down. This is very unfortunate as you will need to make all your positions calculations from the beginning. An easy solution to the problem would be to create a wrapper around the Draw method, which will take into consideration the coordination systems difference and will do the conversion automatically.
  • Finally, the most important difference is that in libgdx the SpriteBatch can take a Camera as an input parameter. MonoGame doesn't associate a camera with 2D drawing. This is an important omision as in libgdx the camera is heavily used in 2D games. The most common use of it is to adapt the game world to the screen dimensions. You can do all the work with fixed viewport dimensions and the camera will do the resizing to fit the device's screen. Input positions can also easily be translated to the fixed dimensions. In MonoGame you use other techniques to achieve the same thing. One simple technique that involves the DrawingSurface is described in a great Code Project MonoGame article that I have already mentioned. Another solution is described here. The camera is also used to implement various events, like Parallax scrolling. In the Super Jumper game it is used in order to create the illusion of the character moving up. Catering for the lack of the camera was the biggest challenge I faced, while porting the code. The solution I used was to add all the camera logic into the wrapper around the Draw method. The wrapper now also needs to know the screen dimensions in order to correctly scale and place the images. For the Super Jumper camera effect I only needed to add a camera position. Images are drawn relative to the camera position.

LibgdxHelper Library

When I had the idea of porting libgdx games to MonoGame, my ambition was to create constructs, so that there as little code differences as possible. The idea is that someone will be able to port a game quickly, without having to edit images and other assets or re-calculate positioning on the screen. In order to implement this idea, I have created a library project, called LibgdxHelper that provides classes and methods similar to libgdx. What the library provides can been seen in the table below, that compares libgdx and MonoGame versions of the same code. The code looks surprisingly similar.

libgdx MonoGame
// Loading images
public static Texture loadTexture (String file) {
  return new Texture(Gdx.files.internal(file));
}

public static void load () {
  background = loadTexture("data/background.png");
  backgroundRegion = new TextureRegion(background, 0, 0, 320, 480);

  items = loadTexture("data/items.png");
  mainMenu = new TextureRegion(items, 0, 224, 300, 110);
  pauseMenu = new TextureRegion(items, 224, 128, 192, 96);
  ready = new TextureRegion(items, 320, 224, 192, 32);

// Drawing images
private void renderPlatforms () {
  int len = world.platforms.size();
  for (int i = 0; i < len; i++) {
    Platform platform = world.platforms.get(i);
    TextureRegion keyFrame = Assets.platform;
    if (platform.state == Platform.PLATFORM_STATE_PULVERIZING) {
        keyFrame = Assets.brakingPlatform.getKeyFrame(
            platform.stateTime, Animation.ANIMATION_NONLOOPING);
    }

    batch.draw(keyFrame, 
        platform.position.x - 1, platform.position.y - 0.25f,
        2, 0.5f);
  }
}




// Loading images
public static Texture2D loadTexture(ContentManager content, String file) {
  return content.Load<Texture2D>(file);
}

public static void load(ContentManager content)
{
  background = loadTexture(content, "data/background");
  backgroundRegion = new TextureRegion(background, 0, 0, 320, 480);

  items = loadTexture(content, "data/items");
  mainMenu = new TextureRegion(items, 0, 224, 300, 110);
  pauseMenu = new TextureRegion(items, 224, 128, 192, 96);
  ready = new TextureRegion(items, 320, 224, 192, 32);

// Drawing images
private void renderPlatforms() 
{
  int len = world.platforms.Count;
  for (int i = 0; i < len; i++)
  {
    Platform platform = world.platforms[i];
    TextureRegion keyFrame = Assets.platform;
    if (platform.state == Platform.PLATFORM_STATE_PULVERIZING)
    {
        keyFrame = Assets.brakingPlatform.getKeyFrame(
            platform.stateTime, Animation.ANIMATION_NONLOOPING);
    }

    spriteWorld.DrawRegion(keyFrame,
                    platform.position.X - 1, platform.position.Y - 0.25f,
                    2, 0.5f);
  }
}

Note: Although LibgdxHelper library has worked well for two projects, it is by no means in a state ready to be re-used by any project. It still has several omisions that make it unsuitable for the general case. However, I believe that it is a very good starting point and you would be able to add the missing parts easily.

The first thing that LibGdxHelper offers is implementations of TextureRegion and TextureAtlas. These are ported directly from libgdx's source code. However, only the absolute necessary parts were migrated. The provided TextureRegion class is simply a placeholder of a region information (position and size inside a packed image) and a reference to the actual texture. This is still enough to allow us to create a Draw method that accepts as an argument and correctly draws a part of an image. The TextureAtlas class can read the same information file created by libgdx's image packing tools. It maintains a list of TextureRegions that can be accessed the same way as in libgdx. This allows us to reuse the same packed image and the same information file. This is a great time saver. In fact, I believe that with some performance optimizations this solution could also be applied to new MonoGame projects as well. The use of these classed in MonoGame is presented below:

// Create a TextureRegion out of Texture
items = content.Load<Texture2D>("data/items");
mainMenu = new TextureRegion(items, 0, 224, 300, 110);
pauseMenu = new TextureRegion(items, 224, 128, 192, 96);
ready = new TextureRegion(items, 320, 224, 192, 32);

// Use a TextureAtlas to create TextureRegions
public class ImageCache
{
    public bool IsLoaded { get; private set; }

    public static Texture2D sheet;
    public static TextureAtlasData atlas;

    public ImageCache()
    {
        IsLoaded = false;
    }

    public async Task Load(ContentManager Content) 
    {
        sheet = Content.Load<Texture2D>("frogger");

        atlas = new TextureAtlasData();
        // frogger.txt is the file created by libgdx
        // It must be added to the project as Content
        await atlas.Load("Data\\frogger.txt", sheet);

        IsLoaded = true;
    }

    // The result of this should be cached and not called every time
    public static TextureRegion getTexture(String name)
    {
        return atlas.FindRegion(name);
    }

    // The result of this should be cached and not called every time
    public static TextureRegion getFrame(String name, int index)
    {
        return atlas.FindRegion(name, index);
    }
}

Note: I have made no performance considerations for LibgdxHelper. My goal was speed of development. For the simple games I will present the performance was more than enough. This may not be the case for more complex games.

The second artifact offered tries to solve the other two main projects described before (different coordination systems, lack of Camera). It is called SpriteBatch and it basically creates a smart wrapper around the Draw method. In order to work SpriteBatch needs to know the dimensions of the device's screen. This is achieved by passing the GraphicsDevice object of the Game at the constructor. It also needs to know the dimensions of the base view port, where are calculations have been made. This is achieved by calling the SpriteBatch.Resize(float width, float height) method. Finally, it can accept an OrthographicCamera object. This emulates an orthographic camera in libgdx. The implementation simply encapsulates the camera position (Vector2) and the camera frustum (Vector3 dimensions). This information allows the SpriteBatch to provide Draw methods that operate both on Texture2D and TextureRegion objects with signatures and functionality equivalent to libgdx draw methods. This what it makes the two code samples presented above look so similar. SpriteBatch also offers a method that translates a touch point on the screen to the game's viewport. The method is called GetWorldPos(Vector2 screenPos) and it is the equivalent of libgdx's camere.unproject(Vector3 touchPos).

Note: I have only tested the OrthographicCamera operation in the context of Super Jumper game. No guarantees are made that it will work in the general case.

Common Tasks

Drawing on the screen isn't the only thing you need in order to create a game. I am going to present here a list of common game development tasks and how are implemented in MonoGame. I won't get into much detail about every task. I am instead going to give you enough information for getting started.

Detecting User Input

MonoGame offers great support for detecting user input. It supports multi-touch events and gesture recognition. The code snippet below shows how to detect a touch on the screen.

TouchCollection touches = TouchPanel.GetState();
if (touches.Count == 1 && touches[0].State == TouchLocationState.Released)
{
    touchPoint = game.SpriteWorld.GetWorldPos(touches[0].Position);

    if (backBounds.Contains(touchPoint.X, touchPoint.Y))
    {
        Assets.playSound(Assets.clickSound);
        game.Screen = new MainMenuScreen(this.game);
    }
}

Working with bitmatp fonts

Bitmap fonts are loaded in MonoGame through the content project. You don't need a special tool for this. With the content project you can create an XNB file out of every font installed in your system (you should pay attention to the license, when using a font). After you have created an XNB file you can easily load it in your code:

SpriteFont font = Content.Load<SpriteFont>("Miramonte");

And then in the Draw method:

spriteBatch.DrawString(Assets.font, "text", new Vector2(x, y), Color.Orange); 

For more details I refer you to this StackOverflow question.

Storing user preferences

This is platform dependent. In Windows Phone 8 you will use IsolatedStorageSettings. In Windows 8 ApplicationData.LocalSettings is used. You can find an example of this in the Settings class of Superjumper application.

Reading and writing files

The methods below, taken from FruitCatcher's HighScoreManager class, demonstrate, how you can serialize and deserialize data in files for the Windows Phone 8 platform. The code for Windows 8 is very similar.

private async Task RetrieveHighScores() {
  try
  {
    StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    StorageFile file = await localFolder.GetFileAsync(SCORES_DATA_FILE);

    var inStream = await file.OpenStreamForReadAsync();
    DataContractSerializer serializer = 
        new DataContractSerializer(typeof(List<HighScore>));
    HighScores = (List<HighScore>)serializer.ReadObject(inStream);
    inStream.Close();
  }
  catch (Exception ex)
  {
    Debug.WriteLine(ex.Message);
  }
}

private async Task Persist() {
  try
  {
    StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    StorageFile file = await localFolder.CreateFileAsync(SCORES_DATA_FILE,
                CreationCollisionOption.ReplaceExisting);
    var outStream = await file.OpenStreamForWriteAsync();
    DataContractSerializer serializer = 
        new DataContractSerializer(typeof(List<HighScore>));
    serializer.WriteObject(outStream, HighScores);
    await outStream.FlushAsync();
    outStream.Close();
  }
  catch (Exception ex)
  {
    Debug.WriteLine(ex.Message);
  }
}

If you have used JSON serialialization in libgdx, the procedure should be familiar to you. In order for the above code to work, you first have to annotate the HighScore class with the DataContract and DataMember attributes. One thing that it may be difficult to understand for Java developers is the asynchronous programming with async and await. The use of asynchronous methods for any long running task (file access, networking) is mandatory in Windows Phone and Windows. I recommend that you study how these two keywords work, before trying to use them.

Lifecycle events

As I've already said MonoGame doesn't support lifecycle events. You need to add these yourself in every platform that you plan to support. You can find an example of how you can do this for Windows Phone 8 in FruitCatcher application. What is needed, is to leverage the events provided in App.xaml.cs file.

// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
    if (Game != null)
    {
        Game.Resume();
    }
}

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    if (Game != null)
    {
        Game.Pause();
    }
}

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
    if (Game != null)
    {
        Game.Dispose();
    }
}

In the App class I have added a Game property, which holds a reference to the implemented game. In this game I have added the Resume, Pause and Dispose methods. These methods have similar meaning are their counterparts in libgdx. In the GamePage.xaml.cs, where the game is created, I pass the reference to the App class:

_game = XamlGame<FruitCatcherGame>.Create("", this);
((App) App.Current).Game = _game;

For more details you can consult FruitCatcher's source code. In general integration with native code isn't difficult in MonoGame, but you should invest some time on learning XAML and the platform internals.

Debug logging

You can write debug output to the console with System.Diagnostics.Debug.WriteLine(). The good thing is that this method supports string format parameters.

What else

The above list is by no means complete. I plan to add more things in the future. I feel however that I also need to mention, what is not supported. If you have used Scene2d in your project, you will find it difficult to port it to MonoGame. Of course MonoGame doesn't support it and it would be very difficult to do the migration yourselves. Also, what it isn't mentioned here is 3D games.

Use Cases

As I promised in the beginning of the article, no less than three libgdx games have been converted to MonoGame. For my first game porting, I of course spent a considerable amount of time. This was because I needed to discover MonoGame and develop the porting techniques. The second and third game were ported really fast. I hope that this article and the provided source code will help you to do a quick migration your games.

Frogger

Frogger

Frogger is a simple game ported to libgdx and other game frameworks by Roger Engelbert. I was happy to do the porting to MonoGame. Both a Windows Store and a Windows Phone 8 versions are available.

SuperJumper

Super Jumper

Super Jumper is one of the most well known libgdx sample games. It is a "Doodle Jump" like game, where you guide Bob up by jumping on platforms. In keyboard systems you use the left and right arrow keys, while on phones the accelerometer is used. Super Jumper is a popular sample and it is usually selected by library vendors to showcase their integration with libgdx. As such, I believe that it was a good choice to demonstrate the porting from libgdx to MonoGame. It also helped me to find out a way to circumvent the lack of a camera in 2D drawing for MonoGame. Both a Windows Store and a Windows Phone 8 versions are available.

Fruit Catcher

Fruit Catcher is another simple libgdx game that I have created and presented here in Code Project. This was my first attempt of porting a libgdx game to MonoGame and it is also published in the Windows Phone Store. Fruit Catcher doesn't use the LibgdxHelper library described before, but similar constructs. It is however the most complete sample application, so I believe that it is worth going through its code. It specifically demonstrates how MonoGame interacts with native code. It also integrates Google's AdMob ads. (However, these are still not working properly for me.) Only a version for Windows Phone 8 is provided.

Summary

This was a long article indeed, but my intention was to write not just one-time-read article, but one that could also be used as a porting reference. I really hope that you would find it useful and that it would inspire you to port your libgdx games to MonoGame. Happy porting!

History

  • First Version: 14th April 2014

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Giannakakis Kostas
Software Developer (Senior) Self employed
Greece Greece
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberFranc Morales4-Dec-14 12:28 
GeneralExcellent - 5 Stars! PinprofessionalDan Colasanti17-Apr-14 3:57 
GeneralRe: Excellent - 5 Stars! PinmemberGiannakakis Kostas17-Apr-14 22:13 
GeneralRe: Excellent - 5 Stars! PinprofessionalDan Colasanti18-Apr-14 6:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141220.1 | Last Updated 14 Apr 2014
Article Copyright 2014 by Giannakakis Kostas
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid