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