package internal import ( "image" "image/color" "math/rand" "time" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/vector" ) type ball struct { x float32 y float32 initX float32 initY float32 speedX float32 speedY float32 r float32 } const ( ballBaseRadius = 5 ballBaseSpeedX = 1.25 ballBaseSpeedY = 1.25 ) func newBall(screenWidth, screenHeight int) *ball { fScreenWidth := float32(screenWidth) fScreenHeight := float32(screenHeight) r := ballBaseRadius * fScreenHeight / BaseHeight x := fScreenWidth/2.0 - r y := fScreenHeight/2.0 - r return &ball{ x: x, y: y, initX: x, initY: y, speedX: randSpeedDirection() * ballBaseSpeedX * fScreenWidth / BaseWidth, speedY: randSpeedDirection() * ballBaseSpeedY * fScreenHeight / BaseHeight, r: r, } } var ballColor = color.White func (b *ball) draw(img *ebiten.Image) { vector.DrawFilledCircle(img, b.x, b.y, b.r, ballColor, false) } func (b *ball) update(bounds image.Rectangle, obstacles ...image.Rectangle) error { b.x, b.y, b.speedX, b.speedY = b.nextPosAndSpeed(bounds, obstacles...) return nil } //nolint:nonamedreturns func (b *ball) nextPos(bounds image.Rectangle, obstacles ...image.Rectangle) (x, y float32) { x, y, _, _ = b.nextPosAndSpeed(bounds, obstacles...) return x, y } //nolint:nonamedreturns func (b *ball) nextPosAndSpeed(bounds image.Rectangle, obstacles ...image.Rectangle) (x, y, speedX, speedY float32) { speedX, speedY = b.nextSpeed(bounds, obstacles...) return b.x + speedX, b.y + speedY, speedX, speedY } //nolint:nonamedreturns,gocyclo func (b *ball) nextSpeed(bounds image.Rectangle, obstacles ...image.Rectangle) (speedX, speedY float32) { ballBounds := b.bounds() speedX = b.speedX speedY = b.speedY if ballBounds.Max.X >= bounds.Max.X || ballBounds.Min.X <= bounds.Min.X { speedX *= -1 } if ballBounds.Max.Y >= bounds.Max.Y || ballBounds.Min.Y <= bounds.Min.Y { speedY *= -1 } for _, ob := range obstacles { inter := ob.Intersect(ballBounds) if inter == (image.Rectangle{}) { continue } if float32(ob.Min.X) >= b.x || float32(ob.Max.X) <= b.x { speedX *= -1 } if float32(ob.Min.Y) >= b.y || float32(ob.Max.Y) <= b.y { speedY *= -1 } } return speedX, speedY } func (b *ball) reset() { b.x = b.initX b.y = b.initY b.speedX *= randSpeedDirection() b.speedY *= randSpeedDirection() } func (b *ball) resize(x, y float32) { b.x *= x b.speedX *= x b.initX *= x b.y *= y b.speedY *= y b.initY *= y b.r *= y } func (b *ball) bounds() image.Rectangle { return image.Rect(int(b.x-b.r), int(b.y-b.r), int(b.x+b.r), int(b.y+b.r)) } func randSpeedDirection() float32 { //nolint:gosec r := rand.New(rand.NewSource(time.Now().Unix())) if r.Intn(101) >= 50 { return -1 } return 1 }