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!
This is the devlog for the Dragonloft. Our current project is Hellenica, a tightly-paced JRPG that explores a steampunk take on ancient Greece.
Showing posts with label code. Show all posts
Showing posts with label code. Show all posts
Friday, April 24, 2015
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.
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!
Thursday, December 4, 2014
A little combat art clean-up
As we finish up more and more of the combat art, some of our old techniques for controlling sprites from the Age of Temp Art have started to bother me. This afternoon I tackled one such issue dealing with the facing of our actor sprites.
When we first started out, we plugged in some representative fantasy character sprites as place holders for our characters so that we could prototype combat mechanics without waiting for an artist. (There are a lot of great resources on the web for this purpose. Check out OpenGameArt.org or the Spriters' Resource as a start.) Here are some examples:
While this was great for communicating character strengths or attack potential, it didn't communicate character facing! The side-on view in these pictures made it difficult to determine which of the 4 possible directions a character was facing in our 3-D world.
To address this at the time, I fudged the sprite's rotation in the world to hint at the direction it was facing. Here's an example using our latest sprites:
Unfortunately, rotating the sprite relative to the camera guarantees that some of the pixels will be blended together to make the final image that makes it to your screen. This means less visual fidelity for our otherwise crispy pixel art.
What I want instead is to have the character sprites always orient themselves in such a way that the plane of their sprite is parallel to the camera's view plane (effectively, the player's screen).
Unity's Transform class provides a handy function called LookAt that gets us most of the way there. This function rotates the object's transform so that its forward vector points down the LookAt vector you provide, with an optional up vector hint. In our case, we tell each sprite to LookAt the camera's position. Here's the modified version:
If you compare this image with the previous one, you should notice that the sprites actually appear slightly larger, especially those near the center of the screen. The sprites are already much clearer, but there's still a slight problem.
Look closely at the three bandits at the top of that last image. Do they look a little different to you?
A naive application of that LookAt function I mentioned doesn't quite give us what we want. This is because our camera uses an orthographic projection instead of a normal perspective projection. (I won't go into this here, see this for the gory details.) As a sprite approaches an edge of the screen, it rotates more and more to face the camera's position. Basically, sprites look skinnier as they approach the left or right side of the screen and shorter as they approach the top or bottom of the screen. Not good!
To fix this, we tell the sprite to LookAt different points on the camera's view plane such that the sprite is perfectly parallel to the screen. Here's the end result:
All three bandits are now the same size!
(Some of you will note that there are slight differences between the three sprites. Our combat rendering is not pixel perfect, so at times a sprite pixel will land on a screen pixel boundary. I may address this in a future post if I get time.)
I can then flip the sprite to give an indication of its facing, which won't affect the rotation of the sprite at all.
For those that are interested, here's a quick code snippet from the component responsible for orienting the sprites. Let me know if you have any questions or suggestions!
private void LateUpdate()
{
// Project the targets position onto the camera's xy-plane
// to get a look-at vector that is orthogonal to
// to the camera's xy-plane. This will ensure that each
// sprite is rotated at a similar angle, regardless of
// its position relative to the camera
// world position of the sprite
Vector3 target_position = m_cached_transform.position;
// world position of the camera
Vector3 camera_position = m_camera_transform.position;
// direction vector from the camera to the target
Vector3 camera_to_target = target_position - camera_position;
// project the camera_to_target vector onto the camera's up/down vectors
float target_up = Vector3.Dot(camera_to_target, m_camera_transform.up);
float target_right = Vector3.Dot(camera_to_target, m_camera_transform.right);
// determine the position on the camera's xy-plane such that
// (target_position - new_position).normalized == camera.forward
// the new look-at vector is orthogonal to the camera's xy-plane
Vector3 look_at_point = camera_position + target_up * m_camera_transform.up + target_right * m_camera_transform.right;
// finally rotate the target to face the newly computed look-at point
m_cached_transform.LookAt(look_at_point, m_camera_transform.up);
}
When we first started out, we plugged in some representative fantasy character sprites as place holders for our characters so that we could prototype combat mechanics without waiting for an artist. (There are a lot of great resources on the web for this purpose. Check out OpenGameArt.org or the Spriters' Resource as a start.) Here are some examples:
While this was great for communicating character strengths or attack potential, it didn't communicate character facing! The side-on view in these pictures made it difficult to determine which of the 4 possible directions a character was facing in our 3-D world.
To address this at the time, I fudged the sprite's rotation in the world to hint at the direction it was facing. Here's an example using our latest sprites:
What I want instead is to have the character sprites always orient themselves in such a way that the plane of their sprite is parallel to the camera's view plane (effectively, the player's screen).
Unity's Transform class provides a handy function called LookAt that gets us most of the way there. This function rotates the object's transform so that its forward vector points down the LookAt vector you provide, with an optional up vector hint. In our case, we tell each sprite to LookAt the camera's position. Here's the modified version:
If you compare this image with the previous one, you should notice that the sprites actually appear slightly larger, especially those near the center of the screen. The sprites are already much clearer, but there's still a slight problem.
Look closely at the three bandits at the top of that last image. Do they look a little different to you?
A naive application of that LookAt function I mentioned doesn't quite give us what we want. This is because our camera uses an orthographic projection instead of a normal perspective projection. (I won't go into this here, see this for the gory details.) As a sprite approaches an edge of the screen, it rotates more and more to face the camera's position. Basically, sprites look skinnier as they approach the left or right side of the screen and shorter as they approach the top or bottom of the screen. Not good!
To fix this, we tell the sprite to LookAt different points on the camera's view plane such that the sprite is perfectly parallel to the screen. Here's the end result:
All three bandits are now the same size!
(Some of you will note that there are slight differences between the three sprites. Our combat rendering is not pixel perfect, so at times a sprite pixel will land on a screen pixel boundary. I may address this in a future post if I get time.)
I can then flip the sprite to give an indication of its facing, which won't affect the rotation of the sprite at all.
For those that are interested, here's a quick code snippet from the component responsible for orienting the sprites. Let me know if you have any questions or suggestions!
private void LateUpdate()
{
// Project the targets position onto the camera's xy-plane
// to get a look-at vector that is orthogonal to
// to the camera's xy-plane. This will ensure that each
// sprite is rotated at a similar angle, regardless of
// its position relative to the camera
// world position of the sprite
Vector3 target_position = m_cached_transform.position;
// world position of the camera
Vector3 camera_position = m_camera_transform.position;
// direction vector from the camera to the target
Vector3 camera_to_target = target_position - camera_position;
// project the camera_to_target vector onto the camera's up/down vectors
float target_up = Vector3.Dot(camera_to_target, m_camera_transform.up);
float target_right = Vector3.Dot(camera_to_target, m_camera_transform.right);
// determine the position on the camera's xy-plane such that
// (target_position - new_position).normalized == camera.forward
// the new look-at vector is orthogonal to the camera's xy-plane
Vector3 look_at_point = camera_position + target_up * m_camera_transform.up + target_right * m_camera_transform.right;
// finally rotate the target to face the newly computed look-at point
m_cached_transform.LookAt(look_at_point, m_camera_transform.up);
}
Wednesday, October 22, 2014
This changes everything
Every once in a while we implement a change in the project that makes me wonder how I ever enjoyed any of the previous versions. The last couple weeks I've been using some cycles to get our characters animating, and now that Diona is running and jumping around our little worlds, I just can't go back to our older builds. It's SO good to see our characters come to life on the screen!
Here's a quick video of some of her animations in place.
This is the animation graph I'm using to control her:
As far as animation graphs go, it's fairly simple. You can think of each rectangle as a single animation (idling in place, running, or shooting her bow), while the white arrows between the rectangles represent possible transitions between those animations.
So, for example, the arrows connecting diona_run to diona_hop allow Diona to immediately perform a hop while she is running. But, since there are no arrows between diona_run and diona_attack_bow, she has to stop moving and return to the diona_idle state before she can attack.
The nature of our turn-based combat means we can get away with a fairly simple set of transitions, as opposed to say, a real-time shooter game. Here's one part of an animation tree (a specialized kind of graph) from a recent Battlefield game:
It's not unusual for a large AAA studio to have a couple of programmers and a dedicated animation team spend all of their time refining the animation tree of the protagonist character. Maybe for our next game. ;)
I also put together some quick footage I took running around one of our test levels. Take a look!
Now that everything is in place, it's fairly simple to plug in the movement animations for our other characters, and that means our game is quickly coming to life!
Stay tuned for more!
Saturday, October 11, 2014
Level construction and keeping focus in the Unity editor
Up until now, creating the geometry for a level in Hellenica was controlled entirely through keyboard input. Here's what it looked like:
It's a cinch once you pick up the hotkeys, and now that I've been using it for a few months, constructing the geometry for a level is one of the quickest parts of the process.
Recently, we've been thinking about how to layer on Valery's art to make our environments more appealing. I decided it was going to be cumbersome and complicated to paint specific faces of the level blocks using keyboard hotkeys. The time had come to add mouse picking to the editor interface.
I immediately ran into issues dealing with Unity's default mouse picking behavior, though. Whenever you click on a GameObject in the scene view, Unity changes its focus to that new object! Generally, this is exactly what the user wants. In our case, however, painting tiles becomes very difficult when every click changes the user's selection!
After some internet research and experimentation on my own, I ended up with a solution that works well for us. It comes down to overriding Unity's GUI focus for the duration of the painting operation. Here's a quick sketch of how this is done:
...
else if (Event.current.isMouse)
{
process_mouse_input(Event.current);
// if we detect a left click while the mesh editor is selected,
// do not allow Unity to change our selection. assume we're editing
// until we release the left mouse button.
if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
{
// hotControl is a static variable that Unity uses to track the
// active GUI control
GUIUtility.hotControl = m_editor_control_id;
}
else if (GUIUtility.hotControl == m_editor_control_id &&
Event.current.type == EventType.MouseUp && Event.current.button == 0)
{
GUIUtility.hotControl = 0;
}
}
...
I begin overriding GUIUtility.hotControl on the MouseDown event and hang on to control until a corresponding MouseUp event to guarantee that the editor captures clicks as well as sustained drags.
m_editor_control_id is generated as follows:
m_editor_control_id = GUIUtility.GetControlID(this.GetHashCode(), FocusType.Passive);
As you may be able to tell, this solution isn't perfect. Any gizmos that end up in front of the level mesh will be unresponsive to mouse clicks so long as the mesh remains behind them, as the mesh editor is now all-consuming. (Muahaha.) But, it works well enough for us to get started painting levels.
Here's a quick gif of a paint job I did with some test tiles:
I have no doubt that this system will keep evolving as we go. If anyone out there has any ideas for improvement, let me know!
It's a cinch once you pick up the hotkeys, and now that I've been using it for a few months, constructing the geometry for a level is one of the quickest parts of the process.
Recently, we've been thinking about how to layer on Valery's art to make our environments more appealing. I decided it was going to be cumbersome and complicated to paint specific faces of the level blocks using keyboard hotkeys. The time had come to add mouse picking to the editor interface.
I immediately ran into issues dealing with Unity's default mouse picking behavior, though. Whenever you click on a GameObject in the scene view, Unity changes its focus to that new object! Generally, this is exactly what the user wants. In our case, however, painting tiles becomes very difficult when every click changes the user's selection!
After some internet research and experimentation on my own, I ended up with a solution that works well for us. It comes down to overriding Unity's GUI focus for the duration of the painting operation. Here's a quick sketch of how this is done:
...
else if (Event.current.isMouse)
{
process_mouse_input(Event.current);
// if we detect a left click while the mesh editor is selected,
// do not allow Unity to change our selection. assume we're editing
// until we release the left mouse button.
if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
{
// hotControl is a static variable that Unity uses to track the
// active GUI control
GUIUtility.hotControl = m_editor_control_id;
}
else if (GUIUtility.hotControl == m_editor_control_id &&
Event.current.type == EventType.MouseUp && Event.current.button == 0)
{
GUIUtility.hotControl = 0;
}
}
...
I begin overriding GUIUtility.hotControl on the MouseDown event and hang on to control until a corresponding MouseUp event to guarantee that the editor captures clicks as well as sustained drags.
m_editor_control_id is generated as follows:
m_editor_control_id = GUIUtility.GetControlID(this.GetHashCode(), FocusType.Passive);
As you may be able to tell, this solution isn't perfect. Any gizmos that end up in front of the level mesh will be unresponsive to mouse clicks so long as the mesh remains behind them, as the mesh editor is now all-consuming. (Muahaha.) But, it works well enough for us to get started painting levels.
Here's a quick gif of a paint job I did with some test tiles:
I have no doubt that this system will keep evolving as we go. If anyone out there has any ideas for improvement, let me know!
Wednesday, February 5, 2014
A simple logger class for Unity projects
Today I thought I'd share a simple utility that you can drop into your own projects to help manage your debug (or release!) logs at run-time.
Unity's included Debug class has some great functionality for logging, raising warnings, and raising errors. Over the course of a project, however, regular use of these functions can lead to a very noisy log, which can make debugging tedious.
With that in mind, I set out to create a custom logger with the following requirements:
- quick to drop into a new project
- easy for a person to use for the first time
- easily filterable at run-time
- zero overhead outside of DEBUG builds
To address the first two constraints, I made the Logger class a static singleton that creates itself whenever any of the public logging functions are invoked. This also means that a new user simply needs to register their logging channel and start logging to start using the Logger in their project. Here's a simple example:
Logger.register_channel(Log_Channel.Combat, Logger.E_Level.Verbose); Logger.log(Log_Channel.Combat, "Really super important combat event!");
By using Unity's Object.DontDestroyOnLoad() function on the Logger object, I guarantee that it will stick around for the lifetime of the game, even across level loads, unless you explicitly destroy it. This keeps lifetime management simple as well.
To be able to modify the filters at run-time, I set up a simple inspector (Logger_Inspector.cs) for the Logger class that generates drop-down lists for each channel that has been registered with the logger. It looks a little something like this:
From here you can set the logging level of any channel directly allowing you to filter out exactly what you want, when you want it.
Finally, to make sure that none of our logging statements have any impact on our final release builds, every publicly accessible member function of the Logger class makes use of the System.Diagnostics.Conditional attribute. Here's an example of what this looks like:
[System.Diagnostics.Conditional("DEBUG")] public static void log(string channel, string message) { ... }
When the DEBUG flag is unset during compilation, none of the public Logger functions will be compiled and calls to these functions will become empty statements (or no-ops). This translates to zero memory and processing overhead in non-DEBUG builds, which makes programmers everywhere happy. Hats off to the .NET and Mono guys!
So that's about it. There's still a good bit of room for extension here: adding different log output streams, adding hierarchical filters, or anything else your heart may desire. You can find all the code and a containing .unityPackage file here in our public BitBucket repo.
I hope this code helps you out in your projects. Let us know what you think!
Subscribe to:
Posts (Atom)