├── src └── docs │ └── asciidoc │ ├── samples │ ├── gradle.properties │ ├── core │ │ └── src │ │ │ └── main │ │ │ ├── kotlin │ │ │ ├── step5 │ │ │ │ ├── Events.kt │ │ │ │ ├── DoorSystem.kt │ │ │ │ ├── SwitchSystem.kt │ │ │ │ └── MyGame.kt │ │ │ ├── step4 │ │ │ │ ├── Components.kt │ │ │ │ ├── SpriteStrategy.kt │ │ │ │ ├── PlayerSystem.kt │ │ │ │ └── MyGame.kt │ │ │ ├── step2 │ │ │ │ └── MyGame.kt │ │ │ └── step3 │ │ │ │ └── MyGame.kt │ │ │ └── assets │ │ │ ├── dungeon_sheet.png │ │ │ ├── dungeon_sheet.tsx │ │ │ └── dungeon.tmx │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── desktop │ │ ├── src │ │ │ └── main │ │ │ │ └── kotlin │ │ │ │ ├── step1 │ │ │ │ └── MainClass.kt │ │ │ │ ├── step2 │ │ │ │ └── MainClass.kt │ │ │ │ ├── step3 │ │ │ │ └── MainClass.kt │ │ │ │ ├── step4 │ │ │ │ └── MainClass.kt │ │ │ │ └── step5 │ │ │ │ └── MainClass.kt │ │ └── step1 │ │ │ └── MainClass.kt │ ├── build.gradle.kts │ ├── gradlew.bat │ └── gradlew │ ├── media │ ├── create_game_01.png │ ├── create_game_02.png │ ├── create_game_02b.png │ ├── create_game_03.png │ ├── create_game_04.png │ ├── create_game_05.gif │ └── create_game_06.png │ ├── 02_ligdx_addons.adoc │ ├── index.adoc │ ├── 03_libgdx_gradle.adoc │ └── 01_create_project.adoc ├── libgdx-template-gradle-plugin ├── src │ ├── core │ │ ├── assets │ │ │ └── README.txt │ │ └── kotlin │ │ │ └── libgdx │ │ │ ├── GameClass.kt │ │ │ └── MyFirstScreen.kt │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── gradle-plugins │ │ │ │ └── com.github.dwursteisen.libgdx.template.properties │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── dwursteisen │ │ │ └── libgdx │ │ │ └── template │ │ │ └── gradle │ │ │ ├── Templates.kt │ │ │ ├── TemplatePlugin.kt │ │ │ └── GenerateModelTask.kt │ └── desktop │ │ └── kotlin │ │ └── libgdx │ │ └── Main.kt ├── README.md └── build.gradle.kts ├── gradle.properties ├── .travis.yml ├── admob-addons ├── README.md ├── admob-core │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── com.github.dwursteisen.libgdx.admob │ │ ├── AdsPosition.kt │ │ ├── AdsSize.kt │ │ └── GdxAdsManager.kt └── admob-desktop │ ├── build.gradle.kts │ └── src │ └── main │ └── kotlin │ └── com.github.dwursteisen.libgdx.admob │ ├── DesktopAdsManager.kt │ └── MockDesktopAdsRenderer.kt ├── docs └── media │ ├── create_game_01.png │ ├── create_game_02.png │ ├── create_game_02b.png │ ├── create_game_03.png │ ├── create_game_04.png │ ├── create_game_05.gif │ └── create_game_06.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── assets-gradle-plugin ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── gradle-plugins │ │ │ │ └── assets.properties │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── dwursteisen │ │ │ └── libgdx │ │ │ └── assets │ │ │ ├── AssetsPluginExtension.kt │ │ │ ├── AssetsPlugin.kt │ │ │ └── AssetsTask.kt │ └── test │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ └── assets │ │ └── AssetsPluginTest.kt ├── README.md └── build.gradle.kts ├── libgdx-gradle-plugin ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── gradle-plugins │ │ │ │ ├── libgdx.properties │ │ │ │ ├── com.github.dwursteisen.libgdx.core.properties │ │ │ │ ├── com.github.dwursteisen.libgdx.android.properties │ │ │ │ └── com.github.dwursteisen.libgdx.desktop.properties │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── dwursteisen │ │ │ └── libgdx │ │ │ └── gradle │ │ │ ├── LibGDXExtensions.kt │ │ │ ├── internal │ │ │ ├── CorePlugin.kt │ │ │ ├── ProjectExts.kt │ │ │ └── AndroidPlugin.kt │ │ │ └── LibGDXPlugin.kt │ └── test │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ └── gradle │ │ ├── TemplatePluginTest.kt │ │ └── LibGDXPluginTest.kt ├── build.gradle.kts └── README.md ├── core-addons ├── build.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ ├── ColorExts.kt │ │ ├── GdxArrayExts.kt │ │ ├── AssetManagerExts.kt │ │ ├── MathExts.kt │ │ ├── ShapeRendererExts.kt │ │ ├── graphics │ │ ├── TextureWatchDogs.kt │ │ ├── RefreshableTextureLoader.kt │ │ └── RefreshableTexture.kt │ │ ├── TiledMapExts.kt │ │ ├── Vector2Exts.kt │ │ ├── ServiceLocator.kt │ │ └── TextureSplitter.kt │ └── test │ └── java │ └── com │ └── github │ └── dwursteisen │ └── libgdx │ ├── ColorExtsTest.java │ └── ServiceLocatorTest.java ├── commons-gradle-plugin ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── com │ └── github │ └── dwursteisen │ └── libgdx │ └── gradle │ └── ProjectExts.kt ├── aseprite-addons ├── build.gradle.kts ├── README.md └── src │ └── main │ └── kotlin │ └── com │ └── github │ └── dwursteisen │ └── libgdx │ └── aseprite │ ├── AsepriteJsonLoader.kt │ └── AsepriteModel.kt ├── packr-gradle-plugin ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── com.github.dwursteisen.libgdx.packr.PackrPlugin.properties │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ └── packr │ │ ├── PackrPluginExtension.kt │ │ ├── PackrPlugin.kt │ │ └── PackrTask.kt ├── build.gradle.kts └── README.md ├── aseprite-gradle-plugin ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── com.github.dwursteisen.libgdx.aseprite.AsepritePlugin.properties │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ └── aseprite │ │ ├── AsepriteFormat.kt │ │ ├── AsepritePlugin.kt │ │ ├── AsepritePluginExtensions.kt │ │ └── AsepriteTask.kt ├── build.gradle.kts └── README.md ├── ashley-addons ├── build.gradle.kts ├── src │ └── main │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ └── ashley │ │ ├── AshleyExts.kt │ │ ├── EntityExts.kt │ │ ├── StateSystem.kt │ │ ├── AnimationSystem.kt │ │ ├── fsm │ │ ├── StateMachine.kt │ │ ├── EntityState.kt │ │ └── StateMachineSystem.kt │ │ ├── EngineExts.kt │ │ ├── Components.kt │ │ ├── RenderSystem.kt │ │ └── EventBus.kt └── README.md ├── .github └── workflows │ └── android.yml ├── libgdx-test ├── build.gradle.kts ├── src │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── github │ │ │ │ └── dwursteisen │ │ │ │ └── libgdx │ │ │ │ └── test │ │ │ │ ├── InputAction.kt │ │ │ │ ├── GdxDsl.kt │ │ │ │ ├── DelegateGame.kt │ │ │ │ └── LibGdxRule.kt │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dwursteisen │ │ │ └── libgdx │ │ │ └── test │ │ │ └── RemoteSender.java │ └── test │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── dwursteisen │ │ └── libgdx │ │ └── test │ │ ├── GameTest.kt │ │ └── GameUnderTest.kt └── README.md ├── settings.gradle.kts ├── README.md ├── .gitignore ├── gradlew.bat └── gradlew /src/docs/asciidoc/samples/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/core/assets/README.txt: -------------------------------------------------------------------------------- 1 | Put all your assets in this directory 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.configureondemand=true 2 | org.gradle.caching=true 3 | org.gradle.parallel=true 4 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step5/Events.kt: -------------------------------------------------------------------------------- 1 | package step5 2 | 3 | const val EVENT_SWITCH_ON = 1 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | script: 3 | - ./gradlew check 4 | - ./gradlew -p src/docs/asciidoc/samples/ check 5 | -------------------------------------------------------------------------------- /admob-addons/README.md: -------------------------------------------------------------------------------- 1 | # admob libgGDX 2 | 3 | Add Admob support to your game. 4 | 5 | # How to use it? 6 | 7 | // TODO -------------------------------------------------------------------------------- /docs/media/create_game_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_01.png -------------------------------------------------------------------------------- /docs/media/create_game_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_02.png -------------------------------------------------------------------------------- /docs/media/create_game_02b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_02b.png -------------------------------------------------------------------------------- /docs/media/create_game_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_03.png -------------------------------------------------------------------------------- /docs/media/create_game_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_04.png -------------------------------------------------------------------------------- /docs/media/create_game_05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_05.gif -------------------------------------------------------------------------------- /docs/media/create_game_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/docs/media/create_game_06.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/README.md: -------------------------------------------------------------------------------- 1 | # LibGDX Template gradle plugin 2 | 3 | Plugin that just generate a project regarding a template. 4 | -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_01.png -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_02.png -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_02b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_02b.png -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_03.png -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_04.png -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_05.gif -------------------------------------------------------------------------------- /src/docs/asciidoc/media/create_game_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/media/create_game_06.png -------------------------------------------------------------------------------- /assets-gradle-plugin/src/main/resources/META-INF/gradle-plugins/assets.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.assets.AssetsPlugin -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/resources/META-INF/gradle-plugins/libgdx.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.gradle.LibGDXPlugin -------------------------------------------------------------------------------- /core-addons/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(kotlin("stdlib")) 3 | api(Dependencies.gdx) 4 | 5 | testImplementation(TestDependencies.junit) 6 | } 7 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/samples/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /commons-gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compileOnly(gradleApi()) 3 | implementation(kotlin("stdlib")) 4 | testImplementation(TestDependencies.junit) 5 | } 6 | -------------------------------------------------------------------------------- /admob-addons/admob-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(kotlin("stdlib")) 3 | implementation(Dependencies.gdx) 4 | testCompile(TestDependencies.junit) 5 | } 6 | -------------------------------------------------------------------------------- /aseprite-addons/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(kotlin("stdlib")) 3 | implementation(Dependencies.gdx) 4 | testImplementation(TestDependencies.junit) 5 | } 6 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.github.dwursteisen.libgdx.core.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.gradle.internal.CorePlugin 2 | -------------------------------------------------------------------------------- /packr-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.github.dwursteisen.libgdx.packr.PackrPlugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.packr.PackrPlugin -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/assets/dungeon_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwursteisen/libgdx-addons/HEAD/src/docs/asciidoc/samples/core/src/main/assets/dungeon_sheet.png -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.github.dwursteisen.libgdx.android.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.gradle.internal.AndroidPlugin 2 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.github.dwursteisen.libgdx.desktop.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.gradle.internal.DesktopPlugin 2 | -------------------------------------------------------------------------------- /aseprite-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.github.dwursteisen.libgdx.aseprite.AsepritePlugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.aseprite.AsepritePlugin -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.github.dwursteisen.libgdx.template.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.github.dwursteisen.libgdx.template.gradle.TemplatePlugin 2 | -------------------------------------------------------------------------------- /ashley-addons/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api(project(":core-addons")) 3 | implementation(kotlin("stdlib")) 4 | api(Dependencies.ashley) 5 | implementation(Dependencies.ktx_log) 6 | } 7 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'my.game' 2 | 3 | // -- generated by libgdx-addons plugin -- // 4 | include("core", "desktop") 5 | 6 | includeBuild("../../../../../libgdx-addons") 7 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ColorExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.graphics.Color 4 | 5 | 6 | fun String.toColor(): Color = Color.valueOf(this) -------------------------------------------------------------------------------- /aseprite-gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":commons-gradle-plugin")) 3 | implementation(kotlin("stdlib")) 4 | implementation(gradleApi()) 5 | testImplementation(TestDependencies.junit) 6 | } 7 | -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/core/kotlin/libgdx/GameClass.kt: -------------------------------------------------------------------------------- 1 | package libgdx 2 | 3 | import com.badlogic.gdx.Game 4 | 5 | class GameClass : Game() { 6 | override fun create() { 7 | setScreen(MyFirstScreen()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /aseprite-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/aseprite/AsepriteFormat.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.aseprite 2 | 3 | enum class AsepriteFormat(val format: String) { 4 | HASH("json-hash"), 5 | ARRAY("json-array") 6 | } 7 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/GdxArrayExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.utils.Array 4 | 5 | fun gdxArrayOf(vararg obj: T) = Array(obj) 6 | fun emptyGdxArray(): Array = gdxArrayOf() 7 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 08 20:16:32 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip 7 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step4/Components.kt: -------------------------------------------------------------------------------- 1 | package step4 2 | 3 | import com.badlogic.ashley.core.Component 4 | import com.github.dwursteisen.libgdx.ashley.StateComponent 5 | 6 | class Player : Component 7 | class Door : StateComponent() 8 | class Switch : StateComponent() 9 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/AssetManagerExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.assets.AssetManager 4 | 5 | 6 | inline operator fun AssetManager.get(filename: String): T { 7 | return this.get(filename, T::class.java) 8 | } -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/template/gradle/Templates.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.template.gradle 2 | 3 | enum class Templates(val templateFolder: String) { 4 | CORE("core/"), 5 | DESKTOP("desktop/"), 6 | ANDROID("android/") 7 | } 8 | -------------------------------------------------------------------------------- /admob-addons/admob-desktop/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":admob-addons:admob-core")) 3 | implementation(kotlin("stdlib")) 4 | implementation(Dependencies.gdx_lwjgl) 5 | implementation(Dependencies.gdx_desktop) 6 | testImplementation(TestDependencies.junit) 7 | } 8 | -------------------------------------------------------------------------------- /src/docs/asciidoc/02_ligdx_addons.adoc: -------------------------------------------------------------------------------- 1 | == LibGDX Libraries 2 | 3 | TIP: WIP 4 | 5 | === admob-addons 6 | 7 | TIP: TODO 8 | 9 | === aseprite-addons 10 | 11 | TIP: TODO 12 | 13 | === ashley-addons 14 | 15 | TIP: TODO 16 | 17 | === core-addons 18 | 19 | TIP: TODO 20 | 21 | === libgdx-test 22 | 23 | TIP: TODO 24 | -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: set up JDK 1.8 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 1.8 16 | - name: Build with Gradle 17 | run: ./gradlew build 18 | -------------------------------------------------------------------------------- /admob-addons/admob-core/src/main/kotlin/com.github.dwursteisen.libgdx.admob/AdsPosition.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.admob 2 | 3 | typealias AdsPosition = Int 4 | 5 | val LEFT: AdsPosition = 1 6 | val CENTER: AdsPosition = LEFT.shl(1) 7 | val RIGHT: AdsPosition = LEFT.shl(2) 8 | 9 | val DOWN: AdsPosition = LEFT.shl(3) 10 | val UP: AdsPosition = LEFT.shl(4) 11 | -------------------------------------------------------------------------------- /assets-gradle-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Assets gradle plugin 2 | 3 | Gradle plugin which generate a `Assets` class with 4 | all your assets filename. So you can use the class 5 | to access your file from your code, to avoid any name mistake. 6 | 7 | ## How to use it? 8 | // TODO 9 | 10 | ## Example 11 | 12 | ``` 13 | Texture(Gdx.files.internal(Assets.assets_badlogic_jpg)) 14 | ``` -------------------------------------------------------------------------------- /commons-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/gradle/ProjectExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.provider.Property 5 | 6 | @Suppress("UnstableApiUsage") 7 | inline fun Project.createProperty(): Property { 8 | return this.objects.property(T::class.java) 9 | } 10 | -------------------------------------------------------------------------------- /assets-gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | apply { plugin("java-gradle-plugin") } 2 | 3 | dependencies { 4 | implementation(project(":commons-gradle-plugin")) 5 | implementation(gradleApi()) 6 | implementation(kotlin("stdlib")) 7 | implementation(Dependencies.kotlinpoet) 8 | testImplementation(TestDependencies.junit) 9 | testImplementation(gradleTestKit()) 10 | } 11 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/MathExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | fun Float.isNotBetween(a: Float, b: Float): Boolean = !isBetween(a, b) 4 | 5 | /** 6 | * Is the number between the number a and b (included). 7 | * Return true if it's the case. 8 | */ 9 | fun Float.isBetween(a: Float, b: Float): Boolean { 10 | return this in a..b 11 | } 12 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/AshleyExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Component 4 | import com.badlogic.ashley.core.ComponentMapper 5 | import com.badlogic.ashley.core.EntitySystem 6 | 7 | inline fun EntitySystem.get(): ComponentMapper = ComponentMapper.getFor(T::class.java) 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libgdx-test/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(kotlin("stdlib")) 3 | implementation(Dependencies.gdx) 4 | implementation(Dependencies.gdx_lwjgl) 5 | implementation(Dependencies.gdx_desktop) 6 | implementation(Dependencies.gif) 7 | 8 | implementation(TestDependencies.junit) 9 | } 10 | 11 | tasks.withType() { 12 | exclude("com/github/dwursteisen/libgdx/test/**") 13 | } 14 | -------------------------------------------------------------------------------- /core-addons/src/test/java/com/github/dwursteisen/libgdx/ColorExtsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class ColorExtsTest { 8 | 9 | @Test 10 | public void toColor() { 11 | Color color = ColorExtsKt.toColor("#FFFFFF"); 12 | Assert.assertEquals(color, Color.WHITE); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/desktop/kotlin/libgdx/Main.kt: -------------------------------------------------------------------------------- 1 | package libgdx 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | 6 | 7 | object MainClass { 8 | @JvmStatic 9 | fun main(args: Array) { 10 | LwjglApplication(GameClass(), LwjglApplicationConfiguration().apply { 11 | width = 600 12 | height = 600 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/EntityExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Component 4 | import com.badlogic.ashley.core.ComponentMapper 5 | import com.badlogic.ashley.core.Entity 6 | 7 | inline operator fun Entity.get(mapper: ComponentMapper): T = mapper.get(this) 8 | 9 | inline fun Entity.getNullable(mapper: ComponentMapper): T? = mapper.get(this) 10 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("GRADLE_METADATA") 2 | 3 | include("core-addons") 4 | include("ashley-addons") 5 | include("aseprite-addons") 6 | 7 | include("libgdx-test") 8 | 9 | include("admob-addons:admob-core") 10 | include("admob-addons:admob-desktop") 11 | 12 | include("libgdx-template-gradle-plugin") 13 | include("commons-gradle-plugin") 14 | include("packr-gradle-plugin") 15 | include("aseprite-gradle-plugin") 16 | include("libgdx-gradle-plugin") 17 | include("assets-gradle-plugin") 18 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/desktop/src/main/kotlin/step1/MainClass.kt: -------------------------------------------------------------------------------- 1 | package step1 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | 6 | 7 | object MainClass { 8 | @JvmStatic 9 | fun main(args: Array) { 10 | LwjglApplication(TODO("Replace With Your Game Class"), LwjglApplicationConfiguration().apply { 11 | width = 600 12 | height = 600 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/desktop/src/main/kotlin/step2/MainClass.kt: -------------------------------------------------------------------------------- 1 | package step2 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | 6 | // tag::body[] 7 | object MainClass { 8 | @JvmStatic 9 | fun main(args: Array) { 10 | LwjglApplication(MyGame(), LwjglApplicationConfiguration().apply { 11 | width = 600 12 | height = 600 13 | }) 14 | } 15 | } 16 | // end::body[] 17 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/desktop/src/main/kotlin/step3/MainClass.kt: -------------------------------------------------------------------------------- 1 | package step3 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | 6 | // tag::body[] 7 | object MainClass { 8 | @JvmStatic 9 | fun main(args: Array) { 10 | LwjglApplication(MyGame(), LwjglApplicationConfiguration().apply { 11 | width = 600 12 | height = 600 13 | }) 14 | } 15 | } 16 | // end::body[] 17 | -------------------------------------------------------------------------------- /packr-gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | repositories { 2 | maven { 3 | url = uri("https://oss.sonatype.org/content/repositories/snapshots") 4 | } 5 | maven { 6 | url = uri("https://jitpack.io") 7 | } 8 | } 9 | 10 | dependencies { 11 | implementation(project(":commons-gradle-plugin")) 12 | implementation(kotlin("stdlib")) 13 | implementation(gradleApi()) 14 | implementation("com.github.dwursteisen:packr:4680924076") 15 | testImplementation(TestDependencies.junit) 16 | } 17 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/desktop/step1/MainClass.kt: -------------------------------------------------------------------------------- 1 | 2 | package libgdx 3 | 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 5 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 6 | 7 | 8 | object MainClass { 9 | @JvmStatic 10 | fun main(args: Array) { 11 | LwjglApplication(TODO("Replace With Your Game Class"), LwjglApplicationConfiguration().apply { 12 | width = 600 13 | height = 600 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/desktop/src/main/kotlin/step4/MainClass.kt: -------------------------------------------------------------------------------- 1 | package step4 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | import step5.MyGame 6 | 7 | // tag::body[] 8 | object MainClass { 9 | @JvmStatic 10 | fun main(args: Array) { 11 | LwjglApplication(MyGame(), LwjglApplicationConfiguration().apply { 12 | width = 600 13 | height = 600 14 | }) 15 | } 16 | } 17 | // end::body[] 18 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/desktop/src/main/kotlin/step5/MainClass.kt: -------------------------------------------------------------------------------- 1 | package step5 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | import step5.MyGame 6 | 7 | // tag::body[] 8 | object MainClass { 9 | @JvmStatic 10 | fun main(args: Array) { 11 | LwjglApplication(MyGame(), LwjglApplicationConfiguration().apply { 12 | width = 600 13 | height = 600 14 | }) 15 | } 16 | } 17 | // end::body[] 18 | -------------------------------------------------------------------------------- /admob-addons/admob-core/src/main/kotlin/com.github.dwursteisen.libgdx.admob/AdsSize.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.admob 2 | 3 | import com.badlogic.gdx.math.Vector2 4 | 5 | class AdsSize private constructor(val size: Vector2) { 6 | 7 | companion object { 8 | val BANNER = AdsSize(Vector2(320f, 50f)) 9 | val LARGE_BANNER = AdsSize(Vector2(320f, 100f)) 10 | val MEDIUM_RECTANGLE = AdsSize(Vector2(300f, 250f)) 11 | val FULL_BANNER = AdsSize(Vector2(468f, 60f)) 12 | val LEADERBOARD = AdsSize(Vector2(728f, 90f)) 13 | } 14 | } -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step4/SpriteStrategy.kt: -------------------------------------------------------------------------------- 1 | package step4 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.github.dwursteisen.libgdx.ashley.Position 5 | import com.github.dwursteisen.libgdx.ashley.TexturedStrategy 6 | import com.github.dwursteisen.libgdx.ashley.get 7 | 8 | class SpriteStrategy : TexturedStrategy() { 9 | 10 | private val position = get() 11 | 12 | override fun zLevel(entity: Entity, delta: Float): Float { 13 | return (entity[position].value.y / -400) + 3 // <-- layer 3 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core-addons/src/test/java/com/github/dwursteisen/libgdx/ServiceLocatorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.Random; 7 | 8 | public class ServiceLocatorTest { 9 | 10 | @Test 11 | public void register() { 12 | Random random = new Random(); 13 | 14 | ServiceLocator.INSTANCE.register(random, Random.class); 15 | Random fromServiceLocator = ServiceLocator.INSTANCE.get(Random.class); 16 | 17 | Assert.assertSame(random, fromServiceLocator); 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /libgdx-test/src/main/kotlin/com/github/dwursteisen/libgdx/test/InputAction.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.test 2 | 3 | internal sealed class InputAction { 4 | class Wait(var duration: Float) : InputAction() 5 | class Push(val key: Int) : InputAction() 6 | class Release(val key: Int) : InputAction() 7 | class Type(val char: Char) : InputAction() 8 | class Touch(val x: Int, val y: Int) : InputAction() 9 | class Screenshot(val name: String) : InputAction() 10 | class StartRecord() : InputAction() 11 | class StopRecord(val filename: String) : InputAction() 12 | object Quit : InputAction() 13 | } 14 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/assets/dungeon_sheet.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /aseprite-addons/README.md: -------------------------------------------------------------------------------- 1 | # Aseprite Addons 2 | 3 | Class to load Aseprite export Json file and 4 | help to create animation from the aseprite definition 5 | 6 | Example: 7 | 8 | 9 | ``` 10 | val assets = AssetManager() 11 | 12 | assets.setLoader(AsepriteJson::class.java, AsepriteJsonLoader(InternalFileHandleResolver())) 13 | assets.setLoader(Aseprite::class.java, AsepriteLoader(InternalFileHandleResolver())) 14 | 15 | // will load player.png and player.json 16 | assets.load("player", Aseprite::class.java) 17 | 18 | // ... 19 | 20 | val player: Aseprite = assets.get("player", Aseprite::class.java) 21 | // will get the jump animation 22 | val jump = player["jump"] 23 | ``` -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step2/MyGame.kt: -------------------------------------------------------------------------------- 1 | package step2 2 | 3 | import com.badlogic.gdx.Game 4 | import com.badlogic.gdx.graphics.Texture 5 | import com.badlogic.gdx.graphics.g2d.SpriteBatch 6 | 7 | // tag::body[] 8 | class MyGame : Game() { 9 | 10 | private lateinit var texture: Texture 11 | 12 | private lateinit var batch: SpriteBatch 13 | 14 | override fun create() { 15 | texture = Texture(Assets.assets_dungeon_sheet_png) 16 | batch = SpriteBatch() 17 | } 18 | 19 | override fun render() { 20 | batch.begin() 21 | batch.draw(texture, 0f, 0f) 22 | batch.end() 23 | } 24 | } 25 | // end::body[] 26 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ShapeRendererExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer 4 | import com.badlogic.gdx.math.Vector2 5 | 6 | fun ShapeRenderer.circle(pos: Vector2, radius: Float) { 7 | this.circle(pos.x, pos.y, radius) 8 | } 9 | 10 | fun ShapeRenderer.circle(pos: Vector2, radius: Float, segment: Int) { 11 | this.circle(pos.x, pos.y, radius, segment) 12 | } 13 | 14 | fun ShapeRenderer.rect(pos: Vector2, width: Float, height: Float) { 15 | this.rect(pos.x, pos.y, width, height) 16 | } 17 | 18 | fun ShapeRenderer.rect(pos: Vector2, size: Vector2) { 19 | this.rect(pos.x, pos.y, size.x, size.y) 20 | } -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/StateSystem.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.badlogic.ashley.core.Family 5 | import com.badlogic.ashley.systems.IteratingSystem 6 | 7 | /** 8 | * System which only increase time on entities 9 | * 10 | * The time can be latter on use to update time based properties. 11 | * (For example: updating animation according to this time) 12 | */ 13 | class StateSystem : IteratingSystem(Family.all(StateComponent::class.java).get()) { 14 | 15 | private val state = get() 16 | 17 | public override fun processEntity(entity: Entity, deltaTime: Float) { 18 | entity[state].time += deltaTime 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/AnimationSystem.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.badlogic.ashley.core.Family 5 | import com.badlogic.ashley.systems.IteratingSystem 6 | 7 | class AnimationSystem : IteratingSystem(Family.all(Animated::class.java).get()) { 8 | private val animation = get() 9 | private val sprite = get() 10 | 11 | override fun processEntity(entity: Entity, deltaTime: Float) { 12 | if (entity[animation].animation == NO_ANIMATION) return 13 | entity[animation].time += deltaTime 14 | val frame = entity[animation].animation.getKeyFrame(entity[animation].time) 15 | entity[sprite].texture = frame 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packr-gradle-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Packr Gradle Support 2 | 3 | Add Packr support to gradle. 4 | 5 | ## How to use it 6 | 7 | // TODO: add gradle configuration 8 | 9 | 10 | // TODO: a task per platform 11 | ``` 12 | task packr(type: com.github.dwursteisen.libgdx.packr.PackrTask,dependsOn: "dist") { 13 | 14 | // the JDK to bundle with 15 | jdk = "/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home" 16 | // the jar to bundle (generally the dist task output) 17 | classpath = project.tasks.getByName("dist").archivePath 18 | // bundle identifier (for mac os app) 19 | bundleIdentifier = "sous.les.apps.sponge" 20 | mainClass = project.mainClassName 21 | outputDir = project.file(project.getBuildDir().absolutePath + "/packr/packr-out.app") 22 | executable = "sponge" 23 | } 24 | ``` 25 | 26 | ## All parameters -------------------------------------------------------------------------------- /assets-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/assets/AssetsPluginExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.assets 2 | 3 | import com.github.dwursteisen.libgdx.gradle.createProperty 4 | import org.gradle.api.Project 5 | import org.gradle.api.file.FileCollection 6 | import java.io.File 7 | 8 | open class AssetsPluginExtension(project: Project) { 9 | /** 10 | * Which directory should be scan so all files will be referenced in the Assets object. 11 | */ 12 | val assetsDirectory = project.createProperty() 13 | .value(project.files("src/main/assets")) 14 | 15 | /** 16 | * Which class (aka Assets object) will reference all assets name. 17 | */ 18 | val assetsClass = project.createProperty() 19 | .value(project.buildDir.resolve("generated/Assets.kt")) 20 | } 21 | -------------------------------------------------------------------------------- /admob-addons/admob-desktop/src/main/kotlin/com.github.dwursteisen.libgdx.admob/DesktopAdsManager.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.admob 2 | 3 | import com.badlogic.gdx.ApplicationListener 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 5 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 6 | 7 | 8 | class DesktopAdsManager(val config: LwjglApplicationConfiguration) : GdxAdsManager() { 9 | override fun close(query: AdsQuery) { 10 | delegate.close(query) 11 | } 12 | 13 | private lateinit var delegate: MockDesktopAdsRenderer 14 | 15 | override fun load(query: AdsQuery) { 16 | delegate.load(query) 17 | } 18 | 19 | override fun initialize(listener: ApplicationListener) { 20 | delegate = MockDesktopAdsRenderer(listener) 21 | LwjglApplication(delegate, config) 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/fsm/StateMachine.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley.fsm 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.github.dwursteisen.libgdx.ashley.EventData 5 | import com.badlogic.gdx.utils.Array as GdxArray 6 | 7 | interface StateMachine { 8 | /** 9 | * Change the state of the current entity into another state. 10 | */ 11 | fun go(newState: EntityState, entity: Entity) 12 | 13 | /** 14 | * Change the state of the entity into another state, using some custom attached data. 15 | */ 16 | fun go(newState: EntityState, entity: Entity, event: EventData) 17 | 18 | /** 19 | * Emit a new event with some custom attached data. 20 | */ 21 | fun emit(event: Event, eventData: EventData) 22 | 23 | /** 24 | * Emit a new event. 25 | */ 26 | fun emit(event: Event) 27 | } 28 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/graphics/TextureWatchDogs.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.graphics 2 | 3 | import com.badlogic.gdx.Gdx 4 | 5 | object TextureWatchDogs { 6 | 7 | var enabled: Boolean = true 8 | 9 | private val references = mutableSetOf() 10 | 11 | private val watchDog = Thread { 12 | while (enabled) { 13 | val toRefresh = references.filter { it.shouldRefresh() } 14 | if (toRefresh.isNotEmpty()) { 15 | Gdx.app.postRunnable { 16 | toRefresh.forEach { it.refresh() } 17 | } 18 | } 19 | Thread.sleep(100) 20 | } 21 | } 22 | 23 | init { 24 | start() 25 | } 26 | 27 | fun register(texture: RefreshableTexture) { 28 | references.add(texture) 29 | } 30 | 31 | fun start() { 32 | watchDog.start() 33 | } 34 | 35 | fun stop() { 36 | enabled = false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/docs/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | = LibGDX Addons 2 | :doctype: book 3 | :toc: left 4 | :data-uri: 5 | :lang: en 6 | :source-highlighter: coderay 7 | 8 | https://libgdx.badlogicgames.com/[LibGDX] is a gaming framework which works on the JVM. 9 | Using it, you can create a game for mobile, desktop and web platform. 10 | 11 | https://kotlinlang.org/[Kotlin] is a language compatible with the JVM (Java Virtual Machine) platform. 12 | It's like Java but shorter. 13 | 14 | LibGDX-addons is a set of addons, gradle plugins, …, to ease the development of games using libGDX and Kotlin. 15 | Get ride of all boilerplate to dive directly into the code of your game. 16 | 17 | IMPORTANT: This documentation is open source. 18 | You can correct it, updated it or add content of you want to. 19 | Just https://github.com/dwursteisen/libgdx-addons/fork[Fork it] and submit 20 | a merge request of your update. **Thanks!** 21 | 22 | include::01_create_project.adoc[] 23 | 24 | include::02_ligdx_addons.adoc[] 25 | 26 | include::03_libgdx_gradle.adoc[] 27 | 28 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | apply { plugin("java-gradle-plugin") } 2 | 3 | repositories { 4 | mavenCentral() 5 | google() 6 | maven { 7 | url = uri("https://plugins.gradle.org/m2/") 8 | } 9 | jcenter() 10 | maven { 11 | url = uri("https://oss.sonatype.org/content/repositories/snapshots") 12 | } 13 | // for custom packr depency. 14 | maven { 15 | url = uri("https://jitpack.io") 16 | } 17 | } 18 | dependencies { 19 | compileOnly(kotlin("stdlib")) 20 | compileOnly(gradleApi()) 21 | 22 | implementation(project(":commons-gradle-plugin")) 23 | api(project(":packr-gradle-plugin")) 24 | api(project(":assets-gradle-plugin")) 25 | api(project(":libgdx-template-gradle-plugin")) 26 | 27 | api(Plugins.intellij) 28 | api(Plugins.kotlin_gradle) 29 | api(Plugins.download) 30 | api(Plugins.android) 31 | 32 | testImplementation(TestDependencies.junit) 33 | testImplementation(TestDependencies.assertJ) 34 | testImplementation(gradleTestKit()) 35 | } 36 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/gradle/LibGDXExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.model.ObjectFactory 5 | import java.io.File 6 | import java.net.URL 7 | 8 | open class OpenJdk { 9 | var macOSX: URL = URL("https://cdn.azul.com/zulu/bin/zulu8.36.0.1-ca-jdk8.0.202-macosx_x64.zip") 10 | var windows: URL = URL("https://download.java.net/java/GA/jdk11/13/GPL/openjdk-11.0.1_windows-x64_bin.zip") 11 | var linux: URL = URL("https://download.java.net/java/GA/jdk11/13/GPL/openjdk-11.0.1_linux-x64_bin.tar.gz") 12 | } 13 | 14 | open class LibGDXExtensions(project: Project, objectFactory: ObjectFactory) { 15 | var assetsDirectory = project.createProperty() 16 | 17 | var version = project.createProperty().value("1.9.10") 18 | 19 | var mainClass = objectFactory.listProperty(String::class.java) 20 | 21 | var androidClass = project.createProperty() 22 | 23 | var openJdk = OpenJdk() 24 | } 25 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/TiledMapExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.maps.MapLayer 4 | import com.badlogic.gdx.maps.MapObject 5 | import com.badlogic.gdx.maps.MapProperties 6 | import com.badlogic.gdx.maps.tiled.TiledMapTileLayer 7 | import kotlin.reflect.KProperty 8 | 9 | fun TiledMapTileLayer.scanCells(action: TiledMapTileLayer.(Int, Int, TiledMapTileLayer.Cell) -> Unit) { 10 | (0 until width).forEach { x -> 11 | (0 until height).forEach { y -> 12 | getCell(x, y)?.run { action(x, y, this) } 13 | } 14 | } 15 | } 16 | 17 | fun MapLayer.scanObjects(action: MapLayer.(Float, Float, MapObject) -> Unit) { 18 | this.objects.forEach { 19 | val x = it.properties["x"].toString().toFloat() 20 | val y = it.properties["y"].toString().toFloat() 21 | 22 | this.action(x, y, it) 23 | } 24 | } 25 | 26 | inline operator fun MapProperties.getValue(thisRef: Any?, property: KProperty<*>): T { 27 | return this[property.name] as T 28 | } 29 | -------------------------------------------------------------------------------- /packr-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/packr/PackrPluginExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.packr 2 | 3 | import com.badlogicgames.packr.PackrConfig 4 | import com.github.dwursteisen.libgdx.gradle.createProperty 5 | import org.gradle.api.Project 6 | import java.io.File 7 | 8 | open class PackrPluginExtension(val name: String, project: Project) { 9 | 10 | val platform = project.createProperty() 11 | 12 | val jdk = project.createProperty() 13 | val executable = project.createProperty() 14 | 15 | val mainClass = project.createProperty() 16 | val vmArgs = project.createProperty>() 17 | val minimizeJre = project.createProperty() 18 | .value("soft") 19 | 20 | val classpath = project.createProperty() 21 | 22 | val outputDir = project.createProperty() 23 | 24 | val bundleIdentifier = project.createProperty() 25 | 26 | val verbose = project.createProperty() 27 | .value(false) 28 | } 29 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/graphics/RefreshableTextureLoader.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.graphics 2 | 3 | import com.badlogic.gdx.assets.AssetManager 4 | import com.badlogic.gdx.assets.loaders.FileHandleResolver 5 | import com.badlogic.gdx.assets.loaders.TextureLoader 6 | import com.badlogic.gdx.files.FileHandle 7 | import com.badlogic.gdx.graphics.Texture 8 | 9 | /** 10 | * Loader for [AssetManager] to load [RefreshableTexture] instad of [Texture]. 11 | * 12 | * Example of use: 13 | * 14 | * ``` 15 | * assetManager.setLoader(Texture::class.java, RefreshableTextureLoader(InternalFileHandleResolver())) 16 | * ``` 17 | * 18 | * After, all texture will be refreshable and so will be reloaded when a change on the file is detected. 19 | */ 20 | class RefreshableTextureLoader(resolver: FileHandleResolver) : TextureLoader(resolver) { 21 | override fun loadSync(manager: AssetManager, fileName: String, file: FileHandle, parameter: TextureParameter?): Texture { 22 | return RefreshableTexture(super.loadSync(manager, fileName, file, parameter)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/core/kotlin/libgdx/MyFirstScreen.kt: -------------------------------------------------------------------------------- 1 | package libgdx 2 | 3 | import com.badlogic.gdx.Gdx 4 | import com.badlogic.gdx.ScreenAdapter 5 | import com.badlogic.gdx.graphics.Color 6 | import com.badlogic.gdx.graphics.GL20 7 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer 8 | import com.badlogic.gdx.math.Interpolation 9 | import com.badlogic.gdx.math.MathUtils.cos 10 | import com.badlogic.gdx.math.Vector2 11 | 12 | class MyFirstScreen : ScreenAdapter() { 13 | 14 | private val shapeRender = ShapeRenderer() 15 | 16 | private val position = Vector2(200f, 0f) 17 | 18 | private var time = 0f 19 | 20 | override fun render(delta: Float) { 21 | 22 | time += delta 23 | 24 | position.y = Interpolation.bounce.apply(cos(time)) * 200f 25 | 26 | Gdx.gl.glClearColor(0f, 0f, 0f, 1f) 27 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) 28 | 29 | shapeRender.begin(ShapeRenderer.ShapeType.Filled) 30 | shapeRender.color = Color.RED 31 | shapeRender.circle(position.x, position.y, 50f) 32 | shapeRender.end() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /assets-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/assets/AssetsPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.assets 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.api.plugins.JavaPlugin 6 | import org.gradle.api.plugins.JavaPluginConvention 7 | import org.gradle.api.tasks.SourceSet 8 | 9 | class AssetsPlugin : Plugin { 10 | override fun apply(project: Project) { 11 | val exts = project.extensions.create("assets", AssetsPluginExtension::class.java, project) 12 | 13 | project.tasks.register("assets", AssetsTask::class.java) { task -> 14 | task.assetsDirectory.set(exts.assetsDirectory) 15 | task.assetsClass.set(exts.assetsClass) 16 | } 17 | 18 | project.plugins.withType(JavaPlugin::class.java) { 19 | val javaConvention = project.convention.getPlugin(JavaPluginConvention::class.java) 20 | val main = javaConvention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) 21 | main.java.srcDir(project.buildDir.resolve("generated")) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/EngineExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.* 4 | 5 | inline fun PooledEngine.createComponent(): T = this.createComponent(T::class.java) 6 | 7 | inline fun PooledEngine.createComponentWith(block: T.() -> Unit): T { 8 | val component = this.createComponent(T::class.java) 9 | block.invoke(component) 10 | return component 11 | } 12 | 13 | 14 | fun PooledEngine.addEntity(builder: Entity.() -> Unit): Entity { 15 | val entity = this.createEntity() 16 | builder(entity) 17 | this.addEntity(entity) 18 | return entity 19 | } 20 | 21 | fun Engine.entity(vararg component: Class): Entity = this.getEntitiesFor(Family.all(*component).get()).first() 22 | 23 | fun Engine.removeAll(entities: Iterable): Unit = entities.forEach { this.removeEntity(it) } 24 | 25 | fun Engine.removeAllWith(vararg components: Class) { 26 | val entities = this.getEntitiesFor(Family.all(*components).get()).toList() 27 | for (e in entities) { 28 | this.removeEntity(e) 29 | } 30 | //removeAll(entities) 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LibGDX Addons 2 | 3 | Addons for LibGDX, for ease game development 4 | 5 | ## Gradle Plugins 6 | 7 | - [libgdx-gradle-plugin](./libgdx-gradle-plugin): Ease the setup of a libgdx project; 8 | - [assets-gradle-plugin](./assets-gradle-plugin): Create an Assets class with all assets filename; 9 | - [packr-gradle-plugin](./packr-gradle-plugin): Add Packr tasks to bundle a JVM with your game; 10 | - [aseprite-gradle-plugin](./aseprite-gradle-plugin): Export Aseprite file to spritesheet. 11 | 12 | ## LibGDX libraries 13 | 14 | - [core-addons](./core-addons): Extensions methods for libgdx core; 15 | - [ashley-addons](./ashley-addons): Extensions methods and several base component/systems for Ashley; 16 | - [aseprite-addons](./aseprite-addons): Allow to load Aseprite JSON and texture as Animation or extract slices; 17 | - [admob-addons](./admob-addons): Add support to Admob to your libgdx game; 18 | - [libgdx-test](./libgdx-test): Test library to control your game from a test. 19 | 20 | ## How to use it? 21 | 22 | See the [documentation website](https://dwursteisen.github.io/libgdx-addons/). 23 | 24 | ## Notes 25 | Please notes that everything is developed in Kotlin 26 | 27 | See also: 28 | - ktx : https://github.com/libktx/ktx 29 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step4/PlayerSystem.kt: -------------------------------------------------------------------------------- 1 | package step4 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.badlogic.ashley.core.Family 5 | import com.badlogic.ashley.systems.IteratingSystem 6 | import com.badlogic.gdx.Gdx 7 | import com.badlogic.gdx.Input 8 | import com.github.dwursteisen.libgdx.ashley.Position 9 | import com.github.dwursteisen.libgdx.ashley.get 10 | import step4.Player 11 | 12 | // tag::body[] 13 | class PlayerSystem : IteratingSystem(Family.all(Player::class.java).get()) { 14 | 15 | private val position = get() 16 | 17 | private val speed = 64f 18 | 19 | override fun processEntity(entity: Entity, deltaTime: Float) { 20 | if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) { 21 | entity[position].value.add(-speed * deltaTime, 0f) 22 | } else if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) { 23 | entity[position].value.add(speed * deltaTime, 0f) 24 | } 25 | 26 | if (Gdx.input.isKeyPressed(Input.Keys.UP)) { 27 | entity[position].value.add(0f, speed * deltaTime) 28 | } else if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) { 29 | entity[position].value.add(0f, -speed * deltaTime) 30 | } 31 | } 32 | } 33 | // end::body[] 34 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step3/MyGame.kt: -------------------------------------------------------------------------------- 1 | package step3 2 | 3 | import com.badlogic.gdx.Game 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.assets.AssetManager 6 | import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver 7 | import com.badlogic.gdx.graphics.GL20 8 | import com.badlogic.gdx.graphics.Texture 9 | import com.badlogic.gdx.graphics.g2d.SpriteBatch 10 | import com.github.dwursteisen.libgdx.graphics.RefreshableTextureLoader 11 | 12 | // tag::body[] 13 | class MyGame : Game() { 14 | 15 | private val assetManager: AssetManager = AssetManager() 16 | 17 | private lateinit var batch: SpriteBatch 18 | 19 | override fun create() { 20 | // <1> 21 | assetManager.setLoader(Texture::class.java, RefreshableTextureLoader(InternalFileHandleResolver())) 22 | assetManager.load(Assets.assets_dungeon_sheet_png, Texture::class.java) 23 | // <2> 24 | assetManager.finishLoading() 25 | batch = SpriteBatch() 26 | } 27 | 28 | override fun render() { 29 | // <3> 30 | Gdx.gl.glClearColor(0f, 0f, 0f, 1f) 31 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) 32 | 33 | // <4> 34 | val texture: Texture = assetManager[Assets.assets_dungeon_sheet_png] 35 | batch.begin() 36 | batch.draw(texture, 0f, 0f) 37 | batch.end() 38 | } 39 | } 40 | // end::body[] 41 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/fsm/EntityState.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley.fsm 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.github.dwursteisen.libgdx.ashley.EventData 5 | 6 | /** 7 | * Implement this class to represent a state. 8 | * 9 | * The lifecycle of a state is always in the same order: 10 | * 11 | * [enter] -> [update] -> [exit] 12 | * 13 | * [enter] will be called when the entity will enter in this new state. 14 | * [update] will be called for each render call when in this state. 15 | * [exit] will be called when the entity before being in another state. 16 | */ 17 | abstract class EntityState { 18 | 19 | /** 20 | * Called when an entity is going into this state. 21 | */ 22 | open fun enter(entity: Entity, eventData: EventData) = Unit 23 | 24 | /** 25 | * Called by the main loop when in this state. 26 | * 27 | * [entity] the current entity. 28 | * [time] the time since the entity is in this new state. 29 | * 30 | */ 31 | // TODO: add delta as third parameter 32 | open fun update(entity: Entity, time: Float) = Unit 33 | 34 | /** 35 | * Called when an entity is going into another state. 36 | */ 37 | open fun exit(entity: Entity, eventData: EventData) = Unit 38 | 39 | companion object { 40 | val STATE_NOP = object : EntityState() {} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /libgdx-test/src/test/kotlin/com/github/dwursteisen/libgdx/test/GameTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.test 2 | 3 | import com.badlogic.gdx.Input 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 5 | import org.junit.Rule 6 | import org.junit.Test 7 | import java.time.Duration 8 | 9 | class GameTest { 10 | 11 | @JvmField 12 | @Rule 13 | val gdx = LibGdxRule(GameUnderTest(), LwjglApplicationConfiguration().apply { 14 | width = 200 15 | height = 200 16 | }) 17 | 18 | 19 | @Test 20 | fun runThenScreenshot() { 21 | gdx.startGame() 22 | .wait(Duration.ofSeconds(1)) 23 | .screenshot("test1.png") 24 | .push(Input.Keys.UP) 25 | .wait(Duration.ofSeconds(1)) 26 | .release(Input.Keys.UP) 27 | .screenshot("test2.png") 28 | //.wait(Duration.ofSeconds(1)) 29 | //.screenshot("test3.png") 30 | } 31 | 32 | @Test 33 | fun runOtherTest() { 34 | gdx.startGame() 35 | .wait(Duration.ofSeconds(1)) 36 | .screenshot("test4.png") 37 | 38 | } 39 | 40 | @Test 41 | fun record() { 42 | gdx.startGame() 43 | .startRecord() 44 | .push(Input.Keys.UP) 45 | .wait(Duration.ofSeconds(1)) 46 | .release(Input.Keys.UP) 47 | .stopAndSaveRecord("record.gif") 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /aseprite-addons/src/main/kotlin/com/github/dwursteisen/libgdx/aseprite/AsepriteJsonLoader.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.aseprite 2 | 3 | import com.badlogic.gdx.assets.AssetDescriptor 4 | import com.badlogic.gdx.assets.AssetLoaderParameters 5 | import com.badlogic.gdx.assets.AssetManager 6 | import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader 7 | import com.badlogic.gdx.assets.loaders.FileHandleResolver 8 | import com.badlogic.gdx.files.FileHandle 9 | import com.badlogic.gdx.utils.Array 10 | import com.badlogic.gdx.utils.Json 11 | 12 | class AsepriteJsonParameter : AssetLoaderParameters() 13 | class AsepriteJsonLoader(resoler: FileHandleResolver) : AsynchronousAssetLoader(resoler) { 14 | 15 | private val json = Json().apply { 16 | setIgnoreUnknownFields(true) 17 | } 18 | 19 | private var data: AsepriteJson? = null 20 | 21 | override fun loadSync(manager: AssetManager, fileName: String, file: FileHandle, parameter: AsepriteJsonParameter?): AsepriteJson? { 22 | val copy = data 23 | data = null 24 | return copy 25 | } 26 | 27 | override fun getDependencies(fileName: String?, file: FileHandle?, parameter: AsepriteJsonParameter?): Array>? = null 28 | 29 | override fun loadAsync(manager: AssetManager, fileName: String, file: FileHandle, parameter: AsepriteJsonParameter?) { 30 | data = json.fromJson(file) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/docs/asciidoc/03_libgdx_gradle.adoc: -------------------------------------------------------------------------------- 1 | == LibGDX Gradle plugins 2 | 3 | TIP: WIP 4 | 5 | === Assets Gradle Plugin 6 | 7 | When developing a game, you need to manage resources. Time to time 8 | you need to rename a file. In such case, you need to track every time you 9 | reference the file name by a string. 10 | 11 | [source,kotlin] 12 | ---- 13 | val sprite = load("mySprite.png") 14 | ---- 15 | 16 | To avoid to search for every place you're loading the file using a `String`, 17 | the assets plugin can generate an `Assets` object that will hold all references to 18 | all your assets. 19 | 20 | .Assets.kt 21 | [source,kotlin] 22 | ---- 23 | object Assets { 24 | val mySprite: String = "mySprite.png" 25 | } 26 | ---- 27 | 28 | If you rename an asset, then the class will be re-generated and you game will not 29 | compile! Which is way better than compile and crash later. 30 | 31 | TIP: It's better to have a game that doesn't compile than a game that compile, 32 | run and crash only when the game will load the missing asset. 33 | 34 | ==== Configuration 35 | 36 | .build.gradle.kts 37 | [source,kotlin] 38 | ---- 39 | include::../../../assets-gradle-plugin/src/test/kotlin/com/github/dwursteisen/libgdx/assets/AssetsPluginTest.kt[tags="configuration"] 40 | ---- 41 | 42 | You can configure the plugin using several options: 43 | 44 | [source,kotlin] 45 | ---- 46 | include::../../../assets-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/assets/AssetsPluginExtension.kt[lines=7..] 47 | ---- 48 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/Vector2Exts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.math.Vector2 4 | 5 | /** 6 | * Return true if the x and y coordinates are close to the vector modulo the delta value. 7 | */ 8 | fun Vector2.isCloseTo(x: Float, y: Float, delta: Float): Boolean { 9 | return x.isBetween(x - delta, x + delta) && y.isBetween(y - delta, y + delta) 10 | } 11 | 12 | /** 13 | * See [isCloseTo] with x,y parameters. 14 | */ 15 | fun Vector2.isCloseTo(vector2: Vector2, delta: Float) = isCloseTo(vector2.x, vector2.y, delta) 16 | 17 | /** 18 | * Return true only if the coordinates are close to the vector. 19 | * Will return false otherwise OR if the coordinate match perfectly. 20 | * If [round] is true, the vector coordinates will be round to the target coordinates. 21 | * 22 | * It means that if you call the method twice, the first time the result will be [true] then [false]. 23 | */ 24 | fun Vector2.onlyCloseTo(x: Float, y: Float, delta: Float, round: Boolean = true): Boolean { 25 | if (this.x == x && this.y == y) return false 26 | val isCloseTo = isCloseTo(x, y, delta) 27 | if (isCloseTo && round) { 28 | this.set(x, y) 29 | } 30 | return isCloseTo 31 | } 32 | /** 33 | * Factory to create a new Vector2 class. 34 | * 35 | * Example: 36 | * 37 | * ``` 38 | * 12 v2 34 // will create a Vector2(12f, 34f) 39 | * ``` 40 | */ 41 | infix fun Number.v2(other: Number): Vector2 { 42 | return Vector2(this.toFloat(), other.toFloat()) 43 | } 44 | -------------------------------------------------------------------------------- /admob-addons/admob-core/src/main/kotlin/com.github.dwursteisen.libgdx.admob/GdxAdsManager.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.admob 2 | 3 | import com.badlogic.gdx.ApplicationListener 4 | 5 | 6 | class AdsQuery(private val manager: GdxAdsManager, var key: String, var format: AdsSize = AdsSize.BANNER, var flags: AdsPosition = 0, var callback: () -> Unit = {}) { 7 | fun close(): Unit { 8 | manager.close(this) 9 | } 10 | } 11 | 12 | 13 | abstract class GdxAdsManager { 14 | abstract fun initialize(listener: ApplicationListener) 15 | 16 | fun configure(key: String): AdsQueryMaker { 17 | return AdsQueryMaker(this, key) 18 | } 19 | 20 | class AdsQueryMaker(manager: GdxAdsManager, key: String) { 21 | 22 | private val query = AdsQuery(manager = manager, key = key) 23 | 24 | fun configure(key: String): AdsQueryMaker { 25 | query.key = key 26 | return this 27 | } 28 | 29 | fun usingFormat(format: AdsSize): AdsQueryMaker { 30 | query.format = format 31 | return this 32 | } 33 | 34 | fun position(flag: AdsPosition): AdsQueryMaker { 35 | query.flags = flag 36 | return this 37 | } 38 | 39 | 40 | fun whenLoaded(apply: () -> Unit): AdsQueryMaker { 41 | query.callback = apply 42 | return this 43 | } 44 | 45 | fun create(): AdsQuery = query 46 | 47 | } 48 | 49 | 50 | abstract fun load(query: AdsQuery) 51 | abstract fun close(query: AdsQuery) 52 | } -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | apply { 2 | plugin("java-gradle-plugin") 3 | plugin("java") 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | google() 9 | maven { 10 | url = uri("https://plugins.gradle.org/m2/") 11 | } 12 | jcenter() 13 | } 14 | 15 | configure { 16 | val core = create("core") 17 | create("desktop") { 18 | compileClasspath += core.output 19 | } 20 | create("android") { 21 | compileClasspath += core.output 22 | } 23 | 24 | main { 25 | resources { 26 | srcDir("src") 27 | // exclude the source code of the plugin itself 28 | exclude("main/**") 29 | // exclude tests 30 | exclude("test/**") 31 | } 32 | } 33 | } 34 | 35 | dependencies { 36 | compileOnly(kotlin("stdlib")) 37 | compileOnly(gradleApi()) 38 | implementation(project(":commons-gradle-plugin")) 39 | 40 | // -- only for template compiling -- // 41 | "coreImplementation"("com.badlogicgames.gdx:gdx:${Version.gdx}") 42 | "coreImplementation"("org.jetbrains.kotlin:kotlin-stdlib:${Version.kotlin}") 43 | 44 | "desktopImplementation"("com.badlogicgames.gdx:gdx-backend-lwjgl:${Version.gdx}") 45 | "desktopImplementation"("org.jetbrains.kotlin:kotlin-stdlib:${Version.kotlin}") 46 | "desktopImplementation"("com.badlogicgames.gdx:gdx-platform:${Version.gdx}:natives-desktop") 47 | 48 | testImplementation(TestDependencies.junit) 49 | testImplementation(gradleTestKit()) 50 | } 51 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ServiceLocator.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import kotlin.reflect.KProperty 4 | 5 | 6 | inline fun locate(): Locator = Locator(T::class.java) 7 | 8 | class Locator(private val clazz: Class) { 9 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T { 10 | return ServiceLocator.get(clazz) 11 | } 12 | } 13 | 14 | object ServiceLocator { 15 | 16 | private var cache: Map, Any> = emptyMap() 17 | 18 | @Suppress("UNCHECKED_CAST") 19 | operator fun get(clazz: Class): T { 20 | val result = cache[clazz] ?: tryCreateInstance(clazz) ?: locatingError(clazz) 21 | return result as T 22 | } 23 | 24 | @Suppress("UNCHECKED_CAST") 25 | private fun tryCreateInstance(clazz: Class): T? { 26 | val emptyConstructor = clazz.constructors.singleOrNull { it.parameterCount == 0 } 27 | emptyConstructor ?: return null 28 | 29 | val newInstance = emptyConstructor.newInstance() 30 | register(newInstance, clazz) 31 | return newInstance as T 32 | 33 | } 34 | 35 | private fun locatingError(clazz: Class): Nothing { 36 | TODO("Impossible to locate any registered class of type $clazz or to create a default instance of it.") 37 | } 38 | 39 | 40 | inline fun locate(): T { 41 | return this[T::class.java] 42 | } 43 | 44 | fun register(instance: Any, clazz: Class) { 45 | cache += clazz to instance 46 | } 47 | } -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/TextureSplitter.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx 2 | 3 | import com.badlogic.gdx.assets.AssetManager 4 | import com.badlogic.gdx.graphics.Texture 5 | import com.badlogic.gdx.graphics.g2d.Animation 6 | import com.badlogic.gdx.graphics.g2d.TextureRegion 7 | 8 | class TextureRegionMap(private val split: Array>) { 9 | operator fun get(column: Int, row: Int): TextureRegion { 10 | return split[row][column] 11 | } 12 | 13 | fun animations(frameDuration: Float, vararg columnAndRow: Pair): Animation { 14 | val frames = columnAndRow.map { get(it.first, it.second) } 15 | return Animation(frameDuration, *frames.toTypedArray()) 16 | } 17 | } 18 | 19 | class TextureSplitter(private val assetManager: AssetManager? = null) { 20 | fun split(texture: Texture, tileWidth: Int, tileHeight: Int): TextureRegionMap { 21 | return TextureRegionMap(TextureRegion.split(texture, tileWidth, tileHeight)) 22 | } 23 | 24 | fun split(resourceName: String, tileWidth: Int, tileHeight: Int): TextureRegionMap { 25 | assetManager ?: throw IllegalStateException("The texture splitter doesn't have any assetManager." + 26 | "To split a resource from the asset manager, it needs to be pass during the construction of the " + 27 | "TextureSplitter: TextureSplitter(yourAssetManager).") 28 | val texture: Texture = assetManager[resourceName] 29 | return split(texture, tileWidth, tileHeight) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.3.41" 5 | } 6 | 7 | group = "group.id.example" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation(kotlin("stdlib-jdk8")) 16 | } 17 | 18 | tasks.withType { 19 | kotlinOptions.jvmTarget = "1.8" 20 | } 21 | 22 | buildscript { 23 | dependencies { 24 | classpath("com.github.dwursteisen.libgdx-addons:libgdx-gradle-plugin:6162ea9") 25 | } 26 | 27 | repositories { 28 | mavenCentral() 29 | google() 30 | jcenter() 31 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 32 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 33 | maven { url = uri("https://jitpack.io") } 34 | } 35 | } 36 | 37 | apply(plugin = "libgdx") 38 | 39 | allprojects { 40 | repositories { 41 | mavenCentral() 42 | google() 43 | jcenter() 44 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 45 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 46 | maven { url = uri("https://jitpack.io") } 47 | } 48 | } 49 | 50 | project(":core") { 51 | dependencies { 52 | implementation("com.github.dwursteisen.libgdx-addons:core-addons:6162ea9") 53 | implementation("com.github.dwursteisen.libgdx-addons:ashley-addons:6162ea9") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step5/DoorSystem.kt: -------------------------------------------------------------------------------- 1 | package step5 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.badlogic.gdx.graphics.g2d.Animation 5 | import com.github.dwursteisen.libgdx.ashley.Animated 6 | import com.github.dwursteisen.libgdx.ashley.EventBus 7 | import com.github.dwursteisen.libgdx.ashley.EventData 8 | import com.github.dwursteisen.libgdx.ashley.fsm.EntityState 9 | import com.github.dwursteisen.libgdx.ashley.fsm.StateMachineSystem 10 | import com.github.dwursteisen.libgdx.ashley.get 11 | import step4.Door 12 | 13 | class DoorSystem(eventBus: EventBus) : StateMachineSystem(eventBus, Door::class.java) { 14 | 15 | private val animated = get() 16 | 17 | override fun describeMachine() { 18 | val close = object : EntityState() { 19 | override fun enter(entity: Entity, eventData: EventData) { 20 | entity[animated].animation.playMode = Animation.PlayMode.REVERSED 21 | entity[animated].time = 0f 22 | } 23 | } 24 | 25 | val open = object : EntityState() { 26 | override fun enter(entity: Entity, eventData: EventData) { 27 | entity[animated].animation.playMode = Animation.PlayMode.NORMAL 28 | entity[animated].time = 0f 29 | } 30 | } 31 | 32 | startsWith(close) { entity -> 33 | entity[animated].finishAnimation() 34 | } 35 | 36 | onState(close).on(EVENT_SWITCH_ON) { entity: Entity, event: EventData -> 37 | go(open, entity, event) 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # Mongo Explorer plugin: 25 | .idea/**/mongoSettings.xml 26 | 27 | ## File-based project format: 28 | *.iws 29 | 30 | ## Plugin-specific files: 31 | 32 | # IntelliJ 33 | /out/ 34 | 35 | # mpeltonen/sbt-idea plugin 36 | .idea_modules/ 37 | 38 | # JIRA plugin 39 | atlassian-ide-plugin.xml 40 | 41 | # Crashlytics plugin (for Android Studio and IntelliJ) 42 | com_crashlytics_export_strings.xml 43 | crashlytics.properties 44 | crashlytics-build.properties 45 | fabric.properties 46 | ### Gradle template 47 | .gradle 48 | /build/ 49 | 50 | # Ignore Gradle GUI config 51 | gradle-app.setting 52 | 53 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 54 | !gradle-wrapper.jar 55 | 56 | # Cache of project 57 | .gradletasknamecache 58 | 59 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 60 | 61 | .DS_Store 62 | .idea/ 63 | *.iml 64 | **/out/ 65 | **/build/ 66 | local.properties -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/template/gradle/TemplatePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.template.gradle 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | class TemplatePlugin : Plugin { 7 | override fun apply(project: Project) { 8 | project.tasks.create("template-core", GenerateModelTask::class.java) { 9 | it.template.set(Templates.CORE) 10 | it.lock.set(project.buildDir.resolve("templates").resolve("desktop.template")) 11 | it.targetDirectory.set(project.projectDir 12 | .resolve("core") 13 | .resolve("src") 14 | .resolve("main") 15 | ) 16 | } 17 | project.tasks.create("template-desktop", GenerateModelTask::class.java) { 18 | it.template.set(Templates.DESKTOP) 19 | it.lock.set(project.buildDir.resolve("templates").resolve("desktop.template")) 20 | it.targetDirectory.set(project.projectDir 21 | .resolve("desktop") 22 | .resolve("src") 23 | .resolve("main") 24 | ) 25 | } 26 | project.tasks.create("template-android", GenerateModelTask::class.java) { 27 | it.template.set(Templates.ANDROID) 28 | it.lock.set(project.buildDir.resolve("templates").resolve("desktop.template")) 29 | it.targetDirectory.set(project.projectDir 30 | .resolve("android") 31 | .resolve("src") 32 | .resolve("main") 33 | ) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /aseprite-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/aseprite/AsepritePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.aseprite 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.api.internal.CollectionCallbackActionDecorator 6 | import org.gradle.internal.reflect.Instantiator 7 | import javax.inject.Inject 8 | 9 | class AsepritePlugin @Inject constructor( 10 | private val instantiator: Instantiator, 11 | private val callbackDecorator: CollectionCallbackActionDecorator 12 | ) : Plugin { 13 | 14 | override fun apply(target: Project) { 15 | val exts = target.extensions.create( 16 | "aseprite", 17 | AsepritePluginExtensions::class.java, 18 | target, 19 | instantiator, 20 | callbackDecorator 21 | ) 22 | 23 | exts.all { ext -> 24 | target.tasks.register(ext.name + "Aseprite", AsepriteTask::class.java) { 25 | it.exec.set(exts.exec.orNull) 26 | it.baseDirectory.set(ext.baseDirectory.orNull) 27 | it.inputFiles.set(ext.inputFiles.orNull) 28 | it.outputDirectory.set(ext.outputDirectory.orNull) 29 | it.outputFiles.set(ext.outputFiles.orNull) 30 | it.scale.set(ext.scale.orNull) 31 | it.format.set(ext.format.orNull) 32 | it.json.set(ext.json.orNull) 33 | it.verbose.set(ext.verbose.orNull) 34 | it.sheetPack.set(ext.sheetPack.orNull) 35 | it.sheetHeight.set(ext.sheetHeight.orNull) 36 | it.sheetWidth.set(ext.sheetWidth.orNull) 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /aseprite-gradle-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Aseprite Gradle exporter 2 | 3 | Add new task to gradle which export your aseprite file to png and json 4 | 5 | ## How to use it? 6 | 7 | Add the plugin as build gradle dependencies 8 | 9 | Read the main [How to use it](../README.md#how-to-use-it) 10 | 11 | Activate the plugin and add an aseprite task 12 | 13 | ``` 14 | apply plugin: "com.github.dwursteisen.libgdx.aseprite.AsepritePlugin" 15 | 16 | 17 | task aseprite(type: com.github.dwursteisen.libgdx.aseprite.AsepriteTask, group: "aseprite") { 18 | json = true 19 | verbose = true 20 | outputDirectory = project.file(project.getBuildDir().absolutePath + "/aseprite") 21 | inputFiles = fileTree("./assets").include("**/*.ase") 22 | 23 | } 24 | 25 | ``` 26 | 27 | Setup Gradle to know where it should locate Aseprite : 28 | 29 | 30 | Configure it using `aseprite.exec` property (ie: in your` ~/.gradle/gradle.properties`) 31 | ``` 32 | aseprite.exec= 33 | ``` 34 | 35 | or using aseprite extension in your `build.gradle 36 | ``` 37 | aseprite { 38 | exec= 39 | } 40 | ``` 41 | 42 | 43 | MacOS specific : point to aseprite located into `/Aseprite.app/Contents/MacOS/aseprite` 44 | 45 | ## How to configure a task ? 46 | 47 | ``` 48 | task aseprite(type: com.github.dwursteisen.libgdx.aseprite.AsepriteTask, group: "aseprite") { 49 | inputFiles = 50 | outputDirectory = 51 | scale = 52 | format = 53 | json = 54 | verbose = 55 | } 56 | ``` 57 | 58 | ## Limitation 59 | -------------------------------------------------------------------------------- /packr-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/packr/PackrPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.packr 2 | 3 | import com.badlogicgames.packr.PackrConfig 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Project 6 | 7 | class PackrPlugin : Plugin { 8 | 9 | override fun apply(project: Project) { 10 | val container = project.container(PackrPluginExtension::class.java) { name -> PackrPluginExtension(name, project) } 11 | 12 | project.extensions.add("packr", container) 13 | 14 | container.all { ext -> 15 | val taskName = ext.name + "Packr" 16 | project.tasks.register(taskName, PackrTask::class.java) { 17 | it.platform.set(ext.platform.orNull) 18 | it.jdk.set(ext.jdk.orNull) 19 | it.executable.set(ext.executable.orNull) 20 | it.mainClass.set(ext.mainClass.orNull) 21 | it.vmArgs.set(ext.vmArgs.getOrElse(emptyList()) + macArgs()) 22 | it.minimizeJre.set(ext.minimizeJre.orNull) 23 | it.classpath.set(ext.classpath.orNull) 24 | it.outputDir.set(ext.outputDir.orNull) 25 | it.bundleIdentifier.set(ext.bundleIdentifier.orNull 26 | ?: tryDefaultBundleName(project, ext.platform.orNull)) 27 | it.verbose.set(ext.verbose.getOrElse(false)) 28 | } 29 | } 30 | } 31 | 32 | private fun macArgs(): List = listOf("-XstartOnFirstThread") 33 | 34 | private fun tryDefaultBundleName(project: Project, targetPlatform: PackrConfig.Platform?) = when (targetPlatform) { 35 | PackrConfig.Platform.MacOS -> project.name + ".app" 36 | else -> null 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/assets/dungeon.tmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 26,26,26,26,26,26,26,26,26,26,26,26,26, 7 | 26,26,26,26,26,26,26,26,26,26,26,26,26, 8 | 26,26,26,26,26,26,26,26,26,26,26,26,26, 9 | 26,26,26,26,26,26,26,26,26,26,26,26,26, 10 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 11 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 12 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 13 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 14 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 15 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 16 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 17 | 55,55,55,55,55,55,55,55,55,55,55,55,55, 18 | 55,55,55,55,55,55,55,55,55,55,55,55,55 19 | 20 | 21 | 22 | 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 27 | 105,105,105,105,105,105,105,105,105,105,105,105,105, 28 | 129,172,102,31,171,129,31,31,129,169,102,170,129, 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 32 | 0,0,0,0,0,0,0,0,0,0,153,0,0, 33 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 34 | 0,0,0,0,0,0,0,0,0,0,0,0,0, 35 | 0,0,0,0,0,0,0,0,0,0,0,0,0 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /libgdx-test/src/test/kotlin/com/github/dwursteisen/libgdx/test/GameUnderTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.test 2 | 3 | import com.badlogic.gdx.ApplicationListener 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.Input 6 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 7 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 8 | import com.badlogic.gdx.graphics.Color 9 | import com.badlogic.gdx.graphics.GL20 10 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer 11 | import com.badlogic.gdx.utils.viewport.ExtendViewport 12 | 13 | 14 | class GameUnderTest : ApplicationListener { 15 | 16 | private lateinit var shape: ShapeRenderer 17 | private val viewport = ExtendViewport(240f, 240f) 18 | 19 | private var size = 50f 20 | 21 | override fun render() { 22 | Gdx.gl.glClearColor(1f, 1f, 1f, 1f) 23 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) 24 | 25 | if (Gdx.input.isKeyPressed(Input.Keys.UP)) { 26 | size += 1f 27 | } else if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) { 28 | size -= 1f 29 | } 30 | 31 | shape.begin(ShapeRenderer.ShapeType.Filled) 32 | shape.projectionMatrix = viewport.camera.combined 33 | shape.color = Color.BLUE 34 | shape.circle(0f, 0f, size) 35 | shape.end() 36 | } 37 | 38 | override fun pause() { 39 | 40 | } 41 | 42 | override fun resume() { 43 | 44 | } 45 | 46 | override fun resize(width: Int, height: Int) { 47 | viewport.update(width, height) 48 | } 49 | 50 | override fun create() { 51 | shape = ShapeRenderer() 52 | } 53 | 54 | override fun dispose() { 55 | 56 | } 57 | 58 | } 59 | 60 | fun main(args: Array) { 61 | val config = LwjglApplicationConfiguration() 62 | config.width = 240 63 | config.height = 240 64 | 65 | LwjglApplication(GameUnderTest(), config) 66 | } -------------------------------------------------------------------------------- /libgdx-test/README.md: -------------------------------------------------------------------------------- 1 | # LibGDX Test 2 | 3 | ## LibGdxRule 4 | 5 | LibGdxRule is a JUnit Rule which allow to run and control 6 | your game from your test. 7 | 8 | Thanks to a DSL, you can create a specific scenario to test 9 | your game and take automatic screenshots or recording. 10 | 11 | 12 | ## How to use it? 13 | 14 | 15 | Add the JUnit Rule `LibGdxRule` to your unit test 16 | 17 | ``` 18 | class TestClass { 19 | 20 | @JvmField 21 | @Rule 22 | val gdx = LibGdxRule(GameUnderTest(), LwjglApplicationConfiguration().apply { 23 | width = 200 24 | height = 200 25 | }) 26 | 27 | // ... 28 | } 29 | ``` 30 | 31 | Then, the rule will expose a DSL that will help you to describe your test case. 32 | 33 | ``` 34 | @Test 35 | fun runThenScreenshot() { 36 | gdx.startGame() 37 | .screenshot("test1.png") 38 | .push(Input.Keys.UP) 39 | .wait(Duration.ofSeconds(1)) 40 | .release(Input.Keys.UP) 41 | .screenshot("test2.png") 42 | .wait(Duration.ofSeconds(2)) 43 | .screenshot("test3.png") 44 | } 45 | ``` 46 | 47 | The DSL expose several methods and control inputs (like pusing keys, touch on the screen, ...) 48 | It help you too to record the game session. It can take screenshots too. 49 | 50 | ## Example of use 51 | 52 | - Automatic screenshot generator for tools like `fastlane` : keep 53 | screenshots from your application store alway updated ! 54 | 55 | - Automatic game testing : help you to test edge case. 56 | 57 | - Automatic game recording. 58 | 59 | ## Issues 60 | 61 | You may need to update the working directory, to help LibGdx 62 | to found assets. But you can change the jUnit working directory 63 | by updating your gradle build file : 64 | 65 | ``` 66 | test { 67 | // path is relative to current project 68 | workingDir = project.file("../android/src/main/assets") 69 | } 70 | ``` 71 | 72 | -------------------------------------------------------------------------------- /core-addons/src/main/kotlin/com/github/dwursteisen/libgdx/graphics/RefreshableTexture.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.graphics 2 | 3 | import com.badlogic.gdx.files.FileHandle 4 | import com.badlogic.gdx.graphics.Texture 5 | import com.badlogic.gdx.graphics.TextureData 6 | import com.badlogic.gdx.graphics.glutils.FileTextureData 7 | 8 | /** 9 | * Refreshable texture. 10 | * 11 | * The texture can reload using the method `refresh`. 12 | * 13 | * It's very convenient when the texture needs to be updated without without reloading a game. 14 | * The purpose of this class is to be used mainly during development. 15 | * 16 | * For performance reason, this type should be avoided in production. 17 | * 18 | * [Texture] can be replaced automatically by this class by using the [com.badlogic.gdx.assets.AssetManager] and replacing 19 | * the texture load with [RefreshableTextureLoader] 20 | * 21 | */ 22 | class RefreshableTexture(texture: Texture) : Texture(texture.textureData) { 23 | 24 | private var lastUpdate: Long 25 | private val fileHandle: FileHandle 26 | 27 | init { 28 | val textureData = textureData 29 | fileHandle = when (textureData) { 30 | is FileTextureData -> textureData.fileHandle 31 | else -> throw IllegalArgumentException("Texture not created using FileHandler are not yet supported.") 32 | } 33 | lastUpdate = fileHandle.lastModified() 34 | TextureWatchDogs.register(this) 35 | } 36 | 37 | fun shouldRefresh(): Boolean = lastUpdate != fileHandle.lastModified() 38 | 39 | fun refresh() { 40 | dispose() 41 | load(TextureData.Factory.loadFromFile(fileHandle, null, false)) 42 | lastUpdate = fileHandle.lastModified() 43 | } 44 | 45 | override fun equals(other: Any?): Boolean { 46 | return (other as? RefreshableTexture)?.let { 47 | fileHandle == other.fileHandle 48 | } ?: false 49 | } 50 | 51 | override fun hashCode(): Int { 52 | return fileHandle.hashCode() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /assets-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/assets/AssetsTask.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.assets 2 | 3 | import com.github.dwursteisen.libgdx.gradle.createProperty 4 | import com.squareup.kotlinpoet.FileSpec 5 | import com.squareup.kotlinpoet.PropertySpec 6 | import com.squareup.kotlinpoet.TypeSpec 7 | import org.gradle.api.DefaultTask 8 | import org.gradle.api.file.FileCollection 9 | import org.gradle.api.tasks.InputFiles 10 | import org.gradle.api.tasks.OutputFile 11 | import org.gradle.api.tasks.TaskAction 12 | import java.io.File 13 | 14 | open class AssetsTask : DefaultTask() { 15 | 16 | init { 17 | group = "libgdx" 18 | description = "Create static class referencing assets file name." 19 | } 20 | 21 | @InputFiles 22 | val assetsDirectory = project.createProperty() 23 | 24 | @OutputFile 25 | val assetsClass = project.createProperty() 26 | 27 | @TaskAction 28 | fun generate() { 29 | val file = FileSpec.builder("", assetsClass.get().nameWithoutExtension) 30 | val builder = TypeSpec.objectBuilder(assetsClass.get().nameWithoutExtension) 31 | 32 | assetsDirectory.get().files.forEach { 33 | val base = if (it.isDirectory) it else it.parentFile 34 | appendDirectory(it, base, builder) 35 | } 36 | 37 | file.addType(builder.build()) 38 | file.build().writeTo(assetsClass.get().parentFile) 39 | } 40 | 41 | private fun appendDirectory(current: File, base: File, builder: TypeSpec.Builder, prefix: String = "") { 42 | if (current.isDirectory) { 43 | current.listFiles()?.forEach { 44 | appendDirectory(it, base, builder, prefix + current.nameWithoutExtension + "_") 45 | } 46 | } else { 47 | builder.addProperty( 48 | PropertySpec.builder(prefix + current.name.replace(".", "_"), String::class) 49 | .initializer("%S", current.relativeTo(base).path) 50 | .build() 51 | ) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /aseprite-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/aseprite/AsepritePluginExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.aseprite 2 | 3 | import com.github.dwursteisen.libgdx.gradle.createProperty 4 | import org.gradle.api.Named 5 | import org.gradle.api.Project 6 | import org.gradle.api.file.FileCollection 7 | import org.gradle.api.internal.AbstractValidatingNamedDomainObjectContainer 8 | import org.gradle.api.internal.CollectionCallbackActionDecorator 9 | import org.gradle.internal.reflect.Instantiator 10 | import java.io.File 11 | 12 | open class AsepriteExtension(private val name: String, project: Project) : Named { 13 | override fun getName(): String = name 14 | 15 | val inputFiles = project.createProperty() 16 | 17 | val outputDirectory = project.createProperty() 18 | 19 | val baseDirectory = project.createProperty() 20 | 21 | val outputFiles = project.createProperty() 22 | 23 | val scale = project.createProperty().value(1.0) 24 | 25 | val format = project.createProperty().value(AsepriteFormat.HASH) 26 | 27 | val json = project.createProperty().value(true) 28 | 29 | val verbose = project.createProperty().value(false) 30 | 31 | val sheetPack = project.createProperty().value(true) 32 | 33 | val sheetHeight = project.createProperty() 34 | val sheetWidth = project.createProperty() 35 | } 36 | 37 | open class AsepritePluginExtensions( 38 | private val project: Project, 39 | instantiator: Instantiator, 40 | callbackActionDecorator: CollectionCallbackActionDecorator 41 | ) : AbstractValidatingNamedDomainObjectContainer( 42 | AsepriteExtension::class.java, 43 | instantiator, 44 | callbackActionDecorator 45 | ) { 46 | 47 | override fun doCreate(name: String): AsepriteExtension { 48 | return AsepriteExtension(name, project) 49 | } 50 | 51 | val exec = project.createProperty() 52 | .value(project.properties["aseprite.exec"]?.let { File(it.toString()) }) 53 | } 54 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step5/SwitchSystem.kt: -------------------------------------------------------------------------------- 1 | package step5 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.badlogic.ashley.core.Family.all 5 | import com.badlogic.gdx.graphics.g2d.Animation 6 | import com.badlogic.gdx.math.Rectangle 7 | import com.github.dwursteisen.libgdx.ashley.* 8 | import com.github.dwursteisen.libgdx.ashley.fsm.EntityState 9 | import com.github.dwursteisen.libgdx.ashley.fsm.StateMachineSystem 10 | import step4.Player 11 | import step4.Switch 12 | 13 | class SwitchSystem(eventBus: EventBus) : StateMachineSystem(eventBus, Switch::class.java) { 14 | 15 | private val animated = get() 16 | private val position = get() 17 | 18 | override fun describeMachine() { 19 | val off = object : EntityState() { 20 | override fun enter(entity: Entity, eventData: EventData) { 21 | entity[animated].animation.playMode = Animation.PlayMode.REVERSED 22 | entity[animated].time = 0f 23 | } 24 | } 25 | 26 | val on = object : EntityState() { 27 | override fun enter(entity: Entity, eventData: EventData) { 28 | entity[animated].animation.playMode = Animation.PlayMode.NORMAL 29 | entity[animated].time = 0f 30 | } 31 | 32 | override fun update(entity: Entity, time: Float) { 33 | val player = engine.getEntitiesFor(all(Player::class.java).get()).first() 34 | Rectangle.tmp.set(player[position].value.x, player[position].value.y, 16f, 16f) 35 | Rectangle.tmp2.set(entity[position].value.x, entity[position].value.y, 16f, 16f) 36 | 37 | if (Rectangle.tmp.overlaps(Rectangle.tmp2)) { 38 | eventBus.emit(EVENT_SWITCH_ON) 39 | } 40 | } 41 | } 42 | 43 | startsWith(on) { entity -> 44 | entity[animated].finishAnimation() 45 | } 46 | 47 | onState(on).on(EVENT_SWITCH_ON) { entity: Entity, event: EventData -> 48 | go(off, entity, event) 49 | } 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/test/kotlin/com/github/dwursteisen/libgdx/gradle/TemplatePluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle 2 | 3 | import org.assertj.core.api.Assertions.* 4 | import org.gradle.testkit.runner.GradleRunner 5 | import org.gradle.testkit.runner.TaskOutcome 6 | import org.junit.Before 7 | import org.junit.Rule 8 | import org.junit.Test 9 | import org.junit.rules.TemporaryFolder 10 | import java.io.File 11 | 12 | class TemplatePluginTest { 13 | 14 | @Rule 15 | @JvmField 16 | val temporaryFolder = TemporaryFolder() 17 | 18 | lateinit var buildFile: File 19 | 20 | @Before 21 | fun setUp() { 22 | buildFile = temporaryFolder.newFile("build.gradle.kts") 23 | buildFile.writeText( 24 | """ 25 | plugins { 26 | kotlin("jvm") version "1.3.30" 27 | id("com.github.dwursteisen.libgdx.template") 28 | } 29 | 30 | buildscript { 31 | repositories { 32 | mavenCentral() 33 | } 34 | } 35 | 36 | allprojects { 37 | 38 | group = "com.github.dwursteisen" 39 | version = "1.0-SNAPSHOT" 40 | 41 | repositories { 42 | mavenCentral() 43 | } 44 | 45 | } 46 | """.trimIndent() 47 | ) 48 | } 49 | 50 | @Test 51 | fun `it should create mandatory folders`() { 52 | val result = GradleRunner.create() 53 | .withProjectDir(temporaryFolder.root) 54 | .withArguments("template-core", "template-desktop") 55 | .withPluginClasspath() 56 | .build() 57 | 58 | assert(result.task(":template-core")?.outcome == TaskOutcome.SUCCESS) 59 | assert(result.task(":template-desktop")?.outcome == TaskOutcome.SUCCESS) 60 | 61 | val gameClass = File(temporaryFolder.root, "core/src/main/kotlin/libgdx/GameClass.kt") 62 | assertThat(gameClass).isFile() 63 | 64 | val assetsDirectory = File(temporaryFolder.root, "core/src/main/assets/") 65 | assertThat(assetsDirectory).isDirectory() 66 | 67 | val desktopDirectory = File(temporaryFolder.root, "desktop/src/main/kotlin/libgdx/Main.kt") 68 | assertThat(desktopDirectory).isFile() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/gradle/internal/CorePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle.internal 2 | 3 | import com.github.dwursteisen.libgdx.assets.AssetsTask 4 | import com.github.dwursteisen.libgdx.gradle.LibGDXExtensions 5 | import org.gradle.api.Plugin 6 | import org.gradle.api.Project 7 | import org.jetbrains.kotlin.gradle.dsl.KotlinCompile 8 | import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions 9 | 10 | class CorePlugin : Plugin { 11 | override fun apply(project: Project) { 12 | // Add Java plugin to access sourceSets extensions 13 | project.apply { it.plugin("org.gradle.java") } 14 | project.apply { it.plugin("org.jetbrains.kotlin.jvm") } 15 | project.apply { it.plugin("java-library") } 16 | 17 | project.rootProject.extensions.configure(LibGDXExtensions::class.java) { exts -> 18 | val version = exts.version 19 | project.dependencies.add("api", "com.badlogicgames.gdx:gdx:$version") 20 | project.dependencies.add("implementation", "org.jetbrains.kotlin:kotlin-stdlib") 21 | addAssetsTask(project, exts) 22 | } 23 | 24 | setupKotlin(project) 25 | } 26 | 27 | private fun setupKotlin(project: Project) { 28 | project.tasks.withType(KotlinCompile::class.java).forEach { 29 | it.kotlinOptions { 30 | this as KotlinJvmOptions 31 | // force the compilation at 1.8 as it may target Android platform 32 | this.jvmTarget = "1.8" 33 | this.freeCompilerArgs = listOf("-Xjsr305=strict") 34 | } 35 | } 36 | } 37 | 38 | private fun addAssetsTask(project: Project, exts: LibGDXExtensions) { 39 | project.apply { it.plugin("assets") } 40 | project.tasks.withType(AssetsTask::class.java) { task -> 41 | val assetsDirectory = exts.assetsDirectory.orNull ?: project.tryFindAssetsDirectory() 42 | task.assetsDirectory.set(project.files(assetsDirectory)) 43 | 44 | val compileKotlinTask = project.tasks.withType(KotlinCompile::class.java) 45 | compileKotlinTask.forEach { compiler -> 46 | compiler.dependsOn(task) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/gradle/internal/ProjectExts.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle.internal 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.file.FileVisitDetails 5 | import org.gradle.api.file.FileVisitor 6 | import org.gradle.api.tasks.SourceSetContainer 7 | import java.io.File 8 | 9 | fun Project.tryFindAssetsDirectory(): File? { 10 | return project.rootDir.resolve("core/src/main/assets") 11 | } 12 | 13 | private fun Project.tryFindClassesWhichMatch(filter: (Sequence) -> Boolean): List { 14 | val sourceSets = project.extensions.getByName("sourceSets") as SourceSetContainer 15 | val mainClassVisitor = MainClassVisitor(filter) 16 | val main = sourceSets.firstOrNull { it.name == "main" } ?: return emptyList() 17 | main.allSource 18 | .asFileTree 19 | .visit(mainClassVisitor) 20 | 21 | return mainClassVisitor.main 22 | } 23 | 24 | private class MainClassVisitor(val filter: (Sequence) -> Boolean, var main: List = emptyList()) : 25 | FileVisitor { 26 | 27 | override fun visitDir(dirDetails: FileVisitDetails) = Unit 28 | 29 | override fun visitFile(fileDetails: FileVisitDetails) { 30 | val use = fileDetails.open().use { 31 | filter.invoke(it.bufferedReader().lineSequence()) 32 | } 33 | 34 | if (use) { 35 | main += fileDetails.path 36 | .replace(".kt", "") 37 | .replace("/", ".") 38 | } 39 | } 40 | } 41 | 42 | fun Project.tryFindMainClass(): List { 43 | return project.subprojects.firstOrNull { it.name == "desktop" } 44 | ?.tryFindClassesWhichMatch { lines -> 45 | lines.filter { line -> line.contains("fun main(") || line.contains("import com.badlogic.gdx.backends.lwjgl.LwjglApplication") } 46 | .count() >= 2 47 | } ?: emptyList() 48 | } 49 | 50 | fun Project.tryFindAndroidMainClass(): List { 51 | return project.subprojects.firstOrNull { it.name == "android" } 52 | ?.tryFindClassesWhichMatch { lines -> 53 | lines.filter { line -> 54 | line.contains(": AndroidApplication()") || line.contains("import com.badlogic.gdx.backends.android.AndroidApplication") 55 | } 56 | .count() >= 2 57 | } ?: emptyList() 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step4/MyGame.kt: -------------------------------------------------------------------------------- 1 | package step4 2 | 3 | import Assets 4 | import com.badlogic.ashley.core.Engine 5 | import com.badlogic.gdx.Game 6 | import com.badlogic.gdx.Gdx 7 | import com.badlogic.gdx.assets.AssetManager 8 | import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver 9 | import com.badlogic.gdx.graphics.GL20 10 | import com.badlogic.gdx.graphics.Texture 11 | import com.badlogic.gdx.utils.viewport.FitViewport 12 | import com.badlogic.gdx.utils.viewport.Viewport 13 | import com.github.dwursteisen.libgdx.TextureSplitter 14 | import com.github.dwursteisen.libgdx.ashley.* 15 | import com.github.dwursteisen.libgdx.graphics.RefreshableTextureLoader 16 | import com.github.dwursteisen.libgdx.v2 17 | 18 | const val SPRITE = 1 19 | 20 | // tag::body[] 21 | class MyGame : Game() { 22 | 23 | private val assetManager: AssetManager = AssetManager() 24 | 25 | // <1> 26 | private val engine = Engine() 27 | 28 | private val viewport: Viewport = FitViewport(200f, 200f) 29 | 30 | override fun create() { 31 | assetManager.setLoader(Texture::class.java, RefreshableTextureLoader(InternalFileHandleResolver())) 32 | assetManager.load(Assets.assets_dungeon_sheet_png, Texture::class.java) 33 | assetManager.finishLoading() 34 | 35 | // <2> 36 | engine.addSystem(RenderSystem(viewport, 37 | mapOf(SPRITE to SpriteStrategy()) 38 | )) 39 | engine.addSystem(PlayerSystem()) 40 | 41 | // <3> 42 | val split = TextureSplitter(assetManager).split( 43 | Assets.assets_dungeon_sheet_png, 16, 16 44 | ) 45 | val playerSprite = split.get(column = 19, row = 7) 46 | 47 | // <4> 48 | val player = engine.createEntity().apply { 49 | add(Player()) 50 | add(Position()) 51 | add(Size(16 v2 16)) 52 | add(Textured(texture = playerSprite)) 53 | add(Render(SPRITE)) 54 | } 55 | engine.addEntity(player) 56 | } 57 | 58 | override fun render() { 59 | Gdx.gl.glClearColor(0f, 0f, 0f, 1f) 60 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) 61 | 62 | // <5> 63 | engine.update(Gdx.graphics.deltaTime) 64 | } 65 | 66 | override fun resize(width: Int, height: Int) { 67 | viewport.update(width, height) 68 | } 69 | } 70 | // end::body[] 71 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/test/kotlin/com/github/dwursteisen/libgdx/gradle/LibGDXPluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.gradle.testkit.runner.GradleRunner 5 | import org.gradle.testkit.runner.TaskOutcome 6 | import org.junit.Before 7 | import org.junit.Rule 8 | import org.junit.Test 9 | import org.junit.rules.TemporaryFolder 10 | import java.io.File 11 | 12 | class LibGDXPluginTest { 13 | 14 | @Rule 15 | @JvmField 16 | val temporaryFolder = TemporaryFolder() 17 | 18 | lateinit var buildFile: File 19 | 20 | @Before 21 | fun setUp() { 22 | buildFile = temporaryFolder.newFile("build.gradle.kts") 23 | buildFile.writeText( 24 | """ 25 | plugins { 26 | kotlin("jvm") version "1.3.30" 27 | id("libgdx") 28 | } 29 | 30 | buildscript { 31 | repositories { 32 | mavenCentral() 33 | google() 34 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 35 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 36 | } 37 | } 38 | 39 | allprojects { 40 | 41 | group = "com.github.dwursteisen" 42 | version = "1.0-SNAPSHOT" 43 | 44 | repositories { 45 | mavenCentral() 46 | google() 47 | jcenter() 48 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 49 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 50 | } 51 | 52 | } 53 | """.trimIndent() 54 | ) 55 | } 56 | 57 | @Test 58 | fun `it should create mandatory folders`() { 59 | val settings = temporaryFolder.newFile("settings.gradle.kts") 60 | 61 | val result = GradleRunner.create() 62 | .withProjectDir(temporaryFolder.root) 63 | .withArguments(":build") 64 | .withPluginClasspath() 65 | .build() 66 | 67 | assertThat(result.task(":build")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 68 | 69 | val coreDirectory = File(temporaryFolder.root, "core/src/main/kotlin/") 70 | assertThat(coreDirectory).isDirectory() 71 | 72 | val desktopDirectory = File(temporaryFolder.root, "desktop/src/main/kotlin/") 73 | assertThat(desktopDirectory).isDirectory() 74 | 75 | assertThat(settings.readText()).contains("include(\"core\")") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /admob-addons/admob-desktop/src/main/kotlin/com.github.dwursteisen.libgdx.admob/MockDesktopAdsRenderer.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.admob 2 | 3 | import com.badlogic.gdx.ApplicationListener 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.graphics.Color 6 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer 7 | import com.badlogic.gdx.utils.viewport.ScreenViewport 8 | 9 | /** 10 | * Created by david on 16/10/2017. 11 | */ 12 | internal class MockDesktopAdsRenderer(val delegate: ApplicationListener) : ApplicationListener { 13 | 14 | private lateinit var batch: ShapeRenderer 15 | private val viewport: ScreenViewport = ScreenViewport() 16 | 17 | private var query: AdsQuery? = null 18 | 19 | override fun render() { 20 | delegate.render() 21 | query?.let { 22 | 23 | val xx = if (it.flags and CENTER == CENTER) { 24 | (viewport.screenWidth - it.format.size.x) * 0.5f 25 | } else if (it.flags and RIGHT == RIGHT) { 26 | viewport.screenWidth - it.format.size.x 27 | } else { 28 | 0f 29 | } 30 | 31 | val yy = if (it.flags and UP == UP) { 32 | viewport.screenHeight - it.format.size.y 33 | } else { 34 | 0f 35 | } 36 | 37 | batch.begin(ShapeRenderer.ShapeType.Filled) 38 | batch.projectionMatrix = viewport.camera.combined 39 | batch.color = Color.WHITE 40 | // TODO: draw an fake ads 41 | batch.rect(xx, yy, it.format.size.x, it.format.size.y) 42 | batch.end() 43 | } 44 | 45 | 46 | } 47 | 48 | override fun pause() { 49 | delegate.pause() 50 | } 51 | 52 | override fun resume() { 53 | delegate.resume() 54 | } 55 | 56 | override fun resize(width: Int, height: Int) { 57 | val w = Gdx.graphics.width * 0.5f 58 | val h = Gdx.graphics.height * 0.5f 59 | 60 | viewport.update(width, height) 61 | viewport.camera.position.set(w, h, 0f) 62 | viewport.camera.update() 63 | delegate.resize(width, height) 64 | } 65 | 66 | override fun create() { 67 | batch = ShapeRenderer() 68 | 69 | delegate.create() 70 | 71 | } 72 | 73 | override fun dispose() { 74 | delegate.dispose() 75 | } 76 | 77 | fun load(query: AdsQuery) { 78 | this.query = query 79 | } 80 | 81 | fun close(query: AdsQuery) { 82 | this.query = null 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /aseprite-addons/src/main/kotlin/com/github/dwursteisen/libgdx/aseprite/AsepriteModel.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.aseprite 2 | 3 | import com.badlogic.gdx.files.FileHandle 4 | import com.badlogic.gdx.graphics.Texture 5 | import com.badlogic.gdx.graphics.g2d.TextureRegion 6 | import com.badlogic.gdx.utils.Json 7 | import java.math.BigInteger 8 | 9 | 10 | inline fun Json.fromJson(json: FileHandle) = this.fromJson(T::class.java, json)!! 11 | 12 | fun Int.gcd(other: Int): Int { 13 | val b1 = BigInteger.valueOf(this.toLong()) 14 | val b2 = BigInteger.valueOf(other.toLong()) 15 | val gcd = b1.gcd(b2) 16 | return gcd.toInt() 17 | } 18 | 19 | 20 | open class AsepriteBound(val x: Int = 0, val y: Int = 0, val w: Int = 0, val h: Int = 0) 21 | open class AsepriteSliceData(val frame: Int = 0, val bounds: AsepriteBound = AsepriteBound()) 22 | open class AsepriteFrameTag(val name: String = "", val from: Int = 0, val to: Int = 0, val direction: String = "") 23 | open class AsepriteSlices(val name: String = "", val data: String = "", val color: String = "", val keys: Array = emptyArray()) { 24 | fun textureRegion(texture: Texture, offset: Pair = Pair(0, 0)): TextureRegion { 25 | val bounds = keys.first().bounds 26 | return TextureRegion(texture, offset.first + bounds.x, offset.second + bounds.y, bounds.w, bounds.h) 27 | } 28 | 29 | fun toAnimationSlices(parent: Aseprite) = AnimationSlices(parent, this) 30 | } 31 | 32 | open class AsepriteMetaData(val frameTags: List = emptyList(), val slices: List = emptyList()) 33 | open class AsepriteFrameData(val x: Int = 0, val y: Int = 0, val w: Int = 0, val h: Int = 0) 34 | open class AsepriteSourceSize(val w: Int = 0, val h: Int = 0) 35 | open class AsepriteFrame(val frame: AsepriteFrameData = AsepriteFrameData(), val sourceSize: AsepriteSourceSize = AsepriteSourceSize(), val duration: Int = 100) 36 | open class AsepriteJson(val frames: HashMap = HashMap(), val meta: AsepriteMetaData = AsepriteMetaData()) { 37 | fun slices(name: String) = meta.slices.filter { it.name == name }.firstOrNull() ?: invalidSlice(name) 38 | 39 | private fun invalidSlice(name: String): Nothing = TODO("Invalid slice name $name. Other candidates : ${meta.slices.map { it.name }}") 40 | fun asFrameIndexedMap() = this.frames.entries.map { 41 | if (it.key.contains(" ")) { 42 | val id = it.key.substringAfterLast(" ").replace("\\.ase.*".toRegex(), "").toInt() 43 | id to it.value 44 | } else { 45 | 0 to it.value 46 | } 47 | }.toMap() 48 | } -------------------------------------------------------------------------------- /libgdx-test/src/main/kotlin/com/github/dwursteisen/libgdx/test/GdxDsl.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.test 2 | 3 | import java.time.Duration 4 | 5 | 6 | class GdxDsl(private val rule: LibGdxRule) { 7 | 8 | /** 9 | * Wait a specific amount of time. 10 | * 11 | * 12 | * dsl.wait(Duration.ofSeconds(1)) 13 | * 14 | */ 15 | fun wait(duration: Duration): GdxDsl { 16 | rule.add(InputAction.Wait(duration.toMillis().toFloat() / 1000f)) 17 | return this 18 | } 19 | 20 | /** 21 | * Push the key. Please note that you'll have to release the key by yourself ! 22 | * 23 | * @key : a Input.Keys code 24 | */ 25 | fun push(key: Int): GdxDsl { 26 | rule.add(InputAction.Push(key)) 27 | return this 28 | } 29 | 30 | 31 | /** 32 | * Push the key and release the key. 33 | * 34 | * @key : a Input.Keys code 35 | */ 36 | fun press(key: Int): GdxDsl { 37 | rule.add(InputAction.Push(key)) 38 | rule.add(InputAction.Release(key)) 39 | return this 40 | } 41 | 42 | /** 43 | * Release a key previously pushed 44 | * 45 | * @key : a Input.Keys code 46 | */ 47 | fun release(key: Int): GdxDsl { 48 | rule.add(InputAction.Release(key)) 49 | return this 50 | } 51 | 52 | fun touch(): GdxDsl { 53 | notYetImplemented() 54 | } 55 | 56 | 57 | /** 58 | * type characters 59 | */ 60 | fun type(str: String): GdxDsl { 61 | str.forEach { char -> 62 | rule.add(InputAction.Type(char)) 63 | } 64 | return this 65 | } 66 | 67 | /** 68 | * Touch on the screen (x, y) coordinate 69 | * 70 | */ 71 | fun touch(x: Int, y: Int): GdxDsl { 72 | rule.add(InputAction.Touch(x, y)) 73 | return this; 74 | } 75 | 76 | fun startRecord(): GdxDsl { 77 | rule.add(InputAction.StartRecord()) 78 | return this; 79 | } 80 | 81 | fun stopAndSaveRecord(filename: String): GdxDsl { 82 | rule.add(InputAction.StopRecord(filename)) 83 | return this 84 | } 85 | 86 | /** 87 | * Take a screenshot and save it using the name 88 | */ 89 | fun screenshot(name: String): GdxDsl { 90 | rule.add(InputAction.Screenshot(name)) 91 | return this 92 | } 93 | 94 | 95 | private fun notYetImplemented(): Nothing { 96 | TODO("""Not yet implemented yet ! Feel free to create a pull request on github for it 97 | | 98 | |You can get more info on https://github.com/dwursteisen/libgdx-addons/tree/master/libgdx-test 99 | """.trimMargin()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /assets-gradle-plugin/src/test/kotlin/com/github/dwursteisen/libgdx/assets/AssetsPluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.assets 2 | 3 | import org.gradle.testkit.runner.GradleRunner 4 | import org.gradle.testkit.runner.TaskOutcome 5 | import org.junit.Before 6 | import org.junit.Rule 7 | import org.junit.Test 8 | import org.junit.rules.TemporaryFolder 9 | import java.io.File 10 | 11 | class AssetsPluginTest { 12 | 13 | @Rule 14 | @JvmField 15 | val temporaryFolder = TemporaryFolder() 16 | 17 | lateinit var buildFile: File 18 | 19 | @Before 20 | fun setUp() { 21 | buildFile = temporaryFolder.newFile("build.gradle.kts") 22 | buildFile.writeText(""" 23 | plugins { 24 | id("assets") 25 | } 26 | """.trimIndent()) 27 | } 28 | 29 | @Test 30 | fun `it should create a Assets object`() { 31 | val asset = File(temporaryFolder.newFolder("src", "main", "assets"), "example.txt") 32 | asset.writeText("hello world") 33 | val inFolder = File(temporaryFolder.newFolder("src", "main", "assets", "folder"), "in_folder.txt") 34 | inFolder.writeText("hello folder") 35 | 36 | val result = GradleRunner.create() 37 | .withProjectDir(temporaryFolder.root) 38 | .withArguments("assets") 39 | .withPluginClasspath() 40 | .build() 41 | 42 | assert(result.task(":assets")?.outcome == TaskOutcome.SUCCESS) 43 | val generated = File(temporaryFolder.root, "build/generated/Assets.kt") 44 | assert(generated.isFile) 45 | assert(generated.readText().contains("example.txt")) 46 | assert(generated.readText().contains("folder/in_folder.txt")) 47 | } 48 | 49 | @Test 50 | fun `it should create a Assets object in another directory`() { 51 | buildFile.writeText(""" 52 | // tag::configuration[] 53 | import com.github.dwursteisen.libgdx.assets.AssetsPlugin 54 | import com.github.dwursteisen.libgdx.assets.AssetsPluginExtension 55 | 56 | plugins { 57 | id("assets") 58 | } 59 | apply() 60 | 61 | configure { 62 | assetsClass.set(project.buildDir.resolve("generated/NewAssets.kt")) 63 | } 64 | // end::configuration[] 65 | """.trimIndent()) 66 | 67 | val asset = File(temporaryFolder.newFolder("src", "main", "assets"), "example.txt") 68 | asset.writeText("hello world") 69 | 70 | val result = GradleRunner.create() 71 | .withProjectDir(temporaryFolder.root) 72 | .withArguments("assets") 73 | .withPluginClasspath() 74 | .build() 75 | 76 | assert(result.task(":assets")?.outcome == TaskOutcome.SUCCESS) 77 | val generated = File(temporaryFolder.root, "build/generated/NewAssets.kt") 78 | assert(generated.isFile) 79 | assert(generated.readText().contains("example.txt")) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/README.md: -------------------------------------------------------------------------------- 1 | # libGDX Gradle Plugin 2 | 3 | libGDX Gradle plugin add all libgdx tasks to your project. 4 | it's rely a lot on a default convention: your project will contains desktop game in a desktop directory, 5 | the core in a core directory, … 6 | ## How to use it? 7 | 8 | - Your gradle project **SHOULD** have, at least, a `core` and a `desktop` and/or `android` directory. 9 | 10 | - In your `build.gradle.kts`: 11 | ``` 12 | buildscript { 13 | dependencies { 14 | // see https://jitpack.io/#dwursteisen/libgdx-addons 15 | classpath("com.github.dwursteisen.libgdx-addons:libgdx-gradle-plugin:") 16 | } 17 | 18 | repositories { 19 | mavenCentral() 20 | google() 21 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 22 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 23 | maven { url = uri("https://jitpack.io") } 24 | } 25 | } 26 | 27 | apply(plugin = "libgdx") 28 | 29 | allprojects { 30 | 31 | repositories { 32 | mavenCentral() 33 | google() 34 | jcenter() 35 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 36 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 37 | } 38 | 39 | } 40 | ``` 41 | 42 | ## Features 43 | 44 | - Add a IntelliJ running configuration; 45 | - Add `run` gradle task (to run the game from Gradle); 46 | - Add `dist` gradle task (to create a fat jar of your game); 47 | - Add packr-<os> tasks to create a bundled app of your game; 48 | - Add a `assets` task which create a `Assets` class with all your assets name (to avoid naming mistakes); 49 | - Add `libgdx` libraries as dependency; 50 | - Add `Kotlin` libraries as dependency. 51 | ## Example 52 | 53 | See [libgdx-sample](https://github.com/dwursteisen/libgdx-sample) 54 | 55 | ## Roadmap 56 | 57 | ### Core 58 | - [x] Detect Android / Desktop / Core module; 59 | - [x] Add kotlin support by default; 60 | - [x] Generate Assets class with all all assets file name (like `R class in Android); 61 | - [ ] create a fat jar do not depends from external repositories? 62 | - [x] Add libgdx dependencies on each modules. 63 | 64 | ### Desktop 65 | - [x] Add `dist` task on desktop module to build the jar game; 66 | - [x] Add `run` task on desktop module to run the jar; 67 | - [ ] Add task `packr` on desktop to build the executable version; 68 | - [x] Detect main class; 69 | - [x] Generate main class if missing; 70 | - [x] Add IntelliJ run configuration. 71 | 72 | ### Android 73 | - [ ] Add needed dependency repositories ⚠️ Didn't find how to do it! Can you help me? 74 | - [x] Copy native libs on Android; 75 | - [ ] Add run task on Android; 76 | - [ ] Generate AndroidManifest.xml if missing; 77 | - [ ] Generate main class if missing; 78 | - [ ] Detect Android SDK location (check ANDROID_SDK_ROOT / local.properties). If missing, try to guess where the SDK is 79 | - [ ] Add tasks to increment version on Android. 80 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/Components.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Component 4 | import com.badlogic.gdx.graphics.g2d.Animation 5 | import com.badlogic.gdx.graphics.g2d.TextureRegion 6 | import com.badlogic.gdx.maps.MapLayer 7 | import com.badlogic.gdx.math.Vector2 8 | import com.badlogic.gdx.utils.Pool 9 | import com.github.dwursteisen.libgdx.ashley.fsm.EntityState 10 | import com.github.dwursteisen.libgdx.emptyGdxArray 11 | import kotlin.reflect.KClass 12 | 13 | /** 14 | * An entity can hold states using [StateComponent]. 15 | * A state can be assign for one component. 16 | * 17 | */ 18 | open class StateComponent() : Component, Pool.Poolable { 19 | 20 | var time: Float = 0f 21 | internal set 22 | 23 | internal var status: MutableMap, EntityState> = mutableMapOf() 24 | internal var timeReset: MutableMap, Float> = mutableMapOf() 25 | 26 | override fun reset() { 27 | time = 0f 28 | status.clear() 29 | timeReset.clear() 30 | } 31 | } 32 | 33 | val NO_TEXTURE = TextureRegion() 34 | 35 | /** 36 | * Hold texture region of your entity. 37 | * You can later use this texture to draw your entity in the screen. 38 | * You can also set the alpha (transparency) and horizontal flip 39 | * of it. 40 | * 41 | * The offset can be use if you want to apply an offset when rendering your 42 | * texture. 43 | */ 44 | class Textured( 45 | var texture: TextureRegion = NO_TEXTURE, 46 | val offset: Vector2 = Vector2(), 47 | var hFlip: Boolean = false, 48 | var alpha: Float = 1f 49 | ) : Component 50 | 51 | val NO_ANIMATION = Animation(0f, emptyGdxArray()) 52 | 53 | /** 54 | * Hold animation of your entity. 55 | * The time of the current animation is set to 0 when the animation is changed. 56 | * 57 | * To update the time of the animation, the [AnimationSystem] needs to be added to the engine. 58 | */ 59 | class Animated( 60 | animation: Animation = NO_ANIMATION, 61 | var time: Float = 0f 62 | ) : Component { 63 | var animation: Animation = animation 64 | set(value) { 65 | time = 0f 66 | field = value 67 | } 68 | 69 | fun finishAnimation() { 70 | time = animation.animationDuration 71 | } 72 | } 73 | 74 | /** 75 | * Hold the information that this entity should be rendered and with which strategy. 76 | * The [RenderSystem] will use this strategy to render the entity on screen. 77 | */ 78 | class Render(var strategy: Int) : Component 79 | 80 | data class Position(val value: Vector2 = Vector2()) : Component 81 | data class Size(val value: Vector2 = Vector2()) : Component 82 | data class Direction(val value: Vector2 = Vector2()) : Component 83 | 84 | /** 85 | * Hold rotation of the current entity. 86 | * Will be use to render the entity. 87 | * 88 | * @property degree: rotation of the entity, in degree 89 | * @property origin: origin of the rotation. 90 | */ 91 | data class Rotation( 92 | var degree: Float = 0f, 93 | val origin: Vector2 = Vector2() 94 | ) : Component 95 | 96 | class MapLayerComponent(var zLevel: Int, var layer: MapLayer) : Component 97 | -------------------------------------------------------------------------------- /packr-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/packr/PackrTask.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.packr 2 | 3 | import com.badlogicgames.packr.Packr 4 | import com.badlogicgames.packr.PackrConfig 5 | import com.github.dwursteisen.libgdx.gradle.createProperty 6 | import org.gradle.api.DefaultTask 7 | import org.gradle.api.tasks.InputFile 8 | import org.gradle.api.tasks.OutputDirectory 9 | import org.gradle.api.tasks.TaskAction 10 | import java.io.File 11 | 12 | open class PackrTask : DefaultTask() { 13 | 14 | init { 15 | group = "distribution" 16 | description = "Package your application with a bundled JRE" 17 | } 18 | 19 | val platform = project.createProperty() 20 | val jdk = project.createProperty() 21 | val executable = project.createProperty() 22 | 23 | val mainClass = project.createProperty() 24 | val vmArgs = project.createProperty>() 25 | val minimizeJre = project.createProperty() 26 | 27 | @InputFile 28 | val classpath = project.createProperty() 29 | 30 | @OutputDirectory 31 | val outputDir = project.createProperty() 32 | 33 | val bundleIdentifier = project.createProperty() 34 | 35 | val verbose = project.createProperty() 36 | 37 | @TaskAction 38 | fun packageIt() { 39 | val config = PackrConfig() 40 | config.platform = this.platform.orNull ?: findCurrentSystem() 41 | config.jdk = this.jdk.orNull ?: currentJavaHome() 42 | config.executable = this.executable.orNull ?: project.name 43 | config.classpath = this.classpath.map { listOf(it.absolutePath) }.orNull ?: invalidClasspath() 44 | 45 | config.mainClass = this.mainClass.get() 46 | 47 | config.vmArgs = this.vmArgs.getOrElse(emptyList()) 48 | 49 | config.minimizeJre = this.minimizeJre.get() 50 | 51 | config.bundleIdentifier = if (config.platform == PackrConfig.Platform.MacOS) { 52 | this.bundleIdentifier.orNull ?: invalidBundle() 53 | } else { 54 | this.bundleIdentifier.orNull 55 | } 56 | config.verbose = this.verbose.getOrElse(false) 57 | 58 | config.outDir = this.outputDir.getOrElse(project.buildDir.resolve("packr-out")) 59 | 60 | Packr().pack(config) 61 | } 62 | 63 | private fun invalidBundle(): Nothing { 64 | TODO("No valid bundle found for Mac OS application. Please specify a bundle.") 65 | } 66 | 67 | private fun invalidClasspath(): Nothing { 68 | TODO("No JAR to package found. Please specity a bundled JAR to include it in the packed application") 69 | } 70 | 71 | 72 | private fun findCurrentSystem(): PackrConfig.Platform { 73 | val osName = System.getProperty("os.name").toLowerCase() 74 | if (osName.contains("win")) { 75 | return PackrConfig.Platform.Windows64 76 | } else if (osName.contains("mac")) { 77 | return PackrConfig.Platform.MacOS 78 | } else if (osName.contains("nux") || osName.contains("nix")) { 79 | return PackrConfig.Platform.Linux64 80 | } 81 | TODO("Current OS type not found. Please configure it using the task configuration") 82 | } 83 | 84 | private fun noJdkFound(): Nothing = TODO("""No zipped JDK configured. 85 | I try $\JAVA_HOME but found nothing ! 86 | Please specify one JDK. It can be your current JDK too. 87 | 88 | packr { 89 | ... 90 | jdk = "yourJRE.zip" 91 | } 92 | """) 93 | 94 | private fun currentJavaHome(): String = System.getenv("JAVA_HOME") ?: noJdkFound() 95 | } 96 | -------------------------------------------------------------------------------- /libgdx-template-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/template/gradle/GenerateModelTask.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.template.gradle 2 | 3 | import com.github.dwursteisen.libgdx.gradle.createProperty 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.tasks.Input 6 | import org.gradle.api.tasks.OutputFile 7 | import org.gradle.api.tasks.TaskAction 8 | import java.io.File 9 | import java.io.FileInputStream 10 | import java.util.zip.ZipEntry 11 | import java.util.zip.ZipInputStream 12 | 13 | open class GenerateModelTask : DefaultTask() { 14 | 15 | init { 16 | group = "libgdx" 17 | description = "Create the module $name using a template." 18 | } 19 | 20 | @OutputFile 21 | val lock = project.createProperty() 22 | 23 | @Input 24 | val targetDirectory = project.createProperty() 25 | 26 | @Input 27 | val template = project.createProperty() 28 | 29 | @TaskAction 30 | fun generate() { 31 | val pluginJar = File(GenerateModelTask::class.java.protectionDomain.codeSource.location.toURI()).path 32 | 33 | val target = targetDirectory.get() 34 | if (!target.exists()) { 35 | target.mkdirs() 36 | } else { 37 | logger.warn(""" 38 | Not running the template plugin for the module ${template.get().name} as the directory ${target.absolutePath} 39 | is already existing. To avoid removing the current resources, the plugin will now just stop. 40 | 41 | To run again the templating, delete the directory. 42 | """.trimIndent()) 43 | return 44 | } 45 | 46 | // select and read files 47 | ZipInputStream(FileInputStream(pluginJar)).use { zip -> 48 | var entry: ZipEntry? = zip.nextEntry 49 | while (entry != null) { 50 | if (entry.name.startsWith(template.get().templateFolder)) { 51 | 52 | if (entry.size != 0L) { 53 | val content = ByteArray(entry.size.toInt()) 54 | zip.read(content) 55 | 56 | val resolvedName = entry.name.removePrefix(template.get().templateFolder) 57 | 58 | // copy content into target directory 59 | val newFile = target.resolve(resolvedName) 60 | if (!newFile.parentFile.exists()) { 61 | newFile.parentFile.mkdirs() 62 | } 63 | newFile.createNewFile() 64 | newFile.appendBytes(content) 65 | } 66 | } 67 | entry = zip.nextEntry 68 | } 69 | } 70 | 71 | updateSettingsFile() 72 | } 73 | 74 | fun updateSettingsFile() { 75 | logger.debug("Updating settings gradle file.") 76 | val settings = """ 77 | // -- generated by libgdx-template plugin -- // 78 | include("${template.get().templateFolder.removeSuffix("/")}") 79 | 80 | """.trimIndent() 81 | 82 | val settingsGroovy = project.file("settings.gradle") 83 | if (settingsGroovy.exists()) { 84 | settingsGroovy.appendText(settings) 85 | logger.warn("Your settings.gradle was just updated. Please reload your project from your IDE.") 86 | } 87 | 88 | val settingsKotlin = project.file("settings.gradle.kts") 89 | if (settingsKotlin.exists()) { 90 | settingsKotlin.appendText(settings) 91 | logger.warn("Your settings.gradle.kts was just updated. Please reload your project from your IDE.") 92 | } 93 | logger.debug("Updated settings gradle file.") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/gradle/LibGDXPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle 2 | 3 | import com.github.dwursteisen.libgdx.gradle.internal.tryFindAssetsDirectory 4 | import com.github.dwursteisen.libgdx.gradle.internal.tryFindMainClass 5 | import org.gradle.api.NamedDomainObjectContainer 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.PolymorphicDomainObjectContainer 8 | import org.gradle.api.Project 9 | import org.gradle.api.plugins.ExtensionAware 10 | import org.gradle.plugins.ide.idea.model.IdeaModel 11 | import org.jetbrains.gradle.ext.Application 12 | import org.jetbrains.gradle.ext.ProjectSettings 13 | import org.jetbrains.gradle.ext.RunConfiguration 14 | import org.slf4j.Logger 15 | import org.slf4j.LoggerFactory 16 | 17 | class LibGDXPlugin : Plugin { 18 | 19 | private val logger: Logger = LoggerFactory.getLogger(LibGDXPlugin::class.java) 20 | 21 | override fun apply(project: Project) { 22 | project.apply { it.plugin("com.github.dwursteisen.libgdx.template") } 23 | 24 | project.extensions.create("libgdx", LibGDXExtensions::class.java, project) 25 | 26 | if (project.subprojects.isEmpty()) { 27 | bootstrapProjects(project) 28 | } else { 29 | project.subprojects.forEach { sub -> 30 | when (sub.name) { 31 | "core" -> sub.apply { it.plugin("com.github.dwursteisen.libgdx.core") } 32 | "desktop" -> sub.apply { it.plugin("com.github.dwursteisen.libgdx.desktop") } 33 | "android" -> sub.apply { it.plugin("com.github.dwursteisen.libgdx.android") } 34 | } 35 | } 36 | 37 | addIntelliJRunConfiguration(project) 38 | } 39 | } 40 | 41 | private fun bootstrapProjects(project: Project) { 42 | logger.info("No subprojects detected. Will bootstrap the project.") 43 | project.tasks.getByName("build").dependsOn("template-core", "template-desktop") 44 | } 45 | 46 | private fun addIntelliJRunConfiguration( 47 | project: Project 48 | ) { 49 | project.apply { it.plugin("org.jetbrains.gradle.plugin.idea-ext") } 50 | project.extensions.configure(LibGDXExtensions::class.java) { exts -> 51 | project.extensions.configure(IdeaModel::class.java) { ideaModel -> 52 | 53 | (ideaModel.project as ExtensionAware).extensions.configure(ProjectSettings::class.java) { projectSettings -> 54 | 55 | (projectSettings as ExtensionAware).extensions.configure>( 56 | "runConfigurations" 57 | ) { runs -> 58 | runs as PolymorphicDomainObjectContainer 59 | val configuredMainClasses = exts.mainClass.get() 60 | val toConfigure = if (configuredMainClasses.isEmpty()) { 61 | project.tryFindMainClass() 62 | } else { 63 | configuredMainClasses 64 | } 65 | toConfigure.forEach { mainClass -> 66 | 67 | val className = mainClass.capitalize() 68 | 69 | runs.create("🎮 Run ${project.name.capitalize()} ➡️ $className", Application::class.java) { 70 | it.mainClass = mainClass 71 | 72 | val assetsDirectory = exts.assetsDirectory.orNull ?: project.tryFindAssetsDirectory() 73 | it.workingDirectory = assetsDirectory?.absolutePath 74 | 75 | if (project.childProjects.containsKey("android")) { 76 | // the plugin android trigger some bugs in IntelliJ. 77 | it.moduleName = "${project.name}.desktop" 78 | } else { 79 | it.moduleName = "${project.name}.desktop.main" // finger crossed 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /aseprite-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/aseprite/AsepriteTask.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.aseprite 2 | 3 | import com.github.dwursteisen.libgdx.gradle.createProperty 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.file.FileCollection 6 | import org.gradle.api.tasks.InputFiles 7 | import org.gradle.api.tasks.OutputFiles 8 | import org.gradle.api.tasks.SkipWhenEmpty 9 | import org.gradle.api.tasks.TaskAction 10 | import org.gradle.process.internal.ExecActionFactory 11 | import java.io.File 12 | import javax.inject.Inject 13 | 14 | open class AsepriteTask @Inject constructor( 15 | private val execActionFactory: ExecActionFactory 16 | ) : DefaultTask() { 17 | 18 | val exec = project.createProperty() 19 | .value(project.properties["aseprite.exec"]?.let { File(it.toString()) }) 20 | 21 | @InputFiles 22 | @SkipWhenEmpty 23 | val inputFiles = project.createProperty() 24 | 25 | val outputDirectory = project.createProperty() 26 | 27 | val baseDirectory = project.createProperty() 28 | 29 | @OutputFiles 30 | val outputFiles = project.createProperty() 31 | 32 | val scale = project.createProperty().value(1.0) 33 | 34 | val format = project.createProperty().value(AsepriteFormat.HASH) 35 | 36 | val json = project.createProperty().value(true) 37 | 38 | val verbose = project.createProperty().value(false) 39 | 40 | val sheetPack = project.createProperty().value(true) 41 | 42 | val sheetHeight = project.createProperty() 43 | val sheetWidth = project.createProperty() 44 | 45 | @TaskAction 46 | fun export() { 47 | val aseprite = this.exec.orNull ?: invalideAsepritePath() 48 | 49 | val exec = execActionFactory.newExecAction() 50 | 51 | inputFiles.get().files.forEach { file -> 52 | logger.info("Will run Aseprite to process $file") 53 | exec.commandLine = args(aseprite, file) 54 | val result = exec.execute() 55 | result.assertNormalExitValue() 56 | } 57 | } 58 | 59 | private fun invalideAsepritePath(): Nothing { 60 | TODO("""Missing aseprite executable path. 61 | Please configure it using aseprite.exec property (ie: in your ~/.gradle/gradle.properties) 62 | aseprite.exec= 63 | 64 | or using aseprite extension in your build.gradle 65 | 66 | aseprite { 67 | exec= 68 | } 69 | 70 | 71 | MacOS specific : point to aseprite located into /Aseprite.app/Contents/MacOS/aseprite""") 72 | } 73 | 74 | private fun args(aseprite: File, input: File): List { 75 | 76 | val exec = aseprite.absolutePath 77 | var args = listOf(exec, "-b") 78 | args += input.absolutePath 79 | 80 | if (verbose.get()) { 81 | args += "--verbose" 82 | } 83 | 84 | val base = baseDirectory.orNull 85 | val path = if (base != null) { 86 | val prefix = input.absolutePath.replace(base.absolutePath, "").replaceAfterLast("/", "") 87 | File(outputDirectory.get().absolutePath + prefix + "/" + input.nameWithoutExtension) 88 | } else { 89 | File(outputDirectory.get().absolutePath + "/" + input.nameWithoutExtension) 90 | } 91 | 92 | if (json.get()) { 93 | args += "--data" 94 | args += path.absolutePath + ".json" 95 | args += "--format" 96 | args += format.get().format 97 | args += "--list-slices" 98 | args += "--list-tags" 99 | } 100 | 101 | 102 | if (scale.get() != 1.0) { 103 | args += "--scale" 104 | args += scale.get().toString() 105 | } 106 | 107 | if (sheetWidth.isPresent) { 108 | args += "--sheet-width" 109 | args += "${sheetWidth.get()}" 110 | } 111 | 112 | if (sheetHeight.isPresent) { 113 | args += "--sheet-height" 114 | args += "${sheetHeight.get()}" 115 | } 116 | 117 | if (sheetPack.get()) { 118 | args += "--sheet-pack" 119 | } 120 | 121 | args += "--sheet" 122 | args += path.absolutePath + ".png" 123 | 124 | logger.debug("Aseprite command line : $args") 125 | return args 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /ashley-addons/README.md: -------------------------------------------------------------------------------- 1 | # Ashley Addons 2 | 3 | Add some utility class for the Ashley library 4 | 5 | ## Event Bus 6 | 7 | An simple event bus. It allow to emit simple event. 8 | All events will be sent outside the (ashley) engine 9 | transactional context 10 | 11 | ### Configure it 12 | 13 | ``` 14 | class CustomScreen : ScreenAdapter() { 15 | 16 | lateinit var eventBus: EventBus 17 | 18 | override fun show() { 19 | // ... 20 | eventBus = EventBus() 21 | } 22 | fun render(delta: Float) { 23 | // ... 24 | eventBus.update(delta) 25 | } 26 | } 27 | ``` 28 | 29 | ### Use it 30 | 31 | You can register an event listener, to listen 32 | one or more events. 33 | 34 | ``` 35 | val EVENT_TOUCH: Int = 1 36 | 37 | eventBus.register(object: EventListener { event, data -> 38 | // event = EVENT_TOUCH 39 | // event_data = data if event comes with datas 40 | }, EVENT_TOUCH) 41 | ``` 42 | 43 | You can emit events too. These events will be process 44 | the next loop update 45 | 46 | ``` 47 | eventBus.emit(EVENT_TOUCH) 48 | 49 | val data = eventBus.createEventData() 50 | data.body = "WHAT YOU WANTS" 51 | eventBus.emit(EVENT_TOUCH, data) 52 | ``` 53 | 54 | you can prepare events to be emitted later. 55 | Duration is in milliseconds. 56 | 57 | ``` 58 | eventBus.emitLater(500, EVENT_TOUCH) 59 | 60 | val data = eventBus.createEventData() 61 | data.body = "WHAT YOU WANTS" 62 | eventBus.emitLater(1000, EVENT_TOUCH, data) 63 | ``` 64 | 65 | ## State Machine 66 | 67 | State machine is a custom system designed to create 68 | finite state machin on the ashley entity system 69 | 70 | ### Configure it 71 | 72 | Create a new class which extends `StateMachinSystem`. 73 | You'll need to pass the ashley familly and the event bus (see bellow) 74 | 75 | Please note that the system __will automaticaly register itselft to the event bus__ 76 | 77 | You'll have to __only__ implement one method (`describeStateMachine` ) that will describe your state machine. 78 | Please note that your entity __should__ have the component `StateComponent`. 79 | 80 | ``` 81 | class CustomScreen : ScreenAdapter() { 82 | 83 | lateinit var eventBus: EventBus 84 | lateinit var engine: PooledEngine 85 | 86 | 87 | override fun show() { 88 | // ... 89 | eventBus = EventBus() 90 | 91 | // mandatory to update states 92 | engine.addSystem(StateSystem()) 93 | engine.addSystem(MyStateSystem()) 94 | 95 | engine.add { 96 | add(StateComponent()) 97 | } 98 | 99 | } 100 | fun render(delta: Float) { 101 | // ... 102 | engine.update(delta) 103 | eventBus.update(delta) 104 | } 105 | } 106 | ``` 107 | 108 | ### Use it 109 | 110 | ``` 111 | class MyStateSystem(eventBus: EventBus) : StateMachineSystem(eventBus, Familly.add(StateComponent::class.java).get()) { 112 | 113 | override fun describeStateMachine() { 114 | val STATE_MAIN = object : EntityState() { 115 | override fun enter(entity: Entity, machine: StateMachineSystem, eventData: EventData) { 116 | println("enter") 117 | } 118 | 119 | override fun update(entity: Entity, machine: StateMachineSystem, delta: Float) { 120 | println("update") 121 | } 122 | 123 | override fun exit(entity: Entity, machine: StateMachineSystem, eventData: EventData) { 124 | println("exit") 125 | } 126 | } 127 | 128 | val STATE_START_GAME = object : EntityState() { 129 | 130 | } 131 | 132 | startWith(STATE_MAIN) 133 | onState(STATE_MAIN).on(StateMachineSystem.EVENT_TOUCHED) { entity, event -> 134 | go(STATE_START_GAME, entity) 135 | } 136 | 137 | } 138 | 139 | } 140 | ``` 141 | 142 | The method will create states (`EntityState`) and will 143 | create transitions between states. 144 | 145 | - `stateWith` : inital state of the `Entity` 146 | - `onState().on()` : explain how to react on events and set a new state. 147 | 148 | 149 | ### Example of use 150 | 151 | - [SuperJumper](https://github.com/dwursteisen/ashley-superjumper) using this `StateEngineSystem` 152 | - [PlatformSystem](https://github.com/dwursteisen/ashley-superjumper/blob/master/core/src/main/kotlin/com/github/dwursteisen/superjumper/systems/PlatformSystem.kt) 153 | - [BobSystem](https://github.com/dwursteisen/ashley-superjumper/blob/master/core/src/main/kotlin/com/github/dwursteisen/superjumper/systems/BobSystem.kt) 154 | -------------------------------------------------------------------------------- /libgdx-test/src/main/kotlin/com/github/dwursteisen/libgdx/test/DelegateGame.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.test 2 | 3 | import com.badlogic.gdx.ApplicationListener 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.graphics.Pixmap 6 | import com.badlogic.gdx.input.RemoteInput 7 | import com.badlogic.gdx.utils.BufferUtils 8 | import com.badlogic.gdx.utils.ScreenUtils 9 | import com.madgag.gif.fmsware.AnimatedGifEncoder 10 | import java.awt.image.BufferedImage 11 | import java.util.concurrent.CountDownLatch 12 | 13 | 14 | internal class DelegateGame(var d: ApplicationListener, var beforeLatch: CountDownLatch, var afterLatch: CountDownLatch) : ApplicationListener { 15 | 16 | private var recording = false 17 | 18 | private var screenshots = emptyList() 19 | private var timestamps: List = emptyList() 20 | 21 | 22 | internal fun startRecord() { 23 | recording = true 24 | } 25 | 26 | internal fun stopRecord(filename: String) { 27 | Gdx.app.postRunnable { 28 | val delay = (timestamps.last() - timestamps.first()) / timestamps.size 29 | val encoder = AnimatedGifEncoder() 30 | 31 | val external = Gdx.files.absolute(filename) 32 | 33 | encoder.start(external.file().absolutePath) 34 | encoder.setSize(screenshots.last().width, screenshots.last().height) 35 | encoder.setDelay(delay.toInt()) 36 | encoder.setRepeat(0) 37 | // encoder.setTransparent(Color.WHITE) 38 | 39 | 40 | val frame = BufferedImage(screenshots.last().width, screenshots.last().height, BufferedImage.TYPE_INT_RGB) 41 | screenshots.forEach { 42 | 43 | 44 | for (x in 0..it.width - 1) { 45 | for (y in 0..it.height - 1) { 46 | // http://stackoverflow.com/questions/27071351/change-the-color-of-each-pixel-in-an-image-java 47 | 48 | 49 | val currentPixel = it.getPixel(x, y) 50 | val red = (currentPixel shr 24 and 0xff) 51 | val green = (currentPixel shr 16 and 0xff) 52 | val blue = (currentPixel shr 8 and 0xff) 53 | 54 | // don't understand why shl 8 with red... 55 | val rgb = red shl 8 or green shl 8 or blue 56 | 57 | frame.setRGB(x, y, rgb) 58 | } 59 | } 60 | encoder.addFrame(frame) 61 | } 62 | encoder.finish() 63 | screenshots = emptyList() 64 | timestamps = emptyList() 65 | recording = false 66 | Gdx.app.log("LIBGDX-TEST", "Recording created at ${external.path()}") 67 | } 68 | } 69 | 70 | override fun render() { 71 | d.render() 72 | 73 | if (recording) { 74 | val pixels = ScreenUtils.getFrameBufferPixels(0, 0, Gdx.graphics.backBufferWidth, Gdx.graphics.backBufferHeight, true) 75 | val pixmap = Pixmap(Gdx.graphics.backBufferWidth, Gdx.graphics.backBufferHeight, Pixmap.Format.RGBA8888) 76 | BufferUtils.copy(pixels, 0, pixmap.pixels, pixels.size) 77 | screenshots += pixmap 78 | 79 | timestamps += System.currentTimeMillis() 80 | 81 | 82 | } 83 | } 84 | 85 | override fun pause() { 86 | d.pause() 87 | } 88 | 89 | override fun resume() { 90 | d.resume() 91 | } 92 | 93 | override fun resize(width: Int, height: Int) { 94 | d.resize(width, height) 95 | } 96 | 97 | private var alreadySetup = false 98 | 99 | override fun create() { 100 | if (!alreadySetup) { 101 | setupDelegate() 102 | alreadySetup = true 103 | } 104 | 105 | beforeLatch.countDown() 106 | d.create() 107 | afterLatch.countDown() 108 | } 109 | 110 | private fun setupDelegate() { 111 | Gdx.input = RemoteInput() 112 | } 113 | 114 | override fun dispose() { 115 | d.dispose() 116 | } 117 | 118 | fun restart(listener: ApplicationListener, before: CountDownLatch, after: CountDownLatch) { 119 | 120 | Gdx.app.postRunnable { 121 | d.dispose() 122 | before.countDown() 123 | listener.create() 124 | after.countDown() 125 | 126 | if (recording) { 127 | TODO("""You are currently recording a game session. But you haven't stopped the recording. 128 | Please stop the recording using the method 'stopRecordAndSave()' in your test""") 129 | } 130 | 131 | listener.resize(Gdx.graphics.width, Gdx.graphics.height) 132 | d = listener 133 | } 134 | 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /libgdx-gradle-plugin/src/main/kotlin/com/github/dwursteisen/libgdx/gradle/internal/AndroidPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.gradle.internal 2 | 3 | import com.android.build.gradle.AppExtension 4 | import com.github.dwursteisen.libgdx.gradle.LibGDXExtensions 5 | import org.gradle.api.Plugin 6 | import org.gradle.api.Project 7 | import org.gradle.api.tasks.Copy 8 | import java.io.File 9 | 10 | class AndroidPlugin : Plugin { 11 | override fun apply(project: Project) { 12 | project.apply { it.plugin("android") } 13 | project.apply { it.plugin("kotlin-android") } 14 | 15 | project.rootProject.extensions.configure(LibGDXExtensions::class.java) { exts -> 16 | addDefaultLibraries(project, exts) 17 | setupAndroidExtension(project, exts) 18 | createNativesLibCopyTasks(project) 19 | } 20 | } 21 | 22 | private fun addDefaultLibraries(project: Project, exts: LibGDXExtensions) { 23 | val version = exts.version.get() 24 | project.configurations.create("natives") 25 | project.dependencies.add("implementation", project.dependencies.project(mapOf("path" to ":core"))) 26 | project.dependencies.add("implementation", "com.badlogicgames.gdx:gdx-backend-android:$version") 27 | project.dependencies.add("implementation", "org.jetbrains.kotlin:kotlin-stdlib") 28 | project.dependencies.add("natives", "com.badlogicgames.gdx:gdx-platform:$version:natives-armeabi") 29 | project.dependencies.add("natives", "com.badlogicgames.gdx:gdx-platform:$version:natives-armeabi-v7a") 30 | project.dependencies.add("natives", "com.badlogicgames.gdx:gdx-platform:$version:natives-x86") 31 | project.dependencies.add("natives", "com.badlogicgames.gdx:gdx-platform:$version:natives-arm64-v8a") 32 | project.dependencies.add("natives", "com.badlogicgames.gdx:gdx-platform:$version:natives-x86_64") 33 | } 34 | 35 | private fun setupAndroidExtension(project: Project, exts: LibGDXExtensions) { 36 | val androidExts = project.extensions.getByName("android") as AppExtension 37 | androidExts.compileSdkVersion = androidExts.compileSdkVersion ?: "android-28" 38 | androidExts.defaultConfig.minSdkVersion(21) 39 | androidExts.defaultConfig.targetSdkVersion(28) 40 | androidExts.sourceSets.getByName("main") { 41 | it.java.srcDirs("src/main/kotlin") 42 | it.assets.srcDirs(exts.assetsDirectory.orNull ?: project.tryFindAndroidMainClass()) 43 | } 44 | } 45 | 46 | private fun createNativesLibCopyTasks(project: Project) { 47 | project.afterEvaluate { 48 | val files = project.configurations.getByName("natives").files 49 | .groupBy { it.name.replace("gdx-platform-([0-9]*\\.[0-9]*\\.[0-9]*-)".toRegex(), "") } 50 | 51 | copyLib( 52 | project = project, 53 | directoryName = "armeabi", 54 | nativeFile = files.getValue("natives-armeabi.jar").first() 55 | ) 56 | copyLib( 57 | project = project, 58 | directoryName = "armeabi-v7a", 59 | nativeFile = files.getValue("natives-armeabi-v7a.jar").first() 60 | ) 61 | copyLib( 62 | project = project, 63 | directoryName = "x86", 64 | nativeFile = files.getValue("natives-x86.jar").first() 65 | ) 66 | copyLib( 67 | project = project, 68 | directoryName = "arm64-v8a", 69 | nativeFile = files.getValue("natives-arm64-v8a.jar").first() 70 | ) 71 | copyLib( 72 | project = project, 73 | directoryName = "x86_64", 74 | nativeFile = files.getValue("natives-x86_64.jar").first() 75 | ) 76 | } 77 | 78 | project.tasks.whenTaskAdded { packageTask -> 79 | if (packageTask.name.contains("package")) { 80 | packageTask.dependsOn( 81 | "copy-armeabi", 82 | "copy-armeabi-v7a", 83 | "copy-x86", 84 | "copy-arm64-v8a", 85 | "copy-x86_64" 86 | ) 87 | } 88 | } 89 | } 90 | 91 | private fun copyLib( 92 | project: Project, 93 | directoryName: String, 94 | nativeFile: File 95 | ) { 96 | project.tasks.create("copy-$directoryName", Copy::class.java) { 97 | it.group = "libgdx" 98 | it.description = "Copy natives *.so files from ${nativeFile.name}" 99 | 100 | it.from(project.zipTree(nativeFile as Any)) 101 | it.into(project.file("src/main/jniLibs/$directoryName")) 102 | it.include("*.so") 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/RenderSystem.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Component 4 | import com.badlogic.ashley.core.ComponentMapper 5 | import com.badlogic.ashley.core.Entity 6 | import com.badlogic.ashley.core.Family 7 | import com.badlogic.ashley.systems.IteratingSystem 8 | import com.badlogic.gdx.graphics.OrthographicCamera 9 | import com.badlogic.gdx.graphics.g2d.SpriteBatch 10 | import com.badlogic.gdx.maps.MapLayer 11 | import com.badlogic.gdx.maps.tiled.TiledMap 12 | import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer 13 | import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile 14 | import com.badlogic.gdx.utils.viewport.Viewport 15 | 16 | interface RenderStrategy { 17 | fun zLevel(entity: Entity, delta: Float): Float 18 | fun render(entity: Entity, batch: SpriteBatch) 19 | fun beginBatch() = Unit 20 | fun endBatch() = Unit 21 | } 22 | 23 | inline fun RenderStrategy.get(): ComponentMapper = ComponentMapper.getFor(T::class.java) 24 | 25 | abstract class TexturedStrategy : RenderStrategy { 26 | private val renderMapper = get() 27 | private val position = get() 28 | private val size = get() 29 | private val rotation = get() 30 | 31 | override fun render(entity: Entity, batch: SpriteBatch) { 32 | val entityRender = entity[renderMapper] 33 | if (entityRender.texture == NO_TEXTURE) return 34 | 35 | val position = entity[position].value 36 | val size = entity[size].value 37 | val rotation = entity.getNullable(rotation) 38 | val offset = entityRender.offset 39 | 40 | val (originX, originY, degree) = if (rotation != null) { 41 | listOf(rotation.origin.x, rotation.origin.y, rotation.degree) 42 | } else { 43 | listOf(0f, 0f, 0f) 44 | } 45 | 46 | val (x, sizeX) = if (entityRender.hFlip) { 47 | (position.x + offset.x + size.x) to (-size.x) 48 | } else { 49 | (position.x + offset.x) to (size.x) 50 | } 51 | batch.setColor(1f, 1f, 1f, entityRender.alpha) 52 | batch.draw( 53 | entityRender.texture, 54 | x, position.y + offset.y, 55 | originX, originY, 56 | sizeX, size.y, 57 | 1f, 1f, 58 | degree 59 | ) 60 | } 61 | } 62 | 63 | class LayerRenderer(batch: SpriteBatch) : OrthogonalTiledMapRenderer(null, batch) { 64 | 65 | fun renderLayerOf(mapLayer: MapLayer) { 66 | renderMapLayer(mapLayer) 67 | } 68 | } 69 | 70 | class MapLayerStrategy(private val viewport: Viewport) : RenderStrategy { 71 | 72 | private val mapLayer = get() 73 | 74 | private var cache: LayerRenderer? = null 75 | 76 | override fun zLevel(entity: Entity, delta: Float): Float { 77 | return entity[mapLayer].zLevel.toFloat() 78 | } 79 | 80 | override fun render(entity: Entity, batch: SpriteBatch) { 81 | val renderer = cache ?: generateRender(batch) 82 | renderer.setView(viewport.camera as OrthographicCamera) 83 | renderer.renderLayerOf(entity[mapLayer].layer) 84 | } 85 | 86 | private fun generateRender(batch: SpriteBatch): LayerRenderer { 87 | cache = LayerRenderer(batch) 88 | return cache!! 89 | } 90 | 91 | override fun beginBatch() { 92 | AnimatedTiledMapTile.updateAnimationBaseTime() 93 | } 94 | } 95 | 96 | class RenderSystem( 97 | private val viewport: Viewport, 98 | private val strategies: Map 99 | ) : IteratingSystem(Family.all(Render::class.java).get()) { 100 | 101 | private val batch = SpriteBatch() 102 | 103 | private val buffer = mutableListOf() 104 | 105 | private val render = get() 106 | 107 | override fun update(deltaTime: Float) { 108 | viewport.apply() 109 | buffer.clear() 110 | super.update(deltaTime) 111 | buffer.sortBy { zLevel(it, deltaTime) } 112 | batch.begin() 113 | strategies.values.forEach(::beginBatch) 114 | batch.projectionMatrix = viewport.camera.combined 115 | buffer.forEach(::renderEntity) 116 | strategies.values.forEach(::endBatch) 117 | batch.end() 118 | } 119 | 120 | private fun beginBatch(strategy: RenderStrategy) { 121 | strategy.beginBatch() 122 | } 123 | 124 | 125 | private fun endBatch(strategy: RenderStrategy) { 126 | strategy.endBatch() 127 | } 128 | 129 | override fun processEntity(entity: Entity, deltaTime: Float) { 130 | buffer.add(entity) 131 | } 132 | 133 | private fun zLevel(entity: Entity, deltaTime: Float): Float { 134 | val id = entity[render].strategy 135 | return strategies[id]?.zLevel(entity, deltaTime) ?: 0.0f 136 | } 137 | 138 | private fun renderEntity(entity: Entity) { 139 | val id = entity[render].strategy 140 | strategies[id]?.render(entity, batch) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/core/src/main/kotlin/step5/MyGame.kt: -------------------------------------------------------------------------------- 1 | package step5 2 | 3 | import Assets 4 | import com.badlogic.ashley.core.Engine 5 | import com.badlogic.gdx.Game 6 | import com.badlogic.gdx.Gdx 7 | import com.badlogic.gdx.assets.AssetManager 8 | import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver 9 | import com.badlogic.gdx.graphics.GL20 10 | import com.badlogic.gdx.graphics.Texture 11 | import com.badlogic.gdx.graphics.g2d.Animation 12 | import com.badlogic.gdx.maps.tiled.TiledMap 13 | import com.badlogic.gdx.maps.tiled.TmxMapLoader 14 | import com.badlogic.gdx.utils.viewport.FitViewport 15 | import com.badlogic.gdx.utils.viewport.Viewport 16 | import com.github.dwursteisen.libgdx.TextureSplitter 17 | import com.github.dwursteisen.libgdx.ashley.* 18 | import com.github.dwursteisen.libgdx.getValue 19 | import com.github.dwursteisen.libgdx.graphics.RefreshableTextureLoader 20 | import com.github.dwursteisen.libgdx.scanObjects 21 | import com.github.dwursteisen.libgdx.v2 22 | import step4.* 23 | 24 | const val SPRITE = 1 25 | const val MAP = 2 26 | 27 | // tag::body[] 28 | class MyGame : Game() { 29 | 30 | private val assetManager: AssetManager = AssetManager() 31 | 32 | private val engine = Engine() 33 | 34 | private val viewport: Viewport = FitViewport(200f, 200f) 35 | 36 | // <1> 37 | private lateinit var eventBus: EventBus 38 | 39 | override fun create() { 40 | 41 | eventBus = EventBus() 42 | 43 | assetManager.setLoader(Texture::class.java, RefreshableTextureLoader(InternalFileHandleResolver())) 44 | assetManager.setLoader(TiledMap::class.java, TmxMapLoader(InternalFileHandleResolver())) 45 | 46 | assetManager.load(Assets.assets_dungeon_sheet_png, Texture::class.java) 47 | assetManager.load(Assets.assets_dungeon_tmx, TiledMap::class.java) 48 | 49 | assetManager.finishLoading() 50 | 51 | viewport.camera.position.set(viewport.worldWidth * 0.5f, viewport.worldHeight * 0.5f, 0f) 52 | 53 | engine.addSystem(PlayerSystem()) 54 | engine.addSystem(SwitchSystem(eventBus)) 55 | engine.addSystem(DoorSystem(eventBus)) 56 | engine.addSystem(AnimationSystem()) 57 | engine.addSystem(StateSystem()) 58 | engine.addSystem( 59 | RenderSystem( 60 | viewport, 61 | mapOf( 62 | SPRITE to SpriteStrategy(), 63 | MAP to MapLayerStrategy(viewport) 64 | ) 65 | ) 66 | ) 67 | 68 | val split = TextureSplitter(assetManager).split( 69 | Assets.assets_dungeon_sheet_png, 16, 16 70 | ) 71 | val playerSprite = split.get(column = 19, row = 7) 72 | val doorAnimation = split.animations( 73 | 0.1f, 74 | 4 to 6, 75 | 3 to 6, 76 | 2 to 6, 77 | 1 to 6, 78 | 0 to 6 79 | ) 80 | val switchSprite = split.animations( 81 | 0.1f, 82 | 0 to 5, 83 | 1 to 5, 84 | 2 to 5, 85 | 3 to 5, 86 | 4 to 5, 87 | 5 to 5, 88 | 6 to 5 89 | ) 90 | 91 | val map: TiledMap = assetManager[Assets.assets_dungeon_tmx] 92 | 93 | map.layers["objs"].scanObjects { x, y, objs -> 94 | val type: String by objs.properties 95 | when (type) { 96 | "player" -> { 97 | val player = engine.createEntity().apply { 98 | add(Player()) 99 | add(Position(x v2 y)) 100 | add(Size(16 v2 16)) 101 | add(Textured(texture = playerSprite)) 102 | add(Render(SPRITE)) 103 | } 104 | engine.addEntity(player) 105 | } 106 | "switch" -> { 107 | val switch = engine.createEntity().apply { 108 | add(Switch()) 109 | add(Position(x v2 y)) 110 | add(Size(16 v2 16)) 111 | add(Textured()) 112 | add(Animated(animation = switchSprite)) 113 | add(Render(SPRITE)) 114 | add(StateComponent()) 115 | } 116 | engine.addEntity(switch) 117 | } 118 | "door" -> { 119 | val door = engine.createEntity().apply { 120 | add(Door()) 121 | add(Position(x v2 y)) 122 | add(Size(16 v2 16)) 123 | add(Textured()) 124 | add(Animated(animation = doorAnimation)) 125 | add(Render(SPRITE)) 126 | add(StateComponent()) 127 | } 128 | engine.addEntity(door) 129 | } 130 | } 131 | } 132 | 133 | val background = engine.createEntity().apply { 134 | add(Render(MAP)) 135 | add(MapLayerComponent(zLevel = 0, layer = map.layers["background"])) 136 | } 137 | engine.addEntity(background) 138 | 139 | val dungeon = engine.createEntity().apply { 140 | add(Render(MAP)) 141 | add(MapLayerComponent(zLevel = 1, layer = map.layers["dungeon"])) 142 | } 143 | engine.addEntity(dungeon) 144 | 145 | 146 | } 147 | 148 | override fun render() { 149 | Gdx.gl.glClearColor(0f, 0f, 0f, 1f) 150 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) 151 | 152 | // <3> 153 | eventBus.update(Gdx.graphics.deltaTime) 154 | engine.update(Gdx.graphics.deltaTime) 155 | } 156 | 157 | override fun resize(width: Int, height: Int) { 158 | viewport.update(width, height) 159 | } 160 | } 161 | // end::body[] 162 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/docs/asciidoc/samples/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /libgdx-test/src/main/java/com/github/dwursteisen/libgdx/test/RemoteSender.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.github.dwursteisen.libgdx.test; 18 | 19 | import com.badlogic.gdx.Gdx; 20 | import com.badlogic.gdx.InputProcessor; 21 | import com.badlogic.gdx.input.RemoteInput; 22 | 23 | import java.io.DataOutputStream; 24 | import java.net.Socket; 25 | 26 | /** 27 | * Sends all inputs from touch, key, accelerometer and compass to a {@link RemoteInput} at the given ip/port. Instantiate this and 28 | * call sendUpdate() periodically. 29 | * 30 | * @author mzechner 31 | */ 32 | public class RemoteSender implements InputProcessor { 33 | private DataOutputStream out; 34 | private boolean connected = false; 35 | 36 | public static final int KEY_DOWN = 0; 37 | public static final int KEY_UP = 1; 38 | public static final int KEY_TYPED = 2; 39 | 40 | public static final int TOUCH_DOWN = 3; 41 | public static final int TOUCH_UP = 4; 42 | public static final int TOUCH_DRAGGED = 5; 43 | 44 | public static final int ACCEL = 6; 45 | public static final int COMPASS = 7; 46 | public static final int SIZE = 8; 47 | public static final int GYRO = 9; 48 | 49 | public RemoteSender(String ip, int port) { 50 | try { 51 | Socket socket = new Socket(ip, port); 52 | socket.setTcpNoDelay(true); 53 | socket.setSoTimeout(3000); 54 | out = new DataOutputStream(socket.getOutputStream()); 55 | // out.writeBoolean(Gdx.input.isPeripheralAvailable(Peripheral.MultitouchScreen)); 56 | out.writeBoolean(false); 57 | connected = true; 58 | // Gdx.input.setInputProcessor(this); 59 | } catch (Exception e) { 60 | Gdx.app.log("RemoteSender", "couldn't connect to " + ip + ":" + port); 61 | } 62 | } 63 | 64 | public void sendUpdate() { 65 | synchronized (this) { 66 | if (!connected) return; 67 | } 68 | try { 69 | out.writeInt(ACCEL); 70 | out.writeFloat(Gdx.input.getAccelerometerX()); 71 | out.writeFloat(Gdx.input.getAccelerometerY()); 72 | out.writeFloat(Gdx.input.getAccelerometerZ()); 73 | out.writeInt(COMPASS); 74 | out.writeFloat(Gdx.input.getAzimuth()); 75 | out.writeFloat(Gdx.input.getPitch()); 76 | out.writeFloat(Gdx.input.getRoll()); 77 | out.writeInt(SIZE); 78 | out.writeFloat(Gdx.graphics.getWidth()); 79 | out.writeFloat(Gdx.graphics.getHeight()); 80 | out.writeInt(GYRO); 81 | out.writeFloat(Gdx.input.getGyroscopeX()); 82 | out.writeFloat(Gdx.input.getGyroscopeY()); 83 | out.writeFloat(Gdx.input.getGyroscopeZ()); 84 | } catch (Throwable t) { 85 | out = null; 86 | connected = false; 87 | } 88 | } 89 | 90 | @Override 91 | public boolean keyDown(int keycode) { 92 | synchronized (this) { 93 | if (!connected) return false; 94 | } 95 | 96 | try { 97 | out.writeInt(KEY_DOWN); 98 | out.writeInt(keycode); 99 | } catch (Throwable t) { 100 | synchronized (this) { 101 | connected = false; 102 | } 103 | } 104 | return false; 105 | } 106 | 107 | @Override 108 | public boolean keyUp(int keycode) { 109 | synchronized (this) { 110 | if (!connected) return false; 111 | } 112 | 113 | try { 114 | out.writeInt(KEY_UP); 115 | out.writeInt(keycode); 116 | } catch (Throwable t) { 117 | synchronized (this) { 118 | connected = false; 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | @Override 125 | public boolean keyTyped(char character) { 126 | synchronized (this) { 127 | if (!connected) return false; 128 | } 129 | 130 | try { 131 | out.writeInt(KEY_TYPED); 132 | out.writeChar(character); 133 | } catch (Throwable t) { 134 | synchronized (this) { 135 | connected = false; 136 | } 137 | } 138 | return false; 139 | } 140 | 141 | @Override 142 | public boolean touchDown(int x, int y, int pointer, int button) { 143 | synchronized (this) { 144 | if (!connected) return false; 145 | } 146 | 147 | try { 148 | out.writeInt(TOUCH_DOWN); 149 | out.writeInt(x); 150 | out.writeInt(y); 151 | out.writeInt(pointer); 152 | } catch (Throwable t) { 153 | synchronized (this) { 154 | connected = false; 155 | } 156 | } 157 | return false; 158 | } 159 | 160 | @Override 161 | public boolean touchUp(int x, int y, int pointer, int button) { 162 | synchronized (this) { 163 | if (!connected) return false; 164 | } 165 | 166 | try { 167 | out.writeInt(TOUCH_UP); 168 | out.writeInt(x); 169 | out.writeInt(y); 170 | out.writeInt(pointer); 171 | } catch (Throwable t) { 172 | synchronized (this) { 173 | connected = false; 174 | } 175 | } 176 | return false; 177 | } 178 | 179 | @Override 180 | public boolean touchDragged(int x, int y, int pointer) { 181 | synchronized (this) { 182 | if (!connected) return false; 183 | } 184 | 185 | try { 186 | out.writeInt(TOUCH_DRAGGED); 187 | out.writeInt(x); 188 | out.writeInt(y); 189 | out.writeInt(pointer); 190 | } catch (Throwable t) { 191 | synchronized (this) { 192 | connected = false; 193 | } 194 | } 195 | return false; 196 | } 197 | 198 | @Override 199 | public boolean mouseMoved(int x, int y) { 200 | return false; 201 | } 202 | 203 | @Override 204 | public boolean scrolled(int amount) { 205 | return false; 206 | } 207 | 208 | public boolean isConnected() { 209 | synchronized (this) { 210 | return connected; 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/fsm/StateMachineSystem.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley.fsm 2 | 3 | import com.badlogic.ashley.core.ComponentMapper 4 | import com.badlogic.ashley.core.Engine 5 | import com.badlogic.ashley.core.Entity 6 | import com.badlogic.ashley.core.EntityListener 7 | import com.badlogic.ashley.core.Family 8 | import com.badlogic.ashley.systems.IteratingSystem 9 | import com.badlogic.gdx.utils.Array 10 | import com.github.dwursteisen.libgdx.ashley.EventBus 11 | import com.github.dwursteisen.libgdx.ashley.EventData 12 | import com.github.dwursteisen.libgdx.ashley.EventListener 13 | import com.github.dwursteisen.libgdx.ashley.StateComponent 14 | import com.github.dwursteisen.libgdx.ashley.get 15 | import com.github.dwursteisen.libgdx.ashley.getNullable 16 | import ktx.log.debug 17 | import kotlin.reflect.KClass 18 | 19 | typealias Transition = StateMachine.(entity: Entity, event: EventData) -> Unit 20 | 21 | typealias Event = Int 22 | 23 | abstract class StateMachineSystem( 24 | val eventBus: EventBus, 25 | private val clazz: Class 26 | ) : IteratingSystem(Family.all(clazz).get()), EventListener, StateMachine { 27 | 28 | private var transitions = emptyMap>() 29 | private var defaultTransition = emptyMap() 30 | 31 | private val state: ComponentMapper = get() 32 | 33 | private val tmpEntities = Array() 34 | 35 | private val events: Set 36 | get() { 37 | val allEvents = transitions.values.flatMap { it.keys }.toSet() 38 | if (defaultTransition.isNotEmpty()) { 39 | return allEvents + EVENT_NOP 40 | } 41 | return allEvents 42 | } 43 | 44 | companion object { 45 | const val EVENT_NOP = -1 46 | const val EVENT_TOUCHED = -2 47 | const val EVENT_SLIDE = -3 48 | const val EVENT_KEY = -4 49 | const val EVENT_KEY_UP = -5 50 | } 51 | 52 | /** 53 | * Describe the current state machine. 54 | */ 55 | abstract fun describeMachine() 56 | 57 | override fun addedToEngine(engine: Engine) { 58 | super.addedToEngine(engine) 59 | describeMachine() 60 | eventBus.register(this, *events.toIntArray()) 61 | engine.addEntityListener(family, object : EntityListener { 62 | override fun entityRemoved(entity: Entity) = Unit 63 | 64 | override fun entityAdded(entity: Entity) { 65 | eventBus.emit(EVENT_NOP, entity) 66 | } 67 | }) 68 | } 69 | 70 | /** 71 | * Will search the current state regarding the entity and the class targeted by this state machine. 72 | */ 73 | private inline fun usingState(entity: Entity, block: (state: EntityState, time: Float) -> Unit) { 74 | val state = entity.getNullable(state) 75 | if (state == null) { 76 | ktx.log.error { 77 | "Your entity SHOULD have the StateComponent component, " + 78 | "as the entity is managed by a State Machine System. " + 79 | "As the current entity doesn't have a state, it will be silently ignored. " + 80 | "But you may wants to fix this issue as it is not expected." 81 | } 82 | } else { 83 | val componentState = state.status[clazz] ?: EntityState.STATE_NOP 84 | val time = state.time - (state.timeReset[clazz] ?: state.time) 85 | block.invoke(componentState, time) 86 | } 87 | } 88 | 89 | override fun processEntity(entity: Entity, deltaTime: Float) { 90 | usingState(entity) { state, time -> 91 | state.update(entity, time) 92 | } 93 | } 94 | 95 | class OnState( 96 | private val state: EntityState, 97 | private val parent: StateMachineSystem 98 | ) { 99 | 100 | fun on(vararg events: Int, block: Transition): OnState { 101 | events.forEach { on(it, block) } 102 | return this 103 | } 104 | 105 | fun on(events: List, block: Transition): OnState { 106 | events.forEach { on(it, block) } 107 | return this 108 | } 109 | 110 | fun on(event: Int, block: Transition): OnState { 111 | var currentTransitions = parent.transitions[state] ?: emptyMap() 112 | currentTransitions = currentTransitions + (event to block) 113 | 114 | parent.transitions += state to currentTransitions 115 | return this 116 | } 117 | 118 | fun default(block: Transition) { 119 | parent.defaultTransition += state to block 120 | } 121 | } 122 | 123 | fun onState(state: EntityState): OnState = OnState(state, this) 124 | 125 | fun startsWith(state: EntityState) = startsWith { entity, data -> go(state, entity, data) } 126 | 127 | fun startsWith(transition: Transition) { 128 | onState(EntityState.STATE_NOP).default(transition) 129 | } 130 | 131 | fun startsWith(state: EntityState, block: (Entity) -> Unit) { 132 | startsWith { entity, data -> 133 | go(state, entity, data) 134 | block(entity) 135 | } 136 | } 137 | 138 | override fun go(newState: EntityState, entity: Entity) = go(newState, entity, eventBus.createEventData()) 139 | 140 | override fun go(newState: EntityState, entity: Entity, event: EventData) { 141 | usingState(entity) { entityState, _ -> 142 | debug("STATE_MACHINE") { "Exit ${entityState::class.java.simpleName} on event ${event.event}" } 143 | entityState.exit(entity, event) 144 | } 145 | 146 | entity[state].status[clazz] = newState 147 | entity[state].timeReset[clazz] = entity[state].time 148 | 149 | usingState(entity) { entityState, _ -> 150 | debug("STATE_MACHINE") { "Enter ${entityState::class.java.simpleName} on event ${event.event}" } 151 | 152 | entityState.enter(entity, event) 153 | } 154 | } 155 | 156 | override fun emit(event: Event) = emit(event, eventBus.createEventData()) 157 | 158 | override fun emit(event: Event, eventData: EventData) { 159 | tmpEntities.clear() 160 | entities?.let { 161 | entities.forEach { tmpEntities.add(it) } 162 | } 163 | tmpEntities.forEach { perform(event, it, eventData) } 164 | } 165 | 166 | private fun perform(event: Event, entity: Entity, eventData: EventData) { 167 | eventData.event = event 168 | usingState(entity) { entityState, _ -> 169 | val transition: Transition? = transitions[entityState]?.get(event) ?: defaultTransition[entityState] 170 | transition?.invoke(this, entity, eventData) 171 | } 172 | } 173 | 174 | override fun onEvent(event: Event, eventData: EventData) { 175 | if (!checkProcessing()) { 176 | return 177 | } 178 | val target = eventData.target 179 | 180 | if (target == null) { 181 | emit(event, eventData) 182 | } else if (family.matches(target)) { 183 | perform(event, target, eventData) 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /ashley-addons/src/main/kotlin/com/github/dwursteisen/libgdx/ashley/EventBus.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.ashley 2 | 3 | import com.badlogic.ashley.core.Entity 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.InputAdapter 6 | import com.badlogic.gdx.InputMultiplexer 7 | import com.badlogic.gdx.InputProcessor 8 | import com.badlogic.gdx.math.Vector2 9 | import com.badlogic.gdx.utils.Pool 10 | import com.github.dwursteisen.libgdx.ashley.fsm.Event 11 | import com.github.dwursteisen.libgdx.ashley.fsm.StateMachineSystem 12 | import ktx.log.debug 13 | 14 | interface EventListener { 15 | fun onEvent(event: Event, eventData: EventData) 16 | } 17 | 18 | class EventData(var event: Int = Int.MIN_VALUE, var target: Entity? = null, var body: Any? = null) : Pool.Poolable { 19 | 20 | override fun reset() { 21 | event = Int.MIN_VALUE 22 | target = null 23 | body = null 24 | } 25 | } 26 | 27 | data class EventTimer(var timer: Float = 0f, val event: Event, val data: EventData) 28 | 29 | /** 30 | * Simple Event bus. 31 | * 32 | * - register listener through register method 33 | * - The update method should be called from the main loop 34 | * 35 | */ 36 | class EventBus(private val eventMapper: Map = emptyMap()) { 37 | 38 | private val pool: Pool = object : Pool() { 39 | override fun newObject(): EventData = EventData() 40 | } 41 | 42 | class EventInputProcessor(private val bus: EventBus) : InputAdapter() { 43 | 44 | private val touchDown = Vector2() 45 | private val touchUp = Vector2() 46 | 47 | override fun keyDown(keycode: Int): Boolean { 48 | val keyEventData = bus.createEventData() 49 | keyEventData.body = keycode 50 | bus.emit(StateMachineSystem.EVENT_KEY, keyEventData) 51 | return super.keyDown(keycode) 52 | } 53 | 54 | override fun keyUp(keycode: Int): Boolean { 55 | val keyEventData = bus.createEventData() 56 | keyEventData.body = keycode 57 | bus.emit(StateMachineSystem.EVENT_KEY_UP, keyEventData) 58 | return super.keyDown(keycode) 59 | } 60 | 61 | override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { 62 | val screenTouchData = bus.createEventData() 63 | touchDown.set(screenX.toFloat(), screenY.toFloat()) 64 | screenTouchData.body = touchDown 65 | bus.emit(StateMachineSystem.EVENT_TOUCHED, screenTouchData) 66 | return super.touchDown(screenX, screenY, pointer, button) 67 | } 68 | 69 | override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean { 70 | touchDown.set(screenX.toFloat(), screenY.toFloat()) 71 | return super.touchDragged(screenX, screenY, pointer) 72 | } 73 | 74 | override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { 75 | val screenTouchData = bus.createEventData() 76 | touchUp.set(screenX.toFloat(), screenY.toFloat()) 77 | screenTouchData.body = touchDown to touchUp 78 | bus.emit(StateMachineSystem.EVENT_SLIDE, screenTouchData) 79 | return super.touchUp(screenX, screenY, pointer, button) 80 | } 81 | } 82 | 83 | init { 84 | resetInputProcessor() 85 | } 86 | 87 | private var currentProcessor: InputProcessor? = null 88 | 89 | private fun resetInputProcessor() { 90 | currentProcessor = Gdx.input.inputProcessor 91 | val processor = EventInputProcessor(this) 92 | if (currentProcessor == null) { 93 | Gdx.input.inputProcessor = processor 94 | } else { 95 | Gdx.input.inputProcessor = InputMultiplexer(processor, currentProcessor) 96 | } 97 | } 98 | 99 | fun clearInputProcessor() { 100 | Gdx.input.inputProcessor = null 101 | } 102 | 103 | private var listeners: MutableMap> = mutableMapOf() 104 | 105 | private var emitter: MutableList> = mutableListOf() 106 | private var emitterMirror: MutableList> = mutableListOf() 107 | 108 | private var emitterLatter: MutableList = mutableListOf() 109 | private var emitterLatterMirror: MutableList = mutableListOf() 110 | 111 | @Deprecated("This method will be marked as private in future release. Use #emit + lambda version instead.") 112 | fun createEventData(): EventData = pool.obtain() 113 | 114 | fun emit(event: Event, entity: Entity, data: EventData = createEventData()) { 115 | data.target = entity 116 | emit(event, data) 117 | } 118 | 119 | fun emit(event: Event, data: EventData = createEventData()) { 120 | data.event = event 121 | debug { "will emit ${eventMapper[event] ?: "??"} (id : $event)" } 122 | emitter.add(event to data) 123 | } 124 | 125 | fun emit(event: Event, vararg entities: Entity, data: EventData = createEventData()) { 126 | entities.forEach { 127 | val cpy = createEventData() 128 | cpy.body = data.body 129 | emit(event, it, cpy) 130 | } 131 | } 132 | 133 | fun emitData(event: Event, body: () -> Any? = { null }) { 134 | val data = createEventData() 135 | data.body = body() 136 | emit(event, data) 137 | } 138 | 139 | fun emitLater(delta: Float, event: Event, entity: Entity, data: EventData = createEventData()) { 140 | data.target = entity 141 | emitLater(delta, event, data) 142 | } 143 | 144 | fun emitLater(delta: Float, event: Event, data: EventData = createEventData()) { 145 | data.event = event 146 | val timer = EventTimer(delta, event, data) 147 | emitterLatter.add(timer) 148 | } 149 | 150 | fun register(eventListener: EventListener, vararg events: Event) { 151 | events.forEach { 152 | val lst = listeners[it] ?: emptyList() 153 | listeners[it] = lst + eventListener 154 | } 155 | } 156 | 157 | fun update(delta: Float) { 158 | 159 | emitterLatter.forEach { it.timer -= delta } 160 | val toEmitNow = emitterLatter.filter { it.timer < 0 } 161 | 162 | emitterLatterMirror.addAll(toEmitNow) 163 | emitterLatterMirror.sortBy { it.event } 164 | 165 | emitterMirror.addAll(emitter) 166 | emitterMirror.sortBy { it.first } 167 | 168 | debug { emitterMirror.joinToString("\n") { "emit ${eventMapper[it.first] ?: "??"} (id : ${it.first})" } } 169 | 170 | emitterLatterMirror.forEach { invoke(it.event, it.data) } 171 | emitterMirror.forEach { invoke(it.first, it.second) } 172 | 173 | val eventDataToReset = toEmitNow.map { it.data } + emitterMirror.map { it.second } 174 | 175 | emitterLatter.removeAll(toEmitNow) 176 | emitter.removeAll(emitterMirror) 177 | 178 | eventDataToReset.forEach { pool.free(it) } 179 | 180 | emitterMirror.clear() 181 | emitterLatterMirror.clear() 182 | } 183 | 184 | private fun invoke(event: Event, data: EventData) { 185 | listeners[event]?.forEach { lst -> lst.onEvent(event, data) } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /libgdx-test/src/main/kotlin/com/github/dwursteisen/libgdx/test/LibGdxRule.kt: -------------------------------------------------------------------------------- 1 | package com.github.dwursteisen.libgdx.test 2 | 3 | import com.badlogic.gdx.ApplicationListener 4 | import com.badlogic.gdx.Gdx 5 | import com.badlogic.gdx.Input 6 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication 7 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration 8 | import com.badlogic.gdx.graphics.Pixmap 9 | import com.badlogic.gdx.graphics.PixmapIO 10 | import com.badlogic.gdx.input.RemoteInput 11 | import com.badlogic.gdx.utils.BufferUtils 12 | import com.badlogic.gdx.utils.ScreenUtils 13 | import org.junit.rules.TestRule 14 | import org.junit.runner.Description 15 | import org.junit.runners.model.Statement 16 | import java.util.concurrent.CountDownLatch 17 | import java.util.concurrent.Executors 18 | import java.util.concurrent.TimeUnit 19 | import java.util.concurrent.atomic.AtomicBoolean 20 | 21 | class LibGdxRule(val listener: ApplicationListener, val configuration: LwjglApplicationConfiguration, val output: String = LibGdxRule::class.java.getResource(".").path) : TestRule { 22 | 23 | 24 | companion object { 25 | private var executor: Thread? = null 26 | private var delegate: DelegateGame? = null 27 | private var app: LwjglApplication? = null 28 | private val sender: RemoteSender by lazy { RemoteSender("127.0.0.1", RemoteInput.DEFAULT_PORT) } 29 | private var queue = emptySequence() 30 | private var killMe = AtomicBoolean(false) 31 | private val isRunning = AtomicBoolean(true) 32 | private var quitLatch: CountDownLatch = CountDownLatch(1) 33 | } 34 | 35 | /** 36 | * Start your game. 37 | * 38 | * It can be quite long but all commands will be started after the game is fully started. 39 | */ 40 | fun startGame(): GdxDsl = GdxDsl(this) 41 | 42 | 43 | internal fun add(action: InputAction) { 44 | queue += action 45 | } 46 | 47 | override fun apply(base: Statement, description: Description): Statement { 48 | return object : Statement() { 49 | override fun evaluate() { 50 | 51 | killMe.set(false) 52 | 53 | val before = CountDownLatch(1) 54 | val after = CountDownLatch(1) 55 | 56 | if (delegate == null) { 57 | delegate = DelegateGame(listener, before, after) 58 | 59 | app = LwjglApplication(delegate, configuration) 60 | } else { 61 | Gdx.app.postRunnable { 62 | delegate!!.restart(listener, before, after) 63 | } 64 | } 65 | before.await() 66 | 67 | quitLatch = CountDownLatch(1) 68 | if (executor == null) { 69 | val thread = Thread() { 70 | while (isRunning.get()) { 71 | queue.firstOrNull()?.run { 72 | when (this) { 73 | is InputAction.Wait -> { 74 | this.duration -= 0.010f 75 | if (this.duration < 0f) { 76 | queue = queue.drop(1) 77 | } 78 | } 79 | is InputAction.Push -> { 80 | sender.keyDown(this.key) 81 | queue = queue.drop(1) 82 | 83 | } 84 | is InputAction.Release -> { 85 | sender.keyUp(this.key) 86 | queue = queue.drop(1) 87 | 88 | } 89 | is InputAction.Type -> { 90 | sender.keyTyped(this.char) 91 | queue = queue.drop(1) 92 | } 93 | 94 | is InputAction.Touch -> { 95 | sender.touchDown(this.x, this.y, 1, Input.Buttons.LEFT) 96 | queue = queue.drop(1) 97 | } 98 | 99 | is InputAction.Screenshot -> { 100 | Gdx.app.postRunnable { 101 | val pixels = ScreenUtils.getFrameBufferPixels(0, 0, Gdx.graphics.backBufferWidth, Gdx.graphics.backBufferHeight, true) 102 | val pixmap = Pixmap(Gdx.graphics.backBufferWidth, Gdx.graphics.backBufferHeight, Pixmap.Format.RGBA8888) 103 | BufferUtils.copy(pixels, 0, pixmap.pixels, pixels.size) 104 | 105 | Gdx.app.log("LIBGDX-TEST", "Start build screenshot") 106 | val external = Gdx.files.absolute(output + this.name) 107 | PixmapIO.writePNG(external, pixmap) 108 | pixmap.dispose() 109 | Gdx.app.log("LIBGDX-TEST", "Screenshot created at ${external.path()}") 110 | } 111 | queue = queue.drop(1) 112 | } 113 | 114 | 115 | is InputAction.StartRecord -> { 116 | delegate?.startRecord() 117 | queue = queue.drop(1) 118 | } 119 | 120 | is InputAction.StopRecord -> { 121 | delegate?.stopRecord(output + this.filename) 122 | queue = queue.drop(1) 123 | } 124 | is InputAction.Quit -> { 125 | quitLatch.countDown() 126 | queue = queue.drop(1) 127 | } 128 | } 129 | } 130 | sender.sendUpdate() 131 | 132 | Thread.sleep(10) 133 | } 134 | } 135 | executor = thread 136 | thread.start() 137 | } 138 | after.await() 139 | base.evaluate() 140 | add(InputAction.Wait(1f)) 141 | add(InputAction.Quit) 142 | quitLatch.await() 143 | 144 | killMe.set(true) 145 | 146 | Executors.newSingleThreadScheduledExecutor().schedule({ 147 | if (killMe.get()) { 148 | isRunning.set(false) 149 | app?.stop() 150 | } 151 | }, 3, TimeUnit.SECONDS) 152 | 153 | } 154 | 155 | } 156 | } 157 | 158 | } 159 | 160 | 161 | -------------------------------------------------------------------------------- /src/docs/asciidoc/01_create_project.adoc: -------------------------------------------------------------------------------- 1 | == Getting started 2 | 3 | LibGDX use https://gradle.org[gradle] as a build system. 4 | You can use the https://libgdx.badlogicgames.com/download.html[official setup app] to create your game. 5 | Instead LibGDX-addons propose a gradle plugin to take care of all gradle setup, 6 | for all platform (Currently supported: Desktop and Android) so you don't have to manage it or update 7 | your gradle script. 8 | 9 | === Setup your game 10 | 11 | Create your first game by using https://www.jetbrains.com/idea/[IntelliJ]. 12 | Click in `File` > `New Project` 13 | 14 | image::media/create_game_01.png[] 15 | 16 | Pick `Gradle` then tick `Kotlin DSL build script`, choose `Kotlin/JVM` and go to the next screen by clicking `Next`. 17 | 18 | image::media/create_game_02.png[] 19 | 20 | Fill the information asked and click `Next` until IntelliJ create your project. 21 | 22 | Open the file `build.gradle.kts` and paste this configuration into. 23 | It adds necessary repositories for dependencies and add the https://github.com/dwursteisen/libgdx-addons/tree/master/libgdx-gradle-plugin[gradle libgdx plugin]. 24 | 25 | .build.gradle.kts 26 | [source,kotlin,subs=attributes+] 27 | ---- 28 | 29 | buildscript { 30 | dependencies { 31 | classpath("com.github.dwursteisen.libgdx-addons:libgdx-gradle-plugin:016e8b7") 32 | } 33 | // <1> 34 | repositories { 35 | mavenCentral() 36 | google() 37 | jcenter() 38 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 39 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 40 | maven { url = uri("https://jitpack.io") } 41 | } 42 | } 43 | 44 | allprojects { 45 | // <2> 46 | repositories { 47 | mavenCentral() 48 | google() 49 | jcenter() 50 | maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } 51 | maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } 52 | maven { url = uri("https://jitpack.io") } 53 | } 54 | } 55 | 56 | apply(plugin = "libgdx") 57 | ---- 58 | <1> Add dependency repositories for plugins; 59 | <2> Add dependency repositories for all modules. 60 | 61 | Run the command `gradlew build`: it will download gradle if needed and download all dependencies and create the minimal setup. 62 | Once done, it might be long depending of your Internet connection, refresh the project in IntelliJ. 63 | 64 | image::media/create_game_02b.png[title="Your project when everything is ready"] 65 | 66 | === Run your game 67 | 68 | Your game project is now ready! 69 | 70 | You can run the game from the command line using `./gradlew run`. Gradle will now compile the code that 71 | was previously generated and run your game. But it might be more convenient to run the game from IntelliJ. 72 | 73 | As you refreshed the project into IntelliJ, a new https://www.jetbrains.com/help/idea/creating-and-editing-run-debug-configurations.html[Run Configuration] just appear in your IntelliJ. 74 | Click on `Add Configuration` and check `Application` run configurations. You will see a run configuration added in IntelliJ 75 | to run your game. 76 | 77 | image::media/create_game_03.png[] 78 | 79 | 80 | === Draw your first texture 81 | 82 | Grab an image and copy it into the `src/main/assets` directory of the `core` module. 83 | You can find free image / sprite sheet on the https://opengameart.org/content/a-blocky-dungeon[OpenGameArt.org]. 84 | 85 | Run the command `./gradlew build`, the command will generate a new class `Assets` that will 86 | contains all of your assets name. 87 | 88 | NOTE: The use of the `Assets` object is not mandatory. But if your rename one of your assets, 89 | it's practical to have a compilation error instead of your game crashing. 90 | 91 | Create your game into the `core` directory and use it in `MainClass.kt`. 92 | 93 | .MyGame.kt 94 | [source,kotlin] 95 | ---- 96 | include::samples/core/src/main/kotlin/step2/MyGame.kt[tags="body"] 97 | ---- 98 | 99 | .MainClass.kt 100 | [source,kotlin] 101 | ---- 102 | include::samples/desktop/src/main/kotlin/step2/MainClass.kt[tags="body"] 103 | ---- 104 | 105 | If you run your game, the texture will be displayed on the screen. 106 | 107 | image::media/create_game_04.png[] 108 | 109 | === Increase your feedback loop 110 | 111 | It's important to have a quick feedback loop while developing a game. 112 | For example, what's the point of restarting your game when you just updated 113 | a texture from your game? 114 | 115 | To enable this feature, you need to add a dependency to 116 | the https://github.com/dwursteisen/libgdx-addons/tree/master/core-addons[libgdx core-addons]. 117 | 118 | Add this into your `build.gradle.kts` then refresh the project in your IDE: 119 | 120 | .build.gradle.kts 121 | [source,kotlin,subs=attributes+] 122 | ---- 123 | project(":core") { 124 | dependencies { 125 | implementation("com.github.dwursteisen.libgdx-addons:core-addons:{gitCommit}") 126 | } 127 | } 128 | ---- 129 | 130 | You can now update your game to use the asset manager of libgdx with `RefreshableTexture`. 131 | 132 | .MyGame.kt 133 | [source,kotlin] 134 | ---- 135 | include::samples/core/src/main/kotlin/step3/MyGame.kt[tags="body"] 136 | ---- 137 | <1> Replace the default Texture Loader with the RefreshableTexture Loader; 138 | <2> Force to load all assets before rendering the game; 139 | <3> Clear the screen; 140 | <4> Getting the texture from the asset manager. 141 | 142 | Run your game and while the game is running, apply a modification on the texture file. 143 | As soon as you save the file, the texture will be updated in your game. 144 | 145 | image::media/create_game_05.gif[] 146 | 147 | NOTE: The asset manager is not mandatory. But by using it, you can use the `RefreshableTexture` 148 | for the desktop version of your game and the default texture loader for your android game without 149 | having to change your whole code base. 150 | 151 | === Setup a entity engine 152 | 153 | A game is a lot of different entities who share a lot of common behaviours. For example, the player 154 | and an enemy have bot a position. They have both a size too. You can create a class that both will 155 | inherit. But it will increase your code complexity. Instead, you can compose your entities of different 156 | properties: an entity can be a player with a size and a position. An enemy is an entity with a size, a position, 157 | some other capacities. 158 | 159 | [Ashley](https://github.com/libgdx/ashley) is one entity engine for libGDX. It is very easy to setup in your project. 160 | Add ashley as dependencies of your project then refresh it in your IDE. 161 | 162 | .build.gradle.kts 163 | [source,kotlin,subs=attributes+] 164 | ---- 165 | project(":core") { 166 | dependencies { 167 | implementation("com.github.dwursteisen.libgdx-addons:core-addons:{gitCommit}") 168 | implementation("com.badlogicgames.ashley:ashley:{versionAshley}") 169 | } 170 | } 171 | ---- 172 | 173 | The code of your game needs to be updated to add the entity engine and create your first entity. 174 | 175 | .MyGame.kt 176 | [source,kotlin] 177 | ---- 178 | include::samples/core/src/main/kotlin/step4/MyGame.kt[tags="body"] 179 | ---- 180 | <1> Create an `EntityEngine`. Please note that you can use a `PooledEntityEngine` too; 181 | <2> Add all systems to the engine. For now, we will just add a system that will render all entities on screen; 182 | <3> Split the texture to get only a region. This region will be use to display the player; 183 | <4> Create your first entity. This entity is marked as the `Player`. Has a position, a size, a texture and marked as to be rendered; 184 | <5> In the render loop, update the engine. 185 | 186 | When creating the player, please note that only the component `Player` needs to be created. 187 | 188 | .Components.kt 189 | [source,kotlin] 190 | ---- 191 | include::samples/core/src/main/kotlin/step4/Components.kt[] 192 | ---- 193 | 194 | Other components are provided by `ashley-addons`: `Position`, `Size`, … 195 | 196 | IMPORTANT: Do not forget to set a size! Otherwise your entity will have a width and heigh 197 | of 0 so will be invisible. 198 | 199 | The player can be moved by detecting if some keys where pressed, like left, right, … 200 | 201 | .PlayerSystem.kt 202 | [source,kotlin] 203 | ---- 204 | include::samples/core/src/main/kotlin/step4/PlayerSystem.kt[tags="body"] 205 | ---- 206 | 207 | Regarding the key that was pressed, the position of the entity is extracted. 208 | 209 | NOTE: The property component is accessed from the entity like it is a map. 210 | More information can be found in the <> section. 211 | 212 | === Create a state machine 213 | 214 | A game can be composed of a lot of state machine. 215 | But what is a state machine? It's an entity that can be in state 216 | and regarding a certain event transition can goes in another state. 217 | 218 | A simple example could be a door: it can be open and goes in a close 219 | state if the player push a switch. 220 | 221 | image::media/create_game_06.png[] 222 | 223 | === React to events 224 | 225 | === Package your game for Desktop 226 | 227 | TIP: WIP 228 | 229 | === Package your game for Android 230 | 231 | TIP: WIP 232 | --------------------------------------------------------------------------------