examples/2048: Pop animation

This commit is contained in:
Hajime Hoshi 2016-07-31 17:25:39 +09:00
parent c44f2819af
commit 941c24e9fd
3 changed files with 64 additions and 29 deletions

View File

@ -75,7 +75,7 @@ func (b *Board) Move(dir Dir) error {
} }
b.tasks = append(b.tasks, func() error { b.tasks = append(b.tasks, func() error {
for t := range b.tiles { for t := range b.tiles {
if t.IsAnimating() { if t.IsMoving() {
return nil return nil
} }
} }
@ -84,7 +84,7 @@ func (b *Board) Move(dir Dir) error {
b.tasks = append(b.tasks, func() error { b.tasks = append(b.tasks, func() error {
nextTiles := map[*Tile]struct{}{} nextTiles := map[*Tile]struct{}{}
for t := range b.tiles { for t := range b.tiles {
if t.IsAnimating() { if t.IsMoving() {
panic("not reach") panic("not reach")
} }
if t.next.value != 0 { if t.next.value != 0 {
@ -101,6 +101,14 @@ func (b *Board) Move(dir Dir) error {
} }
return taskTerminated return taskTerminated
}) })
b.tasks = append(b.tasks, func() error {
for t := range b.tiles {
if t.isPopping() {
return nil
}
}
return taskTerminated
})
return nil return nil
} }
@ -131,7 +139,7 @@ func (b *Board) Draw(boardImage *ebiten.Image) error {
animatingTiles := map[*Tile]struct{}{} animatingTiles := map[*Tile]struct{}{}
nonAnimatingTiles := map[*Tile]struct{}{} nonAnimatingTiles := map[*Tile]struct{}{}
for t := range b.tiles { for t := range b.tiles {
if t.IsAnimating() { if t.IsMoving() {
animatingTiles[t] = struct{}{} animatingTiles[t] = struct{}{}
} else { } else {
nonAnimatingTiles[t] = struct{}{} nonAnimatingTiles[t] = struct{}{}

View File

@ -32,9 +32,10 @@ type TileData struct {
} }
type Tile struct { type Tile struct {
current TileData current TileData
next TileData next TileData
animationCount int moveCount int
popCount int
} }
func NewTile(value int, x, y int) *Tile { func NewTile(value int, x, y int) *Tile {
@ -63,8 +64,12 @@ func (t *Tile) NextPos() (int, int) {
return t.next.x, t.next.y return t.next.x, t.next.y
} }
func (t *Tile) IsAnimating() bool { func (t *Tile) IsMoving() bool {
return 0 < t.animationCount return 0 < t.moveCount
}
func (t *Tile) isPopping() bool {
return 0 < t.popCount
} }
func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile { func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
@ -84,7 +89,7 @@ func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
func nextTileAt(tiles map[*Tile]struct{}, x, y int) *Tile { func nextTileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
var result *Tile var result *Tile
for t := range tiles { for t := range tiles {
if 0 < t.animationCount { if 0 < t.moveCount {
if t.next.x != x || t.next.y != y || t.next.value == 0 { if t.next.x != x || t.next.y != y || t.next.value == 0 {
continue continue
} }
@ -102,7 +107,8 @@ func nextTileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
} }
const ( const (
maxAnimationCount = 10 maxMovingCount = 5
maxPoppingCount = 6
) )
func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool { func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
@ -130,7 +136,7 @@ func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
if t.next != (TileData{}) { if t.next != (TileData{}) {
panic("not reach") panic("not reach")
} }
if t.IsAnimating() { if t.IsMoving() {
panic("not reach") panic("not reach")
} }
ii := i ii := i
@ -151,7 +157,7 @@ func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
if t.current.value != tt.current.value { if t.current.value != tt.current.value {
break break
} }
if 0 < tt.animationCount && tt.current.value != tt.next.value { if 0 < tt.moveCount && tt.current.value != tt.next.value {
// already merged // already merged
break break
} }
@ -167,20 +173,20 @@ func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
tt.next.value = 0 tt.next.value = 0
tt.next.x = ii tt.next.x = ii
tt.next.y = jj tt.next.y = jj
tt.animationCount = maxAnimationCount tt.moveCount = maxMovingCount
} }
next.x = ii next.x = ii
next.y = jj next.y = jj
if t.current != next { if t.current != next {
t.next = next t.next = next
t.animationCount = maxAnimationCount t.moveCount = maxMovingCount
} }
} }
} }
if !moved { if !moved {
for t := range tiles { for t := range tiles {
t.next = TileData{} t.next = TileData{}
t.animationCount = 0 t.moveCount = 0
} }
} }
return moved return moved
@ -189,7 +195,7 @@ func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
func addRandomTile(tiles map[*Tile]struct{}, size int) error { func addRandomTile(tiles map[*Tile]struct{}, size int) error {
cells := make([]bool, size*size) cells := make([]bool, size*size)
for t := range tiles { for t := range tiles {
if t.IsAnimating() { if t.IsMoving() {
panic("not reach") panic("not reach")
} }
i := t.current.x + t.current.y*size i := t.current.x + t.current.y*size
@ -218,16 +224,18 @@ func addRandomTile(tiles map[*Tile]struct{}, size int) error {
} }
func (t *Tile) Update() error { func (t *Tile) Update() error {
if t.animationCount == 0 { switch {
if t.next != (TileData{}) { case 0 < t.moveCount:
panic("not reach") t.moveCount--
if t.moveCount == 0 {
if t.current.value != t.next.value && 0 < t.next.value {
t.popCount = maxPoppingCount
}
t.current = t.next
t.next = TileData{}
} }
return nil case 0 < t.popCount:
} t.popCount--
t.animationCount--
if t.animationCount == 0 {
t.current = t.next
t.next = TileData{}
} }
return nil return nil
} }
@ -248,7 +256,11 @@ func colorToScale(clr color.Color) (float64, float64, float64, float64) {
} }
func mean(a, b int, rate float64) int { func mean(a, b int, rate float64) int {
return int(float64(a)*rate + float64(b)*(1-rate)) return int(float64(a)*(1-rate) + float64(b)*rate)
}
func meanF(a, b float64, rate float64) float64 {
return a*(1-rate) + b*rate
} }
const ( const (
@ -283,11 +295,26 @@ func (t *Tile) Draw(boardImage *ebiten.Image) error {
y := j*tileSize + (j+1)*tileMargin y := j*tileSize + (j+1)*tileMargin
nx := ni*tileSize + (ni+1)*tileMargin nx := ni*tileSize + (ni+1)*tileMargin
ny := nj*tileSize + (nj+1)*tileMargin ny := nj*tileSize + (nj+1)*tileMargin
if 0 < t.animationCount { if 0 < t.moveCount {
rate := float64(t.animationCount) / maxAnimationCount rate := 1 - float64(t.moveCount)/maxMovingCount
x = mean(x, nx, rate) x = mean(x, nx, rate)
y = mean(y, ny, rate) y = mean(y, ny, rate)
} }
if 0 < t.popCount {
const maxScale = 1.2
rate := 0.0
if maxPoppingCount*2/3 <= t.popCount {
// 0 to 1
rate = 1 - float64(t.popCount-2*maxPoppingCount/3)/float64(maxPoppingCount/3)
} else {
// 1 to 0
rate = float64(t.popCount) / float64(maxPoppingCount*2/3)
}
scale := meanF(1.0, maxScale, rate)
op.GeoM.Translate(float64(-tileSize/2), float64(-tileSize/2))
op.GeoM.Scale(scale, scale)
op.GeoM.Translate(float64(tileSize/2), float64(tileSize/2))
}
op.GeoM.Translate(float64(x), float64(y)) op.GeoM.Translate(float64(x), float64(y))
r, g, b, a := colorToScale(tileBackgroundColor(v)) r, g, b, a := colorToScale(tileBackgroundColor(v))
op.ColorM.Scale(r, g, b, a) op.ColorM.Scale(r, g, b, a)

View File

@ -42,7 +42,7 @@ func tilesToCells(tiles map[*Tile]struct{}, size int) ([]int, []int) {
for t := range tiles { for t := range tiles {
x, y := t.Pos() x, y := t.Pos()
cells[x+y*size] = t.Value() cells[x+y*size] = t.Value()
if t.IsAnimating() { if t.IsMoving() {
if t.NextValue() == 0 { if t.NextValue() == 0 {
continue continue
} }