A New Direction: Novel Visual Style in Unity

 


At this point, I think I'm totally disenthused at the prospect of continuing with my exploration of AI image generation. It's not a total dead end, but I just can't see myself being able to put up with it being a core part of my workflow. I'm bowing out now before the sunk costs become too great.

I've decided that I'm basically going to start over with a mind towards exploring traditional 3D rendering techniques. And, even though I've invested much less time into it so far, I feel like I've already learned so much more and gotten much closer to something I would actually deem production-ready than I achieved with AI.

Gameplay vs. Visual Fidelity vs. Visual Style in Fighting Games

I'm personally much more interested in creating unique and novel gameplay than making my game look pretty. When it comes to graphics, I prioritize finding a style that makes the game possible to actually complete, rather than trying to make it look as good as I imagine it could.

For fighitng games specifically, the playerbase tends to place a lot more value on gameplay over graphics: fighting games emphasize iterative gameplay where you spend a lot of time staring at the same assets over and over, so no matter how pretty the game looks, the magic tends to wear off eventually. Strong gameplay is what keeps people coming back.

If you're concerned about selling copies rather than simply providing a good experience, though, the equation changes. It's pretty difficult to tell how well a fighting game plays unless you invest a significant amount of time. And besides, a significant portion of fighting game sales come from casuals who only dabble in the genre and aren't planning on getting to the part where the finer points of the game design matter. So, especially for a smaller, original game with no prior entries and no recognizable preexisting IP, it's really important to get the graphics right.

Now, I don't think the graphics necessarily need to look "good." They just need to stand out and be memorable so that people pay attention. I'm not a huge fan of making things different for the sake of being different in game design, but when it comes to graphics, I do think that difference is inherently valuable: if the graphical style is something that hasn't ever been done before, then the quality standard is probably going to be much lower because people don't have other games to compare it to. Of course, it doesn't hurt if the graphical style in question is actually executed well.

Also, just as a matter of personal preference, I like my fighting games to have a very "clean" graphical style that puts readability and style over model fidelity. 

I greatly prefer the visual style of Street Fighter V (left) to Street Fighter 6 (right) 

I think that characters should primarily express themselves primarily through motion, not through having a really detailed design or high-quality textures or whatever. Not every frame needs to be a painting.

Visual Style Brainstorming

Basically, what I'm looking for is a graphical style that is A. different from what other fighting games are doing, B. relatively easy to make look "good enough" without requiring a lot of work or skills that I don't have, C. provides a strong basis from which I can imagine creating character designs, a world, tone, and other aesthetic elements. 

After some exploration, I found myself thinking about PlatinumGames' MadWorld, released in 2009.


MadWorld has an incredibly distinct visual style, inspired largely by the neo-noir comic Sin City. The game features a limited color palette: black and white for characters and the environment, a sort of dull yellow for UI elements, and red for the sprays of blood used to accentuate your attacks. The general lack of colors makes what color does show up stand out a lot more: in this case, MadWorld really wants to emphasize the violent content of the game.

Now, if I'm being honest, I think I like MadWorld's visual style much more in concept than in execution. While I haven't played the game myself, there's a lot of things I don't like about how the game looks in motion. 

I think that the two-tone style is a little hard on the eyes, and also makes it difficult to read characters' motions. The commitment to pure black and white with no in-between, combined with the blocky shading, means you can't really establish a difference in contrast between your character, the enemies, and the background. In order to tell what's going on, I kind of have to actively exert mental effort and stare at what's happening for longer than I would otherwise, which ruins a lot of the visceral fun of the animations. Subjectively, I'm also just not a huge fan of the visual design and tone beyond the initial concept.

With all that being said, MadWorld's style is certainly memorable, and there are a lot of people that are fond of it. There's not many games that look like this, and most importantly, I don't know of a single fighting game that looks like this. I think there's an opportunity to execute the core conceit of a black-and-white style with limited splashes of color much better, maybe something closer to some of Persona 5's key art.


And, most importantly, I think this style would be relatively easy to execute with my skillset. I predict that making this style work will largely be about making sound technical choices, rather than putting in a lot of effort and attention to detail. The lack of colors means I won't have to put anywhere near as much effort into texturing (which is probably the most difficult part of the 3D production process for me). I don't think the models need to be particularly detailed either: I think this style can look good with relatively cartoony, low-poly character designs. Lighting should also be relatively easy, especially given that it's a fighting game. Fighting games generally don't have characters travel between lots of different environments with different lighting, and there's an expectation for characters' visual profiles to remain consistent in order to maximize readability, so I don't think there's much need to have the character lighting react dynamically to the environment. My current plan is for the character models to be in separate render layers, each with a simple, static directional lighting setup placed so as to emphasize the shading on each of the models. The most I imagine doing is keyframing the lights during certain animations.

I could go into a lot more detail regarding my vision and plan for the game, but I think it makes more sense to start talking about what I've actually done to actually realize the visuals in-engine.

Working with Shaders

My initial plan for creating the black-and-white style is to create a model in entirely grayscale tones, then apply a shader that takes that model and applies stylized shading to it based on localized brightness level. I began searching for tutorials and other resources that seemed to align with my vision, and attempted to implement them using Unity's shader graph and the Universal Render Pipeline. I haven't worked with shaders before, but I was able to understand pretty much all of what was going on in the tutorials, and I found the interface for the shader graph to be pretty intuitive.

This tutorial shows off a method for creating cross-hatch shading and object outlines that resemble a hand-drawn ink style. The outlines are achieved by applying an edge-detection tool called a Sobel filter to the normal map of the environment, then applying a constant-stroke outline to any edges detected this way. The cross-hatching is done using three separate custom textures alongside a step function that "activates" each layer of hatching once the brightness in an area goes past a certain threshold.


While I do quite like these results, I don't think they're quite what I'm looking for. I want my shading to be able to go all the way to pitch-black, and it seems troublesome to implement that effect by layering manually-created textures. There probably has to be something driving the cross-hatch density. The bit about using the edge-detection filter seems like a good tip, though: I'll put a pin in that for later.

I then scrounged through some forum posts looking for people trying to do something similar to me, and found some recommendations for this tutorial on implementing custom functionality in Unity's shader graph using Unity's Universal Render Pipeline and HLSL nodes, along with this tutorial on replicating a manga-inspired style in Blender with procedural cross-hatching and shading.


I was really impressed with the results from the Blender video, so I wanted to see if I could replicate it. I was able to understand what was going on in the URP/HLSL tutorial pretty well, and was able to implement the stuff it showed off without too much trouble (though version differences made some things in the video not quite match up with my experience). I attempted to translate the method shown in the Blender video using Unity's shader graph, but the available nodes between Blender and Unity aren't 1-to-1, so there were some bits that I struggled to replicate. After trying to muddle through for a while, I tracked down this video, which shows off a lot of the same things as seen in the Blender tutorial, except adapted to work in Unity.


After some time, I was able to produce the following effect:


This is a shader that responds to directional light and applies a crosshatching effect. The effect can be modified using parameters that tweak the extent of the shading and how wide the transition zone from bright to dark is.


Here's what the shader graph for this effect looks like:


Let's go over some of the components of this shader graph. The hatching texture is created by applying a triangle wave effect to a UV node, rotating it, turning it greyscale, translating it to polar coordinates, then separating it into red and green components, which creates two patterns of diagonal lines at right angles to each other.


The interaction with the lighting is handled by this section. Using a custom HLSL function, I take the dot product between the normal map of the scene and the main directional light. 


I take this "dot product map" and create two separate remappings so that it goes from 0 to 1, one for each of the two crosshatch textures. The input range of the remapping has a parameter which allows me to control the extent of the shading. The input range is slightly different for the two remappings, making it so that the point where the two crosshatch patterns kick in are slightly offset from each other. Before the remapping, I also apply a bit of noise to the dot product map, in order to give it a "rougher" feel.


I then combine these remappings with the crosshatch textures using two more remap nodes. I multiply the results of these two with each other in order to overlay the two crosshatch directions. I take that and multiply it by the original dot product map, which essentially creates an additional gradient of light to dark on top of the crosshatching. Before I implemented this step, there were little bright dots visible in between the hatches in the dark areas. Finally, I apply a saturate node in order to clamp the brightness from 0 to 1, then output the result.


There's one more bit to go over. Before creating the crosshatch textures, there's this section that takes the dimensions of the screen and the object's screen position, then uses a "tiling and offset" node to scale the size of the crosshatch texture. 


Essentially, what this does is make it so that the spacing of the crosshatch lines on the screen is driven by the screen space, making it consistent regardless of the object's size, angle, or the camera zoom.


Without this step, a lot of gnarly visual artifacts start to appear, especially around the edges of objects and at farther zoom levels:


Unfortunately, I think the constant hatch spacing does mess a little bit with the sense of scale. Objects farther from the camera will have the same hatch density as objects closer to the camera, which I think would make the hatching feel more like a filter that's been applied on top of the image rather than a property of the objects themselves. Whether this is desirable is subjective: it makes the game scene seem more like a stylized drawn image and less like a believable world that actually exists in that style.
 
However, in a fighting game, all of the screen elements are generally going to remain at the same distance from the camera throughout gameplay, and I can control the hatch spacing on an object-by-object basis using shader graph parameters, so I don't think this issue will come up too often. I suppose if I really wanted to, I could use a depth map to scale hatch size based on distance from the camera, but I'll hold off on that for now.

I think the next step is to adapt the shader so that it combines with the object's base texture, then create some simple textured models that I can use to test out how the shader interacts with different styles of texturing.

Comments