Magnetize against metal tiles

This commit is contained in:
Trevor Slocum 2022-06-21 19:52:48 -07:00
parent 1214cb3aa6
commit 1a7bc89695
10 changed files with 343 additions and 295 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.5" tiledversion="1.7.2" orientation="orthogonal" renderorder="right-down" width="100" height="100" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="12">
<map version="1.5" tiledversion="1.7.2" orientation="orthogonal" renderorder="right-down" width="100" height="100" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="31">
<tileset firstgid="1" source="../image/robot-tileset/tileset.tsx"/>
<layer id="1" name="Tile Layer 1" width="100" height="100">
<data encoding="csv">
@ -106,9 +106,25 @@
</data>
</layer>
<objectgroup id="5" name="HAZARDS">
<object id="9" gid="257" x="560" y="160" width="16" height="16"/>
<object id="10" gid="257" x="560" y="144" width="16" height="16"/>
<object id="11" gid="257" x="560" y="128" width="16" height="16"/>
<object id="12" gid="257" x="592" y="160" width="16" height="16"/>
<object id="13" gid="257" x="592" y="144" width="16" height="16"/>
<object id="14" gid="257" x="592" y="128" width="16" height="16"/>
<object id="15" gid="257" x="592" y="112" width="16" height="16"/>
<object id="16" gid="257" x="592" y="96" width="16" height="16"/>
<object id="17" gid="257" x="592" y="80" width="16" height="16"/>
<object id="18" gid="257" x="592" y="48" width="16" height="16"/>
<object id="19" gid="257" x="592" y="64" width="16" height="16"/>
<object id="20" gid="260" x="208" y="368" width="16" height="16"/>
<object id="21" gid="260" x="208" y="384" width="16" height="16"/>
<object id="22" gid="260" x="208" y="400" width="16" height="16"/>
<object id="23" gid="260" x="208" y="416" width="16" height="16"/>
<object id="24" gid="260" x="208" y="432" width="16" height="16"/>
<object id="25" gid="260" x="208" y="448" width="16" height="16"/>
<object id="26" gid="260" x="208" y="480" width="16" height="16"/>
<object id="27" gid="260" x="208" y="464" width="16" height="16"/>
<object id="28" gid="260" x="208" y="496" width="16" height="16"/>
<object id="29" gid="260" x="208" y="512" width="16" height="16"/>
<object id="30" gid="260" x="208" y="528" width="16" height="16"/>
</objectgroup>
<objectgroup id="4" name="TRIGGERS">
<object id="5" name="PLAYERSPAWN" x="480" y="112" rotation="90"/>

View File

@ -10,7 +10,6 @@ type Sprite struct {
Image *ebiten.Image
HorizontalFlip bool
VerticalFlip bool
DiagonalFlip bool // TODO unimplemented
Angle float64

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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