Skip to content

Commit

Permalink
internal/gamepad: ignore the very first MotionEvent with 0 value for …
Browse files Browse the repository at this point in the history
…Android

On Android, MotionEvent with 0 values might come for axes when connecting
a gamepad, even though a user didn't touch any axes. This is problematic
especially for tirgger axes, where the default value should be -1.

This change fixes the issue by adding a new state `axesReady` to check
if an axis is really touched or not. If an axis is not touched yet,
a button value for a standard (trigger) button always returns 0.

This change also removes an old hack to initialize axis values for
triggers.

Closes #2598
  • Loading branch information
hajimehoshi committed Mar 21, 2024
1 parent 4f1dc6a commit e2f26b9
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 9 deletions.
6 changes: 0 additions & 6 deletions cmd/ebitenmobile/_files/EbitenView.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,6 @@ public void onInputDeviceAdded(int deviceId) {
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:
Expand Down
8 changes: 8 additions & 0 deletions internal/gamepad/extern_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions internal/gamepad/gamepad.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
14 changes: 11 additions & 3 deletions internal/gamepad/gamepad_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_desktop_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_js.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_nintendosdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_null.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 4 additions & 0 deletions internal/gamepad/gamepad_xbox_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions internal/gamepaddb/gamepaddb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e2f26b9

Please sign in to comment.