-
-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
34 changed files
with
1,739 additions
and
233 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.