Day 136 — Creating Blood Splat Effects

Hey and welcome!

In this one we’re going to be looking to add a blood splat effect that comes from our enemies whenever we shoot them which will work as a handy visual cue for the player to recognise that they’re doing damage.

If you have Filebase there’s a handy Blood Splatters pack that you can download and import into Unity to make use of:

If not then you can create and find your own, these are just 2D sprites with animations tied to them using a spritesheet. Be sure to create an empty object in your scene to attach your first frame of the blood splatter sprite to as well as an animator component that holds your animation and then prefab this so that it can be instantiated.

When you’re happy with your prefab go ahead and add in this extra bit of code to your Shoot script:

[SerializeField]
private GameObject _bloodSplat;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 center = new Vector3(0.5f, 0.5f, 0);
Ray rayOrigin = Camera.main.ViewportPointToRay(center);
RaycastHit hitInfo;
if (Physics.Raycast(rayOrigin, out hitInfo))
{
Debug.Log("Hit: " + hitInfo.collider.name);
Health health = hitInfo.collider.GetComponent<Health>();
if (health != null)
{
Instantiate(_bloodSplat, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
health.Damage(50);
}
}
}

As you can see here all we’re doing is creating a reference to our Blood Splat prefab that we add through the inspector and then instantiating when we damage the enemy when our ray collides with it. You’ll notice that instead of using Quaternion.identity which sets the rotation of the blood splatter to the default we’re using Quaternion.LookRotation(hitInfo.normal) which sets the rotation of the prefab perpendicular to the point that our raycast collides with which causes the blood splat to face the player.

One thing you’ll notice though is that our blood splats are spawning as our ray collides with the sphere collider surrounding the enemy rather than the enemy itself. To go about fixing this we’re going to separate our collider from the enemy object by creating an empty object in our enemy and copying the sphere collider component and choosing the “Paste Component as New” option when you right click the transform component in your empty object.

Delete your sphere collider in the Enemy object and then rename your empty object to AttackTrigger. Now create 2 new layers called Enemy and another called AttackTrigger and take note of which layer Enemy is on. Next head over to your EnemyAI script and delete both of your OnTrigger functions and replace them with these:

public void StartAttack()
{
_currentState = EnemyState.Attack;
}
public void StopAttack()
{
_currentState = EnemyState.Chase;
}

In order to make use of these go ahead and create an AttackTrigger() script and add in this code:

private EnemyAI _enemyAi;void Start()
{
_enemyAi = GetComponentInParent<EnemyAI>();
if (_enemyAi == null)
{
Debug.LogError("The enemy AI is null");
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
_enemyAi.StartAttack();
}
}
private void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
_enemyAi.StopAttack();
}
}

Since we’ve separated our collider from our enemy we need to also separate our OnTrigger function too as you can see above.

Lastly we need to do one more edit to our Shoot script:

[SerializeField]
private GameObject _bloodSplat;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 center = new Vector3(0.5f, 0.5f, 0);
Ray rayOrigin = Camera.main.ViewportPointToRay(center);
RaycastHit hitInfo;
if (Physics.Raycast(rayOrigin, out hitInfo, Mathf.Infinity, 1 << 7))
{
Debug.Log("Hit: " + hitInfo.collider.name);
Health health = hitInfo.collider.GetComponent<Health>();
if (health != null)
{
Instantiate(_bloodSplat, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
health.Damage(50);
}
}
}

Here we’re giving our Raycast some more specific instructions which is the distance the ray can travel and the layer that it can interact with. We use Mathf.Infinity here so that our ray’s distance is infinity and we’re using bit shifting that we learned about in the mobile game in order to make sure that this ray can only collide with layer 7, or in other word the Enemy layer.

With all that done you should have a pretty good blood splat vfx working for you! We’ll work on tidying it up a bit more when we add in our zombie enemies but with the way we’ve done it these blood splats will show up when we shoot different parts of the body like the legs or arms.

Hey and welcome! My name is Connor and my goal here is to put out a daily post for a full year about my game development journey.