Performance optimization: fast and smooth zoom (#8549)

Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
vegeta1k95 2023-02-02 19:29:05 +01:00 committed by GitHub
parent 38f544a35d
commit 9a35f4a283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 110 additions and 5 deletions

View File

@ -0,0 +1,80 @@
package com.unciv.ui.utils
import com.badlogic.gdx.input.GestureDetector
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.Event
import com.badlogic.gdx.scenes.scene2d.EventListener
import com.badlogic.gdx.scenes.scene2d.InputEvent
open class ZoomGestureListener(
halfTapSquareSize: Float, tapCountInterval: Float, longPressDuration: Float, maxFlingDelay: Float
) : EventListener {
val detector: GestureDetector
var event: InputEvent? = null
constructor() : this(20f, 0.4f, 1.1f, Int.MAX_VALUE.toFloat())
init {
detector = GestureDetector(
halfTapSquareSize,
tapCountInterval,
longPressDuration,
maxFlingDelay,
object : GestureDetector.GestureAdapter() {
override fun zoom(initialDistance: Float, distance: Float): Boolean {
this@ZoomGestureListener.zoom(event, initialDistance, distance)
return true
}
override fun pinch(
stageInitialPointer1: Vector2,
stageInitialPointer2: Vector2,
stagePointer1: Vector2,
stagePointer2: Vector2
): Boolean {
this@ZoomGestureListener.pinch()
return true
}
override fun pinchStop() {
this@ZoomGestureListener.pinchStop()
}
})
}
override fun handle(event: Event?): Boolean {
if (event !is InputEvent)
return false
when (event.type) {
InputEvent.Type.touchDown -> {
detector.touchDown(event.stageX, event.stageY, event.pointer, event.button)
if (event.touchFocus) event.stage.addTouchFocus(
this, event.listenerActor, event.target,
event.pointer, event.button
)
return true
}
InputEvent.Type.touchUp -> {
if (event.isTouchFocusCancel) {
detector.reset()
return false
}
this.event = event
detector.touchUp(event.stageX, event.stageY, event.pointer, event.button)
return true
}
InputEvent.Type.touchDragged -> {
this.event = event
detector.touchDragged(event.stageX, event.stageY, event.pointer)
return true
}
else -> return false
}
}
open fun zoom(event: InputEvent?, initialDistance: Float, distance: Float) {}
open fun pinch() {}
open fun pinchStop() {}
}

View File

@ -29,6 +29,8 @@ open class ZoomableScrollPane(
var onViewportChangedListener: ((width: Float, height: Float, viewport: Rectangle) -> Unit)? = null
var onPanStopListener: (() -> Unit)? = null
var onPanStartListener: (() -> Unit)? = null
var onZoomStopListener: (() -> Unit)? = null
var onZoomStartListener: (() -> Unit)? = null
private val horizontalPadding get() = width / 2
private val verticalPadding get() = height / 2
@ -154,9 +156,23 @@ open class ZoomableScrollPane(
}
}
class ZoomListener(private val zoomableScrollPane: ZoomableScrollPane):ActorGestureListener(){
class ZoomListener(private val zoomableScrollPane: ZoomableScrollPane): ZoomGestureListener(){
private var isZooming = false
private var lastInitialDistance = 0f
var lastScale = 1f
var lastInitialDistance = 0f
override fun pinch() {
if (!isZooming) {
isZooming = true
zoomableScrollPane.onZoomStartListener?.invoke()
}
}
override fun pinchStop() {
isZooming = false
zoomableScrollPane.onZoomStopListener?.invoke()
}
override fun zoom(event: InputEvent?, initialDistance: Float, distance: Float) {
if (lastInitialDistance != initialDistance) {

View File

@ -76,24 +76,33 @@ class WorldMapHolder(
}
/**
* When scrolling the world map, there are two unnecessary (at least currently) things happening that take a decent amount of time:
* When scrolling or zooming the world map, there are two unnecessary (at least currently) things happening that take a decent amount of time:
*
* 1. Checking which [Actor]'s bounds the pointer (mouse/finger) entered+exited and sending appropriate events to these actors
* 2. Running all [Actor.act] methods of all child [Actor]s
* 3. Running all [Actor.hit] methode of all chikld [Actor]s
*
* Disabling them while panning increases the frame rate while panning by approximately 100%.
*/
private fun disablePointerEventsAndActionsOnPan() {
onPanStartListener = {
Log.debug("Disable pointer enter/exit events & TileGroupMap.act()")
(stage as UncivStage).performPointerEnterExitEvents = false
tileGroupMap.shouldAct = false
}
onPanStopListener = {
Log.debug("Enable pointer enter/exit events & TileGroupMap.act()")
(stage as UncivStage).performPointerEnterExitEvents = true
tileGroupMap.shouldAct = true
}
onZoomStartListener = {
(stage as UncivStage).performPointerEnterExitEvents = false
tileGroupMap.shouldAct = false
tileGroupMap.touchable = Touchable.disabled
}
onZoomStopListener = {
(stage as UncivStage).performPointerEnterExitEvents = true
tileGroupMap.shouldAct = true
tileGroupMap.touchable = Touchable.enabled
}
}
// Interface for classes that contain the data required to draw a button