Final Report (Quest System)
From EQUIS Lab Wiki
The Quest System
Xiao Feng Qiu (Eric) Jeffrey Yim
April 12, 2007
Contents |
Objectives and Motivation
Objective
The goal of our project was to create a generic quest system that provides the user with a variety of tasks to perform. These tasks will give the game a sense of purpose as the user is given specific goals to accomplish.
From the user’s perspective they will be given tasks such as collecting items scattered throughout the environment, collecting a certain number of resources, or traveling to a particular location. By completing these tasks the user will be rewarded with new structures to add to their growing village.
The software engineering goal in this project was to create a framework within the system that could manage multiple types of quests. In addition, the quest system must be flexible enough to handle the addition of new quest types in the future.
Motivation
In its original state, the game lacked a sense of purpose. Its open ended nature and lack of activities to partake in left the user confused as to what they should do. By providing goals and rewards we hope to make the game a more enjoyable world to play in.
User Interface
From the user’s perspective, a few changes have been made to the actual interface other than being able to participate in new quest activities.
HUD
The user interface has been slightly modified to accommodate new messages that are unique to the quest system. In the upper left corner an additional box has been added to inform users of the status of their current quest. It will state their primary objective as well as how far along they are on a quest. For instance in a collect quest, a message will display that they need to collect a certain number of items. Another message will display how many items they have collected so far.
Notification
When users approach villagers, a dialog box will pop up with varying messages depending on whether or not they are currently in a quest. Some villagers will merely introduce themselves, while one in particular will provide quests to the user.
A number of arrows have also been introduced in the game as pointers to areas of interest when participating in a quest. Green arrows will point to things such as the destination of a location quest, resource nodes in a resource quest, and items to collect in a collect quest. Similarly a red arrow will always point to the location of the quest giver.
Upon completing a quest, a pop up message will inform the user they have finished their quest and they may talk to a villager to receive a new quest. Moreover, it will disappear automatically after a period of time, or by approaching a villager.
Art Requirements
A large number of new models have been incorporated in to the game with the addition of the quest system. New models such as tires, elephants, umbrellas and others have been included as collectable items for the collect quest. A flag model is used to mark off locations in location quests and an arrow model is used as a compass to point the user in the direction of items of interest. These models are loaded in at run time and may be changed without modifying the code. Additional models may be added to the game without touching the quest code. By adding information about new objects into a building file, they will automatically appear in the game during a collect quest.
Technology
The three main components in the creation of the quest system were the Quest Manager, Quest Triggers, and Quest Giver.
Quest Manager
The purpose of the Quest Manager is to have a central location where all quest related functions would be called. The Quest Manager must handle a wide variety of quest related activities such as:
- Initializing and deleting quests and their triggers
- Maintaining quest messages
- Providing functions used by all quests
- Storing variables shared by various triggers
- Providing all the necessary accessor and mutator methods required to access the variables.
In the original game, no class existed that could be suitably modified to accommodate the needs of a quest system. As a result, an entirely new class, the CAXQuestManager, was created. In addition to the above functionality, there are a few key additions to the Quest Manager that we would like to point out. The first of which is the random quest generator.
Random Quest Generator
The Quest Manager contains method that will randomly generate new quests based on existing quest types. The majority of quests require numerical data. As a result this data is generated at random by utilizing the random number generator in Ogre. Theoretically an infinite number of quests can be generated with these functions.
Once a quest is complete, the difficulty of the game is increased. Difficulty in the game is represented by an increase in the distance needed to travel to collect items or to reach a particular destination during a location quest. This was achieved through the use of a few variables accessible by all members of the Quest Manager class. Upon completing a quest, an increaseDifficulty method was used to increase the value of these integer variables by a random amount. These distance variables were then used in collect and location quests to determine how far a user must travel to finish their quest.
Delete Item
In an earlier version of the code, the collect quest required the system to change game states before an item could be deleted. The rational was we were mimicking the behavior of the existing resource system. With the resource system, as soon as the user walks within the proximity of a resource node and presses a key to activate it, the game switches from the standard game state to what is known as the action game state.
Since collecting an item followed a similar course of action, we created a collect in game state. This worked well at the time however it introduced a great deal of complexity to the code. Numerous methods would need to be duplicated and it appeared as though game state priorities were conflicting. Worst of all odd bugs would crop up seemingly at random when testing the collect quest code.
As a result, the method of collecting items was greatly simplified. Instead of pressing a key to collect an item, users simply need to walk within some range of the item. With this simple change, the complexity of the code was drastically reduced from large sections of repeated code in the game state class, into a single method in the Quest Manager class. In addition, a special visual effect was added. As the player approaches an item, the item’s proximity will trigger fire. At this point, the item moves towards the player while shrinking. This gives the illusion that the item was “sucked†into the player, indicating the item was collected.
Once the item is sufficiently small, the delete item function is called. Within this function, code exists to gracefully delete the trigger, item, and anything else needed to remove its existence from the game.
Compasses
An ongoing issue with the original version of Life is a Village is that it was difficult to find things in the world. Although there was not much to do in the world, users at least had the ability to harvest resources. However even that was an issue as new users had no idea where the resource nodes were located.
While developing the quests, this issue became increasingly apparent as it was difficult to complete collect and location quests that were far away. Items were either hidden behind mountains or too far away to see. As a result, a compass was created to point the user in the right direction. This compass will rotate around the avatar within a certain radius. As the user walks around, the compasses will dynamically rotate and point to the item, location, or resource nodes of interest.
In the mean time, the compass’ arrow will enlarge or shrink to some extent based on the distance between the user and the item. Therefore no matter how far or hidden an item is, the user can rely on the compass to point them in the right direction. Since the compass is a function utilized by all the quests, its initialization, update, and deletion functions are located within the Quest Manager.
Quest Triggers
Generally, triggers are condition checkers that are evaluated every frame. If a specified condition is true, a user specified method is called. This fits perfectly with the concept of quests. Quests are essentially a set of conditions that users must fulfill before they can be considered complete. To provide the functionality required by specific quests, and to evaluate the status of each quest, various types of triggers were created.
To accommodate the creation of quest triggers, the CAXTrigger class was modified with the addition of four new trigger types.
The CAXTrigger class contains code for proximity triggers as well as timer triggers. Each trigger has two phases, an initialization phase, and an evaluation phase.
During the initialization phase, the trigger is initialized with variables and the game world is changed to allow for the proper evaluation of a quest. For instance in a Collect Quest, internal variables such as the number of objects to collect are saved, and the world is changed by adding objects in the world to collect.
Once a quest trigger is created, the system no longer needs to keep track of the status of each quest. The reason is each trigger is self monitoring. This is possible through the evaluation phase. In this phase, each trigger is evaluated every frame to see if its conditions are met. Once they are met, a specified method is fired and the trigger is deleted. For instance, proximity triggers continuously check to see if the avatar has stepped within their proximity. Once they do, the trigger is fired and some user specified method is called.
Every quest is different. Each quest has its own unique properties and goals to satisfy. Therefore multiple triggers specifically tailored to each quest were added to the CAXTrigger class. We have added four triggers to the CAXTrigger class:
- ResourceQuest Trigger
- CollectQuest Trigger
- ItemTrigger
- LocationQuest Trigger
ResourceQuest Trigger
When this trigger is created, the number and types of resources that need to be collected are stored. This trigger will fire once the user has collected enough of each type of resource. This trigger is directly tied in with the existing resource system. Therefore in the future, if new resources are added, they can also be added as resources for this quest. Depending on the number and type of resources required for the quest, a number of compasses will be created to point the user to the nearest resources.
CollectQuest Trigger
Upon initialization, the collect quest trigger will randomly place objects within a radius of a given location. Each object has a corresponding item trigger which will cause the user to collect the item when they step close to it. When the user is within the range of an object, the object and trigger are destroyed and an internal counter increases. Once the counter reaches the goal amount, the collect quest trigger fires and the quest is complete.
ItemTrigger
The purpose of the item trigger is to provide a method to pick up items that are distributed in a collect quest. The Item Trigger is essentially a heavily modified proximity trigger. It is initialized with the location of the item, the range at which the item will be picked up, a compass to point to this particular item, and a pointer to its parent collect quest trigger. Once the user steps within a specified range, the item is deleted and a counter within the parent collect quest trigger will be incremented.
LocationQuest Trigger
In the future, as new quest types are added, users may be required to travel to a particular location before their actual quest begins. Therefore the purpose of the location quest is to mark off points of interest in the game. The trigger is initialized with the destination and range that the user must step in before the quest is considered complete. A flag model is used to visually mark off the location to which the user must travel. Additionally, a compass object will point the user in the direction of the flag.
Quest Giver
Generally, quests are usually issued from some non-playable character (NPC) who can interact with the user. Their jobs include issuing the quest, judging when the user finishes the quest, providing essential information to help the user accomplish the quest, and providing a reward for finishing the quest.
Therefore, a special villager named "Ami" was picked as the quest giver. In the original version of LIAV all villagers are exactly the same. Therefore a new name attribute was added to VillagerData to distinguish between villagers. After names are assigned by the loading order of villagers in the VillagerManager class, Ami is picked as the quest giver. In the future, character quests can be developed based on this name attribute.
A compass is also created for Ami. As a result, the user will always know where she is located. Additional methods are implemented to search for specific villagers by name or location. This is primarily used by the villager compass; however it may be useful for future quests.
Another important change is the addition of proximity triggers to allow for villager interaction. When the user approaches a villager, a large quest dialog will pop out. Right now, only the quest giver provides extra quest information in the dialog. All other villagers simply introduce themselves to the player.
QuestInGame state
When the player receives a quest, this can be thought of as changing from a state where they don’t have a quest to a state where they have one. As a result, a new game state was needed to represent this change within the system. As soon as the player walks within the proximity of a villager, the game switches from the standard game state to the new state. A quest dialog appears and its contents will dynamically change based on the status of the current quest. If the player has no quest, or has finished a quest, they can press a key to receive a new quest.
As a result, this new game state ensures that quests will be issued one at a time. It also handles displaying the proper message based on the state of the quest, and dealing with user input when initializing a random quest. This means the user has the freedom to reject a quest if they do not want one. In the future, this can be further enhanced by generating a new random quest every time the user declines the provided quest.
Timer Trigger Utilization
The final change is the proper utilization of a reusable timer trigger. When a quest is complete, a timer trigger is activated to display a message on screen for a certain period of time. At the end of this time period, the trigger is turned off and the message is hidden. Alternatively, the user may speak to a villager and the message will disappear.
Software Engineering
The following is a brief summary of the changes we have made to the system from a software engineering standpoint.
QuestManager class
A multipurpose class was required to handle all quest related activities. Such a class didn’t exist before therefore a new one had to be created. All quests are initialized and deleted here. Additionally, a multitude of quest related functions are made available. By placing all quest related functions in one Quest Manager, future developers have a centralized base for developing future quests.
Quest Triggers
Each quest was designed to be a self contained unit. This means that once a quest is initialized, the system doesn’t have to worry about it, and the trigger will monitor itself. The existing triggers fit the quest concept perfectly. Therefore triggers were used to represent quests in the game. New quests can be easily added by creating entirely new quest triggers, or using existing triggers.
VillagerManager
A requirement for the quest system was to differentiate villagers so that only one of them could provide quests to the user. By adding a name attribute to the villagers, each villager became unique. As a result, regardless of what type of mesh villagers are loaded with, they may be assigned names to differentiate between them. Moreover, villagers can be found by their names or locations, which is useful for the future development of the game.
QuestDialog class
In the original version, there was no dialog system in place, so users could not converse with villagers in the game. Based on our requirements, villagers would need to interact with users to provide information about quests. As a result, some sort of dialog system needed to be created. Therefore a new QuestDialog class was created to handle displaying and updating information to the user when they speak to villagers. Due to the dynamic nature of displaying messages in the Quest Dialog, adding new information is a straightforward task.
Interaction with other Groups
The crafting system we initially proposed was indirectly handled by the group working on “Building a backbone for Building.†Their project handled the creation of an elegant building menu to choose which buildings to place in the game. By successful completing a number of our quests, new buildings would be unlocked, and would therefore be build-able in the game.
Future Work
The quest system was designed from the ground up to allow future quests to be added. With more time we would have liked to include a wider variety of interesting quests. Currently quests are comprised of a single activity. Once this activity is completed, the quest is complete. In the future, quests may have multiple components to them. For instance the user must first go to a certain location, collect a number of items, then bring them to a blacksmith to forge shoes that the user may wear to increase their walking speed.
The QuestInGame state should be eliminated to simplify the quest system. Ideally it would reside within the quest manager in the form of various functions. However, the quest giver was a special case where the compass and trigger were never deleted. As a result we could not integrate the QuestInGame state and QuestManager in time. In the future, the quest giver concept may be modified into a character quest. Instead of starting the game with a compass pointing to the quest giver, we can enable the compass only when a quest requires one.
Villagers can now be provided with different names after their meshes have been loaded. However, we could not load other villager meshes because they were missing key animations. In the future, when more villager models and skeletons are introduced, various villagers could be added to add variety to the game.
Lessons Learned
In an earlier version of the code, a new game state named QuestInGame was created to handle the quest issuing system. However there were issues with these new game states. As a result, we decided to remove it and recreate it in the form of a trigger. Unfortunately, this was not feasible. The reason is that the villager’s trigger and compass remain in the game at all times. This differs from other quest triggers which are normally deleted once the quest is complete. Therefore, we returned to the initial implementation of using different game states.
Overall our experience working with LIAV was a success. For the most part, all of the major features we set out to complete have been completed. That being said, the first couple months of coding were difficult. None of us were familiar with the code and beginning from scratch was a daunting task. Additionally, understanding how to use the triggers within the game properly was an ongoing issue up until the final days of coding.
Since triggers played such a large part in the creation of the quest system, it was especially discouraging to discover how difficult they were to use correctly. Very often they would crash the game without the slightest hint of what went wrong. Debugging was immensely difficult as triggers were evaluated every frame, and the error messages provided by Visual Studio C++ were of little help. However, after figuring out how to use them correctly, we realized triggers were extremely powerful and useful.
As an example, we needed a timer trigger to properly display messages for a specified period of time. However there was no successful initialization of the timer trigger we could use from the original code. As a result, it was only in the final weeks of development that we finally understood how to properly initialize the basic timer trigger.
Although adding in the first set of features took longer than expected, development accelerated as we became more accustomed to the code. Since we developed sections of the quest system separately, the eventual integration of our code was difficult. As much as we monitored our changes, conflicts still occurred within the code. We learned a great deal from this project and overall this was an extremely challenging and satisfying project to work on.