5 Commits

Author SHA1 Message Date
d23861a427 started with animation interface 2024-04-15 14:42:52 +02:00
8c14f36463 more new tiles 2024-04-15 13:36:40 +02:00
d22042cc72 enhanced 2024-04-12 19:01:17 +02:00
dd25d217dc updated 2024-04-12 15:09:15 +02:00
a84b8b58e1 fixed snap in bug, started with space themed sprites 2024-04-12 15:08:40 +02:00
14 changed files with 281 additions and 77 deletions

37
TODO.md
View File

@@ -1,22 +1,8 @@
## Levels:
- Add shaders for animation (player destruction etc)
- Start New game starts with last played level, better add a Resume
Game menu item for this and use Start New always for level 1.
- for finding caller:
pc := make([]uintptr, 10)
n := runtime.Callers(0, pc)
pc = pc[:n]
fs := runtime.CallersFrames(pc)
source, _ := fs.Next()
source, _ = fs.Next()
source, _ = fs.Next()
slog.Debug("get observer", "minmoves", observer.LevelScore,
"file", source.File, "line", source.Line)
- Turn menu button in hud_system (events in level_scene!) into ebitenui button
- Obstacle don't stop at collectibles
@@ -47,19 +33,10 @@
- Add save to disk feature including settings and player
accomplishements (FIXME: find the lib, asked in Discord)
- Add coded animated background like in https://github.com/tinne26/bindless/tree/main/src/misc/background
- Rework sprites (again!) to match stellar background: maybe more
technical tiles and items? Like some robotic setup?
- Fix collision snapin: sometimes, when an entity moves to the right
or south (NOT left or north!) it snaps in visibly, so a short wobble
can be seen.
- Rework animation system: since it is impossible to assign multiple
animation types to an entity via LDTK, hard code it in Tiles. Keep
implementation though. CAUTION: last time I changed this, the
collectible detonation didn't work anymore!
@@ -82,3 +59,17 @@
Collider.IntersectResolve => func(newpos) {player.pos = newpos; player.vel = stop}
- in the end it must be possible to add new entities without the need
to write a collision check for them, but have collision detection anyway!
## Just in case stuff
- for finding caller:
pc := make([]uintptr, 10)
n := runtime.Callers(0, pc)
pc = pc[:n]
fs := runtime.CallersFrames(pc)
source, _ := fs.Next()
source, _ = fs.Next()
source, _ = fs.Next()
slog.Debug("get observer", "minmoves", observer.LevelScore,
"file", source.File, "line", source.Line)

View File

@@ -0,0 +1,45 @@
JASC-PAL
0100
42
0 0 0
0 0 0
14 14 14
29 29 29
51 51 51
80 80 80
99 99 99
122 122 122
167 167 167
192 192 192
228 228 228
251 245 239
242 211 171
255 227 160
255 216 117
255 202 60
255 168 36
255 141 0
73 77 126
63 69 136
54 61 145
44 52 155
35 44 164
26 36 173
39 39 68
31 31 76
27 27 80
22 22 85
17 17 90
14 14 93
139 109 156
142 98 167
145 86 179
150 68 197
154 52 213
158 36 229
198 159 165
204 153 161
212 145 155
221 136 149
230 127 143
241 116 135

Binary file not shown.

Binary file not shown.

BIN
assets/space/obstacle.ase Normal file

Binary file not shown.

BIN
assets/space/player.ase Normal file

Binary file not shown.

42
assets/space/space.pal Normal file
View File

@@ -0,0 +1,42 @@
JASC-PAL
0100
39
32 0 59
63 0 83
108 0 108
146 0 124
187 0 151
236 0 178
255 19 186
255 64 190
255 113 213
255 147 231
255 186 253
245 220 255
255 255 255
0 0 0
12 12 12
26 26 26
39 39 39
54 54 54
71 71 71
86 86 86
99 99 99
118 118 118
133 133 133
153 153 153
177 177 177
205 205 205
42 2 0
52 9 0
71 24 0
94 46 0
131 71 0
165 96 0
201 106 0
243 119 0
255 165 41
255 186 82
255 239 169
255 250 211
255 255 247

BIN
assets/space/sprites.ase Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -115,3 +115,8 @@ func (tile *Position) Intersects(moving *Position, velocity *Velocity) (bool, *P
return false, nil
}
func (tile *Position) Intersecting(position *Position, velocity *Velocity) bool {
is := tile.Rect.Bounds().Intersect(position.Rect.Bounds())
return is != image.ZR
}

View File

@@ -12,10 +12,21 @@ const (
Destruction = iota
Idle
Collision
Moving
)
// virtual location, aka tile address
type Animation struct {
type AnimationTrigger int
type Animation interface {
GetType() AnimationTrigger
StartAnimation()
StopAnimation()
GetSprite() *ebiten.Image
GetDuration() time.Duration
}
// implements the AnimationInterface
type StandardAnimation struct {
Active bool // animation is running
Loop bool // remove the entity if false, loop endless otherwise
Index int // where we are currently
@@ -24,6 +35,59 @@ type Animation struct {
Timer Timer
Trigger string
Data assets.TileAnimation
Render *Renderable
Which AnimationTrigger
}
func (animation *StandardAnimation) StartAnimation() {
animation.Active = true
animation.Timer.Start(0)
}
func (animation *StandardAnimation) StopAnimation() {
animation.Active = false
}
func (animation *StandardAnimation) GetSprite() *ebiten.Image {
return animation.Sprites[animation.Index].Sprite
}
func (animation *StandardAnimation) GetDuration() time.Duration {
return animation.Sprites[animation.Index].Duration * time.Millisecond
}
func (animation *StandardAnimation) GetType() AnimationTrigger {
return animation.Which
}
type CollisionAnimation struct {
StandardAnimation
}
type IdleAnimation struct {
StandardAnimation
}
type DestructionAnimation struct {
StandardAnimation
}
type MovingAnimation struct {
StandardAnimation
}
func (animation *DestructionAnimation) StartAnimation() {
animation.Render.StopAnimations()
animation.Render.Hidden = true
animation.Active = true
animation.Timer.Start(config.ANIMATION_STARTWAIT)
}
func (animation *IdleAnimation) StartAnimation() {
animation.Render.StopAnimations()
animation.Active = true
animation.Loop = true
animation.Timer.Start(0)
}
type Renderable struct {
@@ -32,49 +96,28 @@ type Renderable struct {
DamageImage *ebiten.Image // FIXME: put into its own struct
Damaged int
Shader *ebiten.Shader
DestructionAnimate Animation
IdleAnimate Animation
CollisionAnimate Animation
Animations map[AnimationTrigger]Animation
Hidden bool
}
func (render *Renderable) StartAnimation(which int) {
func (render *Renderable) StopAnimations() {
for _, animation := range render.Animations {
animation.StopAnimation()
}
}
func (render *Renderable) AddAnimation(which AnimationTrigger) {
render.Animations = make(map[AnimationTrigger]Animation)
switch which {
case Collision:
fallthrough
case Destruction:
render.Hidden = true
render.DestructionAnimate.Active = 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)
idle := &IdleAnimation{
StandardAnimation: StandardAnimation{
Which: Idle,
Render: render,
},
}
render.Animations[Idle] = idle
}
}
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,
}
}
func (animation *Animation) GetSprite() *ebiten.Image {
return animation.Sprites[animation.Index].Sprite
}
func (animation *Animation) GetDuration() time.Duration {
return animation.Sprites[animation.Index].Duration * time.Millisecond
}

View File

@@ -3,6 +3,8 @@ package grid
import (
"openquell/components"
. "openquell/config"
"log/slog"
)
// Check a collision on the grid. We check if the entity in question
@@ -24,8 +26,8 @@ func (grid *Grid) CheckGridCollision(
} else {
ok, tilepos := grid.GetSolidNeighborPosition(position, velocity, true)
if ok {
// slog.Debug("HaveSolidNeighbor", "ok", ok, "tilepos",
// tilepos.Point(), "playerpos", playerposition)
slog.Debug("(3) HaveSolidNeighbor", "tilepos",
tilepos.String(), "playpos", position.String())
intersects, newpos := tilepos.Intersects(position, velocity)
if intersects {
respond_solid(position, velocity, newpos)
@@ -44,6 +46,66 @@ func (grid *Grid) GetSolidNeighborPosition(
return false, nil
}
// set to true if we are already on the last tile in the current
// direction, i.e. on the edge of the grid
edge := true
moving := position.GetMoved(velocity)
neighborpos := position.Point()
if velocity.Direction == East || velocity.Direction == South {
// FIXES snapin to down+right, but now top+left fail. Only in
// these cases we need to look at the neighbor in the future
// position where we will be when we move forward. I don't
// know why it is like this, in fact it doesn't really make
// any sense. But it works, so I'll keep it that way for the
// moment.
neighborpos = moving.Point()
}
switch velocity.Direction {
case East:
if neighborpos.X < grid.TilesX {
neighborpos.X++
edge = false
}
case West:
if neighborpos.X > 0 {
neighborpos.X--
edge = false
}
case South:
if neighborpos.Y < grid.TilesY {
neighborpos.Y++
edge = false
}
case North:
if neighborpos.Y > 0 {
neighborpos.Y--
edge = false
}
}
newpos := components.NewPosition(neighborpos, grid.Tilesize)
slog.Debug("SolidNeighbor?", "player", position.Point(),
"neighbor", neighborpos, "edge", edge, "neighbor-solid",
grid.Map[neighborpos].Solid, "newpos", newpos.Point())
if !edge && grid.Map[neighborpos].Solid {
return true, newpos
}
return false, nil
}
func (grid *Grid) OrigGetSolidNeighborPosition(
position *components.Position,
velocity *components.Velocity,
solid bool) (bool, *components.Position) {
if !solid {
return false, nil
}
// set to true if we are already on the last tile in the current
// direction, i.e. on the edge of the grid
edge := true
@@ -74,9 +136,9 @@ func (grid *Grid) GetSolidNeighborPosition(
newpos := components.NewPosition(neighborpos, grid.Tilesize)
// slog.Debug("SolidNeighbor?", "player", position.Point(),
// "neighbor", neighborpos, "edge", edge, "neighbor-solid",
// grid.Map[neighborpos].Solid, "newpos", newpos.Point())
slog.Debug("SolidNeighbor?", "player", position.Point(),
"neighbor", neighborpos, "edge", edge, "neighbor-solid",
grid.Map[neighborpos].Solid, "newpos", newpos.Point())
if !edge && grid.Map[neighborpos].Solid {
return true, newpos
}

View File

@@ -1,6 +1,7 @@
package systems
import (
"fmt"
"log/slog"
"openquell/components"
. "openquell/components"
@@ -9,6 +10,7 @@ import (
"openquell/observers"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
@@ -46,6 +48,7 @@ func PlayerBumpWallResponder(
vel *components.Velocity,
newpos *components.Position) {
slog.Debug("(2) PlayerBumpWallResponder", "old", pos.String(), "new", newpos.String())
pos.Set(newpos)
vel.Change(Stop)
}
@@ -67,12 +70,16 @@ func (system PlayerSystem) Update() error {
continue
}
// check if the user alters or initiates movement
// check if the user alters or initiates movement, only
// changes player direction
system.CheckMovement(playerposition, velocity, player)
// check if player collides with walls or edges
if velocity.Moving() {
slog.Debug("(2) checking grid collision")
system.GridContainer.Grid.CheckGridCollision(
playerposition, velocity, PlayerBumpEdgeResponder, PlayerBumpWallResponder)
}
if count > 1 {
// check if player collides with another player, fuse them if any
@@ -99,7 +106,11 @@ func (system PlayerSystem) Update() error {
query = system.Selector.Query(system.World)
for query.Next() {
playerposition, velocity, _, _ := query.Get()
oldpos := playerposition.String()
if velocity.Moving() {
playerposition.Move(velocity)
slog.Debug("(4) moving player", "old", oldpos, "new", playerposition.String())
}
}
// we may have lost players, remove them here
@@ -124,6 +135,8 @@ func (system *PlayerSystem) Draw(screen *ebiten.Image) {
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
screen.DrawImage(sprite.Image, op)
ebitenutil.DebugPrintAt(screen, pos.String(), pos.X, pos.Y) // print player pos
}
}
@@ -196,6 +209,9 @@ func (system *PlayerSystem) CheckMovement(
player.LoopCount = 0
observer.AddMove()
}
} else {
fmt.Println("------------------------")
slog.Debug("(1) player is at", "current", position.String())
}
}