123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- // Copyright 2018 The Ebiten Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Package inpututil provides utility functions of input like keyboard or mouse.
- package inpututil
- import (
- "slices"
- "sync"
- "github.com/hajimehoshi/ebiten/v2"
- "github.com/hajimehoshi/ebiten/v2/internal/hook"
- )
- type gamepadState struct {
- buttonDurations [ebiten.GamepadButtonMax + 1]int
- standardButtonDurations [ebiten.StandardGamepadButtonMax + 1]int
- }
- type touchState struct {
- duration int
- x int
- y int
- }
- type inputState struct {
- keyDurations [ebiten.KeyMax + 1]int
- prevKeyDurations [ebiten.KeyMax + 1]int
- mouseButtonDurations [ebiten.MouseButtonMax + 1]int
- prevMouseButtonDurations [ebiten.MouseButtonMax + 1]int
- gamepadStates map[ebiten.GamepadID]gamepadState
- prevGamepadStates map[ebiten.GamepadID]gamepadState
- touchStates map[ebiten.TouchID]touchState
- prevTouchStates map[ebiten.TouchID]touchState
- gamepadIDsBuf []ebiten.GamepadID
- touchIDsBuf []ebiten.TouchID
- m sync.RWMutex
- }
- var theInputState = &inputState{
- gamepadStates: map[ebiten.GamepadID]gamepadState{},
- prevGamepadStates: map[ebiten.GamepadID]gamepadState{},
- touchStates: map[ebiten.TouchID]touchState{},
- prevTouchStates: map[ebiten.TouchID]touchState{},
- }
- func init() {
- hook.AppendHookOnBeforeUpdate(func() error {
- theInputState.update()
- return nil
- })
- }
- func (i *inputState) update() {
- i.m.Lock()
- defer i.m.Unlock()
- // Keyboard
- copy(i.prevKeyDurations[:], i.keyDurations[:])
- for idx := range i.keyDurations {
- if ebiten.IsKeyPressed(ebiten.Key(idx)) {
- i.keyDurations[idx]++
- } else {
- i.keyDurations[idx] = 0
- }
- }
- // Mouse
- copy(i.prevMouseButtonDurations[:], i.mouseButtonDurations[:])
- for idx := range i.mouseButtonDurations {
- if ebiten.IsMouseButtonPressed(ebiten.MouseButton(idx)) {
- i.mouseButtonDurations[idx]++
- } else {
- i.mouseButtonDurations[idx] = 0
- }
- }
- // Gamepads
- // Copy the gamepad states.
- clear(i.prevGamepadStates)
- for id, s := range i.gamepadStates {
- i.prevGamepadStates[id] = s
- }
- i.gamepadIDsBuf = ebiten.AppendGamepadIDs(i.gamepadIDsBuf[:0])
- for _, id := range i.gamepadIDsBuf {
- state := i.gamepadStates[id]
- for b := range i.gamepadStates[id].buttonDurations {
- if ebiten.IsGamepadButtonPressed(id, ebiten.GamepadButton(b)) {
- state.buttonDurations[b]++
- } else {
- state.buttonDurations[b] = 0
- }
- }
- for b := range i.gamepadStates[id].standardButtonDurations {
- if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButton(b)) {
- state.standardButtonDurations[b]++
- } else {
- state.standardButtonDurations[b] = 0
- }
- }
- i.gamepadStates[id] = state
- }
- // Remove disconnected gamepads.
- for id := range i.gamepadStates {
- if !slices.Contains(i.gamepadIDsBuf, id) {
- delete(i.gamepadStates, id)
- }
- }
- // Touches
- // Copy the touch durations and positions.
- clear(i.prevTouchStates)
- for id, state := range i.touchStates {
- i.prevTouchStates[id] = state
- }
- i.touchIDsBuf = ebiten.AppendTouchIDs(i.touchIDsBuf[:0])
- for _, id := range i.touchIDsBuf {
- state := i.touchStates[id]
- state.duration++
- state.x, state.y = ebiten.TouchPosition(id)
- i.touchStates[id] = state
- }
- // Remove released touches.
- for id := range i.touchStates {
- if !slices.Contains(i.touchIDsBuf, id) {
- delete(i.touchStates, id)
- }
- }
- }
- // AppendPressedKeys append currently pressed keyboard keys to keys and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendPressedKeys must be called in a game's Update, not Draw.
- //
- // AppendPressedKeys is concurrent safe.
- func AppendPressedKeys(keys []ebiten.Key) []ebiten.Key {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- for i, d := range theInputState.keyDurations {
- if d == 0 {
- continue
- }
- keys = append(keys, ebiten.Key(i))
- }
- return keys
- }
- // PressedKeys returns a set of currently pressed keyboard keys.
- //
- // PressedKeys must be called in a game's Update, not Draw.
- //
- // Deprecated: as of v2.2. Use AppendPressedKeys instead.
- func PressedKeys() []ebiten.Key {
- return AppendPressedKeys(nil)
- }
- // AppendJustPressedKeys append just pressed keyboard keys to keys and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustPressedKeys must be called in a game's Update, not Draw.
- //
- // AppendJustPressedKeys is concurrent safe.
- func AppendJustPressedKeys(keys []ebiten.Key) []ebiten.Key {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- for i, d := range theInputState.keyDurations {
- if d != 1 {
- continue
- }
- keys = append(keys, ebiten.Key(i))
- }
- return keys
- }
- // AppendJustReleasedKeys append just released keyboard keys to keys and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustReleasedKeys must be called in a game's Update, not Draw.
- //
- // AppendJustReleasedKeys is concurrent safe.
- func AppendJustReleasedKeys(keys []ebiten.Key) []ebiten.Key {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- for i := range theInputState.keyDurations {
- if theInputState.keyDurations[i] != 0 {
- continue
- }
- if theInputState.prevKeyDurations[i] == 0 {
- continue
- }
- keys = append(keys, ebiten.Key(i))
- }
- return keys
- }
- // IsKeyJustPressed returns a boolean value indicating
- // whether the given key is pressed just in the current tick.
- //
- // IsKeyJustPressed must be called in a game's Update, not Draw.
- //
- // IsKeyJustPressed is concurrent safe.
- func IsKeyJustPressed(key ebiten.Key) bool {
- return KeyPressDuration(key) == 1
- }
- // IsKeyJustReleased returns a boolean value indicating
- // whether the given key is released just in the current tick.
- //
- // IsKeyJustReleased must be called in a game's Update, not Draw.
- //
- // IsKeyJustReleased is concurrent safe.
- func IsKeyJustReleased(key ebiten.Key) bool {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- return theInputState.keyDurations[key] == 0 && theInputState.prevKeyDurations[key] > 0
- }
- // KeyPressDuration returns how long the key is pressed in ticks (Update).
- //
- // KeyPressDuration must be called in a game's Update, not Draw.
- //
- // KeyPressDuration is concurrent safe.
- func KeyPressDuration(key ebiten.Key) int {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- return theInputState.keyDurations[key]
- }
- // IsMouseButtonJustPressed returns a boolean value indicating
- // whether the given mouse button is pressed just in the current tick.
- //
- // IsMouseButtonJustPressed must be called in a game's Update, not Draw.
- //
- // IsMouseButtonJustPressed is concurrent safe.
- func IsMouseButtonJustPressed(button ebiten.MouseButton) bool {
- return MouseButtonPressDuration(button) == 1
- }
- // IsMouseButtonJustReleased returns a boolean value indicating
- // whether the given mouse button is released just in the current tick.
- //
- // IsMouseButtonJustReleased must be called in a game's Update, not Draw.
- //
- // IsMouseButtonJustReleased is concurrent safe.
- func IsMouseButtonJustReleased(button ebiten.MouseButton) bool {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- return theInputState.mouseButtonDurations[button] == 0 && theInputState.prevMouseButtonDurations[button] > 0
- }
- // MouseButtonPressDuration returns how long the mouse button is pressed in ticks (Update).
- //
- // MouseButtonPressDuration must be called in a game's Update, not Draw.
- //
- // MouseButtonPressDuration is concurrent safe.
- func MouseButtonPressDuration(button ebiten.MouseButton) int {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- return theInputState.mouseButtonDurations[button]
- }
- // AppendJustConnectedGamepadIDs appends gamepad IDs that are connected just in the current tick to gamepadIDs,
- // and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustConnectedGamepadIDs must be called in a game's Update, not Draw.
- //
- // AppendJustConnectedGamepadIDs is concurrent safe.
- func AppendJustConnectedGamepadIDs(gamepadIDs []ebiten.GamepadID) []ebiten.GamepadID {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- origLen := len(gamepadIDs)
- for id := range theInputState.gamepadStates {
- if _, ok := theInputState.prevGamepadStates[id]; !ok {
- gamepadIDs = append(gamepadIDs, id)
- }
- }
- slices.Sort(gamepadIDs[origLen:])
- return gamepadIDs
- }
- // JustConnectedGamepadIDs returns gamepad IDs that are connected just in the current tick.
- //
- // JustConnectedGamepadIDs must be called in a game's Update, not Draw.
- //
- // Deprecated: as of v2.2. Use AppendJustConnectedGamepadIDs instead.
- func JustConnectedGamepadIDs() []ebiten.GamepadID {
- return AppendJustConnectedGamepadIDs(nil)
- }
- // IsGamepadJustDisconnected returns a boolean value indicating
- // whether the gamepad of the given id is released just in the current tick.
- //
- // IsGamepadJustDisconnected must be called in a game's Update, not Draw.
- //
- // IsGamepadJustDisconnected is concurrent safe.
- func IsGamepadJustDisconnected(id ebiten.GamepadID) bool {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- _, current := theInputState.gamepadStates[id]
- _, prev := theInputState.prevGamepadStates[id]
- return !current && prev
- }
- // AppendPressedGamepadButtons append currently pressed gamepad buttons to buttons and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendPressedGamepadButtons must be called in a game's Update, not Draw.
- //
- // AppendPressedGamepadButtons is concurrent safe.
- func AppendPressedGamepadButtons(id ebiten.GamepadID, buttons []ebiten.GamepadButton) []ebiten.GamepadButton {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return buttons
- }
- for b, d := range state.buttonDurations {
- if d == 0 {
- continue
- }
- buttons = append(buttons, ebiten.GamepadButton(b))
- }
- return buttons
- }
- // AppendJustPressedGamepadButtons append just pressed gamepad buttons to buttons and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustPressedGamepadButtons must be called in a game's Update, not Draw.
- //
- // AppendJustPressedGamepadButtons is concurrent safe.
- func AppendJustPressedGamepadButtons(id ebiten.GamepadID, buttons []ebiten.GamepadButton) []ebiten.GamepadButton {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return buttons
- }
- for b, d := range state.buttonDurations {
- if d != 1 {
- continue
- }
- buttons = append(buttons, ebiten.GamepadButton(b))
- }
- return buttons
- }
- // AppendJustReleasedGamepadButtons append just released gamepad buttons to buttons and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustReleasedGamepadButtons must be called in a game's Update, not Draw.
- //
- // AppendJustReleasedGamepadButtons is concurrent safe.
- func AppendJustReleasedGamepadButtons(id ebiten.GamepadID, buttons []ebiten.GamepadButton) []ebiten.GamepadButton {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return buttons
- }
- prevState, ok := theInputState.prevGamepadStates[id]
- if !ok {
- return buttons
- }
- for b := range state.buttonDurations {
- if state.buttonDurations[b] != 0 {
- continue
- }
- if prevState.buttonDurations[b] == 0 {
- continue
- }
- buttons = append(buttons, ebiten.GamepadButton(b))
- }
- return buttons
- }
- // IsGamepadButtonJustPressed returns a boolean value indicating
- // whether the given gamepad button of the gamepad id is pressed just in the current tick.
- //
- // IsGamepadButtonJustPressed must be called in a game's Update, not Draw.
- //
- // IsGamepadButtonJustPressed is concurrent safe.
- func IsGamepadButtonJustPressed(id ebiten.GamepadID, button ebiten.GamepadButton) bool {
- return GamepadButtonPressDuration(id, button) == 1
- }
- // IsGamepadButtonJustReleased returns a boolean value indicating
- // whether the given gamepad button of the gamepad id is released just in the current tick.
- //
- // IsGamepadButtonJustReleased must be called in a game's Update, not Draw.
- //
- // IsGamepadButtonJustReleased is concurrent safe.
- func IsGamepadButtonJustReleased(id ebiten.GamepadID, button ebiten.GamepadButton) bool {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return false
- }
- prevState, ok := theInputState.prevGamepadStates[id]
- if !ok {
- return false
- }
- return state.buttonDurations[button] == 0 && prevState.buttonDurations[button] > 0
- }
- // GamepadButtonPressDuration returns how long the gamepad button of the gamepad id is pressed in ticks (Update).
- //
- // GamepadButtonPressDuration must be called in a game's Update, not Draw.
- //
- // GamepadButtonPressDuration is concurrent safe.
- func GamepadButtonPressDuration(id ebiten.GamepadID, button ebiten.GamepadButton) int {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return 0
- }
- return state.buttonDurations[button]
- }
- // AppendPressedStandardGamepadButtons append currently pressed standard gamepad buttons to buttons and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendPressedStandardGamepadButtons must be called in a game's Update, not Draw.
- //
- // AppendPressedStandardGamepadButtons is concurrent safe.
- func AppendPressedStandardGamepadButtons(id ebiten.GamepadID, buttons []ebiten.StandardGamepadButton) []ebiten.StandardGamepadButton {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return buttons
- }
- for i, d := range state.standardButtonDurations {
- if d == 0 {
- continue
- }
- buttons = append(buttons, ebiten.StandardGamepadButton(i))
- }
- return buttons
- }
- // AppendJustPressedStandardGamepadButtons append just pressed standard gamepad buttons to buttons and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustPressedStandardGamepadButtons must be called in a game's Update, not Draw.
- //
- // AppendJustPressedStandardGamepadButtons is concurrent safe.
- func AppendJustPressedStandardGamepadButtons(id ebiten.GamepadID, buttons []ebiten.StandardGamepadButton) []ebiten.StandardGamepadButton {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return buttons
- }
- for b, d := range state.standardButtonDurations {
- if d != 1 {
- continue
- }
- buttons = append(buttons, ebiten.StandardGamepadButton(b))
- }
- return buttons
- }
- // AppendJustReleasedStandardGamepadButtons append just released standard gamepad buttons to buttons and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustReleasedStandardGamepadButtons must be called in a game's Update, not Draw.
- //
- // AppendJustReleasedStandardGamepadButtons is concurrent safe.
- func AppendJustReleasedStandardGamepadButtons(id ebiten.GamepadID, buttons []ebiten.StandardGamepadButton) []ebiten.StandardGamepadButton {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return buttons
- }
- prevState, ok := theInputState.prevGamepadStates[id]
- if !ok {
- return buttons
- }
- for b := range state.standardButtonDurations {
- if state.standardButtonDurations[b] != 0 {
- continue
- }
- if prevState.standardButtonDurations[b] == 0 {
- continue
- }
- buttons = append(buttons, ebiten.StandardGamepadButton(b))
- }
- return buttons
- }
- // IsStandardGamepadButtonJustPressed returns a boolean value indicating
- // whether the given standard gamepad button of the gamepad id is pressed just in the current tick.
- //
- // IsStandardGamepadButtonJustPressed must be called in a game's Update, not Draw.
- //
- // IsStandardGamepadButtonJustPressed is concurrent safe.
- func IsStandardGamepadButtonJustPressed(id ebiten.GamepadID, button ebiten.StandardGamepadButton) bool {
- return StandardGamepadButtonPressDuration(id, button) == 1
- }
- // IsStandardGamepadButtonJustReleased returns a boolean value indicating
- // whether the given standard gamepad button of the gamepad id is released just in the current tick.
- //
- // IsStandardGamepadButtonJustReleased must be called in a game's Update, not Draw.
- //
- // IsStandardGamepadButtonJustReleased is concurrent safe.
- func IsStandardGamepadButtonJustReleased(id ebiten.GamepadID, button ebiten.StandardGamepadButton) bool {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return false
- }
- prevState, ok := theInputState.prevGamepadStates[id]
- if !ok {
- return false
- }
- return state.standardButtonDurations[button] == 0 && prevState.standardButtonDurations[button] > 0
- }
- // StandardGamepadButtonPressDuration returns how long the standard gamepad button of the gamepad id is pressed in ticks (Update).
- //
- // StandardGamepadButtonPressDuration must be called in a game's Update, not Draw.
- //
- // StandardGamepadButtonPressDuration is concurrent safe.
- func StandardGamepadButtonPressDuration(id ebiten.GamepadID, button ebiten.StandardGamepadButton) int {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state, ok := theInputState.gamepadStates[id]
- if !ok {
- return 0
- }
- return state.standardButtonDurations[button]
- }
- // AppendJustPressedTouchIDs append touch IDs that are created just in the current tick to touchIDs,
- // and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustPressedTouchIDs must be called in a game's Update, not Draw.
- //
- // AppendJustPressedTouchIDs is concurrent safe.
- func AppendJustPressedTouchIDs(touchIDs []ebiten.TouchID) []ebiten.TouchID {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- origLen := len(touchIDs)
- for id, state := range theInputState.touchStates {
- if state.duration != 1 {
- continue
- }
- touchIDs = append(touchIDs, id)
- }
- slices.Sort(touchIDs[origLen:])
- return touchIDs
- }
- // JustPressedTouchIDs returns touch IDs that are created just in the current tick.
- //
- // JustPressedTouchIDs must be called in a game's Update, not Draw.
- //
- // Deprecated: as of v2.2. Use AppendJustPressedTouchIDs instead.
- func JustPressedTouchIDs() []ebiten.TouchID {
- return AppendJustPressedTouchIDs(nil)
- }
- // AppendJustReleasedTouchIDs append touch IDs that are released just in the current tick to touchIDs,
- // and returns the extended buffer.
- // Giving a slice that already has enough capacity works efficiently.
- //
- // AppendJustReleasedTouchIDs must be called in a game's Update, not Draw.
- //
- // AppendJustReleasedTouchIDs is concurrent safe.
- func AppendJustReleasedTouchIDs(touchIDs []ebiten.TouchID) []ebiten.TouchID {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- origLen := len(touchIDs)
- // Iterate prevTouchStates instead of touchStates since touchStates doesn't have released touches.
- for id, state := range theInputState.prevTouchStates {
- if state.duration == 0 {
- continue
- }
- if theInputState.touchStates[id].duration != 0 {
- continue
- }
- touchIDs = append(touchIDs, id)
- }
- slices.Sort(touchIDs[origLen:])
- return touchIDs
- }
- // IsTouchJustReleased returns a boolean value indicating
- // whether the given touch is released just in the current tick.
- //
- // IsTouchJustReleased must be called in a game's Update, not Draw.
- //
- // IsTouchJustReleased is concurrent safe.
- func IsTouchJustReleased(id ebiten.TouchID) bool {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- current := theInputState.touchStates[id]
- prev := theInputState.prevTouchStates[id]
- return current.duration == 0 && prev.duration > 0
- }
- // TouchPressDuration returns how long the touch remains in ticks (Update).
- //
- // TouchPressDuration must be called in a game's Update, not Draw.
- //
- // TouchPressDuration is concurrent safe.
- func TouchPressDuration(id ebiten.TouchID) int {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- return theInputState.touchStates[id].duration
- }
- // TouchPositionInPreviousTick returns the position in the previous tick.
- // If the touch is a just-released touch, TouchPositionInPreviousTick returns the last position of the touch.
- //
- // TouchPositionInPreviousTick must be called in a game's Update, not Draw.
- //
- // TouchJustReleasedPosition is concurrent safe.
- func TouchPositionInPreviousTick(id ebiten.TouchID) (int, int) {
- theInputState.m.RLock()
- defer theInputState.m.RUnlock()
- state := theInputState.prevTouchStates[id]
- return state.x, state.y
- }
|