Bloop Boop is a 2D physics based arcade game for android, boop your bloop to the end of the levels!

Post news Report RSS Splatter Effects In Bloop Boop

This blog is by another of our programmers and will cover the shader creation for the splatter effect in our game Bloop Boop.

Posted by on

A part of Bloop Boop core aesthetic is in its splatter effects and bulk of the work was done thought the use of stencil shaders. The main idea around this is to have each splatter image with a stencil shader and reference number in the shader itself and have each hazard have a stencil mask shader that checks for the reference number of the splatter images. This then causes the splatters to be invisible to the camera but when the are over a hazard, the stencil shader will render the splatter pixels over the hazard pixels causing the splatter effect.

How this works is that I took a standard unity shader and added in the stencil elements. Stencil doesn’t require much in order for it to work, normally you only need a reference number “Ref”, a comparison “Comp”, what to do if the condition passes “Pass” and what does it do when it fails “Fail”.

SubShader
{
Stencil{
Ref[_StencilVal]
Comp equal
Pass keep
Fail keep
}

These stencil properties are placed just before the pass as it needs it to for when the condition is true or false. What these properties do are, stop the rendering of any object with the stencil shader until it is being viewed by its mask, a stencil shader that checks for a specific reference number. When the mask is over the the stencil object it then its pass condition will become true and this is where you can alter how the object is rendered. In this case I am telling it keep the pass below which is telling it render the object normally.

Stencil
{
Ref [_StencilVal]
Comp always
Pass replace
}

However there is an issue with this shader, due to how unity renders an image, it often reveals hidden detail or creates extra pieces to fill in the gaps of a texture, based on its alpha. This often causes the texture too look terrible and not what we wanted.

rend1

To solve this I started looking into Alpha Cutout and how it worked. In short, alpha cutout is used to determine how far a pixel needs to be along the alpha channel needs to be before it render, else it is cutout and doesn’t render. Since I was using a surface shader to start with, I decided to use an if statement in surface function that would turn the alpha of a pixel to 0 if it was too low.

//Properties
_Cutoff("Alpha cutoff", Range(0,1)) = 0.5

//Sub Shader
float _Cutoff;

//Surf Funtion
if (c.a < _Cutoff)
o.Alpha = 0
else
o.Alpha = c.a;

This however didn’t work and instead of wasting more time on trying to figure out why I decided to rewrite the shader from a surface to a vert/frag shader instead as I could use the discard function in the fraf funcion to not render that pixel if its alpha is to low.

//Properties
_Cutoff("Alpha cutoff", Range(0,1)) = 0.5

//SubShader
float _Cutoff;

//Frag Function
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv) * _Color;

if (col.a < _Cutoff)
discard;
return col;
}

What this does is check each pixel in the frag function for its alpha value, and it’s lower then the the _Cutoff value that I set then discard it and not render it. Now the image looks like this:

rend2.png

Now that the splatters and being rendered correctly, they now needed to be instantiated onto the screen. Since they need to appear on object that the bloop will hit, I decided to use OnCollisionEnter2D() to instantiate one of the splatter images at the players position whenever the bloop hits something and change the colour of the splatter to match the current bloop.

void OnCollisionEnter2D(Collision2D coll)
{
GameObject splatter = Instantiate(splat[index], transform.position, Quaternion.identity) as GameObject;
splatter.GetComponent<SpriteRenderer>().material.color = curColor;
splatter.transform.parent = coll.transform;
}

Without the shader:

without

With the shader:

with

This is the shader that I made and the processes I went thought to make it, this shader acts as are core aesthetic to the game and is easy to tweak and work with when implementing new splatter images, as its a list. One final note is that since the splatter objects are just instantiated and are an actual object on the screen with a parent, more then one hazard could pick them up, expectantly spinning objects that would rotate the splatter image and reveal it on other objects. This however was solved by someone else using some screen layering tricks to stop other hazards from seeing them.

After this was all done, the next issue was scale. On objects that are stretched and distorted, it will affect the splatter itself as it is a child of the object. This is a simple solve as I just instantiated another object with the splatter that will hold the splatter at a normal 1,1,1 scale. Then parent the holder to the object the bloop hit.

void OnCollisionEnter2D(Collision2D coll)
{
//
//extra stuff
//
GameObject splatterHolderCl transform.position, Quaternion.identity) as GameObject;

splatterHolderClone.transform.SetParent(coll.transform);

splatter.transform.SetParent(splatterHolderClone.transform);
}

The last major issue was changing the color of the splatter when the bloop pick’s up a power up. This was solved by creating an extra color variable in the Bloop class that would the base bloop color of that Bloop. I then get the material color of the powerup material and add that as a new color which is then temporarily used on the Bloop material.

public void UpdateColor()
{
Color sc = Color.black;
List<Powerup> powerups = PowerupHandler.GetPowerups();

foreach (Powerup p in powerups)
{
sc += p.GetComponent<Renderer>().material.color;
}

sc /= powerups.Count + 1;
sc += curBall.splatterColor;
sc.a = 1.0f;

splatterColorProper = sc;
}

That’s how I did the splatter system in Bloop Boop

color.png

Thanks for reading, Luke :)

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: