// Clear the canvas and prepare the game stage
clear()
drawStage(ColorMaker.black)
// Retrieve the dimensions of the active canvas
val cb = canvasBounds
// ------------------------------------------------------------
// GAME STATE & VARIABLES
// ------------------------------------------------------------
var score = 0
var gameRunning = true
// FIX: Used Picture.text instead of StPicture
val scoreText = Picture.text(s"Score: $score", 20)
scoreText.setFillColor(white)
scoreText.setPosition(cb.x + 20, cb.y + cb.height - 40)
draw(scoreText)
// FIX: Used Picture.text instead of StPicture
val gameOverText = Picture.text("GAME OVER! Press R to Restart", 24)
gameOverText.setFillColor(red)
gameOverText.setHeading(0)
gameOverText.setPosition(cb.x + (cb.width / 2) - 180, cb.y + (cb.height / 2))
// ------------------------------------------------------------
// GAME OBJECTS (Paddle and Ball)
// ------------------------------------------------------------
// Player Paddle: A green rectangle controlled by arrow keys
val paddle = Picture.rectangle(100, 20)
paddle.setFillColor(green)
paddle.setPosition(cb.x + (cb.width / 2) - 50, cb.y + 40)
draw(paddle)
// Falling Ball: A red circle falling from the top
val ball = Picture.circle(15)
ball.setFillColor(red)
// Function to reset the ball position randomly at the top of the screen
def resetBall(): Unit = {
val randomX = cb.x + randomDouble(15, cb.width - 30)
ball.setPosition(randomX, cb.y + cb.height - 40)
}
resetBall()
draw(ball)
// ------------------------------------------------------------
// KEYBOARD CONTROLS & INTERACTION
// ------------------------------------------------------------
// Listen for player movements and restart command
onKeyPress { k =>
if (gameRunning) {
// FIX: Changed to Kc.VK_LEFT
if (k == Kc.VK_LEFT && paddle.position.x > cb.x) {
paddle.translate(-20, 0)
}
// FIX: Changed to Kc.VK_RIGHT
if (k == Kc.VK_RIGHT && paddle.position.x + 100 < cb.x + cb.width) {
paddle.translate(20, 0)
}
} else {
// FIX: Changed to Kc.VK_R
if (k == Kc.VK_R) {
score = 0
scoreText.update(s"Score: $score")
gameOverText.erase()
resetBall()
gameRunning = true
}
}
}
// ------------------------------------------------------------
// MAIN GAME LOOP (Animation)
// ------------------------------------------------------------
animate {
if (gameRunning) {
// Make the ball fall downwards. Speed increases slightly as score goes up.
val fallSpeed = -4.0 - (score * 0.5)
ball.translate(0, fallSpeed)
// COLLISION CHECK: Did the ball hit the paddle?
if (ball.collidesWith(paddle)) {
score += 1
scoreText.update(s"Score: $score")
resetBall()
}
// LOSE CONDITION: Did the ball fall past the bottom edge?
if (ball.position.y < cb.y) {
gameRunning = false
draw(gameOverText)
}
}
}