Devlog #1:
Addressing Visual Clarity with Character Blurs
Devlog #1:
Addressing Visual Clarity with Character Blurs
It seems that the characters in my game do not stick out from the background enough; how can I use shaders to make them stick out more?
The Goal: Improve visual clarity by having fighters look more visually distinct from the background.
As a solo developer, one of the most rewarding parts of creating Project Berserk has been stepping outside my comfort zone and learning disciplines far beyond programming. Pixel art and animation were two of those disciplines. Although I'm proud of how the game's sprites turned out, I had to keep them at a very low resolution to make animating each character's large moveset realistic within the scope of a solo project.
To compensate, I created higher-fidelity artwork for the UI and promotional material while keeping the in-game sprites much simpler, giving the game a distinctive retro aesthetic. NihongoGamer (see here), for example, described the game as having a "revitalized Game Boy Color aesthetic." While I love that style, it introduced an important gameplay problem: visual clarity.
During playtests and public demos, some players struggled to distinguish fighters from the background. That's an issue in any game, but it's especially problematic in a fighting game, where players rely on instantly recognizable silhouettes and quick visual reads during fast-paced combat.
The higher-fidelity portrait sprite of Project Berserk's 1st character, Renegade
The lower fidelity sprite of Renegade used in game.
Recreating every sprite at a higher resolution simply isn't practical. Not only would it require reanimating hundreds of frames, but it also wouldn't be the best use of my time as a programmer. Instead of replacing the art, I began looking for a scalable solution that would improve readability while preserving the game's existing style.
How do others solve this problem?
Contrast between characters and scenes is a necessity for several mediums, games are no exception. Hand drawn animations, in my opinion, showcase this best, where scenes are spliced into separate layers then composited into the final scene. Typically, the scenes furthest back will have rounded edges and desaturated colors, with vice versa being true for those layers in the front. Plenty of 2D games with a handdrawn animation incorporate this, such as Cuphead, where characters and enemies have a presence thanks to think black outlines and brighter colors. Conversely, we can see that my game has nearly identical saturation levels for fighters and their backgrounds, and the sprites are so low-fidelity that a pixel black outline was not included.
A clip of Cuphead noting the visual separation between foreground and background elements.
So to summarize the options I have to address this issue, I can:
Redraw all of my sprites to a higher fidelity
Decrease the saturation of background elements
Apply some sort of dark/black outline to my fighters
We've already discussed that option #1 is a no-go, and although option #2 is realistic, I think it would be interesting to do #3 but with a gradient outline instead of a hard pixel outline. I already use black gradients in the UI, so if there is a way to generate these gradients for any sprites in real time, that would be a clean and scalable way to split the fighters off from the background!
An intro animation in Project Berserk with visible gradients at the top and bottom of the screen.
The solution? Shaders!
Shaders do exactly what I just described, and if I can find a way to develop one to create a blur effect, I can then apply the resultant material to any sprites that require it, following a pretty extensible and scalable improvement to my game's visuals.
There is a bit of a problem, though; there is not a streamlined way to make a blur (a bit of a weird ommission in my opinion), so I had to do research on how to make a custom shader.
I researched PLENTY of videos and tutorials, and it was still a struggle to find exactly what I needed. I researched a lot on glow, outlines, and anything that could simulate what I was trying to achieve. After plenty of hours, I found that the best approach was to have a copy of the sprite one layer behind, and this would both be transparent black and have the blur shader. This is a good approach because the blur can be a separate shader from whatever shaders are placed on the main sprite, which is necessary as the sprite already use a shader for color palettes (a facet worthy of a future devlog!). Actually achieving the blur was a different story, so this is where I needed to learn a LOT more about shader graphs.
What follows is mostly me following tutorials, but I wanted to go over the process to reflect on what I learned here.
Essentially, the shader graph is going to create a Gaussian blur. To start, we split up our outline into a square of even cells (in this case, a 3x3 square because it is a good amount of blur variation without sacrificing too much performance), and each of them represent a certain part of the final blur effect. Each square gets a copy of the shape that is displaced by a certain U and V to align with that cell, but including 9 different copies of the same shape would end up being far too opaque! Because of this, we need to multiply the transparency of each copy by a fractional scalar, and by distributing the scalars as seen on the left (notice how they accumulate to 1), the center of the blur will have the most opacity and the edges the least!
From there, you can then multiply all of the copies opacities (the 'A' for alpha channel) by a set amount to represent the blur opacity, and the amount of U and V used to shift copies over to their respective cells is set by the blur radius. Lastly, the color of each copy is set by a blur color. Add them up into our final product, and that's how you make a blur in Unity!
Take the base alpha, multiply it by the blur radius, and split it up into Us and Vs.
Pass the Us and Vs into each of the 9 copies being multiplied by the gaussian blur scalar.
We then need to ensure that we add up all of the copies and then divide by 16 (we also could have multiplied each cell by their scalar divided by 16)
Multiply the result by the blur opacity (and the opacity of our blur color)
Set that to the alpha and the blur color to the base color, and there we go!
The resultant shader graph.
This now works for static images, and when placing it onto the higher-fidelity portraits, we can already see this drastically improves clarity!
Without blur
With blur
I can now place it onto our in-game sprites as well! However, the shader does not automatically animate the blurred sprite. Recall that the blur is an entirely separate GameObject, so I could set up the blur sprites for every single animation... for every single fighter... and projectile... that's a lot of work isn't it?
Here's a more sophisiticated approach; why don't we have a separate script that automatically generates a GameObject with a sprite set to this blur material and keeps said sprite updated with a source sprite? This way I do not need to add in work for 100+ animations, I can just add the script to each fighter and projectile!
This script is OutlineAnimator, which will need the SpriteRenderer of the newly generated outline, the SpriteRenderer of the source we are mimicking, and the material (in this case, our blur shader will be set here).
On Awake() we call CreateOutline(), which creates a GameObject parented to whatever GameObject the OutlineAnimator component is added to, sets its values up, and adds in the Material!
On Update(), we call UpdateOutline(), which just matches up all of the necessary attributes to that of the source SpriteRenderer.
From there, we can now see that our blur effect works on our fighters, and wow! It surely does make a difference. I can't even imagine the game without this now!
Without blurs
With blurs!
So now this blur can be applied to every sprite and changed scalably, we now have an extensible solution to the visual clarity issue!
So what's the takeaway?
Visual clarity is a necessity for any game, but especially so for fighting games, and seeing a lack of it encouraged me to find industry standard solutions. It then took a decent amount of research to develop a custom shader graph, but this solution lets me easily set all foreground elements as visually distinct from the background, a great exemplar of working smarter, not harder! And after attending Southeast Game Exchange in July 2026, player feedback does note an improvement on visual clarity! I highly recommend using these tools for any pixel art game or art piece.
This project reinforced an important lesson for me: the first solution isn't always the best one. Rather than manually editing hundreds of animations, I stepped back and asked how the problem could be solved once instead of repeatedly. By separating the blur into its own shader and automating it with OutlineAnimator, I ended up with a solution that's reusable, maintainable, and easy to tweak as the project evolves.
From there, I can probably take what I have learned in a further direction. I mentioned in the 'Research' section that there are multiple strategies to make foreground elements stand out, but this devlog was only incorporating one of them into my game. Perhaps I will look into post-processing filters to decrease background saturation, and there may be a less process-intensive way of rendering the blurs, but this addresses the core issue for now! I am excited to see what other improvements will come along to this game's visuals, a facet I originally wrote off but now have a stronger direction for!