fixed observers, added GameObserver

This commit is contained in:
Thomas von Dein 2024-02-11 14:24:30 +01:00
parent 72f0aa7691
commit 65ddec3fa4
18 changed files with 122 additions and 53 deletions

17
assets/levels/0-own.lvl Normal file
View File

@ -0,0 +1,17 @@
Description: win
Background: background-lila
########
# #o#
# S # #
# #### #
# #### #
# #
# o #
########

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/bash
width=$(grep "width int" ../main.go | awk '{print $4}') width=$(grep "width " ../../main.go | awk '{print $4}')
height=$(grep "height int" ../main.go | awk '{print $4}') height=$(grep "height " ../../main.go | awk '{print $4}')
read -p " Enter level name: " name read -p " Enter level name: " name
read -p " Enter background: " background read -p " Enter background: " background
@ -14,11 +14,12 @@ if test -z "$bbackground"; then
background="background-lila" background="background-lila"
fi fi
w=$(($width/32))
h=$(($height/32))
( (
echo "Description: $des" echo "Description: $des"
echo "Background: $background" echo "Background: $background"
w=$(($width / 32))
h=$(($height / 32))
for x in $(seq 1 $h); do for x in $(seq 1 $h); do
for y in $(seq 1 $w); do for y in $(seq 1 $w); do

View File

@ -1,17 +0,0 @@
Description: Introduction: collect the goods by moving the ball onto them
Background: background-lila
####################
# #
# #
# #
# #
# #
# #
# ######
#
#
#
#
#
#
####################

View File

@ -10,14 +10,18 @@ type Renderable struct {
Image *ebiten.Image Image *ebiten.Image
} }
type Particle struct {
Index int
Particles []*ebiten.Image
}
type Speed struct {
Value int
}
// only tile entities will have those // only tile entities will have those
type Tilish struct{} type Tilish struct{}
type Solid struct{} type Solid struct{}
type Floor struct{} type Floor struct{}
type Player struct{} type Player struct{}
type Collectible struct{} type Collectible struct{}
type Particle struct {
Index int
Particles []*ebiten.Image
}

View File

@ -70,8 +70,8 @@ func (position *Position) String() string {
) )
} }
func (position *Position) Move(velocity *Velocity) { func (position *Position) Move(velocity *Velocity, speed *Speed) {
position.Update(position.X+velocity.Data.X, position.Y+velocity.Data.Y) position.Update(position.X+(velocity.Data.X*speed.Value), position.Y+(velocity.Data.Y*speed.Value))
} }
func (position *Position) Set(newpos *Position) { func (position *Position) Set(newpos *Position) {

View File

@ -11,21 +11,19 @@ type Velocity struct {
} }
func (velocity *Velocity) Change(direction int) { func (velocity *Velocity) Change(direction int) {
ticks := 4
switch direction { switch direction {
case East: case East:
velocity.Data.X = ticks velocity.Data.X = 1
velocity.Data.Y = 0 velocity.Data.Y = 0
case West: case West:
velocity.Data.X = ticks - (ticks * 2) velocity.Data.X = -1
velocity.Data.Y = 0 velocity.Data.Y = 0
case South: case South:
velocity.Data.X = 0 velocity.Data.X = 0
velocity.Data.Y = ticks velocity.Data.Y = 1
case North: case North:
velocity.Data.X = 0 velocity.Data.X = 0
velocity.Data.Y = ticks - (ticks * 2) velocity.Data.Y = -1
case Stop: case Stop:
velocity.Data.X = 0 velocity.Data.X = 0
velocity.Data.Y = 0 velocity.Data.Y = 0

View File

@ -7,3 +7,5 @@ const (
South South
North North
) )
const PLAYERSPEED int = 4

View File

@ -13,16 +13,15 @@ type Game struct {
World *ecs.World World *ecs.World
Bounds image.Rectangle Bounds image.Rectangle
ScreenWidth, ScreenHeight int ScreenWidth, ScreenHeight int
CurrentLevel int
Scenes map[int]Scene Scenes map[int]Scene
Observer *observers.GameObserver
} }
func NewGame(width, height, startlevel int, startscene int) *Game { func NewGame(width, height, cellsize, startlevel int, startscene int) *Game {
world := ecs.NewWorld() world := ecs.NewWorld()
game := &Game{ game := &Game{
Bounds: image.Rectangle{}, Bounds: image.Rectangle{},
CurrentLevel: startlevel,
World: &world, World: &world,
ScreenWidth: width, ScreenWidth: width,
ScreenHeight: height, ScreenHeight: height,
@ -31,6 +30,7 @@ func NewGame(width, height, startlevel int, startscene int) *Game {
observers.NewPlayerObserver(&world) observers.NewPlayerObserver(&world)
observers.NewParticleObserver(&world) observers.NewParticleObserver(&world)
game.Observer = observers.NewGameObserver(&world, startlevel, width, height, cellsize)
game.Scenes[Play] = NewLevelScene(game, startlevel) game.Scenes[Play] = NewLevelScene(game, startlevel)

View File

@ -6,6 +6,7 @@ import (
"openquell/assets" "openquell/assets"
"openquell/components" "openquell/components"
"openquell/grid" "openquell/grid"
"openquell/observers"
"openquell/systems" "openquell/systems"
"strings" "strings"
@ -71,6 +72,10 @@ func (level *Level) SetupGrid(game *Game) {
selector := ecs.All(posID) selector := ecs.All(posID)
level.World.Batch().RemoveEntities(selector) level.World.Batch().RemoveEntities(selector)
// get rid of any players on PlayerObserver. FIXME: remove them in grid.NewGrid()?
playerobserver := observers.GetPlayerObserver(level.World)
playerobserver.RemoveEntities()
// setup world // setup world
level.GridSystem.SetGrid(grid.NewGrid(game.World, level.Cellsize, level.Width, level.Height, level.Mapslice)) level.GridSystem.SetGrid(grid.NewGrid(game.World, level.Cellsize, level.Width, level.Height, level.Mapslice))
} }

View File

@ -1,12 +1,14 @@
package game package game
import ( import (
"fmt"
"openquell/assets" "openquell/assets"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
type LevelScene struct { type LevelScene struct {
Game *Game
CurrentLevel int CurrentLevel int
Levels []*Level Levels []*Level
Next int Next int
@ -16,10 +18,10 @@ type LevelScene struct {
// Implements the actual playing Scene // Implements the actual playing Scene
func NewLevelScene(game *Game, startlevel int) Scene { func NewLevelScene(game *Game, startlevel int) Scene {
scene := &LevelScene{CurrentLevel: startlevel, Whoami: Play} scene := &LevelScene{CurrentLevel: startlevel, Whoami: Play, Game: game}
scene.GenerateLevels(game) scene.GenerateLevels(game)
scene.Levels[game.CurrentLevel].SetupGrid(game) scene.Levels[scene.CurrentLevel].SetupGrid(game)
return scene return scene
} }
@ -40,6 +42,12 @@ func (scene *LevelScene) SetNext() int {
} }
func (scene *LevelScene) Update() error { func (scene *LevelScene) Update() error {
if scene.CurrentLevel != scene.Game.Observer.CurrentLevel {
fmt.Printf("current: %d, next: %d\n", scene.CurrentLevel, scene.Game.Observer.CurrentLevel)
scene.CurrentLevel = scene.Game.Observer.CurrentLevel
scene.Levels[scene.CurrentLevel].SetupGrid(scene.Game)
}
scene.Levels[scene.CurrentLevel].Update() scene.Levels[scene.CurrentLevel].Update()
return nil return nil
} }

View File

@ -6,6 +6,7 @@ import (
"log" "log"
"openquell/assets" "openquell/assets"
"openquell/components" "openquell/components"
"openquell/config"
"openquell/observers" "openquell/observers"
"github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/ecs"
@ -27,10 +28,11 @@ func NewGrid(world *ecs.World,
// we use this to turn our tiles into iterable entities, used for // we use this to turn our tiles into iterable entities, used for
// collision detection, transformation and other things // collision detection, transformation and other things
playermapper := generic.NewMap4[ playermapper := generic.NewMap5[
components.Position, components.Position,
components.Velocity, components.Velocity,
components.Renderable, components.Renderable,
components.Speed,
components.Player](world) components.Player](world)
solidmapper := generic.NewMap4[ solidmapper := generic.NewMap4[
@ -48,6 +50,7 @@ func NewGrid(world *ecs.World,
var pos *components.Position var pos *components.Position
var render *components.Renderable var render *components.Renderable
var speed *components.Speed
playerobserver := observers.GetPlayerObserver(world) playerobserver := observers.GetPlayerObserver(world)
@ -60,9 +63,9 @@ func NewGrid(world *ecs.World,
pos, render, _, _ = solidmapper.Get(entity) pos, render, _, _ = solidmapper.Get(entity)
case tile.Player: case tile.Player:
entity := playermapper.New() entity := playermapper.New()
pos, _, render, _ = playermapper.Get(entity) pos, _, render, speed, _ = playermapper.Get(entity)
playerobserver.AddEntity(entity) playerobserver.AddEntity(entity)
speed.Value = config.PLAYERSPEED
fmt.Printf("player start pos: %d,%d\n", point.X*tilesize, point.Y*tilesize) fmt.Printf("player start pos: %d,%d\n", point.X*tilesize, point.Y*tilesize)
case tile.Collectible: case tile.Collectible:
entity := colmapper.New() entity := colmapper.New()

View File

@ -8,15 +8,16 @@ import (
) )
const ( const (
width int = 640 width int = 640
height int = 480 height int = 480
cellsize int = 32
) )
func main() { func main() {
ebiten.SetWindowSize(width, height) ebiten.SetWindowSize(width, height)
ebiten.SetWindowTitle("openquell") ebiten.SetWindowTitle("openquell")
g := game.NewGame(width, height, 0, game.Play) g := game.NewGame(width, height, cellsize, 0, game.Play)
err := ebiten.RunGame(g) err := ebiten.RunGame(g)
if err != nil { if err != nil {

View File

@ -0,0 +1,31 @@
package observers
import (
"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
)
// Used for global game state
type GameObserver struct {
CurrentLevel, Width, Height, Cellsize, Score int
}
func NewGameObserver(world *ecs.World, startlevel, width, height, cellsize int) *GameObserver {
observer := &GameObserver{
CurrentLevel: startlevel,
Width: width,
Height: height,
Cellsize: cellsize,
}
resmanger := generic.NewResource[GameObserver](world)
resmanger.Add(observer)
return observer
}
func GetGameObserver(world *ecs.World) *GameObserver {
observerID := ecs.ResourceID[GameObserver](world)
observer := world.Resources().Get(observerID).(*GameObserver)
return observer
}

View File

@ -50,3 +50,7 @@ func (observer *PlayerObserver) AddEntity(entity ecs.Entity) {
func (observer *PlayerObserver) RemoveEntity(entity ecs.Entity) { func (observer *PlayerObserver) RemoveEntity(entity ecs.Entity) {
delete(observer.Entities, entity) delete(observer.Entities, entity)
} }
func (observer *PlayerObserver) RemoveEntities() {
observer.Entities = make(map[ecs.Entity]int)
}

View File

@ -35,6 +35,12 @@ func (system *CollectibleSystem) Update() {
EntitiesToRemove := []ecs.Entity{} EntitiesToRemove := []ecs.Entity{}
query := system.Selector.Query(system.World) query := system.Selector.Query(system.World)
numcollectibles := query.Count()
if numcollectibles == 0 {
query.Close()
return
}
for query.Next() { for query.Next() {
colposition, collectible, _ := query.Get() colposition, collectible, _ := query.Get()
@ -58,6 +64,14 @@ func (system *CollectibleSystem) Update() {
for _, entity := range EntitiesToRemove { for _, entity := range EntitiesToRemove {
system.World.RemoveEntity(entity) system.World.RemoveEntity(entity)
numcollectibles--
}
if numcollectibles == 0 {
// winner, winner, chicken dinner!
game := observers.GetGameObserver(system.World)
game.Score++
game.CurrentLevel++
} }
} }

View File

@ -9,7 +9,6 @@ import (
. "openquell/config" . "openquell/config"
"openquell/grid" "openquell/grid"
"github.com/alecthomas/repr"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic" "github.com/mlange-42/arche/generic"
@ -119,7 +118,6 @@ func (system *GridSystem) GetSolidNeighborPosition(
} }
newpos := components.NewPosition(neighborpos, system.Tilesize) newpos := components.NewPosition(neighborpos, system.Tilesize)
repr.Println(newpos)
if !edge && system.Grid.Map[neighborpos].Solid { if !edge && system.Grid.Map[neighborpos].Solid {
return true, newpos return true, newpos

View File

@ -12,7 +12,7 @@ import (
type PlayerSystem struct { type PlayerSystem struct {
World *ecs.World World *ecs.World
Selector *generic.Filter4[Position, Velocity, Player, Renderable] Selector *generic.Filter5[Position, Velocity, Player, Renderable, Speed]
Particle *ParticleSystem Particle *ParticleSystem
Collectible *CollectibleSystem Collectible *CollectibleSystem
Grid *GridSystem Grid *GridSystem
@ -20,7 +20,7 @@ type PlayerSystem struct {
func NewPlayerSystem(world *ecs.World, grid *GridSystem) *PlayerSystem { func NewPlayerSystem(world *ecs.World, grid *GridSystem) *PlayerSystem {
system := &PlayerSystem{ system := &PlayerSystem{
Selector: generic.NewFilter4[Position, Velocity, Player, Renderable](), Selector: generic.NewFilter5[Position, Velocity, Player, Renderable, Speed](),
Particle: NewParticleSystem(world, grid.Tilesize), Particle: NewParticleSystem(world, grid.Tilesize),
Collectible: NewCollectibleSystem(world), Collectible: NewCollectibleSystem(world),
Grid: grid, Grid: grid,
@ -34,7 +34,7 @@ func (system PlayerSystem) Update() error {
query := system.Selector.Query(system.World) query := system.Selector.Query(system.World)
for query.Next() { for query.Next() {
playerposition, velocity, _, _ := query.Get() playerposition, velocity, _, _, speed := query.Get()
if !velocity.Moving() { if !velocity.Moving() {
switch { switch {
@ -72,7 +72,7 @@ func (system PlayerSystem) Update() error {
} }
} }
playerposition.Move(velocity) playerposition.Move(velocity, speed)
} }
system.Particle.Update() system.Particle.Update()
@ -87,7 +87,7 @@ func (system *PlayerSystem) Draw(screen *ebiten.Image) {
query := system.Selector.Query(system.World) query := system.Selector.Query(system.World)
for query.Next() { for query.Next() {
pos, _, _, sprite := query.Get() pos, _, _, sprite, _ := query.Get()
op.GeoM.Reset() op.GeoM.Reset()
op.GeoM.Translate(float64(pos.X), float64(pos.Y)) op.GeoM.Translate(float64(pos.X), float64(pos.Y))