tennis-game/internal/paddle.go

135 lines
3.1 KiB
Go

package internal
import (
"image"
"image/color"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/vector"
)
type paddle struct {
x float32 // top left corner - X
y float32 // top left corner - Y
initX float32
initY float32
width float32
height float32
speed float32
isControlledByPlayer bool
}
func newRightPaddle(screenWidth, screenHeight int, isControlledByPlayer bool) *paddle {
return newPaddle(
screenWidth,
screenHeight,
isControlledByPlayer,
func(paddleWidth, paddleHeight, screenWidth, screenHeight float32) (float32, float32) {
return screenWidth - paddleWidth, screenHeight/2 - paddleHeight/2
},
)
}
func newLeftPaddle(screenWidth, screenHeight int, isControlledByPlayer bool) *paddle {
return newPaddle(
screenWidth,
screenHeight,
isControlledByPlayer,
func(paddleWidth, paddleHeight, screenWidth, screenHeight float32) (float32, float32) {
return 0, screenHeight/2 - paddleHeight/2
},
)
}
const (
paddleBaseWidth = 7
paddleBaseHeight = 75
paddleBaseSpeed = 1
)
func newPaddle(
screenWidth, screenHeight int,
isControlledByPlayer bool,
getPos func(paddleWidth, paddleHeight, screenWidth, screenHeight float32) (x, y float32),
) *paddle {
fScreenWidth := float32(screenWidth)
fScreenHeight := float32(screenHeight)
p := &paddle{
width: paddleBaseWidth * fScreenWidth / BaseWidth,
height: paddleBaseHeight * fScreenHeight / BaseHeight,
isControlledByPlayer: isControlledByPlayer,
speed: paddleBaseSpeed * fScreenHeight / BaseHeight,
}
p.x, p.y = getPos(p.width, p.height, fScreenWidth, fScreenHeight)
p.initX, p.initY = p.x, p.y
return p
}
var paddleColor = color.White
func (p *paddle) draw(img *ebiten.Image) {
vector.DrawFilledRect(img, p.x, p.y, p.width, p.height, paddleColor, false)
}
//nolint:gocyclo
func (p *paddle) update(bounds image.Rectangle, b *ball) error {
newY := p.y
if p.isControlledByPlayer && ebiten.IsFocused() {
_, cursorY := ebiten.CursorPosition()
newY = float32(cursorY) - p.height/2
}
ballNextX, _ := b.nextPos(bounds)
isBallGettingCloser := p.x-ballNextX < p.x-b.x
// move the paddle according to the position of the ball
if !p.isControlledByPlayer && isBallGettingCloser {
if newY+p.height/2 > b.y {
newY -= p.speed
} else if newY-p.height/2 < b.y {
newY += p.speed
}
}
// move the paddle to the init position
if !p.isControlledByPlayer && !isBallGettingCloser {
if newY > p.initY {
newY -= min(newY-p.initY, p.speed)
} else if newY < p.initY {
newY += min(p.initY-newY, p.speed)
}
}
if minY := float32(bounds.Min.Y); newY <= minY {
newY = minY
}
if maxY := float32(bounds.Max.Y); newY+p.height >= maxY {
newY = maxY - p.height
}
p.y = newY
return nil
}
func (p *paddle) reset() {
p.x = p.initX
p.y = p.initY
}
func (p *paddle) resize(x, y float32) {
p.x *= x
p.width *= x
p.initX *= x
p.y *= y
p.height *= y
p.speed *= y
p.initY *= y
}
func (p *paddle) bounds() image.Rectangle {
return image.Rect(int(p.x), int(p.y), int(p.x+p.width), int(p.y+p.height))
}