ebiten: Add VibrateGamepad and implement this on browsers

Updates #1452
This commit is contained in:
Hajime Hoshi 2021-10-25 01:39:01 +09:00
parent 0d9a165e8f
commit 2aa232878d
6 changed files with 89 additions and 6 deletions

View File

@ -32,7 +32,8 @@ const (
)
type Game struct {
touchIDs []ebiten.TouchID
touchIDs []ebiten.TouchID
gamepadIDs []ebiten.GamepadID
}
func (g *Game) Update() error {
@ -42,11 +43,33 @@ func (g *Game) Update() error {
ebiten.Vibrate(200 * time.Millisecond)
}
g.gamepadIDs = g.gamepadIDs[:0]
g.gamepadIDs = ebiten.AppendGamepadIDs(g.gamepadIDs)
for _, id := range g.gamepadIDs {
for b := ebiten.GamepadButton0; b <= ebiten.GamepadButtonMax; b++ {
if !inpututil.IsGamepadButtonJustPressed(id, b) {
continue
}
// TODO: Test weak-magnitude.
op := &ebiten.VibrateGamepadOptions{
Duration: 200 * time.Millisecond,
StrongMagnitude: 1,
WeakMagnitude: 0,
}
ebiten.VibrateGamepad(id, op)
break
}
}
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
ebitenutil.DebugPrint(screen, "Touch the screen to vibrate the screen.")
msg := "Touch the screen to vibrate the screen."
if len(g.gamepadIDs) > 0 {
msg += "\nPress a gamepad button to vibrate the gamepad."
}
ebitenutil.DebugPrint(screen, msg)
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {

View File

@ -14,6 +14,10 @@
package driver
import (
"time"
)
type GamepadID int
type TouchID int
@ -36,5 +40,6 @@ type Input interface {
StandardGamepadAxisValue(id GamepadID, button StandardGamepadAxis) float64
StandardGamepadButtonValue(id GamepadID, button StandardGamepadButton) float64
TouchPosition(id TouchID) (x, y int)
VibrateGamepad(id GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64)
Wheel() (xoff, yoff float64)
}

View File

@ -23,6 +23,7 @@ package glfw
import (
"math"
"sync"
"time"
"unicode"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
@ -409,6 +410,10 @@ func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button drive
return gamepaddb.IsButtonPressed(g.guid, button, gamepadState{&g})
}
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
// TODO: Implement this (#1452)
}
func init() {
// Confirm that all the hat state values are the same.
if gamepaddb.HatUp != glfw.HatUp {

View File

@ -17,11 +17,14 @@ package js
import (
"encoding/hex"
"syscall/js"
"time"
"unicode"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
)
var object = js.Global().Get("Object")
var (
stringKeydown = js.ValueOf("keydown")
stringKeypress = js.ValueOf("keypress")
@ -60,6 +63,8 @@ type pos struct {
}
type gamepad struct {
value js.Value
name string
mapping string
axisNum int
@ -310,7 +315,9 @@ func (i *Input) updateGamepads() {
}
id := driver.GamepadID(gp.Get("index").Int())
g := gamepad{}
g := gamepad{
value: gp,
}
g.name = gp.Get("id").String()
g.mapping = gp.Get("mapping").String()
@ -506,3 +513,37 @@ func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button drive
}
return g.standardButtonPressed[button]
}
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
g, ok := i.gamepads[id]
if !ok {
return
}
// vibrationActuator is avaialble on Chrome.
if va := g.value.Get("vibrationActuator"); va.Truthy() {
if !va.Get("playEffect").Truthy() {
return
}
prop := object.New()
prop.Set("startDelay", 0)
prop.Set("duration", float64(duration/time.Millisecond))
prop.Set("strongMagnitude", strongMagnitude)
prop.Set("weakMagnitude", weakMagnitude)
va.Call("playEffect", "dual-rumble", prop)
return
}
// hapticActuators is available on Firefox.
if ha := g.value.Get("hapticActuators"); ha.Truthy() {
// TODO: Is this order correct?
if ha.Length() > 0 {
ha.Index(0).Call("pulse", strongMagnitude, float64(duration/time.Millisecond))
}
if ha.Length() > 1 {
ha.Index(1).Call("pulse", weakMagnitude, float64(duration/time.Millisecond))
}
return
}
}

View File

@ -191,6 +191,10 @@ func (i *Input) IsMouseButtonPressed(key driver.MouseButton) bool {
return false
}
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
// TODO: Implement this (#1452)
}
func (i *Input) update(keys map[driver.Key]struct{}, runes []rune, touches []Touch) {
i.ui.m.Lock()
defer i.ui.m.Unlock()

View File

@ -28,8 +28,8 @@ func Vibrate(duration time.Duration) {
uiDriver().Vibrate(duration)
}
// GamepadVibrateOptions represents the options to vibrate a gamepad.
type GamepadVibrateOptions struct {
// VibrateGamepadOptions represents the options to vibrate a gamepad.
type VibrateGamepadOptions struct {
// Duration is the time duration of the effect.
Duration time.Duration
@ -42,4 +42,9 @@ type GamepadVibrateOptions struct {
WeakMagnitude float64
}
// TODO: Add a function VibrateGamepad.
// VibrateGamepad vibrates the specified gamepad with the specified options.
//
// VibrateGamepad is concurrent-safe.
func VibrateGamepad(gamepadID GamepadID, options *VibrateGamepadOptions) {
uiDriver().Input().VibrateGamepad(gamepadID, options.Duration, options.StrongMagnitude, options.WeakMagnitude)
}