diff --git a/animationMario.go b/animationMario.go index 4781283..1fa547a 100644 --- a/animationMario.go +++ b/animationMario.go @@ -60,45 +60,96 @@ func (a *Animation) updateMarioPosition() { centerX := a.ctx.Width() / 2 centerY := a.ctx.Height() / 2 + // Determine sprite size (use current updown image if available) + var sprite image.Image + if img, ok := a.mario.images[a.mario.updown]; ok && img != nil { + sprite = img + } else { + for _, im := range a.mario.images { + sprite = im + break + } + } + + // default half sizes if sprite missing + halfW, halfH := 8, 8 + if sprite != nil { + halfW = sprite.Bounds().Dx() / 2 + halfH = sprite.Bounds().Dy() / 2 + } + + // allowable center range so the sprite stays fully on the panel + minCenterX := halfW + maxCenterX := a.ctx.Width() - 1 - halfW + minCenterY := halfH + maxCenterY := a.ctx.Height() - 1 - halfH + + // parametric angle before any reflection t := a.mario.angle - marioX := int(math.Round(float64(a.mario.a * math.Cos(t)))) - marioY := int(math.Round(float64(a.mario.b * math.Sin(t)))) - // Adjust position relative to the center of the panel - marioX += centerX - marioY += centerY + // compute candidate center position on the ellipse + marioX := int(math.Round(a.mario.a*math.Cos(t))) + centerX + marioY := int(math.Round(a.mario.b*math.Sin(t))) + centerY - // Check for edge collision and change direction if necessary - if marioX < 0 { - a.mario.angle = -a.mario.angle + math.Pi // Reflect horizontally and adjust angle - } else if marioX >= a.ctx.Width() { - a.mario.angle = -a.mario.angle + math.Pi // Reflect horizontally and adjust angle + // detect collisions against allowed center ranges + collidedX := marioX < minCenterX || marioX > maxCenterX + collidedY := marioY < minCenterY || marioY > maxCenterY + + // Reflect the parametric angle correctly: + // - For horizontal collision we want cos(t_new) = -cos(t) => t_new = Pi - t + // - For vertical collision we want sin(t_new) = -sin(t) => t_new = -t + if collidedX { + t = math.Pi - t + } + if collidedY { + t = -t } - if marioY < 0 { - a.mario.angle = math.Pi - a.mario.angle // Flip vertically and adjust angle - } else if marioY >= a.ctx.Height() { - a.mario.angle = math.Pi - a.mario.angle // Flip vertically and adjust angle + // normalize angle into [0, 2π) + for t < 0 { + t += 2 * math.Pi + } + for t >= 2*math.Pi { + t -= 2 * math.Pi + } + a.mario.angle = t + + // recompute position from possibly-updated angle so sprite is inside bounds + marioX = int(math.Round(a.mario.a*math.Cos(t))) + centerX + marioY = int(math.Round(a.mario.b*math.Sin(t))) + centerY + + // clamp as a safety net + if marioX < minCenterX { + marioX = minCenterX + } + if marioX > maxCenterX { + marioX = maxCenterX + } + if marioY < minCenterY { + marioY = minCenterY + } + if marioY > maxCenterY { + marioY = maxCenterY } a.mario.position.X = marioX a.mario.position.Y = marioY - // Update angle to move along the ellipse (slower by half) - a.mario.angle += 0.05 // Slow down Mario's movement by half + // advance angle for next frame + a.mario.angle += 0.05 if a.mario.angle >= 2*math.Pi { a.mario.angle -= 2 * math.Pi } - // Direction logic for up and down movement + // Direction logic (based on param t used for the current frame) if math.Sin(t) > 0 { - a.mario.dir.X = -1 // Moving left + a.mario.dir.X = -1 // moving left } else { - a.mario.dir.X = 1 // Moving right + a.mario.dir.X = 1 // moving right } if math.Cos(t) > 0 { - a.mario.updown = "marioDown" // Moving downward + a.mario.updown = "marioDown" // moving downward } else { - a.mario.updown = "marioUp" // Moving upward + a.mario.updown = "marioUp" // moving upward } }