Pages

Saturday, March 28, 2015

Introducing Thoth

An unfortunate rule of game development is that it's hard to really nail down the tools that will be best to make your game before you've actually dived in and started making it. In this case, our original dialogue tool was built to very closely parallel the mechanics of the dialogue system. This was great when we were first figuring out how to tie different conversations and branches together. However, because this dialogue tool focused on lines as atomic entities, managing long conversations in it was difficult. (For example, it was impossible to view more than one line at a time.)

These difficulties made following a conversation's flow in our dialogue tool difficult, and our non-programming writer found the tool overly complicated and confusing. So we decided to experiment with a new tool, Thoth.

With Thoth, our focus was to make the tool behave as much like a word processor as possible. You can start a new conversation and just type away. It's only when you want to add a branch or alternative line that you need to start using the buttons on the side. Additionally, even when you are using branching, it's easy to set what branch of the conversation you want the tool to follow.

There's still more work to do, but here's the current version:



1. The user selects which conversation she wants to edit here.

2. The user types a conversation here and the field automatically expands as new lines are added.

3. These buttons set up different branching behaviors.

4. This space denotes there was a branch choice between the two parts of the conversation.

5. This window allows you to select which of multiple branches to follow.

Thursday, March 5, 2015

Code Dive: Custom iterators in Hellenica (Part 2)

In part one of this code dive, I talked a bit about using custom iterators with IEnumerator and yield return. In this exciting(!!) conclusion post, I want to circle back and provide a concrete example from Hellenica where we made use of a custom iterator.

Let's set the scene: Diona is trapped along with a pirate enemy atop some rocky pillars, deep in the Mediterranean. By now, she's likely fed up with all the 'yarr'-ing and 'avast'-ing that comes with close proximity to a pirate wielding a barrel of wine, and she's ready to send an arrow his way.


For most ranged attacks in Hellenica, we need to check for obstructions between the character performing the attack and their target. If there are any obstructions that are sufficiently... obstructing, then the attack can't be made.

Here's a picture with all of the possible targets drawn. Notice how a couple positions right of the medium pillar, as well as the positions immediately below Diona's pillar, are untargetable.


So how do we determine which tiles to check for any given set of attacker and target positions? I decided to stay true to our old-school aesthetic and walk a pixelated path between the two positions. To do this, I summoned forth the classic Bresenham's Line algorithm. You've likely seen it in action before in your (least?) favorite paint program.


Originally devised to plot out lines on a screen, the algorithm is usually implemented using an iterative approach. That is, from the start position, proceed towards the end position by iterating over the discrete integer values of x that fall in between. As you iterate, you'll use an error metric to decide when you need to step up or down in the y-axis. Then it's just a matter of filling in one new pixel for every x position. If you're interested in more details, the internet has you covered.

In our case, we can use a similar approach if we treat each of our game positions like pixels on a screen. And, we should be able to create a custom iterator like the one we explored last time since this is just another collection of values (positions, in this case). The difference is that this time, instead of processing some data we've stored somewhere, we're now going to generate the data as we go!

Here's the gist of it:


What's most important here is that you pay special attention to those yield return lines that output the game positions to us.

Here's how we'd use it to check for obstacles in our original problem:


The get_discrete_line function returns an enumerator that acts just like a list of game positions, but it's actually computing those positions as the user asks for them. In more computationally expensive functions, it may be worthwhile to only calculate exactly what you need as you go, instead of generating the entire list up front, and this approach provides a clean way to do just that.

Here's the path that the algorithm computes for this example:


Looks clear to me! Fire away, Diona.

Hopefully this exploration has given you some new ideas about using custom iterators. I'll leave you with a teaser: If you think of this IEnumerator object as a way to space out the execution of a function over time, there are even more interesting applications that don't involve collections at all. I may take some time to cover this a bit more in the future.

Friday, February 27, 2015

Clearing up Some Rumors (in Thebes)

I’ve had some exciting tasks this week, including tracking down a particularly tricky save file bug as well as providing support for a mystery feature we'll hopefully be revealing soon.

But for this blog post, I’m gonna be talking about adding the appropriate rumors to sh5 Thebes. As I mentioned in this blog post, rumors are conversations that can seem like just flavor or world building, but also provide the necessary foreshadowing and/or context for events that might occur later in the game (depending on the player's choices).

When originally writing sh5_thebes, we didn't know exactly all the paths the story could take from there, so we just made a note to go back and add rumors. Since we now have a pretty good grasp of how the story’s going to shake out, it was time to get those rumors in.

The first thing I did was look at all possible events the paths leading out of Thebes could go to and examine the corresponding rumors. Then, I removed the rumors that would give redundant information. Sure, the rumor expounding on the state of the Spartan civil war would be relevant if the player chooses the associated path, but the conversations surrounding that choice already touch on a lot of that information. Instead, I chose to feature two rumors which would help introduce a character and a secret society that the party might unexpectedly stumble on in the future, hopefully making those appearances seem more foreshadowed and less Deus Ex Machina.

Even though both of these rumors were already written, they hadn't been implemented in the game yet, so there was some cleanup there too. A couple of flaws in conversation pacing or character voice weren't really obvious until we saw them in game. We additionally caught the characters casually referencing specific events in the 6th century BCE, so we added a glossary entry to make sure players could understand the context without needing to be a Greek history expert.


Sunday, February 22, 2015

Code Dive: Custom iterators in Hellenica (Part 1)

Recently I've been digging around in some code I wrote a long while back, and it reminded me of one handy feature of C# that I thought worth explaining here. It's been too long since we've had a hearty code post! There's a bit of background info I'll need to go over to make sure everyone's on the same page, so I've broken things up into two separate posts. This week I'll explain the C# feature, and next week I'll go over an example of its use in Hellenica.

When you write game code, or any kind of code really, you often spend a good deal of time iterating through collections of things. Usually this is something simple like a list of values, and it's easy enough to write code to step through them.


If you wanted to print out each of these values in order, you could write code like this:

foreach (int value in superImportantValues)
{
    print(value);
}

This would output:

12345678

Sometimes your data isn't so simply organized, however. Usually the code we write needs to perform some task under a specific set of constraints; perhaps we need it to run a specific operation very quickly for a large set of data, or maybe we need it to be able to do many things but only within a very small amount of memory.

Programmers have devised a number of special structures and techniques to store and interact with collections of data in ways that address these constraints. Consider this tree structure:

These are still the same super important values from our list earlier, but now the conceptual model used to represent the collection is very different. This specific structure is handy for quickly searching for a specific value, but it's not as clear how we might step through and add up the total of each of the values in this... thing.

It would be great if there were a simple way to iterate over these values just like I did in the previous list example without having to add complexity to my code every time I wanted to do so. In C#, there is a built-in type called IEnumerator<T> that allows you to define an iterator over any sort of complex collection of items, so long as you can tell the compiler how to do it. Here's what that might look like for this type of data structure:

IEnumerator<int> in_order_tree_traversal(Node root)
{
    Node current_node = root;
    Stack<Node> node_stack = new Stack<Node>();

    while (node_stack.Count > 0 || current_node != null)
    {
        if (current_node == null)
        {
            current_node = node_stack.Pop();

            yield return current_node.value;

            current_node = current_node.right;
        }
        else if (current_node.left != null)
        {
            node_stack.Push(current_node);
            current_node = current_node.left;
        }
        else
        {
            yield return current_node.value;

            current_node = current_node.right;
        }
    }
}

I claim that this function will climb around that tree from above and return each of our super important values in order. The specifics of the algorithm aren't vital to this discussion, but feel free to dig through it if you're interested.

I do want to draw your attention to the bolded statements in the code, though.

The first is IEnumerator<int> in the function definition at the top. This tells us that calling this function in_order_traversal will return to us an object that will enumerate a collection of integer values. Sounds promising so far!

The second are the yield return statements, of which there are two in the example. This is the C# secret sauce. These are the guys that tell the program, "Hey! We found another value that you might care about!" When execution hits one of these statements, it returns the value just like a normal function, but importantly, the state of the function is preserved -- local variables, the next instruction, everything. So, when we come back and ask for another value, the process can start up right where it left off.

It might help to see how to use this IEnumerator<int> thing. Here's an example similar to our previous one:

IEnumerator<int> superImportantValues = in_order_traversal(superImportantTree);
while (superImportantValues.MoveNext())
{
    print(superImportantValues.Current);
}

Pretty cool! Even though the user code looks fairly similar, internally we're now climbing all over a complex tree structure instead of just walking through a linear list. Let's look at what's going on here:

IEnumerator<int> superImportantValues = in_order_traversal(superImportantTree);

First, we're calling our new function and getting an object of type IEnumerator<int>. This is just a thing we can use to iterate over a collection of integer values.

while (superImportantValues.MoveNext())
{

Next, we use MoveNext() to find the next value in our tree by running our code until it hits another yield return statement. (If we reach the end of our iterator function without finding a yield return statement, MoveNext() will return false, signaling to us that we've seen all of the values.)

    print(superImportantValues.Current);

Lastly, we use .Current to look at the last value that our iterator found. We can keep referencing .Current to access the same value until we call MoveNext() again.

Still with me? That's one example of how to use yield return statements and the IEnumerator<T> type. All in all, a pretty convenient way to write a custom iterator for a collection of data.

Perhaps more interestingly, there's no limitation to the kind of collection you can iterate over. That collection could even be something you generate on the fly instead of something stored in memory. Next week, I'll go over one way we use this idea in Hellenica to do some of our combat calculations!

Saturday, February 14, 2015

The Dialogue Tool

(If you’re not familiar with how our dialogue system works, this post should fill in some details about what we mean when talking about “criteria”.)

This past week, I’ve been doing a variety of dialogue fixes, which aren’t particularly interesting on their own. However, the (self-built) tool I’m working in is pretty cool, so I thought I’d talk about that.

There's a bunch of extra stuff in it now, but the basic functionality is being able to add a dialogue line (green) and modify the dialogue line and the details it sets (red) and criteria (blue) that control when it plays.



By itself, these pieces are enough to implement our dialogue system, but in practice, manually setting all the fields to create conversations ended up being too labor intensive and error prone.

To that end, most of the other buttons don't actually add new functionality, they're just ways of automatically filling in fields in ways that were useful. A key example is the "Add Reply" button, which creates a new dialogue line and automatically sets criteria and details to have it continue from a previous line.

Another good one is "Clone Line", which exactly copies a previous line. A user then just needs to modify the text and add an extra criterion and whala!—she just added a custom line in the middle of a conversation.

My favorite feature, though, is the conversation converter. To take a step back, we do a lot of our writing in Google docs first, because it gives Siobhan and me a good place to share ideas and comment on each other's work (as well as sharing it with others to proofread). The conversation converter gives us a way to automatically convert this “normal” writing into lines in the dialogue tool.



Every line of dialogue in the conversation is inserted, with speakers and conversation details automatically set up for us. This only works for branchless conversations, but using the functionality above, it's usually not that much work to go back and add branches. (At least mechanically. The actual writing a coherent story filled with all these branches is still really hard.)

Hope you've enjoyed this look into how the Dialogue Tool works. If you’re curious for a more in-depth explanation on anything, let me know in comments and I’ll either explain there or in a future blog post.

Saturday, February 7, 2015

Hellenica Environments: Rhodes

It's now week two of post-PAX South life, and we're back into the swing of things. I'm currently in the middle of tweaking combat and building new levels, so it seemed like a good opportunity for an art update!

This week's travel destination is Rhodes, an elevated island city surrounded by a great wall on one side and staggering elevated docks on the other. Led by the so-called Mechanist-King Evagoras, the island nation is known for its theomechanical marvels as well as its generosity towards its allies and those in need.

We actually began work on Rhodes before Daniel, our environment artist, started working with us. At the time, we were still searching for the right partner, and we decided to give Collateral Damage Studios a trial run with Rhodes.

To start off, we wanted to work out the important elements unique to Rhodes. This meant figuring out how the steam-powered ship lift worked and what the Colossus of Rhodes may have looked like. We also knew we'd need to include some other elements for story reasons, so we tackled those as well. This exploratory part of the process is always my favorite.

 


A couple of the lifts relied on some questionable physics, so we decided on the rightmost implementation. In general, we were pleased with most variations of the other elements, though we really liked the mechanical solar system installation on the observatory.

With some more concrete details in mind for these elements, we started thinking about the composition of the scene.


We knew we wanted a dramatic view of the harbor that showcased the Colossus as well as the elevated docks. The first option actually worked the best for us, as it provided a nice vantage point from which to view the cityscape in the background. After a little more playing around with the finer points, we settled on option B from the second image. The airy, open feel of this composition provided a nice contrast to some of our other city locations that feel more cramped and urban.

Once that was settled, the artist took off towards the final image. CDS was great about keeping us posted on the various stages involved along the way.




This is where things stood at the end of our trial run with CDS. They exhibited an incredible attentiveness when it came to the fine details, and their transparency throughout the process was fantastic.  We were quite happy with how the final image turned out, but unfortunately the scheduling just didn't work out when it came to a continued partnership.

Now, jump ahead several months with me. Daniel had been on board for a while and was turning out great work on our other locations. We decided that we wanted him to go back over Rhodes and ensure it matched the style of his other pieces. This mostly entailed intensifying the lighting and shadows to match his preference for more dynamic lighting. Flip back and forth between the two images to see the differences.


Notice anything else that's different?

The Colossus and the boats are huge now! Unfortunately, we lost the staggering height that was critical to the island's identity during the transformation, as Daniel had dragged in the Colossus and resized the boats in the harbor due to a miscommunication on our part.

Luckily, this worked to our favor in the end. With a quick change to the horizon, a few additional ship elevators, and some architectural duct tape to help hold up the docks, Daniel was able to make the sense of scale even more pronounced than before.


So there you have it: Rhodes. And if you happen to be in town, don't forget to ask Evagoras about the elevators' auto-load balancers!

Friday, January 30, 2015

Notes from Bioware's PAX South panel

As Kurt mentioned in his last post, we were fortunate enough to spend this past weekend at PAX. We had a lot of fun there checking out different games and panels and even pulling aside a few unsuspecting attendees for usability tests (thanks, guys!). Particularly awesome was a Bioware panel we attended, where the developers largely eschewed the usual platitudes to give us some concrete details of writing techniques they used. I took notes on some of what they said and thought it would be neat to talk about how we arrived at some of the same ideas in the development of Hellenica.

(Story spoilers below.)

1. “Create a rough outline for your world and story first before you create your characters. That way you can ensure that your characters are all people with stakes in the story.”

We got the order of this one right, but our reasoning was different. One of our big goals was thematic cohesion through the world, so once we had arrived at our primary themes we sought to build them into the different political forces of our story. Then once those were in place, we started building our characters: Nephele became emblematic of the burgeoning exuberance of the steam revolution, Brasidas represents the confusion of having societal progress erode your deeply-held traditions, etc. Since our characters are so firmly grounded in our story’s themes, they definitely have stakes in what's happening, but I wonder if maybe we're missing something by tying characters to grand ideas and not focusing as much on an individual’s goals or prejudices.


2. “Write characters first and then introduce romantic relationships later, otherwise it becomes too easy for a character to becomes ‘just the love interest’."

You can ask Kurt about this, but early in development I fought HARD to make sure Diona and Brasidas wouldn't end up in a relationship. I was really worried it would distract from Diona's character and undermine her independence. After listening to the Bioware panel, I now think it could be done (after we've had 100+ pages of dialogue to define the characters) but even if I wasn't scared of writing romance dialogue, our game time’s already stuffed to the gills with story, so I don't see us adding any more.


3. “A good moral choice is one where even after you've seen the immediate consequences, you don't know if you made the right call. One way you can create this is by having the player commit to doing something, but then once she learns more about the situation, have a party member ask her to do something else.”

An early tenant of our writers has been that [uncertainty + conflicting goals = moral choice], which is a more programmer-ish way of conveying the same idea. That being said, where Dragon Age intentionally strives for verisimilitude and shades of moral gray, Hellenica's choices tend to focus more on giving the player exploration options. Although, sometimes certain paths are intrinsically tied to a specific character or story arc, in which case we almost feel a responsibility to add some narrative counter-balance, so the player doesn't just feel railroaded down a specific path.


I have a few other notes, but those don't tie in as directly to what we're trying to do with Hellenica. I'll post them though, if people are interested. Also, if I can track down a youtube video of the panel, I'll be sure to post that too.