Magnetize against metal tiles
This commit is contained in:
parent
1214cb3aa6
commit
1a7bc89695
|
@ -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"/>
|
||||
|
|
|
@ -10,7 +10,6 @@ type Sprite struct {
|
|||
Image *ebiten.Image
|
||||
HorizontalFlip bool
|
||||
VerticalFlip bool
|
||||
DiagonalFlip bool // TODO unimplemented
|
||||
|
||||
Angle float64
|
||||
|
||||
|
|
101
entity/player.go
101
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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue