UNIT MOVEMENT ANIMATION

LET'S FUKKEN GO
This commit is contained in:
yairm210 2024-07-09 23:57:45 +03:00
parent 7c1e0c0d25
commit f021fb692d
4 changed files with 65 additions and 21 deletions

View File

@ -11,7 +11,7 @@ import com.unciv.ui.components.tilegroups.layers.TileLayerFeatures
import com.unciv.ui.components.tilegroups.layers.TileLayerMisc
import com.unciv.ui.components.tilegroups.layers.TileLayerOverlay
import com.unciv.ui.components.tilegroups.layers.TileLayerTerrain
import com.unciv.ui.components.tilegroups.layers.TileLayerUnitArt
import com.unciv.ui.components.tilegroups.layers.TileLayerUnitSprite
import com.unciv.ui.components.tilegroups.layers.TileLayerUnitFlag
import com.unciv.utils.DebugUtils
import kotlin.math.pow
@ -49,7 +49,7 @@ open class TileGroup(
@Suppress("LeakingThis") val layerBorders = TileLayerBorders(this, groupSize)
@Suppress("LeakingThis") val layerMisc = TileLayerMisc(this, groupSize)
@Suppress("LeakingThis") val layerOverlay = TileLayerOverlay(this, groupSize)
@Suppress("LeakingThis") val layerUnitArt = TileLayerUnitArt(this, groupSize)
@Suppress("LeakingThis") val layerUnitArt = TileLayerUnitSprite(this, groupSize)
@Suppress("LeakingThis") val layerUnitFlag = TileLayerUnitFlag(this, groupSize)
@Suppress("LeakingThis") val layerCityButton = TileLayerCityButton(this, groupSize)

View File

@ -13,7 +13,7 @@ import com.unciv.ui.components.tilegroups.layers.TileLayerFeatures
import com.unciv.ui.components.tilegroups.layers.TileLayerMisc
import com.unciv.ui.components.tilegroups.layers.TileLayerOverlay
import com.unciv.ui.components.tilegroups.layers.TileLayerTerrain
import com.unciv.ui.components.tilegroups.layers.TileLayerUnitArt
import com.unciv.ui.components.tilegroups.layers.TileLayerUnitSprite
import com.unciv.ui.components.tilegroups.layers.TileLayerUnitFlag
import com.unciv.ui.components.widgets.ZoomableScrollPane
import kotlin.math.max
@ -106,7 +106,7 @@ class TileGroupMap<T: TileGroup>(
val featureLayers = ArrayList<TileLayerFeatures>()
val borderLayers = ArrayList<TileLayerBorders>()
val miscLayers = ArrayList<TileLayerMisc>()
val pixelUnitLayers = ArrayList<TileLayerUnitArt>()
val pixelUnitLayers = ArrayList<TileLayerUnitSprite>()
val circleFogCrosshairLayers = ArrayList<TileLayerOverlay>()
val unitLayers = ArrayList<TileLayerUnitFlag>()
val cityButtonLayers = ArrayList<TileLayerCityButton>()

View File

@ -9,28 +9,30 @@ import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.tilegroups.TileGroup
private class UnitArtSlot : Group() {
class UnitSpriteSlot : Group() {
var imageLocation = ""
}
class TileLayerUnitArt(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, size) {
class TileLayerUnitSprite(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, size) {
override fun act(delta: Float) {}
override fun hit(x: Float, y: Float, touchable: Boolean): Actor? = null
private var civilianSlot: UnitArtSlot = UnitArtSlot()
private var militarySlot: UnitArtSlot = UnitArtSlot()
private var civilianSlot: UnitSpriteSlot = UnitSpriteSlot()
private var militarySlot: UnitSpriteSlot = UnitSpriteSlot()
init {
addActor(civilianSlot)
addActor(militarySlot)
}
fun getSpriteSlot(unit:MapUnit) = if (unit.isCivilian()) civilianSlot else militarySlot
private fun showMilitaryUnit(viewingCiv: Civilization) = tileGroup.isForceVisible
|| viewingCiv.viewableInvisibleUnitsTiles.contains(tileGroup.tile)
|| !tileGroup.tile.hasEnemyInvisibleUnit(viewingCiv)
private fun updateSlot(slot: UnitArtSlot, unit: MapUnit?, isShown: Boolean) {
private fun updateSlot(slot: UnitSpriteSlot, unit: MapUnit?, isShown: Boolean) {
var location = ""
var nationName = ""

View File

@ -53,7 +53,7 @@ import com.unciv.ui.components.tilegroups.TileGroup
import com.unciv.ui.components.tilegroups.TileGroupMap
import com.unciv.ui.components.tilegroups.TileSetStrings
import com.unciv.ui.components.tilegroups.WorldTileGroup
import com.unciv.ui.components.widgets.UnitGroup
import com.unciv.ui.components.widgets.UnitIconGroup
import com.unciv.ui.components.widgets.ZoomableScrollPane
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.basescreen.BaseScreen
@ -299,8 +299,10 @@ class WorldMapHolder(
// Since this runs in a different thread, even if we check movement.canReach()
// then it might change until we get to the getTileToMoveTo, so we just try/catch it
val tileToMoveTo: Tile
val pathToTile: List<Tile>
try {
tileToMoveTo = selectedUnit.movement.getTileToMoveToThisTurn(targetTile)
pathToTile = selectedUnit.movement.getDistanceToTiles().getPathToTile(targetTile)
} catch (ex: Exception) {
when (ex) {
is UnitMovement.UnreachableDestinationException -> {
@ -308,9 +310,7 @@ class WorldMapHolder(
// Or telling a ship to run onto a coastal land tile.
// Do nothing
}
else -> {
Log.error("Exception in getTileToMoveToThisTurn", ex)
}
else -> Log.error("Exception in getTileToMoveToThisTurn", ex)
}
return@run // can't move here
}
@ -325,6 +325,7 @@ class WorldMapHolder(
// but until it reaches the headTowards the board has changed and so the headTowards fails.
// I can't think of any way to avoid this,
// but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch
val previousTile = selectedUnit.currentTile
selectedUnit.movement.moveToTile(tileToMoveTo)
if (selectedUnit.isExploring() || selectedUnit.isMoving())
selectedUnit.action = null // remove explore on manual move
@ -335,6 +336,9 @@ class WorldMapHolder(
if (selectedUnit.currentMovement > 0) worldScreen.bottomUnitTable.selectUnit(selectedUnit)
worldScreen.shouldUpdate = true
animateMovement(previousTile, selectedUnit, targetTile, pathToTile)
if (selectedUnits.size > 1) { // We have more tiles to move
moveUnitToTargetTile(selectedUnits.subList(1, selectedUnits.size), targetTile)
} else removeUnitActionOverlay() //we're done here
@ -349,6 +353,44 @@ class WorldMapHolder(
}
}
private fun animateMovement(
previousTile: Tile,
selectedUnit: MapUnit,
targetTile: Tile,
pathToTile: List<Tile>
) {
val tileGroup = tileGroups[previousTile]!!
// Steal the current sprites to our new group
val unitSpriteAndIcon = Group().apply { setPosition(tileGroup.x, tileGroup.y) }
val unitSpriteSlot = tileGroup.layerUnitArt.getSpriteSlot(selectedUnit)
for (spriteImage in unitSpriteSlot.children) unitSpriteAndIcon.addActor(spriteImage)
tileGroup.parent.addActor(unitSpriteAndIcon)
// Disable the final tile, so we won't have one image "merging into" the other
val targetTileSpriteSlot = tileGroups[targetTile]!!.layerUnitArt.getSpriteSlot(selectedUnit)
targetTileSpriteSlot.isVisible = false
unitSpriteAndIcon.addAction(
Actions.sequence(
*pathToTile.map { tile ->
Actions.moveTo(
tileGroups[tile]!!.x,
tileGroups[tile]!!.y,
0.5f / pathToTile.size
)
}.toTypedArray(),
Actions.run {
// Re-enable the final tile
targetTileSpriteSlot.isVisible = true
worldScreen.shouldUpdate = true
},
Actions.removeActor(),
)
)
}
private fun swapMoveUnitToTargetTile(selectedUnit: MapUnit, targetTile: Tile) {
markUnitMoveTutorialComplete(selectedUnit)
selectedUnit.movement.swapMoveToTile(targetTile)
@ -505,9 +547,9 @@ class WorldMapHolder(
}
for (unit in unitList) {
val unitGroup = UnitGroup(unit, 48f).surroundWithCircle(68f, resizeActor = false)
unitGroup.circle.color = Color.GRAY.cpy().apply { a = 0.5f }
if (unit.currentMovement == 0f) unitGroup.color.a = 0.66f
val unitIconGroup = UnitIconGroup(unit, 48f).surroundWithCircle(68f, resizeActor = false)
unitIconGroup.circle.color = Color.GRAY.cpy().apply { a = 0.5f }
if (unit.currentMovement == 0f) unitIconGroup.color.a = 0.66f
val clickableCircle = ClickableCircle(68f)
clickableCircle.touchable = Touchable.enabled
clickableCircle.onClick {
@ -515,8 +557,8 @@ class WorldMapHolder(
worldScreen.shouldUpdate = true
removeUnitActionOverlay()
}
unitGroup.addActor(clickableCircle)
table.add(unitGroup)
unitIconGroup.addActor(clickableCircle)
table.add(unitIconGroup)
}
addOverlayOnTileGroup(tileGroups[tile]!!, table)
@ -546,7 +588,7 @@ class WorldMapHolder(
}
val firstUnit = dto.unitToTurnsToDestination.keys.first()
val unitIcon = if (dto.unitToTurnsToDestination.size == 1) UnitGroup(firstUnit, smallerCircleSizes)
val unitIcon = if (dto.unitToTurnsToDestination.size == 1) UnitIconGroup(firstUnit, smallerCircleSizes)
else dto.unitToTurnsToDestination.size.toString().toLabel(fontColor = firstUnit.civ.nation.getInnerColor()).apply { setAlignment(Align.center) }
.surroundWithCircle(smallerCircleSizes).apply { circle.color = firstUnit.civ.nation.getOuterColor() }
unitIcon.y = buttonSize - unitIcon.height
@ -575,7 +617,7 @@ class WorldMapHolder(
}
)
val unitIcon = UnitGroup(dto.unit, smallerCircleSizes)
val unitIcon = UnitIconGroup(dto.unit, smallerCircleSizes)
unitIcon.y = buttonSize - unitIcon.height
swapWithButton.addActor(unitIcon)
@ -595,7 +637,7 @@ class WorldMapHolder(
}
)
val unitIcon = UnitGroup(dto.unit, smallerCircleSizes)
val unitIcon = UnitIconGroup(dto.unit, smallerCircleSizes)
unitIcon.y = buttonSize - unitIcon.height
connectRoadButton.addActor(unitIcon)