Back to Projects
Game Dev · Created at Nov 5, 2024 · Last updated at Nov 5, 2024 · 3 mins read

Daemon Windows

A multi-window action game that turns the Windows desktop into a shrinking battlefield, with wave-based survival, a shop upgrade system, and real Win32 windows as game objects.

C++ DirectX 11 Win32 API FMOD

Overview

Daemon Windows is an experimental action game that breaks out of the single-window paradigm. Instead of rendering inside one viewport, the game spawns real OS windows — the player lives inside one, enemies can each have their own, and the shop opens as a separate window. The player’s window continuously shrinks, creating mounting pressure to eliminate enemies and collect coins before running out of space.

The core loop is wave-based survival: triangle-shaped enemies chase the player across the desktop, bullets fly between windows, defeated enemies drop coins, and between waves the player spends coins in a shop to upgrade fire rate, damage, projectile count, piercing, homing, and more. Boss waves arrive every 5 rounds with scaled difficulty.

Built as a personal project exploring unconventional game design, the game runs on a custom Daemon Engine providing DirectX 11 rendering, FMOD audio, and a widget UI system — extended here with a WindowSubsystem that manages the creation, animation, positioning, and destruction of native Win32 windows as first-class game objects.

Architecture

The game extends the Daemon Engine with two custom subsystems: WindowSubsystem for multi-window management and WidgetSubsystem for in-window UI. The Gameplay layer owns the state machine, entity system, and wave/upgrade managers. All communication flows through the Engine’s EventSystem.

ModuleDescription
GameplayGame state machine, entity base class, Player/Triangle/Bullet/Coin/Shop entities
WaveManagerProgressive difficulty with base × 1.2^(wave-1) scaling, boss waves every 5 rounds
UpgradeManager7 upgrade types × 5 levels with 1.5× cost scaling per level
WindowSubsystemWin32 window creation, animation, entity↔window bidirectional mapping, focus management
WidgetSubsystemButtonWidget text labels rendered on child windows

Features

Multi-Window Gameplay

The WindowSubsystem creates and manages real Win32 child windows at runtime. Each entity can optionally own a window — the player always has one, and enemies spawn with or without windows randomly. Windows track their owning entities, follow entity positions on screen, and support smooth animated resizing and repositioning via interpolation. Each child window gets its own DirectX swap chain for independent rendering.

Wave-Based Survival with Shop Upgrades

The WaveManager drives progressive difficulty: each wave spawns base × 1.2^(wave-1) enemies, with boss waves every 5 rounds. Between waves, the player opens the Shop (a separate OS window) to spend collected coins on 7 upgrade types:

UpgradeEffect
Fire RateFaster bullet cooldown
DamageHigher bullet damage
Projectile CountMore bullets per shot
Bullet SpreadWider shot pattern
Bullet SizeLarger hit area
PiercingBullets pass through enemies
HomingBullets track nearest enemy

Window Shrinking Mechanic

The player’s window continuously shrinks frame-by-frame via AnimateWindowPositionAndDimensions, compressing the playable area. The shrink stops when the client dimensions reach 2.5× the player’s physics radius, preventing a zero-size window. Enemy windows with the hasChildWindow flag also shrink. This creates a ticking clock — clear enemies and progress before the window becomes too small to maneuver.

Event-Driven Architecture

All game state transitions, collisions, wave progression, and upgrades flow through the Engine’s EventSystem with named events and typed arguments. OnCollisionEnter dispatches per-entity collision responses, OnWaveStart/OnWaveComplete manage wave lifecycle, OnUpgradePurchased confirms purchases, and OnEntityDestroyed triggers coin drops. Entities subscribe and unsubscribe independently without direct references to each other.

Code

The WindowSubsystem bridges game entities to real Win32 windows — creating an HWND, wrapping it in a game Window object, registering a DirectX swap chain, and establishing bidirectional entity↔window mapping:

WindowID WindowSubsystem::CreateChildWindow(EntityID const owner,
                                            String const&  windowTitle,
                                            int const      x,
                                            int const      y,
                                            int const      width,
                                            int const      height)
{
    // Check if entity already owns a window
    auto existingIt = m_actorToWindow.find(owner);
    if (existingIt != m_actorToWindow.end())
        return existingIt->second;

    HWND hwnd = CreateOSWindow(windowTitle, x, y, width, height);
    if (!hwnd)
        return INVALID_WINDOW_ID;

    WindowID newId = m_nextWindowID++;

    std::unique_ptr<Window> newWindow = std::make_unique<Window>(config);
    newWindow->SetWindowHandle(hwnd);
    newWindow->SetDisplayContext(GetDC(hwnd));
    newWindow->SetWindowDimensions(Vec2((float)width, (float)height));
    newWindow->SetWindowPosition(Vec2((float)x, (float)y));

    m_windowList.emplace(newId, WindowData{std::move(newWindow), {owner}});
    m_actorToWindow[owner] = newId;

    if (g_renderer)
        g_renderer->CreateWindowSwapChain(*m_windowList[newId].m_window);

    ShowWindow(hwnd, SW_SHOW);
    return newId;
}

Technical Specifications

ComponentTechnology
LanguageC++20 (MSVC)
GraphicsDirectX 11, HLSL
Window ManagementWin32 API (multi-HWND, per-window swap chains)
AudioFMOD
EngineCustom Daemon Engine
PlatformWindows (x64)