From d6e109555d23b316cd9e4c123615934eb38dbe00 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 13 Oct 2018 16:25:33 +0900 Subject: [PATCH] examples/blocks: Bug fix: multiple gamepad detection --- examples/blocks/blocks/gamepad.go | 58 ++++++++++++++++++++------ examples/blocks/blocks/gamepadscene.go | 5 ++- examples/blocks/blocks/input.go | 21 ++++++---- examples/blocks/blocks/titlescene.go | 11 ++++- 4 files changed, 71 insertions(+), 24 deletions(-) diff --git a/examples/blocks/blocks/gamepad.go b/examples/blocks/blocks/gamepad.go index 5cd04e100..f05c29896 100644 --- a/examples/blocks/blocks/gamepad.go +++ b/examples/blocks/blocks/gamepad.go @@ -47,6 +47,9 @@ type axis struct { } type gamepadConfig struct { + gamepadID int + gamepadIDInitialized bool + current virtualGamepadButton buttons map[virtualGamepadButton]ebiten.GamepadButton axes map[virtualGamepadButton]axis @@ -56,7 +59,20 @@ type gamepadConfig struct { defaultAxesValues map[int]float64 } +func (c *gamepadConfig) SetGamepadID(id int) { + c.gamepadID = id + c.gamepadIDInitialized = true +} + +func (c *gamepadConfig) IsInitialized() bool { + return c.gamepadIDInitialized +} + func (c *gamepadConfig) initializeIfNeeded() { + if !c.gamepadIDInitialized { + panic("not reached") + } + if c.buttons == nil { c.buttons = map[virtualGamepadButton]ebiten.GamepadButton{} } @@ -77,15 +93,17 @@ func (c *gamepadConfig) initializeIfNeeded() { // For example, on PS4 controllers, L2/R2's axes valuse can be -1.0. if c.defaultAxesValues == nil { c.defaultAxesValues = map[int]float64{} - const gamepadID = 0 - na := ebiten.GamepadAxisNum(gamepadID) + na := ebiten.GamepadAxisNum(c.gamepadID) for a := 0; a < na; a++ { - c.defaultAxesValues[a] = ebiten.GamepadAxis(gamepadID, a) + c.defaultAxesValues[a] = ebiten.GamepadAxis(c.gamepadID, a) } } } func (c *gamepadConfig) Reset() { + c.gamepadID = 0 + c.gamepadIDInitialized = false + c.buttons = nil c.axes = nil c.assignedButtons = nil @@ -94,37 +112,45 @@ func (c *gamepadConfig) Reset() { // Scan scans the current input state and assigns the given virtual gamepad button b // to the current (pysical) pressed buttons of the gamepad. -func (c *gamepadConfig) Scan(gamepadID int, b virtualGamepadButton) bool { +func (c *gamepadConfig) Scan(b virtualGamepadButton) bool { + if !c.gamepadIDInitialized { + panic("not reached") + } + c.initializeIfNeeded() delete(c.buttons, b) delete(c.axes, b) - ebn := ebiten.GamepadButton(ebiten.GamepadButtonNum(gamepadID)) + ebn := ebiten.GamepadButton(ebiten.GamepadButtonNum(c.gamepadID)) for eb := ebiten.GamepadButton(0); eb < ebn; eb++ { if _, ok := c.assignedButtons[eb]; ok { continue } - if inpututil.IsGamepadButtonJustPressed(gamepadID, eb) { + if inpututil.IsGamepadButtonJustPressed(c.gamepadID, eb) { c.buttons[b] = eb c.assignedButtons[eb] = struct{}{} return true } } - na := ebiten.GamepadAxisNum(gamepadID) + na := ebiten.GamepadAxisNum(c.gamepadID) for a := 0; a < na; a++ { - v := ebiten.GamepadAxis(gamepadID, a) + v := ebiten.GamepadAxis(c.gamepadID, a) + const delta = 0.25 + // Check |v| < 1.0 because there is a bug that a button returns // an axis value wrongly and the value may be over 1 on some platforms. - if axisThreshold <= v && v <= 1.0 && v != c.defaultAxesValues[a] { + if axisThreshold <= v && v <= 1.0 && + (v < c.defaultAxesValues[a]-delta || c.defaultAxesValues[a]+delta < v) { if _, ok := c.assignedAxes[axis{a, true}]; !ok { c.axes[b] = axis{a, true} c.assignedAxes[axis{a, true}] = struct{}{} return true } } - if -1.0 <= v && v <= -axisThreshold && v != c.defaultAxesValues[a] { + if -1.0 <= v && v <= -axisThreshold && + (v < c.defaultAxesValues[a]-delta || c.defaultAxesValues[a]+delta < v) { if _, ok := c.assignedAxes[axis{a, false}]; !ok { c.axes[b] = axis{a, false} c.assignedAxes[axis{a, false}] = struct{}{} @@ -139,16 +165,20 @@ func (c *gamepadConfig) Scan(gamepadID int, b virtualGamepadButton) bool { // IsButtonPressed returns a boolean value indicating whether // the given virtual button b is pressed. func (c *gamepadConfig) IsButtonPressed(b virtualGamepadButton) bool { + if !c.gamepadIDInitialized { + panic("not reached") + } + c.initializeIfNeeded() bb, ok := c.buttons[b] if ok { - return ebiten.IsGamepadButtonPressed(0, bb) + return ebiten.IsGamepadButtonPressed(c.gamepadID, bb) } a, ok := c.axes[b] if ok { - v := ebiten.GamepadAxis(0, a.id) + v := ebiten.GamepadAxis(c.gamepadID, a.id) if a.positive { return axisThreshold <= v && v <= 1.0 } else { @@ -160,6 +190,10 @@ func (c *gamepadConfig) IsButtonPressed(b virtualGamepadButton) bool { // Name returns the pysical button's name for the given virtual button. func (c *gamepadConfig) ButtonName(b virtualGamepadButton) string { + if !c.gamepadIDInitialized { + panic("not reached") + } + c.initializeIfNeeded() bb, ok := c.buttons[b] diff --git a/examples/blocks/blocks/gamepadscene.go b/examples/blocks/blocks/gamepadscene.go index b3c116d03..1bdb1cf43 100644 --- a/examples/blocks/blocks/gamepadscene.go +++ b/examples/blocks/blocks/gamepadscene.go @@ -24,6 +24,7 @@ import ( ) type GamepadScene struct { + gamepadID int currentIndex int countAfterSetting int buttonStates []string @@ -32,6 +33,7 @@ type GamepadScene struct { func (s *GamepadScene) Update(state *GameState) error { if s.currentIndex == 0 { state.Input.gamepadConfig.Reset() + state.Input.gamepadConfig.SetGamepadID(s.gamepadID) } if inpututil.IsKeyJustPressed(ebiten.KeyEscape) { state.Input.gamepadConfig.Reset() @@ -62,8 +64,7 @@ func (s *GamepadScene) Update(state *GameState) error { } b := virtualGamepadButtons[s.currentIndex] - const gamepadID = 0 - if state.Input.gamepadConfig.Scan(gamepadID, b) { + if state.Input.gamepadConfig.Scan(b) { s.currentIndex++ if s.currentIndex == len(virtualGamepadButtons) { s.countAfterSetting = ebiten.MaxTPS() diff --git a/examples/blocks/blocks/input.go b/examples/blocks/blocks/input.go index c45fb9c6b..590cc49d9 100644 --- a/examples/blocks/blocks/input.go +++ b/examples/blocks/blocks/input.go @@ -25,16 +25,17 @@ type Input struct { gamepadConfig gamepadConfig } -// IsAnyGamepadButtonPressed returns a boolean value indicating -// whether any gamepad button is pressed. -func (i *Input) IsAnyGamepadButtonPressed() bool { - const gamepadID = 0 - for b := ebiten.GamepadButton(0); b <= ebiten.GamepadButtonMax; b++ { - if ebiten.IsGamepadButtonPressed(gamepadID, b) { - return true +// GamepadIDButtonPressed returns a gamepad ID where at least one button is pressed. +// If no button is pressed, GamepadIDButtonPressed returns -1. +func (i *Input) GamepadIDButtonPressed() int { + for _, id := range ebiten.GamepadIDs() { + for b := ebiten.GamepadButton(0); b <= ebiten.GamepadButtonMax; b++ { + if ebiten.IsGamepadButtonPressed(id, b) { + return id + } } } - return false + return -1 } func (i *Input) stateForVirtualGamepadButton(b virtualGamepadButton) int { @@ -45,6 +46,10 @@ func (i *Input) stateForVirtualGamepadButton(b virtualGamepadButton) int { } func (i *Input) Update() { + if !i.gamepadConfig.IsInitialized() { + return + } + if i.virtualGamepadButtonStates == nil { i.virtualGamepadButtonStates = map[virtualGamepadButton]int{} } diff --git a/examples/blocks/blocks/titlescene.go b/examples/blocks/blocks/titlescene.go index 05ae1e72a..7b6c47fae 100644 --- a/examples/blocks/blocks/titlescene.go +++ b/examples/blocks/blocks/titlescene.go @@ -40,6 +40,10 @@ type TitleScene struct { } func anyGamepadAbstractButtonPressed(i *Input) bool { + if !i.gamepadConfig.IsInitialized() { + return false + } + for _, b := range virtualGamepadButtons { if i.gamepadConfig.IsButtonPressed(b) { return true @@ -54,6 +58,7 @@ func (s *TitleScene) Update(state *GameState) error { state.SceneManager.GoTo(NewGameScene()) return nil } + if anyGamepadAbstractButtonPressed(state.Input) { state.SceneManager.GoTo(NewGameScene()) return nil @@ -61,8 +66,10 @@ func (s *TitleScene) Update(state *GameState) error { // If 'abstract' gamepad buttons are not set and any gamepad buttons are pressed, // go to the gamepad configuration scene. - if state.Input.IsAnyGamepadButtonPressed() { - state.SceneManager.GoTo(&GamepadScene{}) + if id := state.Input.GamepadIDButtonPressed(); id >= 0 { + g := &GamepadScene{} + g.gamepadID = id + state.SceneManager.GoTo(g) return nil } return nil