4 Hugues Ross - Blog
Hugues Ross

12/6/15

Halberd Update 3: Scaffolding

It has been a long time since the last update post, and I've been quite busy!
I've been wanting to write this post for a while, but kept thinking "one more feature", and now 3 months have passed and I still don't have a post, so I'm going to just do a general writeup of what I've been up to. Hopefully I can get back into the posting rhythm by getting this out of the way.

Refactoring

 I realize that not everyone is as excited by the prospect of refactoring as I am, but it has really done this project a lot of good. The main focus of my latest batch of refactoring was to make Halberd's codebase more modular. Before, I had a single set of code that was sort-of broken up, but not well. Now, I've broken things up into 5 discrete segments, each with a very specific purpose. They can be arranged in a hierarchy as follows:
  • Engine
    • Editor backend (The base editor functionality, with no user interface)
    • Game backend (The base game functionality, with specifics like context creation and hardware input handling missing)
    • Common library (A library containing all the code that's shared between both the editor and the game)
  • Interface
    • Editor interface (The actual editor front-end)
    • Game interface (The executable that the user runs)
The code is still all in C, with the exception of the editor interface which is written in Vala. There's a good reason, of course...

UI Overhaul

...And that reason is the UI.
I tried writing a GTK-based UI in C, and although it worked it was annoying and clunky to write. I decided that since Vala translates to GObject C anyway, it would be easier just to write bindings for the backends than to continue working on this specific part in C.
The startup dialog and project creation workflow are probably the most finished parts, so far.
So far, things have been going well. I've made GTK UIs with Vala a few times now, but this time I'm trying to pay more attention to the details and make things look just a tiny bit nicer.
At this point, the majority of the UI elements above function as intended.
The editor interface is starting to look more like an actual game engine, which is great! This is my first time making an editor with this sort of scope, but I'm pretty happy with my progress so far.

Projects 

Apart from the UI, the other main improvement to the game is the addition of projects. The engine now has actual projects that separate different games being made. This is useful for a lot of obvious reasons, so I won't go too far into it. Overall, though, this will make things much easier for me once I actually start making games with it.

Up Ahead

Even with all that I've done, there's always plenty more. My next goals are as follows, in order:
  • Finish project support, at least as far as file handling goes. This means creating, importing, renaming, moving, opening, and deleting assets from the bottom pane. You can currently open and import these files, but that's about it. I'd also like to get the search feature working.
  • Re-add support for tilesets. Right now, the editor is using garbage data left behind by the graphics card as a temporary stopgap, but I want to be able to use actual tilesets again, as well as tie tilesets to specific maps.
  • Start adding more game/engine features. The only thing you can really do right now is walk around (You can interact with NPCs, but they can't be spawned in yet), and I have quite a bit that needs adding before I can call this a real RPG engine.

12/1/15

Capstone - A Postmortem for Dungeon Restocker

It has been 2 weeks since my last update post, and the verdict is in: Dungeon Restocker isn't going forward to next semester. This is being posted later than expected, because it has taken a long time to write and consider. To be perfectly honest, I'm a bit relieved. Despite the sunny disposition of most of my posts, this project has been fraught with issues, and I'm not sure I could take a second semester of this.

The Project

I think I'm going to kick things off with our meeting schedule. I've always been a supporter of short, infrequent meetings that serve to make sure that the team is still on track. Sadly, this was not the case here.
Our meeting schedule consisted of in-person meetings on Tuesdays, Wednesdays, Fridays, and Sundays every week. Each individual meeting lasted between 1 1/2 and 2 hours, resulting in the team spending about 6 hours a week on meetings. Note that while we did meet on Wednesdays in class, this was not the Official Wednesday Meeting. That one was about 5 hours later. By the way, I live about 15-45 minutes from campus, so I spent an additional 3 hours a week in transit because of these meetings, tallying up to a personal total of 9 hours a week spent on these things (although, only 6 were logged).
On the day that our team was cut, we spent almost 2 hours in an online meeting, in which we made the decision to have a meeting the next day in order to make the actual decisions we wanted to. This meeting lasted 3 1/2 hours. That's right, we spent 5 1/2 hours over the course of 2 days making decisions that didn't actually matter. I think this little anecdote best describes our team's decision-making process.

Next I should probably address the game itself. Dungeon Restocker's design underwent numerous changes as we worked on it, and I don't think all of them were good. I think the main problem with the design can be summed up simply: There are two games in this concept that oppose each other. My view of how the game ought to function differed from the rest of the team, and I had difficulty seeing the value in their concept. To some degree, this is my own problem, but I'll try to address each evenhandedly so that you can decide for yourself which direction is more engaging.

Concept 1

The team's concept is that of a slow, methodical game. The player acts as a sort of hidden caretaker for the Hero, taking time to prepare the 'perfect experience', and then watching and waiting as the hero goes through it. An infinite number of heroes pass through the dungeon, but there's a long break between the exit of one and the arrival of the next. To end the stage, the player must complete certain objectives, usually making the heroes do a cumulative X of Y (e.g. loot 20 chests, kill 10 monsters, etc). Hero emotion is treated more vaguely, instead relaying various needs to the player (This hero wants loot! This hero wants to fight things!). Making heroes happy is a secondary objective, which rewards the player with resources.

Concept 2

My concept is a fast-paced, frantic juggling act. The player must constantly sneak around behind the hero's back, resetting traps, closing doors, and refilling chests without being noticed. A limited number of heroes enter the dungeon at timed intervals, such that in a perfect game the previous hero exits just as the next enters. To be successful, the player must prepare while a hero is exploring, while making sure that the current hero doesn't start messing with the old preparations. Hero emotions are given freely, along with the addition of a suspicion/confusion meter that fills if things are out of place. Screw up a hero too much, and they'll start moving and acting more erratically, trashing your dungeon, and ultimately leaving. On the way out, they'll confuse the new arrivals, resulting in a entertaining spiral of destruction where a loss is its' own reward (This is admittedly inspired by the classic Dwarf Fortess tantrum spiral). Making the heroes happy is a secondary objective, but is necessary to keep the show running smoothly.

Who's right? Beats me, but our current concept (closer to 1) was unable to properly convey the hero's needs and emotions, which is what ultimately resulted in our failure.
As a gamer, I find concepts like #2 to be more fun and engaging (Hence the chaos that was Sports Game), but it's entirely possible that the rest of my team was correct here. The main thing that bothers me is that I felt like I was left out of the decision-making process. I could speak, but since the rest of my team had all silently agreed on #1, my input would never be used or even acknowledged.

The Team

 I'm just going to go down the list when talking about my team. Note that I refuse to name any  names here--My teachers will know them already, and I see no reason to call anyone out publicly for anything they did or didn't do.

I should also stress that while I may come off as being rather negative, I really do like these people. Many of them had to deal with external issues, which affected their work, and I don't think they were able to give their all as a result.

Producer

My producer is the easiest to criticize here. On a 4-person team with 504 hours logged, he has logged 50, and never reached the weekly minimum hours on even a single week. He was absent from the majority of our meetings, and pretty much never did anything apart from the occasional piece of documentation. I understand that he was having issues with other classes and his job, but logging 10% of a 4-person team's hours is rather unacceptable.
I think a team can live or die by it's producer, and this one certainly made things difficult for us.

Programmer

If I had to pick phrase to describe my counterpart on this project, it would have to be "too little, too late". He did good work, but it was always a couple of days later than I expected to see it, and less complete than I'd hoped. If that was all, I wouldn't mind, but he did a rather poor job of communicating in general.
He was prone to disappearing for days at a time, without any warning, and failing to respond to any communications. One time he lost his phone, and inexplicably wouldn't just check the team Slack on his computer like I did for the entire semester. Another time, he was having family issues right before the day of a major presentation, and failed to even send a text (he had his phone this time) informing us of his absence. Instead, we were forced to present with him effectively MIA, only to find out where he'd been that evening.

Designer

 I'm somewhat torn about our designer, because most of the issues I've had with him relate more to the way he acts, and it's hard for me to tell if what he's doing is intentional or just a simple miscommunication. If we had a more active producer, they probably would have had plenty of opportunities to mediate and 'translate' for us, but as it stands I've just put up with his behavior and tried to ignore anything that feels odd or out of place.
Another reading of his actions, the one that I've been considering more lately, is that he simply takes input and criticism poorly. Every time I try to suggest something or point out a potential stumbling block, he immediately gets to work ripping apart what I just said or otherwise attacking my proposals. I initially figured it might have to do with my point above about there being 2 opposing designs, but then I realized that he'd been doing this since long before we decided to go with Dungeon Restocker in the first place. I might even be as bold as to suggest that he's been doing this from the very beginning.
I don't think it's appropriate to discuss any specific instances of this, given that this is such a personal thing and I'm not even sure if he was doing this on purpose, but I'm prepared to give specific examples if asked.

The other issue, which I will go into more detail with, pertains to this old post, or at least the aftermath of it. After superseding me as the team's de facto artist, he proceeded to achieve...nothing. No assets, no concepts, and no documentation. Over the course of almost a month, he modeled and rigged the character base. He finished it a day or two before our final presentation (with no animation), and it never made it into the game. I would be more understanding, but the last time I checked he'd put in close to 50 hours on it. Those 50 hours could've been much better spent, given the state of our game, not to mention that I found out later that he'd never done this before. What this means is that, having made fully rigged and animated humanoid models before, I would've been a significantly better choice for team artist, at least in this instance. I'm over it at this point, but I wasn't happy finding that out at the time. Worse yet, he also proceeded to completely ignore the standards that I set in our art bible when designing our proper level, but also failed to alter any of our art documentation in the process. Rather than talk to the team and make changes to the art direction, he simply chose to ignore it.
At the time this all bothered me quite a bit, given how abruptly and confidently he took on the position.

Me

I'm certainly far from blameless. For instance, most of the issues that I've had with our designer and other programmer are caused by my own perception, and it's quite possible that the others disagree. I also held on to my beliefs about what was best for the game, despite the fact that my team felt otherwise. All of these factors made it difficult for me to get excited about the project. I put in exactly the amount of effort I needed for a decent grade, and no more, because I any passion I had was suppressed by the situation I was in.

Even now, I feel like I'm betraying my team by writing this, but I need to get these feelings off of my chest once and for all. I can only hope that the others felt the same, because I'm not sure I could face them if they read this.

Conclusion

After reading this post over, I really don't know what to think. This is certainly the worst, most negative and venom-filled postmortem that I've ever written, and I don't know if it should be. A few times this semester, I considered the possibility that I might be the problem with this team. Either way, I hope this little insight into a failed project is useful; I think I've learned quite a bit from this myself. Hopefully, next semester will be better. I've found a team that wants me on board, and with any luck that'll work out nicely.

11/16/15

Capstone Update 9: "Who's that handsome fellow?"

We failed our challenge attempt last week, but that's ok. We got quite a bit of good feedback from the attempt, and have been hard at work making sure that everything works nicely. With any luck, our next and final attempt will go much better!

This week, I mostly made a bunch of small boring changes, but I also took out most of my todo list of 'big' items as well. The most interesting thing I did was laying the groundwork for multi-hero maps, something that we've been wanting for a while. You may be surprised to hear this, but the simple inclusion of one or two extra heroes actually increases the game's depth substantially.

When you have a single hero present, the game is fairly straightforward. Make sure that the chests have loot and each room has an enemy or two, and then you're golden. You can go hide in a backstage room and wait for the level to end, if you'd like. When you know that #2's on the way, however, you start to run into an interesting problem. Not only do you need to 'restock' in the wake of the previous hero (Without getting caught), you also need to make sure that the previous hero doesn't start touching the freshly placed items and enemies. On top of that, you know that there's a timer counting down until a second hero shows up, making sneaking around even tougher. My other big addition this week was finishing doors off, so now you also have to tail your hero to catch the door on their way out. Doors can also provide cover, in case you suddenly feel the need to become invisible.

This semester is coming to a close, but we still have about a week's worth of dev time left before the cut, so I have one last addition to make: Frustration. Normally, frustration is something that you should avoid in games, but ours is for heroes only.

Imagine, for a moment, that you are a valiant warrior entering a Completely Ordinary and Totally Legit Dungeon Seriously of Course it's Real Why Would You Even Ask, and you begin to explore. In your travels, you come across a door that's locked up tight. You keep searching, in hopes that a key will turn up somewhere. You search, and search, until you come to a startling conclusion: You've been everywhere except past the door, and there's definitely no key. "That can't be right!", you exclaim, and begin frantically running back and searching all of the old rooms, but there's nothing. Also, there's this weird guy that looks exactly like you and this other dude who keeps darting around corners and giggling. It's complicated.

At this point, I'd be getting pretty ticked off. The above blurb is an embellished description of how I want to see 'stuck' heroes act. When the hero cannot progress at all, they'll start to randomly wander while slowly picking up the pace. A frustration meter will begin filling up, and once it's full their happiness meter will begin to rapidly drain. Once that's over, they'll pack up and head back the way they came, right out of the dungeon.

What happens if you don't let them out? Do they start trashing the place? Who knows. I think that question is best left for another time.

11/9/15

Capstone Update 8: A real adventure

Note: If you somehow managed to get here in a mad search for information on writing EQS Tests in Unreal 4, there's a complete sample towards the bottom of the page.

My last post was a bit of a downer, but worry not! I have yet another dense AI post ready this time around. Today, I'm going to discuss how I'm making the Hero AI more intelligent and interesting to deal with.

The Problem

After a recent round of testing, our team identified a problem: The Hero in our game was predictable to the point of being boring. At the time, the Hero AI would follow a set path to the level exit, only leaving the path to kill enemies and grab loot. No matter where he ended up, and where the player led them, the Hero would always just make a beeline for the end of the level.
Players didn't like this, because it made the Hero feel very robotic. We wanted our Hero to be more interesting, so we decided to try and make the Hero explore levels properly. However, we had a number of smaller issues to work past first:
  1. There is no easy way, to my knowledge, to store and keep track of where an AI actor has been in Unreal. There were lots of little "half solutions" proposed, such as making the Hero spawn zones that affected navigation periodically. None of these ideas really seemed great, though.
  2. As much as we wanted the Hero to be more independent, we also still wanted some level of control over them. The game wouldn't be very fun if the player had no idea where the hero was going, after all!

The Solution

We made several changes to the game to make our idea work. First off, we've decided to move back to a more modular way of building levels. We originally planned to build our levels out of pre-made rooms and corridors, but started moving to building levels out of smaller tile objects. However, if we make the rooms ahead of time then it's easier for us to use them as targets for the Hero.

In addition, I re-purposed the path node objects in our game. These nodes were used to make the hero move to the exit without hugging walls and corners, but I've given them a new function using our new 'Point of Interest' component. This component has 2 main uses: To give the Hero a list of places to explore, and to keep track of where the Hero has been. Path nodes have a trigger volume that marks the point of interest as visited once the Hero enters it. The useful part of this is that even if the hero just passes by the node on the way to another, the hero still 'knows' that it has been there already.

With a setup like this, the level only needs nodes near 'things', such as intersections, rooms, and other important objects, rather than having nodes lying around everywhere. As an additional safeguard, the Hero has a case to path directly to the level exit if it runs out of valid nodes.
The basic level geometry and pathing nodes of our test level.

Handling Distractions

So now we have a method of making the Hero wander around our level without doubling back too much. With this alone, though, the Hero won't do anything but walk around. We want to keep the Hero's current method of dealing with interesting things (that is, walking to them and dealing with them in a context-sensitive manner), but we also want to avoid the main problem: If the Hero picks a new target to walk to, it'll overwrite and erase the old one. To get around this, I broke the blackboard entry controlling the Hero's movement in two: a short-term target for 'distractions' like above, and a long-term target based on the Hero's goal of exploration. As long as the Hero's short-term target is set, the Hero will deal with that first. Needless to say, this all took a while to set up nicely. Here's a screenshot of the Hero's new behaviour tree:
The scary thing is that this will be growing soon...

Coolness

At this point,  we have a much smarter Hero than we started with. The only problem is that now that is so much more independent, we have no goods way to influence its decisions.
That's where the new coolness system comes in. Every Point of Interest on the map has a custom 'coolness' value that our designer can set. This value influences the POI's final score in our EQS test, which probably sounds like a huge load of gobbledygook if you've never done AI in Unreal before. Simply put, it's a heuristic that affects which targets the Hero is likely to select first when exploring.
To actually make this work, we needed to write a custom EQS test to use our coolness values. However, it turns out that there's a small problem with that:

To my knowledge, no one has made any resources on how to actually do it.

Ultimately, I ended up referring to the Unreal Engine source code to make our custom test, and that worked out alright. Still, to save any future coders who might find this the hassle, here is the entirety of our coolness EQS test (This test targets Unreal Engine 4.9.2, so keep that in mind if you're using an older/newer version):

NodeCoolnessTest.h:
    1 // Fill out your copyright notice in the Description page of Project Settings.
    2 
    3 #pragma once
    4 
    5 #include "EnvironmentQuery/EnvQueryTest.h"
    6 #include "NodeCoolnessTest.generated.h"
    7 
    8 UCLASS()
    9 class DUNGEONRESTOCKER_API UNodeCoolnessTest : public UEnvQueryTest
   10 {
   11     // Don't forget your constructor! (I did.)
   12  GENERATED_BODY()
   13     UNodeCoolnessTest(const FObjectInitializer& ObjectInitializer);
   14 
   15  virtual void RunTest(FEnvQueryInstance& QueryInstance) const override;
   16 
   17     // A property like this one will appear in the EQS editor's side pane
   18  UPROPERTY(EditDefaultsOnly)
   19  float MinCoolnessMod = 0.5f;
   20 };
NodeCoolnessTest.cpp:
    1 // Fill out your copyright notice in the Description page of Project Settings.
    2 
    3 #include "DungeonRestocker.h"
    4 #include "NodeCoolnessTest.h"
    5 #include "PoinOfInterestBase.h"
    6 #include "EnvironmentQuery/Items/EnvQueryItemType_ActorBase.h"
    7 
    8 UNodeCoolnessTest::UNodeCoolnessTest(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
    9 {
   10     // This line is important, because it determines what your test actually
   11     // takes as input. Without it, your test will never run.
   12     ValidItemType = UEnvQueryItemType_ActorBase::StaticClass();
   13 }
   14 
   15 // This function is the actual test itself
   16 void UNodeCoolnessTest::RunTest(FEnvQueryInstance& QueryInstance) const
   17 {
   18     // min/max code borrowed from the ue4 source. These return the thresholds
   19     // set in the EQS editor
   20     FloatValueMin.BindData(QueryInstance.Owner.Get(), QueryInstance.QueryID);
   21  float MinThresholdValue = FloatValueMin.GetValue();
   22  FloatValueMax.BindData(QueryInstance.Owner.Get(), QueryInstance.QueryID);
   23  float MaxThresholdValue = FloatValueMax.GetValue();
   24 
   25     // This for loop is ytour friend. It iterates through all of the objects
   26     // returned by the Evironment Query
   27     for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It)
   28     {
   29 
   30         UPoinOfInterestBase* cmp;
   31         
   32         // This is how you retrieve an AActor, but you can do the same for
   33         // other types of objects.
   34         AActor* act = QueryInstance.GetItemAsActor(It.GetIndex());
   35         if(act) {
   36             cmp = act->FindComponentByClass<UPoinOfInterestBase>();
   37             if(cmp) {
   38                 // This is how you actually score your objects.
   39                 // The 3rd argument should be replaced with any float value
   40                 // you have for scoring.
   41     It.SetScore(TestPurpose, FilterType, FMath::RandRange(cmp->CoolnessFactor * MinCoolnessMod, cmp->CoolnessFactor), MinThresholdValue, MaxThresholdValue);
   42             }
   43         }
   44     }
   45 }

What's Next?

This is a great start, but there's still plenty to be done before Our Beloved Hero becomes smart enough to be a really fun and challenging opponent. My first goal, which I've begun, is making the Hero smart enough to solve basic puzzle-like challenges. I'm working on getting it to grab keys to open doors, which will probably evolve into switch puzzles and the like later.
My other main issue with the Hero's new AI is that it still can't be guided by the player. You can put down a breadcrumb trail of items and enemies, and the Hero will follow it, but then it'll just turn around and go back to what it was doing. You can't lead it into a certain branch of the dungeon, then double back while it's distracted to fill out the rest, which I think would add a lot of fun to the game and help combat the problem of not knowing where it's headed.

Either way, I have a lot of work ahead of me!

11/1/15

Capstone Update 7: A Few Grains of Salt in an Overwise Perfectly Decent Milkshake

I have not discussed it on this blog, but I have been responsible for the vast majority of this game's art and art direction. So far, I've produced all of the art documentation for both Sports Game and Dungeon Restocker, including the artist statements and art bible, the the latter of which contains all of the guidelines and details of the art style the game will be taking. Because our team had no artist, I've stepped up to fulfill all of the project's art needs.

It is for this very reason that I feel a little bummed out right now. In what feels like just overnight, I have been rather casually swatted out of my position as artist in favor of my designer. I don't blame anyone for this, nor do I think this was particularly intentional. However, I still can't help but feel a bit used, after defining the game's art style. It's a weird situation all around.

I should stress again that I'm not placing blame or trying to stir up any issues. I know full well why this happened, and it makes sense. Now that Mike has gone and finished the major design work for the moment, he sees an opportunity to keep being useful while also increasing the amount of work being done to implement his design. In that sense, it's a perfectly logical move. It's also not the first time this has happened. I made a similar switch last year as well. The reason I felt better then, though, is that the designer in question was a talented artist with a charming and distinct style, whereas my designer honestly doesn't seem much better or worse than I am.

What am I going to do about it?

Absolutely nothing.
I'm going to go back to programming, and work more on implementing the game's core systems. I have other projects that I can do art for, and I have no intention of creating any unnecessary drama over a move like this. Whoever does our art now is probably getting replaced if/when we go forward to next semester anyway, so I'd be out soon either way.

It's still a little bit sad, though.