Add Textures

This commit is contained in:
Hajime Hoshi 2014-05-03 15:25:41 +09:00
parent 322d0f1924
commit 4e6aeaa291
12 changed files with 220 additions and 128 deletions

View File

@ -93,11 +93,11 @@ func (f *Field) Flush() {
}
}
func (f *Field) Draw(context graphics.Context, geo matrix.Geometry) {
func (f *Field) Draw(context graphics.Context, textures *Textures, geo matrix.Geometry) {
blocks := make([][]BlockType, len(f.blocks))
for i, blockCol := range f.blocks {
blocks[i] = make([]BlockType, len(blockCol))
copy(blocks[i], blockCol[:])
}
drawBlocks(context, blocks, geo)
drawBlocks(context, textures, blocks, geo)
}

View File

@ -17,8 +17,13 @@ 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"]
func drawText(
context graphics.Context,
textures *Textures,
str string,
x, y, scale int,
clr color.Color) {
fontTextureId := textures.GetTexture("font")
parts := []graphics.TexturePart{}
locationX := 0
@ -48,7 +53,12 @@ func drawText(context graphics.Context, str string, x, y, scale int, clr color.C
context.Texture(fontTextureId).Draw(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)
func drawTextWithShadow(
context graphics.Context,
textures *Textures,
str string,
x, y, scale int,
clr color.Color) {
drawText(context, textures, str, x+1, y+1, scale, color.RGBA{0, 0, 0, 0x80})
drawText(context, textures, str, x, y, scale, clr)
}

View File

@ -3,9 +3,6 @@ package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/ui"
"image"
_ "image/png"
"os"
)
type Size struct {
@ -13,20 +10,12 @@ type Size struct {
Height int
}
const ScreenWidth = 256
const ScreenHeight = 240
// TODO: Should they be global??
var texturePaths = map[string]string{}
var renderTargetSizes = map[string]Size{}
// TODO: Make this not a global variable.
var drawInfo = struct {
textures map[string]graphics.TextureId
renderTargets map[string]graphics.RenderTargetId
}{
textures: map[string]graphics.TextureId{},
renderTargets: map[string]graphics.RenderTargetId{},
}
const ScreenWidth = 256
const ScreenHeight = 240
type GameState struct {
SceneManager *SceneManager
@ -36,65 +25,26 @@ type GameState struct {
type Game struct {
sceneManager *SceneManager
input *Input
}
func loadImage(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return img, nil
}
func (game *Game) startLoadingTextures(textureFactory graphics.TextureFactory) {
for tag, path := range texturePaths {
tag := tag
path := path
go func() {
img, err := loadImage(path)
if err != nil {
panic(err)
}
textureFactory.CreateTexture(tag, img, graphics.FilterNearest)
}()
}
for tag, size := range renderTargetSizes {
tag := tag
size := size
go func() {
textureFactory.CreateRenderTarget(tag, size.Width, size.Height, graphics.FilterNearest)
}()
}
textures *Textures
}
func NewGame(textureFactory graphics.TextureFactory) *Game {
game := &Game{
sceneManager: NewSceneManager(NewTitleScene()),
input: NewInput(),
textures: NewTextures(textureFactory),
}
for name, path := range texturePaths {
game.textures.RequestTexture(name, path)
}
for name, size := range renderTargetSizes {
game.textures.RequestRenderTarget(name, size)
}
game.startLoadingTextures(textureFactory)
return game
}
func (game *Game) HandleEvent(e interface{}) {
switch e := e.(type) {
case graphics.TextureCreatedEvent:
if e.Error != nil {
panic(e.Error)
}
drawInfo.textures[e.Tag.(string)] = e.Id
case graphics.RenderTargetCreatedEvent:
if e.Error != nil {
panic(e.Error)
}
drawInfo.renderTargets[e.Tag.(string)] = e.Id
case ui.KeyStateUpdatedEvent:
game.input.UpdateKeys(e.Keys)
case ui.MouseStateUpdatedEvent:
@ -102,12 +52,16 @@ func (game *Game) HandleEvent(e interface{}) {
}
func (game *Game) isInitialized() bool {
if len(drawInfo.textures) < len(texturePaths) {
for name, _ := range texturePaths {
if !game.textures.Has(name) {
return false
}
if len(drawInfo.renderTargets) < len(renderTargetSizes) {
}
for name, _ := range renderTargetSizes {
if !game.textures.Has(name) {
return false
}
}
return true
}
@ -126,5 +80,5 @@ func (game *Game) Draw(context graphics.Context) {
if !game.isInitialized() {
return
}
game.sceneManager.Draw(context)
game.sceneManager.Draw(context, game.textures)
}

View File

@ -97,10 +97,10 @@ func (s *GameScene) Update(state *GameState) {
}
}
func (s *GameScene) Draw(context graphics.Context) {
func (s *GameScene) Draw(context graphics.Context, textures *Textures) {
context.Fill(0xff, 0xff, 0xff)
field := drawInfo.textures["empty"]
field := textures.GetTexture("empty")
geoMat := matrix.IdentityGeometry()
geoMat.Scale(
float64(fieldWidth)/float64(emptyWidth),
@ -117,11 +117,12 @@ func (s *GameScene) Draw(context graphics.Context) {
geoMat = matrix.IdentityGeometry()
geoMat.Translate(20, 20)
s.field.Draw(context, geoMat)
s.field.Draw(context, textures, geoMat)
if s.currentPiece != nil {
s.currentPiece.Draw(
context,
textures,
20, 20,
s.currentPieceX,
s.currentPieceY,

View File

@ -123,7 +123,11 @@ const blockHeight = 10
const fieldBlockNumX = 10
const fieldBlockNumY = 20
func drawBlocks(context graphics.Context, blocks [][]BlockType, geo matrix.Geometry) {
func drawBlocks(
context graphics.Context,
textures *Textures,
blocks [][]BlockType,
geo matrix.Geometry) {
parts := []graphics.TexturePart{}
for i, blockCol := range blocks {
for j, block := range blockCol {
@ -143,7 +147,7 @@ func drawBlocks(context graphics.Context, blocks [][]BlockType, geo matrix.Geome
})
}
}
blocksTexture := drawInfo.textures["blocks"]
blocksTexture := textures.GetTexture("blocks")
context.Texture(blocksTexture).Draw(parts, geo, matrix.IdentityColor())
}
@ -205,7 +209,12 @@ func (p *Piece) AbsorbInto(field *Field, x, y int, angle Angle) {
}
}
func (p *Piece) Draw(context graphics.Context, fieldX, fieldY int, pieceX, pieceY int, angle Angle) {
func (p *Piece) Draw(
context graphics.Context,
textures *Textures,
fieldX, fieldY int,
pieceX, pieceY int,
angle Angle) {
size := len(p.blocks)
blocks := make([][]BlockType, size)
for i, _ := range p.blocks {
@ -222,5 +231,5 @@ func (p *Piece) Draw(context graphics.Context, fieldX, fieldY int, pieceX, piece
y := fieldY + pieceY*blockHeight
geoMat.Translate(float64(x), float64(y))
drawBlocks(context, blocks, geoMat)
drawBlocks(context, textures, blocks, geoMat)
}

View File

@ -14,7 +14,7 @@ func init() {
type Scene interface {
Update(state *GameState)
Draw(context graphics.Context)
Draw(context graphics.Context, textures *Textures)
}
const transitionMaxCount = 20
@ -45,20 +45,20 @@ func (s *SceneManager) Update(state *GameState) {
}
}
func (s *SceneManager) Draw(context graphics.Context) {
func (s *SceneManager) Draw(context graphics.Context, textures *Textures) {
if s.transitionCount == -1 {
s.current.Draw(context)
s.current.Draw(context, textures)
return
}
from := drawInfo.renderTargets["scene_manager_transition_from"]
from := textures.GetRenderTarget("scene_manager_transition_from")
context.SetOffscreen(from)
context.Clear()
s.current.Draw(context)
s.current.Draw(context, textures)
to := drawInfo.renderTargets["scene_manager_transition_to"]
to := textures.GetRenderTarget("scene_manager_transition_to")
context.SetOffscreen(to)
context.Clear()
s.next.Draw(context)
s.next.Draw(context, textures)
context.ResetOffscreen()
color := matrix.IdentityColor()

131
example/blocks/textures.go Normal file
View File

@ -0,0 +1,131 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"image"
_ "image/png"
"os"
"sync"
)
type namePath struct {
name string
path string
}
type nameSize struct {
name string
size Size
}
type Textures struct {
textureFactory graphics.TextureFactory
texturePaths chan namePath
renderTargetSizes chan nameSize
textures map[string]graphics.TextureId
renderTargets map[string]graphics.RenderTargetId
sync.RWMutex
}
func NewTextures(textureFactory graphics.TextureFactory) *Textures {
textures := &Textures{
textureFactory: textureFactory,
texturePaths: make(chan namePath),
renderTargetSizes: make(chan nameSize),
textures: map[string]graphics.TextureId{},
renderTargets: map[string]graphics.RenderTargetId{},
}
go textures.loop()
return textures
}
func loadImage(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return img, nil
}
func (t *Textures) loop() {
for {
select {
case p := <-t.texturePaths:
name := p.name
path := p.path
go func() {
img, err := loadImage(path)
if err != nil {
panic(err)
}
ch := t.textureFactory.CreateTexture(
img,
graphics.FilterNearest)
go func() {
e := <-ch
if e.Error != nil {
panic(e.Error)
}
t.Lock()
defer t.Unlock()
t.textures[name] = e.Id
}()
}()
case s := <-t.renderTargetSizes:
name := s.name
size := s.size
go func() {
ch := t.textureFactory.CreateRenderTarget(
size.Width,
size.Height,
graphics.FilterNearest)
go func() {
e := <-ch
if e.Error != nil {
panic(e.Error)
}
t.Lock()
defer t.Unlock()
t.renderTargets[name] = e.Id
}()
}()
}
}
}
func (t *Textures) RequestTexture(name string, path string) {
t.texturePaths <- namePath{name, path}
}
func (t *Textures) RequestRenderTarget(name string, size Size) {
t.renderTargetSizes <- nameSize{name, size}
}
func (t *Textures) Has(name string) bool {
t.RLock()
defer t.RUnlock()
_, ok := t.textures[name]
if ok {
return true
}
_, ok = t.renderTargets[name]
return ok
}
func (t *Textures) GetTexture(name string) graphics.TextureId {
t.RLock()
defer t.RUnlock()
return t.textures[name]
}
func (t *Textures) GetRenderTarget(name string) graphics.RenderTargetId {
t.RLock()
defer t.RUnlock()
return t.renderTargets[name]
}

View File

@ -26,21 +26,21 @@ func (s *TitleScene) Update(state *GameState) {
}
}
func (s *TitleScene) Draw(context graphics.Context) {
drawTitleBackground(context, s.count)
drawLogo(context, "BLOCKS")
func (s *TitleScene) Draw(context graphics.Context, textures *Textures) {
drawTitleBackground(context, textures, s.count)
drawLogo(context, textures, "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})
drawTextWithShadow(context, textures, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff})
}
func drawTitleBackground(context graphics.Context, c int) {
func drawTitleBackground(context graphics.Context, textures *Textures, c int) {
const textureWidth = 32
const textureHeight = 32
backgroundTextureId := drawInfo.textures["background"]
backgroundTextureId := textures.GetTexture("background")
parts := []graphics.TexturePart{}
for j := -1; j < ScreenHeight/textureHeight+1; j++ {
for i := 0; i < ScreenWidth/textureWidth+1; i++ {
@ -60,10 +60,10 @@ func drawTitleBackground(context graphics.Context, c int) {
context.Texture(backgroundTextureId).Draw(parts, geo, clr)
}
func drawLogo(context graphics.Context, str string) {
func drawLogo(context graphics.Context, textures *Textures, str string) {
scale := 4
textWidth := textWidth(str) * scale
x := (ScreenWidth - textWidth) / 2
y := 32
drawTextWithShadow(context, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff})
drawTextWithShadow(context, textures, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff})
}

View File

@ -38,15 +38,12 @@ func main() {
go func() {
defer close(quit)
textureFactoryEvents := textureFactory.Events()
windowEvents := window.Events()
var game Game = blocks.NewGame(textureFactory)
frameTime := time.Duration(int64(time.Second) / int64(fps))
tick := time.Tick(frameTime)
for {
select {
case e := <-textureFactoryEvents:
game.HandleEvent(e)
case e := <-windowEvents:
game.HandleEvent(e)
if _, ok := e.(ui.WindowClosedEvent); ok {

View File

@ -77,11 +77,11 @@ func (c *Context) Fill(r, g, b uint8) {
}
func (c *Context) Texture(id graphics.TextureId) graphics.Drawer {
return &TextureWithContext{id, c}
return &textureWithContext{id, c}
}
func (c *Context) RenderTarget(id graphics.RenderTargetId) graphics.Drawer {
return &TextureWithContext{c.ids.toTexture(id), c}
return &textureWithContext{c.ids.toTexture(id), c}
}
func (c *Context) ResetOffscreen() {
@ -92,12 +92,12 @@ func (c *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) {
c.currentId = renderTargetId
}
type TextureWithContext struct {
type textureWithContext struct {
id graphics.TextureId
context *Context
}
func (t *TextureWithContext) Draw(
func (t *textureWithContext) Draw(
parts []graphics.TexturePart,
geo matrix.Geometry,
color matrix.Color) {

View File

@ -18,19 +18,16 @@ type TextureId int
type RenderTargetId int
type TextureCreatedEvent struct {
Tag interface{}
Id TextureId
Error error
}
type RenderTargetCreatedEvent struct {
Tag interface{}
Id RenderTargetId
Error error
}
type TextureFactory interface {
CreateRenderTarget(tag interface{}, width, height int, filter Filter)
CreateTexture(tag interface{}, img image.Image, filter Filter)
Events() <-chan interface{}
CreateRenderTarget(width, height int, filter Filter) <-chan RenderTargetCreatedEvent
CreateTexture(img image.Image, filter Filter) <-chan TextureCreatedEvent
}

View File

@ -16,7 +16,6 @@ import (
type sharedContext struct {
inited chan struct{}
events chan interface{}
funcs chan func()
funcsDone chan struct{}
gameWindows chan *GameWindow
@ -72,15 +71,10 @@ func (t *sharedContext) createGameWindow(width, height, scale int, title string)
return w
}
func (t *sharedContext) Events() <-chan interface{} {
if t.events != nil {
return t.events
}
t.events = make(chan interface{})
return t.events
}
func (t *sharedContext) CreateTexture(tag interface{}, img image.Image, filter graphics.Filter) {
func (t *sharedContext) CreateTexture(
img image.Image,
filter graphics.Filter) <-chan graphics.TextureCreatedEvent {
ch := make(chan graphics.TextureCreatedEvent)
go func() {
<-t.inited
var id graphics.TextureId
@ -88,18 +82,19 @@ func (t *sharedContext) CreateTexture(tag interface{}, img image.Image, filter g
t.useGLContext(func() {
id, err = opengl.CreateTexture(img, filter)
})
if t.events == nil {
return
}
t.events <- graphics.TextureCreatedEvent{
Tag: tag,
ch <- graphics.TextureCreatedEvent{
Id: id,
Error: err,
}
close(ch)
}()
return ch
}
func (t *sharedContext) CreateRenderTarget(tag interface{}, width, height int, filter graphics.Filter) {
func (t *sharedContext) CreateRenderTarget(
width, height int,
filter graphics.Filter) <-chan graphics.RenderTargetCreatedEvent {
ch := make(chan graphics.RenderTargetCreatedEvent)
go func() {
<-t.inited
var id graphics.RenderTargetId
@ -107,13 +102,11 @@ func (t *sharedContext) CreateRenderTarget(tag interface{}, width, height int, f
t.useGLContext(func() {
id, err = opengl.CreateRenderTarget(width, height, filter)
})
if t.events == nil {
return
}
t.events <- graphics.RenderTargetCreatedEvent{
Tag: tag,
ch <- graphics.RenderTargetCreatedEvent{
Id: id,
Error: err,
}
close(ch)
}()
return ch
}