Optimizations and debug options

This commit is contained in:
2025-11-25 01:06:35 -07:00
parent 57d08f2f04
commit c84ba37353
13 changed files with 377 additions and 212 deletions

View File

@@ -137,6 +137,9 @@ func New(cfg Config) *Hero {
}
func (h *Hero) Update(input Input, dt float64, bounds Bounds) {
if dt > 0.1 {
dt = 0.1
}
h.updateMovement(input, dt, bounds)
h.updateStamina(input, dt)
h.updateAnimation(dt)
@@ -145,27 +148,30 @@ func (h *Hero) Update(input Input, dt float64, bounds Bounds) {
// Movement and physics
func (h *Hero) updateMovement(input Input, dt float64, bounds Bounds) {
// apply gravity
h.VelocityY += config.Gravity * dt
if h.VelocityY > config.MaxFallSpeed {
h.VelocityY = config.MaxFallSpeed
}
h.Y += h.VelocityY * dt
newY := h.Y + h.VelocityY*dt
footPosition := h.Y
if footPosition >= bounds.Ground {
wasGrounded := h.isGrounded
if newY >= bounds.Ground {
h.Y = bounds.Ground
h.VelocityY = 0
h.isGrounded = true
} else {
h.Y = newY
h.isGrounded = false
}
if input.Jump && h.isGrounded {
if input.Jump && (h.isGrounded || (!wasGrounded && h.isGrounded)) {
h.VelocityY = config.JumpStrength
h.isGrounded = false
}
// horizontal input
targetVelocityX := 0.0
if input.Left {
targetVelocityX -= h.Speed
@@ -178,6 +184,7 @@ func (h *Hero) updateMovement(input Input, dt float64, bounds Bounds) {
h.isMoving = targetVelocityX != 0
// sprinting
h.isSprinting = input.Sprint && h.canSprint && h.Stamina > 0 && h.isMoving
if h.isSprinting {
targetVelocityX *= config.SprintSpeedMultiplier
@@ -188,17 +195,22 @@ func (h *Hero) updateMovement(input Input, dt float64, bounds Bounds) {
friction = config.AirFriction
}
h.VelocityX = h.VelocityX*friction + targetVelocityX*(1-friction)
frictionFactor := 1.0 - (1.0-friction)*dt*60.0
if frictionFactor < 0 {
frictionFactor = 0
}
h.VelocityX = h.VelocityX*frictionFactor + targetVelocityX*(1.0-frictionFactor)
h.X += h.VelocityX * dt
newX := h.X + h.VelocityX*dt
if h.X < h.Radius {
if newX < h.Radius {
h.X = h.Radius
h.VelocityX = 0
}
if h.X > bounds.Width-h.Radius {
} else if newX > bounds.Width-h.Radius {
h.X = bounds.Width - h.Radius
h.VelocityX = 0
} else {
h.X = newX
}
}
@@ -234,12 +246,14 @@ func (h *Hero) updateAnimation(dt float64) {
key.state = animMove
}
// reset animation on state change
if key != h.lastAnimKey {
h.animFrame = 0
h.animTimer = 0
h.lastAnimKey = key
}
// advance animation only when moving
if isMoving {
animSpeed := config.NormalAnimSpeed * 0.5
if h.isSprinting {
@@ -248,12 +262,17 @@ func (h *Hero) updateAnimation(dt float64) {
if !h.isGrounded {
animSpeed = config.JumpingAnimSpeed * 0.5
}
h.animTimer += dt
frameAdvance := int(h.animTimer / animSpeed)
if frameAdvance > 0 {
// precise frame advancement
if h.animTimer >= animSpeed {
frameAdvance := int(h.animTimer / animSpeed)
h.animTimer -= animSpeed * float64(frameAdvance)
h.animFrame = (h.animFrame + frameAdvance) % config.AnimFrameWrap
}
} else {
h.animTimer = 0
}
}
@@ -280,18 +299,28 @@ func (h *Hero) Draw(screen *ebiten.Image) {
actualWidth := float64(bounds.Dx())
op := &ebiten.DrawImageOptions{}
// center sprite horizontally, align bottom to feet
op.GeoM.Translate(-actualWidth/2, -actualHeight)
op.GeoM.Scale(config.HeroSpriteScale, config.HeroSpriteScale)
op.GeoM.Translate(h.X, h.Y)
// round position to nearest pixel for crisp rendering
drawX := float64(int(h.X + 0.5))
drawY := float64(int(h.Y + 0.5))
op.GeoM.Translate(drawX, drawY)
// apply visual state coloring
state := h.getVisualState()
switch state {
case StateExhausted:
op.ColorScale.ScaleWithColor(color.RGBA{R: 255, G: 100, B: 100, A: 255})
case StateSprinting:
// no color modification for sprinting
case StateIdle:
// no color modification for idle
}
op.Filter = ebiten.FilterNearest
screen.DrawImage(sprite, op)
}
}