2023-11-28 05:52:00 +00:00
|
|
|
package internal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image"
|
|
|
|
"image/color"
|
2023-12-02 07:48:46 +00:00
|
|
|
"math/rand"
|
|
|
|
"time"
|
2023-11-28 05:52:00 +00:00
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ball struct {
|
|
|
|
x float32
|
|
|
|
y float32
|
2023-11-30 06:28:09 +00:00
|
|
|
initX float32
|
|
|
|
initY float32
|
|
|
|
speedX float32
|
2023-11-28 05:52:00 +00:00
|
|
|
speedY float32
|
|
|
|
r float32
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2023-11-29 06:59:38 +00:00
|
|
|
ballBaseRadius = 5
|
2023-12-02 07:48:46 +00:00
|
|
|
ballBaseSpeedX = 1.5
|
|
|
|
ballBaseSpeedY = 1.5
|
2023-11-28 05:52:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func newBall(screenWidth, screenHeight int) *ball {
|
|
|
|
fScreenWidth := float32(screenWidth)
|
|
|
|
fScreenHeight := float32(screenHeight)
|
2023-12-02 07:48:46 +00:00
|
|
|
|
2023-11-29 06:59:38 +00:00
|
|
|
r := ballBaseRadius * fScreenHeight / BaseHeight
|
2023-11-30 06:28:09 +00:00
|
|
|
x := fScreenWidth/2.0 - r
|
|
|
|
y := fScreenHeight/2.0 - r
|
2023-12-02 07:48:46 +00:00
|
|
|
|
2023-11-28 05:52:00 +00:00
|
|
|
return &ball{
|
2023-11-30 06:28:09 +00:00
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
initX: x,
|
|
|
|
initY: y,
|
2023-12-02 07:48:46 +00:00
|
|
|
speedX: randSpeedDirection() * ballBaseSpeedX * fScreenWidth / BaseWidth,
|
|
|
|
speedY: randSpeedDirection() * ballBaseSpeedY * fScreenHeight / BaseHeight,
|
2023-11-28 05:52:00 +00:00
|
|
|
r: r,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var ballColor = color.White
|
|
|
|
|
|
|
|
func (b *ball) draw(img *ebiten.Image) {
|
|
|
|
vector.DrawFilledCircle(img, b.x, b.y, b.r, ballColor, false)
|
|
|
|
}
|
|
|
|
|
2023-12-01 05:42:57 +00:00
|
|
|
func (b *ball) update(bounds image.Rectangle, obstacles ...image.Rectangle) error {
|
|
|
|
b.x, b.y, b.speedX, b.speedY = b.nextPosAndSpeed(bounds, obstacles...)
|
2023-11-29 06:59:38 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:nonamedreturns
|
2023-12-01 05:42:57 +00:00
|
|
|
func (b *ball) nextPos(bounds image.Rectangle, obstacles ...image.Rectangle) (x, y float32) {
|
|
|
|
x, y, _, _ = b.nextPosAndSpeed(bounds, obstacles...)
|
2023-11-29 06:59:38 +00:00
|
|
|
return x, y
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:nonamedreturns
|
2023-12-01 05:42:57 +00:00
|
|
|
func (b *ball) nextPosAndSpeed(bounds image.Rectangle, obstacles ...image.Rectangle) (x, y, speedX, speedY float32) {
|
|
|
|
speedX, speedY = b.nextSpeed(bounds, obstacles...)
|
2023-11-29 06:59:38 +00:00
|
|
|
return b.x + speedX, b.y + speedY, speedX, speedY
|
|
|
|
}
|
|
|
|
|
2023-12-01 05:42:57 +00:00
|
|
|
//nolint:nonamedreturns,gocyclo
|
|
|
|
func (b *ball) nextSpeed(bounds image.Rectangle, obstacles ...image.Rectangle) (speedX, speedY float32) {
|
|
|
|
ballBounds := b.bounds()
|
2023-11-29 06:59:38 +00:00
|
|
|
speedX = b.speedX
|
2023-12-01 05:42:57 +00:00
|
|
|
speedY = b.speedY
|
|
|
|
|
|
|
|
if ballBounds.Max.X >= bounds.Max.X || ballBounds.Min.X <= bounds.Min.X {
|
2023-11-29 06:59:38 +00:00
|
|
|
speedX *= -1
|
2023-11-28 05:52:00 +00:00
|
|
|
}
|
|
|
|
|
2023-12-01 05:42:57 +00:00
|
|
|
if ballBounds.Max.Y >= bounds.Max.Y || ballBounds.Min.Y <= bounds.Min.Y {
|
2023-11-29 06:59:38 +00:00
|
|
|
speedY *= -1
|
2023-11-28 05:52:00 +00:00
|
|
|
}
|
|
|
|
|
2023-12-01 05:42:57 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-29 06:59:38 +00:00
|
|
|
return speedX, speedY
|
2023-11-28 05:52:00 +00:00
|
|
|
}
|
|
|
|
|
2023-11-30 06:28:09 +00:00
|
|
|
func (b *ball) reset() {
|
|
|
|
b.x = b.initX
|
|
|
|
b.y = b.initY
|
2023-12-02 07:48:46 +00:00
|
|
|
b.speedX *= randSpeedDirection()
|
|
|
|
b.speedY *= randSpeedDirection()
|
2023-11-30 06:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-11-28 05:52:00 +00:00
|
|
|
func (b *ball) resize(x, y float32) {
|
|
|
|
b.x *= x
|
|
|
|
b.speedX *= x
|
2023-11-30 06:28:09 +00:00
|
|
|
b.initX *= x
|
2023-11-28 05:52:00 +00:00
|
|
|
b.y *= y
|
|
|
|
b.speedY *= y
|
2023-11-30 06:28:09 +00:00
|
|
|
b.initY *= y
|
2023-11-28 05:52:00 +00:00
|
|
|
b.r *= y
|
|
|
|
}
|
2023-11-30 06:28:09 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2023-12-02 07:48:46 +00:00
|
|
|
|
|
|
|
func randSpeedDirection() float32 {
|
|
|
|
//nolint:gosec
|
|
|
|
r := rand.New(rand.NewSource(time.Now().Unix()))
|
|
|
|
|
|
|
|
if r.Intn(101) >= 50 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1
|
|
|
|
}
|