Skip to content

Commit

Permalink
Released 1.9.6-b4. #77
Browse files Browse the repository at this point in the history
  • Loading branch information
czyzby committed Jun 1, 2017
2 parents 4538544 + 18d9fe7 commit 1180c0b
Show file tree
Hide file tree
Showing 34 changed files with 1,739 additions and 233 deletions.
18 changes: 9 additions & 9 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ git checkout develop

### Build tool

The project itself is managed by [Gradle](http://gradle.org/). Gradle wrapper is not included, so you might want to
install it locally. Scripts should be compatible with Gradle `3.+`. If you consider working from sources, these are
The project itself is managed by [Gradle](http://gradle.org/). Gradle wrapper is included, but you can use a local
Gradle installation - scripts should be compatible with Gradle `3.+`. If you consider working from sources, these are
some useful Gradle tasks that you can look into:

- `gradle build install` - builds the libraries archives and pushes them to Maven Local.
- `gradle check` - runs all tests in all projects.
- `gradle clean` - removes `build` directories.
- `gradle distZip` - prepares a zip archive with all jars in `build/distributions` folder. Useful for releases.
- `gradle uploadArchives` - pushes the archives to Maven Central. Requires proper `gradle.properties` with signing and
logging data.
- `gradle closeAndPromoteRepository` - closes and promotes Nexus repository. Should be run after `uploadArchives` in
- `build install` - builds the libraries archives and pushes them to Maven Local.
- `check` - runs all tests in all projects.
- `clean` - removes `build` directories.
- `distZip` - prepares a zip archive with all jars in `build/distributions` folder. Useful for releases.
- `uploadArchives` - pushes the archives to Maven Central. Requires proper `gradle.properties` with archive signing and
Sonatype logging data.
- `closeAndPromoteRepository` - closes and promotes Nexus repository. Should be run after `uploadArchives` in
case of a non-snapshot upload to Maven Central.

### Versioning and uploading
Expand Down
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
#### 1.9.6-SNAPSHOT
#### 1.9.6-b4

- **[UPDATE]** Updated to Kotlin 1.1.2.
- **[FEATURE]** (`ktx-collections`) Added `map`, `filter` and `flatten` extension methods that return LibGDX collections.
- **[FEATURE]** (`ktx-collections`) `PooledList` now properly implements `hashCode` and `equals`.
- **[FEATURE]** (`ktx-app`) Added `KtxGame`: **KTX** equivalent of LibGDX `Game`.
- **[FEATURE]** (`ktx-app`) Added `KtxScreen`: adapter of the LibGDX `Screen` interface making all methods optional to implement.
- **[FEATURE]** (`ktx-app`) Added `emptyScreen` utility method returning a no-op implementation of `Screen`.
- **[FEATURE]** (`ktx-inject`) `Context` now implements `Disposable` and allows to dispose of all registered singletons and providers.
- **[FEATURE]** (`ktx-inject`) Added `Context.remove` and `removeProvider` methods. Now providers for particular types can be removed without clearing the whole context.
- **[FEATURE]** (`ktx-inject`) `getProvider`, `setProvider` and `clear` methods of `Context` are now open and can be overridden.

#### 1.9.6-b3

- **[UPDATE]** Updated to Kotlin 1.1.2-3.
- **[UPDATE]** Updated to Kotlin Coroutines to 0.15.
- **[CHANGE]** (`ktx-assets`) Static `AssetManager` instance container - `Assets` - was removed. All top level functions
depending on the global `AssetManager` were removed.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ compile "io.github.libktx:ktx-$module:$ktxVersion"

Replace `$module` with the name of required **KTX** library. `$ktxVersion` usually matches LibGDX version it was
compiled against - although it might end with `-b1` (if it is a beta release) or `-SNAPSHOT` (if you are using
the snapshots). For example, the first official beta release with the recent group ID was compiled against LibGDX
the snapshots). For example, the first official beta release with the current group ID was compiled against LibGDX
`1.9.6` and its version was `1.9.6-b2`. You can browse through our releases
[here](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.libktx%22).

Expand Down
51 changes: 51 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ time step duration and max time step with its constructor parameters. This is a
implementation if you like working from scratch.
- `KtxApplicationAdapter` is an `ApplicationListener` extension. Provides no-op implementations of all methods, without
being an abstract class like `com.badlogic.gdx.ApplicationAdapter`.
- `KtxGame` is a bit more opinionated `Game` equivalent that not only delegates all game events to the current `Screen`
instance, but also ensures non-nullability of screens, manages screen clearing and fixed rendering step, and maintains
screens collection, which allows to switch screens knowing only their concrete class. `KtxScreen` is an interface
extending `Screen` that provides no-op method implementations, making all methods optional to override.

#### `InputProcessor` implementations

Expand All @@ -40,6 +44,7 @@ different screen sizes.
overriding with optional, named parameters.
- `use` inlined extension methods added to `Batch` and `ShaderProgram`. They allow to omit the `begin()` and `end()`
calls before using batches and shader programs.
- `emptyScreen` provides no-op implementations of `Screen`.

### Usage examples

Expand Down Expand Up @@ -95,6 +100,52 @@ class MyApplicationListener : KtxApplicationAdapter {
}
```

Implementing `KtxGame` with one screen that displays text:

```Kotlin
import com.badlogic.gdx.Screen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import ktx.app.KtxGame
import ktx.app.KtxScreen
import ktx.app.use

class ExampleScreen : KtxScreen {
// Notice no `lateinit var` - ExampleScreen has no create()
// method and is constructed after LibGDX is fully initiated
// in ExampleGame.create method.
val font = BitmapFont()
val batch = SpriteBatch().apply {
color = Color.WHITE
}

override fun render(delta: Float) {
batch.use {
font.draw(it, "Hello Kotlin!", 100f, 100f)
}
}

override fun dispose() {
// Will be automatically disposed of by the game instance.
font.dispose()
batch.dispose()
}
}

/** ApplicationListener implementation. */
class ExampleGame : KtxGame<Screen>() {
override fun create() {
// Registering ExampleScreen in the game object: it will be
// accessible through ExampleScreen class:
addScreen(ExampleScreen())
// Changing current screen to the registered instance of the
// ExampleScreen class:
setScreen<ExampleScreen>()
}
}
```

Implementing `KtxInputAdapter`:

```Kotlin
Expand Down
5 changes: 3 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dependencies {
testCompile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
testCompile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
provided "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
testCompile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
testCompile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
}
221 changes: 221 additions & 0 deletions app/src/main/kotlin/ktx/app/game.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package ktx.app

import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Screen
import com.badlogic.gdx.utils.GdxRuntimeException
import com.badlogic.gdx.utils.ObjectMap

/**
* An equivalent of [com.badlogic.gdx.Game] delegating game events to a [Screen]. On contrary to `Game`, [KtxGame]
* maintains a collection of screens mapped by their type and allows to change screens knowing only their type with
* [setScreen]. Thanks to this, its easier to cache screens without maintaining a singleton with all [Screen] instances
* manually. [ScreenType] generic type allows to users to use an extended specific base class (or interface) for all
* screens, without locking into [Screen].
*
* Since this is a [KotlinApplication] extension, it also handles screen clearing and fixed rendering time step. See
* [KotlinApplication] for more info.
*
* @param firstScreen will be immediately used by the application. Note that it cannot use any resources initiated by
* the LibGDX (like the OpenGL context) in the constructor, as the screen will be created before the application is
* launched. Defaults to an empty, mock-up screen implementation that should be replaced with the first [setScreen]
* method call in [create]. Note: `firstScreen` still has to be explicitly registered with [addScreen] if you want it to
* be accessible with [getScreen].
* @param fixedTimeStep see [KotlinApplication.fixedTimeStep].
* @param maxDeltaTime see [KotlinApplication.maxDeltaTime].
* @param ScreenType common base interface or class of all screens. Allows to use custom extended [Screen] API.
* @see KtxScreen
*/
open class KtxGame<ScreenType : Screen>(
firstScreen: ScreenType? = null,
fixedTimeStep: Float = 1f / 60f,
maxDeltaTime: Float = 1f) : KotlinApplication(fixedTimeStep, maxDeltaTime) {
/** Holds references to all screens registered with [addScreen]. Allows to get a reference of the screen instance
* knowing only its type. */
protected val screens: ObjectMap<Class<out ScreenType>, ScreenType> = ObjectMap()
/** Currently shown screen. Unless overridden with [setScreen], uses an empty mock-up implementation to work around
* nullability and `lateinit` issues. [shownScreen] is a public property exposing this value as [ScreenType].
* @see shownScreen */
protected var currentScreen: Screen = firstScreen ?: emptyScreen()
/** Provides direct access to current [Screen] instance handling game events. */
open val shownScreen: ScreenType
@Suppress("UNCHECKED_CAST")
get() = currentScreen as ScreenType

/**
* By default, this method resizes ([Screen.resize]) and shows ([Screen.show]) initial view. You do not have to call
* super if you used [setScreen] in the overridden [create] method or the selected first view does not need to be
* resized and showed before usage.
*/
override fun create() {
currentScreen.resize(Gdx.graphics.width, Gdx.graphics.height)
currentScreen.show()
}

override fun render(delta: Float) {
currentScreen.render(delta)
}

override fun resize(width: Int, height: Int) {
currentScreen.resize(width, height)
}

override fun pause() {
currentScreen.pause()
}

override fun resume() {
currentScreen.resume()
}

/**
* Registers an instance of [Screen].
* @param Type concrete class of the [Screen] instance. The implementation assumes that screens are singletons and
* only one implementation of each class will be registered.
* @param screen instance of [Type]. After invocation of this method, [setScreen] can be used with the appropriate
* class to change current screen to this object.
* @throws GdxRuntimeException if a screen of selected type is already registered. Use [removeScreen] first to replace
* the screen.
* @see getScreen
* @see setScreen
* @see removeScreen
*/
inline fun <reified Type : ScreenType> addScreen(screen: Type) = addScreen(Type::class.java, screen)

/**
* Registers an instance of [Screen]. Override this method to change the way screens are registered.
* @param type concrete class of the [Screen] instance. The implementation assumes that screens are singletons and
* only one implementation of each class will be registered.
* @param screen instance of [Type]. After invocation of this method, [setScreen] can be used with the appropriate
* class to change current screen to this object.
* @see getScreen
* @see setScreen
* @see removeScreen
*/
open fun <Type : ScreenType> addScreen(type: Class<Type>, screen: Type) {
!screens.containsKey(type) || throw GdxRuntimeException("Screen already registered to type: $type.")
screens.put(type, screen)
}

/**
* Replaces current screen with the registered screen instance of the passed type.
* @param Type concrete class of the screen implementation. The screen instance of this type must have been added
* with [addScreen] before calling this method.
* @see addScreen
* @see shownScreen
*/
inline fun <reified Type : ScreenType> setScreen() = setScreen(Type::class.java)

/**
* Replaces current screen with the registered screen instance of the passed type. Calls `hide` method of the
* previous screen and `show` method of the current screen. Override this method to control screen transitions.
* @param type concrete class of the screen implementation. The screen instance of this type must have been added with
* [addScreen] before calling this method.
* @see addScreen
* @see shownScreen
*/
open fun <Type : ScreenType> setScreen(type: Class<Type>) {
currentScreen.hide()
currentScreen = getScreen(type)
currentScreen.resize(Gdx.graphics.width, Gdx.graphics.height)
currentScreen.show()
}

/**
* Returns cached instance of [Screen] of the selected type.
* @param Type concrete class of the screen implementation. The screen instance of this type must have been added with
* [addScreen] before calling this method.
* @return an instance of [Screen] extending passed [Type].
* @throws GdxRuntimeException if instance of the selected [Type] was not registered with [addScreen].
* @see addScreen
*/
inline fun <reified Type : ScreenType> getScreen(): Type = getScreen(Type::class.java)

/**
* Returns cached instance of [Type] of the selected type.
* @param type concrete class of the screen implementation. The screen instance of this type must have been added with
* [addScreen] before calling this method.
* @return an instance of [Type] extending passed [type].
* @throws GdxRuntimeException if instance of the selected [type] was not registered with [addScreen].
* @see addScreen
*/
@Suppress("UNCHECKED_CAST")
open fun <Type : ScreenType> getScreen(type: Class<Type>): Type
= screens[type] as Type? ?: throw GdxRuntimeException("Missing screen instance of type: $type.")

/**
* Removes cached instance of [Screen] of the selected type. Note that this method does not dispose of the screen and
* will not affect [shownScreen].
* @param Type concrete class of the screen implementation.
* @return removed instance of [Screen] extending passed [Type] if was registered. `null` otherwise.
* @see addScreen
*/
inline fun <reified Type : ScreenType> removeScreen(): Type? = removeScreen(Type::class.java)

/**
* Removes cached instance of [Screen] of the selected type. Note that this method does not dispose of the screen and
* will not affect [shownScreen].
* @param type concrete class of the screen implementation.
* @return removed instance of [Screen] extending passed [type] if was registered. `null` otherwise.
* @see addScreen
*/
@Suppress("UNCHECKED_CAST")
open fun <Type : ScreenType> removeScreen(type: Class<Type>): Type? = screens.remove(type) as Type?

/**
* Checks if screen of the given type is registered.
* @param Type concrete class of a screen implementation.
* @return true if a [Screen] is registered with the selected type, false otherwise.
*/
inline fun <reified Type : ScreenType> containsScreen(): Boolean = containsScreen(Type::class.java)

/**
* Checks if screen of the given type is registered.
* @param type concrete class of a screen implementation.
* @return true if a [Screen] is registered with the selected type, false otherwise.
*/
open fun <Type : ScreenType> containsScreen(type: Class<Type>): Boolean = screens.containsKey(type)

/**
* Disposes of all registered screens with [Screen.dispose]. Catches thrown errors and logs them with LibGDX
* application API by default. Override [onScreenDisposalError] method to change error handling behavior. Should be
* called automatically by LibGDX application lifecycle handler.
*/
override fun dispose() {
screens.values().forEach {
try {
it.dispose()
} catch (exception: Throwable) {
onScreenDisposalError(it, exception)
}
}
}

/**
* Invoked during screens disposal on [dispose] if an error occurs.
* @param screen thrown [exception] during disposal.
* @param exception unexpected screen disposal exception.
*/
protected open fun onScreenDisposalError(screen: ScreenType, exception: Throwable) {
Gdx.app.error("KTX", "Unable to dispose of ${screen.javaClass} screen.", exception)
}
}

/**
* Provides empty implementations of all [Screen] methods, making them optional to override.
* @see KtxGame
*/
interface KtxScreen : Screen {
override fun show() = Unit
override fun render(delta: Float) = Unit
override fun resize(width: Int, height: Int) = Unit
override fun pause() = Unit
override fun resume() = Unit
override fun hide() = Unit
override fun dispose() = Unit
}

/**
* Provides a no-op implementation of [Screen]. A workaround of [Screen] nullability issues.
* @see KtxGame
*/
fun emptyScreen(): Screen = object : KtxScreen {}
2 changes: 0 additions & 2 deletions app/src/main/kotlin/ktx/app/letterboxingViewport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import com.badlogic.gdx.utils.viewport.ScalingViewport
* It is advised to pair this viewport with FitViewport - LetterboxingViewport can be used for the GUI, while
* FitViewport is excellent for the actual (2D) game rendering.
* @author MJ
*
* @param targetPpiX this is the targeted pixel per inch ratio on X axis, which allows to scale the viewport
* correctly on different devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles.
* Make sure to call [updateScale] after changing this variable.
Expand Down
Loading

0 comments on commit 1180c0b

Please sign in to comment.