2022-06-17 21:17:03 +00:00
|
|
|
package entity
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
2022-06-18 21:08:53 +00:00
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
|
|
|
|
"github.com/jakecoffman/cp"
|
|
|
|
|
|
|
|
"code.rocketnine.space/tslocum/doctorlectro/world"
|
|
|
|
|
2022-06-17 21:17:03 +00:00
|
|
|
"code.rocketnine.space/tslocum/doctorlectro/asset"
|
|
|
|
|
|
|
|
"code.rocketnine.space/tslocum/doctorlectro/component"
|
|
|
|
"code.rocketnine.space/tslocum/gohan"
|
|
|
|
)
|
|
|
|
|
2022-06-18 21:08:53 +00:00
|
|
|
const (
|
|
|
|
playerVelocity = 40.0
|
|
|
|
playerGroundAccelTime = 0.1
|
|
|
|
playerGroundAccel = playerVelocity / playerGroundAccelTime
|
|
|
|
playerAirAccelTime = 0.25
|
|
|
|
playerAirAccel = playerVelocity / playerAirAccelTime
|
|
|
|
fallVelocity = 900.0
|
|
|
|
)
|
|
|
|
|
2022-06-17 21:17:03 +00:00
|
|
|
func NewPlayer() gohan.Entity {
|
2022-06-18 21:08:53 +00:00
|
|
|
|
|
|
|
playerBody := world.Space.AddBody(cp.NewBody(4, 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.SetElasticity(0)
|
|
|
|
playerShape.SetFriction(0)
|
|
|
|
|
|
|
|
world.PlayerBody = playerBody
|
|
|
|
world.PlayerShape = playerShape
|
|
|
|
|
2022-06-17 21:17:03 +00:00
|
|
|
player := gohan.NewEntity()
|
|
|
|
|
2022-06-18 21:08:53 +00:00
|
|
|
player.AddComponent(&component.Position{
|
|
|
|
Body: world.PlayerBody,
|
|
|
|
})
|
2022-06-17 21:17:03 +00:00
|
|
|
|
|
|
|
player.AddComponent(&component.Velocity{})
|
|
|
|
|
|
|
|
player.AddComponent(&component.Sprite{
|
|
|
|
Frames: asset.PlayerIdleFrames,
|
2022-06-18 21:08:53 +00:00
|
|
|
FrameTime: 150 * time.Millisecond,
|
2022-06-17 21:17:03 +00:00
|
|
|
NumFrames: len(asset.PlayerIdleFrames),
|
2022-06-18 21:08:53 +00:00
|
|
|
OffsetY: -10,
|
2022-06-17 21:17:03 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
player.AddComponent(&component.Player{})
|
|
|
|
|
|
|
|
return player
|
|
|
|
}
|
2022-06-18 21:08:53 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
})*/
|
|
|
|
|
|
|
|
grounded := groundNormal.Y > 0
|
|
|
|
if groundNormal.Y < 0 {
|
|
|
|
//remainingBoost = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do a normal-ish update
|
|
|
|
boost := jumpState // && remainingBoost > 0
|
|
|
|
var g cp.Vector
|
|
|
|
if !boost {
|
|
|
|
g = gravity
|
|
|
|
}
|
|
|
|
body.UpdateVelocity(g, damping, dt)
|
|
|
|
|
|
|
|
xDir := 0.0
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
|
|
|
xDir -= 1
|
|
|
|
world.LastWalkDirL = false
|
|
|
|
}
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyD) { // TODO
|
|
|
|
xDir += 1
|
|
|
|
world.LastWalkDirL = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Target horizontal speed for air/ground control
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply air control if not grounded
|
|
|
|
if !grounded {
|
|
|
|
v := world.PlayerBody.Velocity()
|
|
|
|
world.PlayerBody.SetVelocity(cp.LerpConst(v.X, targetVx, playerAirAccel*dt), v.Y)
|
|
|
|
}
|
|
|
|
|
|
|
|
v := body.Velocity()
|
|
|
|
body.SetVelocity(v.X, cp.Clamp(v.Y, -fallVelocity, cp.INFINITY))
|
|
|
|
}
|