Optimizations and debug options
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user