Files
Terrarum/src/net/torvald/spriteanimation/SpriteAnimation.kt

233 lines
7.1 KiB
Kotlin

package net.torvald.spriteanimation
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath
import net.torvald.terrarum.Second
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* This class should not be serialised; save its Animation Description Language instead.
*
* Created by minjaesong on 2022-03-23.
*/
abstract class SpriteAnimation(@Transient val parentActor: ActorWithBody) : Disposable {
protected abstract val currentDelay: Second
abstract fun update(delta: Float)
abstract fun render(frameDelta: Float, batch: SpriteBatch, posX: Float, posY: Float, scale: Float = 1f, mode: Int = 0, forcedColourFilter: Color? = null)
var flipHorizontal = false
var flipVertical = false
var colourFilter = Color(-1)
open fun flip(horizontal: Boolean, vertical: Boolean) {
flipHorizontal = horizontal
flipVertical = vertical
}
}
/* Original code author: Sean Laurvick
* This code is based on the original author's code written in Lua.
*/
/**
* This class should not be serialised; save its Animation Description Language instead.
*
* Maximum rows: 64
*
* Maximum frames: unlimited
*/
class SheetSpriteAnimation(parentActor: ActorWithBody) : SpriteAnimation(parentActor) {
internal lateinit var textureRegion: TextureRegionPack; private set
var currentFrame = 0
var currentRow = 0
var nFrames: IntArray = intArrayOf(1)
internal set
var nRows: Int = 1
internal set
override val currentDelay: Second
get() = delays[currentRow].coerceAtLeast(1f / 16f) // animation delay cannot be too short
/**
* Sets delays for each rows. Array size must be the same as the rows of the sheet
*/
var delays: FloatArray = FloatArray(64) { 0.2f }
set(value) {
if (value.filter { it <= 0f }.isNotEmpty()) {
throw IllegalArgumentException("Delay array contains zero or negative value: $delays")
}
field = value
}
private var delta = 0f
var looping = true
private var animationRunning = true
private val visible: Boolean
get() = parentActor.isVisible
private val offsetX = 0
private val offsetY = 0
var cellWidth: Int = 0
var cellHeight: Int = 0
fun setSpriteImage(regionPack: TextureRegionPack) {
textureRegion = regionPack
cellWidth = regionPack.tileW
cellHeight = regionPack.tileH
}
/**
* Sets sheet rows and animation frames. Will default to
* 1, 1 (still image of top left from the sheet) if not called.
* @param nRows
* *
* @param nFrames
*/
fun setRowsAndFrames(nRows: Int, nFrames: Int) {
this.nRows = nRows
this.nFrames = IntArray(nRows) { nFrames }
}
fun setFramesOf(row: Int, frameCount: Int) {
nFrames[row] = frameCount
}
fun setFramesCount(framesCount: IntArray) {
nFrames = framesCount
}
override fun update(delta: Float) {
if (animationRunning) {
//skip this if animation is stopped
this.delta += delta
//println("delta accumulation: $delta, currentDelay: $currentDelay")
//check if it's time to advance the frame
while (this.delta >= currentDelay) {
// advance frame
if (looping) { // looping, wrap around
currentFrame = (currentFrame + 1) % nFrames[currentRow]
}
else if (currentFrame < nFrames[currentRow] - 1) { // not looping and haven't reached the end
currentFrame += 1
}
// discount counter
this.delta -= currentDelay
}
//println("row, frame: $currentRow, $currentFrame")
}
}
/**
* Render to specific coordinates. Will assume bottom-center point as image position.
* Will floor to integer.
* @param g
* *
* @param posX bottom-center point
* *
* @param posY bottom-center point
* *
* @param scale
*/
override fun render(frameDelta: Float, batch: SpriteBatch, posX: Float, posY: Float, scale: Float, mode: Int, forcedColourFilter: Color?) {
assert(cellWidth > 0 || cellHeight > 0) {
"Sprite width or height is set to zero! ($cellWidth, $cellHeight); master: $parentActor"
}
if (visible) {
val region = textureRegion.get(currentFrame, currentRow)
batch.color = forcedColourFilter ?: colourFilter
val tx = (parentActor.hitboxTranslateX) * scale
val txF = (parentActor.hitboxTranslateX + parentActor.baseHitboxW) * scale
val ty = (parentActor.hitboxTranslateY + (cellHeight - parentActor.baseHitboxH)) * scale
val tyF = (parentActor.hitboxTranslateY + parentActor.baseHitboxH) * scale
if (flipHorizontal && flipVertical) {
batch.draw(region,
FastMath.floor(posX).toFloat() + txF,
FastMath.floor(posY).toFloat() + tyF,
-FastMath.floor(cellWidth * scale).toFloat(),
-FastMath.floor(cellHeight * scale).toFloat()
)
}
else if (flipHorizontal && !flipVertical) {
batch.draw(region,
FastMath.floor(posX).toFloat() + txF,
FastMath.floor(posY).toFloat() - ty,
-FastMath.floor(cellWidth * scale).toFloat(),
FastMath.floor(cellHeight * scale).toFloat()
)
}
else if (!flipHorizontal && flipVertical) {
batch.draw(region,
FastMath.floor(posX).toFloat() - tx,
FastMath.floor(posY).toFloat() + tyF,
FastMath.floor(cellWidth * scale).toFloat(),
-FastMath.floor(cellHeight * scale).toFloat()
)
}
else {
batch.draw(region,
FastMath.floor(posX).toFloat() - tx,
FastMath.floor(posY).toFloat() - ty,
FastMath.floor(cellWidth * scale).toFloat(),
FastMath.floor(cellHeight * scale).toFloat()
)
}
}
}
fun switchRow(newRow: Int) {
if (newRow != currentRow) {
currentRow = newRow
currentFrame = 1
}
}
fun reset() {
currentFrame = 1
}
fun start() {
//starts the animation
animationRunning = true
}
fun start(selectFrame: Int) {
//starts the animation
animationRunning = true
//optional: seleft the frame no which to start the animation
currentFrame = selectFrame
}
fun stop() {
animationRunning = false
}
fun stop(selectFrame: Int) {
animationRunning = false
currentFrame = selectFrame
}
override fun dispose() {
textureRegion.dispose()
}
}