feat: render paddles #2
|
@ -258,7 +258,7 @@ linters-settings:
|
||||||
- name: function-result-limit
|
- name: function-result-limit
|
||||||
severity: warning
|
severity: warning
|
||||||
disabled: false
|
disabled: false
|
||||||
arguments: [3]
|
arguments: [4]
|
||||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-length
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-length
|
||||||
- name: function-length
|
- name: function-length
|
||||||
severity: warning
|
severity: warning
|
||||||
|
|
|
@ -17,15 +17,15 @@ type ball struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ballBaselRadius = 5
|
ballBaseRadius = 5
|
||||||
ballBaseSpeedX = 1
|
ballBaseSpeedX = 1
|
||||||
ballBaseSpeedY = 1
|
ballBaseSpeedY = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func newBall(screenWidth, screenHeight int) *ball {
|
func newBall(screenWidth, screenHeight int) *ball {
|
||||||
fScreenWidth := float32(screenWidth)
|
fScreenWidth := float32(screenWidth)
|
||||||
fScreenHeight := float32(screenHeight)
|
fScreenHeight := float32(screenHeight)
|
||||||
r := ballBaselRadius * fScreenHeight / BaseHeight
|
r := ballBaseRadius * fScreenHeight / BaseHeight
|
||||||
return &ball{
|
return &ball{
|
||||||
x: fScreenWidth/2.0 - r,
|
x: fScreenWidth/2.0 - r,
|
||||||
speedX: ballBaseSpeedX * fScreenWidth / BaseWidth,
|
speedX: ballBaseSpeedX * fScreenWidth / BaseWidth,
|
||||||
|
@ -42,20 +42,37 @@ func (b *ball) draw(img *ebiten.Image) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ball) update(bounds image.Rectangle) error {
|
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.x, b.y, b.speedX, b.speedY = b.nextPosAndSpeed(bounds)
|
||||||
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
|
|
||||||
|
|
||||||
return nil
|
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) {
|
func (b *ball) resize(x, y float32) {
|
||||||
b.x *= x
|
b.x *= x
|
||||||
b.speedX *= x
|
b.speedX *= x
|
||||||
|
|
|
@ -15,26 +15,44 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
ball *ball
|
ball *ball
|
||||||
debug bool
|
leftPaddle *paddle
|
||||||
img *ebiten.Image
|
rightPaddle *paddle
|
||||||
|
debug bool
|
||||||
|
img *ebiten.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ebiten.Game = (*Game)(nil)
|
var _ ebiten.Game = (*Game)(nil)
|
||||||
|
|
||||||
func NewGame(width, height int, debug bool) *Game {
|
func NewGame(width, height int, debug bool) *Game {
|
||||||
return &Game{
|
return &Game{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
ball: newBall(width, height),
|
ball: newBall(width, height),
|
||||||
debug: debug,
|
leftPaddle: newLeftPaddle(width, height, true),
|
||||||
|
rightPaddle: newRightPaddle(width, height, false),
|
||||||
|
debug: debug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Update() error {
|
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
|
var backgroundColor = color.Black
|
||||||
|
@ -47,6 +65,8 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
g.img.Fill(backgroundColor)
|
g.img.Fill(backgroundColor)
|
||||||
|
|
||||||
g.ball.draw(g.img)
|
g.ball.draw(g.img)
|
||||||
|
g.leftPaddle.draw(g.img)
|
||||||
|
g.rightPaddle.draw(g.img)
|
||||||
|
|
||||||
if g.debug {
|
if g.debug {
|
||||||
g.drawDebug()
|
g.drawDebug()
|
||||||
|
@ -74,7 +94,12 @@ func (g *Game) drawDebug() {
|
||||||
//nolint:nonamedreturns
|
//nolint:nonamedreturns
|
||||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (_screenWidth, _screenHeight int) {
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (_screenWidth, _screenHeight int) {
|
||||||
if g.width != outsideWidth || g.height != outsideHeight {
|
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 {
|
if g.img != nil {
|
||||||
g.img.Dispose()
|
g.img.Dispose()
|
||||||
|
|
125
internal/paddle.go
Normal file
125
internal/paddle.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user