LibGDX is a gaming framework which works on the JVM. Using it, you can create a game for mobile, desktop and web platform.
Kotlin is a language compatible with the JVM (Java Virtual Machine) platform. It’s like Java but shorter.
LibGDX-addons is a set of addons, gradle plugins, …, to ease the development of games using libGDX and Kotlin. Get ride of all boilerplate to dive directly into the code of your game.
Important
|
This documentation is open source. You can correct it, updated it or add content of you want to. Just Fork it and submit a merge request of your update. Thanks! |
Getting started
LibGDX use gradle as a build system. You can use the official setup app to create your game. Instead LibGDX-addons propose a gradle plugin to take care of all gradle setup, for all platform (Currently supported: Desktop and Android) so you don’t have to manage it or update your gradle script.
Setup your game
Create your first game by using IntelliJ.
Click in File
> New Project
Pick Gradle
then tick Kotlin DSL build script
, choose Kotlin/JVM
and go to the next screen by clicking Next
.
Fill the information asked and click Next
until IntelliJ create your project.
Open the file build.gradle.kts
and paste this configuration into.
It adds necessary repositories for dependencies and add the gradle libgdx plugin.
buildscript {
dependencies {
classpath("com.github.dwursteisen.libgdx-addons:libgdx-gradle-plugin:016e8b7")
}
// (1)
repositories {
mavenCentral()
google()
jcenter()
maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") }
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") }
maven { url = uri("https://jitpack.io") }
}
}
allprojects {
// (2)
repositories {
mavenCentral()
google()
jcenter()
maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") }
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") }
maven { url = uri("https://jitpack.io") }
}
}
apply(plugin = "libgdx")
-
Add dependency repositories for plugins;
-
Add dependency repositories for all modules.
Run the command gradlew build
: it will download gradle if needed and download all dependencies and create the minimal setup.
Once done, it might be long depending of your Internet connection, refresh the project in IntelliJ.
Run your game
Your game project is now ready!
You can run the game from the command line using ./gradlew run
. Gradle will now compile the code that
was previously generated and run your game. But it might be more convenient to run the game from IntelliJ.
As you refreshed the project into IntelliJ, a new Run Configuration just appear in your IntelliJ.
Click on Add Configuration
and check Application
run configurations. You will see a run configuration added in IntelliJ
to run your game.
Draw your first texture
Grab an image and copy it into the src/main/assets
directory of the core
module.
You can find free image / sprite sheet on the OpenGameArt.org.
Run the command ./gradlew build
, the command will generate a new class Assets
that will
contains all of your assets name.
Note
|
The use of the Assets object is not mandatory. But if your rename one of your assets,
it’s practical to have a compilation error instead of your game crashing.
|
Create your game into the core
directory and use it in MainClass.kt
.
class MyGame : Game() {
private lateinit var texture: Texture
private lateinit var batch: SpriteBatch
override fun create() {
texture = Texture(Assets.assets_dungeon_sheet_png)
batch = SpriteBatch()
}
override fun render() {
batch.begin()
batch.draw(texture, 0f, 0f)
batch.end()
}
}
object MainClass {
@JvmStatic
fun main(args: Array<String>) {
LwjglApplication(MyGame(), LwjglApplicationConfiguration().apply {
width = 600
height = 600
})
}
}
If you run your game, the texture will be displayed on the screen.
Increase your feedback loop
It’s important to have a quick feedback loop while developing a game. For example, what’s the point of restarting your game when you just updated a texture from your game?
To enable this feature, you need to add a dependency to the libgdx core-addons.
Add this into your build.gradle.kts
then refresh the project in your IDE:
project(":core") {
dependencies {
implementation("com.github.dwursteisen.libgdx-addons:core-addons:a49ba13")
}
}
You can now update your game to use the asset manager of libgdx with RefreshableTexture
.
class MyGame : Game() {
private val assetManager: AssetManager = AssetManager()
private lateinit var batch: SpriteBatch
override fun create() {
// (1)
assetManager.setLoader(Texture::class.java, RefreshableTextureLoader(InternalFileHandleResolver()))
assetManager.load(Assets.assets_dungeon_sheet_png, Texture::class.java)
// (2)
assetManager.finishLoading()
batch = SpriteBatch()
}
override fun render() {
// (3)
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
// (4)
val texture: Texture = assetManager[Assets.assets_dungeon_sheet_png]
batch.begin()
batch.draw(texture, 0f, 0f)
batch.end()
}
}
-
Replace the default Texture Loader with the RefreshableTexture Loader;
-
Force to load all assets before rendering the game;
-
Clear the screen;
-
Getting the texture from the asset manager.
Run your game and while the game is running, apply a modification on the texture file. As soon as you save the file, the texture will be updated in your game.
Note
|
The asset manager is not mandatory. But by using it, you can use the RefreshableTexture
for the desktop version of your game and the default texture loader for your android game without
having to change your whole code base.
|
Setup a entity engine
A game is a lot of different entities who share a lot of common behaviours. For example, the player and an enemy have bot a position. They have both a size too. You can create a class that both will inherit. But it will increase your code complexity. Instead, you can compose your entities of different properties: an entity can be a player with a size and a position. An enemy is an entity with a size, a position, some other capacities.
[Ashley](https://github.com/libgdx/ashley) is one entity engine for libGDX. It is very easy to setup in your project. Add ashley as dependencies of your project then refresh it in your IDE.
project(":core") {
dependencies {
implementation("com.github.dwursteisen.libgdx-addons:core-addons:a49ba13")
implementation("com.badlogicgames.ashley:ashley:1.7.3")
}
}
The code of your game needs to be updated to add the entity engine and create your first entity.
class MyGame : Game() {
private val assetManager: AssetManager = AssetManager()
// (1)
private val engine = Engine()
private val viewport: Viewport = FitViewport(200f, 200f)
override fun create() {
assetManager.setLoader(Texture::class.java, RefreshableTextureLoader(InternalFileHandleResolver()))
assetManager.load(Assets.assets_dungeon_sheet_png, Texture::class.java)
assetManager.finishLoading()
// (2)
engine.addSystem(RenderSystem(viewport,
mapOf(SPRITE to SpriteStrategy())
))
engine.addSystem(PlayerSystem())
// (3)
val split = TextureSplitter(assetManager).split(
Assets.assets_dungeon_sheet_png, 16, 16
)
val playerSprite = split.get(column = 19, row = 7)
// (4)
val player = engine.createEntity().apply {
add(Player())
add(Position())
add(Size(16 v2 16))
add(Textured(texture = playerSprite))
add(Render(SPRITE))
}
engine.addEntity(player)
}
override fun render() {
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
// (5)
engine.update(Gdx.graphics.deltaTime)
}
override fun resize(width: Int, height: Int) {
viewport.update(width, height)
}
}
-
Create an
EntityEngine
. Please note that you can use aPooledEntityEngine
too; -
Add all systems to the engine. For now, we will just add a system that will render all entities on screen;
-
Split the texture to get only a region. This region will be use to display the player;
-
Create your first entity. This entity is marked as the
Player
. Has a position, a size, a texture and marked as to be rendered; -
In the render loop, update the engine.
When creating the player, please note that only the component Player
needs to be created.
package step4
import com.badlogic.ashley.core.Component
import com.github.dwursteisen.libgdx.ashley.StateComponent
class Player : Component
class Door : StateComponent()
class Switch : StateComponent()
Other components are provided by ashley-addons
: Position
, Size
, …
Important
|
Do not forget to set a size! Otherwise your entity will have a width and heigh of 0 so will be invisible. |
The player can be moved by detecting if some keys where pressed, like left, right, …
class PlayerSystem : IteratingSystem(Family.all(Player::class.java).get()) {
private val position = get<Position>()
private val speed = 64f
override fun processEntity(entity: Entity, deltaTime: Float) {
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
entity[position].value.add(-speed * deltaTime, 0f)
} else if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
entity[position].value.add(speed * deltaTime, 0f)
}
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
entity[position].value.add(0f, speed * deltaTime)
} else if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
entity[position].value.add(0f, -speed * deltaTime)
}
}
}
Regarding the key that was pressed, the position of the entity is extracted.
Note
|
The property component is accessed from the entity like it is a map. More information can be found in the [ashley-addons] section. |
Create a state machine
A game can be composed of a lot of state machine. But what is a state machine? It’s an entity that can be in state and regarding a certain event transition can goes in another state.
A simple example could be a door: it can be open and goes in a close state if the player push a switch.
React to events
Package your game for Desktop
Tip
|
WIP |
Package your game for Android
Tip
|
WIP |
LibGDX Libraries
Tip
|
WIP |
admob-addons
Tip
|
TODO |
aseprite-addons
Tip
|
TODO |
ashley-addons
Tip
|
TODO |
core-addons
Tip
|
TODO |
libgdx-test
Tip
|
TODO |
LibGDX Gradle plugins
Tip
|
WIP |
Assets Gradle Plugin
When developing a game, you need to manage resources. Time to time you need to rename a file. In such case, you need to track every time you reference the file name by a string.
val sprite = load("mySprite.png")
To avoid to search for every place you’re loading the file using a String
,
the assets plugin can generate an Assets
object that will hold all references to
all your assets.
object Assets {
val mySprite: String = "mySprite.png"
}
If you rename an asset, then the class will be re-generated and you game will not compile! Which is way better than compile and crash later.
Tip
|
It’s better to have a game that doesn’t compile than a game that compile, run and crash only when the game will load the missing asset. |
Configuration
import com.github.dwursteisen.libgdx.assets.AssetsPlugin
import com.github.dwursteisen.libgdx.assets.AssetsPluginExtension
plugins {
id("assets")
}
apply<AssetsPlugin>()
configure<AssetsPluginExtension> {
assetsClass.set(project.buildDir.resolve("generated/NewAssets.kt"))
}
You can configure the plugin using several options:
open class AssetsPluginExtension(project: Project) {
/**
* Which directory should be scan so all files will be referenced in the Assets object.
*/
val assetsDirectory = project.createProperty<FileCollection>()
.value(project.files("src/main/assets"))
/**
* Which class (aka Assets object) will reference all assets name.
*/
val assetsClass = project.createProperty<File>()
.value(project.buildDir.resolve("generated/Assets.kt"))
}