image -> billboard
This commit is contained in:
parent
2c94fc918c
commit
a625845d30
8 changed files with 159 additions and 53 deletions
|
@ -31,7 +31,7 @@ func (r *AnimRef) Load(assets fs.FS) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Slow path: load from gobz file
|
// Slow path: load from gobz file
|
||||||
if err := loadGobz(&r.anim, assets, r.Path); err != nil {
|
if err := LoadGobz(&r.anim, assets, r.Path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
animCache[assetKey{assets, r.Path}] = r.anim
|
animCache[assetKey{assets, r.Path}] = r.anim
|
||||||
|
|
|
@ -26,7 +26,7 @@ type assetKey struct {
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadGobz(dst interface{}, assets fs.FS, path string) error {
|
func LoadGobz(dst interface{}, assets fs.FS, path string) error {
|
||||||
f, err := assets.Open(path)
|
f, err := assets.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -39,9 +39,9 @@ func loadGobz(dst interface{}, assets fs.FS, path string) error {
|
||||||
return gob.NewDecoder(gz).Decode(dst)
|
return gob.NewDecoder(gz).Decode(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveGobz takes an object, gob-encodes it, gzips it, and writes to disk.
|
// SaveGobz takes an object, gob-encodes it, gzips it, and writes to disk.
|
||||||
// This requires running on something with a disk to write to (not JS)
|
// This requires running on something with a disk to write to (not JS)
|
||||||
func saveGobz(src interface{}, name string) error {
|
func SaveGobz(src interface{}, name string) error {
|
||||||
f, err := os.CreateTemp(".", name)
|
f, err := os.CreateTemp(".", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
46
engine/billboard.go
Normal file
46
engine/billboard.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure Image satisfies interfaces.
|
||||||
|
var (
|
||||||
|
_ Identifier = &Billboard{}
|
||||||
|
_ Drawer = &Billboard{}
|
||||||
|
_ DrawOrderer = &Billboard{}
|
||||||
|
_ ParallaxScaler = &Billboard{}
|
||||||
|
_ Scanner = &Billboard{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gob.Register(&Billboard{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Billboard draws an image at a position.
|
||||||
|
type Billboard struct {
|
||||||
|
ID
|
||||||
|
Hidden
|
||||||
|
Parallax
|
||||||
|
Pos image.Point
|
||||||
|
Src ImageRef
|
||||||
|
ZOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws the image.
|
||||||
|
func (b *Billboard) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
|
if b.Hidden {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var geom ebiten.GeoM
|
||||||
|
geom.Translate(float64(b.Pos.X), float64(b.Pos.Y))
|
||||||
|
geom.Concat(opts.GeoM)
|
||||||
|
opts.GeoM = geom
|
||||||
|
screen.DrawImage(b.Src.Image(), &opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan returns a slice containing Src.
|
||||||
|
func (b *Billboard) Scan() []interface{} { return []interface{}{&b.Src} }
|
|
@ -1,46 +0,0 @@
|
||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/gob"
|
|
||||||
"image"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ensure Image satisfies interfaces.
|
|
||||||
var (
|
|
||||||
_ Identifier = &Image{}
|
|
||||||
_ Drawer = &Image{}
|
|
||||||
_ DrawOrderer = &Image{}
|
|
||||||
_ ParallaxScaler = &Image{}
|
|
||||||
_ Scanner = &Image{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
gob.Register(&Image{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image draws an image at a position.
|
|
||||||
type Image struct {
|
|
||||||
ID
|
|
||||||
Hidden
|
|
||||||
Parallax
|
|
||||||
Pos image.Point
|
|
||||||
Src ImageRef
|
|
||||||
ZOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw draws the image.
|
|
||||||
func (i *Image) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
|
||||||
if i.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var geom ebiten.GeoM
|
|
||||||
geom.Translate(float64(i.Pos.X), float64(i.Pos.Y))
|
|
||||||
geom.Concat(opts.GeoM)
|
|
||||||
opts.GeoM = geom
|
|
||||||
screen.DrawImage(i.Src.Image(), &opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan returns a slice containing Src.
|
|
||||||
func (i *Image) Scan() []interface{} { return []interface{}{&i.Src} }
|
|
|
@ -37,7 +37,7 @@ type SceneRef struct {
|
||||||
// Load loads the scene from the file.
|
// Load loads the scene from the file.
|
||||||
func (r *SceneRef) Load(assets fs.FS) error {
|
func (r *SceneRef) Load(assets fs.FS) error {
|
||||||
sc := new(Scene)
|
sc := new(Scene)
|
||||||
if err := loadGobz(sc, assets, r.Path); err != nil {
|
if err := LoadGobz(sc, assets, r.Path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.scene = sc
|
r.scene = sc
|
||||||
|
@ -45,7 +45,7 @@ func (r *SceneRef) Load(assets fs.FS) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save saves the scene to a file in the current directory.
|
// Save saves the scene to a file in the current directory.
|
||||||
func (r *SceneRef) Save() error { return saveGobz(r.scene, filepath.Base(r.Path)) }
|
func (r *SceneRef) Save() error { return SaveGobz(r.scene, filepath.Base(r.Path)) }
|
||||||
|
|
||||||
// The rest of the methods forward to r.scene, as such they will
|
// The rest of the methods forward to r.scene, as such they will
|
||||||
// panic if the scene isn't loaded.
|
// panic if the scene isn't loaded.
|
||||||
|
|
Binary file not shown.
|
@ -26,7 +26,6 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Awakeman struct {
|
type Awakeman struct {
|
||||||
engine.ID
|
|
||||||
engine.Disabled
|
engine.Disabled
|
||||||
engine.Sprite
|
engine.Sprite
|
||||||
|
|
||||||
|
@ -44,6 +43,9 @@ type Awakeman struct {
|
||||||
animIdleLeft, animIdleRight, animRunLeft, animRunRight, animWalkLeft, animWalkRight *engine.Anim
|
animIdleLeft, animIdleRight, animRunLeft, animRunRight, animWalkLeft, animWalkRight *engine.Anim
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ident returns "awakeman". There should be only one!
|
||||||
|
func (aw *Awakeman) Ident() string { return "awakeman" }
|
||||||
|
|
||||||
func (aw *Awakeman) Update() error {
|
func (aw *Awakeman) Update() error {
|
||||||
if aw.Disabled {
|
if aw.Disabled {
|
||||||
return nil
|
return nil
|
||||||
|
|
104
main.go
104
main.go
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -16,6 +18,10 @@ func main() {
|
||||||
ebiten.SetWindowSize(640, 480)
|
ebiten.SetWindowSize(640, 480)
|
||||||
ebiten.SetWindowTitle("TODO")
|
ebiten.SetWindowTitle("TODO")
|
||||||
|
|
||||||
|
if false {
|
||||||
|
writeLevel1()
|
||||||
|
}
|
||||||
|
|
||||||
g := &engine.Game{
|
g := &engine.Game{
|
||||||
ScreenHeight: 240,
|
ScreenHeight: 240,
|
||||||
ScreenWidth: 320,
|
ScreenWidth: 320,
|
||||||
|
@ -46,3 +52,101 @@ func main() {
|
||||||
log.Fatalf("Game error: %v", err)
|
log.Fatalf("Game error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeLevel1 dumps a test level into level1.gobz
|
||||||
|
func writeLevel1() {
|
||||||
|
redTileAnim := &engine.Anim{Frames: []engine.AnimFrame{
|
||||||
|
{Frame: 3, Duration: 12},
|
||||||
|
{Frame: 4, Duration: 12},
|
||||||
|
{Frame: 5, Duration: 12},
|
||||||
|
{Frame: 6, Duration: 12},
|
||||||
|
}}
|
||||||
|
greenTileAnim := &engine.Anim{Frames: []engine.AnimFrame{
|
||||||
|
{Frame: 0, Duration: 16},
|
||||||
|
{Frame: 1, Duration: 16},
|
||||||
|
{Frame: 2, Duration: 16},
|
||||||
|
}}
|
||||||
|
denseTiles := [][]engine.Tile{
|
||||||
|
{engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.StaticTile(9)},
|
||||||
|
{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil},
|
||||||
|
{nil, nil, nil, nil, engine.StaticTile(9), engine.StaticTile(9), engine.StaticTile(9), nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil},
|
||||||
|
{engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.AnimatedTile{Animer: redTileAnim.Copy()}, engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8)},
|
||||||
|
{engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.AnimatedTile{Animer: redTileAnim.Copy()}, engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.AnimatedTile{Animer: greenTileAnim.Copy()}, engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7)},
|
||||||
|
{engine.StaticTile(9), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(9)},
|
||||||
|
}
|
||||||
|
tiles := make(map[image.Point]engine.Tile)
|
||||||
|
for j, row := range denseTiles {
|
||||||
|
for i, tile := range row {
|
||||||
|
if tile == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tiles[image.Pt(i, j)] = tile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
level1 := &engine.Scene{
|
||||||
|
ID: "level_1",
|
||||||
|
Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)),
|
||||||
|
Components: []interface{}{
|
||||||
|
&engine.Fill{
|
||||||
|
Color: color.Gray{100},
|
||||||
|
ZOrder: 0,
|
||||||
|
},
|
||||||
|
&engine.Billboard{
|
||||||
|
ID: "bg_image",
|
||||||
|
Parallax: 0.5,
|
||||||
|
ZOrder: 1,
|
||||||
|
Pos: image.Pt(-160, -120),
|
||||||
|
Src: engine.ImageRef{Path: "assets/space.png"},
|
||||||
|
},
|
||||||
|
&engine.Tilemap{
|
||||||
|
ID: "terrain",
|
||||||
|
ZOrder: 2,
|
||||||
|
Map: tiles,
|
||||||
|
Src: engine.ImageRef{Path: "assets/boxes.png"},
|
||||||
|
TileSize: 16,
|
||||||
|
},
|
||||||
|
&engine.SolidRect{
|
||||||
|
ID: "ceiling",
|
||||||
|
Bounds: engine.Bounds(image.Rect(0, -1, 320, 0)),
|
||||||
|
},
|
||||||
|
&engine.SolidRect{
|
||||||
|
ID: "left_wall",
|
||||||
|
Bounds: engine.Bounds(image.Rect(-1, 0, 0, 240)),
|
||||||
|
},
|
||||||
|
&engine.SolidRect{
|
||||||
|
ID: "right_wall",
|
||||||
|
Bounds: engine.Bounds(image.Rect(320, 0, 321, 240)),
|
||||||
|
},
|
||||||
|
&game.Awakeman{
|
||||||
|
CameraID: "game_camera",
|
||||||
|
ToastID: "toast",
|
||||||
|
Sprite: engine.Sprite{
|
||||||
|
Actor: engine.Actor{
|
||||||
|
CollisionDomain: "level_1",
|
||||||
|
Pos: image.Pt(100, 100),
|
||||||
|
Size: image.Pt(8, 16),
|
||||||
|
},
|
||||||
|
ZOrder: 3,
|
||||||
|
FrameOffset: image.Pt(-1, 0),
|
||||||
|
FrameSize: image.Pt(10, 16),
|
||||||
|
Src: engine.ImageRef{Path: "assets/aw.png"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := engine.SaveGobz(level1, "level1.gobz"); err != nil {
|
||||||
|
log.Fatalf("Couldn't save level1.gobz: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue