From e93c08f81fefd117141d75257bc12d568e8120c7 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Thu, 21 Mar 2024 13:25:06 +0100 Subject: [PATCH 1/2] fixes: - using 1 func for grid collision checking with func arguments in player and obstacle systems. - moving obstacles now kill player if business ends points toward him on contact - added retry in popup menu --- TODO.md | 8 -- assets/levels/openquell.ldtk | 117 ++++++++++++++++++++++++- components/velocity.go | 2 + game/game.go | 4 +- game/{level_scene.go => play_scene.go} | 22 ++--- game/popup_scene.go | 13 +-- grid/collider.go | 23 +++-- systems/obstacle_system.go | 58 ++++++------ systems/player_system.go | 44 ++++------ 9 files changed, 201 insertions(+), 90 deletions(-) rename game/{level_scene.go => play_scene.go} (79%) diff --git a/TODO.md b/TODO.md index 3aad153..b0032e7 100644 --- a/TODO.md +++ b/TODO.md @@ -24,15 +24,7 @@ - Turn menu button in hud_system (events in level_scene!) into ebitenui button -- On level loose, do not offer "next level" -- Moving obstacles don't kill the player when they hit him, they just stop there. - -- use grid/collider.CheckGridCollision() for players AND - obstacles. Currenty if I use it with obstacles, the don't move - anymore when a player bumps into it, for whatever strange reasons. - Maybe add a func argument to it so that the function can respond to - the event as the entity wishes. ## Collider Rework [abandoned: see branch collider-system, fails] diff --git a/assets/levels/openquell.ldtk b/assets/levels/openquell.ldtk index 3c5fea2..3a8b5ae 100644 --- a/assets/levels/openquell.ldtk +++ b/assets/levels/openquell.ldtk @@ -11,7 +11,7 @@ "iid": "267e9380-d7b0-11ee-a97e-35bec9c19d52", "jsonVersion": "1.5.3", "appBuildId": 473703, - "nextUid": 31, + "nextUid": 33, "identifierStyle": "Capitalize", "toc": [], "worldLayout": "Free", @@ -3502,6 +3502,121 @@ } ], "__neighbours": [] + }, + { + "identifier": "Level_13", + "iid": "df3a04b0-d7b0-11ee-b56f-5371345878c2", + "uid": 32, + "worldX": 480, + "worldY": 1632, + "worldDepth": 0, + "pxWid": 640, + "pxHei": 480, + "__bgColor": "#696A79", + "bgColor": null, + "useAutoIdentifier": true, + "bgRelPath": "../sprites/background-lila.png", + "bgPos": "Cover", + "bgPivotX": 0.5, + "bgPivotY": 0.5, + "__smartColor": "#ADADB5", + "__bgPos": { "topLeftPx": [0,0], "scale": [1,1], "cropRect": [0,0,640,480] }, + "externalRelPath": null, + "fieldInstances": [ + { "__identifier": "level", "__type": "Int", "__value": 13, "__tile": null, "defUid": 11, "realEditorValues": [{ "id": "V_Int", "params": [13] }] }, + { "__identifier": "description", "__type": "String", "__value": "test", "__tile": null, "defUid": 12, "realEditorValues": [{ + "id": "V_String", + "params": ["test"] + }] }, + { "__identifier": "background", "__type": "String", "__value": "background-lila", "__tile": null, "defUid": 13, "realEditorValues": [] }, + { "__identifier": "minmoves", "__type": "Int", "__value": 7, "__tile": null, "defUid": 14, "realEditorValues": [] } + ], + "layerInstances": [ + { + "__identifier": "Entities", + "__type": "Entities", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "df3a04b1-d7b0-11ee-b56f-31d2a4bd4c5c", + "levelId": 32, + "layerDefUid": 5, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 1934321, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [ + { + "__identifier": "PlayerPrimary", + "__grid": [9,7], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 96, "w": 32, "h": 32 }, + "__smartColor": "#2F3BBE", + "iid": "f00cf6d0-d7b0-11ee-b56f-4d0f006a98b4", + "width": 32, + "height": 32, + "defUid": 3, + "px": [288,224], + "fieldInstances": [], + "__worldX": 768, + "__worldY": 1856 + }, + { + "__identifier": "ObstacleEast", + "__grid": [12,7], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 64, "y": 32, "w": 32, "h": 32 }, + "__smartColor": "#D77643", + "iid": "f5429510-d7b0-11ee-b56f-4d263bc512ec", + "width": 32, + "height": 32, + "defUid": 8, + "px": [384,224], + "fieldInstances": [], + "__worldX": 864, + "__worldY": 1856 + } + ] + }, + { + "__identifier": "Tiles", + "__type": "Tiles", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": 1, + "__tilesetRelPath": "../sprites/map.png", + "iid": "df3a04b2-d7b0-11ee-b56f-99f67bd5211d", + "levelId": 32, + "layerDefUid": 2, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 2843103, + "overrideTilesetUid": null, + "gridTiles": [{ "px": [352,320], "src": [64,0], "f": 0, "t": 2, "d": [211], "a": 1 }], + "entityInstances": [] + } + ], + "__neighbours": [] } ], "worlds": [], diff --git a/components/velocity.go b/components/velocity.go index c1ad45d..1dfcf28 100644 --- a/components/velocity.go +++ b/components/velocity.go @@ -61,6 +61,8 @@ func (velocity *Velocity) InvertDirection() int { return South case All: return Stop + case Stop: + return Stop } // should not happen diff --git a/game/game.go b/game/game.go index e52990d..e53f77c 100644 --- a/game/game.go +++ b/game/game.go @@ -18,7 +18,7 @@ type Game struct { Scenes map[SceneName]Scene CurrentScene SceneName Observer *observers.GameObserver - Levels []*Level // fed in LevelScene.GenerateLevels() + Levels []*Level // fed in PlayScene.GenerateLevels() Config *config.Config } @@ -42,7 +42,7 @@ func NewGame(width, height, cellsize int, cfg *config.Config, startscene SceneNa game.Scenes[Menu] = NewMenuScene(game) game.Scenes[About] = NewAboutScene(game) game.Scenes[Popup] = NewPopupScene(game) - game.Scenes[Play] = NewLevelScene(game, cfg.Startlevel) + game.Scenes[Play] = NewPlayScene(game, cfg.Startlevel) game.Scenes[Select] = NewSelectScene(game) game.CurrentScene = startscene diff --git a/game/level_scene.go b/game/play_scene.go similarity index 79% rename from game/level_scene.go rename to game/play_scene.go index 0ca5ef7..3785857 100644 --- a/game/level_scene.go +++ b/game/play_scene.go @@ -10,7 +10,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/inpututil" ) -type LevelScene struct { +type PlayScene struct { Game *Game CurrentLevel int Levels []*Level @@ -21,8 +21,8 @@ type LevelScene struct { } // Implements the actual playing Scene -func NewLevelScene(game *Game, startlevel int) Scene { - scene := &LevelScene{Whoami: Play, Next: Play, Game: game} +func NewPlayScene(game *Game, startlevel int) Scene { + scene := &PlayScene{Whoami: Play, Next: Play, Game: game} scene.GenerateLevels(game) scene.SetLevel(startlevel) @@ -33,7 +33,7 @@ func NewLevelScene(game *Game, startlevel int) Scene { return scene } -func (scene *LevelScene) GenerateLevels(game *Game) { +func (scene *PlayScene) GenerateLevels(game *Game) { min := []int{} for _, level := range assets.Project.Levels { level := level @@ -46,30 +46,30 @@ func (scene *LevelScene) GenerateLevels(game *Game) { scene.Game.Levels = scene.Levels } -func (scene *LevelScene) SetLevel(level int) { +func (scene *PlayScene) SetLevel(level int) { scene.CurrentLevel = level scene.Levels[scene.CurrentLevel].SetupGrid(scene.Game) } // Interface methods -func (scene *LevelScene) SetNext(next SceneName) { +func (scene *PlayScene) SetNext(next SceneName) { scene.Next = next } -func (scene *LevelScene) GetNext() SceneName { +func (scene *PlayScene) GetNext() SceneName { // FIXME: set to winner or options screen return scene.Next } -func (scene *LevelScene) ResetNext() { +func (scene *PlayScene) ResetNext() { scene.Next = scene.Whoami } -func (scene *LevelScene) Clearscreen() bool { +func (scene *PlayScene) Clearscreen() bool { return false } -func (scene *LevelScene) Update() error { +func (scene *PlayScene) Update() error { scene.Levels[scene.CurrentLevel].Update() switch { @@ -87,7 +87,7 @@ func (scene *LevelScene) Update() error { return nil } -func (scene *LevelScene) Draw(screen *ebiten.Image) { +func (scene *PlayScene) Draw(screen *ebiten.Image) { // FIXME: why not in Update() ?!?!?! if scene.CurrentLevel != scene.Game.Observer.CurrentLevel { slog.Debug("level", "current", scene.CurrentLevel, diff --git a/game/popup_scene.go b/game/popup_scene.go index be73f57..28f4391 100644 --- a/game/popup_scene.go +++ b/game/popup_scene.go @@ -5,6 +5,7 @@ import ( "log/slog" "openquell/assets" "openquell/gameui" + "openquell/observers" "github.com/ebitenui/ebitenui" "github.com/ebitenui/ebitenui/widget" @@ -75,6 +76,7 @@ func (scene *PopupScene) Draw(screen *ebiten.Image) { func (scene *PopupScene) SetupUI() { blue := color.RGBA{0, 255, 128, 255} + observer := observers.GetGameObserver(scene.Game.World) rowContainer := gameui.NewRowContainer(false) @@ -88,10 +90,11 @@ func (scene *PopupScene) SetupUI() { scene.SetNext(Menu) }) - // buttonOptions := gameui.NewMenuButton("Options", *assets.FontRenderer.FontNormal, - // func(args *widget.ButtonClickedEventArgs) { - // scene.SetNext(Settings) - // }) + buttonRetry := gameui.NewMenuButton("Retry", *assets.FontRenderer.FontNormal, + func(args *widget.ButtonClickedEventArgs) { + scene.SetNext(Play) + observer.Retry = true + }) label := widget.NewText( widget.TextOpts.Text("Menu", *assets.FontRenderer.FontBig, blue), @@ -100,8 +103,8 @@ func (scene *PopupScene) SetupUI() { rowContainer.AddChild(label) rowContainer.AddChild(buttonContinue) + rowContainer.AddChild(buttonRetry) rowContainer.AddChild(buttonAbort) - //rowContainer.AddChild(buttonOptions) scene.Ui = &ebitenui.UI{ Container: rowContainer.Container(), diff --git a/grid/collider.go b/grid/collider.go index 8579093..de2f661 100644 --- a/grid/collider.go +++ b/grid/collider.go @@ -5,25 +5,30 @@ import ( . "openquell/config" ) -// FIXME: make available everywhere +// Check a collision on the grid. We check if the entity in question +// bumps into the egde of the grid or if it bumps onto a solid wall +// tile. For both cases the user must provide responder funcs in which +// it must be implemented how to react on those events. func (grid *Grid) CheckGridCollision( - playerposition *components.Position, - velocity *components.Velocity) { + position *components.Position, + velocity *components.Velocity, + respond_edge func(*components.Position, *components.Velocity, *components.Position), + respond_solid func(*components.Position, *components.Velocity, *components.Position), +) { if velocity.Moving() { - ok, newpos := grid.BumpEdge(playerposition, velocity) + ok, newpos := grid.BumpEdge(position, velocity) if ok { //slog.Debug("falling off the edge", "newpos", newpos) - playerposition.Set(newpos) + respond_edge(position, velocity, newpos) } else { - ok, tilepos := grid.GetSolidNeighborPosition(playerposition, velocity, true) + ok, tilepos := grid.GetSolidNeighborPosition(position, velocity, true) if ok { // slog.Debug("HaveSolidNeighbor", "ok", ok, "tilepos", // tilepos.Point(), "playerpos", playerposition) - intersects, newpos := tilepos.Intersects(playerposition, velocity) + intersects, newpos := tilepos.Intersects(position, velocity) if intersects { - playerposition.Set(newpos) - velocity.Change(Stop) + respond_solid(position, velocity, newpos) } } } diff --git a/systems/obstacle_system.go b/systems/obstacle_system.go index bc24488..7651d87 100644 --- a/systems/obstacle_system.go +++ b/systems/obstacle_system.go @@ -30,6 +30,23 @@ func NewObstacleSystem(world *ecs.World, gridcontainer *grid.GridContainer) Syst return system } +func ObstacleBumpEdgeResponder( + pos *components.Position, + vel *components.Velocity, + newpos *components.Position) { + + pos.Set(newpos) +} + +func ObstacleBumpWallResponder( + pos *components.Position, + vel *components.Velocity, + newpos *components.Position) { + + pos.Set(newpos) + vel.ResetDirectionAndStop() +} + func (system *ObstacleSystem) Update() error { observer := observers.GetGameObserver(system.World) @@ -58,16 +75,15 @@ func (system *ObstacleSystem) Update() error { ok, newpos := obsposition.Intersects(playerposition, playervelocity) if ok { - // slog.Debug("bumped into obstacle", "obstacle", obstacle) - - if CheckObstacleSide(playervelocity, obsvelocity.Direction) { + if CheckObstacleSide(playervelocity, obsvelocity) { // player died EntitiesToRemove = append(EntitiesToRemove, player) } else { // bumped into nonlethal obstacle side, stop the // player, set the obstacle in motion, if possible - //slog.Debug("bump not die", "originalpos", playerposition, "newpos", newpos, "obspos", obsposition) + //slog.Debug("bump not die", "playervelo", playervelocity) obsvelocity.Set(playervelocity) + //slog.Debug("bump not die", "obsvelo", obsvelocity) playervelocity.Change(Stop) playerposition.Set(newpos) } @@ -91,29 +107,12 @@ func (system *ObstacleSystem) Update() error { } } - //system.GridContainer.Grid.CheckGridCollision(obsposition, obsvelocity) - // FIXME: this is the same loop as in player_system, unite the - // two, just iterate over all entities with pos,vel,render, dammit + // check if [moving] obstacle collides with walls or edges + system.GridContainer.Grid.CheckGridCollision( + obsposition, obsvelocity, ObstacleBumpEdgeResponder, ObstacleBumpWallResponder) + if obsvelocity.Moving() { - ok, newpos := system.GridContainer.Grid.BumpEdge(obsposition, obsvelocity) - if ok { - //slog.Debug("falling off the edge", "newpos", newpos) - obsposition.Set(newpos) - } else { - ok, tilepos := system.GridContainer.Grid.GetSolidNeighborPosition(obsposition, obsvelocity, true) - if ok { - intersects, newpos := tilepos.Intersects(obsposition, obsvelocity) - if intersects { - // slog.Debug("collision with foreign obstacle detected", "tile", - // tilepos, "obs", obsposition, "new", newpos) - - obsposition.Set(newpos) - obsvelocity.ResetDirectionAndStop() - } - } - - obsposition.Move(obsvelocity) - } + obsposition.Move(obsvelocity) } } @@ -152,13 +151,14 @@ func (system *ObstacleSystem) Draw(screen *ebiten.Image) { } // return true if obstacle weapon points into the direction the player is moving -func CheckObstacleSide(playervelocity *Velocity, obsdirection int) bool { +// OR if the weapon points towards a non-moving player +func CheckObstacleSide(playervelocity *Velocity, obsvelocity *Velocity) bool { movingdirection := playervelocity.InvertDirection() - if movingdirection == Stop || movingdirection == obsdirection { + if movingdirection == Stop || movingdirection == obsvelocity.PointingAt { slog.Debug("Damaging obstacle collision", "playerdirection", util.DirectionStr(playervelocity.Direction), - "obsdirection", util.DirectionStr(obsdirection), + "obsdirection", util.DirectionStr(obsvelocity.PointingAt), "movingdirection", util.DirectionStr(movingdirection), ) return true diff --git a/systems/player_system.go b/systems/player_system.go index 59b3ec8..8d4f2a1 100644 --- a/systems/player_system.go +++ b/systems/player_system.go @@ -33,6 +33,23 @@ func NewPlayerSystem(world *ecs.World, gridcontainer *grid.GridContainer, width, return system } +func PlayerBumpEdgeResponder( + pos *components.Position, + vel *components.Velocity, + newpos *components.Position) { + + pos.Set(newpos) +} + +func PlayerBumpWallResponder( + pos *components.Position, + vel *components.Velocity, + newpos *components.Position) { + + pos.Set(newpos) + vel.Change(Stop) +} + func (system PlayerSystem) Update() error { var EntitiesToRemove []ecs.Entity @@ -54,7 +71,8 @@ func (system PlayerSystem) Update() error { system.CheckMovement(playerposition, velocity, player) // check if player collides with walls or edges - system.CheckGridCollision(playerposition, velocity) + system.GridContainer.Grid.CheckGridCollision( + playerposition, velocity, PlayerBumpEdgeResponder, PlayerBumpWallResponder) if count > 1 { // check if player collides with another player, fuse them if any @@ -175,30 +193,6 @@ func (system *PlayerSystem) CheckMovement( } } -func (system *PlayerSystem) CheckGridCollision( - playerposition *components.Position, - velocity *components.Velocity) { - - if velocity.Moving() { - ok, newpos := system.GridContainer.Grid.BumpEdge(playerposition, velocity) - if ok { - //slog.Debug("falling off the edge", "newpos", newpos) - playerposition.Set(newpos) - } else { - ok, tilepos := system.GridContainer.Grid.GetSolidNeighborPosition(playerposition, velocity, true) - if ok { - // slog.Debug("HaveSolidNeighbor", "ok", ok, "tilepos", - // tilepos.Point(), "playerpos", playerposition) - intersects, newpos := tilepos.Intersects(playerposition, velocity) - if intersects { - playerposition.Set(newpos) - velocity.Change(Stop) - } - } - } - } -} - func (system *PlayerSystem) CheckPlayerCollision( position *components.Position, velocity *components.Velocity, From 52c4cb4e6662b19d63c80463c21aedd720b53d66 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Thu, 21 Mar 2024 13:28:44 +0100 Subject: [PATCH 2/2] bump --- config/static.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/static.go b/config/static.go index 9aa4789..0f6e9a4 100644 --- a/config/static.go +++ b/config/static.go @@ -16,7 +16,7 @@ const ( const PLAYERSPEED int = 5 const PARTICLE_LOOPWAIT time.Duration = 250 * time.Millisecond const LEVEL_END_WAIT time.Duration = 500 * time.Millisecond -const version string = "1.1.0" +const version string = "1.2.0" const MenuRectX int = 600 const MenuRectY int = 0