mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-02-09 17:43:18 +01:00
blocks: Flushing blocks
This commit is contained in:
parent
9288c9dcc9
commit
083dafa627
@ -18,8 +18,12 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxFlushCount = 30
|
||||||
|
|
||||||
type Field struct {
|
type Field struct {
|
||||||
blocks [fieldBlockNumX][fieldBlockNumY]BlockType
|
blocks [fieldBlockNumX][fieldBlockNumY]BlockType
|
||||||
|
flushCount int
|
||||||
|
onEndFlushing func(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewField() *Field {
|
func NewField() *Field {
|
||||||
@ -75,16 +79,44 @@ func (f *Field) RotatePieceRight(piece *Piece, x, y int, angle Angle) Angle {
|
|||||||
return angle.RotateRight()
|
return angle.RotateRight()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Field) AbsorbPiece(piece *Piece, x, y int, angle Angle) int {
|
func (f *Field) AbsorbPiece(piece *Piece, x, y int, angle Angle) {
|
||||||
piece.AbsorbInto(f, x, y, angle)
|
piece.AbsorbInto(f, x, y, angle)
|
||||||
return f.Flush()
|
if f.flushable() {
|
||||||
|
f.flushCount = maxFlushCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field) Flushing() bool {
|
||||||
|
return 0 < f.flushCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field) SetEndFlushing(fn func(lines int)) {
|
||||||
|
f.onEndFlushing = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field) flushable() bool {
|
||||||
|
for j := fieldBlockNumY - 1; 0 <= j; j-- {
|
||||||
|
if f.flushableLine(j) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field) flushableLine(j int) bool {
|
||||||
|
for i := 0; i < fieldBlockNumX; i++ {
|
||||||
|
if f.blocks[i][j] == BlockTypeNone {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Field) setBlock(x, y int, blockType BlockType) {
|
func (f *Field) setBlock(x, y int, blockType BlockType) {
|
||||||
f.blocks[x][y] = blockType
|
f.blocks[x][y] = blockType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Field) Flush() int {
|
func (f *Field) endFlushing() int {
|
||||||
flushedLineNum := 0
|
flushedLineNum := 0
|
||||||
for j := fieldBlockNumY - 1; 0 <= j; j-- {
|
for j := fieldBlockNumY - 1; 0 <= j; j-- {
|
||||||
if f.flushLine(j + flushedLineNum) {
|
if f.flushLine(j + flushedLineNum) {
|
||||||
@ -111,11 +143,53 @@ func (f *Field) flushLine(j int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Field) Update() error {
|
||||||
|
if 0 <= f.flushCount {
|
||||||
|
f.flushCount--
|
||||||
|
if f.flushCount == 0 {
|
||||||
|
l := f.endFlushing()
|
||||||
|
if f.onEndFlushing != nil {
|
||||||
|
f.onEndFlushing(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field) flushingColor() ebiten.ColorM {
|
||||||
|
clr := ebiten.ColorM{}
|
||||||
|
alpha := (float64(f.flushCount) / maxFlushCount) / 2
|
||||||
|
clr.Concat(ebiten.ScaleColor(1, 1, 1, alpha))
|
||||||
|
r := (1 - float64(f.flushCount)/maxFlushCount) * 2
|
||||||
|
g := (1 - float64(f.flushCount)/maxFlushCount) / 2
|
||||||
|
b := (1 - float64(f.flushCount)/maxFlushCount) / 2
|
||||||
|
clr.Concat(ebiten.TranslateColor(r, g, b, 0))
|
||||||
|
return clr
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Field) Draw(r *ebiten.Image, x, y int) error {
|
func (f *Field) Draw(r *ebiten.Image, x, y int) error {
|
||||||
blocks := make([][]BlockType, len(f.blocks))
|
blocks := make([][]BlockType, len(f.blocks))
|
||||||
for i, blockCol := range f.blocks {
|
flushingBlocks := make([][]BlockType, len(f.blocks))
|
||||||
blocks[i] = make([]BlockType, len(blockCol))
|
for i := 0; i < fieldBlockNumX; i++ {
|
||||||
copy(blocks[i], blockCol[:])
|
blocks[i] = make([]BlockType, fieldBlockNumY)
|
||||||
|
flushingBlocks[i] = make([]BlockType, fieldBlockNumY)
|
||||||
}
|
}
|
||||||
return drawBlocks(r, blocks, x, y)
|
for j := 0; j < fieldBlockNumY; j++ {
|
||||||
|
if f.flushableLine(j) {
|
||||||
|
for i := 0; i < fieldBlockNumX; i++ {
|
||||||
|
flushingBlocks[i][j] = f.blocks[i][j]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < fieldBlockNumX; i++ {
|
||||||
|
blocks[i][j] = f.blocks[i][j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := drawBlocks(r, blocks, x, y, ebiten.ColorM{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := drawBlocks(r, flushingBlocks, x, y, f.flushingColor()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ const charWidth = 8
|
|||||||
const charHeight = 8
|
const charHeight = 8
|
||||||
|
|
||||||
func textWidth(str string) int {
|
func textWidth(str string) int {
|
||||||
|
// TODO: Take care about '\n'
|
||||||
return charWidth * len(str)
|
return charWidth * len(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,10 @@ func (s *GameScene) addScore(lines int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *GameScene) Update(state *GameState) error {
|
func (s *GameScene) Update(state *GameState) error {
|
||||||
|
s.field.Update()
|
||||||
|
|
||||||
if s.gameover {
|
if s.gameover {
|
||||||
|
// TODO: Go back to the title by pressing something
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,12 +148,15 @@ func (s *GameScene) Update(state *GameState) error {
|
|||||||
s.nextPiece = s.choosePiece()
|
s.nextPiece = s.choosePiece()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moved := false
|
||||||
|
piece := s.currentPiece
|
||||||
|
angle := s.currentPieceAngle
|
||||||
|
|
||||||
// Move piece by user input.
|
// Move piece by user input.
|
||||||
|
if !s.field.Flushing() {
|
||||||
piece := s.currentPiece
|
piece := s.currentPiece
|
||||||
x := s.currentPieceX
|
x := s.currentPieceX
|
||||||
y := s.currentPieceY
|
y := s.currentPieceY
|
||||||
angle := s.currentPieceAngle
|
|
||||||
moved := false
|
|
||||||
if state.Input.StateForKey(ebiten.KeySpace) == 1 {
|
if state.Input.StateForKey(ebiten.KeySpace) == 1 {
|
||||||
s.currentPieceAngle = s.field.RotatePieceRight(piece, x, y, angle)
|
s.currentPieceAngle = s.field.RotatePieceRight(piece, x, y, angle)
|
||||||
moved = angle != s.currentPieceAngle
|
moved = angle != s.currentPieceAngle
|
||||||
@ -170,38 +176,54 @@ func (s *GameScene) Update(state *GameState) error {
|
|||||||
s.score++
|
s.score++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Drop the current piece with gravity.
|
// Drop the current piece with gravity.
|
||||||
|
if !s.field.Flushing() {
|
||||||
|
y := s.currentPieceY
|
||||||
|
angle := s.currentPieceAngle
|
||||||
s.currentPieceYCarry += 2*s.level() + 1
|
s.currentPieceYCarry += 2*s.level() + 1
|
||||||
for 60 <= s.currentPieceYCarry {
|
for 60 <= s.currentPieceYCarry {
|
||||||
s.currentPieceYCarry -= 60
|
s.currentPieceYCarry -= 60
|
||||||
s.currentPieceY = s.field.DropPiece(piece, s.currentPieceX, s.currentPieceY, angle)
|
s.currentPieceY = s.field.DropPiece(piece, s.currentPieceX, s.currentPieceY, angle)
|
||||||
moved = y != s.currentPieceY
|
moved = y != s.currentPieceY
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if moved {
|
if moved {
|
||||||
s.landingCount = 0
|
s.landingCount = 0
|
||||||
} else if !s.field.PieceDroppable(piece, s.currentPieceX, s.currentPieceY, angle) {
|
} else if !s.field.Flushing() && !s.field.PieceDroppable(piece, s.currentPieceX, s.currentPieceY, angle) {
|
||||||
if 0 < state.Input.StateForKey(ebiten.KeyDown) {
|
if 0 < state.Input.StateForKey(ebiten.KeyDown) {
|
||||||
s.landingCount += 10
|
s.landingCount += 10
|
||||||
} else {
|
} else {
|
||||||
s.landingCount++
|
s.landingCount++
|
||||||
}
|
}
|
||||||
if maxLandingCount <= s.landingCount {
|
if maxLandingCount <= s.landingCount {
|
||||||
lines := s.field.AbsorbPiece(piece, s.currentPieceX, s.currentPieceY, angle)
|
s.field.AbsorbPiece(piece, s.currentPieceX, s.currentPieceY, angle)
|
||||||
|
if s.field.Flushing() {
|
||||||
|
s.field.SetEndFlushing(func(lines int) {
|
||||||
s.lines += lines
|
s.lines += lines
|
||||||
if 0 < lines {
|
if 0 < lines {
|
||||||
s.addScore(lines)
|
s.addScore(lines)
|
||||||
}
|
}
|
||||||
|
s.goNextPiece()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
s.goNextPiece()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GameScene) goNextPiece() {
|
||||||
s.initCurrentPiece(s.nextPiece)
|
s.initCurrentPiece(s.nextPiece)
|
||||||
s.nextPiece = s.choosePiece()
|
s.nextPiece = s.choosePiece()
|
||||||
s.landingCount = 0
|
s.landingCount = 0
|
||||||
if s.currentPiece.Collides(s.field, s.currentPieceX, s.currentPieceY, s.currentPieceAngle) {
|
if s.currentPiece.Collides(s.field, s.currentPieceX, s.currentPieceY, s.currentPieceAngle) {
|
||||||
s.gameover = true
|
s.gameover = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GameScene) Draw(r *ebiten.Image) error {
|
func (s *GameScene) Draw(r *ebiten.Image) error {
|
||||||
@ -252,7 +274,7 @@ func (s *GameScene) Draw(r *ebiten.Image) error {
|
|||||||
if err := s.field.Draw(r, fieldX, fieldY); err != nil {
|
if err := s.field.Draw(r, fieldX, fieldY); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s.currentPiece != nil {
|
if s.currentPiece != nil && !s.field.Flushing() {
|
||||||
x := fieldX + s.currentPieceX*blockWidth
|
x := fieldX + s.currentPieceX*blockWidth
|
||||||
y := fieldY + s.currentPieceY*blockHeight
|
y := fieldY + s.currentPieceY*blockHeight
|
||||||
if err := s.currentPiece.Draw(r, x, y, s.currentPieceAngle); err != nil {
|
if err := s.currentPiece.Draw(r, x, y, s.currentPieceAngle); err != nil {
|
||||||
|
@ -143,7 +143,7 @@ const blockHeight = 10
|
|||||||
const fieldBlockNumX = 10
|
const fieldBlockNumX = 10
|
||||||
const fieldBlockNumY = 20
|
const fieldBlockNumY = 20
|
||||||
|
|
||||||
func drawBlocks(r *ebiten.Image, blocks [][]BlockType, x, y int) error {
|
func drawBlocks(r *ebiten.Image, blocks [][]BlockType, x, y int, clr ebiten.ColorM) error {
|
||||||
parts := []ebiten.ImagePart{}
|
parts := []ebiten.ImagePart{}
|
||||||
for i, blockCol := range blocks {
|
for i, blockCol := range blocks {
|
||||||
for j, block := range blockCol {
|
for j, block := range blockCol {
|
||||||
@ -161,6 +161,7 @@ func drawBlocks(r *ebiten.Image, blocks [][]BlockType, x, y int) error {
|
|||||||
}
|
}
|
||||||
return r.DrawImage(imageBlocks, &ebiten.DrawImageOptions{
|
return r.DrawImage(imageBlocks, &ebiten.DrawImageOptions{
|
||||||
Parts: parts,
|
Parts: parts,
|
||||||
|
ColorM: clr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,5 +243,5 @@ func (p *Piece) Draw(r *ebiten.Image, x, y int, angle Angle) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return drawBlocks(r, blocks, x, y)
|
return drawBlocks(r, blocks, x, y, ebiten.ColorM{})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user