Improved city button (#771)

* improved CityButton

* keep city button out of the way if a unit on the city tile is selected

* fixed city button to be clickable always. however this hides units behind it

* added click area for city buttons in an own layer, so the units render in front and the city button click area handles the clicks

* city button: simplified code
This commit is contained in:
sulai 2019-05-18 22:09:02 +02:00 committed by Yair Morgenstern
parent 054b3b000d
commit 73e397c2c2
4 changed files with 75 additions and 55 deletions

View File

@ -2,9 +2,10 @@ package com.unciv.ui.tilegroups
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Interpolation
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.scenes.scene2d.ui.Skin
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
@ -24,9 +25,8 @@ class CityButton(val city: CityInfo, internal val tileGroup: WorldTileGroup, ski
touchable= Touchable.disabled
}
var offset: Float = 0f;
var isButtonMoved = false
var isLabelClicked = false
var buttonDownClickArea: Actor? = null
fun isButtonMoved() = buttonDownClickArea != null
fun update(isCityViewable:Boolean) {
val cityButtonText = city.population.population.toString() + " | " + city.name
@ -36,61 +36,37 @@ class CityButton(val city: CityInfo, internal val tileGroup: WorldTileGroup, ski
label.setFontColor(city.civInfo.getNation().getSecondaryColor())
clear()
val unitTable = tileGroup.worldScreen.bottomBar.unitTable
if (UnCivGame.Current.viewEntireMapForDebug || city.civInfo.isCurrentPlayer()) {
// So you can click anywhere on the button to go to the city
touchable = Touchable.enabled
label.touchable = Touchable.enabled
label.onClick {
isLabelClicked = true
// clicking on the label swings that label a little down to allow selection of units there.
// second tap on the label will go to the city screen
if (tileGroup.selectCity(city)) {
val floatAction = object : FloatAction(0f, 1f, 0.4f) {
override fun update(percent: Float) {
offset = -height*percent
update(isCityViewable)
}
override fun end() {
isButtonMoved=true
}
}
floatAction.interpolation = Interpolation.swingOut
tileGroup.addAction(floatAction)
}
else {
UnCivGame.Current.screen = CityScreen(city)
}
}
// clicking anywhere else on the button opens the city screen immediately
onClick {
// we need to check if the label was just clicked, as onClick will propagate
// the click event to its touchable parent.
if(!isLabelClicked)
UnCivGame.Current.screen = CityScreen(city)
isLabelClicked=false
onClickEvent { _, x, y ->
if (!isButtonMoved()) {
if (hit(x, y, true) == label) {
// clicking on the label swings that label a little down to allow selection of units there.
// this also allows to target selected units to move to the city tile from elsewhere.
// second tap on the label will go to the city screen
moveButtonDown()
if (unitTable.selectedUnit == null || unitTable.selectedUnit!!.currentMovement==0f)
tileGroup.selectCity(city)
} else {
UnCivGame.Current.screen = CityScreen(city)
}
}
}
}
// when deselected, move city button to its original position
val unitTable = tileGroup.worldScreen.bottomBar.unitTable
if (isButtonMoved
&& unitTable.selectedCity == null
if (isButtonMoved()
&& unitTable.selectedCity != city
&& unitTable.selectedUnit?.currentTile != city.ccenterTile) {
isButtonMoved = false
val floatAction = object : FloatAction(0f, 1f, 0.4f) {
override fun update(percent: Float) {
offset = -height*(1-percent)
update(isCityViewable)
}
}
floatAction.interpolation = Interpolation.sine
tileGroup.addAction(floatAction)
moveButtonUp()
}
if (isCityViewable && city.health < city.getMaxHealth().toFloat()) {
@ -129,12 +105,47 @@ class CityButton(val city: CityInfo, internal val tileGroup: WorldTileGroup, ski
add(iconTable).row()
pack()
setOrigin(Align.center)
center(tileGroup)
y += offset // for animated shifting of City button
centerX(tileGroup)
touchable = Touchable.enabled
updateClickArea()
}
private fun moveButtonDown() {
val floatAction = Actions.sequence(
Actions.moveBy(0f, -height, 0.4f, Interpolation.swingOut),
Actions.run {
buttonDownClickArea = Actor().onClick {
UnCivGame.Current.screen = CityScreen(city)
}
tileGroup.cityButtonLayerGroup.addActor(buttonDownClickArea)
updateClickArea()
}
)
tileGroup.addAction(floatAction)
}
private fun moveButtonUp() {
val floatAction = Actions.sequence(
Actions.moveBy(0f, height, 0.4f, Interpolation.sine),
Actions.run {
buttonDownClickArea?.remove()
buttonDownClickArea = null
}
)
tileGroup.addAction(floatAction)
}
private fun updateClickArea() {
buttonDownClickArea?.let { clickArea ->
clickArea.setSize(width, height)
clickArea.setScale(scaleX, scaleY)
clickArea.setOrigin(Align.center)
clickArea.centerX(tileGroup.cityButtonLayerGroup)
clickArea.y = y-height
clickArea.touchable = Touchable.enabled
}
}
private fun getConstructionGroup(cityConstructions: CityConstructions): Group {
val group= Group()

View File

@ -55,6 +55,8 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
protected var civilianUnitImage: UnitGroup? = null
protected var militaryUnitImage: UnitGroup? = null
val cityButtonLayerGroup = Group().apply { isTransform=true; setSize(groupSize,groupSize);touchable=Touchable.childrenOnly }
val circleCrosshairFogLayerGroup = Group().apply { isTransform=false; setSize(groupSize,groupSize) }
private val circleImage = ImageGetter.getCircle() // for blue and red circles on the tile
private val crosshairImage = ImageGetter.getImage("OtherIcons/Crosshair.png") // for when a unit is targete
@ -76,6 +78,7 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
this.addActor(featureLayerGroup)
this.addActor(miscLayerGroup)
this.addActor(unitLayerGroup)
this.addActor(cityButtonLayerGroup)
this.addActor(circleCrosshairFogLayerGroup)
updateTileImage(false)

View File

@ -122,20 +122,23 @@ fun Label.setFontSize(size:Int): Label {
}
// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries
fun Actor.onClick(sound:String,function: () -> Unit){
/** same as [onClick], but sends the [InputEvent] and coordinates along */
fun Actor.onClickEvent(sound: String = "click", function: (event: InputEvent?, x: Float, y: Float) -> Unit) {
this.addListener(object : ClickListener() {
override fun clicked(event: InputEvent?, x: Float, y: Float) {
if(sound!="") Sounds.play(sound)
function()
if (sound != "") Sounds.play(sound)
function(event, x, y)
}
} )
})
}
// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries
fun Actor.onClick(sound: String = "click", function: () -> Unit) {
onClickEvent(sound) { _, _, _ -> function() }
}
fun Actor.onClick(function: () -> Unit): Actor {
onClick("click",function)
onClick("click", function)
return this
}

View File

@ -31,6 +31,7 @@ class TileGroupMap<T: TileGroup>(val tileGroups:Collection<T>, padding:Float): G
val featureLayers = ArrayList<Group>()
val miscLayers = ArrayList<Group>()
val unitLayers = ArrayList<Group>()
val cityButtonLayers = ArrayList<Group>()
val circleCrosshairFogLayers = ArrayList<Group>()
for(group in tileGroups.sortedByDescending { it.tileInfo.position.x + it.tileInfo.position.y }){
@ -39,6 +40,7 @@ class TileGroupMap<T: TileGroup>(val tileGroups:Collection<T>, padding:Float): G
featureLayers.add(group.featureLayerGroup.apply { setPosition(group.x,group.y) })
miscLayers.add(group.miscLayerGroup.apply { setPosition(group.x,group.y) })
unitLayers.add(group.unitLayerGroup.apply { setPosition(group.x,group.y) })
cityButtonLayers.add(group.cityButtonLayerGroup.apply { setPosition(group.x,group.y) })
circleCrosshairFogLayers.add(group.circleCrosshairFogLayerGroup.apply { setPosition(group.x,group.y) })
}
for(group in baseLayers) addActor(group)
@ -46,6 +48,7 @@ class TileGroupMap<T: TileGroup>(val tileGroups:Collection<T>, padding:Float): G
for(group in miscLayers) addActor(group)
for(group in circleCrosshairFogLayers) addActor(group)
for(group in tileGroups) addActor(group) // The above layers are for the visual layers, this is for the clickability
for(group in cityButtonLayers) addActor(group) // city buttons clickability
for(group in unitLayers) addActor(group) // Aaand units above everything else.