Spawn particles depends on materials

Hi everyone, here is my “problem”. I have a wooden shield and a metal weapon, so I have two materials. What I would like to do is play particles depend on the material hit.

Exemple:

  • A metal weapon hit a shield, I want my shield to play the wooden patricles but my weapon nothing
  • A metal weapon hit a metal shield, I want some spark particles play from each one

I have some ideas to do that but nothing very practical that is why I am looking for some help.

The best thing could be a matrix like the Physic matrix in Project Settings but I think it is not easy to do.

Thank you in advance.

So, what have you tried? This seems pretty straightforward. Just check for a collision, and check both object’s Material.

Easiest is to have a weapon hit particle effect controller that does all this in one simple prefab.

Steps to building it:

  • make it a Unity singleton so it just comes into being, perhaps loaded off a prefab (see below - Note #1)
  • it would contain ALL the types of particle effects you can do
  • when something hits something, have it pass into this effect thing:
    ------> what it is
    ------> what it hit
    ------> where it hit
  • then the effect thing just makes the appropriate spark (see below - Note #2)

As far as identifying the materials, attach a tiny “material effect identifier” to each collider, and use GetComponent() to dig that out of the colliders you get back from the collision event, and if you don’t find one, assume something.

Note #1:

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

Note #2:
ParticleSystems (particlesystems): emitting in various positions in space (no play / stop):

And the advice you didn’t ask for:
I wouldn’t do that. You are putting yourself into a position where you need to write code whenever you put a material on a thing. And you aren’t separating two important things: looks and function. You shouldn’t write functional code based on looks. It is only the other way around (you write function to change the looks). What I would do is that put a “tag” (you don’t have to put real tag on them, but some component or an enum in a script) on your items and compare those.
What allows you to do is this: you can have “bronze” material but “metal” tag. You can have “mithrill” material but “supermetal” tag. And then you can have “iron” material but still “metal” tag and you can have “damascus” material but still “supermetal” tag. Or whatever. This means, you lowered your comparisons (decision space) from 16 to 4. And no matter what materials you put on your items you only need the tags to decide what to do. Not to mention you’re opening up the possibility to have wooden weapons behaving like metals and so on. Like “magic” :smile:

Thank you for your answers. To complete some of your questions, this is the way I check collisions, I don’t know if it’s the best way but I find it very useful for now and quite simple.

I made a interface called OnHit:

using UnityEngine;

public interface IOnHit<T> {
    void OnHit(T obj);
}

Then in the player (human) class, I have added the OnTriggerEnter function like that:

public class Human : MonoBehaviour, IOnHit<WeaponMelee>, IOnHit<Projectile> {

// So when something hit the human, it checks if the other implements the IOnHit interface of its class in this case, Human class
    protected void OnTriggerEnter(Collider other)
    {
        IOnHit<Human> hit = other.gameObject.GetComponent<IOnHit<Human>>();
        hit?.OnHit(this);
    }

    public void OnHit(WeaponMelee weaponMelee)
    {
        Damage(weaponMelee);
    }

    public void OnHit(Projectile projectile)
    {
        Damage(projectile);
    }
}

I have done the same thing with the shield collider and weapon collider :

public class ShieldCollider : Collider<Shield>, IOnHit<WeaponMelee>, IOnHit<Projectile> {

    public ParticleSystem particles;

    #region OnHit

    protected override void OnTriggerEnter(Collider other)
    {
        base.OnTriggerEnter(other);

        IOnHit<Shield> hit = other.gameObject.GetComponent<IOnHit<Shield>>();
        hit?.OnHit(item);
    }

    public void OnHit(WeaponMelee weaponMelee)
    {
        item.InstantiateHolderEffects();
        item.InstantiateEntityEffects(weaponMelee.holder);

        particles?.Play();

        item.holder.PlayAnimation("TakeAttack");
        item.holder.stamina.Remove(weaponMelee.weight, false);
        item.RemoveDurability(weaponMelee);

        if (item.durability.value <= 0)
            item.holder.UnequipShield();
    }

    public void OnHit(Projectile projectile)
    {
        item.InstantiateHolderEffects();
        item.InstantiateEntityEffects(projectile.caster);

        particles?.Play();

        item.holder.PlayAnimation("TakeAttack");
    }

    #endregion
}
public class WeaponMeleeCollider : Collider<WeaponMelee>, IOnHit<Human>, IOnHit<Shield> {

    public ParticleSystem particles;

    #region OnHit

    protected override void OnTriggerEnter(Collider other)
    {
        base.OnTriggerEnter(other);

        IOnHit<WeaponMelee> hit = other.gameObject.GetComponent<IOnHit<WeaponMelee>>();
        hit?.OnHit(item);
    }

    public void OnHit(Human human)
    {
        item.InstantiateHolderEffects();
        item.InstantiateEntityEffects(human);

        item.RemoveDurability(weaponDamages);

        if (item.durability.value <= 0)
            item.holder.UnequipWeapon();
    }

    public void OnHit(Shield shield)
    {
        item.InstantiateHolderEffects();
        item.InstantiateEntityEffects(shield.holder);

        item.collider.Enable(false);

        particles?.Play();
    }

    #endregion
}

For the particles, I have added a public variable where I put the particules system in the prefab and play it depend on what the collider hit.

The reason I did this, is because it allows me to custom particles for each weapon and shield since some of them are very differents which a particulesManager can’t. And I can manage this in the weapon or shield prefab instead of code which is easier in my opinion.

Maybe there is a better way.

I hop it’s clear enough to understand what I have done.

If you have any suggestion, I would happy to read them.