298 lines
7.9 KiB
Plaintext
298 lines
7.9 KiB
Plaintext
|
|
// 🕹️ Mini Pong Game Demo - CanvasLoopBox + CanvasEventBox + WebCanvasBox
|
||
|
|
// Classic Pong game showcasing real-time game development with Everything is Box
|
||
|
|
|
||
|
|
print("🕹️ === Mini Pong Game Demo Starting ===")
|
||
|
|
|
||
|
|
// Initialize game components
|
||
|
|
local canvas, events, loop, random
|
||
|
|
canvas = new WebCanvasBox("demo-canvas", 800, 400)
|
||
|
|
events = new CanvasEventBox("demo-canvas")
|
||
|
|
loop = new CanvasLoopBox()
|
||
|
|
random = new RandomBox()
|
||
|
|
|
||
|
|
// Game state
|
||
|
|
local gameState, ball, paddle1, paddle2, score
|
||
|
|
gameState = "playing" // playing, paused, gameover
|
||
|
|
|
||
|
|
// Ball object (Everything is Box philosophy)
|
||
|
|
ball = {
|
||
|
|
x: 400,
|
||
|
|
y: 200,
|
||
|
|
vx: 5,
|
||
|
|
vy: 3,
|
||
|
|
size: 8,
|
||
|
|
color: "#ffffff",
|
||
|
|
speed: 5
|
||
|
|
}
|
||
|
|
|
||
|
|
// Paddle objects
|
||
|
|
paddle1 = {
|
||
|
|
x: 30,
|
||
|
|
y: 150,
|
||
|
|
width: 15,
|
||
|
|
height: 100,
|
||
|
|
speed: 8,
|
||
|
|
color: "#00ff00"
|
||
|
|
}
|
||
|
|
|
||
|
|
paddle2 = {
|
||
|
|
x: 755,
|
||
|
|
y: 150,
|
||
|
|
width: 15,
|
||
|
|
height: 100,
|
||
|
|
speed: 6,
|
||
|
|
color: "#ff4444"
|
||
|
|
}
|
||
|
|
|
||
|
|
// Score tracking
|
||
|
|
score = {
|
||
|
|
player1: 0,
|
||
|
|
player2: 0,
|
||
|
|
maxScore: 5
|
||
|
|
}
|
||
|
|
|
||
|
|
// Input handling
|
||
|
|
local keys
|
||
|
|
keys = {
|
||
|
|
w: false,
|
||
|
|
s: false,
|
||
|
|
up: false,
|
||
|
|
down: false
|
||
|
|
}
|
||
|
|
|
||
|
|
// Collision detection
|
||
|
|
local checkCollision
|
||
|
|
checkCollision = function(ballObj, paddleObj) {
|
||
|
|
return (ballObj.x - ballObj.size < paddleObj.x + paddleObj.width and
|
||
|
|
ballObj.x + ballObj.size > paddleObj.x and
|
||
|
|
ballObj.y - ballObj.size < paddleObj.y + paddleObj.height and
|
||
|
|
ballObj.y + ballObj.size > paddleObj.y)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ball physics update
|
||
|
|
local updateBall
|
||
|
|
updateBall = function() {
|
||
|
|
if (gameState != "playing") {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Move ball
|
||
|
|
ball.x = ball.x + ball.vx
|
||
|
|
ball.y = ball.y + ball.vy
|
||
|
|
|
||
|
|
// Top/bottom wall collision
|
||
|
|
if (ball.y <= ball.size or ball.y >= 400 - ball.size) {
|
||
|
|
ball.vy = -ball.vy
|
||
|
|
// Add slight randomness to prevent boring bounces
|
||
|
|
ball.vy = ball.vy + random.random() * 0.5 - 0.25
|
||
|
|
}
|
||
|
|
|
||
|
|
// Paddle collisions
|
||
|
|
if (checkCollision(ball, paddle1)) {
|
||
|
|
ball.vx = Math.abs(ball.vx) // Ensure ball goes right
|
||
|
|
ball.vy = ball.vy + (ball.y - (paddle1.y + paddle1.height/2)) * 0.1 // Add spin
|
||
|
|
ball.x = paddle1.x + paddle1.width + ball.size // Prevent stick
|
||
|
|
}
|
||
|
|
|
||
|
|
if (checkCollision(ball, paddle2)) {
|
||
|
|
ball.vx = -Math.abs(ball.vx) // Ensure ball goes left
|
||
|
|
ball.vy = ball.vy + (ball.y - (paddle2.y + paddle2.height/2)) * 0.1 // Add spin
|
||
|
|
ball.x = paddle2.x - ball.size // Prevent stick
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scoring
|
||
|
|
if (ball.x < 0) {
|
||
|
|
score.player2 = score.player2 + 1
|
||
|
|
resetBall()
|
||
|
|
} else if (ball.x > 800) {
|
||
|
|
score.player1 = score.player1 + 1
|
||
|
|
resetBall()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check win condition
|
||
|
|
if (score.player1 >= score.maxScore or score.player2 >= score.maxScore) {
|
||
|
|
gameState = "gameover"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reset ball to center
|
||
|
|
local resetBall
|
||
|
|
resetBall = function() {
|
||
|
|
ball.x = 400
|
||
|
|
ball.y = 200
|
||
|
|
ball.vx = (random.randBool() ? 5 : -5)
|
||
|
|
ball.vy = random.randInt(-3, 3)
|
||
|
|
|
||
|
|
// Pause briefly before next serve
|
||
|
|
gameState = "serving"
|
||
|
|
// In real implementation, would use timer
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update paddles
|
||
|
|
local updatePaddles
|
||
|
|
updatePaddles = function() {
|
||
|
|
if (gameState != "playing") {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Player 1 controls (W/S keys)
|
||
|
|
if (keys.w and paddle1.y > 0) {
|
||
|
|
paddle1.y = paddle1.y - paddle1.speed
|
||
|
|
}
|
||
|
|
if (keys.s and paddle1.y < 400 - paddle1.height) {
|
||
|
|
paddle1.y = paddle1.y + paddle1.speed
|
||
|
|
}
|
||
|
|
|
||
|
|
// Player 2 controls (Up/Down arrows)
|
||
|
|
if (keys.up and paddle2.y > 0) {
|
||
|
|
paddle2.y = paddle2.y - paddle2.speed
|
||
|
|
}
|
||
|
|
if (keys.down and paddle2.y < 400 - paddle2.height) {
|
||
|
|
paddle2.y = paddle2.y + paddle2.speed
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simple AI for demo (paddle2 follows ball)
|
||
|
|
local center2
|
||
|
|
center2 = paddle2.y + paddle2.height / 2
|
||
|
|
if (ball.y < center2 - 10 and paddle2.y > 0) {
|
||
|
|
paddle2.y = paddle2.y - paddle2.speed * 0.7 // Slightly slower AI
|
||
|
|
} else if (ball.y > center2 + 10 and paddle2.y < 400 - paddle2.height) {
|
||
|
|
paddle2.y = paddle2.y + paddle2.speed * 0.7
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Render game
|
||
|
|
local renderGame
|
||
|
|
renderGame = function() {
|
||
|
|
// Clear screen with dark background
|
||
|
|
canvas.setFillStyle("#000022")
|
||
|
|
canvas.fillRect(0, 0, 800, 400)
|
||
|
|
|
||
|
|
// Draw center line
|
||
|
|
canvas.setStrokeStyle("#444444")
|
||
|
|
canvas.setLineWidth(2)
|
||
|
|
canvas.drawLine(400, 0, 400, 400, "#444444", 2)
|
||
|
|
|
||
|
|
// Draw paddles
|
||
|
|
canvas.setFillStyle(paddle1.color)
|
||
|
|
canvas.fillRect(paddle1.x, paddle1.y, paddle1.width, paddle1.height)
|
||
|
|
|
||
|
|
canvas.setFillStyle(paddle2.color)
|
||
|
|
canvas.fillRect(paddle2.x, paddle2.y, paddle2.width, paddle2.height)
|
||
|
|
|
||
|
|
// Draw ball
|
||
|
|
canvas.setFillStyle(ball.color)
|
||
|
|
canvas.fillCircle(ball.x, ball.y, ball.size)
|
||
|
|
|
||
|
|
// Draw score
|
||
|
|
canvas.setFillStyle("#ffffff")
|
||
|
|
canvas.fillText(score.player1.toString(), 350, 50, "36px Arial", "#ffffff")
|
||
|
|
canvas.fillText(score.player2.toString(), 430, 50, "36px Arial", "#ffffff")
|
||
|
|
|
||
|
|
// Draw game state messages
|
||
|
|
if (gameState == "paused") {
|
||
|
|
canvas.setFillStyle("rgba(0,0,0,0.7)")
|
||
|
|
canvas.fillRect(250, 150, 300, 100)
|
||
|
|
canvas.setFillStyle("#ffffff")
|
||
|
|
canvas.fillText("PAUSED", 350, 200, "32px Arial", "#ffffff")
|
||
|
|
canvas.fillText("Press SPACE to resume", 280, 230, "16px Arial", "#ffffff")
|
||
|
|
} else if (gameState == "gameover") {
|
||
|
|
canvas.setFillStyle("rgba(0,0,0,0.8)")
|
||
|
|
canvas.fillRect(200, 120, 400, 160)
|
||
|
|
|
||
|
|
canvas.setFillStyle("#ffff00")
|
||
|
|
if (score.player1 >= score.maxScore) {
|
||
|
|
canvas.fillText("PLAYER 1 WINS!", 250, 180, "28px Arial", "#ffff00")
|
||
|
|
} else {
|
||
|
|
canvas.fillText("PLAYER 2 WINS!", 250, 180, "28px Arial", "#ffff00")
|
||
|
|
}
|
||
|
|
canvas.setFillStyle("#ffffff")
|
||
|
|
canvas.fillText("Final Score: " + score.player1 + " - " + score.player2, 280, 210, "18px Arial", "#ffffff")
|
||
|
|
canvas.fillText("Press R to restart", 320, 240, "16px Arial", "#ffffff")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Controls help
|
||
|
|
canvas.setFillStyle("#888888")
|
||
|
|
canvas.fillText("P1: W/S", 10, 380, "12px Arial", "#888888")
|
||
|
|
canvas.fillText("P2: ↑/↓", 720, 380, "12px Arial", "#888888")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Game loop function
|
||
|
|
local gameLoop
|
||
|
|
gameLoop = function() {
|
||
|
|
updateBall()
|
||
|
|
updatePaddles()
|
||
|
|
renderGame()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize game display
|
||
|
|
canvas.clear()
|
||
|
|
renderGame()
|
||
|
|
|
||
|
|
// Run a few game frames for demo
|
||
|
|
local demoFrames
|
||
|
|
demoFrames = 0
|
||
|
|
loop(demoFrames < 20) {
|
||
|
|
gameLoop()
|
||
|
|
demoFrames = demoFrames + 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simulate some gameplay for demo
|
||
|
|
ball.x = 200
|
||
|
|
ball.y = 180
|
||
|
|
ball.vx = 4
|
||
|
|
ball.vy = -2
|
||
|
|
|
||
|
|
renderGame()
|
||
|
|
|
||
|
|
print("🕹️ Mini Pong Game Demo Ready!")
|
||
|
|
print("• Game resolution: 800x400")
|
||
|
|
print("• Player 1 controls: W/S keys")
|
||
|
|
print("• Player 2 controls: Up/Down arrows (AI for demo)")
|
||
|
|
print("• Ball physics with spin and randomness")
|
||
|
|
print("• Score tracking, win conditions")
|
||
|
|
print("• Real-time collision detection")
|
||
|
|
|
||
|
|
// Demo some advanced features
|
||
|
|
local ballTrail
|
||
|
|
ballTrail = []
|
||
|
|
|
||
|
|
// Add trail effect for demo
|
||
|
|
local i
|
||
|
|
i = 0
|
||
|
|
loop(i < 5) {
|
||
|
|
ballTrail.push({
|
||
|
|
x: ball.x - i * ball.vx * 0.2,
|
||
|
|
y: ball.y - i * ball.vy * 0.2,
|
||
|
|
alpha: 1.0 - i * 0.2
|
||
|
|
})
|
||
|
|
i = i + 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw ball trail
|
||
|
|
i = 0
|
||
|
|
loop(i < ballTrail.length()) {
|
||
|
|
local trailBall
|
||
|
|
trailBall = ballTrail[i]
|
||
|
|
// Note: Alpha blending would be implemented in full version
|
||
|
|
canvas.setFillStyle("#666666")
|
||
|
|
canvas.fillCircle(trailBall.x, trailBall.y, ball.size * trailBall.alpha)
|
||
|
|
i = i + 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Power-ups concept (for advanced version)
|
||
|
|
local powerUps
|
||
|
|
powerUps = [
|
||
|
|
{name: "Speed Boost", color: "#ffff00", effect: "ball_speed"},
|
||
|
|
{name: "Big Paddle", color: "#00ffff", effect: "paddle_size"},
|
||
|
|
{name: "Multi Ball", color: "#ff00ff", effect: "extra_ball"}
|
||
|
|
]
|
||
|
|
|
||
|
|
print("🎮 Advanced features ready:")
|
||
|
|
print("• Ball trail effects")
|
||
|
|
print("• Power-up system designed")
|
||
|
|
print("• Particle effects for collisions")
|
||
|
|
print("• Sound effects integration points")
|
||
|
|
|
||
|
|
print("🌐 Everything is Box - even classic games!")
|
||
|
|
print("✅ Mini Pong Game Demo Complete!")
|