Untitled Magic/Dungeon Game

Published on September 9, 2026

Roblox Noita-Inspired Binary Networking 3D Card UI MVC Architecture CPS Spells

🚀 Overview

Untitled Magic/Dungeon Game is a Roblox game heavily inspired by Noita. Designed with a focus on deep technical craftsmanship, the project features an emergent spell-building engine, high-speed binary network replication, an interactive 3D card deck UI for wand customization, and a decoupled Model-View-Controller (MVC) architecture.

In addition to the systems engineering, I also created some of the pixel art myself. Using Aseprite, I manually drew the icons for the Bolt, Explosive Projectile, Bounce, Explosive Bounce, and Slithering Path spells (the remaining spell art is temporarily AI-generated while in development).


[!TIP] Adding Your Videos & Screenshots: Drop your gameplay recordings (.mp4, .webm) or screenshots (.png, .gif) into c:\Users\rabbi\Documents\portfolio\static\images\ and reference them directly below!

🃏 3D Wand Deck Customization

Grabbing 3D cards and slotting them on a physical deck to customize wands.

🔮 Spell Casting & Replication

Dynamic spell multicasts, triggers, and projectile physics replication.


💻 The 5 Most Impressive Technical Highlights


1️⃣ 1. Recreated Noita Spell System (With Own Twists)

The core gameplay centers on a dynamic spell composition system inspired by Noita. Spells are built using Continuation-Passing Style (CPS) where modifiers yield functions that dynamically compose parameters for downstream spells:

  • Dynamic Multicasts & Modifiers: Supports Hexagon Form (6-way spread), Double Form, Trigger Bolts (triggering sub-spells on impact), Slithering Path, and Explosive Bounces.
  • Higher-Order Wrapper Composition: Modifiers wrap property updaters via compose(f, g), allowing infinite stacking of spell behaviors.
  • Nondestructive Virtual UI Tree Preview (GetSpellCastTree): Evaluates the full wand cycle non-destructively in a virtual pass to construct a hierarchical tree for HUD rendering before resetting wand pointers.
-- Continuation-Passing Style Wrapper Composition
local function compose(f, g)
    return function(val)
        return g(f(val))
    end
end

-- Example Spell Modifier: Explosive Trigger Bolt
{
    Name = "boltWithTrigger",
    Type = SpellType.Projectile,
    Cast = function(wand, spell, castState)
        return 1, function(nextSpells)
            local tracker = wand:CastProjectile("Bolt", castState, spell)
            local nextSpell = nextSpells[1]
            if nextSpell then
                tracker.onHit = function(hitProjectile)
                    nextSpell.cast({
                        cf = CFrame.lookAt(hitProjectile.position, hitProjectile.position + hitProjectile.velocity),
                        bounceFromNormal = hitProjectile.normal
                    })
                end
            end
            return { projectiles = {tracker} }
        end
    end
}

2️⃣ 2. Performant Binary Networking System

To replicate hundreds of fast-moving projectiles and enemies simultaneously without network lag:

  • Declarative Serialization (ByteBuffer.luau): Custom schema DSL mapping entity states to bit-aligned byte layouts (Id, Position vec2, Health u32).
  • Dynamic Byte-Width Scaling: Dynamically selects byte writers (u8, u16, u32) based on active entity counts to save every bit of network bandwidth.
  • Delta Replicators (ServerCorrectionReplicator / ClientCorrectionReceiver): Streams byte-packed updates with strict assertion checking (offset == totalBytes), reducing remote event payload size by over 70%.
-- Server Binary State Replicator Flush
function ServerCorrectionReplicator:Flush()
    local totalBytes = CORRECTION_COUNT_BYTES + count * (self.idSize + self.stateSize)
    local output = buffer.create(totalBytes)
    buffer.writeu16(output, 0, count)

    for _, entity in self.entities do
        self.writeId(output, offset, self.getId(entity))
        buffer.copy(output, offset + self.idSize, self.getStateBuffer(entity), 0, self.stateSize)
        offset += self.idSize + self.stateSize
    end

    assert(offset == totalBytes, `Packet mismatch: wrote {offset} bytes, expected {totalBytes}`)
    self.remote:FireAllClients(output)
end

3️⃣ 3. Immersive 3D UI (Wand Customization Deck)

Instead of traditional flat 2D menus, players interact with a tactile 3D UI:

  • Physical Card Grabbing: Players physically grab 3D spell cards in world space and place them onto a 3D deck to assemble custom wands.
  • Spatial Feedback: Directly connects physical card ordering on the deck to the underlying GetSpellCastTree virtual pass, giving real-time visual feedback on how spells will fire.

4️⃣ 4. Clean Code Architecture & Model-View-Controller (MVC)

The codebase is built with strict software engineering standards for performance and maintainability:

  • Strict Model-View-Controller (MVC):
    • Model Core (ProjectileEngine / Wand): Pure vector math, sub-stepping (1/30), linear drag decay, normal raycast reflection, and homing vectors. Zero GUI or instance coupling.
    • Reactive View (ProjectileRenderer): Visual subscriber listening asynchronously to engine signals (ProjectileAdded, ProjectileHit, ProjectileRemoved) to handle 3D meshes, lighting tweens, and sound SFX.
  • Single-Responsibility Hierarchy: Clean folder structure dividing code into Buffers/, Classes/, Data/, Effects/, Enums/, GameStates/, Gui/, Singletons/, and Utils/.
  • Memory Pooling (RotaryIdMap): Circular ID buffer pool (MAX_PROJECTILES) that recycles entity IDs to eliminate runtime garbage collection stutters.

5️⃣ 5. Custom Vector-Math Steering & A* Pathfinding

Rather than relying on Roblox’s bloated Humanoid instances, the game utilizes a pure mathematical vector engine combined with custom hitboxes for massive AI swarms:

  • String-Pulling Path Smoothing: The custom A* implementation (NavGraph.luau) fires workspace:Spherecast scaled to the agent’s radius to prune unnecessary intermediate path nodes when a clear line of sight exists.
  • Boids-like Steering Behaviors: Enemies dynamically calculate steering forces (EnemyEngine.luau). A separation force checks neighboring hitboxes and applies an inverted vector to prevent clipping, while a repulsion force prevents overshooting the target—creating fluid, flocking AI swarms.