examples/blocks: Bug fix: multiple gamepad detection

This commit is contained in:
Hajime Hoshi 2018-10-13 16:25:33 +09:00
parent 8ed02efd28
commit d6e109555d
4 changed files with 71 additions and 24 deletions

View File

@ -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]

View File

@ -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()

View File

@ -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{}
}

View File

@ -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