Developer Diary Week 6 - Hobbies, Stat Widget Rework, Code Refactoring
- gtristanitristani
- Jun 29, 2023
- 7 min read
Once again, I've had a busy week in regard to this project, and have a lot of new updates to share with you all! This week, I wanted to primarily tackle hobbies. But as I had mentioned last week, the codebase was beginning to outgrow its organization. As a result, I had to spend additional time with reworking it, particularly in regard to state transitions, as well as the DialogSystem. As a final piece, I was starting to have trouble with reading the stats of NPCs over time due to the awkward positioning and font of their associated widgets. I'll discuss all of those in a bit more detail below!
Hobbies of course took up the bulk of my time this week! They have a few different features that I implemented, as listed below:
Villagers can randomly choose to do one of their hobbies when in an Idle state.
Performing hobbies requires them to go to an actor in the world, specified by the hobby in question.
When they reach the actor, they calculate a random period of time between the minimum hobby timestamp and maximum hobby timestamp, this being the time they want to perform the hobby.
Hobbies degrade energy at a specific configurable rate.
Hobbies improve positive moods and reduce negative moods at a specific rate.
Hobbies play different animations corresponding to the hobby in question.
If the villager gets tired and relaxes during the hobby, the hobby is canceled.
Hobbies have a delay (specified by a timestamp) that indicates how long they need to wait after ending a hobby to do the hobby again.
Villagers can have as many or as few hobbies as desired.

This week, I completed all of the tasks I outlined above to implement the hobby system! It is now ready for use and experimentation in the system. All the planned hobbies; jogging, gaming, exercise, gymnastics, dancing, swimming, guitar, and piano; are implemented, each with their own unique animation.
As you can see in the video above, you can talk to the villager while they are invoking the hobby, and they get right back to it after the conversation ends. The hobby is consistently improving their mood but degrading their energy while it is in progress. Currently, no real models are implemented besides the square, white “stages”, but the actors that they travel to for each hobby can be customized at the user’s will.
With all the code implemented, the steps used to actually add hobbies in simulation are as follows:

To start, the user can bring a BaseHobby object out of the content drawer and drop it in the world.

After that, they can customize the variable values within the new hobby object, specifically:
Name – The name of the hobby. This is used to indicate the animation to be played by the animation blueprint.
MinHobbyTime – This is a timestamp object specifying the minimum hour and minute amount that the hobby should be invoked by the villager.
MaxHobbyTime – This is a timestamp object specifying the maximum hour and minute amount that the hobby should be invoked by the villager.
HobbyDelay – This is a timestamp object specifying the time that the villager has to wait after the hobby has ended before they can invoke the hobby again.
AssociatedObject – This is an actor in the world that the villager must travel to before performing a hobby. An example is a pool actor for the swimming hobby.
At this point, the hobby can be added to the hobby arrays by any villagers in the world that should have it! I want to ensure that it is as easy to use and modify as possible, so would love to hear feedback about what features would make it more helpful or versatile.

I finished up a rework for the Animation Blueprint to allow several different specified hobby animations, determined by the hobby name passed in. For now, each state is transitioned into if the name matches the hobby in question and the state has changed to hobby. The state is transitioned out of as soon as the state is no longer hobby.

The Dialog System is a key element of my project, so I wanted to spend additional time making it as short and easy to understand as possible. Following my rework of it, it was down from 685 lines to 573 lines of code, with the same logic. At the same time, I was able to make it easier to work and experiment with by composing it of more modular functions. I added this modularity by implementing a few different features:
There are three Booleans implemented for each sub-bank search function: one specifies if the personality bank should be searched, one specifies if the job bank should be searched, one specifies whether or not this is the only sub-bank group being searched.
Sub-bank groups that correspond to a specific value (gifts, energy, moods, affinity) have pointers to each sub-bank in the group stored in an array.

Specifically, these changes mean that searching one sub-bank is incredibly easy to perform, as it can be done in a single function call, regardless of whether a Personality, Job, or both derived response is desired. By keeping arrays of addresses to the sub-banks in each group, it’s easier to add and remove sub-banks from the system depending on what is desired for the game, and less of the code depends on other unrelated parts. I think this will save a lot of time for me and other users going forward!
In regard to state changes, I wanted to continue revising system organization to promote fast prototyping and understanding by other users. I ended up taking some time to look back at what I have already and how I could improve it.

To start, I put each state’s primary behavior into its own separate function. Instead of using a switch statement, I put each function into a spot in a function pointer array, with its numeric index corresponding to the value of the state enum value in question.



Another change I wanted to make was ensuring that the villagers operated off of tick as little as possible, as there is the possibility of many being in a scene at a time. Instead, I moved towards the UpdateStatus function. UpdateStatus, is called with each in-game minute passed on every villager. It functions by getting rid of any active hobby data if the state has transitioned out of Hobby, then checking if there should be a mood improvement from resting and giving it if so. After that, it calls the function that represents behavior for the current state. Afterwards, the idle meander timer is cleared if the NPC is not Idle. Finally, energy values are updated for the NPC. With these changes, custom on-tick behavior on the NPCs is unnecessary to implement.
As mentioned, I managed to fix the stats widget component on the villagers into a much better place! Initially, the widget component was using screen space. This was because I wanted to avoid the bloom and glow effects that came off of the text and background when in world space. Additionally, it seemed to require a background, which I didn’t want. I wanted it to be transparent and appear as text next to the villagers. I don’t have much experience in working with materials, and was uncertain of what to do to fix this initially. However, I was beginning to have a lot of trouble reading the values and getting distracted by those for different villagers, so I knew I had to spend time looking into the problem this week.

To demonstrate the issue, this is from last week’s video. Not only is the text obscuring Viola; Lucy is nowhere near her and her stats are still appearing onscreen to the left!

In the end, this was a multistep process. I started by disabling the bloom effect on the player camera. This did help to a degree, but I saw that some blur still remained. Scouring through documentation for the font materials provided for widget components, I found out that each of them, instead of using a base color value, use an emissive color value. By swapping the input for emissive into base color, I was able to get rid of the glow!
Similarly, one of the materials provided, Widget3DPassThrough_Translucent, does get rid of the background when the blend mode is set to masked. With that changed and the widget set to use world instead of screen space, I ended up with a much more readable display!

Finally, I got done a few minor fixes and tweaks to the system:
The affinity is clamped between 0 and 100.
A random positive mood is decreased in addition to the negative mood boost after the NPC is hit.
NPCs get launched backward after being hit.
NPCs return to their hobby right after talking to the player while in the middle of a hobby.
NPCs will continue traveling to a hobby, even if interrupted en-route by the player.
While I got my major plans completed this week, I missed the opportunity to work on the proposed external file input system I wanted for specifying Dialogue Bank values. The hobby system and code rework took even more time than I had planned. But as a result of being so thorough, I’m confident in the stability of my project and that it will be even easier to iterate on moving forward. Next week, I have a lot of other things I’d like to work on!
To start, I plan to implement the sleep system, another method of regaining energy at the end of every day. This will make use of the already incorporated day/night cycle, and allow the villagers to adhere to an even more similar schedule to real people. The time that they go to sleep and wake up should be specified through their job. Along with this, they will have a reference to an actor representing their bed. They can be woken up by the player only by hitting them.
To further iterate on the hobby system, I want to allow a new method of player interaction: performing hobbies with the NPC! This will let the NPC gain affinity with the player at a configurable rate as they perform the hobby together. I will need to start this by spending time thinking on how to visually indicate this to the player.
The last major goal I have is letting NPCs initiate conversation, both with other NPCs and the player! I expect this will take a good degree of fine-tuning, so I want to be able to devote a good deal of time to it next week.
If I manage to have additional time after all of this, I would like to get back to the implementation of an external file input for the Dialogue Bank system.
Once again, I wanted to link the project on GitHub and invite anyone interested to experiment with it and provide feedback! It would mean a lot. Thank you for reading, and I hope you'll be looking forward to next week's update!
Project Link: https://github.com/grist-maker/NPCVillagers




