while (true) + delay
One way of animation in KorGE is to just make a loop and place a delay. This method allows you to define complex logic inside the loop and define state machines just by code. (Have in mind that this approach is likely to have some kind of stuttering.)
launchImmediately {
while (true) {
view.x++
delay(16.milliseconds) // suspending
}
}
addFixedUpdater and addHrUpdater
One way of performing animations with KorGE is by attaching an updater component to a view. While the view is visible that updater will be executed from time to time.
If you want a code to be executed a number of times per second, you can use addFixedUpdater. (Have in mind that this approach is likely to have some kind of stuttering.)
view.addFixedUpdater(60.timesPerSecond) {
x++
}
And if your code is designed to support arbitrary time deltas, you can use an updater:
view.addUpdater { dt -> // dt contains the delta time using as a TimeSpan inline class instance
val scale = dt / 16.66666.milliseconds
x += 2.0 * scale
}
Tweens
Korge integrates tweens and easings, and it is fully integrated with coroutines for your coding pleasure.
Games require tweening visual properties in order to be appealing. Korge provides a simple, yet powerful interface for creating tweens.
Simple interface
View
has an extension method called View.tween
that allows you to do the magic. And has the following definition:
suspend fun View?.tween(vararg vs: V2<*>, time: Int, easing: Easing = Easing.LINEAR, callback: (Double) -> Unit = { })
You have to use bound callable references to define properties that will change. And Korge provides some extension methods to bound callable references to generate tween parameters.
If you want to linearly interpolate view.x
from 10.0
to 100.0
in one second you would write:
view.tween(view::x[10.0, 100.0], time = 1000.milliseconds)
Or interpolating to a specific position using the current x as the starting value:
view.tween(view::x[100.0], time = 1000.milliseconds)
Tip: import
com.soywiz.korge.tween.get
for this to compile. In IntelliJ place the caret in the [ and press ALT+enter.
delay + duration + easing
You can control the start time, duration and easing per interpolated property by chaining them in the parameter call, using these V2 extensions:
V2.delay(timeMs:TimeSpan):V2
, V2.duration(timeMs:TimeSpan):V2
, V2.easing(easing:Easing):V2
view.tween(
view::x[100.0].delay(100.milliseconds).duration(500.milliseconds).easing(Easing.EASE_IN_OUT_QUAD),
view::y[0.0, 200.0].delay(50.milliseconds),
time = 1000.milliseconds
)
If you want to apply one easing for all subtweens defined in the paramters, just add an extra easing parameter to the tween
call:
view.tween(
//...
easing = Easing.EASE_IN
)
Implementation details
The tween execution will be attached as a component to the receiver View that holds the tween method. That means that the view has to be in the stage or be manually updated. Also means that any View.speed
changes in that view or ancestors will affect the tween.
PRO Tip: You can even interpolate the View.speed
property to get some cool time effects.
Animator
You can also use an animator, which is almost as flexible as the tweens. Check the animate sample here
An animator is an object attached to a view that supports adding sequence
and parallel
tweens and actions.
You can create an animator attached to a view with:
val myAnimator = animator()
Or with a predefined set of actions:
val myAnimator = animator {
parallel {
moveToWithSpeed(view, 500.0, 500.0, 300.0, Easing.EASE_IN_OUT)
scaleTo(view, 5.0, 5.0)
}
parallel {
moveTo(view, 0.0, 0.0)
}
block {
rotateTo(view, 10.degrees)
}
}
There is also a lazy singleton for views equivalent to view.animator(parallel = true)
called view.simpleAnimator
:
view.simpleAnimator
Animators can be updated, by adding new elements, and depending of whether it is parallel
or sequence
, it will be executed right away, or enqueued to be executed at the end of the animation.
val view = solidRect(100, 100, Colors.BLUE)
uiButton("Hello").clicked {
view.simpleAnimator.moveBy(view, x = +100.0)
}
Completing an existing animation
In some cases we want to finish the existing animation right away, instead of enqueuing or running an animation in parallel. To do so we have two methods: cancel()
and complete()
.
cancel()
, cancels the animation not running the rest of the elements and keeping the properties as they are right now.complete()
, executes all the pending Cancelling the animation setting the properties to its final value and executing all the blocks
So for example:
If we execute this code every second, the final x
position would not be multiple of 100 without jumps in the X:
sequenceAnimator.cancel().moveBy(view, x = +100.0, time = 5.seconds)
On the other hand, if we execute this code every second, the final x
position will be multiple of 100 and there will be a jump in the X:
sequenceAnimator.complete().moveBy(view, x = +100.0, time = 5.seconds)
If we don’t call either the cancel or complete, and just the moveBy, if the animation is sequential, the operation would be queued:
repeat(5) {
sequenceAnimator.moveBy(view, x = +100.0, time = 1.seconds)
}
If the animation is parallel, the animation will be executed in parallel immediately without stopping other animations, unless the same properties are being updated in which case the latest one will likely prevail, though might have issues.
Animator operations
Animator provides several operations you can perform in each step:
Tweens: animator.tween(view::x[0, 100], time = 1.seconds)
Executing synchronous blocks: animator.block { println("reached this point") }
Waiting: animator.wait(2.seconds)
Removing a view from its parent: animator.removeFromParent(view)
Attaching new sequence or parallel nodes: animator.sequence { ... }
and animator.parallel { }
For example:
val animator1 = animator(parallel = false)
uiButton("Hello").clicked {
animator1.parallel { // try chaing parallel with sequence
moveBy(view1, x = +100, y = +100)
moveBy(view2, x = +100, y = +100)
}
}
Moving a view relatively or absolutely to its position:
animator.moveBy(view, x = +10, y = +20)
animator.moveTo(view, x = 100, y = 200)
Moving a view to a specific position specifying speed instead of time:
animator.moveToWithSpeed(view, x = 200, y = 200, speed = 100.0)
Moving a view following a path with a time or a speed:
val path = buildVectorPath {
line(Point(0, 0), Point(200 + 100, 200))
arcTo(Point(100, 100), Point(100, 300), 100.0)
}
// Displaying the path
graphics { stroke(Colors.WHITE) { path(path) } }
// Moving around the path
animator.moveInPath(view1, path, time = 2.seconds)
moveInPathWithSpeed(view1, path, speed = { 200.0 }, easing = Easing.LINEAR)
Rotating a view relatively or to a specified angle:
animator.rotateBy(+30.degrees)
animator.rotateTo(+30.degrees)
Adjusting the alpha of a view:
animator.alpha(view, 0.5)
animator.hide()
animator.show()
There are some lazy variants of those methods supporting lambdas instead of values, whose lambdas are executed when the animation is going to start instead of at enqueuing time.
It is also possible to provide time =
and easing =
parameters to most of the functions provided to specify the duration of the animation and the easing function to use for the properties.
Awaiting the animation to finish
You can call the animator.awaitComplete()
suspending function to wait the animation to be completed.
Or register a signal to be executed when the animation is completed:
animator1.onComplete.once { println("completed!") }
A complete Animator example
class MyScene : Scene() {
override suspend fun SContainer.sceneMain() {
val view1 = solidRect(100, 100, Colors.BLUE).xy(0, 0)
val view2 = solidRect(100, 100, Colors.RED).xy(100, 0)
val animator1 = animator(parallel = false)
uiHorizontalStack {
uiButton("Reset").clicked {
launchImmediately {
sceneContainer.changeTo { MyScene() }
} } uiButton("Hello").clicked {
animator1.sequence {
parallel {
alpha(view1, 0.5)
alpha(view2, 0.5)
}
parallel {
moveBy(view1, x = +100, y = +100)
moveBy(view2, x = +100, y = +100)
}
sequence {
alpha(view1, 1.0)
alpha(view2, 1.0)
}
} } } }
}
Easings
Korge provides an Easing class with the most common easings. And allows you to create your own easings.
Easings.EASE_IN_ELASTIC | Easings.EASE_OUT_ELASTIC | Easings.EASE_OUT_BOUNCE | Easings.LINEAR |
Easings.EASE_IN | Easings.EASE_OUT | Easings.EASE_IN_OUT | Easings.EASE_OUT_IN |
Easings.EASE_IN_BACK | Easings.EASE_OUT_BACK | Easings.EASE_IN_OUT_BACK | Easings.EASE_OUT_IN_BACK |
Easings.EASE_IN_OUT_ELASTIC | Easings.EASE_OUT_IN_ELASTIC | Easings.EASE_IN_BOUNCE | Easings.EASE_IN_OUT_BOUNCE |
Easings.EASE_OUT_IN_BOUNCE | Easings.EASE_IN_QUAD | Easings.EASE_OUT_QUAD | Easings.EASE_IN_OUT_QUAD |
ANI/SWF Files
KorGE supports SWF animation files by a kproject module available in the store. For more details check it here: https://docs.korge.org/store_proxy/?url=/module/korge-swf/#