Simple hit indicator system using Full screen shader in Unity

Hello everyone, welcome back.

Today, we will look into how to create directional hit indicators with full screen shader.



Intro

For this I am using Unity version 2022.3 and I have project setup in URP.

I have gone ahead and created full screen shader and material and also applied it to our renderer feature.

I have also set the Inject point to Before rendering post processing.

If you don't know how to setup a full screen material, Check out this blog!

Creating the shader

In the shader graph, first go to Graph inspector, in the Graph settings select the Blend mode to Additive.

Now let's create a Texture2D property, call it MainTexture, I have cooked this splash texture which I will set as a default, then check this Use tiling and offset, and drag it in.

In order to work with the texture we need to sample it, so create a Sample texture 2D node.

Feed our texture into it.

Let's create a Color property, call it BaseColor, set the mode HDR, give default red color, drag it in.

We will multiply our BaseColor with our MainTexture.

Then feed our Multiply node's output into BaseColor block.

We also want to control the opacity of our effect so we can fade in and fade out,

For that let's create a Float property, call it Alpha, give default value of 1, set the mode to Slider with range 0 to 1. Drag it in.

We will multiply our Alpha with our MainTexture.

Then feed the Multiply node's output into the Alpha block.



We will rotate our texture in the direction of incoming attacks, for that let's create a Rotate node, keep the Unit to Radians because we will calculate the angle in radians.

Then take Rotate node's output and feed it into Sample texture 2D node's UVs.

We will feed the angle from the script so let's create another Float property, call it Angle, drag it in and feed it into Rotate node's Rotation.

We will rotate our texture by calculating angle between our player's forward direction and the direction of the attack.

So let's create 2 Vector3 properties, I will call them PlayerDirection and HitDirection, Drag them in.

We will calculate the angle using dot product, so let's create a Dot product node.

Feed our directions.

Dot product returns the values between -1 and 1. It returns 1 for parallel vectors, 0 for perpendicular vectors and -1 for opposite vectors.



Then to get the angle we will take our Dot product node's output and feed it into Arccosine node.



Arccosine is the inverse of cosine operation, like we pass angle in cosine to get the value, similarly we can pass that value in arccosine to get the angle, but in radians.

Then we will split both our directions using Split node.

Then we will create Comparison node, It will compare input A with input B based on the condition.

We will take X of our HitDirection and feed it into A.

Then take X or our PlayerForward and feed it into B, and we will check if A is greater than or equals to B.

Then we will take our Comparison node's output and feed it into Branch node.

Think of Branch node as IF statement.

So if our HitDirection's X is greater than or equals to our PlayerForward's X, we will pass -1 otherwise will pass 1.

Finally we will multiply it with our angle from Arccosine node.

Now take our Multiply node's output and feed it into Rotate node's Rotations.

That's it for our shader part, It will look something like this at the end.



Example to calculate the angle

Following HitVisuals script is an example on how we can pass our directions and alpha to our full screen material.

I am using a tweening library from asset store called LeanTween by dented pixel for this script.

using UnityEngine;

public class HitVisuals : MonoBehaviour
{
[SerializeField] private Material fullScreenMaterial;

[SerializeField] private float fadeTime = 2f;
[SerializeField] private AnimationCurve easeCurve = AnimationCurve.Linear(0, 0, 1, 1);

private void OnCollisionEnter(Collision collision) {
if(collision.gameObject.TryGetComponent(out Projectile projectile)) {
CalculateHitDirection(projectile.transform.position);
}
}

private void CalculateHitDirection(Vector3 projectilePosition) {
Vector3 direction = projectilePosition - transform.position;
Vector3 directionParallelToGround = Vector3.ProjectOnPlane(direction, Vector3.up);
directionParallelToGround.Normalize();

SetMaterialProperties(directionParallelToGround);
}

private void SetMaterialProperties(Vector3 direction) {
fullScreenMaterial.SetVector("_HitDirection", direction);       fullScreenMaterial.SetVector("_PlayerForward", transform.forward);              LeanTween.cancelAll();       float from = 1;      float to = 0; LTDescr tween = LeanTween.value(gameObject, from, to, fadeTime);       tween.setEase(easeCurve);      tween.setOnUpdate(delegate (float currentFadeValue) {         fullScreenMaterial.SetFloat("_Alpha", currentFadeValue);       }); } }

In our script, we are taking reference of our material, then the fadeTime and AnimationCurve is used to fade in and fade out our effect.

In OnCollisionEnter(), we are checking if our player is collided with projectile, if so we are passing our projectile's position into CalculateHitDirection().

In CalculateHitDirection(), we are getting the direction vector, then I made it parallel to the ground and normalized it, then we are passing the normalized direction to SetMaterialProperties().

In SetMaterialProperties(), we are passing our hit direction, and our player's forward.

Then we will animate the alpha with LeanTween, so first we will cancel all the tweens running on the gameobject, then we have created a Value tween which will run on this gameobject, and goes from 1 to 0 in the specified fadeTime.

Then set the interpolation based on our AnimationCurve.

And this delegate might look intimidating at first but here all we did is, while our tween is updating we are getting the new alpha value, and pass it to our material.

In Unity editor, I have set the animation curve so that our effect quickly fades in and then slowly fades out over the course of 2 seconds.

Trouble following along? Check out this video!



Thank you so much for reading!

Comments