switched to go coded animation system, LDTK is not used for this anymore

This commit is contained in:
Thomas von Dein 2024-04-08 18:51:25 +02:00
parent 2793710819
commit f23bdaf121
15 changed files with 233 additions and 240 deletions

View File

@ -14,62 +14,66 @@ import (
var Project = LoadLDTK("levels") var Project = LoadLDTK("levels")
var Tiles = InitTiles() var Tiles = InitTiles()
type TileAnimation struct {
OnCollision bool // wether to animate a collision
OnDestruction bool // wether to animate destruction
OnIdle bool // wether to animate during idling
CollisionSheet AnimationSet // an entry in assets.Animations[name]
DestructionSheet AnimationSet
IdleSheet AnimationSet
}
// Tile: contains image, identifier (as used in level data) and // Tile: contains image, identifier (as used in level data) and
// additional properties // additional properties
type Tile struct { type Tile struct {
Id, Ref string Id, Ref string
Sprite *ebiten.Image Sprite *ebiten.Image
ToggleSprite *ebiten.Image ToggleSprite *ebiten.Image
Solid bool // wall brick Solid bool // wall brick
Player bool // player sphere Player bool // player sphere
IsPrimary bool // primary player sphere IsPrimary bool // primary player sphere
Renderable bool // visible, has sprite Renderable bool // visible, has sprite
Velocity bool // movable Velocity bool // movable
Collectible bool // collectible, vanishes once collected Collectible bool // collectible, vanishes once collected
Transient bool // turns into brick wall when traversed Transient bool // turns into brick wall when traversed
Destroyable bool // turns into empty floor when bumped into twice Destroyable bool // turns into empty floor when bumped into twice
Animation int // -1=unused, 0-3 = show image of slice Tiles []*ebiten.Image // has N sprites
Tiles []*ebiten.Image // has N sprites TileNames []string // same thing, only the names
TileNames []string // same thing, only the names Obstacle bool // is an obstacle/enemy
Obstacle bool // is an obstacle/enemy Direction int // obstacle business end shows into this direction
Direction int // obstacle business end shows into this direction Shader *ebiten.Shader
Shader *ebiten.Shader Alpha *ebiten.Image
Alpha *ebiten.Image Bond bool // denotes an entity which can have a relation to another
Bond bool // denotes an entity which can have a relation to another Door bool // a door, can be manipulated by a switch
Door bool // a door, can be manipulated by a switch Switch bool // opens|closes a door
Switch bool // opens|closes a door Animation TileAnimation
AnimateOnDestruct bool // wether to animate destruction
AnimationTrigger string // dynamically configured via LDTP
AnimationSpriteSheet AnimationSet // which sprites to use (refers to an entry in assets.Animations[name])
} }
func (tile *Tile) Clone() *Tile { func (tile *Tile) Clone() *Tile {
newtile := &Tile{ newtile := &Tile{
Id: tile.Id, Id: tile.Id,
Ref: tile.Ref, Ref: tile.Ref,
Sprite: tile.Sprite, Sprite: tile.Sprite,
ToggleSprite: tile.ToggleSprite, ToggleSprite: tile.ToggleSprite,
Solid: tile.Solid, Solid: tile.Solid,
Player: tile.Player, Player: tile.Player,
IsPrimary: tile.IsPrimary, IsPrimary: tile.IsPrimary,
Renderable: tile.Renderable, Renderable: tile.Renderable,
Velocity: tile.Velocity, Velocity: tile.Velocity,
Collectible: tile.Collectible, Collectible: tile.Collectible,
Transient: tile.Transient, Transient: tile.Transient,
Destroyable: tile.Destroyable, Destroyable: tile.Destroyable,
Animation: tile.Animation, Tiles: tile.Tiles,
Tiles: tile.Tiles, TileNames: tile.TileNames,
TileNames: tile.TileNames, Obstacle: tile.Obstacle,
Obstacle: tile.Obstacle, Direction: tile.Direction,
Direction: tile.Direction, Alpha: tile.Alpha,
Alpha: tile.Alpha, Shader: tile.Shader,
Shader: tile.Shader, Bond: tile.Bond,
Bond: tile.Bond, Door: tile.Door,
Door: tile.Door, Switch: tile.Switch,
Switch: tile.Switch, Animation: tile.Animation,
AnimateOnDestruct: tile.AnimateOnDestruct,
AnimationSpriteSheet: tile.AnimationSpriteSheet,
AnimationTrigger: tile.AnimationTrigger,
} }
return newtile return newtile
@ -115,6 +119,12 @@ func NewTileCollectible() *Tile {
Solid: false, Solid: false,
Renderable: true, Renderable: true,
Collectible: true, Collectible: true,
Animation: TileAnimation{
OnDestruction: true,
DestructionSheet: Animations["collectible-detonating"],
OnIdle: true,
IdleSheet: Animations["collectible-idle"],
},
} }
} }
@ -128,17 +138,6 @@ func NewTileObstacle(direction int) *Tile {
} }
} }
func NewTileAnimation(class []string) *Tile {
sprites := GetSprites(class)
return &Tile{
Solid: false,
Renderable: false,
Animation: 0,
Tiles: sprites,
}
}
func NewTileTranswall() *Tile { func NewTileTranswall() *Tile {
return &Tile{ return &Tile{
Solid: false, Solid: false,
@ -191,39 +190,22 @@ func InitTiles() TileRegistry {
"ObstacleSouth": NewTileObstacle(config.South), "ObstacleSouth": NewTileObstacle(config.South),
"ObstacleWest": NewTileObstacle(config.West), "ObstacleWest": NewTileObstacle(config.West),
"ObstacleEast": NewTileObstacle(config.East), "ObstacleEast": NewTileObstacle(config.East),
"Animation": NewTileAnimation([]string{ "Transient": NewTileTranswall(),
"collectible-detonating1", "HiddenDoor": NewTileHiddenDoor("damage"),
"collectible-detonating2", "HiddenDoor2": NewTileHiddenDoor("damage"),
"collectible-detonating3", "HiddenDoor3": NewTileHiddenDoor("damage"),
"collectible-detonating4", "HiddenDoor4": NewTileHiddenDoor("damage"),
"collectible-detonating5", "HiddenDoor5": NewTileHiddenDoor("damage"),
"collectible-detonating6", "HiddenDoor6": NewTileHiddenDoor("damage"),
"collectible-detonating7", "HiddenDoor7": NewTileHiddenDoor("damage"),
"collectible-detonating8", "HiddenDoor8": NewTileHiddenDoor("damage"),
"collectible-detonating9", "HiddenDoor9": NewTileHiddenDoor("damage"),
"collectible-detonating10", "HiddenDoor10": NewTileHiddenDoor("damage"),
"collectible-detonating11", "HiddenDoor11": NewTileHiddenDoor("damage"),
"collectible-detonating12", "HiddenDoor12": NewTileHiddenDoor("damage"),
"collectible-detonating13", "HiddenDoor13": NewTileHiddenDoor("damage"),
"collectible-detonating14", "Switch": NewTileSwitch(),
"collectible-detonating15", "Door": NewTileDoor(),
}),
"Transient": NewTileTranswall(),
"HiddenDoor": NewTileHiddenDoor("damage"),
"HiddenDoor2": NewTileHiddenDoor("damage"),
"HiddenDoor3": NewTileHiddenDoor("damage"),
"HiddenDoor4": NewTileHiddenDoor("damage"),
"HiddenDoor5": NewTileHiddenDoor("damage"),
"HiddenDoor6": NewTileHiddenDoor("damage"),
"HiddenDoor7": NewTileHiddenDoor("damage"),
"HiddenDoor8": NewTileHiddenDoor("damage"),
"HiddenDoor9": NewTileHiddenDoor("damage"),
"HiddenDoor10": NewTileHiddenDoor("damage"),
"HiddenDoor11": NewTileHiddenDoor("damage"),
"HiddenDoor12": NewTileHiddenDoor("damage"),
"HiddenDoor13": NewTileHiddenDoor("damage"),
"Switch": NewTileSwitch(),
"Door": NewTileDoor(),
} }
} }

View File

@ -60,6 +60,9 @@ type AnimationSet struct {
File string File string
} }
// names in the registry match the sprite set png file name, the JSON
// file names are irrelevant as they point to the matching file using
// the "image" field.
type AnimationRegistry map[string]AnimationSet type AnimationRegistry map[string]AnimationSet
//go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg sprites/*.json //go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg sprites/*.json

View File

@ -1,42 +1,42 @@
{ "frames": [ { "frames": [
{ {
"filename": "collectible 0.ase", "filename": "collectible #Detonation 0.ase",
"frame": { "x": 0, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 },
"duration": 100 "duration": 20
}, },
{ {
"filename": "collectible 1.ase", "filename": "collectible #Detonation 1.ase",
"frame": { "x": 64, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 64, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 },
"duration": 100 "duration": 20
}, },
{ {
"filename": "collectible 2.ase", "filename": "collectible #Detonation 2.ase",
"frame": { "x": 128, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 128, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 },
"duration": 100 "duration": 20
}, },
{ {
"filename": "collectible 3.ase", "filename": "collectible #Detonation 3.ase",
"frame": { "x": 192, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 192, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 },
"duration": 100 "duration": 20
}, },
{ {
"filename": "collectible 4.ase", "filename": "collectible #Detonation 4.ase",
"frame": { "x": 256, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 256, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -45,7 +45,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 5.ase", "filename": "collectible #Detonation 5.ase",
"frame": { "x": 320, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 320, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -54,7 +54,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 6.ase", "filename": "collectible #Detonation 6.ase",
"frame": { "x": 384, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 384, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -63,7 +63,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 7.ase", "filename": "collectible #Detonation 7.ase",
"frame": { "x": 448, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 448, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -72,7 +72,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 8.ase", "filename": "collectible #Detonation 8.ase",
"frame": { "x": 512, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 512, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -81,7 +81,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 9.ase", "filename": "collectible #Detonation 9.ase",
"frame": { "x": 576, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 576, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -90,7 +90,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 10.ase", "filename": "collectible #Detonation 10.ase",
"frame": { "x": 640, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 640, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -99,7 +99,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 11.ase", "filename": "collectible #Detonation 11.ase",
"frame": { "x": 704, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 704, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -108,7 +108,7 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 12.ase", "filename": "collectible #Detonation 12.ase",
"frame": { "x": 768, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 768, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
@ -117,22 +117,13 @@
"duration": 100 "duration": 100
}, },
{ {
"filename": "collectible 13.ase", "filename": "collectible #Detonation 13.ase",
"frame": { "x": 832, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 832, "y": 0, "w": 64, "h": 64 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 },
"duration": 100 "duration": 100
},
{
"filename": "collectible 14.ase",
"frame": { "x": 896, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
} }
], ],
"meta": { "meta": {
@ -140,9 +131,10 @@
"version": "1.x-dev", "version": "1.x-dev",
"image": "collectible-detonating.png", "image": "collectible-detonating.png",
"format": "I8", "format": "I8",
"size": { "w": 960, "h": 64 }, "size": { "w": 896, "h": 64 },
"scale": "1", "scale": "1",
"frameTags": [ "frameTags": [
{ "name": "Detonation", "from": 1, "to": 14, "direction": "forward" }
], ],
"layers": [ "layers": [
{ "name": "Yellow Sphere", "opacity": 255, "blendMode": "normal" }, { "name": "Yellow Sphere", "opacity": 255, "blendMode": "normal" },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

View File

@ -1,38 +1,38 @@
{ "frames": [ { "frames": [
{ {
"filename": "collectible #Idle 0.ase", "filename": "collectible-idle 0.ase",
"frame": { "x": 0, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 0, "y": 0, "w": 32, "h": 32 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 32, "h": 32 },
"duration": 200 "duration": 200
}, },
{ {
"filename": "collectible #Idle 1.ase", "filename": "collectible-idle 1.ase",
"frame": { "x": 64, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 32, "y": 0, "w": 32, "h": 32 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 32, "h": 32 },
"duration": 200 "duration": 200
}, },
{ {
"filename": "collectible #Idle 2.ase", "filename": "collectible-idle 2.ase",
"frame": { "x": 128, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 64, "y": 0, "w": 32, "h": 32 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 32, "h": 32 },
"duration": 200 "duration": 200
}, },
{ {
"filename": "collectible #Idle 3.ase", "filename": "collectible-idle 3.ase",
"frame": { "x": 192, "y": 0, "w": 64, "h": 64 }, "frame": { "x": 96, "y": 0, "w": 32, "h": 32 },
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
"sourceSize": { "w": 64, "h": 64 }, "sourceSize": { "w": 32, "h": 32 },
"duration": 200 "duration": 200
} }
], ],
@ -40,26 +40,13 @@
"app": "http://www.aseprite.org/", "app": "http://www.aseprite.org/",
"version": "1.x-dev", "version": "1.x-dev",
"image": "collectible-idle.png", "image": "collectible-idle.png",
"format": "I8", "format": "RGBA8888",
"size": { "w": 256, "h": 64 }, "size": { "w": 128, "h": 32 },
"scale": "1", "scale": "1",
"frameTags": [ "frameTags": [
{ "name": "Detonation", "from": 1, "to": 14, "direction": "forward" },
{ "name": "Idle", "from": 15, "to": 18, "direction": "forward" }
], ],
"layers": [ "layers": [
{ "name": "Yellow Sphere", "opacity": 255, "blendMode": "normal" }, { "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
{ "name": "Layer 8", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 7", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 6", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 5", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 4", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 3", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 2", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" },
{ "name": "Blitz Outer", "opacity": 70, "blendMode": "normal" },
{ "name": "Blitz Middle", "opacity": 84, "blendMode": "normal" },
{ "name": "Blitz Inner", "opacity": 97, "blendMode": "normal" }
], ],
"slices": [ "slices": [
] ]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 685 B

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

View File

@ -4,7 +4,9 @@ package components
type Tilish struct{} type Tilish struct{}
type Solid struct{} type Solid struct{}
type Floor struct{} type Floor struct{}
type Collectible struct{} type Collectible struct {
Hit bool
}
type Obstacle struct { type Obstacle struct {
Direction int Direction int

View File

@ -8,40 +8,66 @@ import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
const (
Destruction = iota
Idle
Collision
)
// virtual location, aka tile address // virtual location, aka tile address
type Animation struct { type Animation struct {
Active bool // animation is running Active bool // animation is running
Loop bool // remove the entity if false, loop endless otherwise Loop bool // remove the entity if false, loop endless otherwise
Index int // where we are currently Index int // where we are currently
Sprites []assets.AnimationSprite Sprites []assets.AnimationSprite // each element contains the sprite and duration
Width, Height int // single sprite measurements Width, Height int // single sprite measurements
Timer Timer Timer Timer
Trigger string Trigger string
Data assets.TileAnimation
} }
type Renderable struct { type Renderable struct {
Pos *Position // just for debugging, will not used as positiion! Pos *Position // just for debugging, will not used as positiion!
Image *ebiten.Image Image *ebiten.Image
DamageImage *ebiten.Image // FIXME: put into its own struct DamageImage *ebiten.Image // FIXME: put into its own struct
Damaged int Damaged int
Shader *ebiten.Shader Shader *ebiten.Shader
Animate Animation DestructionAnimate Animation
IdleAnimate Animation IdleAnimate Animation
Hidden bool CollisionAnimate Animation
Hidden bool
} }
func (render *Renderable) StartAnimation() { func (render *Renderable) StartAnimation(which int) {
render.Hidden = true switch which {
render.Animate.Active = true case Collision:
render.Animate.Timer.Start(config.ANIMATION_STARTWAIT)
switch render.Animate.Trigger {
case "OnCollision":
fallthrough fallthrough
case "OnDestruct": case Destruction:
render.Animate.Loop = false render.Hidden = true
case "OnIdle": render.DestructionAnimate.Active = true
render.Animate.Loop = true render.DestructionAnimate.Timer.Start(config.ANIMATION_STARTWAIT)
render.IdleAnimate.Active = false
case Idle:
render.IdleAnimate.Active = true
render.IdleAnimate.Loop = true
render.IdleAnimate.Timer.Start(0)
}
}
func (render *Renderable) StopAnimation(which int) {
switch which {
case Collision:
render.CollisionAnimate.Active = false
case Idle:
render.IdleAnimate.Active = false
}
}
func (render *Renderable) Animations() map[int]*Animation {
return map[int]*Animation{
Collision: &render.CollisionAnimate,
Destruction: &render.DestructionAnimate,
Idle: &render.IdleAnimate,
} }
} }

View File

@ -18,8 +18,8 @@ const (
PLAYERSPEED int = 5 PLAYERSPEED int = 5
ANIMATION_STARTWAIT time.Duration = 30 * time.Millisecond // how long to wait to start collectible animation ANIMATION_STARTWAIT time.Duration = 30 * time.Millisecond // how long to wait to start collectible animation
ANIMATION_LOOPWAIT time.Duration = 40 * time.Millisecond // how much time to wait between the sprites ANIMATION_LOOPWAIT time.Duration = 40 * time.Millisecond // how much time to wait between the sprites
LEVEL_END_WAIT time.Duration = 500 * time.Millisecond LEVEL_END_WAIT time.Duration = 100 * time.Millisecond
version string = "1.3.0" version string = "1.4.0"
MenuRectX int = 600 MenuRectX int = 600
MenuRectY int = 0 MenuRectY int = 0

View File

@ -2,7 +2,6 @@ package game
import ( import (
"image" "image"
"log"
"log/slog" "log/slog"
"openquell/assets" "openquell/assets"
"openquell/components" "openquell/components"
@ -198,27 +197,6 @@ func LevelToSlice(game *Game, level *ldtkgo.Level, tilesize int) (Map, Map) {
} }
tileRect := entity.TileRect tileRect := entity.TileRect
animationtrigger := util.GetPropertyString(entity, "AnimationTrigger")
slog.Debug("got trigger", "trigger", animationtrigger)
//animateondestruct := util.GetPropertyBool(entity, "AnimateOnDestruct")
// FIXME: also check for AnimationLoop and other animation reasons
// if animateondestruct {
if animationtrigger != "" {
tile.AnimateOnDestruct = true
animation := util.GetPropertyString(entity, "AnimateSpriteSheet")
if animation != "" {
if !util.Exists(assets.Animations, animation) {
log.Fatalf("entity %s refers to non existent animation set %s",
entity.Identifier, animation)
}
}
tile.AnimationSpriteSheet = assets.Animations[animation]
tile.AnimationTrigger = animationtrigger
}
tile.Sprite = tileset.SubImage( tile.Sprite = tileset.SubImage(
image.Rect(tileRect.X, tileRect.Y, image.Rect(tileRect.X, tileRect.Y,
tileRect.X+tileRect.W, tileRect.X+tileRect.W,

View File

@ -159,13 +159,24 @@ func NewGrid(world *ecs.World,
render.Image = tile.Sprite render.Image = tile.Sprite
render.Pos = pos render.Pos = pos
//if tile.AnimateOnDestruct { if tile.Animation.OnCollision {
if tile.AnimationTrigger != "" { render.CollisionAnimate.Sprites = tile.Animation.CollisionSheet.Sprites
// FIXME: be more generic, use LDTK enum render.CollisionAnimate.Width = tile.Animation.CollisionSheet.Width
render.Animate.Sprites = tile.AnimationSpriteSheet.Sprites render.CollisionAnimate.Height = tile.Animation.CollisionSheet.Height
render.Animate.Width = tile.AnimationSpriteSheet.Width }
render.Animate.Height = tile.AnimationSpriteSheet.Height
render.Animate.Trigger = tile.AnimationTrigger if tile.Animation.OnDestruction {
render.DestructionAnimate.Sprites = tile.Animation.DestructionSheet.Sprites
render.DestructionAnimate.Width = tile.Animation.DestructionSheet.Width
render.DestructionAnimate.Height = tile.Animation.DestructionSheet.Height
}
if tile.Animation.OnIdle {
render.IdleAnimate.Sprites = tile.Animation.IdleSheet.Sprites
render.IdleAnimate.Width = tile.Animation.IdleSheet.Width
render.IdleAnimate.Height = tile.Animation.IdleSheet.Height
render.StartAnimation(components.Idle)
render.Hidden = true // we do NOT render the sprite, but the idle animation instead
} }
default: default:

View File

@ -1,8 +1,9 @@
package systems package systems
import ( import (
"log/slog"
"openquell/components"
. "openquell/components" . "openquell/components"
"openquell/config"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/ecs"
@ -32,28 +33,35 @@ func (system *AnimationSystem) Update() error {
for query.Next() { for query.Next() {
_, render := query.Get() _, render := query.Get()
if render.Animate.Active {
if render.Animate.Timer.IsReady() { for animationtype, animate := range render.Animations() {
switch { if animate.Active {
// animation shows from earlier tick, animate if animate.Timer.IsReady() {
case render.Animate.Index > -1 && render.Animate.Index < len(render.Animate.Sprites)-1: switch {
render.Animate.Index += 1 // animation shows from earlier tick, animate
render.Animate.Timer.Start(config.ANIMATION_LOOPWAIT) case animate.Index > -1 && animate.Index < len(animate.Sprites)-1:
default: animate.Index += 1
// last sprite reached animate.Timer.Start(animate.GetDuration())
if render.Animate.Loop { default:
render.Animate.Index = 0 // last sprite reached
} else { if animate.Loop {
EntitiesToRemove = append(EntitiesToRemove, query.Entity()) animate.Index = 0
animate.Timer.Start(animate.GetDuration())
}
if animationtype == components.Destruction {
EntitiesToRemove = append(EntitiesToRemove, query.Entity())
}
} }
} else {
animate.Timer.Update()
} }
} else {
render.Animate.Timer.Update()
} }
} }
} }
for _, entity := range EntitiesToRemove { for _, entity := range EntitiesToRemove {
slog.Debug("remove collectible")
system.World.RemoveEntity(entity) system.World.RemoveEntity(entity)
} }
@ -68,10 +76,13 @@ func (system *AnimationSystem) Draw(screen *ebiten.Image) {
for query.Next() { for query.Next() {
pos, render := query.Get() pos, render := query.Get()
if render.Animate.Active { for _, animate := range render.Animations() {
op.GeoM.Reset() if animate.Active {
op.GeoM.Translate(float64(pos.X), float64(pos.Y)) op.GeoM.Reset()
screen.DrawImage(render.Animate.GetSprite(), op) op.GeoM.Translate(float64(pos.X), float64(pos.Y))
sprite := animate.GetSprite()
screen.DrawImage(sprite, op)
}
} }
} }
} }

View File

@ -4,6 +4,7 @@ import (
"log/slog" "log/slog"
"openquell/components" "openquell/components"
. "openquell/components" . "openquell/components"
. "openquell/config" . "openquell/config"
"openquell/observers" "openquell/observers"
@ -38,12 +39,18 @@ func (system *CollectibleSystem) Update() error {
numcollectibles := query.Count() numcollectibles := query.Count()
if numcollectibles == 0 || observer.Lost { if numcollectibles == 0 || observer.Lost {
slog.Debug("WON")
timer := observers.GetGameObserver(system.World).StopTimer
if !timer.Running {
timer.Start(LEVEL_END_WAIT)
}
query.Close() query.Close()
return nil return nil
} }
for query.Next() { for query.Next() {
colposition, _, render := query.Get() colposition, collectible, render := query.Get()
for _, player := range observer.GetPlayers() { for _, player := range observer.GetPlayers() {
if !system.World.Alive(player) { if !system.World.Alive(player) {
@ -54,10 +61,10 @@ func (system *CollectibleSystem) Update() error {
playervelocity := (*Velocity)(system.World.Get(player, veloID)) playervelocity := (*Velocity)(system.World.Get(player, veloID))
ok, _ := colposition.Intersects(playerposition, playervelocity) ok, _ := colposition.Intersects(playerposition, playervelocity)
if ok && !render.Hidden { if ok && !collectible.Hit {
slog.Debug("bumped into collectible", "colpos", colposition) slog.Debug("bumped into collectible", "colpos", colposition)
render.StartAnimation() render.StartAnimation(components.Destruction)
// position the animation relative to the middle of the current entity // position the animation relative to the middle of the current entity
colposition.Update( colposition.Update(
@ -65,19 +72,13 @@ func (system *CollectibleSystem) Update() error {
colposition.Y-(system.Cellsize/2), colposition.Y-(system.Cellsize/2),
64, 64,
) )
collectible.Hit = true
numcollectibles-- numcollectibles--
} }
} }
} }
if numcollectibles == 0 {
// winner, winner, chicken dinner!
timer := observers.GetGameObserver(system.World).StopTimer
if !timer.Running {
timer.Start(LEVEL_END_WAIT)
}
}
return nil return nil
} }