diff --git a/asset/map/map.tmx b/asset/map/map.tmx index 7b1c948..88b2393 100644 --- a/asset/map/map.tmx +++ b/asset/map/map.tmx @@ -1,5 +1,5 @@ - + @@ -106,9 +106,25 @@ - - - + + + + + + + + + + + + + + + + + + + diff --git a/component/sprite.go b/component/sprite.go index cbf5f25..ffb4691 100644 --- a/component/sprite.go +++ b/component/sprite.go @@ -10,7 +10,6 @@ type Sprite struct { Image *ebiten.Image HorizontalFlip bool VerticalFlip bool - DiagonalFlip bool // TODO unimplemented Angle float64 diff --git a/entity/player.go b/entity/player.go index 8514a6f..5cab84b 100644 --- a/entity/player.go +++ b/entity/player.go @@ -16,7 +16,7 @@ import ( ) const ( - playerVelocity = 40.0 + playerVelocity = 50.0 playerGroundAccelTime = 0.1 playerGroundAccel = playerVelocity / playerGroundAccelTime playerAirAccelTime = 0.25 @@ -25,15 +25,11 @@ const ( ) func NewPlayer() gohan.Entity { - - playerBody := world.Space.AddBody(cp.NewBody(4, cp.INFINITY)) + playerBody := world.Space.AddBody(cp.NewBody(1, cp.INFINITY)) playerBody.SetPosition(cp.Vector{0, 0}) playerBody.SetVelocityUpdateFunc(playerUpdateVelocity) - x1, y1 := 1.0, 1.0 - x2, y2 := 15.0, 15.0 - - playerShape := world.Space.AddShape(cp.NewBox2(playerBody, cp.BB{x1, y2, x2, y1}, 0)) + playerShape := world.Space.AddShape(cp.NewCircle(playerBody, 8, cp.Vector{8, 0})) playerShape.SetElasticity(0) playerShape.SetFriction(0) @@ -57,36 +53,60 @@ func NewPlayer() gohan.Entity { player.AddComponent(&component.Player{}) + world.PlayerIdleFrames = asset.PlayerIdleFrames + world.PlayerWalkFrames = asset.PlayerWalkFrames + return player } func playerUpdateVelocity(body *cp.Body, gravity cp.Vector, damping, dt float64) { - jumpState := false - - // Grab the grounding normal from last frame - groundNormal := cp.Vector{} - /*world.PlayerBody.EachArbiter(func(arb *cp.Arbiter) { - n := arb.Normal().Neg() - - if n.Y > groundNormal.Y { - groundNormal = n + var normal cp.Vector + var grounded bool + world.PlayerBody.EachArbiter(func(arb *cp.Arbiter) { + normal = arb.Normal().Neg() + if normal.X != 0 || normal.Y != 0 { + grounded = true } - })*/ + }) + world.PlayerNormal = normal + world.PlayerGrounded = grounded - grounded := groundNormal.Y > 0 - if groundNormal.Y < 0 { - //remainingBoost = 0 + // Apply magnetization by overwriting gravity. + if world.MagnetActive { + // Case MagnetizeDown is not handled because gravity is already set to the normal vector. + switch world.MagnetDirection { + case world.MagnetizeDownLeft: + gravity = cp.Vector{-world.Gravity / 4, world.Gravity / 4} + case world.MagnetizeLeft: + gravity = cp.Vector{-world.Gravity, 0} + case world.MagnetizeUpLeft: + gravity = cp.Vector{-world.Gravity / 4, -world.Gravity / 4} + case world.MagnetizeUp: + gravity = cp.Vector{0, -world.Gravity} + case world.MagnetizeUpRight: + gravity = cp.Vector{world.Gravity / 4, -world.Gravity / 4} + case world.MagnetizeRight: + gravity = cp.Vector{world.Gravity, 0} + case world.MagnetizeDownRight: + gravity = cp.Vector{world.Gravity / 4, world.Gravity / 4} + if grounded && (ebiten.IsKeyPressed(ebiten.KeyW) || ebiten.IsKeyPressed(ebiten.KeyS)) { + //gravity.Y = 0 + } + //log.Println("DOWN RIGHT", gravity) + } } - - // Do a normal-ish update - boost := jumpState // && remainingBoost > 0 - var g cp.Vector - if !boost { - g = gravity - } - body.UpdateVelocity(g, damping, dt) + body.UpdateVelocity(gravity, damping, dt) xDir := 0.0 + yDir := 0.0 + if ebiten.IsKeyPressed(ebiten.KeyW) { + yDir -= 1 + world.LastWalkDirU = true + } + if ebiten.IsKeyPressed(ebiten.KeyS) { + yDir += 1 + world.LastWalkDirU = false + } if ebiten.IsKeyPressed(ebiten.KeyA) { xDir -= 1 world.LastWalkDirL = false @@ -96,25 +116,18 @@ func playerUpdateVelocity(body *cp.Body, gravity cp.Vector, damping, dt float64) world.LastWalkDirL = true } - // Target horizontal speed for air/ground control + // Target velocities based on user input. targetVx := playerVelocity * xDir - - // Update the surface velocity and friction - // Note that the "feet" move in the opposite direction of the player. - surfaceV := cp.Vector{-targetVx, 0} - world.PlayerShape.SetSurfaceV(surfaceV) - if grounded { - world.PlayerShape.SetFriction(playerGroundAccel / world.Gravity) - } else { - world.PlayerShape.SetFriction(0) + targetVy := playerVelocity * yDir + if targetVx == 0 { + targetVx = gravity.X } - - // Apply air control if not grounded - if !grounded { - v := world.PlayerBody.Velocity() - world.PlayerBody.SetVelocity(cp.LerpConst(v.X, targetVx, playerAirAccel*dt), v.Y) + if targetVy == 0 { + targetVy = gravity.Y } v := body.Velocity() - body.SetVelocity(v.X, cp.Clamp(v.Y, -fallVelocity, cp.INFINITY)) + vx, vy := cp.Clamp(v.X, -fallVelocity, fallVelocity), cp.Clamp(v.Y, -fallVelocity, fallVelocity) + vx, vy = cp.LerpConst(vx, targetVx, playerAirAccel*dt), cp.LerpConst(vy, targetVy, playerAirAccel*dt) + body.SetVelocity(vx, vy) } diff --git a/game/game.go b/game/game.go index b4f6f5a..2acab49 100644 --- a/game/game.go +++ b/game/game.go @@ -27,8 +27,8 @@ func NewGame() (*Game, error) { } func (g *Game) addSystems() { - gohan.AddSystem(system.NewPlayerMoveSystem()) gohan.AddSystem(system.NewPhysicsSystem()) + gohan.AddSystem(system.NewPlayerMoveSystem()) gohan.AddSystem(system.NewMovementSystem()) gohan.AddSystem(system.NewRenderSystem()) gohan.AddSystem(system.NewRenderMessageSystem()) diff --git a/level/level.go b/level/level.go index 373e293..0b38330 100644 --- a/level/level.go +++ b/level/level.go @@ -3,6 +3,7 @@ package level import ( "image" "log" + "math/rand" "path/filepath" "time" @@ -74,7 +75,6 @@ func LoadMap() { Image: world.TileImages[t.Tileset.FirstGID+t.ID], HorizontalFlip: t.HorizontalFlip, VerticalFlip: t.VerticalFlip, - DiagonalFlip: t.DiagonalFlip, } mapTile.AddComponent(sprite) @@ -105,6 +105,7 @@ func LoadMap() { sprite.Frames = orangeBeamTiles } sprite.NumFrames = len(sprite.Frames) + sprite.Frame = rand.Intn(sprite.NumFrames) sprite.FrameTime = 100 * time.Millisecond break } @@ -205,7 +206,7 @@ func LoadMap() { for y := range metalTiles { for x, filled := range metalTiles[y] { if filled { - r := image.Rect(x*world.TileSize, y*world.TileSize, (x+1)*world.TileSize-1, (y+1)*world.TileSize-1) + r := image.Rect(x*world.TileSize, y*world.TileSize, (x+1)*world.TileSize, (y+1)*world.TileSize) world.MetalRects = append(world.MetalRects, r) addCollisionBox(x, y) diff --git a/system/input_move.go b/system/input_move.go index c227ccb..35f0f21 100644 --- a/system/input_move.go +++ b/system/input_move.go @@ -1,6 +1,7 @@ package system import ( + "image" "os" "code.rocketnine.space/tslocum/doctorlectro/component" @@ -33,15 +34,11 @@ func (s *playerMoveSystem) Update(e gohan.Entity) error { } if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyV) { - v := 1 - if ebiten.IsKeyPressed(ebiten.KeyShift) { - v = 2 - } - if world.Debug == v { - world.Debug = 0 - } else { - world.Debug = v + v := world.Debug + 1 + if v > 2 { + v = 0 } + world.Debug = v return nil } @@ -67,41 +64,157 @@ func (s *playerMoveSystem) Update(e gohan.Entity) error { return nil } - return nil // TODO + magnetActive := ebiten.IsKeyPressed(ebiten.KeySpace) + if magnetActive { + // Find nearest surface, then magnetize. - pressLeft := ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) - pressRight := ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) - pressUp := ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) - pressDown := ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) + wallThreshold := 16 // TODO cling when added space is used - if (pressLeft && !pressRight) || - (pressRight && !pressLeft) { - if pressLeft { - if s.Velocity.X > -maxMoveSpeed { - s.Velocity.X += -moveSpeed + boxOffset := 4 + + position := world.PlayerBody.Position() + + collisionSize := 12 + + px1, py1 := int(position.X)+boxOffset, int(position.Y)+boxOffset + px2, py2 := px1+collisionSize-(boxOffset*2), py1+collisionSize-(boxOffset*2) + + playerRectD := image.Rect(px1, py1, px2, py2+wallThreshold) + playerRectDL := image.Rect(px1-wallThreshold, py1, px2, py2+wallThreshold) + playerRectL := image.Rect(px1-wallThreshold, py1, px2, py2) + playerRectUL := image.Rect(px1-wallThreshold, py1-wallThreshold, px2, py2) + playerRectU := image.Rect(px1, py1-wallThreshold, px2, py2) + playerRectUR := image.Rect(px1, py1-wallThreshold, px2+wallThreshold, py2) + playerRectR := image.Rect(px1, py1, px2+wallThreshold, py2) + playerRectDR := image.Rect(px1, py1, px2+wallThreshold, py2+wallThreshold) + + var ( + collideL bool + collideR bool + collideU bool + collideD bool + collideUL bool + collideUR bool + collideDL bool + collideDR bool + ) + for i := range world.MetalRects { + if !collideL && world.MetalRects[i].Overlaps(playerRectL) { + collideL = true } - world.LastWalkDirL = true - } else if pressRight { - if s.Velocity.X < maxMoveSpeed { - s.Velocity.X += moveSpeed + if !collideR && world.MetalRects[i].Overlaps(playerRectR) { + /*log.Println("OVERLAP", position.X, position.Y) + log.Println(playerRectR) + log.Println(world.MetalRects[i])*/ + collideR = true } - world.LastWalkDirL = false + if !collideU && world.MetalRects[i].Overlaps(playerRectU) { + collideU = true + } + if !collideD && world.MetalRects[i].Overlaps(playerRectD) { + collideD = true + } + if !collideUL && world.MetalRects[i].Overlaps(playerRectUL) { + collideUL = true + } + if !collideUR && world.MetalRects[i].Overlaps(playerRectUR) { + collideUR = true + } + if !collideDL && world.MetalRects[i].Overlaps(playerRectDL) { + collideDL = true + } + if !collideDR && world.MetalRects[i].Overlaps(playerRectDR) { + collideDR = true + } + } + // TODO combined vector (allow diagonal gravity) + //log.Println("COLLIDE") + if collideD { + if collideR { + world.MagnetDirection = world.MagnetizeDown + if ebiten.IsKeyPressed(ebiten.KeyS) { + world.MagnetDirection = world.MagnetizeDown + } else { + world.MagnetDirection = world.MagnetizeDownRight + } + //log.Println("DOWN RIGHT") + } else if collideL { + // TODO copy for all + if ebiten.IsKeyPressed(ebiten.KeyS) { + world.MagnetDirection = world.MagnetizeDown + } else { + world.MagnetDirection = world.MagnetizeDownLeft + } + //log.Println("DOWN LEFT") + } else { + world.MagnetDirection = world.MagnetizeDown + //log.Println("DOWN") + } + } else if collideU { + //log.Println("U") + if collideR { + world.MagnetDirection = world.MagnetizeUp + world.MagnetDirection = world.MagnetizeUpRight + //log.Println("UP RIGHT") + } else if collideL { + world.MagnetDirection = world.MagnetizeUp + world.MagnetDirection = world.MagnetizeUpLeft + //log.Println("UP LEFT") + } else { + world.MagnetDirection = world.MagnetizeUp + //log.Println("UP") + } + } else if collideR { + //log.Println("R") + world.MagnetDirection = world.MagnetizeRight + + if collideU { + world.MagnetDirection = world.MagnetizeUpRight + //log.Println("UP RIGHT") + } else if collideD { + world.MagnetDirection = world.MagnetizeDownRight + //log.Println("DOWN RIGHT") + } else { + world.MagnetDirection = world.MagnetizeRight + //log.Println("RIGHT") + } + } else if collideL { + //log.Println("L") + if collideU { + world.MagnetDirection = world.MagnetizeUpLeft + //log.Println("UP LEFT") + } else if collideD { + world.MagnetDirection = world.MagnetizeDownLeft + //log.Println("DOWN LEFT") + } else { + world.MagnetDirection = world.MagnetizeLeft + //log.Println("LEFT") + } + } else if collideUL { + //log.Println("UL") + world.MagnetDirection = world.MagnetizeUpLeft + } else if collideUR { + //log.Println("UR") + world.MagnetDirection = world.MagnetizeUpRight + } else if collideDL { + //log.Println("DL") + world.MagnetDirection = world.MagnetizeDownLeft + } else if collideDR { + //log.Println("DR") + world.MagnetDirection = world.MagnetizeDownRight + } else { + //log.Println("NONE") + magnetActive = false } } - if (pressUp && !pressDown) || - (pressDown && !pressUp) { - if pressUp && world.Clinging { - s.Velocity.Y += -moveSpeed - world.LastWalkDirU = true - } else if pressDown && world.Clinging { - s.Velocity.Y += moveSpeed - world.LastWalkDirU = false - } - } + world.MagnetActive = magnetActive return nil } func (s *playerMoveSystem) Draw(_ gohan.Entity, _ *ebiten.Image) error { return gohan.ErrUnregister } + +func (s *playerMoveSystem) updateSprite() { +} diff --git a/system/movement.go b/system/movement.go index e68c699..7be1f60 100644 --- a/system/movement.go +++ b/system/movement.go @@ -8,6 +8,8 @@ import ( "github.com/hajimehoshi/ebiten/v2" ) +const walkThreshold = 4.00 + type MovementSystem struct { Position *component.Position @@ -31,29 +33,81 @@ func (s *MovementSystem) Update(e gohan.Entity) error { if position.Body != nil { pos := position.Body.Position() + vel := position.Body.Velocity() position.X, position.Y = pos.X, pos.Y - if s.Sprite != nil { - vel := position.Body.Velocity() + if e == world.Player && s.Sprite != nil { + if world.MagnetActive { + if world.MagnetDirection == world.MagnetizeDown || world.MagnetDirection == world.MagnetizeLeft || world.MagnetDirection == world.MagnetizeUp || world.MagnetDirection == world.MagnetizeRight { + world.PlayerDirection = world.MagnetDirection + } else if world.MagnetDirection == world.MagnetizeDownLeft || world.MagnetDirection == world.MagnetizeUpLeft { + world.PlayerDirection = world.MagnetizeLeft + } else if world.MagnetDirection == world.MagnetizeDownRight || world.MagnetDirection == world.MagnetizeUpRight { + world.PlayerDirection = world.MagnetizeRight + } else { + panic(world.MagnetDirection) + } + } + /*if world.MagnetDirection == world.MagnetizeDownRight && (world.PlayerNormal.Y > -0.5 || world.PlayerNormal.Y < 0.5) { + faceDirection = world.MagnetizeDown + }*/ + // TODO + + // Reset to MagnetizeDown values. s.Sprite.HorizontalFlip = !world.LastWalkDirL + s.Sprite.VerticalFlip = false + s.Sprite.OffsetX = 0 + s.Sprite.OffsetY = -17 idleFrames := asset.PlayerIdleFrames walkFrames := asset.PlayerWalkFrames + switch world.PlayerDirection { + case world.MagnetizeDown: + // Already set. + case world.MagnetizeLeft: + if vel.X <= walkThreshold { + s.Sprite.HorizontalFlip = false + s.Sprite.VerticalFlip = world.LastWalkDirU + idleFrames = asset.PlayerIdleFramesRot90 + walkFrames = asset.PlayerWalkFramesRot90 + s.Sprite.OffsetX = -1 + s.Sprite.OffsetY = -8 + } else { + // Magnetize down. + } + case world.MagnetizeUp: + s.Sprite.HorizontalFlip = !world.LastWalkDirL + s.Sprite.VerticalFlip = true + s.Sprite.OffsetY = 1 + case world.MagnetizeRight: + if vel.X >= -walkThreshold { + s.Sprite.HorizontalFlip = true + s.Sprite.VerticalFlip = world.LastWalkDirU + idleFrames = asset.PlayerIdleFramesRot90 + walkFrames = asset.PlayerWalkFramesRot90 + s.Sprite.OffsetX = 1 + s.Sprite.OffsetY = -8 + } else { + // Magnetize down. + } + default: + panic("DEFAULT") + } + world.PlayerIdleFrames = idleFrames + world.PlayerWalkFrames = walkFrames - const walkThreshold = 4.00 - walking := vel.X < -walkThreshold || vel.X > walkThreshold + moveX := vel.X < -walkThreshold || vel.X > walkThreshold + moveY := vel.Y < -walkThreshold || vel.Y > walkThreshold + walking := (ebiten.IsKeyPressed(ebiten.KeyW) && moveY) || (ebiten.IsKeyPressed(ebiten.KeyS) && moveY) || (ebiten.IsKeyPressed(ebiten.KeyA) && moveX) || (ebiten.IsKeyPressed(ebiten.KeyD) && moveX) if walking { s.Sprite.Frames = walkFrames } else { s.Sprite.Frames = idleFrames } + s.Sprite.Image = s.Sprite.Frames[s.Sprite.Frame] } } - - return nil // TODO - s.handleLevelCollisions(e) - s.handleScreenCollisions(e) return nil } @@ -65,200 +119,6 @@ func (_ *MovementSystem) Draw(_ gohan.Entity, _ *ebiten.Image) error { return gohan.ErrUnregister } -// handleLevelCollisions handles collisions between the player and the level. -func (s *MovementSystem) handleLevelCollisions(e gohan.Entity) { - /* - vx, vy := velocity.X, velocity.Y - if e == world.Player && world.Debug != 0 && ebiten.IsKeyPressed(ebiten.KeyShift) { - vx, vy = vx*2, vy*2 - } - - // Check hazard collisions. - playerRect := image.Rect(int(position.X), int(position.Y), int(position.X+world.PlayerWidth), int(position.Y+world.PlayerHeight)) - for i := range world.HazardRects { - if playerRect.Overlaps(world.HazardRects[i]) { - world.SetGameOver(false) - return - } - } - - // Check velocity collisions. - const gravityCollisionCheck = 0.1 - newX, newY := position.X+vx, position.Y+vy - playerRectX := image.Rect(int(newX), int(position.Y), int(newX+world.PlayerWidth), int(position.Y+world.PlayerHeight)) - playerRectY := image.Rect(int(position.X), int(newY), int(position.X+world.PlayerWidth), int(newY+world.PlayerHeight)) - playerRectXY := image.Rect(int(newX), int(newY), int(newX+world.PlayerWidth), int(newY+world.PlayerHeight)) - playerRectG := image.Rect(int(position.X), int(position.Y+gravityCollisionCheck), int(position.X+world.PlayerWidth), int(position.Y+gravityCollisionCheck+world.PlayerHeight)) - - var ( - collideX bool - collideY bool - collideXY bool - collideG bool - ) - for i := range world.MetalRects { - if !collideX && world.MetalRects[i].Overlaps(playerRectX) { - collideX = true - } - if !collideY && world.MetalRects[i].Overlaps(playerRectY) { - collideY = true - } - if !collideXY && world.MetalRects[i].Overlaps(playerRectXY) { - collideXY = true - } - if !collideG && world.MetalRects[i].Overlaps(playerRectG) { - collideG = true - } - } - if collideXY || collideX || collideY { - if collideY { - if !collideX { - position.X = position.X + vx - } else { - s.Velocity.X = 0 - } - s.Velocity.Y = 0 - } else if collideX { - if !collideY { - position.Y = position.Y + vy - } else { - s.Velocity.Y = 0 - } - s.Velocity.X = 0 - } else { - s.Velocity.X = 0 - s.Velocity.Y = 0 - } - } else { - position.X, position.Y = position.X+vx, position.Y+vy - } - - world.MagnetActive = ebiten.IsKeyPressed(ebiten.KeySpace) - - const gravitySize = 0.02 - gravityX, gravityY := 0.0, gravitySize - - // Magnetize against nearest wall. - var clinging bool - if world.MagnetActive && !collideG { - wallThreshold := 16.0 // TODO cling when added space is used - - playerRectL := image.Rect(int(position.X-wallThreshold), int(position.Y), int(position.X-wallThreshold+world.PlayerWidth), int(position.Y+world.PlayerHeight)) - playerRectR := image.Rect(int(position.X+wallThreshold), int(position.Y), int(position.X+wallThreshold+world.PlayerWidth), int(position.Y+world.PlayerHeight)) - playerRectU := image.Rect(int(position.X), int(position.Y-wallThreshold), int(position.X+world.PlayerWidth), int(position.Y-wallThreshold+world.PlayerHeight)) - - var ( - collideL bool - collideR bool - collideU bool - ) - for i := range world.MetalRects { - if !collideL && world.MetalRects[i].Overlaps(playerRectL) { - collideL = true - } - if !collideR && world.MetalRects[i].Overlaps(playerRectR) { - collideR = true - } - if !collideU && world.MetalRects[i].Overlaps(playerRectU) { - collideU = true - } - } - if collideL || collideR || collideU { - log.Println("ground collision", collideG) - log.Println("cling", velocity.Y) - - clinging = true - - if collideL { - gravityX, gravityY = -gravitySize, 0.00 - } else if collideR { - gravityX, gravityY = gravitySize, 0.00 - } else if collideU { - gravityX, gravityY = 0.00, -gravitySize - } - } - } - world.Clinging = clinging - - dampenMultiplierX, dampenMultiplierY := 3.0, 1.0 - if gravityX != 0 { - dampenMultiplierX, dampenMultiplierY = 1, 3 - } - - const dampen = 100 - if s.Velocity.Y < 0 { - s.Velocity.Y -= s.Velocity.Y / (dampen / dampenMultiplierY) - } else if s.Velocity.Y > 0 { - s.Velocity.Y -= s.Velocity.Y / (dampen / dampenMultiplierY) - } - if s.Velocity.X < 0 { - s.Velocity.X -= s.Velocity.X / (dampen / dampenMultiplierX) - } else if s.Velocity.X > 0 { - s.Velocity.X -= s.Velocity.X / (dampen / dampenMultiplierX) - } - - if s.Velocity.X > -0.01 && s.Velocity.X < 0.01 { - s.Velocity.X = 0 - } - if s.Velocity.Y > -0.01 && s.Velocity.Y < 0.01 { - s.Velocity.Y = 0 - } - - // Apply gravity. - const maxSpeed = 8.0 - s.Velocity.X, s.Velocity.Y = s.Velocity.X+gravityX, s.Velocity.Y+gravityY - if s.Velocity.X < -maxSpeed { - s.Velocity.X = -maxSpeed - } else if s.Velocity.X > maxSpeed { - s.Velocity.X = maxSpeed - } - if s.Velocity.Y < -maxSpeed { - s.Velocity.Y = -maxSpeed - } else if s.Velocity.Y > maxSpeed { - s.Velocity.Y = maxSpeed - } - - // Update player sprite. - s.Sprite.HorizontalFlip = world.LastWalkDirL - s.Sprite.VerticalFlip = false - - offsetY := -12.0 - - idleFrames := asset.PlayerIdleFrames - walkFrames := asset.PlayerWalkFrames - if gravityX < 0 { - idleFrames = asset.PlayerIdleFramesRot90 - walkFrames = asset.PlayerWalkFramesRot90 - s.Sprite.VerticalFlip = world.LastWalkDirU - s.Sprite.HorizontalFlip = false - - offsetY = 0 - } else if gravityX > 0 { - idleFrames = asset.PlayerIdleFramesRot90 - walkFrames = asset.PlayerWalkFramesRot90 - s.Sprite.VerticalFlip = world.LastWalkDirU - s.Sprite.HorizontalFlip = true - - offsetY = 0 - } else if gravityY < 0 { - s.Sprite.VerticalFlip = true - - offsetY = 11 - } - - s.Sprite.OffsetY = offsetY - - const walkThreshold = 0.04 - walking := (gravityY != 0 && (s.Velocity.X < -walkThreshold || s.Velocity.X > walkThreshold)) || (gravityX != 0 && (s.Velocity.Y < -walkThreshold || s.Velocity.Y > walkThreshold)) - if walking { - s.Sprite.Frames = walkFrames - } else { - s.Sprite.Frames = idleFrames - } - - */ -} - // handleScreenCollisions forces the player to remain within the screen bounds. func (s *MovementSystem) handleScreenCollisions(e gohan.Entity) { if e != world.Player { @@ -266,6 +126,8 @@ func (s *MovementSystem) handleScreenCollisions(e gohan.Entity) { } position := s.Position + // TODO check if in screen transition area, automatically scroll based on current screen position + screenX, screenY := s.levelCoordinatesToScreen(position.X, position.Y) if screenX < 0 { diff := screenX diff --git a/system/render.go b/system/render.go index 97d97b9..ff388a9 100644 --- a/system/render.go +++ b/system/render.go @@ -41,12 +41,6 @@ func (s *RenderSystem) Update(_ gohan.Entity) error { return gohan.ErrUnregister } -func (s *RenderSystem) levelCoordinatesToScreen(x, y float64) (float64, float64) { - px, py := world.CamX, world.CamY - py *= -1 - return (x - px) * s.camScale, (y + py) * s.camScale -} - // renderSprite renders a sprite on the screen. func (s *RenderSystem) renderSprite(x float64, y float64, offsetx float64, offsety float64, angle float64, geoScale float64, colorScale float64, alpha float64, hFlip bool, vFlip bool, sprite *ebiten.Image, target *ebiten.Image) int { if alpha < .01 || colorScale < .01 { @@ -54,7 +48,7 @@ func (s *RenderSystem) renderSprite(x float64, y float64, offsetx float64, offse } // Skip drawing off-screen tiles. - drawX, drawY := s.levelCoordinatesToScreen(x, y) + drawX, drawY := world.LevelCoordinatesToScreen(x, y) const padding = TileWidth * 4 width, height := float64(TileWidth), float64(TileWidth) left := drawX diff --git a/system/renderdebug.go b/system/renderdebug.go index 46f61f3..6c3225b 100644 --- a/system/renderdebug.go +++ b/system/renderdebug.go @@ -2,6 +2,7 @@ package system import ( "fmt" + "image" "image/color" _ "image/png" @@ -10,6 +11,7 @@ import ( "code.rocketnine.space/tslocum/gohan" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/jakecoffman/cp" ) type RenderDebugTextSystem struct { @@ -39,6 +41,27 @@ func (s *RenderDebugTextSystem) Draw(e gohan.Entity, screen *ebiten.Image) error return nil } + if world.Debug > 1 { + world.Space.EachBody(func(body *cp.Body) { + fillColor := color.RGBA{255, 255, 255, 255} + if body == world.PlayerBody { + fillColor = color.RGBA{124, 0, 255, 255} + } + + body.EachShape(func(shape *cp.Shape) { + bb := shape.BB() + var emptyBB cp.BB + if bb != emptyBB { + l, t := world.LevelCoordinatesToScreen(bb.L, bb.T) + r, b := world.LevelCoordinatesToScreen(bb.R, bb.B) + rect := image.Rect(int(l), int(t), int(r), int(b)) + screen.SubImage(rect).(*ebiten.Image).Fill(fillColor) + return + } + }) + }) + } + position := s.Position vel := world.PlayerBody.Velocity() diff --git a/world/world.go b/world/world.go index 6cef2bc..c79a46b 100644 --- a/world/world.go +++ b/world/world.go @@ -28,6 +28,17 @@ const ( const Gravity = 500.0 +const ( + MagnetizeDown = iota + MagnetizeDownLeft + MagnetizeLeft + MagnetizeUpLeft + MagnetizeUp + MagnetizeUpRight + MagnetizeRight + MagnetizeDownRight +) + var ( Debug int @@ -61,7 +72,12 @@ var ( MetalRects []image.Rectangle - MagnetActive bool + PlayerGrounded bool + + MagnetActive bool + MagnetDirection int // 0 down, 1 left, 2 up, 3 right + + PlayerDirection int Clinging bool @@ -73,6 +89,11 @@ var ( PlayerShape *cp.Shape PlayerBody *cp.Body + + PlayerNormal cp.Vector + + PlayerIdleFrames []*ebiten.Image + PlayerWalkFrames []*ebiten.Image ) func StartGame() { @@ -99,3 +120,9 @@ func SetGameOver(win bool) { }) } } + +func LevelCoordinatesToScreen(x, y float64) (float64, float64) { + px, py := CamX, CamY + py *= -1 + return (x - px) * 1, (y + py) +}