Howdy folks. Aside from some technical difficulties this week, I've been busy designing and iterating gameplay lately, tweaking hero abilities, level layouts, and enemy stats. Once these are all feeling good, I can be confident about moving forward with final combat art assets, and then we can ship this thing!
Now that we're getting closer to the finish line, we finally moved forward with a couple of the remaining character portraits we required. I don't feel too bad spoiling this one, as he shows up very early on in the game.
This is Sisyphus, one of the early big baddies you'll encounter throughout the world of Hellenica.
For the unacquainted, Sisyphus was a dastardly king sentenced to eternal torment by the gods for his deceitful, sometimes disgusting deeds. As punishment, Sisyphus was required to push a boulder up an impossible slope, endlessly stuck in a loop as the boulder always rolled back down before reaching the summit.
In Hellenica, Sisyphus has found a way to finally crest his impossible obstacle thanks to some supernatural assistance. However, his curse is not entirely broken, as he is still eternally burdened with the remains of the boulder.
In typical villain fashion, he has converted the source of his torment into a weapon, which he will happily use to defeat your party if they get in his way. Coupled with some new supernatural powers related to his curiously transformed legs, he is a force to be reckoned with.
The original sketch YDY put together was pretty spot on. The hair and face communicated the frenzied craze that we expected of someone previously mired in eternal, divine torment. His brute strength developed over years of continuous labor was obvious. His supernatural aspects are tricky to talk about in detail here without spoiling the story, but we were shooting for a craggy, ancient forest vibe. I think it works pretty well.
After some tweaks to the chains, we moved on to coloring. The first pass was good, but we felt there wasn't enough contrast between the leather bands and the boulder. Switching over to the green of the cloth also reduced some complexity which we thought helped to tie things together.
Let us know what you think of Sisyphus in the comments!
This is the devlog for the Dragonloft. Our current project is Hellenica, a tightly-paced JRPG that explores a steampunk take on ancient Greece.
Monday, November 16, 2015
A Development Tale
This won't be my usual post, but as this is a development blog, it felt relevant to touch on this.
Finding myself in a particularly agreeable mood one evening this week, I surrendered myself to the update Windows had been proffering for weeks. Pleased with my munificence, I skipped off to bed eager to see what treats would await me, nestled under my keyboard.
Imagine my surprise when my poor laptop's screen flickered furiously between full black and the Windows load screen upon first boot, never to reach the login prompt. Surely, safe mode would reveal the culprit? Nope! Stuck in the same, interminable loop.
After some technical voodoo, I was able to wrangle important files onto a separate drive and reinstall Windows. Balance is restored and peace reigns across the land once again.
The importance of this story isn't that Windows' desperately urgent security patches must never be trusted (though my recent moratorium on patches now feels powerfully justified). The takeaway should instead be that if you are working on anything important, you should ALWAYS be backing up your changes to an external repository!
In my experience, similarly unexpected events occur at least once per project, but they're usually harmless with a little daily diligence. This time, file extraction was still possible for me, but had that not been the case, I would have only lost a couple days of work (and my carefully curated gallery of hilarious ungulate gifs).
There are a number of free solutions available to you.
If you're working on any creative work and the incremental changes are important to track, I highly recommend using bitbucket. They provide free, private repositories and support git and mercurial (my personal choice). There are numerous tutorials available for using these technologies.
If you need to store documents or final assets, I've found Google Drive to be a convenient tool. Sharing controls are robust, and if you use gmail, the integration trivializes a number of tasks.
So take control of your destiny friends. Back-up your data regularly!
Finding myself in a particularly agreeable mood one evening this week, I surrendered myself to the update Windows had been proffering for weeks. Pleased with my munificence, I skipped off to bed eager to see what treats would await me, nestled under my keyboard.
Imagine my surprise when my poor laptop's screen flickered furiously between full black and the Windows load screen upon first boot, never to reach the login prompt. Surely, safe mode would reveal the culprit? Nope! Stuck in the same, interminable loop.
After some technical voodoo, I was able to wrangle important files onto a separate drive and reinstall Windows. Balance is restored and peace reigns across the land once again.
The importance of this story isn't that Windows' desperately urgent security patches must never be trusted (though my recent moratorium on patches now feels powerfully justified). The takeaway should instead be that if you are working on anything important, you should ALWAYS be backing up your changes to an external repository!
In my experience, similarly unexpected events occur at least once per project, but they're usually harmless with a little daily diligence. This time, file extraction was still possible for me, but had that not been the case, I would have only lost a couple days of work (and my carefully curated gallery of hilarious ungulate gifs).
There are a number of free solutions available to you.
If you're working on any creative work and the incremental changes are important to track, I highly recommend using bitbucket. They provide free, private repositories and support git and mercurial (my personal choice). There are numerous tutorials available for using these technologies.
If you need to store documents or final assets, I've found Google Drive to be a convenient tool. Sharing controls are robust, and if you use gmail, the integration trivializes a number of tasks.
So take control of your destiny friends. Back-up your data regularly!
Tuesday, September 29, 2015
Monday Update #5 - Feedback and visual polish
It's been about a month since my last update here, and you're probably wondering what we've been up to!
Our focus has been on bringing the player experience closer to final polish, which means we've now got a good chunk of new visual stuff that I can actually show off!
Here's a quick breakdown of things we've updated/added:
- Final combat UI!
- Combat objectives and progression system
- Icons for all playable character skills
- Effects system to drive visual and aural feedback effects
- Lots of WIP visual/aural effects
- Final skills for all ally and enemy characters
- Lots of juice added to the character animations
We've also finished creating all of the city location music and the narrative outlines for all the possible endings! Hopefully we'll be wrapping up the final script in the coming weeks.
Here's a quick peek at some of the party skill icons before they get stamped into brass buttons for our UI:
Can you tell which party members correspond to which icons? We're curious to see how closely you can guess what each skill does from the icon alone!
Here's a shot of the objectives screen:
The current design calls for each combat level to have three objectives. The first is the main objective, to be announced with great gusto: "Defeat all of the enemies", "Escape the bad place", etc. Completing this objective will allow you to progress further in the story. You'll also gain some favor from the gods, which can be used to upgrade your party's skills and impress your friends.
The second objective is a bonus objective that's meant to help teach the player a tactic they may not have considered. Completing this objective should hopefully make the level easier for those that are struggling, while also providing some bonus favor to spend towards skill upgrades. Sometimes this objective may call for a specific skill that the player hasn't unlocked yet, hinting that they should replay it after they've unlocked that skill.
The third objective is the elusive challenge objective, and it is unlocked once the player has completed the main objective. Currently, there's no explicit reward for completing the challenge objective, though I will be thrilled if you complete all of the challenges. These challenges are meant to satisfy those players that are looking for some added difficulty, the achievement hunters and expert tacticians out there.
Each objective is loosely associated with a Greek god or goddess. Since you're on a quest from Artemis, hers is the main objective (the moon). Apollo provides the second, more teaching-oriented objective (the sun). The final challenge objective (the shield and spear) is a challenge from Ares, the god of war himself.
I'll be doing some streaming later in the week to show off some of the other improvements we've made to the visual/aural feedback. Once I get a recording made I'll be plopping it on here for you guys to watch at your leisure. Stay tuned!
Our focus has been on bringing the player experience closer to final polish, which means we've now got a good chunk of new visual stuff that I can actually show off!
Here's a quick breakdown of things we've updated/added:
- Final combat UI!
- Combat objectives and progression system
- Icons for all playable character skills
- Effects system to drive visual and aural feedback effects
- Lots of WIP visual/aural effects
- Final skills for all ally and enemy characters
- Lots of juice added to the character animations
We've also finished creating all of the city location music and the narrative outlines for all the possible endings! Hopefully we'll be wrapping up the final script in the coming weeks.
Here's a quick peek at some of the party skill icons before they get stamped into brass buttons for our UI:
Can you tell which party members correspond to which icons? We're curious to see how closely you can guess what each skill does from the icon alone!
Here's a shot of the objectives screen:
The current design calls for each combat level to have three objectives. The first is the main objective, to be announced with great gusto: "Defeat all of the enemies", "Escape the bad place", etc. Completing this objective will allow you to progress further in the story. You'll also gain some favor from the gods, which can be used to upgrade your party's skills and impress your friends.
The second objective is a bonus objective that's meant to help teach the player a tactic they may not have considered. Completing this objective should hopefully make the level easier for those that are struggling, while also providing some bonus favor to spend towards skill upgrades. Sometimes this objective may call for a specific skill that the player hasn't unlocked yet, hinting that they should replay it after they've unlocked that skill.
The third objective is the elusive challenge objective, and it is unlocked once the player has completed the main objective. Currently, there's no explicit reward for completing the challenge objective, though I will be thrilled if you complete all of the challenges. These challenges are meant to satisfy those players that are looking for some added difficulty, the achievement hunters and expert tacticians out there.
Each objective is loosely associated with a Greek god or goddess. Since you're on a quest from Artemis, hers is the main objective (the moon). Apollo provides the second, more teaching-oriented objective (the sun). The final challenge objective (the shield and spear) is a challenge from Ares, the god of war himself.
I'll be doing some streaming later in the week to show off some of the other improvements we've made to the visual/aural feedback. Once I get a recording made I'll be plopping it on here for you guys to watch at your leisure. Stay tuned!
Tuesday, August 25, 2015
Hellenica sound update
Hey everyone!
This week I'm down in LA finalizing some story details with Victor and his new puppies! It's good to be back in the same space working on Hellenica again.
Between the game updates and dog distractions, I put together this short video that hops around a few of the cities the party will visit along their journeys.
It showcases some of Bob's awesome work bringing these places to life with music and ambient sounds. What do you all think?
This week I'm down in LA finalizing some story details with Victor and his new puppies! It's good to be back in the same space working on Hellenica again.
Between the game updates and dog distractions, I put together this short video that hops around a few of the cities the party will visit along their journeys.
It showcases some of Bob's awesome work bringing these places to life with music and ambient sounds. What do you all think?
Tuesday, August 11, 2015
Monday Update #4 - Combat UI and Usability
Hello!
This update I've focused mostly on working with Andrea to get the new combat planning UI in, along with some other usability improvements.
Here's a WIP shot of the new UI:
We're trying to reinforce the mechanics throughout each stage of combat. To that end, during planning the damage type/armor type relationship is called out immediately, in addition to any other bonuses or penalties that affect your attack. In this case, Brasidas deals piercing damage, but that doesn't do any bonus damage against light armor. He does gain a damage bonus for attacking the rogue from the side (flanking), but takes a damage penalty for attacking her from below.
Andrea's planned out mechanical sequences for how these panels will slide in and out. Once the various bits are in place and animating, I'll get some more gifs up here for your ogling.
One of the other usability improvements worth mentioning is the actor outlining:
Not only is it handy for finding actors that are blocked by terrain, it also increases player confidence when it comes to clicking on the actors in the world.
Meanwhile, Bob's whipped up a bunch of new music and ambience tracks for some more of the story locations. Pretty soon they'll all be aurally enhanced! And Victor's still diligently fleshing out the end game narrative and writing more dialogue.
I'm going to be spacing these updates out a bit more than every week, as you may have noticed, just to keep the content interesting. Sometimes we're all in the middle of bigger features, and there's not quite enough publishable content for a decent update. Hopefully we can find a good balance between depth and regularity over time.
Thanks for stopping by!
This update I've focused mostly on working with Andrea to get the new combat planning UI in, along with some other usability improvements.
Here's a WIP shot of the new UI:
We're trying to reinforce the mechanics throughout each stage of combat. To that end, during planning the damage type/armor type relationship is called out immediately, in addition to any other bonuses or penalties that affect your attack. In this case, Brasidas deals piercing damage, but that doesn't do any bonus damage against light armor. He does gain a damage bonus for attacking the rogue from the side (flanking), but takes a damage penalty for attacking her from below.
Andrea's planned out mechanical sequences for how these panels will slide in and out. Once the various bits are in place and animating, I'll get some more gifs up here for your ogling.
One of the other usability improvements worth mentioning is the actor outlining:
Not only is it handy for finding actors that are blocked by terrain, it also increases player confidence when it comes to clicking on the actors in the world.
Meanwhile, Bob's whipped up a bunch of new music and ambience tracks for some more of the story locations. Pretty soon they'll all be aurally enhanced! And Victor's still diligently fleshing out the end game narrative and writing more dialogue.
I'm going to be spacing these updates out a bit more than every week, as you may have noticed, just to keep the content interesting. Sometimes we're all in the middle of bigger features, and there's not quite enough publishable content for a decent update. Hopefully we can find a good balance between depth and regularity over time.
Thanks for stopping by!
Tuesday, July 28, 2015
Monday Update #3
Oops! This is going up a little past the due date, but hopefully you'll forgive me. We got a little distracted by Rocket League tonight. ;)
New stuff!
- Fixed some long-standing issues with the animation system.
- Implemented social hub UI for all existing social hubs
- Moved party customization so that it occurs after the intro cinematic and objectives reveal
- Reworked terrain checking for direct abilities with 3D positions
- New music for Athens Acropolis, Corinth, and Rhodes
- Completed first pass at SH6 Athens dialogue
Re-implementing the social hubs with the new UI was a big task that I had put off in favor of focusing on our critical path, but having it in place is pretty vital when we need to reference a story detail. Now that they're all hooked up, it also means playtesters can explore a bit more freely, which is a bonus. Expect another post this week with a playthrough video of the new experience.
Moving the customization screen into the combat path itself is something I'm really excited about. I always wondered why Final Fantasy Tactics let you position your troops in a precise manner before you had any idea what the encounter actually entailed.
Is the enemy going to be on the left? In front? Behind? Are the first two squares on top of a cliff? You had no idea until you locked it in.
Now in Hellenica, you'll see the introduction cinematic play out that shows off the battlefield, highlights the threats you'll be facing, and explains the objectives. Only then will you choose what abilities you'd like to take with you into the fight. Hopefully this will set the player up for success.
As for this week, I'm planning on getting back into some level building later in the week, so expect some twitch streaming right here!
New stuff!
- Fixed some long-standing issues with the animation system.
- Implemented social hub UI for all existing social hubs
- Moved party customization so that it occurs after the intro cinematic and objectives reveal
- Reworked terrain checking for direct abilities with 3D positions
- New music for Athens Acropolis, Corinth, and Rhodes
- Completed first pass at SH6 Athens dialogue
Re-implementing the social hubs with the new UI was a big task that I had put off in favor of focusing on our critical path, but having it in place is pretty vital when we need to reference a story detail. Now that they're all hooked up, it also means playtesters can explore a bit more freely, which is a bonus. Expect another post this week with a playthrough video of the new experience.
Moving the customization screen into the combat path itself is something I'm really excited about. I always wondered why Final Fantasy Tactics let you position your troops in a precise manner before you had any idea what the encounter actually entailed.
Is the enemy going to be on the left? In front? Behind? Are the first two squares on top of a cliff? You had no idea until you locked it in.
Now in Hellenica, you'll see the introduction cinematic play out that shows off the battlefield, highlights the threats you'll be facing, and explains the objectives. Only then will you choose what abilities you'd like to take with you into the fight. Hopefully this will set the player up for success.
As for this week, I'm planning on getting back into some level building later in the week, so expect some twitch streaming right here!
Monday, July 20, 2015
Monday Update #2
Another Monday, another status update!
Last week:
- Updated visuals for each actor's in-world UI
- More social hub background music
- AI reasoning about multiple abilities
- AI reasoning about abilities that target multiple tiles
- Added a new ability for Nephele tentatively called "Sky Shot"
- Made moving platforms play nice with new 3D game positions
- Started nailing down story arcs for some lesser villains
- Added new destination descriptions and departure dialogue lines
Nothing too crazy last week, but we're really excited about the new in-world UI around the actors. The health readouts and facing arrows solve two of the biggest complaints we've heard in playtests.
Also, it's always fun hooking up a new ability to see how it changes up the formula. Sky Shot bears some resemblance to abilities like Charge from Final Fantasy Tactics and Rain of Arrows from Banner Saga. Nephele fires a volley of bolts up into the air that target a small area on the battlefield. Then, at the end of the player's next turn, those bolts land and deal damage to any enemies in those locations. Lovely payout if you're able to predict your enemies' movements, or just move them into position yourself with a few pushes or tosses. ;)
This week we're still working towards getting the final UI elements in for the combat portion of the game. I'm also planning on tackling some long-standing bugs and design improvements as we polish things up on our way to finishing the critical path.
Last week:
- Updated visuals for each actor's in-world UI
- More social hub background music
- AI reasoning about multiple abilities
- AI reasoning about abilities that target multiple tiles
- Added a new ability for Nephele tentatively called "Sky Shot"
- Made moving platforms play nice with new 3D game positions
- Started nailing down story arcs for some lesser villains
- Added new destination descriptions and departure dialogue lines
Nothing too crazy last week, but we're really excited about the new in-world UI around the actors. The health readouts and facing arrows solve two of the biggest complaints we've heard in playtests.
Also, it's always fun hooking up a new ability to see how it changes up the formula. Sky Shot bears some resemblance to abilities like Charge from Final Fantasy Tactics and Rain of Arrows from Banner Saga. Nephele fires a volley of bolts up into the air that target a small area on the battlefield. Then, at the end of the player's next turn, those bolts land and deal damage to any enemies in those locations. Lovely payout if you're able to predict your enemies' movements, or just move them into position yourself with a few pushes or tosses. ;)
This week we're still working towards getting the final UI elements in for the combat portion of the game. I'm also planning on tackling some long-standing bugs and design improvements as we polish things up on our way to finishing the critical path.
Monday, July 13, 2015
The first Monday Update!
Wow, sorry lovely readers. You have suffered some serious neglect, and for that I apologize!
Based on the previous post, it must look like the end of days for Hellenica. In fact, that couldn't be farther from the truth. We've made a ton of progress over the last two months, we've just been doing it very, very quietly (disregarding all of the chortling and singing that takes place in the office).
From here on out, I'm going to attempt to put up a quick post on Mondays detailing what we did the previous week and providing some hints about developments in the upcoming week. Then every so often I will try to piece together a more in-depth look into a specific topic. I know they're more interesting to read, but they also take up quite a bit of precious development time so, you know, trade-offs.
Consider this the first Monday update post!
What we've been up to:
- New social hub UI (this deserves its own post later)
- New sound effects to support social hub UI
- New social hub ambient and background music tracks (check one out here and be sure to loop it!)
- New map UI (note to Kurt: one more separate blog post)
- New map UI (note to Kurt: one more separate blog post)
- Ported the dialogue UI from NGUI to the new Unity UI
- New dialogue authoring tool. Should hopefully make it easier for people other than Victor to generate complex, branching dialogues.
This week:
- Updated visuals for each actor's in-world UI
- More social hub background music
- AI reasoning about multiple abilities
- AI reasoning about abilities that target multiple tiles
- Update pathfinding to support new 3D game positions
- Make platforms play nice with new 3D game positions
- More dialogue :)
Whew! Usually these will be much, much shorter, but hopefully you're convinced that Hellenica is still alive and kicking. I'll try to get some more detailed posts going every few weeks to explore some of the more interesting features I listed.
Until next time!
- New combat features:
- Neutral actors. Currently using this for destructible obstacles.
- Moving platforms. Think steam-powered conveyor belts, elevators, and maybe some more mystical devices that make the environments more dynamic.
- 3D game positions. This might sound a little weird since our game is 3D, and it probably deserves its own explanatory post in the future. For now, just know that it's going to let us do some cool new things, but it'll take some time to iron out all of the wrinkles.
- New playable characters! Unfortunately, they're still a secret.
- Status effects. Root, stun, taunt, etc.
- Cinematics! The new cinematics editor is hooked up and we've got cinematics built for our critical path.
- A whole slew of new party abilities and a progression system to unlock them. (My favorite new ability is code-named Beardozer, and it is a thing to behold.)
- Some new enemy abilities
- Smarter camera work during turn execution.
- More intentional backgrounds to help establish mood.
- Redesign of the various UI pieces in the combat portion of the game. These will start popping in as I get time to integrate them.
- A bunch of new combat levels (you'll see this one a lot)
- Improvements to the level editor
- AI perf improvements
- A bunch of new story dialogue
- A bunch of new story dialogue
This week:
- Updated visuals for each actor's in-world UI
- More social hub background music
- AI reasoning about multiple abilities
- AI reasoning about abilities that target multiple tiles
- Update pathfinding to support new 3D game positions
- Make platforms play nice with new 3D game positions
- More dialogue :)
Whew! Usually these will be much, much shorter, but hopefully you're convinced that Hellenica is still alive and kicking. I'll try to get some more detailed posts going every few weeks to explore some of the more interesting features I listed.
Until next time!
Sunday, May 10, 2015
Transitions
Sorry our updates have become less regular as of late; it's been a period of change for Dragonloft. There's a couple reasons, but the biggest one is that I've accepted a programming job in California. My motivations for this move are mostly personal, though we certainly appreciate the financial stability that my employment gives us.
One thing that hasn't changed is our commitment to finishing this project, Kurt is still working full-time and pounding out levels, and I made sure that my new job allows me at least some freedom to work on Hellenica. Additionally, our new dialogue tool, Thoth (reveal video forthcoming) should better enable our writer to implement lines directly, as opposed to needing to go through me.
We're still not sure what this means for the blog, but my guess is it won't follow a regular schedule, but instead be driven by when we have something cool to show off (I thought Kurt's last post on asset reuse was awesome). I know Kurt's experimenting with streaming, so check that out if you're more interested in day-to-day development.
Friday, April 24, 2015
Sprite palette swapping with shaders in Unity
As any game developer knows, asset reuse is your friend. Whenever the design calls for a new character, I often turn to our existing assets to see if I can create what we need more quickly and cheaply than contracting out another character sprite sheet.
One method for reusing assets is to swap out the colors. RPGs have used this trick for years, and it works just as well in Hellenica.
Every sprite in Hellenica uses a relatively limited color palette. To make a new variation, we just choose a few colors in the palette and assign new colors. Here's what our friendly pirate's color palette looks like:
What I ended up doing is creating a custom shader that takes a sprite and a small palette texture (like the one above) and swaps the colors on the fly.
-- Start of the technical bit! --
Creating the palette texture is pretty straightforward, but I thought it would be interesting to explain how the shader knows which color to use when it's drawing a pixel.
One way to do this is to embed the palette indices straight into the original sprite. But that presents a tricky problem! How can I change the data stored in the image without changing how the image looks? It turns out that it's doable if you don't change too much.
Before diving in to how this works, it's important that we're on the same page regarding digital color. For the purposes of this post, all you need to know is that every displayable color can be represented as a combination of three colors: red, green, and blue, or RGB for short. If you've used a paint program, you've probably seen this before:
If the color depth is sufficiently high, changing these RGB values by very small amounts doesn't change the final color much. In our case, we're using 32-bit color depth, which means each RGB value can range from 0 to 255.
This image contains 64 different colors, but to my eye it's all the same color. You can open it up in a paint program and poke around with the eyedropper to see what I mean.
Starting with a bright red color, RGB(252, 0, 0), I generated all possible color combinations that can be made by adding 0 - 3 to each of the color channels (red, green, and blue). That's 4 * 4 * 4 = 64 combinations.
This is the approach I'm using to embed the palette index into the original sprite. Since all of our character sprites in Hellenica use fewer than 64 colors, I can safely store the palette index inside the sprite pixels without modifying the end result visually.
Here's what it looks like when I modified our pirate friend:
The pirate on the left is the original source sprite, but the pirate on the right is the sprite that I modified. Can you tell the difference?
Here's a visual breakdown of the process for storing the indices:
We support at most 64 unique colors, so that means we only need 6 bits to represent the color index. First, I split this index into three separate 2-bit values. Then I hide these in the two least significant bits of the three color channels (red, green, and blue). As you saw before, since I'm only changing the two least significant bits, the visual impact is negligible.
I put together a little editor script to generate a color palette texture from a sprite and embed the indices using this process. Then I made a shader that can pick out the index and use it to look up the actual color in the palette texture.
Putting all of this together, creating a new variation now takes about as much time as it takes to swap out some colors in Paint.
-- End of the technical bit! --
Here are some variations on our pirate friend and the associated palette textures:
Aside from simpler color swapping, this technique is also amenable to some more imaginative uses!
I'm really happy with how things turned out, and I'm sure I'll come up with more uses as the project progresses. Let me know what you think in the comments!
One method for reusing assets is to swap out the colors. RPGs have used this trick for years, and it works just as well in Hellenica.
Every sprite in Hellenica uses a relatively limited color palette. To make a new variation, we just choose a few colors in the palette and assign new colors. Here's what our friendly pirate's color palette looks like:
What I ended up doing is creating a custom shader that takes a sprite and a small palette texture (like the one above) and swaps the colors on the fly.
-- Start of the technical bit! --
Creating the palette texture is pretty straightforward, but I thought it would be interesting to explain how the shader knows which color to use when it's drawing a pixel.
One way to do this is to embed the palette indices straight into the original sprite. But that presents a tricky problem! How can I change the data stored in the image without changing how the image looks? It turns out that it's doable if you don't change too much.
Before diving in to how this works, it's important that we're on the same page regarding digital color. For the purposes of this post, all you need to know is that every displayable color can be represented as a combination of three colors: red, green, and blue, or RGB for short. If you've used a paint program, you've probably seen this before:
If the color depth is sufficiently high, changing these RGB values by very small amounts doesn't change the final color much. In our case, we're using 32-bit color depth, which means each RGB value can range from 0 to 255.
This image contains 64 different colors, but to my eye it's all the same color. You can open it up in a paint program and poke around with the eyedropper to see what I mean.
Starting with a bright red color, RGB(252, 0, 0), I generated all possible color combinations that can be made by adding 0 - 3 to each of the color channels (red, green, and blue). That's 4 * 4 * 4 = 64 combinations.
This is the approach I'm using to embed the palette index into the original sprite. Since all of our character sprites in Hellenica use fewer than 64 colors, I can safely store the palette index inside the sprite pixels without modifying the end result visually.
Here's what it looks like when I modified our pirate friend:
The pirate on the left is the original source sprite, but the pirate on the right is the sprite that I modified. Can you tell the difference?
Here's a visual breakdown of the process for storing the indices:
We support at most 64 unique colors, so that means we only need 6 bits to represent the color index. First, I split this index into three separate 2-bit values. Then I hide these in the two least significant bits of the three color channels (red, green, and blue). As you saw before, since I'm only changing the two least significant bits, the visual impact is negligible.
I put together a little editor script to generate a color palette texture from a sprite and embed the indices using this process. Then I made a shader that can pick out the index and use it to look up the actual color in the palette texture.
Putting all of this together, creating a new variation now takes about as much time as it takes to swap out some colors in Paint.
-- End of the technical bit! --
Here are some variations on our pirate friend and the associated palette textures:
Aside from simpler color swapping, this technique is also amenable to some more imaginative uses!
I'm really happy with how things turned out, and I'm sure I'll come up with more uses as the project progresses. Let me know what you think in the comments!
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.
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.
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.
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:
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!
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);
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.
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!
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.
(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.
Friday, January 23, 2015
Off to PAX South!
We're hopping in the dragonmobile this weekend to head up to the very first PAX South! (That's right, we'll be one of the few cars traveling north to reach the convention.) We don't have an official booth, but I'm hoping to get as many hands and eyes on Hellenica as I can manage in the two-day period.
To prepare, I've been polishing up one of the more unique levels in the game: the train ride to Thebes! I won't spoil the plot details, but suffice it to say that our party's in for a rather bumpy ride.
Unlike the combat stages in most tactics games, this particular stage is broken up into multiple parts (different train cars), and I'm really happy with how it turned out. Here are some shots of a few of the cars:
If you're going to be at PAX South this weekend, shoot us a note here or on twitter @thedragonloft! We'd be more than happy to show you the demo.
To prepare, I've been polishing up one of the more unique levels in the game: the train ride to Thebes! I won't spoil the plot details, but suffice it to say that our party's in for a rather bumpy ride.
Unlike the combat stages in most tactics games, this particular stage is broken up into multiple parts (different train cars), and I'm really happy with how it turned out. Here are some shots of a few of the cars:
If you're going to be at PAX South this weekend, shoot us a note here or on twitter @thedragonloft! We'd be more than happy to show you the demo.
Thursday, January 15, 2015
A New Year in Sparta
Happy New Year!
(The following post contains moderate story spoilers.)
We've done a variety of sundry tasks since returning from our holiday, but my great white whale so far has been the implementation of level 6's Sparta. I've mentioned before that the cumulative weight of all the player’s previous choices makes writing later scenes more difficult, and Sparta's no exception. First off, multiple characters have baggage tied to this location. Brasidas in particular has a checkered history the player might have uncovered hints about in her previous adventures.
Additionally, the party comes to Sparta to deliver a warning, but the exact details of that warning vary depending on what the party's learned. Our dialogue system is awesome for this sort of thing, allowing us to easily accent a conversation depending on what details the player has learned. I've additionally started using what I'm calling "general knowledge (GK)" variables which track how much a party knows about a topic (as opposed to checking against various specific events). These GK variables work well when that information is likely to be mentioned and referenced in various paths; for example, one such variable is checked in Sparta (and elsewhere) to see if the party has specific knowledge about the enemies’ weapons.
From a narrative standpoint, sh6_sparta is exciting because the player gets a big reveal of a certain clandestine order. Though they're peripherally (and furtively) involved in a few other social hubs the player could have visited, this is the first time you can stop and ask questions about their philosophy and motivations.
Next Week: Hopefully something with more pictures and less spoilers.
(The following post contains moderate story spoilers.)
We've done a variety of sundry tasks since returning from our holiday, but my great white whale so far has been the implementation of level 6's Sparta. I've mentioned before that the cumulative weight of all the player’s previous choices makes writing later scenes more difficult, and Sparta's no exception. First off, multiple characters have baggage tied to this location. Brasidas in particular has a checkered history the player might have uncovered hints about in her previous adventures.
Additionally, the party comes to Sparta to deliver a warning, but the exact details of that warning vary depending on what the party's learned. Our dialogue system is awesome for this sort of thing, allowing us to easily accent a conversation depending on what details the player has learned. I've additionally started using what I'm calling "general knowledge (GK)" variables which track how much a party knows about a topic (as opposed to checking against various specific events). These GK variables work well when that information is likely to be mentioned and referenced in various paths; for example, one such variable is checked in Sparta (and elsewhere) to see if the party has specific knowledge about the enemies’ weapons.
From a narrative standpoint, sh6_sparta is exciting because the player gets a big reveal of a certain clandestine order. Though they're peripherally (and furtively) involved in a few other social hubs the player could have visited, this is the first time you can stop and ask questions about their philosophy and motivations.
Next Week: Hopefully something with more pictures and less spoilers.
Subscribe to:
Posts (Atom)