examples/drag: Add touches (#506)

This commit is contained in:
Hajime Hoshi 2018-05-10 03:19:07 +09:00
parent 5677c8a916
commit 252f4430d8

View File

@ -23,6 +23,7 @@ import (
_ "image/png" _ "image/png"
"log" "log"
"math/rand" "math/rand"
"time"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/ebitenutil"
@ -30,6 +31,10 @@ import (
"github.com/hajimehoshi/ebiten/inpututil" "github.com/hajimehoshi/ebiten/inpututil"
) )
func init() {
rand.Seed(time.Now().UnixNano())
}
const ( const (
screenWidth = 320 screenWidth = 320
screenHeight = 240 screenHeight = 240
@ -82,63 +87,104 @@ func (s *Sprite) Draw(screen *ebiten.Image, dx, dy int, alpha float64) {
screen.DrawImage(s.image, op) screen.DrawImage(s.image, op)
} }
type DragPhase int // StrokeSource represents a input device to provide strokes.
type StrokeSource interface {
Position() (int, int)
IsJustReleased() bool
}
const ( // MouseStrokeSource is a StrokeSource implementation of mouse.
DragPhaseNone DragPhase = iota type MouseStrokeSource struct{}
DragPhaseStart
DragPhaseDrag
DragPhaseEnd
)
// DragState manages the current drag state. func (m *MouseStrokeSource) Position() (int, int) {
type DragState struct { return ebiten.CursorPosition()
phase DragPhase }
func (m *MouseStrokeSource) IsJustReleased() bool {
return inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft)
}
// TouchStrokeSource is a StrokeSource implementation of touch.
type TouchStrokeSource struct {
ID int
}
func (t *TouchStrokeSource) Position() (int, int) {
return ebiten.TouchPosition(t.ID)
}
func (t *TouchStrokeSource) IsJustReleased() bool {
return inpututil.IsTouchJustReleased(t.ID)
}
// Stroke manages the current drag state by mouse.
type Stroke struct {
source StrokeSource
// initX and initY represents the position when dragging starts. // initX and initY represents the position when dragging starts.
// initX and initY values don't make sense when phase is DragPhaseNone.
initX int initX int
initY int initY int
// currentX and currentY represents the current position // currentX and currentY represents the current position
// initX and initY values don't make sense when phase is DragPhaseNone.
currentX int currentX int
currentY int currentY int
released bool
// draggingObject represents a object (sprite in this case)
// that is being dragged.
draggingObject interface{}
} }
func (d *DragState) Update() { func NewStroke(source StrokeSource) *Stroke {
switch d.phase { cx, cy := source.Position()
case DragPhaseNone: return &Stroke{
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { source: source,
cx, cy := ebiten.CursorPosition() initX: cx,
d.phase = DragPhaseStart initY: cy,
d.initX = cx currentX: cx,
d.initY = cy currentY: cy,
d.currentX = cx
d.currentY = cy
}
case DragPhaseStart:
d.phase = DragPhaseDrag
case DragPhaseDrag:
x, y := ebiten.CursorPosition()
d.currentX = x
d.currentY = y
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) {
d.phase = DragPhaseEnd
}
case DragPhaseEnd:
d.phase = DragPhaseNone
} }
} }
type Game struct { func (s *Stroke) Update() {
dragState DragState if s.released {
sprites []*Sprite return
}
if s.source.IsJustReleased() {
s.released = true
return
}
x, y := s.source.Position()
s.currentX = x
s.currentY = y
}
// draggingSpriteIndex represents the index of the sprites func (s *Stroke) IsReleased() bool {
// that is being dragged. If draggingSpriteIndex is -1, return s.released
// there is not such sprite. }
draggingSpriteIndex int
func (s *Stroke) Position() (int, int) {
return s.currentX, s.currentY
}
func (s *Stroke) PositionDiff() (int, int) {
dx := s.currentX - s.initX
dy := s.currentY - s.initY
return dx, dy
}
func (s *Stroke) DraggingObject() interface{} {
return s.draggingObject
}
func (s *Stroke) SetDraggingObject(object interface{}) {
s.draggingObject = object
}
type Game struct {
strokes map[*Stroke]struct{}
sprites []*Sprite
} }
var theGame *Game var theGame *Game
@ -173,40 +219,67 @@ func init() {
// Initialize the game. // Initialize the game.
theGame = &Game{ theGame = &Game{
sprites: sprites, strokes: map[*Stroke]struct{}{},
draggingSpriteIndex: -1, sprites: sprites,
} }
} }
func (g *Game) update(screen *ebiten.Image) error { func (g *Game) spriteAt(x, y int) *Sprite {
g.dragState.Update() // As the sprites are ordered from back to front,
switch g.dragState.phase { // search the clicked/touched sprite in reverse order.
case DragPhaseStart: for i := len(g.sprites) - 1; i >= 0; i-- {
if g.draggingSpriteIndex == -1 { s := g.sprites[i]
// As the sprites are ordered from back to front, if s.In(x, y) {
// search the clicked/touched sprite in reverse order. return s
for i := len(g.sprites) - 1; i >= 0; i-- {
s := g.sprites[i]
if s.In(g.dragState.initX, g.dragState.initY) {
g.draggingSpriteIndex = i
break
}
}
} }
case DragPhaseEnd: }
if g.draggingSpriteIndex != -1 { return nil
dx := g.dragState.currentX - g.dragState.initX }
dy := g.dragState.currentY - g.dragState.initY
g.sprites[g.draggingSpriteIndex].MoveBy(dx, dy)
// Move the dragged sprite to the front. func (g *Game) updateStroke(stroke *Stroke) {
s := g.sprites[g.draggingSpriteIndex] stroke.Update()
g.sprites = append( if !stroke.IsReleased() {
g.sprites[:g.draggingSpriteIndex], return
g.sprites[g.draggingSpriteIndex+1:]...) }
g.sprites = append(g.sprites, s)
g.draggingSpriteIndex = -1 s := stroke.DraggingObject().(*Sprite)
if s == nil {
return
}
s.MoveBy(stroke.PositionDiff())
index := -1
for i, ss := range g.sprites {
if ss == s {
index = i
break
}
}
// Move the dragged sprite to the front.
g.sprites = append(g.sprites[:index], g.sprites[index+1:]...)
g.sprites = append(g.sprites, s)
stroke.SetDraggingObject(nil)
}
func (g *Game) update(screen *ebiten.Image) error {
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
s := NewStroke(&MouseStrokeSource{})
s.SetDraggingObject(g.spriteAt(s.Position()))
g.strokes[s] = struct{}{}
}
for _, id := range inpututil.JustPressedTouchIDs() {
s := NewStroke(&TouchStrokeSource{id})
s.SetDraggingObject(g.spriteAt(s.Position()))
g.strokes[s] = struct{}{}
}
for s := range g.strokes {
g.updateStroke(s)
if s.IsReleased() {
delete(g.strokes, s)
} }
} }
@ -214,19 +287,19 @@ func (g *Game) update(screen *ebiten.Image) error {
return nil return nil
} }
for i, s := range g.sprites { ss := map[*Sprite]*Stroke{}
if i == g.draggingSpriteIndex { for s := range g.strokes {
s.Draw(screen, 0, 0, 0.5) ss[s.DraggingObject().(*Sprite)] = s
}
for _, s := range g.sprites {
if stroke, ok := ss[s]; ok {
dx, dy := stroke.PositionDiff()
s.Draw(screen, dx, dy, 0.5)
} else { } else {
s.Draw(screen, 0, 0, 1) s.Draw(screen, 0, 0, 1)
} }
} }
if g.draggingSpriteIndex != -1 {
s := g.sprites[g.draggingSpriteIndex]
dx := g.dragState.currentX - g.dragState.initX
dy := g.dragState.currentY - g.dragState.initY
s.Draw(screen, dx, dy, 1)
}
ebitenutil.DebugPrint(screen, "Drag & Drop the sprites!") ebitenutil.DebugPrint(screen, "Drag & Drop the sprites!")