top of page

DunMesh-ish

  • Writer: michaeldiazcompsci
    michaeldiazcompsci
  • Dec 21, 2024
  • 5 min read

Updated: Jan 29

Game Genre

Roguelike, Technical Demo

  • During the third semester, our Technical Design class was focused entirely on the design, architecture, and development of enemy AI systems in games, with a semester-long project to create a custom AI from scratch

    • For my project I opted to create a Utility System AI, as systems such as Minimax and Behavior Trees seemed to simple for a month-long project, but GOAP and HFSMs were incredibly intimidating to me at the time

    • As for the context in which the AI existed, I took inspiration from both Hungry Knight (the precursor game that inspired Team Cherry's Hollow Knight) and the show Dungeon Meshi

      • In essence, the player needs to kill enemies that spawn in endlessly in order to refill their health and stamina; the caveat being that enemies have their own diets and can act both according to their aggression towards other enemies and their individual hunger levels


Game Engine

Unity 2022.3.13

  • As a requirement for this project, we needed to code our AIs from scratch; given my greater proficiency in C# compared to C++, I opted to use Unity to maintain the most control and comprehension over my code base

    • Additionally, my familiarity with how Unity structures its component architecture was much more familiar to me, allowing me to rapidly test implementations and alter my prototyped enemies as needed


Responsibilities

Technical Designer

  • With DunMesh-ish, I knew I wanted various categories of diets to mimic the natural balances that you see in nature when organisms in certain niches are overhunted or resources become scarce, and began with 3 categories of diet to represent that: carnivores, omnivores

    • Each entity in the level that spawned would be one of the categories, and the food that they dropped when killed would correspond to either a carnivore or herbivore's diet (omnivores were able to eat anything)

      • I realized I needed a quick way to balance the drop rates of each enemy so each enemy type would be equally capable of eating & attacking, and created a generic "Taxonomy" datatype as a scriptable object for each individual enemy

        ree

      • After that, I began the attack and health values, along with the rates of hunger & aggression per style of enemy; in this case, their size and type (animalistic vs. humanoid)

        • I created a separate "Archetype" datatype to store the various stats, along with a list of Taxonomies that fell within each Archetype for the sake of spawning

          ree

    • Once I had a modular method to quickly create new enemies and modify their stats on a whim, I then moved onto creating a formula for the existing utility values and the available behaviors they mapped to on an enemy


      • First, each enemy evaluates it's own hp as a ratio with its maximum in relation to every other non-ally's hp in order to determine a threat value from -1 - 1; food items are evaluated with a scale of 0 - 1 based on how enticing they are to the enemy; these individual values are multiplied by the inverse of their distance to the enemy in question


        • When the hunger meter reaches an arbitrary threshold, or (in the case that both hunger an aggression are above the threshold) when the difference between hunger and aggression is above a certain value:

          • The enemy will scan the room for food items compatible with its diet, arranging them by how enticing they are, and then move based on the items' distance vectors

            ree
            ree

          • In the case there is no food, the enemy's aggression is increased instead; when aggression is high enough, other 'sentient' entities can then be considered food and evaluated as such

        • Otherwise, as long as the aggression value is above the threshold:

          • The enemy will scan the room for enemies (and the player) whose drops match its diet type, arranging them by the threat they pose to said enemy, before moving based off of the average of the distance vectors

            ree
            ree

        • When neither threshold is reached, the enemy will idle in a small circular area around it, and have their movement vector dictated by a random point inside the circle

      • These methods allowed the AI to pathfind to 'targets' without needing a navmesh- granted there is the drawback that entities can clip on objects in the center of the room, but I believe this could be resolved by giving objects a 'negative reinforcement' vector to be considered when moving, causing enemies to gravitate away from them when too close

    • After the AI was complete, I created a character controller for the player, giving them movement, attacks, and a health an stamina bar that need to be refilled through eating food (that can only be obtained through killing enemies)

      • This resulted in the player needing to attack enemies to continue eating before their stamina went down, resulting in an eventual domino chain of emergent behaviors that led not just to the player and enemies fighting, but causing enemies to team up against others in an almost pack-tactics manner

    • Finally, I commissioned a friend to create art for the player to remove the placeholder visuals on the random enemy spawns and some basic props to fill the test level, along with UI elements to visualize the stats and decision making from the AI during runtime


Duration

4 Months

  • This project was worked on gradually throughout the last semester of our technical design class, and progress was gradually made as we learned new concepts and systems per lecture

    • I had initially come up with the hunger and aggression systems for the enemy AI prior to having learned about utility systems, and found them be greatly compatible with my design plans, leading me to initially scrap the first code framework I had

      • Before then, I was struggling with how to implement my design ideas as an HSFM, as the states and transitions felt too rigid for the rapid world state changes, requiring-hard coded tripwires to choose whether to eat or attack and then how to eat and attack

    • As time passed, we were required to showcase our progress, with expectations to incorporate the feedback received afterwards into our work for the subsequent showcase

      • While I was told the initial idea was incredibly ambitious based off of my design ideas, I decided to continue onward as I believed this implementation would be a suitable challenge for my technical skills


Lessons Learned

  • Throughout this semester, and during the course of this project, I learned about various AI types, starting from behavior trees and ending with genetic algorithms

    • During my time scripting the utility system, I became incredibly familiar the various aspects that go into making a cohesive utility AI:

      • Opting for the proper exponential and logarithmic curves for calculating individual utility values

      • Combining and calculating intermediary decision factors for better nuance as opposed to tripwires and thresholds

      • Creating inertia to prevent the AI from rapidly oscillating between two options with similar utility values

  • From the design standpoint, the initial concepting for DunMesh-ish involved examining non-game media and figuring out what can be turned into systems and how aspects of it can be gamified

    • Questions arose during brainstorming such as:

      • "How do I represent the self-balancing aspects of wildlife populations (or rather, dungeon populations) in nature?"

      • "How can I break down the decision-making process of less-intelligent creatures into an expression of a few quantifiable variables?"

      • "How can the themes of eating and food preparation be expressed in the form of easy-to-pick up gameplay mechanics?"

 
 
 

Comentários


Leave a Message

Have questions, feedback, or want to talk in general? Feel free to reach out to me using the contact form below- I'm always eager to hear from other devs in the industry!

Find me on:

bottom of page