feat: render paddles

This commit is contained in:
Dawid Wysokiński 2023-11-28 07:43:22 +01:00
parent 4d0d0bb8a6
commit b58f6439cb
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
4 changed files with 194 additions and 27 deletions

View File

@ -258,7 +258,7 @@ linters-settings:
- name: function-result-limit
severity: warning
disabled: false
arguments: [3]
arguments: [4]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-length
- name: function-length
severity: warning

View File

@ -17,15 +17,15 @@ type ball struct {
}
const (
ballBaselRadius = 5
ballBaseSpeedX = 1
ballBaseSpeedY = 1
ballBaseRadius = 5
ballBaseSpeedX = 1
ballBaseSpeedY = 1
)
func newBall(screenWidth, screenHeight int) *ball {
fScreenWidth := float32(screenWidth)
fScreenHeight := float32(screenHeight)
r := ballBaselRadius * fScreenHeight / BaseHeight
r := ballBaseRadius * fScreenHeight / BaseHeight
return &ball{
x: fScreenWidth/2.0 - r,
speedX: ballBaseSpeedX * fScreenWidth / BaseWidth,
@ -42,20 +42,37 @@ func (b *ball) draw(img *ebiten.Image) {
}
func (b *ball) update(bounds image.Rectangle) error {
if b.x+b.r >= float32(bounds.Max.X) || b.x-b.r <= float32(bounds.Min.X) {
b.speedX *= -1
}
if b.y+b.r >= float32(bounds.Max.Y) || b.y-b.r <= float32(bounds.Min.Y) {
b.speedY *= -1
}
b.x += b.speedX
b.y += b.speedY
b.x, b.y, b.speedX, b.speedY = b.nextPosAndSpeed(bounds)
return nil
}
//nolint:nonamedreturns
func (b *ball) nextPos(bounds image.Rectangle) (x, y float32) {
x, y, _, _ = b.nextPosAndSpeed(bounds)
return x, y
}
//nolint:nonamedreturns
func (b *ball) nextPosAndSpeed(bounds image.Rectangle) (x, y, speedX, speedY float32) {
speedX, speedY = b.nextSpeed(bounds)
return b.x + speedX, b.y + speedY, speedX, speedY
}
//nolint:nonamedreturns
func (b *ball) nextSpeed(bounds image.Rectangle) (speedX, speedY float32) {
speedX = b.speedX
if b.x+b.r >= float32(bounds.Max.X) || b.x-b.r <= float32(bounds.Min.X) {
speedX *= -1
}
speedY = b.speedY
if b.y+b.r >= float32(bounds.Max.Y) || b.y-b.r <= float32(bounds.Min.Y) {
speedY *= -1
}
return speedX, speedY
}
func (b *ball) resize(x, y float32) {
b.x *= x
b.speedX *= x

View File

@ -15,26 +15,44 @@ const (
)
type Game struct {
width int
height int
ball *ball
debug bool
img *ebiten.Image
width int
height int
ball *ball
leftPaddle *paddle
rightPaddle *paddle
debug bool
img *ebiten.Image
}
var _ ebiten.Game = (*Game)(nil)
func NewGame(width, height int, debug bool) *Game {
return &Game{
width: width,
height: height,
ball: newBall(width, height),
debug: debug,
width: width,
height: height,
ball: newBall(width, height),
leftPaddle: newLeftPaddle(width, height, true),
rightPaddle: newRightPaddle(width, height, false),
debug: debug,
}
}
func (g *Game) Update() error {
return g.ball.update(image.Rect(0, 0, g.width, g.height))
bounds := image.Rect(0, 0, g.width, g.height)
if err := g.ball.update(bounds); err != nil {
return fmt.Errorf("couldn't update ball: %w", err)
}
if err := g.leftPaddle.update(bounds, g.ball); err != nil {
return fmt.Errorf("couldn't update left paddle: %w", err)
}
if err := g.rightPaddle.update(bounds, g.ball); err != nil {
return fmt.Errorf("couldn't update right paddle: %w", err)
}
return nil
}
var backgroundColor = color.Black
@ -47,6 +65,8 @@ func (g *Game) Draw(screen *ebiten.Image) {
g.img.Fill(backgroundColor)
g.ball.draw(g.img)
g.leftPaddle.draw(g.img)
g.rightPaddle.draw(g.img)
if g.debug {
g.drawDebug()
@ -74,7 +94,12 @@ func (g *Game) drawDebug() {
//nolint:nonamedreturns
func (g *Game) Layout(outsideWidth, outsideHeight int) (_screenWidth, _screenHeight int) {
if g.width != outsideWidth || g.height != outsideHeight {
g.ball.resize(float32(outsideWidth)/float32(g.width), float32(outsideHeight)/float32(g.height))
ratioWidth := float32(outsideWidth) / float32(g.width)
ratioHeight := float32(outsideHeight) / float32(g.height)
g.ball.resize(ratioWidth, ratioHeight)
g.leftPaddle.resize(ratioWidth, ratioHeight)
g.rightPaddle.resize(ratioWidth, ratioHeight)
if g.img != nil {
g.img.Dispose()

125
internal/paddle.go Normal file
View File

@ -0,0 +1,125 @@
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 = 10
paddleBaseHeight = 100
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:Revive,cyclomatic
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) 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
}