Rogue Ascent VR

In September 2022, I was hired onto the project as a programmer and VR generalist. My responsibilities have included level generation algorithm design, leader board systems testing and development, optimization, localization tooling, implementation of the engineer’s ultimate ability, mixed reality capture implementation, and boss encounter design and implementation.
You can check out the game here: https://www.rogueascent.com/

Level Generation Design

This was my introduction to the project, and probably one of the most complex parts. But despite the complexity it was a good place to start, because level generation sits at the beginning of the run meaning I got to see all the other important bits of state being set up.

I gathered a lot of inspiration from many source: tabletop maps, screenshots of player built ships in other games, maze generation algorithms. I identified a number of different formats level generation could take. Due to some game design considerations, one won out.

Rooms and halls were not to be too big. Exploration and repositioning are game pillars, walking is not. The player should not have to move more than three times to get to any place they already have line-of-sight on. The game takes place on a space station. It is big, but not cavernous. All this boiled down to making the most out of little space.

My algorithm that made it into the game is based on subdividing and area. It only happens for two iterations. It basically takes a large room and divides it. Then takes the two rooms generated and divides those. Rooms are randomly and automatically linked by doors. This creates a compact area with limited hall length and distinct rooms.

Optimization

Upon my arrival on the project, it ran great. Jordan had been considerate of the limitations the game would face from the beginning. (We actually had room to increase the poly budget.) However, there came a point in testing the game that I began to notice some longer load times. Of course, when testing, the last thing a developer wants is to disrupt their flow with periods of waiting. Further, these hangs did create a less professional appearance for the game. So I really wanted to address this.

There were two points at which these loading times were noticeable: the initial launch of the game (the longer of the two) and the lifts between levels (resulted in dropped frames, unacceptable for VR). Jordan was able to address the initial load time by more intelligently importing many of the heavier assets. He and I collaborated on a fix for the lift load times. The challenge here was more about maintaining frames and VR tracking while the heavy level loading processes executed. I designed and implemented a system where any commands could be queued, then as many commands as would fit in the time span of a frame would be run one frame after another.

Some commands could not be fit in the time span of a single frame. These cases were often optimized by Jordan and I putting our heads together. We were able to get most of them to fit, or at least to run over a lot less. Since it was still possible for some operations to run over time a little, it became important to not let these stack up on adjacent frames. Sorting and packing operations was out of the question, because operations were not going to be consistent between different level seeds and many operations had to happen in a specific order. The top priority here was also to avoid dropped frames, not achieve faster speeds (though that did happen). So I added a sub-second waiting period between operations if the previous operation went over its allotted time. This waiting period was longer (up to a second) the more an operation went over. In this way, the dropped frames were hidden between the waiting periods which were characterized by smooth tracking.

The saw tooth pattern in the profiler shows operations being split across frames. Note that after a peak there is a period of rest.

Localization Tooling

Having worked on localization for both Coffin Rot Brewing Co. and Jetpack Vacation, it made sense to assign me these tooling tasks. Much of the work was already done, as Jordan was maintaining a spreadsheet of all the strings in the game and there was a translation lookup implementation already in place. I was tasked with making tools that hooked into those and established a pipeline for adding or changing strings, and syncing the project files with the master translation spreadsheet.

Scraping all of the text components was something I had done for my previous localization projects. Where Rogue Ascent VR was different was the strings present in code. So I had to not only scrape those, but implement a way for them to use translated text when possible. This did require the creation of a LocalizedString class with which we had to replace every player-readable string in the code base. I made a macro to speed that up and it took about 15-30 minutes. Once that was in place, the localization scraper could identify strings that needed localized in the code. Further, the LocalizedString constructor takes and index pointing to the row of the text in the master spreadsheet. When adding a new string, it is set to -1. Then when the game files are synced with the master localization sheet, the C# files are automatically edited to point to the correct row.

This made me super nervous at first, but I had source control and could run all the tests I needed. Eventually I had made a system that would edit to code base to maintain correct string indices.

This pipeline would later become super handy, as we investigate AI technologies to supplement the great body of work that our translation team started.

Engineer’s Turret

I was asked to restructure the turret code in the game to accommodate the addition of a deployable turret for the engineer (playable character). Our artist handed over a cute little model of the turret companion as well as some rough animations.

I implemented the deployment and behaviors of the turret, locking it to a cardinal direction based on a few heuristics, and fully animated it.

The spinning throw was inspired by Pokeball animations. I made sure to add some anticipation and bounce when it pops up and unfolds.
Noticing a target, rejoicing in its elimination, returning to scanning

Boss Encounters

I was quite excited when I was presented with the opportunity to craft a few boss encounters. The first ended up being a gunship, which the player takes down on foot. This idea grew quite naturally from the intersection of the game’s design pillars and limitations. The core pillar of the game is, “make the player feel like an unstoppable badass.” From this a limitation is born, “Enemies cannot stop the player from acting. Notably, do not affect the player’s movement.”

So I knew I had to make an impressive enemy, one that only an “unstoppable badass” could overcome. But I also knew I would not be able have a gigantic enemy in the same space as the player, because I couldn’t allow it to limit the player’s movement and I did not want it clipping with the VR frustums. Now, there were some spaceship models being used elsewhere in the game, so I thought one of those would make a good boss fight. It had to hover around the perimeter of the arena to focus fire on the player but also to allow the player to get some shots in. So I knew it would have to be more like a helicopter than a jet. It was at this point I started to draw inspiration from the Hind-D fight in Metal Gear Solid, later realizing just how much our design pillars overlapped.

Through the process of designing my own boss fight, I came to a more academic understanding of what boss fights are, and this fed back into my design. I am working on a much longer post about this so I will keep it short here. Basically, the boss is the final test of what the game has been teaching the player. It’s a place for the player to show of their mastery and pull off something spectacular. So I started by identifying the what all the verbs of Rogue Ascent VR are: point/aim, shoot, reload, move(reposition), dodge (via VR tracking), shield, scan, and the ultimate ability of each player class (usually augmenting one of the above). To me, the gunship boss was a place for the player to make snap decisions about which of these to do and when. There are times when shooting at the gunship is easier. There are lots of times when shielding or dodging is needed. Like in the Metal Gear fight, the gunship will dive out of sight, so the scanner can help you ready yourself when for when it comes up guns blazing. To encourage the player to move around the stage I gave the gunship two attacks that target the arena spaces, rather than the character. These attack patterns are sometimes random but always telegraphed and can be completely avoided if given priority.

The second boss I have been working on is inspired by the studio’s previous game, Coffin Rot Brewing Co. It is a sci-fi shooter twist on that game’s Debt Collector boss, the Electro Kraken. The same principles are being used to develop this fight as the Gunship. The main difference is in the number of entities. The gunship focuses the players attention, while the kraken splits it. A number of tentacles slither into the arena, each with its own offensive or defensive ability. By playing with the types and positioning of tentacles, different player actions can be encouraged. AoEs can drive the player out of an area. Gunfire can force them to put up their own defenses. Shields can force the player to move or switch targets.