The Curse of Yuria Devlog


Art Design:

Unlike the previous demo, Tales of Legindrea--which changed its art style many times during the development process, the art style for this game has not changed.  However, the choice to lower the saturation of color and use bloom (among other post processing effects) did not happen until later in development.  The art assets used for this game mostly involve four different assets: Fantasy Heros: Character Editor and Fantasy Monsters Animated, both by Hippo.  The next one is 2D Platformer Tileset by Danil Chernyaev.  Lastly, the magic spells are from 118 Sprite Effects Bundle by BestGameKits.  All of the assets used are from the Unity Asset Store.

The Different Programming Components:


A component represents several pages of related code that achieve the same goal.  This game has 18 components, all of which total about 300 pages worth of code.  The components are Actor, Canvas, Controller States, Databases, Factories, Globals, Independent, Interactables, Interfaces, Items, ItemTypes, Level, Managers, Pathfinding, Quests, ScriptedSequencers, StatusEffects, and Targeters.

Actor governs the stats, status effects, names, and other identifiable characteristics of each enemy, NPC and party member that does NOT deal with movement.

In contrast, Pathfinding and Controller States deal exclusively with movement or things that are movement-related for each enemy, NPC and party member,  such climbing a ladder, jumping, running, following a target, etc.

Canvas deals with menus that the player interacts with.  Any window, floating text, icon, arrow or displays in the corner of the screen, fit into this category.  

Databases handle large (or sometimes small) quantities of static data that must be accessed during gameplay such as Item profiles.  These databases usually use scriptable objects and addressables as their backing. 

Factories are similar to databases.  They also handle (usually small) quantities of data.  But the data represents blueprints that can be instantiated in the game world repeatedly when needed such as damage popup prefabs.

Globals are simple classes (pages of code) and class templates which lists the properties and methods that other classes must implement.  These classes act as interfaces between different components.

Interfaces behave in a very similar way to Globals, except they are not classes but actual Interfaces denoted with the word "I" at the beginning of their name.

Independent manages any class that is completely decoupled from any other class with the exception of built in Unity libraries or downloaded assets.  An example of this could be a rotation script (class/ page of code) that rotates an object in the game world.  It only manipulates the game object and does not need to know about anything else.

Items extend from scriptable objects and handle all of the features that items have such as attack power, probability of inflicting status effects, item description, different "type" categories of identification, market value, modifiers (example: strength +2, defense +4), whether the item heals a target or damages a target, etc. 

Item Types are useful for any situation where the "type" of an item that has just been used or is about to be used needs to be verified.  For example, if a target has a status effect that inflicts extra (bonus) damage from all melee attacks, then an attack needs a "melee" type so it can be identified or not identified as such.  Types are also useful for knowing if an item ignores a target's defense or if an item replenishes HP (or MP) or inflicts damage instead.

Level manages the music and ambience of each scene in Unity.  It also details how the Main Menu, Game Over and "Initializer" scenes initially behave.  Lastly, it utilizes the parallax functionality of each 2D level.

Managers are globally accessible singletons that need to be accessed across multiple components.  This includes classes such as Inventory Manager, Save Manager, Audio Source Manager, etc.

Quests were never implemented in the game because a quest system was already demonstrated in a previous game called The Tales of Legindrea.

Scripted Sequencers deal with character dialogue and scripted movement in the game.  At current, it only controls NPC text in the game but contains the functionality and architecture to accomplish the aforementioned.

Status Effects extends scriptable objects to create a variety of effects unlike traditional status effects.  An effect can damage a target, heal a target, and can be activated once, activated periodically in random intervals, on a set timer, after a certain condition has been met, etc.  This component as well as all components were designed with scalability and modularity in mind.

Targeters also extend scriptable objects and are utilized by Enemy AI to determine which party member to target for an attack.  For example, an Enemy can target the closest party member or vice versa, the party member with the lowest defense, HP, etc. or vice versa.  

A Custom-Made Pathfinding Mechanic:




pathfinding is used by party members that follow the lead party member.  Unfortunately, Unity does not have a pathfinding system for a 2D side scroller (unless the enemies are flying).  This component took the longest amount of time to make.  At its core, it uses A* pathfinding.  However instead of a grid, it uses a node-based system as demonstrated in the pictures above.  A character travels from one blue circle (node) to another blue circle (node) via a yellow line.  That yellow line informs the object of "how" it should move between the two nodes.  Should it run, jump, climb, etc.  Pathfinding can be incredibly complicated and cumbersome.  But by restricting how AI can move via a node-based system, it helps substantially limit the amount of errors that can occur.

This is how it works.  Think of the yellow lines as a web that detects where an object of interest (target) is.  The AI can be programmed to follow that target.  To do this, the AI reads its position on the web and the position of the target on the web.  It then calculates the shortest path through the web to get to that target.

State Machine Design Pattern: Goal States And Action States:


Goal States and Action States work with the pathfinding component to create a variety of different movements and actions for enemies and party members.  Each Controller Unit (which can either be an enemy or party member) contains a goal state and an action state.  A goal state details the end result that the state is trying to achieve.  This usually involves "where" the controller unit is trying to go.  Is it following a moving target, is it being controlled by the player, is it patrolling the area, etc.  By contrast, an action state describes "how" the controller unit is achieving its goal.  Is it climbing a ladder, walking on a platform, jumping etc.

Action states that begin with an "Auto" denotation are controlled by the AI whereas action states with no denotation are controlled by the player.

Active Dimension Battle (ADB) System:


ADB was first introduced in Final Fantasy XII.  This battle system uses a gauge to determine when enemies and party members can attack.  Battles take place on the field (level) itself and are initiated by how close the player is to enemies.  This is the battle system chosen for this title.  A complex set of coroutines were used for the battle loop, and each command that is carried out while in battle.  In this way, the player can switch party members, move around, and access the menu during battle.  This also created many potential errors.  There was a constant need to check for null reference exceptions (among other checks) at certain pivotal points during the battle loop.

Command Design Pattern For Enemy And Boss AI:


Since all items are scriptable objects, they can be referenced as a potential skill (attack) in a monobehaviour attached to each enemy prefab.  This essentially uses the command design pattern.  In this way, multiple skills can be paired with each enemy prefab in a modular fashion.  The "Moves" category in the picture above shows this example.

Targeting System For Enemy And Boss AI:

Each enemy has one or more skills it can use in battle.  Each skill is also paired with a targeter ( which describes how the enemy chooses a target to use the skill on).  This further increases the reusability of both skills and targeters.

Scriptable Objects With Customizable Icons For Items:



This game contains over 600 different items (however not all are shown in the demo).  As a result, certain tools were developed to ease the management of large datasets.  One such tool is the ability to change the default, scriptable object icon to a picture of the item the scriptable object represents.  This helps programmers and other game developers distinguish between different items more easily.  The pictures above represent this tool.  The icons that are seen in these pictures are all scriptable objects.  You can also see the options in the inspector for the currently selected scriptable object.  These icons will also show in the object picker.

Automation Tools For Managing Items And Enemies:



Another tool useful for managing a large datasets is automation.  Instead of manually changing all 600 items, a large group of items, or all enemies, an automation system was created because it can accomplish the same task significantly faster.  You can also program the automation system to change certain characteristics of all items of a certain type.  The above pictures show the inspectors for the automation scripts used for this game.

Using The Flyweight Pattern For Items


Each scriptable object version of an item acts as a profile for that item.  The actual items that are seen in the game are game object instantiations that the player can interact with.  However, it saves memory to have all of the static data for each item encapsulated in a scriptable object.  This is why the scriptable object version of an item is separate from the game object version of that same item.  This pattern is known as the flyweight pattern and it decreases each item's memory footprint.  It is particularly useful when many instantiations of the same item are in a given scene in Unity.  The picture above is an example of item prefabs which can be dragged into a scene in Unity as game objects.

An Improved UI Component:


The last demo had a poor menu system.  Consequently an improved one was created using better visual design and a state machine design pattern.  For optimization purposes, many canvases were used (a unity script that usually handles groups of UI elements).  One such canvas handled all the menus: loading screen, main menu, options menu, equipment menu, command window, etc.  Only one of these menus can be open at a time via the state machine design pattern.  When one of these menus opened, it closed all other menus.  Each menu utilized the OnEnable() and OnDisable() built-in, Unity methods such that when a menu is either closed or opened, certain lines of code could immediately execute to ensure each menu is properly opened and closed.  The other canvases managed other UI components such as damage popup text or interaction arrows.

Files

The Curse of Yuria.exe 638 kB
57 days ago

Get The Curse of Yuria (Demo)

Leave a comment

Log in with itch.io to leave a comment.