From ea12ede12794f8668fefa72850b6c87ddf73da41 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 12 Sep 2021 20:13:23 +0900 Subject: [PATCH] ebiten: Add StandardGamepadButtonValue Closes #1721 --- examples/gamepad/main.go | 129 +++++++++--------------------- input.go | 9 +++ internal/driver/input.go | 1 + internal/gamepaddb/gamepaddb.go | 34 ++++++++ internal/uidriver/glfw/input.go | 11 +++ internal/uidriver/js/input_js.go | 28 ++++--- internal/uidriver/mobile/input.go | 5 ++ 7 files changed, 116 insertions(+), 101 deletions(-) diff --git a/examples/gamepad/main.go b/examples/gamepad/main.go index f59cd23e3..d8842db56 100644 --- a/examples/gamepad/main.go +++ b/examples/gamepad/main.go @@ -98,99 +98,44 @@ func (g *Game) Update() error { return nil } -func standardMap(id ebiten.GamepadID) string { - m := ` [FBL] [FBR] - [FTL] [FTR] +var standardButtonToString = map[ebiten.StandardGamepadButton]string{ + ebiten.StandardGamepadButtonRightBottom: "RB", + ebiten.StandardGamepadButtonRightRight: "RR", + ebiten.StandardGamepadButtonRightLeft: "RL", + ebiten.StandardGamepadButtonRightTop: "RT", + ebiten.StandardGamepadButtonFrontTopLeft: "FTL", + ebiten.StandardGamepadButtonFrontTopRight: "FTR", + ebiten.StandardGamepadButtonFrontBottomLeft: "FBL", + ebiten.StandardGamepadButtonFrontBottomRight: "FBR", + ebiten.StandardGamepadButtonCenterLeft: "CL", + ebiten.StandardGamepadButtonCenterRight: "CR", + ebiten.StandardGamepadButtonLeftStick: "LS", + ebiten.StandardGamepadButtonRightStick: "RS", + ebiten.StandardGamepadButtonLeftBottom: "LB", + ebiten.StandardGamepadButtonLeftRight: "LR", + ebiten.StandardGamepadButtonLeftLeft: "LL", + ebiten.StandardGamepadButtonLeftTop: "LT", + ebiten.StandardGamepadButtonCenterCenter: "CC", +} - [LT] [CC] [RT] - [LL][LR] [CL][CR] [RL][RR] - [LB] [RB] - (LS) (RS) +func standardMap(id ebiten.GamepadID) string { + m := ` [FBL ] [FBR ] + [FTL ] [FTR ] + + [LT ] [CC ] [RT ] + [LL ][LR ] [CL ][CR ] [RL ][RR ] + [LB ] [RB ] + [LS ] [RS ] ` - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonRightBottom) { - m = strings.Replace(m, "[RB]", "[**]", 1) - } else { - m = strings.Replace(m, "[RB]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonRightRight) { - m = strings.Replace(m, "[RR]", "[**]", 1) - } else { - m = strings.Replace(m, "[RR]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonRightLeft) { - m = strings.Replace(m, "[RL]", "[**]", 1) - } else { - m = strings.Replace(m, "[RL]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonRightTop) { - m = strings.Replace(m, "[RT]", "[**]", 1) - } else { - m = strings.Replace(m, "[RT]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonFrontTopLeft) { - m = strings.Replace(m, "[FTL]", "[**]", 1) - } else { - m = strings.Replace(m, "[FTL]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonFrontTopRight) { - m = strings.Replace(m, "[FTR]", "[**]", 1) - } else { - m = strings.Replace(m, "[FTR]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonFrontBottomLeft) { - m = strings.Replace(m, "[FBL]", "[**]", 1) - } else { - m = strings.Replace(m, "[FBL]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonFrontBottomRight) { - m = strings.Replace(m, "[FBR]", "[**]", 1) - } else { - m = strings.Replace(m, "[FBR]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonCenterLeft) { - m = strings.Replace(m, "[CL]", "[**]", 1) - } else { - m = strings.Replace(m, "[CL]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonCenterRight) { - m = strings.Replace(m, "[CR]", "[**]", 1) - } else { - m = strings.Replace(m, "[CR]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftStick) { - m = strings.Replace(m, "(LS)", "[**]", 1) - } else { - m = strings.Replace(m, "(LS)", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonRightStick) { - m = strings.Replace(m, "(RS)", "[**]", 1) - } else { - m = strings.Replace(m, "(RS)", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftBottom) { - m = strings.Replace(m, "[LB]", "[**]", 1) - } else { - m = strings.Replace(m, "[LB]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftRight) { - m = strings.Replace(m, "[LR]", "[**]", 1) - } else { - m = strings.Replace(m, "[LR]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftLeft) { - m = strings.Replace(m, "[LL]", "[**]", 1) - } else { - m = strings.Replace(m, "[LL]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftTop) { - m = strings.Replace(m, "[LT]", "[**]", 1) - } else { - m = strings.Replace(m, "[LT]", "[ ]", 1) - } - if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonCenterCenter) { - m = strings.Replace(m, "[CC]", "[**]", 1) - } else { - m = strings.Replace(m, "[CC]", "[ ]", 1) + + for b, str := range standardButtonToString { + placeholder := "[" + str + strings.Repeat(" ", 4-len(str)) + "]" + v := ebiten.StandardGamepadButtonValue(id, b) + if ebiten.IsStandardGamepadButtonPressed(id, b) { + m = strings.Replace(m, placeholder, fmt.Sprintf("[%0.2f]", v), 1) + } else { + m = strings.Replace(m, placeholder, fmt.Sprintf(" %0.2f ", v), 1) + } } m += fmt.Sprintf(" Left Stick: X: %+0.2f, Y: %+0.2f\n Right Stick: X: %+0.2f, Y: %+0.2f", @@ -222,7 +167,7 @@ func (g *Game) Draw(screen *ebiten.Image) { str += fmt.Sprintf(" Buttons: %s\n", strings.Join(g.pressedButtons[id], ", ")) if ebiten.IsStandardGamepadLayoutAvailable(id) { str += "\n" - str += standardMap(id) + str += standardMap(id) + "\n" str += "\n" } str += "\n" diff --git a/input.go b/input.go index 7146ae591..e585c7b52 100644 --- a/input.go +++ b/input.go @@ -219,6 +219,15 @@ func StandardGamepadAxisValue(id GamepadID, axis StandardGamepadAxis) float64 { return uiDriver().Input().StandardGamepadAxisValue(id, axis) } +// StandardGamepadButtonValue returns a float value [0.0 - 1.0] of the given gamepad (id)'s standard button (button). +// +// StandardGamepadButtonValue returns 0 when the gamepad doesn't have a standard gamepad layout mapping. +// +// StandardGamepadButtonValue is concurrent safe. +func StandardGamepadButtonValue(id GamepadID, button StandardGamepadButton) float64 { + return uiDriver().Input().StandardGamepadButtonValue(id, button) +} + // IsStandardGamepadButtonPressed reports whether the given gamepad (id)'s standard gamepad button (button) is pressed. // // IsStandardGamepadButtonPressed returns false when the gamepad doesn't have a standard gamepad layout mapping. diff --git a/internal/driver/input.go b/internal/driver/input.go index 42b1524b3..c9f1db278 100644 --- a/internal/driver/input.go +++ b/internal/driver/input.go @@ -34,6 +34,7 @@ type Input interface { IsStandardGamepadButtonPressed(id GamepadID, button StandardGamepadButton) bool IsStandardGamepadLayoutAvailable(id GamepadID) bool StandardGamepadAxisValue(id GamepadID, button StandardGamepadAxis) float64 + StandardGamepadButtonValue(id GamepadID, button StandardGamepadButton) float64 TouchPosition(id TouchID) (x, y int) Wheel() (xoff, yoff float64) } diff --git a/internal/gamepaddb/gamepaddb.go b/internal/gamepaddb/gamepaddb.go index be92eaadd..1ca456250 100644 --- a/internal/gamepaddb/gamepaddb.go +++ b/internal/gamepaddb/gamepaddb.go @@ -394,6 +394,40 @@ func AxisValue(id string, axis driver.StandardGamepadAxis, state GamepadState) f return 0 } +func ButtonValue(id string, button driver.StandardGamepadButton, state GamepadState) float64 { + mappingsM.RLock() + defer mappingsM.RUnlock() + + mappings, ok := gamepadButtonMappings[id] + if !ok { + return 0 + } + + switch m := mappings[button]; m.Type { + case mappingTypeAxis: + v := state.Axis(m.Index)*float64(m.AxisScale) + float64(m.AxisOffset) + if v > 1 { + return 1 + } else if v < -1 { + return -1 + } + // Adjust [-1, 1] to [0, 1] + return (v + 1) / 2 + case mappingTypeButton: + if state.Button(m.Index) { + return 1 + } + return 0 + case mappingTypeHat: + if state.Hat(m.Index)&m.HatState != 0 { + return 1 + } + return 0 + } + + return 0 +} + func IsButtonPressed(id string, button driver.StandardGamepadButton, state GamepadState) bool { mappingsM.RLock() defer mappingsM.RUnlock() diff --git a/internal/uidriver/glfw/input.go b/internal/uidriver/glfw/input.go index 9255e9afb..b6d93ae7e 100644 --- a/internal/uidriver/glfw/input.go +++ b/internal/uidriver/glfw/input.go @@ -388,6 +388,17 @@ func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.Standa return gamepaddb.AxisValue(g.guid, axis, gamepadState{&g}) } +func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 { + i.ui.m.Lock() + defer i.ui.m.Unlock() + + if len(i.gamepads) <= int(id) { + return 0 + } + g := i.gamepads[int(id)] + return gamepaddb.ButtonValue(g.guid, button, gamepadState{&g}) +} + func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool { i.ui.m.Lock() defer i.ui.m.Unlock() diff --git a/internal/uidriver/js/input_js.go b/internal/uidriver/js/input_js.go index 5ad9a5b7f..d0721027d 100644 --- a/internal/uidriver/js/input_js.go +++ b/internal/uidriver/js/input_js.go @@ -66,8 +66,10 @@ type gamepad struct { axes [16]float64 buttonNum int buttonPressed [256]bool + buttonValues [256]float64 standardButtonPressed [driver.StandardGamepadButtonMax + 1]bool + standardButtonValues [driver.StandardGamepadButtonMax + 1]float64 standardAxisValues [driver.StandardGamepadAxisMax + 1]float64 } @@ -315,27 +317,24 @@ func (i *Input) updateGamepads() { axes := gp.Get("axes") axesNum := axes.Length() g.axisNum = axesNum - for a := 0; a < len(g.axes); a++ { - if axesNum <= a { - break - } + for a := 0; a < axesNum; a++ { g.axes[a] = axes.Index(a).Float() } buttons := gp.Get("buttons") buttonsNum := buttons.Length() g.buttonNum = buttonsNum - for b := 0; b < len(g.buttonPressed); b++ { - if buttonsNum <= b { - break - } - g.buttonPressed[b] = buttons.Index(b).Get("pressed").Bool() + for b := 0; b < buttonsNum; b++ { + btn := buttons.Index(b) + g.buttonPressed[b] = btn.Get("pressed").Bool() + g.buttonValues[b] = btn.Get("value").Float() } if g.mapping == "standard" { // When the gamepad's mapping is "standard", the button and axis IDs are already mapped as the standard layout. // See https://www.w3.org/TR/gamepad/#remapping. copy(g.standardButtonPressed[:], g.buttonPressed[:]) + copy(g.standardButtonValues[:], g.buttonValues[:]) copy(g.standardAxisValues[:], g.axes[:]) } @@ -486,6 +485,17 @@ func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.Standa return g.standardAxisValues[axis] } +func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 { + g, ok := i.gamepads[id] + if !ok { + return 0 + } + if !g.hasStandardLayoutMapping() { + return 0 + } + return g.standardButtonValues[button] +} + func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool { g, ok := i.gamepads[id] if !ok { diff --git a/internal/uidriver/mobile/input.go b/internal/uidriver/mobile/input.go index 6f667e2ab..8b3e6fb00 100644 --- a/internal/uidriver/mobile/input.go +++ b/internal/uidriver/mobile/input.go @@ -142,6 +142,11 @@ func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button drive return false } +func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 { + // TODO: Implement this (#1557) + return 0 +} + func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 { // TODO: Implement this (#1557) return 0