docsReading time: 17 minutes
General
Basics
- Getting Started
- Game API
- Input API
- Configuration
- Tile Maps
- Resource Management
- Entity Framework
- Control Entities
- User Interface
- utiLITI
- Deployment
- Savegames
- Libraries and Tools
- Glossary
Advanced
- The Particle System
- Dynamic Lighting
- Static Lighting
- Performance Optimization
- Custom MapObjectLoaders
- String Localization
- Object Serialization
- Utility Classes
- Network Communication
- Advanced Entity Knowledge
Tutorials
Creating a platformer with LITIENGINE
So, you’ve learned about LITIENGINE – great!
You’ve created a project in eclipse and imported LITIENGINE.
If not, check out our Download Page that comes with detailed installation instructions.
Now you’ve probably come to the point where you’ve asked yourself:
“How am I supposed to actually make a game with this?”
In this How-To-Series, we guide you through the steps necessary for creating a platformer game inspired by the old Duke Nukem games, made in a glorious four-colour gameboy look.
The this project is entirely open sourced (under the MIT license). You can find the code among all the assets in the GitHub repository for Gurk Nukem.

Each blog chapter will represent the work that can be done in one day by following our step-by-step explanations.
Chapter 1: The first Level
Creating a project in utiLITI
For a general idea about what utiLITI is, read the docs page about utiLITI first.
Start the utiLITI editor. Hit “File -> New…” (CTRL+N)

In the file browser that pops up, navigate to your project directory and hit “Open“.
Even if you still have done nothing, hit “File -> Save…” (CTRL+S) to create a game resource file.
You can name it whatever you want, we go for something like “game.litidata” in this guide.
Importing Assets
Let’s assume that you’ve already created assets for your game. In our case, we picked a 4-colour GameBoy style for our project.
So what basic Assets do we have at this point?
Player sprites:
For entities that are child classes of Creature, there are naming conventions for walking and idle sprites.
We need sprites named like {SPRITE_PREFIX}-{STATE}-{DIRECTION}.{EXTENSION}.
Following these naming conventions, we create gurknukem-idle-left.png and gurknukem-walk-left.png.
You only need to provide a left or a right sprite in both cases, the CreatureAnimationController will automatically flip it when necessary.

gurknukem-walk-left.png
Enemy sprites:
We also add two enemy designs to the game. The naming is done in the same way as for our main character, so we end up with four image files: dean-idle-left.png, dean-walk-left.png, jorge-idle-left.png, and jorge-walk-left.png.

dean-walk-left.png

jorge-walk-left.png
Prop sprites:
For now, we will only add two props: a bunker and a destructive barrel.
For props, there are naming conventions, too:
prop-{PROPNAME}-{STATE}.{EXTENSION}
This means that we need three image files for our destructible barrel:
prop-barrel-intact.png, prop-barrel-damaged.png, and prop-barrel-destroyed.png
Non-destructible props such as our bunker only need one animation file, in our case prop-bunker.png.

prop-bunker.png

prop-barrel.png
Our game logo:

- Our game icon:
![]()
We also need:
- A music track
- A .tsx tileset created with Tiled editor from an image file
- A .tmx map created with Tiled editor using our Tileset
To import Sprite files into utiLITI, either drag&drop selected files into the Asset panel on the lower end of the screen or hit “Resources -> Import Spritesheets…“. The same goes for tilesets (“Resources -> Import Tilesets…“). Maps can be imported by clicking “Map -> Import…“. If a map was successfully imported, it will be rendered in the map panel and listed in the map list right to it.

Basic mapping
Adjust your grid and snapping settings in the “View” menu.
In the Layer toolbox in the upper right corner, you can Add, Delete, Recolor, Rename, Duplicate, Focus / Hide, and reorder MapObjectLayers.
To maintain a comprehensive structure to our map files, we try to create at least one layer for each Entitytype (There is the “Entities” tab to help you navigate through entities by type, as well).
Let us now create your first Entity!
Select a MapObjectLayer, then right-click somewhere on the map and hit “Add -> Add CollisionBox“.
Click and drag with your mouse to specify the bounding box of your Collision box as shown in the gif below.

In the same manner, create a Spawnpoint that will be used to spawn our player entity.
Edit the Spawnpoint’s Attributes in the Map Object Panel on the right as shown below.
You can give the Spawpoint a name for identification and set the spawn direction for our Player.

Before we look into coding now, we will set some basic properties for your map such as its name, description, and static shadow color.
Simply hit “Map -> Properties” and you will see a popup for editing Map properties.

Congratulations! You’ve successfully completed the first steps on your way to creating a platforming shooter with LITIENGINE.
Learn about setting up the basic functionality of our game in the next chapter.
Chapter 2: Time to pull out your hacker gloves
Now that you’ve created a first test map for your platforming game, we will show you how to load the map into your game, how to configure and spawn a Player entity, and how to implement basic gravity and jumping behaviours.
Our Main Class
Let’s start today by opening up the Eclipse IDE (as it is the IDE of choice for this how-to-series).
At this point, we assume that you’ve already created a Java project and referenced LITIENGINE in it as described HERE.
Also, you have created resource folders and added them to your project’s build path.
In our case, there are three resource folders: ‘maps’, ‘sprites’, and ‘audio’, which are located in the root folder of our project.
We proceed with creating a class containing our main-method to actually run our program like in the example shown below:
public class Program {
public static void main(String[] args) {
// set meta information about the game
Game.info().setName("GURK NUKEM");
Game.info().setSubTitle("");
Game.info().setVersion("v0.0.1");
Game.info().setWebsite("https://github.com/gurkenlabs/litiengine-gurk-nukem");
Game.info().setDescription("An example 2D platformer with shooter elements made in the LITIENGINE");
// init the game infrastructure
Game.init(args);
// set the icon for the game (this has to be done after initialization because the ScreenManager will not be present otherwise)
Game.window().setIcon(Resources.images().get("icon.png"));
Game.graphics().setBaseRenderScale(4f);
// load data from the utiLITI game file
Resources.load("game.litidata");
// load the first level (resources for the map were implicitly loaded from the game file)
Game.world().loadEnvironment("level1");
Game.start();
}
}- First, we set some basic information for our game such as its name,
subtitle, version, website, and description by using the respective
setters ofGame.info(). - Calling
Game.init()is then needed to initialize the Game
infrastructure (see the [Javadocs][] for an in-depth understanding
of what this does). - Next, we set the window icon from the logo we created in Day 1 by
callingGame.window().setIcon(). Fancy! Of course,
the image file ‘icon.png’ has to be present in our project for this
to work. - Our game world will be scaled by a factor of 4, which we achieve
withGame.graphics().setBaseRenderScale(). - Now we load up our game resource file created with utiLITI in Day 1.
Just callResources.load("game.litidata")and all our
maps, sprites, etc. will be accessible directly from code hereafter. - Let’s try loading our ‘level1’ with
Game.world().loadEnvironment("level1"). - The last thing needed to get our game running is
Game.start().
We’ve all been waiting for this moment, let’s go ahead and RUN THE GAME!
But wait, what’s this? Our game window is there, but it doesn’t show anything.
The Ingame Screen
As it would be nice to actually have our game shown in the empty window, we need to render the game world.
For this purpose, we create an IngameScreen that extends GameScreen.
public class IngameScreen extends GameScreen {
public static final String NAME = "INGAME-SCREEN";
public IngameScreen() {
super(NAME);
}
}Once we add the IngameScreen to the ScreenManager by calling Game.screens().add(new IngameScreen()) in our main-method, two magic things happen:
- Since there was previously no
Screenpresent, adding a new one
will also callScreenManager.display(), making the newly added
IngameScreenthe currently visible screen. - Every
ScreenextendingGameScreencontains a call to
Game.world().environment().render()in itsrender()-method.
Just remember: Once you override the
GameScreen.render()-method, you’ll need to reference the inherited
behaviour withsuper.render()or else the game world won’t be
rendered.
Running the game again, we finally see something happening in our window!

Right now, we only see the upper left corner of our map since we haven’t defined any Camera yet. The default camera is locked to the map coordinates (0,0).
You can enable and disable the game metrics shown in the image above, as well as other debug options, by modifying the config.properties-file in your project’s main directory:
- Set the variable
cl_showGameMetricsto true to see basic info about fps, ups, and networking performance.
A noble Hero
Finally, we can put our awesomely edgy main protagonist inside the game!
For this, we utilize LITIENGINE’s Entity framework. Let’s go ahead and create a Player-class:
@EntityInfo(width = 18, height = 18)
@MovementInfo(velocity = 70)
@CollisionInfo(collisionBoxWidth = 8, collisionBoxHeight = 16, collision = true)
public class Player extends Creature implements IUpdateable {
private static Player instance;
public static Player instance() {
if (instance == null) {
instance = new Player();
}
return instance;
}
private Player() {
super("gurknukem");
}
@Override
public void update() {
}
@Override
protected IMovementController createMovementController() {
// setup movement controller
return new PlatformingMovementController<>(this);
}
}Right at the top, you’ll notice the annotations. This is what they do:
EntityInfospecifies basic properties that allEntitieshave,
namely their width, height, and RenderType.MovementInfois exclusive toMobileEntities: It contains
movement attributes such as velocity or acceleration.CollisionInfoconstitutes the size and alignment of a
CollisionEntity‘s collision box. .- Our Player is a child of
Creature, meaning that it inherits
everything fromCombatEntity,CollisionEntity, andEntity,
while also implementing the methods fromIMobileEntityand
IUpdateable. For a clearer unterstanding of the entity
hierarchy, you can have a look at the Javadocs again. - We adopt a Singleton pattern for the
Player-class, meaning
that we only allow the existence of one single instance ofPlayer
at all times. Calling the public staticPlayer.instance()will
only invoke the private constructor on its first call. All future
calls will return the same instance ofPlayer. - In the
Player‘s private constructor, we invoke the constructor of
superclassCreaturewithsuper("gurknukem"), where ‘gurknukem’
is the so-calledspritePrefixattribute of aCreature.
We need to declare thespritePrefixso that the
CreatureAnimationController, which is added in aCreature‘s
constructor, will be able to associate the right Spritesheets with
our Creature (remember the naming conventions for
Spritesheets?). - Furthermore, we override the
createMovementController()-method to add aPlatformingMovementControllerthat allows us
to move entities horizontally with player input. This method will be called upon the initialization of the `Player“.
For now, we leave the update()-method empty and focus on spawning our freshly created player into our game world.
Where to put my Game logic?
To keep our main()-method as atomic and light as possible, we go ahead and create a class GurkNukemLogic, where we will initialize global game logic such as spawning behavior or defining cameras.
public final class GurkNukemLogic {
private GurkNukemLogic() {
}
/**
* Initializes the game logic for the GURK NUKEM game.
*/
public static void init() {
// we'll use a camera in our game that is locked to the location of the player
Camera camera = new PositionLockCamera(Player.instance());
camera.setClampToMap(true);
Game.world().setCamera(camera);
// set a basic gravity for all levels.
Game.world().setGravity(120);
// add default game logic for when a level was loaded
Game.world().onLoaded(e -> {
// spawn the player instance on the spawn point with the name "enter"
Spawnpoint enter = e.getSpawnpoint("enter");
if (enter != null) {
enter.spawn(Player.instance());
}
});
}
}- LITIENGINE comes with a few basic pre-implemented cameras.
Momentarily, we’ll use aPositionLockCameralocked to the current
position of ourPlayerinstance. - With
Game.world().setGravity(120), we apply a constant
GravityForceto allMobileEntitiesin all levels. - Once an
Environmentis loaded, we want to spawn our player on the
Spawnpointwith the name ‘enter‘.
Note that Entity names are considered unique per level in LITIENGINE by design. If you want to retrieve a collection of Spawnpoints from the environment to pick a random one from, you could add Tags to them in utiLITI and call
Game.world().environment().getByTag("tag1","tag2",...).Once we call GurkNukemLogic.init() from our main()-method, we can now walk around the ground in our map and we’ll even fall down into pits!

Of course, there’s one crucial thing missing here: The ability to jump!
But first, let’s briefly create a class to manage all input (except player movement, which is already handled by the Player’s PlatformingMovementController.
Organizing Player Input
An odd thing you might have noticed by now is that you’re not yet able to exit the game with any keypress.
For initializing our key bindings, we create the class PlayerInput:
public final class PlayerInput {
private PlayerInput() {
}
public static void init() {
// make the game exit upon pressing ESCAPE (by default there is no such key binding and the window needs to be shutdown otherwise, e.g. ALT-F4 on Windows)
Input.keyboard().onKeyPressed(KeyEvent.VK_ESCAPE, e -> System.exit(0));
}
}- For now, we bind
ESCAPEto kill our program. Of course, this will change later on, but it will save some time in development.
Again, we need to call this code from our main()-method by adding PlayerInput.init() to it.
Up we go – Implementing a Jump Ability
For the jump mechanic, we choose to utilize LITIENGINE’s Ability framework. Go ahead and create the class Jump:
@AbilityInfo(cooldown = 500, origin = EntityPivotType.COLLISIONBOX_CENTER, duration = 300, value = 240)
public class Jump extends Ability {
public Jump(Creature executor) {
super(executor);
this.addEffect(new JumpEffect(this));
}
private class JumpEffect extends ForceEffect {
protected JumpEffect(Ability ability) {
super(ability, ability.getAttributes().value().get().intValue(), EffectTarget.EXECUTINGENTITY);
}
@Override
protected Force applyForce(IMobileEntity affectedEntity) {
// create a new force and apply it to the player
GravityForce force = new GravityForce(affectedEntity, this.getStrength(), Direction.UP);
affectedEntity.movement().apply(force);
return force;
}
@Override
protected boolean hasEnded(final EffectApplication appliance) {
return super.hasEnded(appliance) || this.isTouchingCeiling();
}
/**
* Make sure that the jump is cancelled when the entity touches a static collision box above it.
*
* @return True if the entity touches a static collision box above it.
*/
private boolean isTouchingCeiling() {
Optional opt = Game.world().environment().getCollisionBoxes().stream().filter(x -> x.getBoundingBox().intersects(this.getAbility().getExecutor().getBoundingBox())).findFirst();
if (!opt.isPresent()) {
return false;
}
CollisionBox box = opt.get();
return box.getCollisionBox().getMaxY() <= this.getAbility().getExecutor().getCollisionBox().getMinY();
}
}
}- As mentioned before, the class
Jumpis a child of LITIENGINE’s
Ability. As such, we can annotate the class with the
@AbilityInfo-annotation to determine its cooldown, origin
location, duration, andvalue, which is an abstract numeral used
in different ways, depending on what your ability does. - In an
Ability‘s constructor, theCreaturewhich casts the
Abilityis always passed as a parameter. In our case, we also add
aJumpEffectto theJump‘s list of effects. - The inner class
JumpEffecthere is aForceEffect, i.e. it will
apply a given force to its affected entities. In its constructor, we
establish its strength andEffectTarget. - In the
applyForce-method, we create aGravityForcedirected
upward which adopts theForceEffect‘s strength. The force will
then be applied to ability executor for the duration of the ability. - We also provide the
isTouchingCeiling– condition for cancelling
theForceEffect, which determines if the jumping entity’s
collision box intersects any static collision box above it.
Where it all comes together
Now, back to our Player-class, where we have to perform a few more adjustments, ending up with the following code:
@EntityInfo(width = 18, height = 18)
@MovementInfo(velocity = 70)
@CollisionInfo(collisionBoxWidth = 8, collisionBoxHeight = 16, collision = true)
public class Player extends Creature implements IUpdateable {
public static final int MAX_ADDITIONAL_JUMPS = 1;
private static Player instance;
private final Jump jump;
private int consecutiveJumps;
private Player() {
super("gurknukem");
// setup the player's abilities
this.jump = new Jump(this);
}
public static Player instance() {
if (instance == null) {
instance = new Player();
}
return instance;
}
@Override public void update() {
// reset the number of consecutive jumps when touching the ground
if (this.isTouchingGround()) {
this.consecutiveJumps = 0;
}
}
@Override
protected IMovementController createMovementController() {
// setup movement controller
return new PlatformingMovementController<>(this);
}
/**
* Checks whether this instance can currently jump and then performs the Jump ability.
*
* Note that the name of this methods needs to be equal to {@link PlatformingMovementController#JUMP}} in order for the controller
* to be able to use this method.
* Another option is to explicitly specify the Action.name() attribute on the annotation.
*/
@Action(description = "This performs the jump ability for the player's entity.")
public void jump() {
if (this.consecutiveJumps >= MAX_ADDITIONAL_JUMPS || !this.jump.canCast()) {
return;
}
this.jump.cast();
this.consecutiveJumps++;
}
private boolean isTouchingGround() {
// the idea of this ground check is to extend the current collision box by
// one pixel and see if
// a) it collides with any static collision box
Rectangle2D groundCheck = new Rectangle2D.Double(this.getCollisionBox().getX(), this.getCollisionBox().getY(), this.getCollisionBoxWidth(), this.getCollisionBoxHeight() + 1);
// b) it collides with the map's boundaries
if (groundCheck.getMaxY() > Game.physics().getBounds().getMaxY()) {
return true;
}
return Game.physics().collides(groundCheck, Collision.STATIC);
}
}- First, we declare the maximum number of mid-air-jumps, a
Jump
instance and the current number of consecutive jumps as fields. - Then, we write a
jump()-method. It casts the Jump ability and
raises the consecutive jump counter by one if the jump limit hasn’t
been reached and theJumpability’scanCast()-detection returns
true. Adding the jump method at this point is also an
introduction to a feature established in v0.5.0-beta:
EntityActions. For an intuition about their workings, let’s have a
look at thePlatformingMovementController: it can very well call
all the methods that are declared by theIMobileEntityit is
registered for. But what if we want the
PlatformingMovementControllerto call logic that is exclusive to
one singleEntityimplementing theIMobileEntityinterface? For
this purpose, there is a reflection-based system in LITIENGINE
to call methods fromIEntitiesthat are otherwise inaccessible in
certain places: AnyIEntitycan declare an@Action-annotation
on its methods. During runtime, these methods will be registered
automatically by their method name and callable with
IEntity.perform(String actionName). Once the SPACEBAR (or any
other declared jump key) is pressed, the
PlatformingMovementControllerwill callPlayer.perform("jump")
to run thejump()-method annotated with@Action.
EntityActions can also be registered explicitly without the
annotation by using
IEntity.register(String name, Runnable action). However, you
should try avoiding this approach whenever possible as using
reflection can come with severe security risks (among other
drawbacks)! In most cases, you should stick to the inheritance-based
Entity framework that LITIENGINE provides. - Hereafter, we declare the
isTouchingGround()-method, which returns
true, if the player’s collision box touches a static collision box
or is intersecting with the lower map boundary. - In the
update()-method, we reset the current jump counter to zero
on collision.

And that’s it for today!
Sit back and enjoy your awesome player double-jumping around the platforms in your test map, just waiting for some foes to defeat!
There are binaries available on the ‘Gurk Nukem’ GitHub page. Head over there and compare your work to our reference project!
We can’t wait to share more about the journey of creating games with LITIENGINE, so stay tuned.
Last updated 4 years ago
