Weekly Update – September 23, 2022

I threw away a big chunk of the code that I just wrote last week for Stat Modifiers. I made this decision when it became evident that maintaining a running sum of all Stat Modifiers for each Stat Type wasn’t worth the complexity. I originally chose this approach for efficiency; since the modifiers didn’t change that often, it would be more efficient to precalculate and store the values. In practice, however, the values are used infrequently enough that recalculating them every time doesn’t impact performance. The added complexity, primarily due to having to synchronize the running sums with the individual modifiers, increased the implementation effort and defect risk. In general, it’s preferable to store information in one place only.

  • Improved Inventory Panel. The number of inventory slots has expanded from 24 to 32, the panel has widened, and combat stats are now displayed.
Inventory with stats
  • Tooltips for stat bars and status effects. Hovering over the Health, Stamina, or Magic bars, or a status effect icon, now displays a tooltip with additional info.
  • Actor Debugging Panel. A new toggleable panel displays detailed info about active actors, including location, vitals, status effects, and AI state. It’s been very useful for squashing AI bugs. This is a developer-only feature; it won’t be available to players.
Actor debugger
  • Bug fixes.

Next week starts with closing out AI bugs, followed by validating combat calculations (now that Stat Modifiers are in place) and continuing to add sound effects. Also, I’m going to start on interactions between items and objects, such as scooping water into an empty vial from a fountain. 

Weekly Update – August 19, 2022

This week, there were lots of miscellaneous improvements that stemmed from play testing. The summoned swarm of giant rats continued to be a source of both AI improvements and entertainment. I took great sadistic pleasure in hitting one of the rats to turn it against me and then watching its brethren surround it and rip it apart in my defense. The Conjurer class is going to be fun…

  • AI enhancements
    • Summoned actors now turn hostile if attacked by the summoner.
    • Actors will now attack the closest target rather than an arbitrary target.
  • Actor items now transfer to the actor’s corpse. Previously, when an actor died, all of the actor’s items were added to the cell that the actor died on. Now, the items are contained in the actor’s corpse and can be taken by inspecting the corpse.
  • Random actor items can now be limited to specific item types. This enables actors to carry items that are more applicable to their descriptions. For example, a bandit may carry some gold and throwing knives, while a wizard may carry some scrolls and potions.
  • Actor actions. That actions that can be performed on an object or item are defined by that object or item. Previously, any actor could perform these actions. This didn’t make sense in some cases. For example, a blue slime isn’t intelligent enough to open a door (and doesn’t have hands of course!). Actions that an actor is capable of performing can now be defined.
  • Extended Tier system to actors. Each item has a Tier attribute that indicates the item’s power relative to other items. Tier is used during map generation to ensure that the items placed on the map are appropriate for the dungeon level. I extended Tiers to actors for the same reason. Plus, for actors that have randomly generated items, the possible items can now be limited to the actor’s Tier.
  • Gold is represented as an inventory item for non-player actors. The player’s gold is tracked and displayed in a counter rather than a physical item in the player’s inventory. Now that other actors can carry gold, gold needs to appear as an item that can be taken in the actor’s inventory. When gold is dragged into the player’s inventory, it is added to the player’s gold counter and removed from inventory.
  • Actor sprite support for map themes. Tiles, such as floors and walls, have had theming (caves, dungeons, etc.) from early on. Changing stairs from tiles to actors necessitated adding theme support for actors too. This was trickier than I anticipated. The implementation for tile themes wouldn’t work for actors. The solution was to set the sprite during actor instantiation, based on the theme of the cell where the actor was located. But, that didn’t work when I tested it. It took me a while to realize what should have been an obvious problem – actors were instantiated before their locations were set, so the instantiation method didn’t know which theme to apply. I added an optional parameter to the instantiation method for the theme. It’s a bad hack, but it works and its scope is limited (there are very few actors that will have a different sprite for each theme).
  • Bug fixes
    • Tracked actors aren’t re-evaluated when an actor’s attitude toward the player changes
    • The camera doesn’t follow the player when the player was temporarily controlled by AI (such as under the effect of a Fear spell)
    • Rats can be summoned in walls
    • Rats are able to take items
    • Dragging from the hotbar causes the screen to scroll
    • Clicking the hotbar after scrolling the screen causes the player to move to the cell under the clicked hotbar slot
    • The default player click action for a dry fountain is attack
    • Mass Fear stopped working on the player

Next week, there will be more miscellaneous improvements as I work toward making Legend fully playable. I need to stop putting off the decision of how to limit spell and ability use. This decision is required to balance gameplay. Currently, spells and abilities can be used without limits, making the game far too easy.

Weekly Update – August 5, 2022

Testing the Resurrection spell
  • Resurrection ability particle effects. I mentioned last week that implementing Resurrection was simple. However, this week I hit a snag when I tried adding particle effects. The particle effects were triggered to start at the same time that the Resurrection effect itself started. This didn’t look right because the corpse would change to a living actor just as the particle effects were starting. I added a new Action Step for particle effects so that the particle effects could run in parallel with the Effect Action Step. Then I added a delay parameter to the Effect Action Step so that it could start half a second after the particle effects started. Now the actor appears to come back to life half-way through the particle effect emission and looks much better.
  • AI fixes. I fixed some problems that were rooted in my losing track of what the ActorTracker class is supposed to do. Actors are designed to only act when they need to, which is typically when they see another actor that they’re interested in. This is done for efficiency; the game would be too slow if every actor in the level acted on every turn.  The ActorTracker class was created to allow an actor to continue acting after it can no longer see any actors that it’s interested in, enabling the actor to pursue another actor temporarily. At some point, I started using this class to permanently track actors that an actor was interested in, and built logic on top of this new use case. When the player’s actor was instantiated, every enemy in the level added the player to its ActorTracker. That caused every enemy to check if it could see the player at the start of each turn. This is an expensive check when the player is in the enemy’s sight range, because line of sight is used to confirm that the enemy can see the player. Aside from the performance issues, the shift in how ActorTracker was used broke AI. And, ironically, pursuing actors that were no longer visible, which was the original purpose of ActorTracker, didn’t even work anymore. To solve these problems, I went to my best tools for walking through complicated flows: graph paper and a pen. I diagrammed the AI steps, located the logic flaws, and fixed them in the code. I scaled ActorTracker back to its original purpose and added comments to help me remember its scope in the future.
  • Chest and corpse user experience design. One of the oldest items on my todo list is getting chests fully working. This has been held up by incomplete design. Until this week, I hadn’t determined how the player would interact with chests, and how that would translate to the user interface. I’ve been putting off making that determination until I played some other roguelikes and CRPG’s to get a sense of how other games handled this. Downloading, installing, and playing multiple games until I found a chest seemed too time-consuming, so I resorted to watching a bunch of Youtube videos to find examples. The latter was much more efficient. 

Next week, I’ll get chests fully working and fix bugs along the way.

Weekly Update – July 29, 2022

  • New items
    • Ring of Protection. Improves the wearer’s damage absorption.
    • Ring of Evasion. Improves the wearer’s chance of evasion.
  • New abilities
    • Resurrection. Resurrects a targeted corpse. I was pleasantly surprised by how easy it was to implement this. It was 95% accomplished in the Unity Editor leveraging existing capabilities.
  • Fear now works on the player. The Fear ability, implemented several weeks ago, only worked on enemies. It needs to work on the player too because an enemy may have this ability. Also, I debated whether the caster should be affected if a Fear spell’s area of effect included the caster. I decided that gameplay would be more interesting if the caster was also affected. This forces the caster to be more careful about choosing the area of effect.
  • AI can now be applied to the player. This enables status effects that temporarily take control of the player, such as Fear. This required a lot of work to implement. I originally assumed the player wouldn’t need AI, and that assumption was reflected in multiple code locations that needed to be modified. This enables some interesting potential future capabilities. For instance, an AI could be written to simulate playing the game.
  • Fixed several bugs related to torches. Torches have been the source of many time-eating bugs. The underlying logic is undoubtedly overengineered. Torches are designed to be lit when placed in a Quick Switch Slot, and extinguished when placed back in inventory. When in a Quick Switch Slot, they generate a light tied to the carrier’s position. Lighting and extinguishing torches is handled by a Convert Item effect, which destroys the original item and replaces it with the new item. This effect is triggered by moving the torch in or out of the Quick Switch Slot. I’ve spent far too much time getting this all to work. Bug fixing has been arduous because it requires tracing through a sequence of triggered events rather than walking through code line-by-line. The implementation had to be simplified, even if that meant hard-coding. I settled on changing how Convert Item worked. Instead of replacing an item, Convert Item now keeps the original item and changes the item type and some other item attributes. It’s not as clean, but it gets rid of item destruction and creation (and the logic triggered by those events). It’s also preferable from a performance standpoint, but that was a minor concern.
  • Changing levels is fully working (again). I’ve been play-testing on a single level for months and broke most of the functionality around changing levels (example: going upstairs to the previous level placed the player on the upstairs cell rather than the downstairs cell). To support the new way that actions work, I had to add Ascend and Descend actions. I discovered some functionality that had never been implemented, for example preserving Quick Switch and Hotbar slots. This was a little tricky because the Quick Switch slot state is part of the standard actor inventory (as selected items, decoupled from the UI), while the Hotbar slot state is housed outside of actor inventory as it’s only needed by the player.
  • Automated tests added for potions, scrolls, and rings.

Next week, I’ll keep fixing the known bugs and play-testing to find more bugs. This will continue until the game is in a state where I can play it for an extended length of time and not encounter a single error in the log or exception. I’m getting there…

Weekly Update – July 15, 2022

The bugs keep crawling out. I’m fixing them and finding new ones at about the same pace. On the bright side, they’re getting easier to find. I usually know where to go or at least where to start now. I don’t know if that’s the result of the code improving or me starting to recognize where the common problem areas are. Bugs are also getting easier to fix. Often I simply have to change a configuration value or modify a single line of code rather than having to do major rework or implement a system I didn’t anticipate. However, I’m spending 25% of my dev time on bugs and that is too high. To lower this percentage, I’m allocating more time for automated test development. My strategy is to incrementally add tests from a master list of test cases, and add tests related to the bugs I find.

A major achievement this week was reworking interactions between entities in the same cell. This was originally handled in the following manner:

  1. When an entity enters a cell, it applies its interaction effects to all existing entities in the cell, and all existing entities in the cell apply their interaction effects to the entering entity.
  2. Each turn that entities co-occupy a cell, their AI reapplies interaction effects. For example, a fire will continue applying a burning status effect to each entity in the cell.
  3. When an entity exits a cell, the interaction effects it caused are removed from the remaining entities in the cell, and the interaction effects caused by the exiting enemy are removed from the remaining entities in the cell.

This implementation had a few limitations. It prematurely removed some status effects, such as poison, that have a multi-turn duration. It required effects to be applied by the AI, which never conceptually made sense. Because it relied on AI to apply effects each turn, it hurt performance because it increased the number of entities that acted in each turn. For example, in a room filled with poisonous gas, the gas in each cell acted in each turn. To address these limitations, I did the following:

  • Added a game service to manage per-turn interactions between entities occupying the same cell, allowing efficient interaction handling on each new turn.
  • Added a new effect trigger that only occurs when two entities interact for the first time. This is used when the player walks onto spikes, for example. The player is damaged when walking onto the spikes, but will not be further damaged while remaining in the cell containing the spikes.
  • Removed the logic that removes effects when an entity leaves a cell.
  • Removed the AI logic that applies effects each turn (this is now handled by the interaction manager).

I added a new Map Element, Darkness. When the player walks into a room with Darkness, visibility becomes limited to the adjacent cells. This is intended to be an unnatural or magically-induced darkness, as opposed to normal darkness. It’s not fully working yet. When the player opens the door into the room, all of the room cells the player can see from the doorway are visible. I need to fix this.

The most difficult bug I fixed this week was in the grid traversal raycast algorithm, which is used for line of sight and projectile calculations. Sometimes, arrows didn’t travel to the cell the player clicked. It wasn’t obvious why this was happening, and it wasn’t a major problem, so I didn’t do anything about it for a long time. Now that I’m fixing all of the known bugs, I decided to tackle it. I traced the issue to a method that calculates the final position of a ray (a projectile or light) given a start and end point. The algorithm used is similar to Bresenham’s line algorithm, but it includes every cell that the ray passes through. I can’t remember when I added this algorithm, but it’s been in use long enough for me to have forgotten when I started using it. So, I was surprised to find that there was a problem in it (I should have written unit tests for it). I spent a couple of hours adding debugging statements and tracing through the code. I could see that the algorithm was generating the wrong points, but I couldn’t figure out what I needed to do to fix it. I got out some graph paper and sketched an example. The visual representation was much more helpful than the code. There were actually two errors in the algorithm. The code fixes were trivial at that point. 

Next week will be 40% bug fixing, 40% automated testing, and 20% feature development. The feature development will involve expanding the types of interactions between entities. There’s already a framework for this based on the primary physical material that an entity is made of, but more interactions between materials need to be defined (for example, when fire hits a cell containing a puddle).

Weekly Update – July 8, 2022

The two days I had off from work this week doubled my available dev time. Note those weren’t full days; they were just two extra 3-4 hour blocks of time similar to weekend work. A wide assortment of to-do’s got done, including minor tweaks and enhancements, bug fixes, refactoring, and some miscellaneous items. The general theme was getting the game to a stable, bug-free experience.

  • New Abilities
    • Turn Undead. Classic cleric ability that causes nearby undead to flee.
    • Heal. Heals the caster.
  • Self-Targeted Abilities. Abilities, such as Heal, can now be configured to target the user by default.
  • Entity collection-specific Abilities. Abilities can now target specific entity or entity collection types. This capability was used to implement the Turn Undead Ability (it’s basically the Fear status effect, limited to undead actors). 
  • Interesting bug fixes
    • Some enemies were fleeing from the player unexpectedly. Upon further inspection I determined that this only occurred when multiple enemies were present. Even stranger, when a new enemy spawned, an enemy that previously attacked the player started fleeing. It turned out that enemies were reacting to the movement events of other enemies. This shouldn’t happen because enemy AI contains an actor tracker component that limits the actors that the enemy will react to (typically, enemies only react to player actions). The actor tracker wasn’t being used by the enemy’s movement generator.
    • I wanted to display bones on some of the spikes so I configured the map generator to randomly add a bone pile actor on top of some of the spikes. When I tested this, ghosts appeared where bones should have been. I knew instantly what had happened. The bones are configured to spawn ghosts when they are destroyed. The spike damage destroyed the bones in the first game turn, causing the ghosts to appear. This was an easy fix. I changed the effect trigger for spikes from Touch, which is applied each turn, to DidEnterCell, which is only applied when an actor enters a new cell.
  • Other bug fixes
    • Tooltips remain on screen when the Inventory and Ability panels are closed.
    • Exception thrown when displaying the Abilities panel.
    • The default player action for Rugs and Summoning Circles was attack.
    • Cracked Eggs stopped hatching.
    • Eggs weren’t changing into Cracked Eggs.
    • Exception thrown when removing the Ring of Invisibility.
    • Heal Ability wasn’t working.
    • Closing the Select Cell prompt ended the player’s turn.
    • Poisonous gas, once spawned, would spread throughout the entire dungeon instead of a limited area.
    • Run Ability wasn’t working.
    • Exception thrown when Fires burned out.
  • Minor tweaks and enhancements
    • Eggs now have 1 HP so they are killed in one hit.
    • Destroyed eggs now  leave behind the corpse of the creature inside the egg.
    • Added descriptions for Cauldrons and Crystals.
    • Grass is no longer damaged by arrows or other piercing weapons.
    • Bones are now randomly added to some spike cells. 
    • When the game crashes before play starts, unusable saved game files are created. These are now automatically removed.
    • The default action type for an Open Door was changed from Close to Move. Having Open Doors close when clicked was annoying because 99% of the time the desired action is to move into the doorway rather than close the door. Closing a door can still be done through the Inspect Panel. 
  • Refactored / cleaned up several classes, including the Cell class, which had gotten up to 1,000 lines. There was a lot of code that didn’t belong in Cell – handling projectiles landing on the Cell and handling actors taking items in the Cell, for example. I pulled this code into new static handler classes. This is a poor long-term solution, but it’s an effective way of quickly extracting code that belongs elsewhere.
  • New automated tests: Ring of Invisibility. I haven’t added any new automated tests in a while. The Ring of Invisibility provides a good set of tests not just for the ring itself but for the Invisibility status effect as well. I intend to write automated tests for all Abilities.    
  • Improved ergonomics (for me). I’ve been having some pain in my right arm lately. I’m not an ergonomics expert, but after identifying the positions that caused my arm to hurt, I concluded that I needed to reduce the rotation of both my forearm and wrist. My mouse was too far off to the side, and I was holding it with a pronated grip (the normal mouse grip). I bought a smaller keyboard (the Logitech MX Keys Mini) to move my mouse closer to me. I bought a vertical mouse (the Logitech MX Vertical Advanced Ergonomic Mouse) to maintain a more natural, neutral grip when using my mouse. I’m very happy with the result. In the first week of use, the pain has mostly subsided.

Next week, and for the near future, stability and bug removal will remain the focus.

Weekly Update – June 17, 2022

I designated this week a bug-fixing week. I do this when the list of little (and big) issues that I can tolerate during play-testing but players won’t tolerate gets too large. I have found this to be a productive way of dealing with bug-fixing. When I have to fix bugs in the middle of feature development, it’s a hassle. When I allocate a large block of time just for bug-fixing, I’m more motivated to do it and it’s a great feeling at the end because I’ve made the game more playable and moved it closer to launch. Here’s a sample of bugs that were fixed:

  • AI now responds to getting hit. Previously, AI response was based on seeing the player. If an invisible player hit an enemy, the enemy would just stand there. Adding a new observation type for getting hit fixed this.
  • Minimum projectile range works properly now. This was hard-coded originally because all bows had a minimum range of two cells. Then I added other projectiles such as thrown weapons and wands, and the two-cell minimum no longer applied to every ranged attack. I added a minimum range attribute to item types to fix.
  • After adding a regeneration status effect and discovering that one-time use healing potions were healing the player every turn, I realized I couldn’t rely solely on a number of turns item attribute to determine how many turns to apply an effect (the value 0 was used for one-time use and infinite turns; the value 1 caused a one-time effect to be applied twice, once when immediately used and again at the start of the next turn). To fix this, I added a duration type enum to status effects. The possible values are Once, Finite, and Infinite.
  • Tooltips were no longer appearing. I discovered this resulted from some code improvement I did a while ago in which I changed how tooltips appeared. Instead of code explicitly calling a method to display and populate a tooltip, I had the tooltip respond to an event instead. This reduced coupling. However, the tooltip GameObject is inactive when the game starts, so the Awake event that adds the event listener wasn’t being called. The solution was to add a wrapper GameObject that is active.
  • Tooltips were appearing behind the inventory panel instead of in front of it. I couldn’t fix this with layers or z position, and I couldn’t easily reorder the GameObjects because the tooltip is a child of a GameObject that appears before the inventory panel. I took the path of least resistance by changing the inventory panel layout so that it didn’t overlap the location of the tooltip.
  • When the player teleported into a room with enemies, the enemies didn’t act until the player moved. This was fixed by registering a new observation when an actor teleports. Enemies that observe the player teleporting into the room are awakened.
  • Similar to the previous bug, creatures that hatched from eggs didn’t act until the player moved. However, the solution was different in this case. Since the creature didn’t have an observation, it didn’t awaken. By changing the creature’s starting state to active in its configuration, the creature can act after hatching.
  • The teleport scroll stopped working. 

For the next couple of weeks, game dev time is limited due to travel. As time permits, I’ll continue fixing bugs.

Weekly Update – January 21, 2022

Two big features were completed this week: lighting and partial wall hiding. Both of these features affect what the player sees to produce a more authentic dungeon crawling experience. Finishing them feels great; they required a lot of brain power! 

  • Lighting (v0.95). Lighting now works as desired, mostly. With the ambient lighting, shadows, and illumination, the maps feel a lot more like dark and foreboding dungeons. It may be too much; I’m still tweaking brightness, colors, and distances to get the right balance of visibility. Also, the frame rate has taken a hit, so I will need to improve performance.
  • Added partial wall hiding. Now, the portions of walls that the player can’t see, for example the other side of a wall shared by two rooms, are hidden. This prevents players from knowing what’s on the other side of a wall. It also solves the problem of secret doors being too easy to spot.
Partial wall hiding
  • New test console features. Entities can be spawned in specific locations now (previously they spawned in a random cell adjacent to the player). Extended logging can now be enabled/disabled from the console. These features were added to speed up testing. I’m often using the console to spawn health potions – I need to have more of those drop in the game and/or do some more balancing.
  • Map generation bug fixes. Some objects, such as braziers, were being added under walls or next to doors.
  • Time-consuming animation bug fix. Occasionally, the player’s character would stop responding to player input and effectively bring the game to a halt. I used a new test console feature to view the game turn state and found that the game was stuck on an enemy actor’s turn. I added some breakpoints and found a clue: the actor’s state was stuck in attacking; the actor never received the event that’s fired when an attack animation finishes. That made some sense, because the attack animation wasn’t playing in the first place. I thought maybe it had to do with how I had configured the animation controller and transitions in Unity, or perhaps it was periodically skipping animation frames that fired events. There are plenty of reports of these types of issues in the Unity forums. Those turned out to be red herrings, though it took hours to realize that. The real cause was some code I recently introduced (mistake #1 – not reviewing recent code changes) that enable/disabled animations based on whether the actor could be seen by the player. I only added this functionality when the player moved to a new cell, but not when an actor moved to a new cell. So, if an actor moved from a cell the player couldn’t see to a cell that the player could see, the animator component was still disabled and the animation didn’t play. I solved this by moving the enable/disable logic into the actor visibility property (mistake #2 – implementing logic at too high of a level in the code instead of where the property changes).

Next week, I’m fine-tuning lighting and making some UI improvements. Now that there’s a lighting system, I want to add some more light sources like luminescent fungi and crystals, if I have time.

Weekly Update – October 30, 2021

Thanks to a very rainy weekend and a day off in the middle of the week, I got a lot done this week. The focus remained largely on procedural map content generation.

  • Added a debugging console. A key press displays a prompt. Typing the name of an entity (actor/object/item) creates an instance of the entity in a cell near the player. It’s a time saver because I no longer have to look for an instance of the entity on the map. Why didn’t I add this sooner?! I will extend it in the future as needed.
  • New actor action, Transform, that transforms an actor into another actor. This is used to animate a pile of bones as a skeleton and replace a treasure chest with a mimic.
  • New object, Animated Bones, that changes into a skeleton when the player comes near it.
  • New Map Elements: Animated Bones Area, Debris Area, Storage Cluster. Animated Bones Area places a few piles of animated bones in the specified area. Debris Area adds rubble and other debris to an area. Storage Cluster places storage objects (barrels, crates, etc.) in clusters for more natural-looking placement.
  • Added more Treasure Area Map Element variations. Treasure Areas can be actively used or inactive (abandoned / forgotten), and looted or not looted. The combinations of these parameters produce different effects. For example, an abandoned and looted area will have only empty, open chests, with cobwebs and debris. Conversely, an active, not looted area will have all of its treasure items and chests intact and no cobwebs or debris.
  • New enemies (partially): mimic, gelatinous cube, cultist, bandit, witch. The Unity prefabs, animations, and definition data were created. I still need to add different behaviors for each. I also need to incorporate some of them into Map Elements. As of now, the mimic can appear as a chest in a treasure room, and some shrine rooms may contain cultists.
  • Combat modifier visibility. The cell inspect panel now displays combat modifiers applied by the cell. For example, a cell with rubble, or a cell that is an open door, will modify the chance to hit and/or chance to evade modifiers for any actor occupying the cell.
  • Procedural generation tuning. I fine-tuned some parameters such as probability of caves, numbers of objects and enemies, and non-combat vs combat Map Elements. The final values used for each map are still randomly generated, but the ranges of possible values were adjusted. There’s still a lot of work to do in this area. Some rooms are way too big, or filled with way too many enemies. Some maps have too many rooms, and some don’t have enough.
  • Bug fixes, mostly around map generation. In the course of troubleshooting an AI bug, I discovered that actor tracking was extremely inefficient. Each turn, each actor was checking to see if it could see every other actor. This was occurring because the visibility check was executing before the check for whether the actor was tracking the other actor.

Next week, I need to build expand the utility class responsible for getting cells in different patterns (e.g. room corners, rows/columns, rectangles) to accelerate the creation of new Map Elements that place objects in patterns (e.g. Library, Armory). I also plan on building out the behaviors for the new actors added this week.

Weekly Update – May 22, 2021

I’m still focused on testing and bug fixing as I try to wrap up the second release by end of May.

  • Switched from Visual Studio for Mac OS X to JetBrains Rider. I’m forcing myself to give Rider another try after quickly dismissing it the first time I installed it. Rider is highly recommended by some well-respected Unity developers. I didn’t like it at first because I felt that it excessively decorated and annotated the code displayed in the editor. And, some key features I constantly used in VS were missing or worked differently (keyboard mappings, search box, finding usages). But, Rider’s growing on me. Debugging is much better; being able to see the current values of variables inline with the code is awesome. Expensive method calls and heap allocations are highlighted. It’s better integrated with Unity.
  • Optimization. There was a brief but noticeable stutter each time the player moved onto a new cell. I fired up the Unity Profiler and found the cause: on the start of each new turn, every actor checked which other actors it could see. The check was expensive; it involved getting all the cells between the two actors (using the Bresenham line algorithm) and iterating through the cells until reaching the other actor or hitting a cell that blocked line of sight. The performance was terrible because the line method was allocating a new list every time, and the check was being performed many times (O(n2)). Initially I tried using the ArrayPool class to avoid the new list allocations, but it didn’t work well for a few reasons I won’t go into. Then I realized a list wasn’t needed at all; I just needed the last point. I wrote a modified version of the Bresenham line algorithm that started from the origin and stopped when the current cell matched the specified predicate and returned the current cell. I reran the Profiler and the performance was much better, but could still be improved. Having each actor call the “CanSee” method for each other actor was simple to code but very wasteful; most of those checks weren’t needed. I changed the code from checking each actor in the list to checking for actors only in the other cells the actor could see. This made performance worse, because the checks against all the cells were costly, and the map is relatively sparse in terms of actors. I removed that change, and went back to checking the list of actors, but added a new check to determine if an actor is in the viewing range of the other actor before running the line algorithm. This optimization significantly reduced the number of times that the line algorithm ran and greatly improved performance.
  • Created an instant status effect for adding/removing money. It seems a little strange to have an effect for money, but it basically does the same thing the instant healing effect does in that it’s changing an attribute by some amount. This allowed a few hard-coded references to money item types (like gold coins) to be removed.
  • Added the ability for items to cause status effects as soon as they are picked up. This was needed for the money status effect and future instant effects.
  • Fixed the health bar display logic. I kept having issues with when health bars were displayed and hidden. I’d fix one issue and create a new one. After sketching out the logic on paper the solution was obvious. The basic rule is: health bars are only visible when an actor/object is damaged and visible. However, when an actor/object is first damaged, the health bar needs to appear at 100% and animate down to the % of health after being damaged, so the basic rule doesn’t apply. And, when an actor/object dies, the health bar shouldn’t be removed (even though health is at 0%) until the animation finishes and the health bar shows 0%.
  • Peeled some more code off of the GameManager class.
  • Fixed a lot of bugs found during playtesting.

Next week my goal is to complete testing on Release 2. I’ve found and fixed 100+ issues over the past few weeks, but it’s still going to be tight.