-
Notifications
You must be signed in to change notification settings - Fork 0
Tutorial
This comprehensive guide walks you through building a complete, high-performance space shooter game from scratch. It utilizes every core module inside pyforge-engine: window initialization, cross-platform font rendering, custom sprites, multi-track audio playback, vector particle physics, and live hardware profiling.
Our space shooter game handles a full arcade loop completely fileless:
- Interactive Main Menu: Tracks mouse positions and highlights your "START" button using perimeter collision math.
-
Dynamic Physics Player Ship: Steers via
WASDor Arrow keys, locked cleanly within screen boundary lines. - Weapon Laser Systems: Spawns high-velocity laser quads that travel upward, playing custom sound effects on deployment.
- Meteor Hazard Spawners: Continuously drops randomized meteor targets from the upper screen boundary lines down into the combat grid.
- GPU Particle Explosions: Blasts shimmering color fragments in all directions when a laser intersects a meteor hazard perimeter box.
Create a clean file named exactly space_shooter.py inside your initialized engine sandbox environment directory, and paste down this first half of the script layout:
import pyforge
import random
import os
from PIL import Image, ImageDraw
# =====================================================================
# 🚀 PYFORGE ENGINE CORE INITIALIZATION
# =====================================================================
SCREEN_WIDTH = 1024
SCREEN_HEIGHT = 768
pyforge.init(SCREEN_WIDTH, SCREEN_HEIGHT, "Pyforge Framework Showcase - Astro Dodger")
# Load your cross-platform system font dynamically in RAM cache
pyforge.load_system_font("sans-serif", font_size=26)
# =====================================================================
# 🛠️ FILELESS ASSET PRE-BAKING (NO LOCAL FILES REQUIRED!)
# =====================================================================
# If hero.png isn't found locally, bake a professional pixel-art ship in RAM!
if not os.path.exists("hero.png"):
print("📦 Pre-baking fileless pixel-art player ship directly into RAM...")
fallback_img = Image.new('RGBA', (64, 64), (0, 0, 0, 0))
fb_draw = ImageDraw.Draw(fallback_img)
# Draw a clean cyber delta-wing interceptor ship profile
fb_draw.polygon([(32, 4), (8, 56), (32, 44), (56, 56)], fill=(0, 180, 255, 255))
fb_draw.polygon([(24, 20), (32, 4), (40, 20)], fill=(255, 255, 255, 255))
fb_draw.rectangle((28, 44, 36, 52), fill=(255, 100, 10, 255)) # Thruster flame
raw_bytes = fallback_img.tobytes("raw", "RGBA")
tex_id = pyforge.pyforge_core.load_texture(raw_bytes, 64, 64)
player_texture = pyforge.engine.Texture(tex_id, 64, 64)
else:
player_texture = pyforge.load_image("hero.png")
# Audio file mapping checks (Developers can place custom WAV/MP3 files into this folder)
if os.path.exists("game_theme.mp3"): pyforge.play_music("game_theme.mp3", loop=True)
elif os.path.exists("my_music.wav"): pyforge.play_music("my_music.wav", loop=True)
SFX_LASER = "laser.wav" if os.path.exists("laser.wav") else None
SFX_EXPLOSION = "explosion.wav" if os.path.exists("explosion.wav") else None
# Pre-calculate primitive geometry meshes on the graphics hardware
quad_mesh = pyforge.shape(4) # 4-sided square for laser lines and meteors
meteor_mesh = pyforge.shape(8) # 8-sided regular octagon for jagged hazards
# =====================================================================
# ⚙️ ARCADE GAME STATE ENTITY MATRICES
# =====================================================================
game_state = "MAIN_MENU" # Configurations: "MAIN_MENU", "GAMEPLAY"
# Player Ship States
player_x = 512.0
player_y = 650.0
player_speed = 6.5
player_size = 32.0
# Lasers and Hazards Array Lists
lasers_pool = [] # Holds coordinate tuples: [x, y]
meteors_pool = [] # Holds coordinate dictionaries: [{"x": x, "y": y, "speed": s}]
laser_cooldown = 0
running_score = 0
high_score = 0
start_btn_color = (0.2, 0.25, 0.3)# =====================================================================
# 🔄 MASTER HARDWARE ENGINE REAL-TIME RENDERING LOOP
# =====================================================================
while pyforge.is_open():
# Paint smooth space linear vertical background gradient panel
pyforge.clear_gradient(top_color=(0.02, 0.03, 0.08), bottom_color=(0.08, 0.05, 0.18))
mx, my = pyforge.get_mouse_pos()
# -----------------------------------------------------------------
# STATE 1: THE INTERACTIVE MAIN MENU SCREEN
# -----------------------------------------------------------------
if game_state == "MAIN_MENU":
# Render a dark center title card banner
pyforge.draw_button(212, 180, 600, 100, bg_color=(0.01, 0.01, 0.03))
pyforge.draw_text("ASTRO DODGER: PYFORGE ENGINE", x=245, y=212, scale=0.6, color=(0.4, 0.7, 1.0))
# Start button perimeter limits tracking parameters
btn_x, btn_y, btn_w, btn_h = 412, 450, 200, 60
if btn_x <= mx <= (btn_x + btn_w) and btn_y <= my <= (btn_y + btn_h):
start_btn_color = (0.1, 0.6, 0.3) # Highlight green on hover
if pyforge.is_button_clicked(btn_x, btn_y, btn_w, btn_h):
# PARTICLES: Blast beautiful cyan flares straight right under your mouse pointer!
pyforge.spawn_particles(mx, my, color=(0.3, 0.8, 1.0))
game_state = "GAMEPLAY"
else:
start_btn_color = (0.2, 0.25, 0.3)
pyforge.draw_button(btn_x, btn_y, btn_w, btn_h, bg_color=start_btn_color)
pyforge.draw_text("START", x=472, y=464, scale=0.6, color=(1.0, 1.0, 1.0))
# -----------------------------------------------------------------
# STATE 2: ACTIVE SHOOTER GAMEPLAY RUNTIME PIPELINE
# -----------------------------------------------------------------
elif game_state == "GAMEPLAY":
# --- 1. PLAYER SHIP MOVEMENT CONTROLS ---
if pyforge.is_key_pressed(pyforge.KEY_A) or pyforge.is_key_pressed(pyforge.KEY_LEFT):
player_x -= player_speed
if pyforge.is_key_pressed(pyforge.KEY_D) or pyforge.is_key_pressed(pyforge.KEY_RIGHT):
player_x += player_speed
if pyforge.is_key_pressed(pyforge.KEY_W) or pyforge.is_key_pressed(pyforge.KEY_UP):
player_y -= player_speed
if pyforge.is_key_pressed(pyforge.KEY_S) or pyforge.is_key_pressed(pyforge.KEY_DOWN):
player_y += player_speed
# Restrict player ship location coordinates safely within viewport borders
if player_x < player_size: player_x = player_size
if player_x > SCREEN_WIDTH - player_size: player_x = SCREEN_WIDTH - player_size
if player_y < 150: player_y = 150
if player_y > SCREEN_HEIGHT - player_size - 20: player_y = SCREEN_HEIGHT - player_size - 20
# --- 2. WEAPON LASER CONTROLLER ---
if laser_cooldown > 0:
laser_cooldown -= 1
if pyforge.is_key_pressed(pyforge.KEY_SPACE) and laser_cooldown == 0:
# Spawn a laser beam coordinate tracking item slightly ahead of the nose cone
lasers_pool.append([player_x, player_y - player_size])
laser_cooldown = 12 # Rate of fire delay throttling steps
if SFX_LASER: pyforge.play_sound(SFX_LASER)
# Move lasers upward and wipe elements exiting the upper border lines
for laser in lasers_pool[:]:
laser[1] -= 10.0
if laser[1] < 70:
lasers_pool.remove(laser)
# --- 3. METEOR HAZARDS SPAWNER ---
if len(meteors_pool) < 6 and random.randint(1, 25) == 1:
meteors_pool.append({
"x": float(random.randint(50, SCREEN_WIDTH - 50)),
"y": 70.0,
"speed": random.uniform(2.5, 5.0),
"size": float(random.randint(20, 35))
})
# Process obstacle tracking movement loops
for meteor in meteors_pool[:]:
meteor["y"] += meteor["speed"]
if meteor["y"] > SCREEN_HEIGHT + 40:
meteors_pool.remove(meteor)
# --- 4. AXIS-ALIGNED BOUNDING OVERLAP COLLISIONS (AABB Math) ---
# A) Laser vs. Meteor Collisions
for laser in lasers_pool[:]:
lx, ly = laser[0], laser[1]
for meteor in meteors_pool[:]:
mx_pos, my_pos, ms = meteor["x"], meteor["y"], meteor["size"]
# Perform bounding circle radius overlap intersection math tests
if mx_pos - ms <= lx <= mx_pos + ms and my_pos - ms <= ly <= my_pos + ms:
# PARTICLES: Explode glowing orange debris sparks directly at point of impacts!
pyforge.spawn_particles(mx_pos, my_pos, color=(1.0, 0.5, 0.1))
if SFX_EXPLOSION: pyforge.play_sound(SFX_EXPLOSION)
running_score += 10
meteors_pool.remove(meteor)
lasers_pool.remove(laser)
break
# B) Player vs. Meteor Fatal Crash Collisions
for meteor in meteors_pool[:]:
mx_pos, my_pos, ms = meteor["x"], meteor["y"], meteor["size"]
distance = ((player_x - mx_pos) ** 2 + (player_y - my_pos) ** 2) ** 0.5
if distance < (player_size + ms) * 0.8:
# PARTICLES: Blast violent red firework shards covering the player coordinates position!
pyforge.spawn_particles(player_x, player_y, color=(0.9, 0.1, 0.1))
if SFX_EXPLOSION: pyforge.play_sound(SFX_EXPLOSION)
print("💥 Ship Destroyed! Returning to application dashboard panel...")
if running_score > high_score: high_score = running_score
# Reset game states completely
running_score = 0
lasers_pool.clear()
meteors_pool.clear()
player_x, player_y = 512.0, 650.0
game_state = "MAIN_MENU"
break
# --- 5. HARDWARE ACCELERATED RENDER PLATFORM DRAW LOOPS ---
# Draw Weapon Lasers
for laser in lasers_pool:
pyforge.draw_button(laser[0] - 2, laser[1], 4, 15, bg_color=(0.2, 0.9, 0.4))
# Draw Asteroid Hazard Obstacles
for meteor in meteors_pool:
pyforge.drawshape(meteor_mesh, x=meteor["x"], y=meteor["y"], size=meteor["size"], angle=meteor["y"], color=(0.6, 0.55, 0.5))
# Draw Player Ship Sprite Quad Map
image_template = [(-1.0, -1.0), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)]
pyforge.drawshape(image_template, x=player_x, y=player_y, size=player_size, angle=0.0, color=(1.0, 1.0, 1.0), opacity=1.0, texture=player_texture)
# Upper HUD Dashboard Banner Bar Panel Overlay
pyforge.draw_button(10, 10, 1004, 55, bg_color=(0.02, 0.02, 0.05))
pyforge.draw_text(f"SCORE: {running_score}", x=40, y=22, scale=0.55, color=(0.4, 0.7, 1.0))
pyforge.draw_text(f"HIGH SCORE: {high_score}", x=390, y=22, scale=0.55, color=(1.0, 0.85, 0.1))
pyforge.draw_text(f"PERFORMANCE: {pyforge.get_fps()} FPS", x=740, y=22, scale=0.55, color=(0.2, 0.9, 0.4))
# --- 6. FLUSH MULTI-EFFECT PARTICLES PIPELINE AND FLIP FRAME BUFFERS ---
pyforge.update_effects()
pyforge.refresh()The tutorial script safely uses the built-in python check if not os.path.exists("hero.png"):. If a beginner developer runs this code without carrying any game asset folders, Pillow will instantly compile a sleek blue cyber delta-wing ship outline directly inside memory RAM and convert it into binary byte channels before feeding it straight into your C core texture slots!
By referencing pyforge.get_fps(), the text rendering system displays active hardware capabilities calculations directly on your header banner. Because your core logic runs on pre-compiled C functions under an orthogonal viewport loop with zero heavy CPU structural bloat, you will see frame rates hitting maximum desktop monitor thresholds.
When you smash your spacebar controls to fire weapons or vaporize incoming hazards, your engine tracks coordinates arrays seamlessly across your split codebase dependencies. pyforge.spawn_particles() tells src/effects.c to instantiate alpha fading point quads, running entirely hardware-accelerated side-by-side with your music tracks decoding buffers!
Because pyforge-engine is actively evolving in its Beta phase, you may occasionally run into edge cases or system-specific library rendering anomalies. If you find any bugs, kindly report them immediately so we can make the engine better together!
To file a professional bug report that our development team can fix in minutes, please follow these steps:
- Navigate directly over to our GitHub Issues Dashboard.
- Click on the green "New Issue" button.
- Provide your system profile specifics (e.g., Ubuntu 24.04, M1 Mac, or Windows 11 MSVC).
- Include a minimal copy-pasteable script reproducing the unexpected matrix behavior or audio glitch.
Your active testing feedback directly sharpens the framework's stability, pushing us closer to our stable v1.0.0 milestone!