Add sprite cache

main
Sean Hickey 2022-10-04 22:11:42 -07:00
parent 56ba80c935
commit f10ffa739d
8 changed files with 294 additions and 66 deletions

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

15
internal/game/camera.go Normal file
View File

@ -0,0 +1,15 @@
package game
import "github.com/veandco/go-sdl2/sdl"
// camera represents a view of the world. It's a projection through the window looking at the world.
// Since this is only a 2D game with SDL, the projection is relatively simple: window + camera = world.
// https://gamedev.stackexchange.com/a/123844
type camera struct {
pos *sdl.Point
}
func NewCamera() *camera {
c := camera{}
return &c
}

View File

@ -0,0 +1,79 @@
package entity
import (
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/sprite"
"github.com/veandco/go-sdl2/sdl"
)
type Sprite interface {
Draw(section *sdl.Rect, placement *sdl.Rect) error
}
type penguin struct {
renderer *sdl.Renderer
position *sdl.Point
sprite Sprite
}
func NewPenguin(renderer *sdl.Renderer) *penguin {
sprite := sprite.GetSprite("assets/images/penguin.png")
pos := sdl.Point{X: 0, Y: 0}
p := penguin{
renderer: renderer,
sprite: sprite,
position: &pos,
}
return &p
}
func (p *penguin) Draw() error {
// This is where on the screen to plop that sprite sheet chunk
placement := &sdl.Rect{X: p.position.X, Y: p.position.Y, W: 200, H: 200}
// Select the section of the spritesheet to draw
// TODO this will depend on animation cycle
section := &sdl.Rect{X: 0, Y: 0, W: 100, H: 100}
err := p.sprite.Draw(section, placement)
if err != nil {
return err
}
return nil
}
func (p *penguin) SdlDrawNoTexture() error {
sdl.Do(func() {
p.renderer.SetDrawColor(0, 0, 255, 255)
rect := &sdl.Rect{X: p.position.X, Y: p.position.Y, W: 200, H: 200}
p.renderer.DrawRect(rect)
p.renderer.FillRect(rect)
})
return nil
}
func (p *penguin) MoveTo(point *sdl.Point) {
p.position = point
}
func (p *penguin) MoveRight() {
p.position.X += 1
}
func (p *penguin) MoveLeft() {
p.position.X -= 1
}
func (p *penguin) MoveUp() {
// (0,0) is the top left, so negative y moves up
p.position.Y -= 1
}
func (p *penguin) MoveDown() {
// positive y moves down
p.position.Y += 1
}

13
internal/game/player.go Normal file
View File

@ -0,0 +1,13 @@
package game
// player represents a collection of stuff controlled by the user's input.
// It contains the camera used to view the world,
// the viewport for the part of the screen to draw to (splitscreen support),
// as well as the entity the player is currently controlling.
type player struct {
}
func NewPlayer() *player {
p := player{}
return &p
}

View File

@ -0,0 +1,84 @@
package sprite
import (
"fmt"
"log"
"github.com/veandco/go-sdl2/img"
"github.com/veandco/go-sdl2/sdl"
)
type sprite struct {
filename string
renderer *sdl.Renderer
image *sdl.Surface // the original png file
spriteSheet *sdl.Texture // the SDL texture created from the image
}
func NewSprite(renderer *sdl.Renderer, filename string) (*sprite, error) {
var err error
// Load the image file
var image *sdl.Surface
sdl.Do(func() {
image, err = img.Load(filename)
})
if err != nil {
err = fmt.Errorf("failed to load image: %w", err)
return nil, err
}
// Create the sprite sheet texture from the image
var spriteSheet *sdl.Texture
sdl.Do(func() {
spriteSheet, err = renderer.CreateTextureFromSurface(image)
})
if err != nil {
err = fmt.Errorf("failed to create texture: %w", err)
return nil, err
}
s := sprite{
filename: filename,
renderer: renderer,
image: image,
spriteSheet: spriteSheet,
}
return &s, nil
}
func (s *sprite) Cleanup() {
// Clean up image
defer func() {
if s.image != nil {
sdl.Do(func() {
s.image.Free()
})
}
}()
// Clean up spritesheet
defer func() {
if s.spriteSheet != nil {
sdl.Do(func() {
err := s.spriteSheet.Destroy()
if err != nil {
log.Printf("error destroying spritesheet %v: %v\n", s.filename, err)
}
})
}
}()
}
func (s *sprite) Draw(section *sdl.Rect, placement *sdl.Rect) error {
var err error
sdl.Do(func() {
err = s.renderer.Copy(s.spriteSheet, section, placement)
})
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,80 @@
package sprite
import (
"log"
"github.com/veandco/go-sdl2/sdl"
)
var spriteFileList []string = []string{
"assets/images/penguin.png",
}
type Sprite interface {
Draw(section *sdl.Rect, placement *sdl.Rect) error
Cleanup()
}
var SpriteCache map[string]Sprite
var DefaultSprite Sprite
func InitSpriteCache(renderer *sdl.Renderer) error {
var err error
DefaultSprite, err = createDefaultSprite(renderer)
if err != nil {
log.Printf("failed to create DefaultSprite: %v", err)
return err
}
SpriteCache = make(map[string]Sprite)
for _, filename := range spriteFileList {
s, err := NewSprite(renderer, filename)
if err != nil {
log.Printf("error creating sprite %v, using DefaultSprite: %v", filename, err)
SpriteCache[filename] = DefaultSprite
} else {
SpriteCache[filename] = s
}
}
return nil
}
func GetSprite(filename string) Sprite {
s, exists := SpriteCache[filename]
if !exists {
log.Printf("no sprite found for %v, using DefaultSprite", filename)
return DefaultSprite
}
return s
}
func CleanupSpriteCache() {
for _, v := range SpriteCache {
defer v.Cleanup()
}
}
func createDefaultSprite(renderer *sdl.Renderer) (*sprite, error) {
var err error
surface, err := sdl.CreateRGBSurface(0, 100, 100, 32, 0, 0, 0, 0)
if err != nil {
return nil, err
}
texture, err := renderer.CreateTextureFromSurface(surface)
if err != nil {
return nil, err
}
s := sprite{
renderer: renderer,
spriteSheet: texture,
}
return &s, nil
}

View File

@ -39,7 +39,7 @@ func NewWindow(title string) (*window, error) {
return gw, nil
}
func (g *window) Destroy() {
func (g *window) Cleanup() {
if g.SdlWindow != nil {
sdl.Do(func() {
err := g.SdlWindow.Destroy()

87
main.go
View File

@ -7,8 +7,9 @@ import (
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/config"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/game"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/entity"
"gitea.wisellama.rocks/Project-Ely/project-ely/internal/game/sprite"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
"github.com/veandco/go-sdl2/img"
"github.com/veandco/go-sdl2/sdl"
)
@ -59,7 +60,7 @@ func run(configMap gosimpleconf.ConfigMap) error {
err = fmt.Errorf("failed creating GameWindow: %w", err)
return err
}
defer gameWindow.Destroy()
defer gameWindow.Cleanup()
var renderer *sdl.Renderer
sdl.Do(func() {
@ -78,43 +79,16 @@ func run(configMap gosimpleconf.ConfigMap) error {
})
}()
var penguinImage *sdl.Surface
sdl.Do(func() {
penguinImage, err = img.Load("assets/images/penguin-right.png")
})
err = sprite.InitSpriteCache(renderer)
if err != nil {
err = fmt.Errorf("failed to load image: %w", err)
err = fmt.Errorf("failed in InitSpriteCache: %w", err)
return err
}
defer func() {
sdl.Do(func() {
penguinImage.Free()
})
}()
defer sprite.CleanupSpriteCache()
var penguinTexture *sdl.Texture
sdl.Do(func() {
penguinTexture, err = renderer.CreateTextureFromSurface(penguinImage)
})
if err != nil {
err = fmt.Errorf("failed to create texture: %w", err)
return err
}
defer func() {
sdl.Do(func() {
err = penguinTexture.Destroy()
if err != nil {
log.Printf("error destroying renderer: %v\n", err)
}
})
}()
penguinRect := &sdl.Rect{X: 0, Y: 0, W: 100, H: 100}
var rect *sdl.Rect
var x int32 = 0
var y int32 = 0
penguin := entity.NewPenguin(renderer)
p2 := entity.NewPenguin(renderer)
p2.MoveTo(&sdl.Point{X: 100, Y: 100})
keystates := make(map[sdl.Keycode]bool)
@ -148,31 +122,16 @@ func run(configMap gosimpleconf.ConfigMap) error {
}
if keystates[sdl.K_a] {
x -= 1
if x < 0 {
x = 0
}
penguin.MoveLeft()
}
if keystates[sdl.K_d] {
x += 1
w, _ := gameWindow.SdlWindow.GetSize()
if x+rect.W > w {
x = w - rect.W
}
penguin.MoveRight()
}
// Y is inverted, positive Y moves downward
if keystates[sdl.K_w] {
y -= 1
if y < 0 {
y = 0
}
penguin.MoveUp()
}
if keystates[sdl.K_s] {
y += 1
_, h := gameWindow.SdlWindow.GetSize()
if y+rect.H > h {
y = h - rect.H
}
penguin.MoveDown()
}
// Background
@ -188,17 +147,15 @@ func run(configMap gosimpleconf.ConfigMap) error {
}
})
// Square
sdl.Do(func() {
//renderer.SetDrawColor(0, 0, 255, 255)
rect = &sdl.Rect{X: x, Y: y, W: 200, H: 200}
err = renderer.Copy(penguinTexture, penguinRect, rect)
if err != nil {
log.Printf("error in renderer.Copy: %v\n", err)
}
//renderer.DrawRect(rect)
//renderer.FillRect(rect)
})
// Everything else
err = penguin.Draw()
if err != nil {
log.Printf("error drawing: %v\n", err)
}
err = p2.Draw()
if err != nil {
log.Printf("error drawing: %v\n", err)
}
// Draw
sdl.Do(func() {