2021 Year in Review

2021 Retrospective

Legend has been in development for 2.3 years. It’s hard to believe that that much time has passed since I started working on the game. I don’t know if anyone else has experienced this, but how I felt at the two-year mark was in stark contrast to my feelings at the one-year mark. After the first year of development, I was thrilled with how much I had accomplished and excited for the future. At two years, panic set in. How could I possibly finish at the rate I was going? Was I wasting my time? Was this game even any good? 

Ultimately, the mid-gamedev crisis was a good thing. This was my brain telling me to reassess and correct course. That’s exactly what I did in the latter part of the year. The reality was that, at the rate Legend was progressing, it would need at least several more years to release. I don’t want to wait that long (I have more games to do!). I cut a large chunk of planned features while preserving the original vision. I forced myself to make decisions. I have a tendency to postpone decisions as long as possible to avoid limiting possibilities. It’s been a sure-fire way of keeping completion in the distant future.

Development thus far has consisted primarily of building the game system framework, and rebuilding many facets of the framework as my Unity knowledge increased and my ideas crystalized. Heading into 2022, the framework is done and development shifts to using the framework to flesh out the actual game.

What I said I was going to do in 2021:

Replacing the stock art

Completion: 0%

I’m still using Oryx. I still have some uncertainty about the exact 2D perspective that will be used. I also feel that the art doesn’t need to be replaced until the game is ready for a public release.

More content 

Completion: 5%

A handful of new enemies, items, and objects were added. Every piece of existing content was reworked in some way, for example extracting a parent class for actors and items, and moving from room-based to element-based map generation.

More polish

Completion: 30%

At the beginning of 2021, I never would have expected to accomplish as much as I did in this area. I considered polish something you do at the end of development. That largely is the case, but I’m using “polish” loosely here to refer to any visual and audio effects beyond the bare minimum, and refinement of any sort, such as fine tuning procedural generation and balancing combat. The game looked bad and felt dull whenever I did testing. Even though I knew I’d improve the look and feel before launch, I was still getting discouraged. For my own psychological benefit more than anything else, I added some game juice, including:

  • Particle effects
  • Better combat and movement animations 
  • Screen shake
  • Environmental impact: corpses, particle effect residue
  • Sound effects

Visuals | Sound Effects

This did the trick; I could finally envision other people playing the game.

Community building

Completion: 5%

I posted a weekly dev update on the website and Sharing Saturday on r/roguelikedev. I posted the link to each Sharing Saturday update on Twitter. I occasionally posted videos on Youtube. I have tiny followings on those channels. I continued to spend a minimal amount of time on community in favor of game development.

Early access release (aspirational goal)

Completion: 10%

This was nowhere close to happening, but there was movement toward this goal due to focusing on it at the end of the year. I also created a mind map visualizing features by release and a roadmap.

What else happened in 2021:

The new map generator, started in 2020, was finished in January. I built some dev tools for procedural generation analysis, tuning, and troubleshooting: an interactive map generation visualizer and a map graph visualizer. These proved to be very handy.

A map graph

I made another major change to map generation later in the year with the addition of Map Elements. These are the basic building blocks for populating a map with content after the map structure’s been created. They’re hierarchical and modular, enabling rooms to be constructed from layers of interchangeable components and reuse common components. For example, many room types can include an Abandoned Map Element that adds cobwebs and debris to give the appearance that the room is abandoned.

Maps became both more varied and more playable. Map configuration parameters, which dictate a map’s structure (number of rooms, room sizes, room distances, room themes, etc.) are now randomized. This additional layer of procedural generation increased the variety of maps while maintaining consistency within a map. Map configuration parameter ranges were fine-tuned to avoid problematic maps. Room type probability changed from a linear distribution to a weighted distribution based on rarity to give maps a more logical composition of rooms.

Significant UI work was done. New screens were added, a hotbar was added, and the main game UI was refined. 

Class selection screen
Inventory and hotbar

In action:

AI was expanded. Now, actors can have their own AI controllers and behave differently than other actors. AI controllers can be reused across multiple actor types. Actors can now track any number of other actors, enabling them to do much more than charging the player. They can attack, defend, and interact in other ways with tracked actors as dictated by their AI controller. Actors now gain awareness of events based on their vision and hearing ranges and act based on the type of event occurring. Each actor has its own inventory and the ability to pick up items and use them. When an actor dies, other actors can acquire the items it was carrying.

In light of my time constraints and the mountain of work remaining, I made a deliberate effort throughout the year to increase my productivity. The ways in which I did this include:

  • Reducing and simplifying code.
  • Moving logic from code to configuration (physical material interactions, game events).
  • Adding unit testing.
  • Adding an in-game console for spawning objects to accelerate playtesting. 
  • Adding more granular logging so that I have more information to troubleshoot.
  • Consolidating test settings into a single design-time editable object.
  • Switching my IDE from Visual Studio to Rider.
  • Reorganizing my Unity editor layout.
Reorganized Unity Editor layout

Finally, code rework occurred throughout the year. Most of it was necessary to keep the code maintainable, but I also know that I am overly eager to rework code and sometimes create more problems for myself than I solve. As the year progressed, the rework did slow down. There are two reasons for this: 1) the framework reached maturity and 2) I became more selective with when I reworked code. Before I make changes or add new features, I now consider what compromises I can make and what I can do within the existing framework to avoid rework. 

2022 Outlook

This year’s goals are essentially the same as last year’s. However, the priorities have changed. An early access release is now the primary goal. Original art, a requirement for the release, is also a top priority. Effort will be concentrated on what is absolutely necessary for public release, including tightening the game loop, balancing, and clearing the bug list. 

Weekly Update – December 24, 2021

Happy Holidays everyone! With a few days off from work this week, I got a lot done! Many of the changes were visual, which always feels more productive. I primarily worked on combat game juice.

Combat game juice
  • Corpses. Slain enemies now leave corpses behind. This increases the player’s impact on the environment and will be used for a variety of mechanics including reanimating corpses, enemies reacting to corpses, and combat modifiers when fighting on a cell containing a corpse. I was pleasantly surprised to find that this feature didn’t require any code changes. It was implemented entirely in the Unity Editor using the custom trigger/effect framework developed earlier in this year.
  • Particle effect residue. Particle effects can now leave permanent residue, such as blood stains and bone fragments. Along with corpses, this is intended to increase the player’s feeling of leaving a mark on the environment. You can very clearly tell where battles took place and the size of those battles.
  • New walk and attack animations. After getting frustrated a couple of weeks ago by not being able to make a decent walk animation, I took some time to learn more about classic animation techniques. I applied the squash and stretch technique to walk and attack animations. I also added a slight pause to the beginning of attacks for anticipation. I got much better results this time around, due primarily to a more precise application of squash and stretch.
  • Replaced animation implementation. In researching Unity animation techniques I discovered Unity’s new 2D Animation package. This package provides a way to reuse animations with different sprites. Previously, I was using what I believe is the traditional methodology – for each animated game object prefab, create an animation override controller for the animation state machine, duplicate the animations referenced in the animation controller, and replace the sprites in the animations. This technique has major drawbacks: 1) it’s tedious 2) it generates a lot of redundancy. If I ever needed to change a common animation such as walking, I’d have to change every copy of the animation. The 2D Animation package provides a way to define an animation only once and swap out sprites based on the game object using the animation. This is done by adding a sprite library and sprite resolver component to each animated actor and configuring animations to reference the spriter renderer properties rather than the sprite directly.
  • Added a parent to all animated actor prefabs. This was suggested by /u/Notnasiul as a way to use relative positioning in animations and as a general good practice. It took around an hour to go through the existing actor prefabs, add a parent game object, and move the Sprite Renderer component to a child game object. And… it worked great!
  • Directional attack animations. With the capability to control relative game object position in the Unity animator, and trigger events in animations, I was able to remove the code that was previously responsible for this. This code was complicated. It updated the game object position on each Update() based on an easing function and managed time-based state changes for different points in the animation where events had to occur. I was glad to see it go.
  • Reconfigured my Unity Editor layout. Up until now I’ve been using the default Unity layout. This layout has been workable but not optimal. The project asset hierarchy panel isn’t large enough, the game object hierarchy panel is too large, and some tabs that need to be viewed concurrently (such as Animation and Scene) are by default in the same panel. I reorganized the panels to better suit the needs of this project. This is the end result.

Next week’s goal is to give level 1 the right amount of challenge. To do this, I’m going to tweak stats and drop frequencies for items and enemies. I want to finish the year with at least one good level. 🙂

Weekly Update – September 24, 2021

I continued to work on combat this week, but shifted from core mechanics to “game feel.” After adding particle effects and sound effects, combat is much more satisfying. The game is getting closer to being fun. 🙂 

New Videos

Combat particle and sound effects

New GIFs

Blood splatter
Bone spray
Stone spray

This Week’s Achievements

  • Combat particle effects. I’ve never worked with particle effects. There’s a learning curve with Unity’s particle effects system, but being able to change settings in the editor and see the effect in real-time helped immensely. Also, I accelerated by learning my buying a particle effect package in the Unity asset store and studying how it worked. I created a few particle effects for when an actor/object is hit with a weapon. The target’s physical material determines the particle effect. For example, when a rat is hit it will spray blood, while a skeleton archer will spray bones and bone fragments. Additionally, the size and number of particles vary based on the amount of damage inflicted.
  • Improved combat sound effects. I was annoyed with the combat sound effects I chose. They were so quiet and boring. I was going to have to either find better assets, increase the volumes of the assets I had, or use the Unity audio mixer to get better sound. I was also running into an issue where the direction the sound was coming from was wrong. But, that issue ended up being a blessing in disguise because it made me realize why I was having low volume issues: the audio listener was attached to the main camera, which was way up above the player. When I attached the listener to the player, all the volume issues went away (though I haven’t fixed the directional issue yet).
  • Added hooks for additional sound effects, including dying, taking an object, ambient sound, footsteps, and walking on different types of terrain. Footsteps are challenging. I lowered the volume and slightly randomized the pitch of each step to make them less prominent and repetitive.
  • Right-clicking does something. Right-clicking on an object will display the Inspect Panel for that object. I should’ve done this a long time ago, but it was overlooked because the original target platform was mobile rather than PC.
  • Mapping the number keys to hotbar slots. Another basic feature I overlooked – allowing items in the hotbar to be used by pressing the corresponding number key.
  • Added probabilities to triggered game events. For example, when a pile of bones is destroyed, there’s a 50% chance that a ghost will spawn. This provides some unpredictability and more player choice.
  • Framework for player notifications in the Inspect Panel. When a player inspects an object, in addition to the description of the object, I want to communicate important gameplay information. For example, if the player inspects an object that is far away, I want to inform players that they need to be standing next to the object to interact with it. There’s now a framework for collecting notifications from various sources and prominently displaying them on the Inspect Panel.
  • Fixed many combat bugs. There were a surprising number of things that didn’t work or caused crashes. The majority of these bugs were from the last major refactoring.

Next Week’s Goals

Next week, I’ll continue working on “game feel” and small refinements that go a long way. I’m way off track on the milestone schedule, but I feel closer to being done than I think I would have had I stuck with the planned milestones. The additional content I was previously working on wasn’t making the game any better because the core loop was lacking.

Weekly Update – July 23, 2021

Two Release 3 features completed this week: Class Selection Screen and Continue Game Screen.

Select Class Screen
Continue Game Screen
Title, Class, and Continue Screen Video

Only three classes will be available when the game is installed. Additional classes can be unlocked for a total of 16. I haven’t determined how the additional classes will be unlocked yet. 

An unlimited number of games can be saved and resumed at a later time. Games are saved automatically when a new game is started and on application exit. A saved game is deleted when it is loaded to prevent save scumming.

Aside from new features, I did some more cleanup from the big actor refactoring two weeks ago.

Next week, I’m starting on the hotbar and doing a lot of design work on spells and abilities.

Weekly Update – June 25, 2021

  • I stuck to the plan this week (for a change) and finalized the Release 3 features. I’ll publish a summary in next week’s post.
  • Added cell-based status effects. These are effects that are added when an actor moves onto a cell and removed when an actor leaves a cell. An example is a Darkness effect that limits the player’s visibility. This builds on the existing capability to add status effects by using an item or interacting with an object. This feature was unexpectedly difficult to add; a lot of the status effect code and Unity configuration needed to be reworked.
Darkness cell effect
  • Room features. Each room has a room type that determines what’s in the room. For example, a Shop Room contains a shop and a Treasure Room contains treasure. I needed to be able to apply different features to a room to do things like placing a trap inside of a treasure room. I added a base RoomFeature class and some child classes like ShopRoomFeature and TrapRoomFeature. This makes it possible to mix and match different features can create a wider variety of rooms than was possible using room types. I still need to fully incorporate this into the map generator, specifically by replacing the existing room generation with feature-based generation.
  • Overhauled effect triggering. I was never comfortable with the way I originally implemented the configuration of status effects caused by an object or item. Effects inherited from MonoBehaviour so that they could be added to game objects in the Unity editor. It worked, and it was designer-friendly, but it was unintuitive (it wasn’t obvious the purpose of the component was to cause an effect) and too heavyweight (multiple effects required multiple components). I converted effect types to ScriptableObjects and created a new class containing a list of effect types. I called this class TriggeredEffectCollection and used it to replace the individual effect components. Adding effects is now easier (add/remove from a list editor in the Unity inspector) and the purpose of the component is more clear.
  • Removed the limit of one static object per cell. Now cells can have any number of static (not moving) and dynamic (moving) actors/objects. For example, a cell can now have a floor trap and a statue. This also simplified the code because now there’s just a list of actors/objects instead of a list plus the static object.
  • Extensive actor AI logging. Now the log very clearly shows why an actor chose a particular action and not others. Very handy for troubleshooting. The basic AI process is to 1) identify the potential actions and 2) select the best action. The logging explicitly indicates why potential actions were available or unavailable, and why potential actions were rejected or selected.
  • Added new effects: Cure Poison, Darkness, and Paralysis.
  • Added new items: Cure Poison Potion, Throwing Axe.
  • Damage type resistance. Physical materials now have explicit resistances to different damage types. This will be used to do things like making an axe more effective against a wooden door than a short sword.

The plan for next week is to replace Room Type-based room generation with Room Feature-based room generation and start on two new features: Enemy Alerts and Knowledge. The former provides visual indicators for whether enemies have seen the player and the former varies the descriptions of things based on the player’s knowledge.

Weekly Update – May 7, 2021

I continued firming up Release 2 this week. 

  • Improved cell highlighting. When selecting a target cell to shoot at or throw an object at, highlighting now excludes cells that can’t be seen
Highlighting cells for shooting
  • Improved actor tracking. Enemy and NPC AI controllers were hardcoded to track the player. This caused a bug where other actors weren’t tracking the player because they were being instantiated during map generation, before the player was instantiated. But, the real problems were that a) the AI should determine what is being tracked instead of hardcoding it and b) the decision to track should be made when the other actor becomes visible, not when the tracking actor is created. I removed the hardcoding, extended the AI to determine what an actor tracks, and moved adding tracked actors to the point when the tracking actor sees the actor to be tracked.
  • Improved health bar visibility logic. Health bars used to appear over every damageable, dynamic actor. Now they only appear when an actor has been damaged, and appear for static actors like doors.
Health bars now only appear when actor/object is damaged
  • Miscellaneous refactoring and code improvement, which includes:
    • Changing variables from public to private
    • Removing unused classes and code
    • Consolidating redundant code
    • Method comments
  • Many bug fixes. When things break, I’m spending more time on the fixes to lower the chance of the same things breaking in the future. I’m making progress but I have a lot of room for improvement.
  • Trimmed scope. I moved some planned enhancements from Release 2 to Release 3. I’ve been working on Release 2 for 15 months. That’s a lot longer than I want to spend on each release, so I pushed out the remaining planned enhancements to the next release.

Next week, play testing and bug fixing continues as I work toward wrapping up Release 2.

Weekly Update – April 17, 2021

Improved Player Status UI

Player health and gold indicators got a visual upgrade. They used to be shown in a single text control as numbers, which worked but wasn’t visually appealing. I moved player health to a health bar. I moved the gold counter to the inventory panel. I don’t think it’s necessary for players to always see their gold amount, so I removed it from the main screen. For both the health bar and gold counter, linear interpolation (lerp) is used to smoothly transition to new values.

Player and nonplayer health bars
Gold indicator

Evolving AI 

Now that basic AI is in place for enemies, I occasionally encounter (pleasantly) surprising behavior during testing. 

For example, a skeleton archer kept trying to shoot the player when the player ran out of the room and was no longer visible to the archer. I checked all of the associated code and still couldn’t figure out why the archer was shooting. Then I realized the archer wasn’t shooting at all; it determined it couldn’t shoot the player, so it was throwing the arrows in its inventory instead (the player visibility check for thrown items wasn’t implemented yet). 

Another time, an archer ran out of arrows and started moving toward the player for a melee attack. It walked over an arrow that had missed the player, picked it up, and shot it at the player. I had no idea that would happen. Turns out, the item pick up code was used by both the player and non-players.

Miscellaneous

Lots of small-scale refactoring, improvements, bug fixes this week.

I haven’t planned out next week yet. One thing I need to get done soon is the scope of the next playable version. Without a specific set of features to work toward, I’ll never get to “done.”

Weekly Update – January 16, 2021

This week I completed the map generation visualizer. I made a short video to demonstrate. This is best viewed frame-by-frame if you want to get into the details of each step. I’ll create a longer video with commentary at some point.

I fixed a bunch of bugs in the map generator and map generation visualizer. It’s amazing how much faster I’ve been able to spot and fix bugs after overhauling the map generator and being able to see the exact step at which an issue occurs in the visualizer.

Next week, I’m going to add some new map content – new room types, new interactive objects, improved locked door/key placement.