separate-collision-checker #1

Merged
scip merged 2 commits from separate-collision-checker into master 2024-03-21 13:31:03 +01:00
10 changed files with 202 additions and 91 deletions

View File

@ -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]

View File

@ -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": [],

View File

@ -61,6 +61,8 @@ func (velocity *Velocity) InvertDirection() int {
return South
case All:
return Stop
case Stop:
return Stop
}
// should not happen

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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(),

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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,