Filter on screen buttons by controller type

This commit is contained in:
Willi Ye 2021-04-30 21:38:13 +02:00 committed by ◱ Mark
parent 2ff06d5421
commit ce60101989
4 changed files with 97 additions and 80 deletions

View File

@ -218,7 +218,10 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
// Hide on screen controls when first controller is not set
binding.onScreenControllerView.apply {
isGone = inputManager.controllers[0]!!.type == ControllerType.None || !settings.onScreenControl
inputManager.controllers[0]!!.type.let {
controllerType = it
isGone = it == ControllerType.None || !settings.onScreenControl
}
setOnButtonStateChangedListener(::onButtonStateChanged)
setOnStickStateChangedListener(::onStickStateChanged)
recenterSticks = settings.onScreenControlRecenterSticks
@ -414,14 +417,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
private fun onButtonStateChanged(buttonId : ButtonId, state : ButtonState) = setButtonState(0, buttonId.value(), state.state)
private fun onStickStateChanged(buttonId : ButtonId, position : PointF) {
val stickId = when (buttonId) {
ButtonId.LeftStick -> StickId.Left
ButtonId.RightStick -> StickId.Right
else -> error("Invalid button ID")
}
private fun onStickStateChanged(stickId : StickId, position : PointF) {
setAxisValue(0, stickId.xAxis.ordinal, (position.x * Short.MAX_VALUE).toInt())
setAxisValue(0, stickId.yAxis.ordinal, (-position.y * Short.MAX_VALUE).toInt()) // Y is inverted, since drawing starts from top left
}

View File

@ -17,22 +17,35 @@ import android.view.View
import android.view.View.OnTouchListener
import emu.skyline.input.ButtonId
import emu.skyline.input.ButtonState
import emu.skyline.input.ControllerType
import emu.skyline.input.StickId
import emu.skyline.utils.add
import emu.skyline.utils.multiply
import emu.skyline.utils.normalize
import kotlin.math.roundToLong
typealias OnButtonStateChangedListener = (buttonId : ButtonId, state : ButtonState) -> Unit
typealias OnStickStateChangedListener = (buttonId : ButtonId, position : PointF) -> Unit
typealias OnStickStateChangedListener = (stickId : StickId, position : PointF) -> Unit
/**
* Renders On-Screen Controls as a single view, handles touch inputs and button toggling
*/
class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = 0, defStyleRes : Int = 0) : View(context, attrs, defStyleAttr, defStyleRes) {
companion object {
private val controllerTypeMappings = mapOf(*ControllerType.values().map {
it to (setOf(*it.buttons) to setOf(*it.sticks))
}.toTypedArray())
}
private val controls = Controls(this)
private var onButtonStateChangedListener : OnButtonStateChangedListener? = null
private var onStickStateChangedListener : OnStickStateChangedListener? = null
private val joystickAnimators = mutableMapOf<JoystickButton, Animator?>()
var controllerType : ControllerType? = null
set(value) {
field = value
invalidate()
}
var recenterSticks = false
set(value) {
field = value
@ -42,11 +55,16 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
override fun onDraw(canvas : Canvas) {
super.onDraw(canvas)
controls.allButtons.forEach {
if (it.config.enabled) {
it.width = width
it.height = height
it.render(canvas)
val allowedIds = controllerTypeMappings[controllerType]
controls.allButtons.forEach { button ->
if (button.config.enabled
&& allowedIds?.let { (buttonIds, stickIds) ->
if (button is JoystickButton) stickIds.contains(button.stickId) else buttonIds.contains(button.buttonId)
} != false
) {
button.width = width
button.height = height
button.render(canvas)
}
}
}
@ -128,14 +146,14 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
val vector = direction.multiply(value)
val newPosition = position.add(vector)
joystick.onFingerMoved(newPosition.x, newPosition.y, false)
onStickStateChangedListener?.invoke(joystick.buttonId, vector.multiply(1f / radius))
onStickStateChangedListener?.invoke(joystick.stickId, vector.multiply(1f / radius))
invalidate()
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation : Animator?) {
super.onAnimationCancel(animation)
onAnimationEnd(animation)
onStickStateChangedListener?.invoke(joystick.buttonId, PointF(0f, 0f))
onStickStateChangedListener?.invoke(joystick.stickId, PointF(0f, 0f))
}
override fun onAnimationEnd(animation : Animator?) {
@ -163,7 +181,7 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
if (joystick.shortDoubleTapped)
onButtonStateChangedListener?.invoke(joystick.buttonId, ButtonState.Pressed)
if (recenterSticks)
onStickStateChangedListener?.invoke(joystick.buttonId, joystick.outerToInnerRelative())
onStickStateChangedListener?.invoke(joystick.stickId, joystick.outerToInnerRelative())
performClick()
handled = true
}
@ -173,7 +191,7 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
for (i in 0 until event.pointerCount) {
if (event.getPointerId(i) == joystick.touchPointerId) {
val centerToPoint = joystick.onFingerMoved(event.getX(i), event.getY(i))
onStickStateChangedListener?.invoke(joystick.buttonId, centerToPoint)
onStickStateChangedListener?.invoke(joystick.stickId, centerToPoint)
handled = true
}
}

View File

@ -12,25 +12,27 @@ import androidx.core.graphics.minus
import emu.skyline.R
import emu.skyline.input.ButtonId
import emu.skyline.input.ButtonId.*
import emu.skyline.input.StickId
import emu.skyline.input.StickId.*
import emu.skyline.utils.add
import emu.skyline.utils.multiply
import kotlin.math.roundToInt
open class CircularButton(
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeRadiusToX : Float,
drawableId : Int = R.drawable.ic_button
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeRadiusToX : Float,
drawableId : Int = R.drawable.ic_button
) : OnScreenButton(
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeRadiusToX * 2f,
defaultRelativeRadiusToX * CONFIGURED_ASPECT_RATIO * 2f,
drawableId
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeRadiusToX * 2f,
defaultRelativeRadiusToX * CONFIGURED_ASPECT_RATIO * 2f,
drawableId
) {
val radius get() = itemWidth / 2f
@ -49,18 +51,18 @@ open class CircularButton(
}
class JoystickButton(
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeRadiusToX : Float
onScreenControllerView : OnScreenControllerView,
val stickId : StickId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeRadiusToX : Float
) : CircularButton(
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeRadiusToX,
R.drawable.ic_button
onScreenControllerView,
stickId.button,
defaultRelativeX,
defaultRelativeY,
defaultRelativeRadiusToX,
R.drawable.ic_button
) {
private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, R.drawable.ic_stick)
@ -155,21 +157,21 @@ class JoystickButton(
}
open class RectangularButton(
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeWidth : Float,
defaultRelativeHeight : Float,
drawableId : Int = R.drawable.ic_rectangular_button
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeWidth : Float,
defaultRelativeHeight : Float,
drawableId : Int = R.drawable.ic_rectangular_button
) : OnScreenButton(
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeWidth,
defaultRelativeHeight,
drawableId
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeWidth,
defaultRelativeHeight,
drawableId
) {
override fun isTouched(x : Float, y : Float) = currentBounds.contains(x.roundToInt(), y.roundToInt())
@ -183,26 +185,26 @@ open class RectangularButton(
}
class TriggerButton(
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeWidth : Float,
defaultRelativeHeight : Float
onScreenControllerView : OnScreenControllerView,
buttonId : ButtonId,
defaultRelativeX : Float,
defaultRelativeY : Float,
defaultRelativeWidth : Float,
defaultRelativeHeight : Float
) : RectangularButton(
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeWidth,
defaultRelativeHeight,
when (buttonId) {
ZL -> R.drawable.ic_trigger_button_left
onScreenControllerView,
buttonId,
defaultRelativeX,
defaultRelativeY,
defaultRelativeWidth,
defaultRelativeHeight,
when (buttonId) {
ZL -> R.drawable.ic_trigger_button_left
ZR -> R.drawable.ic_trigger_button_right
ZR -> R.drawable.ic_trigger_button_right
else -> error("Unsupported trigger button")
}
else -> error("Unsupported trigger button")
}
)
class Controls(onScreenControllerView : OnScreenControllerView) {
@ -229,14 +231,14 @@ class Controls(onScreenControllerView : OnScreenControllerView) {
val buttonPairs = circularButtonPairs + triggerButtonPairs
val circularButtons = circularButtonPairs.flatten() + listOf(
CircularButton(onScreenControllerView, Plus, 0.57f, 0.75f, 0.025f),
CircularButton(onScreenControllerView, Minus, 0.43f, 0.75f, 0.025f),
CircularButton(onScreenControllerView, Menu, 0.5f, 0.75f, 0.025f)
CircularButton(onScreenControllerView, Plus, 0.57f, 0.75f, 0.025f),
CircularButton(onScreenControllerView, Minus, 0.43f, 0.75f, 0.025f),
CircularButton(onScreenControllerView, Menu, 0.5f, 0.75f, 0.025f)
)
val joysticks = listOf(
JoystickButton(onScreenControllerView, LeftStick, 0.1f, 0.8f, 0.05f),
JoystickButton(onScreenControllerView, RightStick, 0.75f, 0.6f, 0.05f)
JoystickButton(onScreenControllerView, Left, 0.1f, 0.8f, 0.05f),
JoystickButton(onScreenControllerView, Right, 0.75f, 0.6f, 0.05f)
)
val rectangularButtons = listOf(buttonL, buttonR)

View File

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
@ -20,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_scrollFlags="enterAlwaysCollapsed">
app:layout_scrollFlags="scroll">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"