ebiten: Add AppendInputChars, AppendGamepadIDs, and AppendTouchIDs

These functions reduce unnecessary allocations of arrays.

Closes #1692
This commit is contained in:
Hajime Hoshi 2021-07-10 02:03:48 +09:00
parent 99a6b1b03e
commit 431cd33839
9 changed files with 100 additions and 55 deletions

View File

@ -83,6 +83,7 @@ type Input struct {
mouseInitPosY int
mouseDir Dir
touches []ebiten.TouchID
touchState touchState
touchID ebiten.TouchID
touchInitPosX int
@ -147,12 +148,13 @@ func (i *Input) Update() {
case mouseStateSettled:
i.mouseState = mouseStateNone
}
i.touches = ebiten.AppendTouchIDs(i.touches[:0])
switch i.touchState {
case touchStateNone:
ts := ebiten.TouchIDs()
if len(ts) == 1 {
i.touchID = ts[0]
x, y := ebiten.TouchPosition(ts[0])
if len(i.touches) == 1 {
i.touchID = i.touches[0]
x, y := ebiten.TouchPosition(i.touches[0])
i.touchInitPosX = x
i.touchInitPosY = y
i.touchLastPosX = x
@ -160,21 +162,20 @@ func (i *Input) Update() {
i.touchState = touchStatePressing
}
case touchStatePressing:
ts := ebiten.TouchIDs()
if len(ts) >= 2 {
if len(i.touches) >= 2 {
break
}
if len(ts) == 1 {
if ts[0] != i.touchID {
if len(i.touches) == 1 {
if i.touches[0] != i.touchID {
i.touchState = touchStateInvalid
} else {
x, y := ebiten.TouchPosition(ts[0])
x, y := ebiten.TouchPosition(i.touches[0])
i.touchLastPosX = x
i.touchLastPosY = y
}
break
}
if len(ts) == 0 {
if len(i.touches) == 0 {
dx := i.touchLastPosX - i.touchInitPosX
dy := i.touchLastPosY - i.touchInitPosY
d, ok := vecToDir(dx, dy)
@ -188,7 +189,7 @@ func (i *Input) Update() {
case touchStateSettled:
i.touchState = touchStateNone
case touchStateInvalid:
if len(ebiten.TouchIDs()) == 0 {
if len(i.touches) == 0 {
i.touchState = touchStateNone
}
}

View File

@ -21,6 +21,7 @@ import (
// Input manages the input state including gamepads and keyboards.
type Input struct {
gamepadIDs []ebiten.GamepadID
virtualGamepadButtonStates map[virtualGamepadButton]int
gamepadConfig gamepadConfig
}
@ -28,7 +29,8 @@ type Input struct {
// GamepadIDButtonPressed returns a gamepad ID where at least one button is pressed.
// If no button is pressed, GamepadIDButtonPressed returns -1.
func (i *Input) GamepadIDButtonPressed() ebiten.GamepadID {
for _, id := range ebiten.GamepadIDs() {
i.gamepadIDs = ebiten.AppendGamepadIDs(i.gamepadIDs[:0])
for _, id := range i.gamepadIDs {
for b := ebiten.GamepadButton(0); b <= ebiten.GamepadButtonMax; b++ {
if ebiten.IsGamepadButtonPressed(id, b) {
return id

View File

@ -176,6 +176,8 @@ type Game struct {
pipeTileYs []int
gameoverCount int
gamepadIDs []ebiten.GamepadID
}
func NewGame() *Game {
@ -204,7 +206,7 @@ func isAnyKeyJustPressed() bool {
return false
}
func jump() bool {
func (g *Game) jump() bool {
if isAnyKeyJustPressed() {
return true
}
@ -214,7 +216,8 @@ func jump() bool {
if len(inpututil.JustPressedTouchIDs()) > 0 {
return true
}
for _, g := range ebiten.GamepadIDs() {
g.gamepadIDs = ebiten.AppendGamepadIDs(g.gamepadIDs[:0])
for _, g := range g.gamepadIDs {
for i := 0; i < ebiten.GamepadButtonNum(g); i++ {
if inpututil.IsGamepadButtonJustPressed(g, ebiten.GamepadButton(i)) {
return true
@ -231,13 +234,13 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
func (g *Game) Update() error {
switch g.mode {
case ModeTitle:
if jump() {
if g.jump() {
g.mode = ModeGame
}
case ModeGame:
g.x16 += 32
g.cameraX += 2
if jump() {
if g.jump() {
g.vy16 = -96
jumpPlayer.Rewind()
jumpPlayer.Play()
@ -260,7 +263,7 @@ func (g *Game) Update() error {
if g.gameoverCount > 0 {
g.gameoverCount--
}
if g.gameoverCount == 0 && jump() {
if g.gameoverCount == 0 && g.jump() {
g.init()
g.mode = ModeTitle
}

View File

@ -60,7 +60,8 @@ func init() {
}
type Game struct {
count int
touches []ebiten.TouchID
count int
}
func (g *Game) Update() error {
@ -74,7 +75,8 @@ func (g *Game) Update() error {
}
// Paint the brush by touches
for _, t := range ebiten.TouchIDs() {
g.touches = ebiten.AppendTouchIDs(g.touches[:0])
for _, t := range g.touches {
x, y := ebiten.TouchPosition(t)
g.paint(canvasImage, x, y)
drawn = true
@ -102,7 +104,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
mx, my := ebiten.CursorPosition()
msg := fmt.Sprintf("(%d, %d)", mx, my)
for _, t := range ebiten.TouchIDs() {
for _, t := range g.touches {
x, y := ebiten.TouchPosition(t)
msg += fmt.Sprintf("\n(%d, %d) touch %d", x, y, t)
}

View File

@ -115,9 +115,10 @@ const (
)
type Game struct {
sprites Sprites
op ebiten.DrawImageOptions
inited bool
touchIDs []ebiten.TouchID
sprites Sprites
op ebiten.DrawImageOptions
inited bool
}
func (g *Game) init() {
@ -144,8 +145,8 @@ func (g *Game) init() {
}
}
func leftTouched() bool {
for _, id := range ebiten.TouchIDs() {
func (g *Game) leftTouched() bool {
for _, id := range g.touchIDs {
x, _ := ebiten.TouchPosition(id)
if x < screenWidth/2 {
return true
@ -154,8 +155,8 @@ func leftTouched() bool {
return false
}
func rightTouched() bool {
for _, id := range ebiten.TouchIDs() {
func (g *Game) rightTouched() bool {
for _, id := range g.touchIDs {
x, _ := ebiten.TouchPosition(id)
if x >= screenWidth/2 {
return true
@ -168,9 +169,10 @@ func (g *Game) Update() error {
if !g.inited {
g.init()
}
g.touchIDs = ebiten.AppendTouchIDs(g.touchIDs[:0])
// Decrease the number of the sprites.
if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) || leftTouched() {
if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) || g.leftTouched() {
g.sprites.num -= 20
if g.sprites.num < MinSprites {
g.sprites.num = MinSprites
@ -178,7 +180,7 @@ func (g *Game) Update() error {
}
// Increase the number of the sprites.
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) || rightTouched() {
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) || g.rightTouched() {
g.sprites.num += 20
if MaxSprites < g.sprites.num {
g.sprites.num = MaxSprites

View File

@ -74,10 +74,11 @@ type Game struct {
x, y float64
zoom float64
touches map[ebiten.TouchID]*touch
pinch *pinch
pan *pan
taps []tap
touchIDs []ebiten.TouchID
touches map[ebiten.TouchID]*touch
pinch *pinch
pan *pan
taps []tap
}
func (g *Game) Update() error {
@ -111,15 +112,17 @@ func (g *Game) Update() error {
// What touches are new in this frame?
for _, id := range inpututil.JustPressedTouchIDs() {
x, y := ebiten.TouchPosition(id)
g.touches[ebiten.TouchID(id)] = &touch{
g.touches[id] = &touch{
originX: x, originY: y,
currX: x, currY: y,
}
}
g.touchIDs = ebiten.AppendTouchIDs(g.touchIDs[:0])
// Update the current position and durations of any touches that have
// neither begun nor ended in this frame.
for _, id := range ebiten.TouchIDs() {
for _, id := range g.touchIDs {
t := g.touches[id]
t.duration = inpututil.TouchPressDuration(id)
t.currX, t.currY = ebiten.TouchPosition(id)
@ -133,7 +136,7 @@ func (g *Game) Update() error {
// If the diff between their origins is different to the diff between
// their currents and if these two are not already a pinch, then this is
// a new pinch!
id1, id2 := ebiten.TouchIDs()[0], ebiten.TouchIDs()[1]
id1, id2 := g.touchIDs[0], g.touchIDs[1]
t1, t2 := g.touches[id1], g.touches[id2]
originDiff := distance(t1.originX, t1.originY, t2.originX, t2.originY)
currDiff := distance(t1.currX, t1.currY, t2.currX, t2.currY)
@ -149,7 +152,7 @@ func (g *Game) Update() error {
}
case 1:
// Potentially this is a new pan.
id := ebiten.TouchIDs()[0]
id := g.touchIDs[0]
t := g.touches[id]
if !t.wasPinch && g.pan == nil && g.pinch == nil {
diff := math.Abs(distance(t.originX, t.originY, t.currX, t.currY))

View File

@ -48,15 +48,17 @@ func repeatingKeyPressed(key ebiten.Key) bool {
}
type Game struct {
runes []rune
text string
counter int
}
func (g *Game) Update() error {
// Add a string from InputChars, that returns string input by users.
// Note that InputChars result changes every frame, so you need to call this
// Add a string by AppendInputChars, that appends runes input by users.
// Note that AppendInputChars result changes every frame, so you need to call this
// every frame.
g.text += string(ebiten.InputChars())
g.runes = ebiten.AppendInputChars(g.runes[:0])
g.text += string(g.runes)
// Adjust the string to be at most 10 lines.
ss := strings.Split(g.text, "\n")

View File

@ -18,21 +18,30 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/driver"
)
// InputChars return "printable" runes read from the keyboard at the time update is called.
// AppendInputChars appends "printable" runes, read from the keyboard at the time update is called, to runes,
// and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// InputChars represents the environment's locale-dependent translation of keyboard
// AppendInputChars represents the environment's locale-dependent translation of keyboard
// input to Unicode characters.
//
// IsKeyPressed is based on a mapping of device (US keyboard) codes to input device keys.
// "Control" and modifier keys should be handled with IsKeyPressed.
//
// InputChars is concurrent-safe.
// AppendInputChars is concurrent-safe.
//
// On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys.
//
// Keyboards don't work on iOS yet (#1090).
func AppendInputChars(runes []rune) []rune {
return uiDriver().Input().AppendInputChars(runes)
}
// InputChars return "printable" runes read from the keyboard at the time update is called.
//
// Deprecated: as of v2.2.0. Use AppendInputChars instead.
func InputChars() []rune {
return uiDriver().Input().AppendInputChars(nil)
return AppendInputChars(nil)
}
// IsKeyPressed returns a boolean indicating whether key is pressed.
@ -134,13 +143,21 @@ func GamepadName(id GamepadID) string {
return uiDriver().Input().GamepadName(id)
}
// AppendGamepadIDs appends available gamepad IDs to gamepadIDs, and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// AppendGamepadIDs is concurrent-safe.
//
// AppendGamepadIDs doesn't append anything on iOS.
func AppendGamepadIDs(gamepadIDs []GamepadID) []GamepadID {
return uiDriver().Input().AppendGamepadIDs(gamepadIDs)
}
// GamepadIDs returns a slice indicating available gamepad IDs.
//
// GamepadIDs is concurrent-safe.
//
// GamepadIDs always returns an empty slice on iOS.
// Deprecated: as of v2.2.0. Use AppendGamepadIDs instead.
func GamepadIDs() []GamepadID {
return uiDriver().Input().AppendGamepadIDs(nil)
return AppendGamepadIDs(nil)
}
// GamepadAxisNum returns the number of axes of the gamepad (id).
@ -188,17 +205,25 @@ func IsGamepadButtonPressed(id GamepadID, button GamepadButton) bool {
// TouchID represents a touch's identifier.
type TouchID = driver.TouchID
// TouchIDs returns the current touch states.
// AppendTouchIDs appends the current touch states to touches, and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// If you want to know whether a touch started being pressed in the current frame,
// use inpututil.JustPressedTouchIDs
//
// TouchIDs returns nil when there are no touches.
// TouchIDs always returns nil on desktops.
// AppendTouchIDs doesn't append anything when there are no touches.
// AppendTouchIDs always does nothing on desktops.
//
// TouchIDs is concurrent-safe.
// AppendTouchIDs is concurrent-safe.
func AppendTouchIDs(touches []TouchID) []TouchID {
return uiDriver().Input().AppendTouchIDs(touches)
}
// TouchIDs returns the current touch states.
//
// Deperecated: as of v2.2.0. Use AppendTouchIDs instead.
func TouchIDs() []TouchID {
return uiDriver().Input().AppendTouchIDs(nil)
return AppendTouchIDs(nil)
}
// TouchPosition returns the position for the touch of the specified ID.

View File

@ -40,6 +40,9 @@ type inputState struct {
touchDurations map[ebiten.TouchID]int
prevTouchDurations map[ebiten.TouchID]int
gamepadIDsBuf []ebiten.GamepadID
touchIDsBuf []ebiten.TouchID
m sync.RWMutex
}
@ -117,7 +120,8 @@ func (i *inputState) update() {
for id := range i.gamepadIDs {
delete(i.gamepadIDs, id)
}
for _, id := range ebiten.GamepadIDs() {
i.gamepadIDsBuf = ebiten.AppendGamepadIDs(i.gamepadIDsBuf[:0])
for _, id := range i.gamepadIDsBuf {
i.gamepadIDs[id] = struct{}{}
if _, ok := i.gamepadButtonDurations[id]; !ok {
i.gamepadButtonDurations[id] = make([]int, ebiten.GamepadButtonMax+1)
@ -150,7 +154,8 @@ func (i *inputState) update() {
for id := range i.touchIDs {
delete(i.touchIDs, id)
}
for _, id := range ebiten.TouchIDs() {
i.touchIDsBuf = ebiten.AppendTouchIDs(i.touchIDsBuf[:0])
for _, id := range i.touchIDsBuf {
i.touchIDs[id] = struct{}{}
i.touchDurations[id]++
}