Wiki
Clone wikirxgdx / Home
Reactive gdx
Framework for crossplatform gui and 2d-game development base on libgdx
Curent state
This project in early prototype.
Implemented features
- crossplatform (desktop, android, ios) thanks to libgdx!
- DI-drived
- modules + components + properties scene
- reactive
- events driven with reactive implementation
- property bindings driven with reactive implementation
- kotlin implementaion!
Features and compoents not implemented yet
- automaticaly load and save scene state based on properties
- ui skins
- common ui widgets and layouts (except image, label, vertical layout and some more, that already implemented)
DI + Modules + Components + Properties
Components and Properties
:::kotlin
@DependentComponents(PositionComponent::class)
class AlignAtCenterOfWorldComponent(parent: Module) : AbstractComponent(parent) {
private val position: PositionComponent by dependency()
val atCenterOfWorldProperty: Property<Boolean> = property(true)
var atCenterOfWorld:Boolean by atCenterOfWorldProperty
@OnEvent(WorldSizeChangedEvent::class)
fun onWorldChanged(event: WorldSizeChangedEvent) {
if (atCenterOfWorld) {
position.set(event.width/2f, event.height/2f)
}
}
}
Reactive
Loading assets,
:::kotlin val subscription = loadDependencies() .withRunner(Runners.newThread(executorService)).map { loadData() } .withRunner(Runners.gdx).map { createAsset(it) } .subscribe { this.asset = it }
Events
Events is a reactive stream. Use filter to handle events:
:::kotlin class SomeComponent(parent: Module) : AbstractComponent(parent) { override fun setup() { events().filterIs<UpdateEvent>().subscribe { doSomething() } } }
:::kotlin // instead of events().filterIs<UpdateEvent>().subscribe { doSomething() } onEvent<UpdateEvent> { doSomething() }
:::kotlin @OnEvent(UpdateEvent::class) fun update(event:UpdateEvent) { doSomething() } // or @OnUpdate fun update() { doSomething() }
Bindings
::kotlin val stringProperty = property("300") val intProperty1 = property(10) val intProperty2 = property(20) val binding = intProperty1 + intProperty2 stringProperty.bindOneWay(binding.asString()) Assert.assertEquals("30", stringProperty.value) intProperty1.value = 100 Assert.assertEquals("120", stringProperty.value)
Example
:::kotlin fun main(args: Array<String>) { startLwjglApplication { debug = true addInputTranslator(::InputToEventsProcessor) addInputTranslator { GestureDetector(GesturesToEventProcessor(it)) } buildScene("test-scene") { layer("main-layer") { withComponent<ImageCopierComponent>() withScreenSize() camera("camera") { freeTransform() } projection("camera") } withModule(debugGuiLayer()) } scheduleScene("test-scene") } } class ImageCopierComponent(parent: Module, val moduleFactory: ModuleFactory, val director: SceneDirector) : AbstractComponent(parent) { private var counter = 0 private fun newSprite(x:Float, y:Float) = spriteDefinition("sprite-${counter++}", "whiteflag.png") { withComponent<ActionsComponent>() withComponent<ClickToDetachComponent>() withPivotAtCenter() withValues { transform.position(x, y) } } @OnEvent(TapGestureEvent::class) fun attachImage(event: AbstractScreenEvent) = poolable { event.handled() val sprite = moduleFactory.create(newSprite(event.worldX, event.worldY)) parent.attach(sprite) val time = 3f.random() + 3f val target = immutableVec2(director.screen.width.random(), director.screen.height.random()) val actions = sprite.actions() actions.runCircle { parallel { sequential { scaleTo(ImmutableVec2f.zero, time / 2f, Interpolations.random()) scaleTo(3f.random().run { ImmutableVec2f.readonly(this, this) }, time / 2f, Interpolations.random()) } sequential { move(target, time / 2f, Interpolations.random()) move(target * -1f, time / 2f, Interpolations.random()) } rotate(1800f.random(), time, Interpolations.random()) } } } } class ClickToDetachComponent(parent: Module) : AbstractComponent(parent) { @OnEvent(TapGestureEvent::class) fun detach(event:TapGestureEvent) { parent.detach() event.handled() } }
Updated