diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2025-04-29 10:24:39 -0500 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-05-01 09:46:15 -0500 |
| commit | 39a35e9f498b39926d4e4a4cfbe154bb8395e9eb (patch) | |
| tree | 995d2450bae27a33e4de9cbd7cd17dff1cb18e6d | |
| parent | 644d848e9e67f408166141027a098234beb8f442 (diff) | |
| download | muhqs-game-39a35e9f498b39926d4e4a4cfbe154bb8395e9eb.tar.gz muhqs-game-39a35e9f498b39926d4e4a4cfbe154bb8395e9eb.zip | |
add a singleton to handle touch inputs
| -rw-r--r-- | go/TODO | 1 | ||||
| -rw-r--r-- | go/ui/touchManager.go | 220 |
2 files changed, 221 insertions, 0 deletions
@@ -7,5 +7,6 @@ * implement target selection for triggers * implement draft * implement game log +* implement LongTap as hover equivalent * finish AIs * tyrant diff --git a/go/ui/touchManager.go b/go/ui/touchManager.go new file mode 100644 index 00000000..cedff82c --- /dev/null +++ b/go/ui/touchManager.go @@ -0,0 +1,220 @@ +// Copyright 2021 Huw Griffiths +// Copyright 2025 Florian Fischer +// +// 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 ui + +import ( + "math" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/inpututil" +) + +type TouchType int + +const ( + _tap TouchType = iota + 1 + _pan + _pinch +) + +var TouchTypes = struct { + Tap TouchType + Pan TouchType + Pinch TouchType +}{Tap: _tap, + Pan: _pan, + Pinch: _pinch} + +// TouchInput is a "manager" for touch input and provides logical encapsulations of +// touch interactions like panning, pinching, and tapping. +type TouchInput struct { + touchIDs []ebiten.TouchID + touches map[ebiten.TouchID]*touch + + Pinch *pinch + Pan *pan + Taps []Tap +} + +var ( + TouchManager *TouchInput +) + +func init() { + TouchManager = NewTouchInput() +} + +func NewTouchInput() *TouchInput { + return &TouchInput{ + touches: map[ebiten.TouchID]*touch{}, + } +} + +// hypotenuse of a right triangle. +func hypotenuse(xa, ya, xb, yb int) float64 { + x := math.Abs(float64(xa - xb)) + y := math.Abs(float64(ya - yb)) + return math.Sqrt(x*x + y*y) +} + +// Update the touch input manager. +// After a call to Update, if we have two touches, then we have a pinch, if we +// have one touch, held longer than (N)ms, then we have a pan. +// If we have quickly released presses, then they are represented in taps. +func (in *TouchInput) Update() error { + in.Taps = []Tap{} + + // What's gone? + for id, t := range in.touches { + if inpututil.IsTouchJustReleased(id) { + if in.Pinch != nil && (id == in.Pinch.id1 || id == in.Pinch.id2) { + // FIXME: what about this frame's movement? + in.Pinch = nil + } + if in.Pan != nil && id == in.Pan.id { + // FIXME: what about this frame's movement? + in.Pan = nil + } + + // If this one has not been touched long, or moved far, then it's a tap. + diff := hypotenuse(t.originX, t.originY, t.currX, t.currY) + if !t.wasPinch && !t.isPan && (t.duration <= 75 || diff < 5) { + in.Taps = append(in.Taps, Tap{ + X: t.currX, + Y: t.currY, + }) + } + + delete(in.touches, id) + } + } + + // What's new? + in.touchIDs = inpututil.AppendJustPressedTouchIDs(in.touchIDs[:0]) + for _, id := range in.touchIDs { + x, y := ebiten.TouchPosition(id) + in.touches[ebiten.TouchID(id)] = &touch{ + originX: x, originY: y, + currX: x, currY: y, + } + } + + // What's going on? + in.touchIDs = ebiten.AppendTouchIDs(in.touchIDs[:0]) + for _, id := range in.touchIDs { + t := in.touches[id] + t.duration = inpututil.TouchPressDuration(id) + t.currX, t.currY = ebiten.TouchPosition(id) + } + + if len(in.touches) == 2 { + // Potential pinch? + // 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 := in.touchIDs[0], in.touchIDs[1] + t1, t2 := in.touches[id1], in.touches[id2] + originDiff := hypotenuse(t1.originX, t1.originY, t2.originX, t2.originY) + currDiff := hypotenuse(t1.currX, t1.currY, t2.currX, t2.currY) + if in.Pinch == nil && in.Pan == nil && math.Abs(originDiff-currDiff) > 3 { + t1.wasPinch = true + t2.wasPinch = true + in.Pinch = &pinch{ + id1: id1, + id2: id2, + originH: originDiff, + prevH: originDiff, + } + } + } else if len(in.touches) == 1 { + // Potential pan. + id := in.touchIDs[0] + t := in.touches[id] + if !t.wasPinch && in.Pan == nil && in.Pinch == nil { + diff := math.Abs(hypotenuse(t.originX, t.originY, t.currX, t.currY)) + if diff > 0.25 { + t.isPan = true + in.Pan = &pan{ + id: id, + originX: t.originX, + originY: t.originY, + prevX: t.originX, + prevY: t.originY, + } + } + } + } + + return nil +} + +type touch struct { + originX, originY int + currX, currY int + duration int + wasPinch, isPan bool +} + +type pinch struct { + id1, id2 ebiten.TouchID + originH float64 + prevH float64 +} + +func (p *pinch) currentH() float64 { + x1, y1 := ebiten.TouchPosition(p.id1) + x2, y2 := ebiten.TouchPosition(p.id2) + return hypotenuse(x1, y1, x2, y2) +} + +func (p *pinch) Total() float64 { + return -(p.currentH() - p.originH) +} + +// Incremental zoom that has occurred in this frame. +func (p *pinch) Incremental() float64 { + curr := p.currentH() + delta := curr - p.prevH + p.prevH = curr + return -delta +} + +type pan struct { + id ebiten.TouchID + + prevX, prevY int + originX, originY int +} + +// Total panning that has occurred since this pan started. +func (p *pan) Total() (float64, float64) { + currX, currY := ebiten.TouchPosition(p.id) + return float64(currX - p.originX), float64(currY - p.originY) +} + +// Incremental panning that has occurred in this frame. +func (p *pan) Incremental() (float64, float64) { + currX, currY := ebiten.TouchPosition(p.id) + deltaX, deltaY := currX-p.prevX, currY-p.prevY + + p.prevX, p.prevY = currX, currY + + return float64(deltaX), float64(deltaY) +} + +type Tap struct { + X, Y int +} |
