Show a piece

This commit is contained in:
Hajime Hoshi 2013-12-19 03:21:25 +09:00
parent 71e08bca41
commit bc7dd51adf
11 changed files with 425 additions and 94 deletions

23
example/blocks/field.go Normal file
View File

@ -0,0 +1,23 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
)
type Field struct {
blocks [fieldBlockNumX][fieldBlockNumY]BlockType
}
func NewField() *Field {
return &Field{}
}
func (f *Field) Draw(context graphics.Context, geo matrix.Geometry) {
blocks := make([][]BlockType, len(f.blocks))
for i, blockLine := range f.blocks {
blocks[i] = make([]BlockType, len(blockLine))
copy(blocks[i], blockLine[:])
}
drawBlocks(context, blocks, geo)
}

54
example/blocks/font.go Normal file
View File

@ -0,0 +1,54 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
"image/color"
)
func init() {
texturePaths["font"] = "images/blocks/font.png"
}
const charWidth = 8
const charHeight = 8
func textWidth(str string) int {
return charWidth * len(str)
}
func drawText(context graphics.Context, str string, x, y, scale int, clr color.Color) {
fontTextureId := drawInfo.textures["font"]
parts := []graphics.TexturePart{}
locationX := 0
locationY := 0
for _, c := range str {
if c == '\n' {
locationX = 0
locationY += charHeight
continue
}
code := int(c)
x := (code % 16) * charWidth
y := ((code - 32) / 16) * charHeight
parts = append(parts, graphics.TexturePart{
LocationX: locationX,
LocationY: locationY,
Source: graphics.Rect{x, y, charWidth, charHeight},
})
locationX += charWidth
}
geoMat := matrix.IdentityGeometry()
geoMat.Scale(float64(scale), float64(scale))
geoMat.Translate(float64(x), float64(y))
clrMat := matrix.IdentityColor()
clrMat.Scale(clr)
context.DrawTextureParts(fontTextureId, parts, geoMat, clrMat)
}
func drawTextWithShadow(context graphics.Context, str string, x, y, scale int, clr color.Color) {
drawText(context, str, x+1, y+1, scale, color.RGBA{0, 0, 0, 0x80})
drawText(context, str, x, y, scale, clr)
}

View File

@ -1,7 +1,6 @@
package blocks package blocks
import ( import (
"fmt"
"github.com/hajimehoshi/go-ebiten/graphics" "github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/ui" "github.com/hajimehoshi/go-ebiten/ui"
"image" "image"
@ -14,18 +13,13 @@ type Size struct {
Height int Height int
} }
var texturePaths = map[string]string{
"background": "images/blocks/background.png",
"font": "images/blocks/font.png",
}
var renderTargetSizes = map[string]Size{
"whole": Size{256, 254},
}
const ScreenWidth = 256 const ScreenWidth = 256
const ScreenHeight = 240 const ScreenHeight = 240
var texturePaths = map[string]string{}
var renderTargetSizes = map[string]Size{}
// TODO: Make this not a global variable.
var drawInfo = struct { var drawInfo = struct {
textures map[string]graphics.TextureId textures map[string]graphics.TextureId
renderTargets map[string]graphics.RenderTargetId renderTargets map[string]graphics.RenderTargetId
@ -34,8 +28,14 @@ var drawInfo = struct {
renderTargets: map[string]graphics.RenderTargetId{}, renderTargets: map[string]graphics.RenderTargetId{},
} }
type GameState struct {
SceneManager *SceneManager
Input *Input
}
type Game struct { type Game struct {
sceneManager *SceneManager sceneManager *SceneManager
input *Input
} }
func loadImage(path string) (image.Image, error) { func loadImage(path string) (image.Image, error) {
@ -77,6 +77,7 @@ func (game *Game) startLoadingTextures(textureFactory graphics.TextureFactory) {
func NewGame(textureFactory graphics.TextureFactory) *Game { func NewGame(textureFactory graphics.TextureFactory) *Game {
game := &Game{ game := &Game{
sceneManager: NewSceneManager(NewTitleScene()), sceneManager: NewSceneManager(NewTitleScene()),
input: NewInput(),
} }
game.startLoadingTextures(textureFactory) game.startLoadingTextures(textureFactory)
return game return game
@ -95,7 +96,7 @@ func (game *Game) HandleEvent(e interface{}) {
} }
drawInfo.renderTargets[e.Tag.(string)] = e.Id drawInfo.renderTargets[e.Tag.(string)] = e.Id
case ui.KeyStateUpdatedEvent: case ui.KeyStateUpdatedEvent:
fmt.Printf("%v\n", e.Keys) game.input.UpdateKeys(e.Keys)
case ui.MouseStateUpdatedEvent: case ui.MouseStateUpdatedEvent:
} }
} }
@ -114,7 +115,11 @@ func (game *Game) Update() {
if !game.isInitialized() { if !game.isInitialized() {
return return
} }
game.sceneManager.Update() game.input.Update()
game.sceneManager.Update(&GameState{
SceneManager: game.sceneManager,
Input: game.input,
})
} }
func (game *Game) Draw(context graphics.Context) { func (game *Game) Draw(context graphics.Context) {
@ -123,36 +128,3 @@ func (game *Game) Draw(context graphics.Context) {
} }
game.sceneManager.Draw(context) game.sceneManager.Draw(context)
} }
/*func (game *Game) drawText(g graphics.Context, text string, x, y int, clr color.Color) {
const letterWidth = 6
const letterHeight = 16
parts := []graphics.TexturePart{}
textX := 0
textY := 0
for _, c := range text {
if c == '\n' {
textX = 0
textY += letterHeight
continue
}
code := int(c)
x := (code % 32) * letterWidth
y := (code / 32) * letterHeight
source := graphics.Rect{x, y, letterWidth, letterHeight}
parts = append(parts, graphics.TexturePart{
LocationX: textX,
LocationY: textY,
Source: source,
})
textX += letterWidth
}
geometryMatrix := matrix.IdentityGeometry()
geometryMatrix.Translate(float64(x), float64(y))
colorMatrix := matrix.IdentityColor()
colorMatrix.Scale(clr)
g.DrawTextureParts(drawInfo.textures["text"], parts,
geometryMatrix, colorMatrix)
}*/

View File

@ -0,0 +1,66 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
"image/color"
"math/rand"
"time"
)
func init() {
texturePaths["empty"] = "images/blocks/empty.png"
}
type GameScene struct {
field *Field
rand *rand.Rand
currentPiece *Piece
nextPiece *Piece
}
func NewGameScene() *GameScene {
return &GameScene{
field: NewField(),
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}
const emptyWidth = 16
const emptyHeight = 16
const fieldWidth = blockWidth * fieldBlockNumX
const fieldHeight = blockHeight * fieldBlockNumY
func (s *GameScene) choosePiece() *Piece {
// Omit BlockTypeNone.
num := int(BlockTypeMax) - 1
blockType := BlockType(s.rand.Intn(num) + 1)
return Pieces[blockType]
}
func (s *GameScene) Update(state *GameState) {
if s.currentPiece == nil {
s.currentPiece = s.choosePiece()
}
}
func (s *GameScene) Draw(context graphics.Context) {
context.Fill(0xff, 0xff, 0xff)
field := drawInfo.textures["empty"]
geoMat := matrix.IdentityGeometry()
geoMat.Scale(float64(fieldWidth) / float64(emptyWidth),
float64(fieldHeight) / float64(emptyHeight))
geoMat.Translate(20, 20) // magic number?
colorMat := matrix.IdentityColor()
colorMat.Scale(color.RGBA{0, 0, 0, 0x80})
context.DrawTexture(field, geoMat, colorMat)
geoMat = matrix.IdentityGeometry()
geoMat.Translate(20, 20)
s.field.Draw(context, geoMat)
if s.currentPiece != nil {
s.currentPiece.Draw(context, geoMat)
}
}

41
example/blocks/input.go Normal file
View File

@ -0,0 +1,41 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/ui"
)
type Input struct {
states map[ui.Key]int
lastPressedKeys map[ui.Key]struct{}
}
func NewInput() *Input {
states := map[ui.Key]int{}
for key := ui.Key(0); key < ui.KeyMax; key++ {
states[key] = 0
}
return &Input{
states: states,
}
}
func (i *Input) StateForKey(key ui.Key) int {
return i.states[key]
}
func (i *Input) Update() {
for key, _ := range i.states {
if _, ok := i.lastPressedKeys[key]; !ok {
i.states[key] = 0
continue
}
i.states[key] += 1
}
}
func (i *Input) UpdateKeys(keys []ui.Key) {
i.lastPressedKeys = map[ui.Key]struct{}{}
for _, key := range keys {
i.lastPressedKeys[key] = struct{}{}
}
}

141
example/blocks/piece.go Normal file
View File

@ -0,0 +1,141 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
)
func init() {
texturePaths["blocks"] = "images/blocks/blocks.png"
}
type BlockType int
const (
BlockTypeNone BlockType = iota
BlockType1
BlockType2
BlockType3
BlockType4
BlockType5
BlockType6
BlockType7
BlockTypeMax
)
type Piece struct {
blockType BlockType
blocks [][]bool
}
func toBlocks(ints [][]int) [][]bool {
blocks := make([][]bool, len(ints))
for i, line := range ints {
blocks[i] = make([]bool, len(line))
for j, v := range line {
blocks[i][j] = v != 0
}
}
return blocks
}
var Pieces = map[BlockType]*Piece{
BlockTypeNone: nil,
BlockType1: &Piece{
blockType: BlockType1,
blocks: toBlocks([][]int{
{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0},
}),
},
BlockType2: &Piece{
blockType: BlockType2,
blocks: toBlocks([][]int{
{0, 1, 1},
{0, 1, 0},
{0, 1, 0},
}),
},
BlockType3: &Piece{
blockType: BlockType3,
blocks: toBlocks([][]int{
{0, 1, 0},
{0, 1, 1},
{0, 1, 0},
}),
},
BlockType4: &Piece{
blockType: BlockType4,
blocks: toBlocks([][]int{
{0, 1, 0},
{0, 1, 0},
{0, 1, 1},
}),
},
BlockType5: &Piece{
blockType: BlockType5,
blocks: toBlocks([][]int{
{0, 0, 1},
{0, 1, 1},
{0, 1, 0},
}),
},
BlockType6: &Piece{
blockType: BlockType6,
blocks: toBlocks([][]int{
{0, 1, 0},
{0, 1, 1},
{0, 0, 1},
}),
},
BlockType7: &Piece{
blocks: toBlocks([][]int{
{1, 1},
{1, 1},
}),
},
}
const blockWidth = 10
const blockHeight = 10
const fieldBlockNumX = 10
const fieldBlockNumY = 20
func drawBlocks(context graphics.Context, blocks [][]BlockType, geo matrix.Geometry) {
parts := []graphics.TexturePart{}
for i, blockLine := range blocks {
for j, block := range blockLine {
if block == BlockTypeNone {
continue
}
locationX := j * blockWidth
locationY := i * blockHeight
source := graphics.Rect{
(int(block) - 1) * blockWidth, 0,
blockWidth, blockHeight}
parts = append(parts,
graphics.TexturePart{
LocationX: locationX,
LocationY: locationY,
Source: source,
})
}
}
blocksTexture := drawInfo.textures["blocks"]
context.DrawTextureParts(blocksTexture, parts, geo, matrix.IdentityColor())
}
func (p *Piece) Draw(context graphics.Context, geo matrix.Geometry) {
blocks := make([][]BlockType, len(p.blocks))
for i, blockLine := range p.blocks {
blocks[i] = make([]BlockType, len(blockLine))
for j, v := range blockLine {
if v {
blocks[i][j] = p.blockType
}
}
}
drawBlocks(context, blocks, geo)
}

View File

@ -2,42 +2,74 @@ package blocks
import ( import (
"github.com/hajimehoshi/go-ebiten/graphics" "github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
) )
type GameState struct { func init() {
SceneManager *SceneManager renderTargetSizes["scene_manager_transition_from"] =
Size{ScreenWidth, ScreenHeight}
renderTargetSizes["scene_manager_transition_to"] =
Size{ScreenWidth, ScreenHeight}
} }
type Scene interface { type Scene interface {
Update(state GameState) Update(state *GameState)
Draw(context graphics.Context) Draw(context graphics.Context)
} }
const transitionMaxCount = 20
type SceneManager struct { type SceneManager struct {
current Scene current Scene
next Scene next Scene
transitionCount int
} }
func NewSceneManager(initScene Scene) *SceneManager { func NewSceneManager(initScene Scene) *SceneManager {
return &SceneManager{ return &SceneManager{
current: initScene, current: initScene,
transitionCount: -1,
} }
} }
func (s *SceneManager) Update() { func (s *SceneManager) Update(state *GameState) {
if s.next != nil { if s.transitionCount == -1 {
s.current.Update(state)
return
}
s.transitionCount++
if transitionMaxCount <= s.transitionCount {
s.current = s.next s.current = s.next
s.next = nil s.next = nil
s.transitionCount = -1
} }
s.current.Update(GameState{
SceneManager: s,
})
} }
func (s *SceneManager) Draw(context graphics.Context) { func (s *SceneManager) Draw(context graphics.Context) {
if s.transitionCount == -1 {
s.current.Draw(context)
return
}
from := drawInfo.renderTargets["scene_manager_transition_from"]
context.SetOffscreen(from)
context.Clear()
s.current.Draw(context) s.current.Draw(context)
to := drawInfo.renderTargets["scene_manager_transition_to"]
context.SetOffscreen(to)
context.Clear()
s.next.Draw(context)
context.ResetOffscreen()
color := matrix.IdentityColor()
context.DrawRenderTarget(from, matrix.IdentityGeometry(), color)
alpha := float64(s.transitionCount) / float64(transitionMaxCount)
color.Elements[3][3] = alpha
context.DrawRenderTarget(to, matrix.IdentityGeometry(), color)
} }
func (s *SceneManager) GoTo(scene Scene) { func (s *SceneManager) GoTo(scene Scene) {
s.next = scene s.next = scene
s.transitionCount = 0
} }

View File

@ -3,9 +3,14 @@ package blocks
import ( import (
"github.com/hajimehoshi/go-ebiten/graphics" "github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix" "github.com/hajimehoshi/go-ebiten/graphics/matrix"
"github.com/hajimehoshi/go-ebiten/ui"
"image/color" "image/color"
) )
func init() {
texturePaths["background"] = "images/blocks/background.png"
}
type TitleScene struct { type TitleScene struct {
count int count int
} }
@ -14,13 +19,21 @@ func NewTitleScene() *TitleScene {
return &TitleScene{} return &TitleScene{}
} }
func (s *TitleScene) Update(state GameState) { func (s *TitleScene) Update(state *GameState) {
s.count++ s.count++
if state.Input.StateForKey(ui.KeySpace) == 1 {
state.SceneManager.GoTo(NewGameScene())
}
} }
func (s *TitleScene) Draw(context graphics.Context) { func (s *TitleScene) Draw(context graphics.Context) {
drawTitleBackground(context, s.count) drawTitleBackground(context, s.count)
drawLogo(context, "BLOCKS") drawLogo(context, "BLOCKS")
message := "PRESS SPACE TO START"
x := (ScreenWidth-textWidth(message)) / 2
y := ScreenHeight-48
drawTextWithShadow(context, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff})
} }
func drawTitleBackground(context graphics.Context, c int) { func drawTitleBackground(context graphics.Context, c int) {
@ -32,50 +45,25 @@ func drawTitleBackground(context graphics.Context, c int) {
for j := -1; j < ScreenHeight/textureHeight+1; j++ { for j := -1; j < ScreenHeight/textureHeight+1; j++ {
for i := 0; i < ScreenWidth/textureWidth+1; i++ { for i := 0; i < ScreenWidth/textureWidth+1; i++ {
parts = append(parts, graphics.TexturePart{ parts = append(parts, graphics.TexturePart{
LocationX: i*textureWidth, LocationX: i * textureWidth,
LocationY: j*textureHeight, LocationY: j * textureHeight,
Source: graphics.Rect{0, 0, textureWidth, textureHeight}, Source: graphics.Rect{0, 0, textureWidth, textureHeight},
}) })
} }
} }
dx := -c % textureWidth / 2 dx := (-c/4) % textureWidth
dy := c % textureHeight / 2 dy := (c/4) % textureHeight
geo := matrix.IdentityGeometry() geo := matrix.IdentityGeometry()
geo.Translate(float64(dx), float64(dy)) geo.Translate(float64(dx), float64(dy))
context.DrawTextureParts(backgroundTextureId, parts, geo, matrix.IdentityColor()) clr := matrix.IdentityColor()
context.DrawTextureParts(backgroundTextureId, parts, geo, clr)
} }
func drawLogo(context graphics.Context, str string) { func drawLogo(context graphics.Context, str string) {
const charWidth = 8 scale := 4
const charHeight = 8 textWidth := textWidth(str) * scale
x := (ScreenWidth - textWidth) / 2
fontTextureId := drawInfo.textures["font"] y := 32
parts := []graphics.TexturePart{} drawTextWithShadow(context, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff})
locationX := 0
locationY := 0
for _, c := range str {
if c == '\n' {
locationX = 0
locationY += charHeight
continue
}
code := int(c)
x := (code % 16) * charWidth
y := ((code - 32) / 16) * charHeight
parts = append(parts, graphics.TexturePart{
LocationX: locationX,
LocationY: locationY,
Source: graphics.Rect{x, y, charWidth, charHeight},
})
locationX += charWidth
}
geo := matrix.IdentityGeometry()
geo.Scale(4, 4)
clr := matrix.IdentityColor()
clr.Scale(color.RGBA{0x00, 0x00, 0x60, 0xff})
context.DrawTextureParts(fontTextureId, parts, geo, clr)
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

View File

@ -58,15 +58,28 @@ func Monochrome() Color {
} }
} }
func (matrix *Color) Scale(clr color.Color) { func rgba(clr color.Color) (float64, float64, float64, float64) {
r, g, b, a := clr.RGBA() r, g, b, a := clr.RGBA()
rf := float64(r) / float64(math.MaxUint16) rf := float64(r) / float64(math.MaxUint16)
gf := float64(g) / float64(math.MaxUint16) gf := float64(g) / float64(math.MaxUint16)
bf := float64(b) / float64(math.MaxUint16) bf := float64(b) / float64(math.MaxUint16)
af := float64(a) / float64(math.MaxUint16) af := float64(a) / float64(math.MaxUint16)
return rf, gf, bf, af
}
func (matrix *Color) Scale(clr color.Color) {
rf, gf, bf, af := rgba(clr)
for i, e := range []float64{rf, gf, bf, af} { for i, e := range []float64{rf, gf, bf, af} {
for j := 0; j < 4; j++ { for j := 0; j < 4; j++ {
matrix.Elements[i][j] *= e matrix.Elements[i][j] *= e
} }
} }
} }
func (matrix *Color) Translate(clr color.Color) {
rf, gf, bf, af := rgba(clr)
matrix.Elements[0][4] = rf
matrix.Elements[1][4] = gf
matrix.Elements[2][4] = bf
matrix.Elements[3][4] = af
}

View File

@ -12,6 +12,7 @@ const (
KeyLeft KeyLeft
KeyRight KeyRight
KeySpace KeySpace
KeyMax
) )
type WindowSizeUpdatedEvent struct { type WindowSizeUpdatedEvent struct {