Unity Version:

  • Unity 2015.6.0f3
  • Unity 2017.3.1f1
  • Unity 2018.1.5f1

Uses:

  • To Damage an enemy
  • To damage a group of enemies (like a grenade)
  • Moba Style Games
  • FPS Games
  • Projectile Type weapons (Pistols, Bow and Arrow, grenades, etc.)

Summary

This is a follow up to the Damage System. If you haven’t already seen it, please take a look at the Damage System Tutorial, as we will be modifying the code from the Damage System to add the missing Bullet Effects, after a collision has occurred. Other then that, I’ve noticed that some people like to just call a single effect or bullet hole immediately. However, you can have varying degrees of effects that can occur. Such as ‘Plaster’, ‘Brick’, ‘Blood Effects’, etc. and this can differ depending on who or what the bullets target actually is.

This is also used in the UYD Project.

  • NOTE: This is a base implementation, that’s meant to be modifiable to a degree. I Also recommend adding an object pooling system for your projectiles as well, as it’s not covered in this tutorial.

Setup

  • Scripts:
    • DamageSystem/ImpactInfo.cs
    • DamageSystem/MaterialType.cs
    • DamageSystem/ImpactElements.cs

  • BulletHoleSpawnHandler.cs

Process:

To start off the Build, we need to know the types of materials we will be using, and the Effects for this material. Basically the Prefab of the Particle Effect, and the label of this effect.

 

The Interface code is meant to be simple, just covering the necessities that is needed.

ImpactInfo:

[System.Serializable]
public class ImpactInfo
{
   public MaterialType.MaterialTypeEnum MaterialType;
   public GameObject ImpactEffect;
}

Now for my labels, I am storing everything as an Enum in MaterialType.cs to keep things strong typed instead:

MaterialType:

public class MaterialType
{
   [System.Serializable]
   public enum MaterialTypeEnum
   {
     Plaster,
     Metal,
     Folliage,
     Rock,
     Wood,
     Brick,
     Concrete,
     Dirt,
     Glass,
     Blood,
     Water
   }
}

The best way I can describe this next part is sort of a database table that contains the ImpactElements, and allows you to access the Impact Elements that you need, and create copies of the elements as they are needed.

  • NOTE: Since bulletHoles will be created and disposed constantly, I recommend having an object pooling solution for your bullet holes, and have a GetImpactElement method signature from your ImpactElements, Or if you need too, have a seperate object pooling reference, and have your ImpactElement.cs act as your holder, or table.

ImpactElement:

[CreateAssetMenu(fileName ="ImpactElements", menuName ="FuzzyFiles/Helper/FPSFireManager", order=1)]
public class ImpactElements : SerializedScriptableObject
{
   [TableList]
   public Dictionary<int, ImpactInfo> ImpactElementsList;
}

Now I do have a PoolManager System that controls the amount of bulletholes being used, and the amount available for the game, as they are being used. The Code Snippet below reflects how it is being used, but does not actually show the PoolManager code. However, the base idea is there for use.

BulletHoleSpawnHandler:

public class BulletHoleSpawnHandler : MonoBehaviour
{
   public SpawnHandlerDetails handlerDetails;
   protected int _spawnHandlerKey;

   public ImpactElements impactElements;
   public Dictionary<int, SpawnObject> spawnObjects;

   public virtual void Start()
   {
      spawnObjects = new Dictionary<int, SpawnObject>();
      _spawnHandlerKey = PoolManager.CreateNewSpawnHandler(this.gameObject, handlerDetails);
      AddSpawnObjectsToHandler();
   }
   protected void AddSpawnObjectsToHandler()
   {
      if(impactElements.ImpactElementsList.Count > 0)
      {
         foreach(var element in impactElements.ImpactElementsList)
         {
            Debug.Log("Adding SpawnObjects to Handler " + element.Value.ImpactEffect.name);
            SpawnObject obj = PoolManager.AddGOToSpawnPool(element.Value.ImpactEffect, _spawnHandlerKey);
            spawnObjects.Add(element.Key, obj);
         }
      }
   }

   //Get the correct bulletHole from the list of BulletHoles.
   public SpawnObject GetBulletHole(Fuzzy.DamageSystem.MaterialType.MaterialTypeEnum materialType, Transform location)
   {
      KeyValuePair<int, ImpactInfo> value = impactElements.ImpactElementsList.FirstOrDefault(x => x.Value.MaterialType == materialType);
      var refObj = spawnObjects.FirstOrDefault(x => x.Key == value.Key);
      return PoolManager.SpawnGOAt(refObj.Value, location);
   }
}

Finally we need to modify our OnCollisionEnter from our DamageListener script to include, and spawn the bulletholes at the corresponding point of contact. This calls the BulletHoleSpawnHandler.GetBulletHole. If the bulletholes don’t exist, it creates them, and if they do exist, it re-uses them from the object pooling system.

DamageListener:

public MaterialType.MaterialTypeEnum typeOfMaterial;
public BulletHoleSpawnHandler spawnHandler;

public void OnCollisionEnter(Collision collision)
{
   DamageMessenger messenger;
   ContactPoint contactPoint = collision.contacts[0];

   messenger = collision.gameObject.GetComponent(DamageMessenger>();
   if(messenger != null)
   {
      if(messenger.damageMessage.sender.GetInstanceID() != colliderOwner.GetInstanceID())
      {
         if(OnCollision != null)
         {
            OnCollision.Invoke(messenger.damageMessage);
         }

         //Spawn BulletHole @ Position
         SpawnObject bulletHole = spawnHandler.GetBulletHole(typeOfMaterial, this.transform);
         if(bulletHole != null)
         {
            bulletHole.trasnform.position = contactPoint.point;
            bulletHole.trasnform.LookAt(contactPoint.point + contactPoint.normal);
         }
         //this just disables the object, as its apart of the pooling system.
         messenger.DestroyObject();
      }
   }
}

Hope this helps!

Happy Coding!