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
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
l.Add(item), l.Remove(item)
l[i]
</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;
private final double radius;
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
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() {
}
@Override
public void render() {
}
@Override
public void dispose() {
}
@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";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
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;
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();
}
|
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 |
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)
|
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
<code>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 |
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);
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);
}
}
|
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);
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:
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);
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();
await atlas.Load("Data\\frogger.txt", sheet);
IsLoaded = true;
}
public static TextureRegion getTexture(String name)
{
return atlas.FindRegion(name);
}
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.
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (Game != null)
{
Game.Resume();
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
if (Game != null)
{
Game.Pause();
}
}
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 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 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