Astral Productions - Technical Gameplay Design

Job Overview

Astral Productions is a game development studio I founded to build projects I genuinely care about, while deliberately expanding both my technical and design skill sets. The work produced under Astral falls into two primary categories:

Large-Scale Projects
Multi-year productions and smaller commercial titles intended for public release. These projects prioritize production-quality systems, long-term scalability, and shipping discipline. A current example is Storm Reavers (as of February 2026), a co-op heist game developed (solo) in UE5. Projected launch in late 2026

Experimental Projects
Shorter, focused explorations targeting specific mechanics, workflows, tools, or design problems. These are not intended for release and instead function as rapid R&D. Common themes include gameplay prototyping, UI experimentation, procedural systems, and custom tooling, all designed to feed back into larger productions or meaningfully expand my skill set.

The core objective of Astral Productions is to push forward gameplay design and technical tooling simultaneously. Although this is an independent studio, every system is approached with a AAA production mindset—prioritizing scalability, iteration velocity, maintainability, and long-term team growth. Nothing is built as a one-off solution.

Several other sections of this site contain deeper technical breakdowns of systems currently under development. I strongly recommend exploring those pages for detailed design and implementation context. This page will continue to evolve as Storm Reavers approaches its playable demo milestone.

Want to read something specific? Jump to…

  1. Storm Reavers

    1. What is it?

    2. Gravity Mechanics

      1. GAS Setup

      2. Replication

      3. Ragdoll

    3. Island Generation

    4. Enemies

  2. Tooling

  3. R&D - 3D movement in 2D

    1. Spline-Based 3D Navigation for 2D Characters

    2. Mapping Movement to a Spline

    3. RVT Spline

    4. PCG Integrated World

Storm Reavers

What is storm Reavers?

The plan is dead. The guards are closing in. You flip gravity and the floor becomes a wall, flying sideways toward the exit with the loot in your arms. Storm Reavers is a co-op heist game where every job falls apart and every player can save the run. Plans may fail, but gravity doesn’t.

Storm Reavers is ultimately a co-op heist game driven by gravity. My goal was to create a replayable co-op experience like recent hits ala R.E.P.O or Lethal Company but with more mechanical depth and combat — so less horror and more combat.

Gravity

Gravity was initially an experimental idea for me, as Epic had exposed their gravity logic to blueprints only a version or two prior. With that I dove into the idea with a handful of gravity types:

  1. Directional Gravity - Gravity that causes you to fall exactly towards your hit direction.

  2. Plane Gravity - Gravity that causes you to fall towards the normal (or plane) of the hit surface. This is closer to the six cardinal directions. Less percise, but much faster to use.

  3. Tethered Gravity - Very similar to planetary gravity where you will continue to fall towards the hit location even if you go past it. This creates a sort of slingshot effect.

  4. Gravity Flip - A single input press that swaps your gravity to the inverse of your current gravity (e.g. 0,0,-1 swaps to 0,0,1). It does require a valid surface above you to work however.

  5. Gravity on Run/Move - This is effectively foot based gravity as it checks your foot location, velocity, and determines the best gravity based on those values. This allows players to run on/across a surface with varied normals such as a sphere.

Gameplay Ability + Replication

The core logic for the gravity mechanics relies primarily on normals, traces, and a mix of GAS and RPC logic.

Gravity was initially an experimental idea for me, as Epic had only exposed their gravity logic to blueprints a version or two prior. With that I dove in with a handful of gravity types: Directional, which falls exactly toward your hit location; Plane, which snaps to the nearest cardinal direction of the hit surface normal for faster less precise shifts; Tethered, which anchors to a world position and continuously recalculates the pull vector even after you pass it, creating a slingshot effect; Gravity Flip, a single input that inverts your current gravity vector gated behind a ceiling trace so it cannot be used in open air; and Gravity on Run, which samples your foot position and velocity each tick to compute gravity from the surface beneath you, allowing players to run across geometry with varied normals like the outside of a sphere.

Under the hood, each gravity type resolves to a direction vector which gets fed into Epic's exposed gravity override on the Character Movement Component. The heavier lifting is in how each type arrives at that vector. Directional and Plane both fire a single trace on activation and derive the vector from the hit result, with Plane adding a quantization step to snap to the nearest cardinal. Tethered stores the hit location as a persistent anchor and recalculates toward it every frame, which required careful handling to avoid fighting the CMC during transitions. Gravity on Run is the most involved, running a multi-trace sample from the foot each tick and blending normals to smooth out transitions across uneven geometry. GAS handles activation and state signaling, while the actual vector math and CMC overrides are driven from the Character Blueprint with manual RPC calls to keep replication clean.

Ragdoll

One of the goals I had for the gravity mechanic was a high opportunity cost. You could traverse distances quickly, sure, but there needed to be a risk. As such I opted for a sort of quick time event. Right before the player lands they can press their dodge input to gain momentary Iframes from fall damage. This has other effects as well, but if they fail they take fall damage and get ragdolled.

Having never (personally) made a replicated ragdoll I didn't quite estimate just how challenging this would be.

My first approach relied on Gameplay Abilities using existing logic to activate/deactivate logic. However this proved unreliable as the GA didn't properly replicate across clients. It looked great on server, but clients would often get bad desync as Gameplay Abilities aren't designed to work on Tick.

The next approach was a Hybrid, using GAS to activate/signal to activate the ragdoll, but driving the core logic via the character blueprint. This approach proved much more reliable as GAS replicates organically, then any additional RPC calls can be handled manually.

The hybrid approach works by having the Gameplay Ability fire an RPC to the Character Blueprint the moment the ragdoll condition is met, which then sets the mesh to simulate physics and applies the custom gravity override locally on each client. Because physics simulation state is inherently non-deterministic across the network, the server acts as the authority on when ragdoll begins and ends, broadcasting those events via multicast RPCs rather than trying to sync the raw physics frames themselves.

Recovery works on a timer driven from the server, and once the recovery window opens, the Character Blueprint blends back from physics to the AnimGraph using a Get Bone Transform snapshot, snapping the capsule to the mesh's current pelvis position before re-enabling movement. This prevents the jarring teleport you get when the capsule and mesh diverge during a long ragdoll.

The custom gravity component presented its own wrinkle here since standard Character Movement gravity is bypassed during physics simulation, meaning the gravity had to be re-applied as a direct impulse or force on the physics body rather than through CMC. This was handled via a tick-driven force application on the mesh's root body, scoped to only run while the ragdoll state is active, and disabled immediately on recovery to avoid fighting the CMC on re-enable. Just to be clear about what would happen here: if the ragdoll activated on, say, the roof the capsule of the character would properly be on the roof, but the mesh itself would fall onto the ground. This made for horrible desync as it seemed like the player was on the ground, not the roof, then they'd snap and tp back to the roof.

Island Generation

Proof of concept island generation taken around Feb 2026.

Enemies

Coming soonTM

Reinventing Movement in 2.5 D — R&D

Spline-Based 3D Movement for 2D Characters

Okay, that’s a lot of words—so what does it actually mean?

Essentially, while there are plenty of 2D, 3D, and even 2.5D games, very few fully side-scrolling titles preserve the freedom of movement you find in a 3D game. I wanted to explore that intersection. What if a player could move with true omnidirectional control—like in a 3D action game—while still following a guided side-scrolling path? And what if that path wasn’t locked to a flat axis, but could twist, curve, and branch, shaping traversal in a more dynamic and spatially interesting way?

Mapping Movement to a line…

The first thing I had to do was decide an approach, Unreal's spline were an obvious choice as they were lightweight and flexible while offering my just enough existing support and info to not require a bunch of additional legwork.

In the first prototyped iteration I grabbed directional input from the player's Input Action (X Value) to determine if the player was moving left or right. Once I had this value I could access the player's current spline owner and run through a series of functions to get the Actor's closest point on the spline. From there you almost need to do the inverse which is simply move forward or backwards on the spline, then convert that spline on the point back to the player's location.

It's important to note, because we have full 3D movement, the player's movement is rarely directly on top of the spline. They are usually off to the top or bottom, so retaining a persistent offset is important. This is key when later discussing the player's dash or other movement abilities.

Functionally this is a somewhat simple setup: Get spline -> Find Closest Point -> Transform Point -> Set Actor's Rotation. It's when the system begins to expand and account for more than basic movement that complex interactions occur.

Spline Rotation Adjustment Function. Called by *most* movement actions.

Choosing A Spline Owner

One of my main objectives was to fully embrace a 3D environment with multiple paths, which meant building a system that could dynamically transfer a player’s owning spline whenever they encountered a new route.

To achieve this, I needed to handle four things:

  1. Spline bounds

  2. Path validity and overlap

  3. Path priority

  4. Path transition

For brevity, I’ll briefly walk through each:

Spline Bounds

Splines in Unreal typically use very few points, which makes their bounds unreliable for path ownership. To fix this, I subdivided the spline in the construction script. By defining a width and dividing by collider size, I could spawn colliders at each subdivision. This gave me far more accurate path bounds, minimizing gaps or overlaps.

Path Validity & Overlap

Without guardrails, the camera would snap aggressively the moment a new spline was detected. To prevent this, I implemented a short timer system that checked two things:

  • Is the path valid? (basic safety check)

  • What type of path is it?

In this system, main paths always took priority over side paths. However, certain “unique” paths (like overlooks) could even override main paths. Each type also had its own overlap duration requirement: unique paths would take over ownership quickly, while side paths required a longer overlap before control shifted.

This layering gave us more natural transitions. Combined with environmental art cues to visually distinguish side routes, it prevented abrupt snapping while still allowing flexibility in player movement.

One of my main objectives was to fully embrace a 3D environment with multiple paths, which meant building a system that could dynamically transfer a player’s owning spline whenever they encountered a new route.

To achieve this, I needed to handle four things:

  1. Spline bounds

  2. Path validity and overlap

  3. Path priority

  4. Path transition

For brevity, I’ll briefly walk through each:

Spline Bounds

Splines in Unreal typically use very few points, which makes their bounds unreliable for path ownership. To fix this, I subdivided the spline in the construction script. By defining a width and dividing by collider size, I could spawn colliders at each subdivision. This gave me far more accurate path bounds, minimizing gaps or overlaps.

Path Validity & Overlap

Without guardrails, the camera would snap aggressively the moment a new spline was detected. To prevent this, I implemented a short timer system that checked two things:

  • Is the path valid? (basic safety check)

  • What type of path is it?

In this system, main paths always took priority over side paths. However, certain “unique” paths (like overlooks) could even override main paths. Each type also had its own overlap duration requirement: unique paths would take over ownership quickly, while side paths required a longer overlap before control shifted.

This layering gave us more natural transitions. Combined with environmental art cues to visually distinguish side routes, it prevented abrupt snapping while still allowing flexibility in player movement.

Accounting for Movement Abilities

A major consideration for this system was how to handle movement abilities. Basic movement along the spline was straightforward: rotation was continuously adjusted to match the spline, so the player’s motion naturally aligned with the path. But abilities that moved the player by a fixed offset—for example, dashing 100 units forward—didn’t work with this approach.

The solution was simple in principle but crucial for feel. After calculating the end point on the spline, I determined the offset and set that as the target location for root motion. However, since root motion doesn’t adjust rotation by default, I also had to apply rotation updates. Without this, abilities produced unnatural, straight-line motions that ignored spline curvature.

This is the key point: the system lives and dies on whether characters convincingly follow the spline. Any diagonal or disconnected movement (not driven by player input) breaks the illusion immediately. Imagine if, instead of rotating continuously, a character just moved forward and then snapped downward—it would feel exactly like AI pathfinding, which often looks awkward and robotic. The player should never be asking themselves “why is my character moving like that?” when using core movement or abilities.

Spline Dash Ability - Uses GAS.

RVT Path

To create path system, I designed a workflow that takes spline input (via PCG) and converts it into material data via Runtime Virtual Textures suitable for rendering and terrain interaction. The process begins by obtaining spline data via PCG tags. This data is then parsed for additional information such as a spline volume/size.

Once the core spline data is obtained we actually need subdivide the spline as our in-world spline has very few points. After subdivision a new spline is spawned via a spline mesh and a special material that maps to a RVT is implemented.

This mesh is only used for RVT data and has neither collision nor visibility. This RVT data can then be obtained in other materials (I use a global Landscape material) and mapped to the designed texture. This enables smooth integration with terrain materials, additional effects like blending dirt or foliage suppression, and ensures the paths feel naturally embedded into the environment without heavy manual sculpting or texture painting.

In addition, I also do some other actions inside of the PCG graph such as spawning rocks/pebbles on the path as well as spawning trees outside of the path's bounds.

Early Prototyped Path Using Generic Materials & Textures. Everything is spawned via PCG, no manual placement.

Final Note

The thing about this system it's its applicable for more than just my 2.5D application. You could also use this as a way of setting up a scripted path for a quest or perhaps pathfinding for AI. It isn't as though this system was designed and siloed off, it does support other direction if I or another designer wanted to do something diferent with it.

About

Gameplay Designer

B.S. Interactive Design

3+ Years Exp.

Resume

Location

Columbus, Georgia

Will relocate

All content on this site was designed and developed by Cole Andrews, unless otherwise noted. Any third-party tools, assets, or references are credited where applicable.

About

Gameplay Designer

B.S. Interactive Design

3+ Years Exp.

Resume

Location

Columbus, Georgia

Will relocate

All content on this site was designed and developed by Cole Andrews, unless otherwise noted. Any third-party tools, assets, or references are credited where applicable.