PowerShell eignet sich nur für Batch- und und Automatisierungsscripts, sondern auch zur Retro-Spieleentwicklung. In diesem Artikel zeige ich, wie ich einen “vollständigen” Breakout-Klon erstellt habe, der direkt in der PowerShell-Konsole läuft.
Die Herausforderung
Das Ziel war es, das klassische Arcade-Spiel Breakout mit nichts anderem als PowerShell-Skripten nachzubauen. Breakout, ursprünglich 1976 von Atari veröffentlicht, ist ein zeitloser Klassiker: Ein Ball prallt zwischen Wänden hin und her, der Spieler steuert ein Paddle und muss damit Blöcke zerstören.
Technische Umsetzung
ASCII-Grafik im Terminal
Da PowerShell keine nativen Grafikfunktionen besitzt, musste ich kreativ werden. Die Lösung: ASCII-Zeichen als Grafik-Engine verwenden. Der Ball wird als O
dargestellt, das Paddle als ===
und die zerstörbaren Blöcke als ###
.
Gameplay-Features
Das fertige Spiel bietet:
- ✅ “Physik” mit Wandkollisionen
- ✅ Steuerbare Paddle-Bewegung (A/D-Tasten)
- ✅ 32 zerstörbare Blöcke in 4 Reihen
- ✅ Punktesystem und Leben-System
- ✅ Sieg- und Verlust-Bedingungen
Code-Highlights
Das Herzstück ist die Game-Loop, die in einem while
-Loop läuft:
while ($gameRunning) {
Draw-Game # Bildschirm zeichnen
Get-Input # Tastatureingaben verarbeiten
Update-Ball # Ball-Position und Kollisionen aktualisieren
Start-Sleep -Milliseconds 100 # 10 FPS
}
Die Bildschirm-Ausgabe erfolgt über ASCII-Rahmen mit Unicode-Zeichen:
╔════════════════════════════════════════╗
║ ║
║ ### ### ### ### ### ### ### ### ║
║ ### ### ### ### ### ### ### ### ║
║ ║
║ O ║
║ ║
║ ====== ║
╚════════════════════════════════════════╝
Fazit
Dieses Projekt zeigt eindrucksvoll, dass PowerShell weit mehr kann als nur Scripts für IT-Administration. Mit etwas Kreativität lassen sich sogar vollwertige Spiele entwickeln!
Der vollständige Quellcode umfasst knapp 200 Zeilen und demonstriert verschiedene PowerShell-Konzepte: Arrays, Funktionen, Schleifen, Benutzereingaben und String-Manipulation.
Wer Lust bekommen hat, kann das Spiel direkt ausprobieren - einfach den Code in eine .ps1
-Datei speichern und in PowerShell ausführen. Viel Spaß beim Zerstören der Blöcke!
Code
# PowerShell Breakout Clone
# Run this script in PowerShell to play!
# Game configuration
$gameWidth = 40
$gameHeight = 20
$paddleWidth = 6
$brickRows = 4
$bricksPerRow = 8
# Game state
$score = 0
$lives = 3
$gameRunning = $true
# Ball properties
$ballX = [math]::Floor($gameWidth / 2)
$ballY = $gameHeight - 3
$ballDX = 1
$ballDY = -1
# Paddle properties
$paddleX = [math]::Floor(($gameWidth - $paddleWidth) / 2)
$paddleY = $gameHeight - 1
# Initialize bricks (1 = brick exists, 0 = destroyed)
$bricks = @()
for ($row = 0; $row -lt $brickRows; $row++) {
$brickRow = @()
for ($col = 0; $col -lt $bricksPerRow; $col++) {
$brickRow += 1
}
$bricks += ,$brickRow
}
function Clear-GameScreen {
Clear-Host
[Console]::CursorVisible = $false
}
function Draw-Game {
# Initialize screen as array of strings
$screen = @()
for ($y = 0; $y -lt $gameHeight; $y++) {
$screen += " " * $gameWidth
}
# Draw bricks
for ($row = 0; $row -lt $brickRows; $row++) {
for ($col = 0; $col -lt $bricksPerRow; $col++) {
if ($bricks[$row][$col] -eq 1) {
$brickX = $col * 5 + 1
$brickY = $row + 2
if ($brickX -lt $gameWidth -and $brickY -lt $gameHeight -and $brickX -ge 0) {
$chars = $screen[$brickY].ToCharArray()
$chars[$brickX] = '#'
if ($brickX + 1 -lt $gameWidth) { $chars[$brickX + 1] = '#' }
if ($brickX + 2 -lt $gameWidth) { $chars[$brickX + 2] = '#' }
$screen[$brickY] = -join $chars
}
}
}
}
# Draw paddle
if ($paddleY -ge 0 -and $paddleY -lt $gameHeight) {
$chars = $screen[$paddleY].ToCharArray()
for ($i = 0; $i -lt $paddleWidth; $i++) {
if ($paddleX + $i -ge 0 -and $paddleX + $i -lt $gameWidth) {
$chars[$paddleX + $i] = '='
}
}
$screen[$paddleY] = -join $chars
}
# Draw ball
if ($ballX -ge 0 -and $ballX -lt $gameWidth -and $ballY -ge 0 -and $ballY -lt $gameHeight) {
$chars = $screen[$ballY].ToCharArray()
$chars[$ballX] = 'O'
$screen[$ballY] = -join $chars
}
# Output screen
Clear-GameScreen
Write-Host "╔$('═' * $gameWidth)╗"
for ($y = 0; $y -lt $gameHeight; $y++) {
Write-Host "║$($screen[$y])║"
}
Write-Host "╚$('═' * $gameWidth)╝"
Write-Host "Score: $score | Lives: $lives | Controls: A/D to move, Q to quit"
}
function Update-Ball {
# Move ball
$script:ballX += $script:ballDX
$script:ballY += $script:ballDY
# Wall collisions
if ($script:ballX -le 0 -or $script:ballX -ge $gameWidth - 1) {
$script:ballDX = -$script:ballDX
}
if ($script:ballY -le 0) {
$script:ballDY = -$script:ballDY
}
# Ball goes below paddle (lose life)
if ($script:ballY -ge $gameHeight) {
$script:lives--
if ($script:lives -le 0) {
$script:gameRunning = $false
return
}
# Reset ball position
$script:ballX = [math]::Floor($gameWidth / 2)
$script:ballY = $gameHeight - 3
$script:ballDX = 1
$script:ballDY = -1
return
}
# Paddle collision - check if ball is hitting paddle from above
if ($script:ballY -eq $paddleY -and $script:ballX -ge $paddleX -and $script:ballX -lt $paddleX + $paddleWidth -and $script:ballDY -gt 0) {
$script:ballDY = -$script:ballDY
# Add some angle based on where ball hits paddle
$hitPos = $script:ballX - $paddleX
if ($hitPos -lt $paddleWidth / 3) {
$script:ballDX = -1
} elseif ($hitPos -gt ($paddleWidth * 2 / 3)) {
$script:ballDX = 1
}
}
# Brick collision
$brickRow = $script:ballY - 2
$brickCol = [math]::Floor(($script:ballX - 1) / 5)
if ($brickRow -ge 0 -and $brickRow -lt $brickRows -and $brickCol -ge 0 -and $brickCol -lt $bricksPerRow) {
if ($bricks[$brickRow][$brickCol] -eq 1) {
$bricks[$brickRow][$brickCol] = 0
$script:ballDY = -$script:ballDY
$script:score += 10
# Check if all bricks destroyed
$bricksLeft = 0
for ($row = 0; $row -lt $brickRows; $row++) {
for ($col = 0; $col -lt $bricksPerRow; $col++) {
$bricksLeft += $bricks[$row][$col]
}
}
if ($bricksLeft -eq 0) {
$script:gameRunning = $false
Write-Host "`nCongratulations! You won! Final Score: $score"
return
}
}
}
}
function Get-Input {
if ([Console]::KeyAvailable) {
$key = [Console]::ReadKey($true)
switch ($key.Key) {
'A' {
$script:paddleX = [math]::Max(0, $paddleX - 2)
}
'D' {
$script:paddleX = [math]::Min($gameWidth - $paddleWidth, $paddleX + 2)
}
'Q' {
$script:gameRunning = $false
}
}
}
}
# Game loop
Write-Host "PowerShell Breakout!"
Write-Host "Use A and D keys to move the paddle"
Write-Host "Press any key to start..."
[Console]::ReadKey() | Out-Null
while ($gameRunning) {
Draw-Game
Get-Input
Update-Ball
Start-Sleep -Milliseconds 100
}
# Game over
Clear-GameScreen
if ($lives -le 0) {
Write-Host "Game Over! Final Score: $score"
} else {
Write-Host "Thanks for playing!"
}
Write-Host "Press any key to exit..."
[Console]::ReadKey() | Out-Null