William Vennes
William Vennes
  • Home
  • Individual Projects
    • Beat Detection
    • Tile-based Level Editor
    • Viola
  • Team Projects
    • Scrapped
    • Robbin' Robin
  • Resume
  • About
  • Home
  • Individual Projects
    • Beat Detection
    • Tile-based Level Editor
    • Viola
  • Team Projects
    • Scrapped
    • Robbin' Robin
  • Resume
  • About

Picture
Bitbucket Repo
Game Download

Project Overview

Languages: C++
Development Time: 12 Weeks
  • Utilized custom C++ engine to create a data-driven roguelike RPG.
  • Used procedural content generation to create map generators for a cave, labyrinth, graveyard, and necromancer's lair.
  • Employed external XML data files to define attributes for map generators, monsters, factions, items, AI behaviors and save data.
  • Created a system for self-registering C++ code to allow for easy integration of new asset code.
  • Implemented A* pathing algorithm for enemy AI that takes movement properties into account.

Demonstration Video

Viola Demo Playthrough from William Vennes on Vimeo.


Gallery


Development and Strategy

Self-Registering Code / Factories

One of the goals of this project is to create an interface that allows classes to self-register themselves, so that developers do not have to edit the base game code in order to use new map generators, monsters, AI behaviors, and other elements. The system needs to allow developers to simply drop new files into designated folders, which the game loads up automatically without needing to #include the necessary headers.

This goal is achieved through the use of static registration maps for each type of class. Every child of the base class registers itself in the .cpp of that class using the registration map and a pointer to a construction function.
Picture
General diagram of the generator registration process.
Since the registration object lives in static memory, the construction function for the map is added at compile time, which means the game code only needs to search for the generator inside the registration map. Since the map contains BaseMapGenerator pointers, the base map generator needs an interface that all generators use through virtual functions. Similar systems exist for AI Behaviors, Items and NPCs.

NPCs and Factions

Enemies have predefined AI Behaviors associated with them, which are defined in external XML data files. AI Behaviors are written in C++ code and registered in a similar fashion to map generators. Enemies are generated in code by using NPC factories, which are generated through parsing the XML files in the NPC folder. These factories only create new NPCs, who act using their associated AI Behaviors. Enemies also have factions associated to them, which determines how friendly or unfriendly they are to enemies that belong to other factions. Attacks and deaths will affect the faction relationship for enemies that are near enough to see them happening. For example, if a player is friendly with guards, but keeps attacking guards, all guards near the player will turn on them and start attacking back.

A* and Movement Properties

Each cell has movement properties associated with them, and each NPC, as well as the player, has movement properties that are associated with the cells. For example, if the player is walking over lava cells, they should take some damage, or ghost NPCs should have the ability to walk on solid objects, and so on. These properties are taken into account when calculating the G-Cost for A* pathfinding. NPCs will try to avoid cells that they cannot traverse, but they are also willing to walk through cells that damage or slow them if going around them has a higher cost.

Postmortem

What Went Well

  • The data-driven architecture allowed for fast iteration and implementation of new gameplay objects, such as NPCs, items, and features.
  • Exploring methods of procedural content generation for generating maps and placing gameplay objects helped to gather ideas for new, custom maps generators.
  • Mixing different types of AI behaviors ​within NPCs created interesting NPC actions in terms of gameplay.
  • Bit-wise movement properties is a powerful parameter as a G-Cost for A*, as it gives NPCs different behaviors based on how they can traverse the map.

Challenges

  • Having to adopt the factory design pattern was difficult at first. It took some time to wrap my brain around how exactly the self-registering code should operate, and how to implement such a system.
  • There was difficulty finding a cost-effective way to handle NPC visibility and map lighting. I needed to find a method that collected map cells for visibility that was generic enough for all different agent types and was efficient.
  • There were difficulties with A* pathfinding and performance that came with trying to understand the algorithm itself.

What Was Learned

  • I learned how powerful the factory design pattern is, and how it can be useful for adding additional content to a game with minimal code adjustments.
  • There are additional parameters to consider for pathfinding beyond just the A* algorithm. Enemies need to know when to recalculate new paths, such as during combat or if their current path has expired. In addition, the pathfinding needs to take movement properties into account when generating the path.

Code Samples

  • BaseMapGenerator.hpp
  • BaseMapGenerator.cpp
  • Map.hpp
  • Map.cpp
<
>

Home

Individual Projects

Team Projects

Resume

About

Last Updated - 9/24/2019