Website powered by

Targeting Opportunities: "When You Gotta Go...." Level Breakdown

Making Of / 08 March 2020


Intro:

Borderlands 3 remains one of my favorite games in a long time both aesthetically and in terms of level design. With my latest project "When You Gotta Go..." I wanted to explore the design of one of my favorite additions to the game, the Crew Challenges, by making one of my own! Over the past few weeks, I've been putting the finishing touches on the assets and lighting (to be uploaded soon), but in the meantime I wanted to talk about my thought process and design philosophy behind the environment's layout. 

Inspiration and Analysis:

Pictured: The Target of Opportunity Area from Jakob's Estate, Borderlands 3.

Before I tackled trying to design the area, I wanted to get a better sense of how they were constructed. Since the in-game planet of Eden-6 was closest in aesthetic to the mood I was trying to capture art-wise, I scouted out the Target of Opportunity Locations there (more specifically, those located in the Jakob's Estate and Ambermire zones). The results of which (after admittedly dying once to an unfortunate lapse in judgement with grenades) I documented below:

  • Each location was situated slightly off the "main route" of the area and it usually marked by an entryway that serves as a choke-point.
  • Distance was limited in each location, but movement was not. While I couldn't get away from the combat area, I could still find cover and enough space to quickly reload or use explosives. 
  • Locations were designed to prevent spamming. Either higher walls (in the case of the Jakob's Estate area) or the foliage (in the Ambermire) prevented me from simply brute-forcing the target with grenades or launchers and from using the Slam melee attack. 
  • Multiple breakables that dropped health/ammo were placed around the area, but rarely ammo chests. 
  • Consideration had to be given to different player characters and amounts. Each area could be navigated by utilized by the four playable characters and could still prove challenging to a four player party.  

 With all of this in mind, I got to work. 


Layout:

Pictured: The final layout of "When You Gotta Go..."

Note: The main "target" of the Target of Opportunity mission would be the enemy spawn marked with the "x"


After the requisite choke-point entryway, I designed my version of a "Target of Opportunity" Crew Challenge location as a funnel-shaped path culminating with the target located in the outhouse at the center. With this area, I wanted to add a bit more space to the target encounter without counteracting the close-quarters environments found in the locations I'd explored on Eden-6. To make the most out of this added space, I did the following:

  •  Reduced cover in the middle segment between the outhouse and the entrance choke-point. Full cover areas were placed nearer to the choke-point, but had severely reduced visibility to counteract a potential over-reliance on sniping. Additionally, two half cover areas were placed closer to the main group of enemies, which would allow characters with "tankier" action skills such as Amara and Moze to reduce their distance to the target before using their action skills to get close and deal more damage. 

Pictured: Some of the various high cover/low visibility (green) and half cover/better visibility and distance (orange) areas

  • Increased the enemy count to include several more standard enemy units in addition to the "target" NPC. While it would increase the initial difficulty of the encounter, this would also allow for players to more easily gain a second wind. By using psychos as some of these standard units, it would encourage more player movement instead of simply staying behind cover. 
  • Added in some ammo/health chests to increase survivability for solo players. To counterbalance this advantage, I placed these in areas that would be more exposed to enemy fire and would force players to leave cover, while the less-beneficial breakables were placed closer to areas of cover. 
  • Placed an area for lootables in the back of the area. Because of the length from the entrance to the target, I realized that backtracking would be inevitable for the player (*). Especially if the loot rolled from the target wasn't desirable to the player, I wanted to avoid players feeling like the destination hadn't been worth the journey back. The two chests in the back right of the area were meant to address this concern while also further rewarding players for investigating the map fully. 


Pictured: The in-map location of the lootable chests.


(*) while "teleport to vehicle" and fast travel to map waypoints are also options in the game, I didn't want to use these mechanics as a crutch, especially as both these features are not present in every area of the game.  


  • Put a second "choke-point" between the entrance and the target. Group-proofing the area was one of my biggest concerns. Especially after deciding to increase the size of the space, I wanted to find a layout that wouldn't give a four player group a dominant strategy for farming. The addition of the secondary choke-point formed by the pathway leading up to the outhouse would help to prevent the stacking of multiple close-range action skills (ie, Bear Fist and Salamander on Moze and Phaseslam and Fracture on Amara) or group attacks with crowd-control weapons (ie, Flakker, Ultraball, or Quadomizer). 


Conclusion: 

Overall, designing this environment was both incredibly fun and an interesting experience in deconstructing, analyzing, and recreating an area type from Borderlands. I hope you enjoyed seeing some of the results from the design side of things, I look forward to sharing the art side of this project on both Artstation and my website in the coming week! 


See you then, and thank you for reading!

-Dixon Dubow




Lessons Learned: Scripting Randomized in Unreal 4 Bonus

General / 08 May 2019

Hello again! 

In the last post, I touched briefly on some of the lessons I learned from scripting the dialogue system for Boardwalk Gulch. In the summary, I went back to the old system to clean it up; it was a good experience, but nevertheless I really wanted to use what I'd learned about dialogue moving forward. Thankfully, I got to do just that during a new project. With the added benefit of this new project not being a solo task but rather a group effort, I got the added challenge of creating a much-more standalone system then one I'd previously created. In this post, I'll be going over this new system, the changes from the previously created dialogue interface, and some major improvements I was able to make! 


First Improvement: the Master Audio Handler


By far, one of the biggest changes from the Boardwalk dialogue system was the segmentation of the entire audio system into a single blueprint class rather than part of the larger game instance. This was by far one of the best changes made for this system. In addition to being a lot easier for the main coder on our team to implement and being nicely standalone, it was nicely convenient to have all audio-related variables and functions in one place rather than spread across multiple BPs. 


One of the best advantages to this method was that it allowed me to easily alter the levels of the ambient noise during dialogue playback. Being able to temporarily lower and raise the volume of the ambient radio while playing dialogue was a nice addition made possible with this change. 


Second Improvement: the BPI 

In addition to the main blueprint for the master audio handler, I created a secondary blueprint interface for calling the master's functions. This was a much, much more versatile choice than the previous "cast to game instance" required for the Boardwalk system as it allowed for a lot more freedom to message and trigger functions for the audio from other blueprints. 


Third Improvement: Randomization Handling


  One of the biggest drawbacks of the Boardwalk system was its clunky way of randomizing what line was being played. Instead of this, I broke the randomization into two functions that utilized a specified tag from the BPI message. The tag array built corresponded to the audio array (ie, index 1 of the audio array is a generic exclamatory phrase and index 1 of the tag array is "exclamation"). When messaged, the "GetPossible" function was called, which looped through the tag array to find all the entries matching the specified tag, then added their index numbers to a temporary "matching entries" array. This array was then used to get a random line from that group of selected indices. The results are functionally similar to the Boardwalk system, but allow for much greater iteration and expansion than the former. 


Conclusions:

Overall, this system was a great experiment in taking what I'd learned from a previous system to rebuild a better, newer version. This system is a heck of a lot cleaner, and as such, I was able to quickly and successfully integrate it into the main project file. 


Thank you for reading this post! Next time, I'll be getting back into more completed blueprints! 

Lessons Learned: Scripting Randomized Dialogue in UE4 Part 2

General / 30 March 2019

When I undertook the task of creating a short level based off of my previously made Boardwalk Gulch environment, there were two things I set out to accomplish: create a unique character that would inhabit the level, and give the player a sense that this character was interacting with them. 

In the last post, I covered the scripting process for the ambient lines in the dialogue system. This time, I'll be talking about the contextual dialogue scripts built for the game.  


Contextual Dialogue:

With the ambient dialogue system mostly completed, I turned my attention to the main method by which I wanted the NPC personality I had created to interact with the player: context-based voice lines. Since the majority of lines would play based on objects the player picked up in the game world, I decided (for better or worse) to fire the voice lines inside the interaction script for each object. 



Notably, this decision was a bit of a double-edged sword in several ways. 

Pros:

  • Since it used a method of playback fairly similar to the ambient system that came before it, I was able to get basic functionality up and running fairly painlessly. 
  • Having the script executed per-item allowed for easy troubleshooting (when a problem would emerge with a particular line, it was easy to find the associated item and therefore isolate the problem). 
  • The actual logic behind the system itself was fairly simple: cast to the game instance where the dialogue is stored, pick a random line from the relevant set, then display the notification and play the sound file in the same method the ambient lines used.


Cons: 

  • Headaches when it came to overlapping dialogue lines. Since each item's dialogue script ran similarly but entirely separately from both the ambient dialogue and from the script of other objects, a somewhat unwieldy global timer system had to be constructed to make sure that contextual lines would not overlap one another.  
  • This script needed to be inserted into every item with corresponding dialogue. For the scope of the game (only four objects with recorded voice lines) this was not a major issue, but nevertheless it limited the versatility of the system as a whole. 
  • The whole script was very node-heavy, resulting in a fairly messy-looking blueprint. 


Lessons Learned: 

In hindsight, the contextual dialogue script was a slightly clunky but functional system that suffered from poor legibility and limited versatility. That being said, creating the system was a great learning experience for me that taught me several important lessons: 

  • Building out a more versatile system should take priority over a "quick fix", as the latter can create unforeseen problems later in a project and limit expansion further down the line.  
  •  collapsing several of the messier portions into functions and reordering some of the inputs and outputs results in a much cleaner and elegant-looking script that still is functional when used (pictured below): 



Thanks for reading this post! Next post, I'll be covering some of my work with the scripting of endless hallways and impossible spaces! Until next time! 

Lessons Learned: Scripting Randomized Dialogue in UE4 Part 1

General / 28 March 2019

When I undertook the task of creating a short level based off of my previously made Boardwalk Gulch environment, there were two things I set out to accomplish: create a unique character that would inhabit the level, and give the player a sense that this character was interacting with them.

Creating a Character:

The first task was fairly easy to begin. I had already created a mascot of sorts for the doomed "desert beach resort" I had created: Helios Sam, the Sun Cowboy. Creating Sam's backstory of a moderately deranged boardwalk employee accidentally locked into the PA booth and left with a book of cheesy mascot dialogue was to say the least, enjoyable, and being able to recruit the ever-talented Alex Norton to record the voice lines was truly a pleasure. With the character made and the voice lines recorded though, the more technical part of the project would begin. 


The Beginning: 

I reached the decision early on that it would be best to have Sam interacting with the player through an intercom rather than in person, as it both fit in more with the created backstory and, quite frankly, saved a lot of time that would've otherwise been spent on animation and modeling of a character model. This decision, however, came with a natural problem: how to successfully create the illusion of interaction with a NPC without the NPC being having a physical presence in the game space. To combat this problem, I decided to sort the dialogue into two separate groups: ambient and contextual. This first post will cover how I scripted the former. 


Ambient Lines: 

Ambient lines would be by far the easiest type of dialogue to script. A new function (pictured above) was set up in the global game instance to handle both the randomization of the dialogue and the actual audio playback itself. A "master array" of the sound files was set up alongside a string array which contained the corresponding written text for each line (see the example at the bottom of this post). This meant that when a ranged random integer was set for a line, the audio played would match the displayed subtitle. After playing the sound, a separate function would set a new interval of between 15 and 30 seconds before a new line could be played (script below). 

 

Overall, this method proved to be an effective way of managing a dialogue system that felt fairly natural, albeit with some repetition of voice lines. While this method of scripting was by no means a flawless solution (more on that in a later post), it still proved suitable for the scope and stricter time limitation of the project it was a part of.  

Thanks for reading! The next post will cover the scripting of the project's contextual dialogue, as well as a brief post-mortem on the dialogue system as a whole! Until next time!