diff --git a/cmd/ebitenmobile/_files/EbitenView.java b/cmd/ebitenmobile/_files/EbitenView.java index fe797bf0d..4cb4c3f22 100644 --- a/cmd/ebitenmobile/_files/EbitenView.java +++ b/cmd/ebitenmobile/_files/EbitenView.java @@ -244,12 +244,6 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis int axisMask = getAxisMask(inputDevice); Ebitenmobileview.onGamepadAdded(deviceId, inputDevice.getName(), gamepad.axes.size(), gamepad.hats.size()/2, descriptor, vendorId, productId, buttonMask, axisMask); - - // Initialize the trigger axes values explicitly, or the initial button values would be 0.5 instead of 0. - if (gamepad.axes.size() >= 6) { - Ebitenmobileview.onGamepadAxisChanged(deviceId, 4, -1); - Ebitenmobileview.onGamepadAxisChanged(deviceId, 5, -1); - } } // The implementation is copied from SDL: diff --git a/internal/gamepad/extern_android.go b/internal/gamepad/extern_android.go index b1e016c67..80532f26b 100644 --- a/internal/gamepad/extern_android.go +++ b/internal/gamepad/extern_android.go @@ -47,6 +47,7 @@ func (g *gamepads) addAndroidGamepad(androidDeviceID int, name, sdlID string, ax gp := g.add(name, sdlID) gp.native = &nativeGamepadImpl{ androidDeviceID: androidDeviceID, + axesReady: make([]bool, axisCount), axes: make([]float64, axisCount), buttons: make([]bool, gamepaddb.SDLControllerButtonMax+1), hats: make([]int, hatCount), @@ -110,6 +111,13 @@ func (g *Gamepad) updateAndroidGamepadAxis(axis int, value float64) { return } n.axes[axis] = value + + // MotionEvent with 0 value can be sent when a gamepad is connected even though an axis is not touched (#2598). + // This is problematic when an axis is a trigger button where -1 should be the default value. + // When MotionEvent with non-0 value is sent, it seems fine to assume that the axis is actually touched and ready. + if value != 0 { + n.axesReady[axis] = true + } } func (g *Gamepad) updateAndroidGamepadButton(button Button, pressed bool) { diff --git a/internal/gamepad/gamepad.go b/internal/gamepad/gamepad.go index 27f503af9..3a984d1f6 100644 --- a/internal/gamepad/gamepad.go +++ b/internal/gamepad/gamepad.go @@ -243,6 +243,7 @@ type nativeGamepad interface { axisCount() int buttonCount() int hatCount() int + isAxisReady(axis int) bool axisValue(axis int) float64 buttonValue(button int) float64 isButtonPressed(button int) bool @@ -296,6 +297,14 @@ func (g *Gamepad) HatCount() int { return g.native.hatCount() } +// IsAxisReady is concurrent-safe. +func (g *Gamepad) IsAxisReady(axis int) bool { + g.m.Lock() + defer g.m.Unlock() + + return g.native.isAxisReady(axis) +} + // Axis is concurrent-safe. func (g *Gamepad) Axis(axis int) float64 { g.m.Lock() diff --git a/internal/gamepad/gamepad_android.go b/internal/gamepad/gamepad_android.go index 914a35194..7a5c430e1 100644 --- a/internal/gamepad/gamepad_android.go +++ b/internal/gamepad/gamepad_android.go @@ -39,9 +39,10 @@ func (*nativeGamepadsImpl) update(gamepads *gamepads) error { type nativeGamepadImpl struct { androidDeviceID int - axes []float64 - buttons []bool - hats []int + axesReady []bool + axes []float64 + buttons []bool + hats []int } func (*nativeGamepadImpl) update(gamepad *gamepads) error { @@ -73,6 +74,13 @@ func (g *nativeGamepadImpl) hatCount() int { return len(g.hats) } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + if axis < 0 || axis >= len(g.axesReady) { + return false + } + return g.axesReady[axis] +} + func (g *nativeGamepadImpl) axisValue(axis int) float64 { if axis < 0 || axis >= len(g.axes) { return 0 diff --git a/internal/gamepad/gamepad_darwin.go b/internal/gamepad/gamepad_darwin.go index 01b76bb43..78c11cd9e 100644 --- a/internal/gamepad/gamepad_darwin.go +++ b/internal/gamepad/gamepad_darwin.go @@ -399,6 +399,10 @@ func (g *nativeGamepadImpl) hatCount() int { return len(g.hatValues) } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (g *nativeGamepadImpl) axisValue(axis int) float64 { if axis < 0 || axis >= len(g.axisValues) { return 0 diff --git a/internal/gamepad/gamepad_desktop_windows.go b/internal/gamepad/gamepad_desktop_windows.go index ae9f43372..edd7defbb 100644 --- a/internal/gamepad/gamepad_desktop_windows.go +++ b/internal/gamepad/gamepad_desktop_windows.go @@ -721,6 +721,10 @@ func (g *nativeGamepadDesktop) hatCount() int { return 1 } +func (g *nativeGamepadDesktop) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (g *nativeGamepadDesktop) axisValue(axis int) float64 { if g.usesDInput() { if axis < 0 || axis >= len(g.dinputAxes) { diff --git a/internal/gamepad/gamepad_ios.go b/internal/gamepad/gamepad_ios.go index 209b1760f..61f74aa75 100644 --- a/internal/gamepad/gamepad_ios.go +++ b/internal/gamepad/gamepad_ios.go @@ -78,6 +78,10 @@ func (g *nativeGamepadImpl) hatCount() int { return len(g.hats) } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (g *nativeGamepadImpl) axisValue(axis int) float64 { if axis < 0 || axis >= len(g.axes) { return 0 diff --git a/internal/gamepad/gamepad_js.go b/internal/gamepad/gamepad_js.go index 203ce5e98..650d3ac12 100644 --- a/internal/gamepad/gamepad_js.go +++ b/internal/gamepad/gamepad_js.go @@ -152,6 +152,10 @@ func (g *nativeGamepadImpl) hatCount() int { return 0 } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (g *nativeGamepadImpl) axisValue(axis int) float64 { axes := g.value.Get("axes") if axis < 0 || axis >= axes.Length() { diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go index 50932f7c5..617f2557f 100644 --- a/internal/gamepad/gamepad_linux.go +++ b/internal/gamepad/gamepad_linux.go @@ -592,6 +592,10 @@ func (g *nativeGamepadImpl) hatCount() int { return g.hatCount_ } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (g *nativeGamepadImpl) axisValue(axis int) float64 { if axis < 0 || axis >= g.axisCount_ { return 0 diff --git a/internal/gamepad/gamepad_nintendosdk.go b/internal/gamepad/gamepad_nintendosdk.go index c09ca22b7..795b2f13a 100644 --- a/internal/gamepad/gamepad_nintendosdk.go +++ b/internal/gamepad/gamepad_nintendosdk.go @@ -146,6 +146,10 @@ func (g *nativeGamepadImpl) hatCount() int { return 0 } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (g *nativeGamepadImpl) axisValue(axis int) float64 { if axis < 0 || axis >= len(g.axisValues) { return 0 diff --git a/internal/gamepad/gamepad_null.go b/internal/gamepad/gamepad_null.go index 6ebfc7a52..c195411de 100644 --- a/internal/gamepad/gamepad_null.go +++ b/internal/gamepad/gamepad_null.go @@ -66,6 +66,10 @@ func (*nativeGamepadImpl) hatCount() int { return 0 } +func (g *nativeGamepadImpl) isAxisReady(axis int) bool { + return false +} + func (*nativeGamepadImpl) axisValue(axis int) float64 { return 0 } diff --git a/internal/gamepad/gamepad_xbox_windows.go b/internal/gamepad/gamepad_xbox_windows.go index 4e210317a..ed1b2a091 100644 --- a/internal/gamepad/gamepad_xbox_windows.go +++ b/internal/gamepad/gamepad_xbox_windows.go @@ -192,6 +192,10 @@ func (n *nativeGamepadXbox) hatCount() int { return 0 } +func (g *nativeGamepadXbox) isAxisReady(axis int) bool { + return axis >= 0 && axis < g.axisCount() +} + func (n *nativeGamepadXbox) axisValue(axis int) float64 { switch gamepaddb.StandardAxis(axis) { case gamepaddb.StandardAxisLeftStickHorizontal: diff --git a/internal/gamepaddb/gamepaddb.go b/internal/gamepaddb/gamepaddb.go index 0c9472654..fddb0d90a 100644 --- a/internal/gamepaddb/gamepaddb.go +++ b/internal/gamepaddb/gamepaddb.go @@ -390,6 +390,7 @@ func HasStandardLayoutMapping(id string) bool { } type GamepadState interface { + IsAxisReady(index int) bool Axis(index int) float64 Button(index int) bool Hat(index int) int @@ -435,6 +436,9 @@ func AxisValue(id string, axis StandardAxis, state GamepadState) float64 { switch mapping.Type { case mappingTypeAxis: + if !state.IsAxisReady(mapping.Index) { + return 0 + } v := state.Axis(mapping.Index)*float64(mapping.AxisScale) + float64(mapping.AxisOffset) if v > 1 { return 1 @@ -496,6 +500,9 @@ func buttonValue(id string, button StandardButton, state GamepadState) float64 { switch mapping.Type { case mappingTypeAxis: + if !state.IsAxisReady(mapping.Index) { + return 0 + } v := state.Axis(mapping.Index)*float64(mapping.AxisScale) + float64(mapping.AxisOffset) if v > 1 { v = 1