completed refactoring to use systems
This commit is contained in:
75
systems/collectible_system.go
Normal file
75
systems/collectible_system.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
. "openquell/components"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/mlange-42/arche/ecs"
|
||||
"github.com/mlange-42/arche/generic"
|
||||
)
|
||||
|
||||
type CollectibleSystem struct {
|
||||
World *ecs.World
|
||||
Selector *generic.Filter3[Position, Collectible, Renderable]
|
||||
EntitiesToRemove []ecs.Entity
|
||||
}
|
||||
|
||||
func NewCollectibleSystem(world *ecs.World) *CollectibleSystem {
|
||||
system := &CollectibleSystem{
|
||||
Selector: generic.NewFilter3[Position, Collectible, Renderable](),
|
||||
World: world,
|
||||
}
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
func (system *CollectibleSystem) CheckPlayerCollision(
|
||||
playerposition *Position,
|
||||
playervelocity *Velocity) (bool, image.Point) {
|
||||
|
||||
particle_pos := image.Point{}
|
||||
var bumped bool
|
||||
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
colposition, collectible, _ := query.Get()
|
||||
|
||||
ok, _ := playerposition.Intersects(colposition, playervelocity)
|
||||
if ok {
|
||||
fmt.Printf("bumped into collectible %v\n", collectible)
|
||||
system.EntitiesToRemove = append(system.EntitiesToRemove, query.Entity())
|
||||
particle_pos.X = colposition.X
|
||||
particle_pos.Y = colposition.Y
|
||||
bumped = true
|
||||
}
|
||||
}
|
||||
|
||||
return bumped, particle_pos
|
||||
}
|
||||
|
||||
func (system *CollectibleSystem) Update() {
|
||||
// remove collectible if collected
|
||||
for _, entity := range system.EntitiesToRemove {
|
||||
system.World.RemoveEntity(entity)
|
||||
}
|
||||
|
||||
system.EntitiesToRemove = []ecs.Entity{}
|
||||
}
|
||||
|
||||
func (system *CollectibleSystem) Draw(screen *ebiten.Image) {
|
||||
// write the movable tiles
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
pos, _, sprite := query.Get()
|
||||
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
|
||||
|
||||
screen.DrawImage(sprite.Image, op)
|
||||
}
|
||||
}
|
||||
163
systems/grid_system.go
Normal file
163
systems/grid_system.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"openquell/components"
|
||||
. "openquell/components"
|
||||
. "openquell/config"
|
||||
"openquell/grid"
|
||||
|
||||
"github.com/alecthomas/repr"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/mlange-42/arche/ecs"
|
||||
"github.com/mlange-42/arche/generic"
|
||||
)
|
||||
|
||||
type GridSystem struct {
|
||||
World *ecs.World
|
||||
Selector *generic.Filter3[Renderable, Position, Solid]
|
||||
UseCache bool
|
||||
Cache *ebiten.Image
|
||||
Background *ebiten.Image
|
||||
Width, Height, TilesX, TilesY, Tilesize int
|
||||
Grid *grid.Grid
|
||||
}
|
||||
|
||||
func NewGridSystem(world *ecs.World, width, height,
|
||||
tilesize int, background *ebiten.Image) *GridSystem {
|
||||
|
||||
cache := ebiten.NewImage(width, height)
|
||||
|
||||
system := &GridSystem{
|
||||
Selector: generic.NewFilter3[Renderable, Position, Solid](),
|
||||
UseCache: false,
|
||||
Cache: cache,
|
||||
Width: width,
|
||||
Height: height,
|
||||
TilesX: width / tilesize,
|
||||
TilesY: height / tilesize,
|
||||
Tilesize: tilesize,
|
||||
Background: background,
|
||||
World: world,
|
||||
}
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
func (system *GridSystem) SetGrid(grid *grid.Grid) {
|
||||
system.Grid = grid
|
||||
}
|
||||
|
||||
func (system *GridSystem) Update() {}
|
||||
|
||||
func (system *GridSystem) Draw(screen *ebiten.Image) {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
|
||||
if !system.UseCache {
|
||||
// map not cached or cacheable, write it to the cache
|
||||
draw.Draw(system.Cache, system.Background.Bounds(), system.Background, image.ZP, draw.Src)
|
||||
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
sprite, pos, _ := query.Get()
|
||||
|
||||
draw.Draw(
|
||||
system.Cache,
|
||||
image.Rect(pos.X, pos.Y, pos.X+pos.Cellsize, pos.Y+pos.Cellsize),
|
||||
sprite.Image, image.ZP, draw.Over)
|
||||
}
|
||||
|
||||
op.GeoM.Reset()
|
||||
screen.DrawImage(system.Cache, op)
|
||||
|
||||
system.UseCache = true
|
||||
} else {
|
||||
// use the cached map
|
||||
op.GeoM.Reset()
|
||||
screen.DrawImage(system.Cache, op)
|
||||
}
|
||||
}
|
||||
|
||||
func (system *GridSystem) GetSolidNeighborPosition(
|
||||
position *components.Position,
|
||||
velocity *components.Velocity,
|
||||
solid bool) (bool, *components.Position) {
|
||||
|
||||
if !solid {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// set to true, ifwe are already on the last tile in the current
|
||||
// direction, i.e. on the edge of the grid
|
||||
edge := true
|
||||
neighborpos := position.Point()
|
||||
|
||||
switch velocity.Direction {
|
||||
case East:
|
||||
if neighborpos.X < system.TilesX {
|
||||
neighborpos.X++
|
||||
edge = false
|
||||
}
|
||||
case West:
|
||||
if neighborpos.X > 0 {
|
||||
neighborpos.X--
|
||||
edge = false
|
||||
}
|
||||
case South:
|
||||
if neighborpos.Y < system.TilesY {
|
||||
neighborpos.Y++
|
||||
edge = false
|
||||
}
|
||||
case North:
|
||||
if neighborpos.Y > 0 {
|
||||
neighborpos.Y--
|
||||
edge = false
|
||||
}
|
||||
}
|
||||
|
||||
newpos := components.NewPosition(neighborpos, system.Tilesize)
|
||||
repr.Println(newpos)
|
||||
|
||||
if !edge && system.Grid.Map[neighborpos].Solid {
|
||||
return true, newpos
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (system *GridSystem) BumpEdge(
|
||||
pos *components.Position,
|
||||
velocity *components.Velocity) (bool, *components.Position) {
|
||||
|
||||
x := pos.X + velocity.Data.X
|
||||
y := pos.Y + velocity.Data.Y
|
||||
|
||||
if x < 0 || x > system.Width-system.Tilesize || y < 0 || y > system.Height-system.Tilesize {
|
||||
newpos := &components.Position{}
|
||||
X := pos.X
|
||||
Y := pos.Y
|
||||
|
||||
switch velocity.Direction {
|
||||
case East:
|
||||
X = 0
|
||||
fmt.Println("east X=0")
|
||||
case West:
|
||||
X = system.Width - system.Tilesize
|
||||
fmt.Println("west X=max")
|
||||
case South:
|
||||
Y = 0
|
||||
fmt.Println("south y=0")
|
||||
case North:
|
||||
Y = system.Height - system.Tilesize
|
||||
fmt.Println("north y=max")
|
||||
}
|
||||
|
||||
newpos.Update(X, Y, system.Tilesize)
|
||||
return true, newpos
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
73
systems/particle_system.go
Normal file
73
systems/particle_system.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"image"
|
||||
. "openquell/components"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/mlange-42/arche/ecs"
|
||||
"github.com/mlange-42/arche/generic"
|
||||
)
|
||||
|
||||
type ParticleSystem struct {
|
||||
World *ecs.World
|
||||
Selector *generic.Filter2[Position, Particle]
|
||||
Cellsize int
|
||||
}
|
||||
|
||||
func NewParticleSystem(world *ecs.World, cellsize int) *ParticleSystem {
|
||||
system := &ParticleSystem{
|
||||
Selector: generic.NewFilter2[Position, Particle](),
|
||||
World: world,
|
||||
Cellsize: cellsize,
|
||||
}
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
func (system *ParticleSystem) Update(detonate bool, position *image.Point) {
|
||||
// display debris after collecting
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
// we loop, but it's only one anyway
|
||||
ptposition, particle := query.Get()
|
||||
|
||||
if detonate {
|
||||
// particle appears
|
||||
ptposition.Update(
|
||||
position.X-(system.Cellsize/2),
|
||||
position.Y-(system.Cellsize/2),
|
||||
64,
|
||||
)
|
||||
|
||||
particle.Index = 0 // start displaying the particle
|
||||
} else {
|
||||
switch {
|
||||
// particle shows from earlier tick, animate
|
||||
case particle.Index > -1 && particle.Index < len(particle.Particles)-1:
|
||||
particle.Index++
|
||||
default:
|
||||
// last sprite reached, remove it
|
||||
particle.Index = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (system *ParticleSystem) Draw(screen *ebiten.Image) {
|
||||
// write particles (these are no tiles!)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
pos, particle := query.Get()
|
||||
|
||||
if particle.Index > -1 {
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
|
||||
screen.DrawImage(particle.Particles[particle.Index], op)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
106
systems/player_system.go
Normal file
106
systems/player_system.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
. "openquell/components"
|
||||
. "openquell/config"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/mlange-42/arche/ecs"
|
||||
"github.com/mlange-42/arche/generic"
|
||||
)
|
||||
|
||||
type PlayerSystem struct {
|
||||
World *ecs.World
|
||||
Selector *generic.Filter4[Position, Velocity, Player, Renderable]
|
||||
Particle *ParticleSystem
|
||||
Collectible *CollectibleSystem
|
||||
Grid *GridSystem
|
||||
}
|
||||
|
||||
func NewPlayerSystem(world *ecs.World, grid *GridSystem) *PlayerSystem {
|
||||
system := &PlayerSystem{
|
||||
Selector: generic.NewFilter4[Position, Velocity, Player, Renderable](),
|
||||
Particle: NewParticleSystem(world, grid.Tilesize),
|
||||
Collectible: NewCollectibleSystem(world),
|
||||
Grid: grid,
|
||||
World: world,
|
||||
}
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
func (system PlayerSystem) Update() error {
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
var bumped bool
|
||||
var particle_pos image.Point
|
||||
|
||||
for query.Next() {
|
||||
playerposition, velocity, _, _ := query.Get()
|
||||
|
||||
if !velocity.Moving() {
|
||||
switch {
|
||||
case ebiten.IsKeyPressed(ebiten.KeyRight):
|
||||
velocity.Change(East)
|
||||
case ebiten.IsKeyPressed(ebiten.KeyLeft):
|
||||
velocity.Change(West)
|
||||
case ebiten.IsKeyPressed(ebiten.KeyDown):
|
||||
velocity.Change(South)
|
||||
case ebiten.IsKeyPressed(ebiten.KeyUp):
|
||||
velocity.Change(North)
|
||||
// other keys: <tab>: switch player, etc
|
||||
}
|
||||
}
|
||||
|
||||
if velocity.Moving() {
|
||||
ok, newpos := system.Grid.BumpEdge(playerposition, velocity)
|
||||
if ok {
|
||||
fmt.Printf("falling off the edge, new pos: %v\n", newpos)
|
||||
playerposition.Set(newpos)
|
||||
} else {
|
||||
ok, tilepos := system.Grid.GetSolidNeighborPosition(playerposition, velocity, true)
|
||||
if ok {
|
||||
intersects, newpos := tilepos.Intersects(playerposition, velocity)
|
||||
if intersects {
|
||||
fmt.Printf("collision detected. tile: %s\n", tilepos)
|
||||
fmt.Printf(" player: %s\n", playerposition)
|
||||
fmt.Printf(" new: %s\n", newpos)
|
||||
|
||||
playerposition.Set(newpos)
|
||||
fmt.Printf(" player new: %s\n", playerposition)
|
||||
velocity.Change(Stop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bumped, particle_pos = system.Collectible.CheckPlayerCollision(playerposition, velocity)
|
||||
}
|
||||
|
||||
playerposition.Move(velocity)
|
||||
}
|
||||
|
||||
system.Particle.Update(bumped, &particle_pos)
|
||||
system.Collectible.Update()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (system *PlayerSystem) Draw(screen *ebiten.Image) {
|
||||
// write the movable tiles
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
query := system.Selector.Query(system.World)
|
||||
|
||||
for query.Next() {
|
||||
pos, _, _, sprite := query.Get()
|
||||
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
|
||||
|
||||
screen.DrawImage(sprite.Image, op)
|
||||
}
|
||||
|
||||
system.Collectible.Draw(screen)
|
||||
system.Particle.Draw(screen)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package system
|
||||
package systems
|
||||
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user