mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 11:48:55 +01:00
Add Textures
This commit is contained in:
parent
322d0f1924
commit
4e6aeaa291
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
131
example/blocks/textures.go
Normal 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]
|
||||
}
|
@ -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})
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user