Metroidvania video game
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
3.5 KiB

package entity
import (
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/jakecoffman/cp"
"code.rocketnine.space/tslocum/doctorlectro/world"
"code.rocketnine.space/tslocum/doctorlectro/asset"
"code.rocketnine.space/tslocum/doctorlectro/component"
"code.rocketnine.space/tslocum/gohan"
)
const (
playerVelocity = 120.0
playerGroundAccelTime = 0.1
playerGroundAccel = playerVelocity / playerGroundAccelTime
playerAirAccelTime = 0.5
playerAirAccel = playerVelocity / playerAirAccelTime
fallVelocity = 900.0
)
func NewPlayer() gohan.Entity {
playerBody := world.Space.AddBody(cp.NewBody(1, cp.INFINITY))
playerBody.SetPosition(cp.Vector{0, 0})
playerBody.SetVelocityUpdateFunc(playerUpdateVelocity)
playerShape := world.Space.AddShape(cp.NewCircle(playerBody, 7.5, cp.Vector{}))
playerShape.SetElasticity(0)
playerShape.SetFriction(0)
world.PlayerBody = playerBody
world.PlayerShape = playerShape
player := gohan.NewEntity()
player.AddComponent(&component.Position{
Body: world.PlayerBody,
})
player.AddComponent(&component.Velocity{})
player.AddComponent(&component.Sprite{
Frames: asset.PlayerIdleFrames,
FrameTime: 150 * time.Millisecond,
NumFrames: len(asset.PlayerIdleFrames),
Layer: world.LayerPlayer,
})
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) {
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
// Apply magnetization by overwriting gravity.
// Case MagnetizeDown is not handled because gravity is already set to the normal vector.
if world.MagnetActive {
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}
}
}
body.UpdateVelocity(gravity, damping, dt)
xDir := 0.0
yDir := 0.0
if ebiten.IsKeyPressed(ebiten.KeyW) && world.PlayerGrounded {
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
}
if ebiten.IsKeyPressed(ebiten.KeyD) {
xDir += 1
world.LastWalkDirL = true
}
targetVelocity := playerVelocity
if world.Debug > 0 && ebiten.IsKeyPressed(ebiten.KeyShift) {
targetVelocity *= 4
}
// Target velocities based on user input.
targetVx := targetVelocity * xDir
targetVy := targetVelocity * yDir
if targetVx == 0 {
targetVx = gravity.X
}
if targetVy == 0 {
targetVy = gravity.Y
}
v := body.Velocity()
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)
}