Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
// Package main implements Brkout - a classic Breakout game for Dreamcast. // This is a port of the arcade classic using Go and KOS. // Ported from https://3e8.org/hacks/brkout/ package main import "kos" // Screen dimensions const ( ScreenWidth = 640 ScreenHeight = 480 ) // Playing field configuration const ( FieldX = 10 FieldY = 40 FieldWidth = 500 FieldHeight = 420 ) // Brick grid configuration const ( BrickColumns = 20 BrickRows = 15 BrickWidth = 25 BrickHeight = 12 ) // Paddle configuration const ( PaddleWidth = 70 PaddleHeight = 10 PaddleSpeed = 6.0 PaddleY = FieldHeight - 30 ) // Ball configuration const ( BallRadius = 6 BallInitialSpeed = 3.0 BallMaxSpeed = 7.0 BallSpeedStep = 0.5 ) // Input configuration const ( JoystickDeadzone = 15 JoystickSensitivity = 18.0 ) // Game rules const ( InitialLives = 3 PointsPerBrick = 10 ) // Audio settings const ( SoundVolume = 160 SoundCenter = 128 ) // Textures loaded from romdisk var ( titleTexture *kos.PlxTexture fontTexture *kos.PlxTexture fieldTexture *kos.PlxTexture ) // Sound effects var ( clickSound kos.SfxHandle bounceSound kos.SfxHandle hitSound kos.SfxHandle loseSound kos.SfxHandle winSound kos.SfxHandle ) // Controller state var ( currentButtons uint32 previousButtons uint32 joystickX int32 ) // Game state var ( paddleX float32 ballX float32 ballY float32 ballSpeedX float32 ballSpeedY float32 bricks [BrickColumns * BrickRows]int bricksLeft int lives int score int level int ) // Brick color palette var brickColors []uint32 // ---------------------------------------------------------------------------- // Math helpers // ---------------------------------------------------------------------------- func abs(x float32) float32 { if x < 0 { return -x } return x } // ---------------------------------------------------------------------------- // Input handling // ---------------------------------------------------------------------------- func readController() { previousButtons = currentButtons controller := kos.MapleEnumType(0, kos.MAPLE_FUNC_CONTROLLER) if controller == nil { currentButtons, joystickX = 0, 0 return } state := controller.ContState() if state == nil { currentButtons, joystickX = 0, 0 return } currentButtons = state.Buttons joystickX = state.Joyx } func buttonPressed(button uint32) bool { wasUp := (previousButtons & button) == 0 isDown := (currentButtons & button) != 0 return wasUp && isDown } func buttonHeld(button uint32) bool { return (currentButtons & button) != 0 } func getJoystickMovement() float32 { if joystickX > -JoystickDeadzone && joystickX < JoystickDeadzone { return 0 } return float32(joystickX) / JoystickSensitivity } // ---------------------------------------------------------------------------- // Drawing primitives (using Plx library like other examples) // ---------------------------------------------------------------------------- func drawColoredQuad(x, y, width, height, z float32, color uint32) { kos.PlxVertInp(kos.PLX_VERT, x, y+height, z, color) kos.PlxVertInp(kos.PLX_VERT, x, y, z, color) kos.PlxVertInp(kos.PLX_VERT, x+width, y+height, z, color) kos.PlxVertInp(kos.PLX_VERT_EOS, x+width, y, z, color) } func drawTexturedQuad(x, y, width, height, z float32, color uint32, u1, v1, u2, v2 float32) { kos.PlxVertIfp(kos.PLX_VERT, x, y+height, z, color, u1, v2) kos.PlxVertIfp(kos.PLX_VERT, x, y, z, color, u1, v1) kos.PlxVertIfp(kos.PLX_VERT, x+width, y+height, z, color, u2, v2) kos.PlxVertIfp(kos.PLX_VERT_EOS, x+width, y, z, color, u2, v1) } func setupColoredContext(list uint32) { kos.PlxCxtInit() kos.PlxCxtTexture(nil) kos.PlxCxtCulling(kos.PLX_CULL_NONE) kos.PlxCxtSend(int32(list)) } func setupTexturedContext(list uint32, texture *kos.PlxTexture) { kos.PlxCxtInit() kos.PlxCxtTexture(texture.Ptr()) kos.PlxCxtCulling(kos.PLX_CULL_NONE) kos.PlxCxtSend(int32(list)) } // ---------------------------------------------------------------------------- // Text rendering (bitmap font from texture) // ---------------------------------------------------------------------------- const ( CharWidth = 12 CharHeight = 24 FontSize = 256.0 ) func drawString(x, y, z float32, color uint32, text string) { for i, char := range text { if char < 32 { continue } column := int(char) % 16 row := int(char) / 16 u1 := float32(column*CharWidth) / FontSize v1 := float32(row*CharHeight) / FontSize u2 := u1 + CharWidth/FontSize v2 := v1 + CharHeight/FontSize drawTexturedQuad(x+float32(i*CharWidth), y, CharWidth, CharHeight, z, color, u1, v1, u2, v2) } } func drawInteger(x, y, z float32, color uint32, value int) { if value == 0 { drawString(x, y, z, color, "0") return } text := "" for value > 0 { digit := byte(value % 10) text = string('0'+digit) + text value = value / 10 } drawString(x, y, z, color, text) } // ---------------------------------------------------------------------------- // Asset loading // ---------------------------------------------------------------------------- func loadAssets() { kos.PvrInitDefaults() // Load textures from romdisk titleTexture = kos.PlxTxrLoad("/rd/brkout.png", false, 0) fontTexture = kos.PlxTxrLoad("/rd/font.png", true, 0) fieldTexture = kos.PlxTxrLoad("/rd/field.png", false, 0) // Load sound effects kos.SndStreamInit() clickSound = kos.SndSfxLoad("/rd/click.wav") bounceSound = kos.SndSfxLoad("/rd/bounce.wav") hitSound = kos.SndSfxLoad("/rd/toggled.wav") loseSound = kos.SndSfxLoad("/rd/failure.wav") winSound = kos.SndSfxLoad("/rd/success.wav") // Initialize brick color palette (rainbow gradient) brickColors = []uint32{ kos.PlxPackColor(1, 1.0, 0.2, 0.2), // Red kos.PlxPackColor(1, 1.0, 0.6, 0.2), // Orange kos.PlxPackColor(1, 1.0, 1.0, 0.2), // Yellow kos.PlxPackColor(1, 0.2, 1.0, 0.2), // Green kos.PlxPackColor(1, 0.2, 1.0, 1.0), // Cyan kos.PlxPackColor(1, 0.2, 0.2, 1.0), // Blue kos.PlxPackColor(1, 0.8, 0.2, 1.0), // Purple } } // ---------------------------------------------------------------------------- // Colors (pre-computed for performance) // ---------------------------------------------------------------------------- func colorWhite() uint32 { return kos.PlxPackColor(1, 1, 1, 1) } func colorGray() uint32 { return kos.PlxPackColor(1, 0.5, 0.5, 0.5) } func colorBackground() uint32 { return kos.PlxPackColor(1, 0.05, 0.05, 0.1) } func colorWall() uint32 { return kos.PlxPackColor(1, 0.4, 0.4, 0.6) } func colorPaddle() uint32 { return kos.PlxPackColor(1, 0.4, 0.4, 0.7) } func colorShadow() uint32 { return kos.PlxPackColor(0.7, 0, 0, 0) } func colorGameOverBg() uint32 { return kos.PlxPackColor(1, 0.1, 0.1, 0.15) } // ---------------------------------------------------------------------------- // Title Menu // ---------------------------------------------------------------------------- func showTitleMenu() bool { kos.PvrSetBgColor(0, 0, 0) selection := 0 for { readController() // Navigate menu if buttonPressed(kos.CONT_DPAD_UP) || buttonPressed(kos.CONT_DPAD_DOWN) { selection = 1 - selection kos.SndSfxPlay(clickSound, SoundVolume, SoundCenter) } // Confirm selection if buttonPressed(kos.CONT_A) || buttonPressed(kos.CONT_START) { kos.SndSfxPlay(hitSound, SoundVolume, SoundCenter) fadeOutMenu(selection) return selection == 1 // true = quit } renderTitleMenu(selection, 1.0) } } func fadeOutMenu(selection int) { for frame := 30; frame >= 0; frame-- { alpha := float32(frame) / 30.0 renderTitleMenu(selection, alpha) } } func renderTitleMenu(selection int, alpha float32) { kos.PvrWaitReady() kos.PvrSceneBegin() kos.PvrListBegin(kos.PVR_LIST_OP_POLY) // Title screen background if titleTexture != nil { setupTexturedContext(kos.PVR_LIST_OP_POLY, titleTexture) tintColor := kos.PlxPackColor(1, alpha, alpha, alpha) drawTexturedQuad(0, 0, ScreenWidth, ScreenHeight, 100, tintColor, 0, 0, 640.0/1024.0, 480.0/512.0) } kos.PvrListFinish() // Menu cursor kos.PvrListBegin(kos.PVR_LIST_TR_POLY) setupColoredContext(kos.PVR_LIST_TR_POLY) cursorColor := kos.PlxPackColor(alpha, 1, 1, 1) cursorY := float32(314 + selection*50) drawColoredQuad(250, cursorY, 20, 20, 200, cursorColor) kos.PvrListFinish() kos.PvrSceneFinish() } // ---------------------------------------------------------------------------- // Game Logic // ---------------------------------------------------------------------------- func startNewGame() { lives = InitialLives score = 0 level = 1 setupLevel() } func setupLevel() { // Center paddle paddleX = float32(FieldWidth-PaddleWidth) / 2.0 resetBall() // Create brick grid (more rows at higher levels) rows := 3 + level if rows > BrickRows { rows = BrickRows } bricksLeft = 0 colorCount := len(brickColors) for row := 0; row < BrickRows; row++ { for col := 0; col < BrickColumns; col++ { index := row*BrickColumns + col if row < rows { bricks[index] = (row % colorCount) + 1 bricksLeft++ } else { bricks[index] = 0 } } } } func resetBall() { ballX = paddleX + float32(PaddleWidth)/2.0 - float32(BallRadius) ballY = float32(FieldHeight) - 50.0 speed := BallInitialSpeed + float32(level)*BallSpeedStep if speed > BallMaxSpeed { speed = BallMaxSpeed } ballSpeedX = speed * 0.6 ballSpeedY = -speed } func runGameLoop() { kos.PvrSetBgColor(0.05, 0.05, 0.1) startNewGame() for lives > 0 { readController() // Exit to menu on START+B if buttonHeld(kos.CONT_START) && buttonHeld(kos.CONT_B) { return } updatePaddle() updateBall() renderGameFrame() } } func updatePaddle() { // D-pad movement if buttonHeld(kos.CONT_DPAD_LEFT) { paddleX -= PaddleSpeed } if buttonHeld(kos.CONT_DPAD_RIGHT) { paddleX += PaddleSpeed } // Analog stick movement paddleX += getJoystickMovement() // Clamp to field bounds if paddleX < 0 { paddleX = 0 } maxX := float32(FieldWidth - PaddleWidth) if paddleX > maxX { paddleX = maxX } } func updateBall() { ballX += ballSpeedX ballY += ballSpeedY handleWallCollisions() handlePaddleCollision() handleBrickCollisions() handleBallLost() handleLevelComplete() } func handleWallCollisions() { // Left wall if ballX < 0 { ballX = 0 ballSpeedX = -ballSpeedX kos.SndSfxPlay(bounceSound, 140, 64) } // Right wall maxX := float32(FieldWidth - BallRadius*2) if ballX > maxX { ballX = maxX ballSpeedX = -ballSpeedX kos.SndSfxPlay(bounceSound, 140, 192) } // Top wall if ballY < 0 { ballY = 0 ballSpeedY = -ballSpeedY kos.SndSfxPlay(bounceSound, 140, SoundCenter) } } func handlePaddleCollision() { paddleTop := float32(PaddleY) ballBottom := ballY + float32(BallRadius*2) ballCenterX := ballX + float32(BallRadius) // Check if ball is at paddle height if ballBottom < paddleTop || ballY >= paddleTop+float32(PaddleHeight) { return } // Check if ball is horizontally aligned with paddle if ballCenterX < paddleX || ballCenterX >= paddleX+float32(PaddleWidth) { return } // Bounce off paddle ballY = paddleTop - float32(BallRadius*2) ballSpeedY = -abs(ballSpeedY) // Adjust horizontal speed based on hit position hitPosition := (ballCenterX - paddleX) / float32(PaddleWidth) ballSpeedX = (hitPosition - 0.5) * 8.0 kos.SndSfxPlay(bounceSound, SoundVolume, SoundCenter) } func handleBrickCollisions() { ballCenterX := ballX + float32(BallRadius) ballCenterY := ballY + float32(BallRadius) // Convert ball position to brick grid coordinates brickCol := int(ballCenterX) / BrickWidth brickRow := int(ballCenterY) / BrickHeight // Check bounds if brickCol < 0 || brickCol >= BrickColumns || brickRow < 0 || brickRow >= BrickRows { return } index := brickRow*BrickColumns + brickCol if bricks[index] == 0 { return } // Destroy brick bricks[index] = 0 bricksLeft-- score += PointsPerBrick * level // Determine bounce direction brickCenterX := float32(brickCol*BrickWidth) + float32(BrickWidth)/2.0 brickCenterY := float32(brickRow*BrickHeight) + float32(BrickHeight)/2.0 horizontalOverlap := abs(ballCenterX-brickCenterX) / float32(BrickWidth) verticalOverlap := abs(ballCenterY-brickCenterY) / float32(BrickHeight) if horizontalOverlap > verticalOverlap { ballSpeedX = -ballSpeedX } else { ballSpeedY = -ballSpeedY } kos.SndSfxPlay(hitSound, 150, SoundCenter) } func handleBallLost() { if ballY <= float32(FieldHeight) { return } lives-- kos.SndSfxPlay(loseSound, SoundVolume, SoundCenter) if lives <= 0 { showGameOver() return } resetBall() pauseFrames(60) } func handleLevelComplete() { if bricksLeft > 0 { return } kos.SndSfxPlay(winSound, SoundVolume, SoundCenter) level++ setupLevel() pauseFrames(60) } func pauseFrames(count int) { for i := 0; i < count; i++ { renderGameFrame() } } // ---------------------------------------------------------------------------- // Game Over Screen // ---------------------------------------------------------------------------- func showGameOver() { for frame := 0; frame < 180; frame++ { kos.PvrWaitReady() kos.PvrSceneBegin() // Background kos.PvrListBegin(kos.PVR_LIST_OP_POLY) setupColoredContext(kos.PVR_LIST_OP_POLY) drawColoredQuad(0, 0, ScreenWidth, ScreenHeight, 1, colorGameOverBg()) kos.PvrListFinish() // Text kos.PvrListBegin(kos.PVR_LIST_TR_POLY) if fontTexture != nil { setupTexturedContext(kos.PVR_LIST_TR_POLY, fontTexture) drawString(220, 180, 100, colorWhite(), "GAME OVER") drawString(220, 230, 100, colorGray(), "Score:") drawInteger(320, 230, 100, colorWhite(), score) } kos.PvrListFinish() kos.PvrSceneFinish() readController() if buttonPressed(kos.CONT_A) { return } } } // ---------------------------------------------------------------------------- // Game Rendering // ---------------------------------------------------------------------------- func renderGameFrame() { kos.PvrWaitReady() kos.PvrSceneBegin() // Opaque geometry kos.PvrListBegin(kos.PVR_LIST_OP_POLY) renderBackground() renderField() renderWalls() renderBricks() renderPaddle() renderBall() kos.PvrListFinish() // Translucent geometry (HUD text) kos.PvrListBegin(kos.PVR_LIST_TR_POLY) renderHUD() kos.PvrListFinish() kos.PvrSceneFinish() } func renderBackground() { setupColoredContext(kos.PVR_LIST_OP_POLY) drawColoredQuad(0, 0, ScreenWidth, ScreenHeight, 1, colorBackground()) } func renderField() { if fieldTexture == nil { return } setupTexturedContext(kos.PVR_LIST_OP_POLY, fieldTexture) drawTexturedQuad(FieldX, FieldY, FieldWidth, FieldHeight, 50, colorWhite(), 0, 0, float32(FieldWidth)/512.0, float32(FieldHeight)/512.0) } func renderWalls() { setupColoredContext(kos.PVR_LIST_OP_POLY) wall := colorWall() drawColoredQuad(FieldX-5, FieldY, 5, FieldHeight, 100, wall) // Left drawColoredQuad(FieldX+FieldWidth, FieldY, 5, FieldHeight, 100, wall) // Right drawColoredQuad(FieldX-5, FieldY-5, FieldWidth+10, 5, 100, wall) // Top } func renderBricks() { shadow := colorShadow() colorCount := len(brickColors) for row := 0; row < BrickRows; row++ { for col := 0; col < BrickColumns; col++ { colorIndex := bricks[row*BrickColumns+col] if colorIndex == 0 { continue } x := float32(FieldX + col*BrickWidth) y := float32(FieldY + row*BrickHeight) // Shadow drawColoredQuad(x+2, y+2, BrickWidth-2, BrickHeight-2, 99, shadow) // Brick drawColoredQuad(x, y, BrickWidth-2, BrickHeight-2, 100, brickColors[(colorIndex-1)%colorCount]) } } } func renderPaddle() { x := float32(FieldX) + paddleX y := float32(FieldY + PaddleY) drawColoredQuad(x+3, y+3, PaddleWidth, PaddleHeight, 99, colorShadow()) drawColoredQuad(x, y, PaddleWidth, PaddleHeight, 100, colorPaddle()) } func renderBall() { x := float32(FieldX) + ballX y := float32(FieldY) + ballY drawColoredQuad(x, y, BallRadius*2, BallRadius*2, 100, colorWhite()) } func renderHUD() { if fontTexture == nil { return } setupTexturedContext(kos.PVR_LIST_TR_POLY, fontTexture) hudX := float32(FieldX + FieldWidth + 15) white := colorWhite() gray := colorGray() drawString(hudX, 50, 200, white, "BRKOUT") drawString(hudX, 100, 200, gray, "Level") drawInteger(hudX, 125, 200, white, level) drawString(hudX, 170, 200, gray, "Score") drawInteger(hudX, 195, 200, white, score) drawString(hudX, 250, 200, gray, "Lives") drawInteger(hudX, 275, 200, white, lives) } // ---------------------------------------------------------------------------- // Main Entry Point // ---------------------------------------------------------------------------- func main() { loadAssets() for { if showTitleMenu() { return // User selected Quit } runGameLoop() } }
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)