How to code BOTW or Divinity Original Sin2 like Chemistry System?

Hi everyone! I’m trying to figure out how to code a chemistry system, what’s the better apporach to do this? Also I would be glad to know how would experienced developer handle these things?

  1. interface interaction. if object A which extend fire interface, collide with object B, if object B extend burnable interface, burn.
    Fire
public interface IActor
{
    public GameObject gameObject { get; }
    public Transform transform { get; }
}

public class SimpleFire : MonoBehaviour, IActor, IFire
{
    public bool Interact(IActor actor, IActor passive)
    {
        var burnable = actor.gameObject.GetComponent<IBurnable>();
        if (burnable != null)
        {
            return burnable.Interact(actor, null);
        }

        return false;
    }

    private void OnTriggerStay2D(Collider2D other)
    {
      
        Debug.Log("trigger");
        other.GetComponent<IBurnable>()?.Interact(this, null);
    }
}

Burnable

public interface IInteraction
{
    bool Interact(IActor actor, IActor passive);
}

public interface IBurnable : IInteraction{}


public class SimpleBurnable : MonoBehaviour, IBurnable
{
    public bool Interact(IActor actor, IActor passive)
    {
        var fire = actor.gameObject.GetComponent<IFire>();
        if (fire == null)
            return false;

        var ownFire = GetComponent<IFire>();
        if (ownFire == null)
        {
            return Burn();
        }
        else
        {
            return false;
        }
    }
}
  1. Tag/Mask comparison. If object A has fire tag, and object B has burnable tag, when they collide, burn. Actually I’m using flag enum to define the tags belongs to the gameobject, also the requirement of the interaction.
    I guess this one might better than the first one because it save a lot of work to do comparison by hand foreach new kind of interaction.
    Code
[Flags]
public enum ChemistryMask
{
    fire = 1,
    burnable = 2,
    water = 4,
}

public interface IChemistryLink
{
    ChemistryMask Mask { get; }
    DetectorType RequireDetectorType { get; }
   
    public bool SameDetector(DetectorType detectorType) => detectorType == RequireDetectorType;
    public bool Interact(ChemistryMask aMask, ChemistryMask bMask, GameObject a, GameObject b);

    public bool Match(ChemistryMask a, ChemistryMask b)
    {
        return Mask == (Mask & (a | b));
    }
}

public bool TryLink(ChemistryMask aChemistryMask, ChemistryMask bChemistryMask, GameObject a, GameObject b, DetectorType detector)
{
    foreach (var chemistry in ChemistryNodes)
    {
        if (!chemistry.Match(aChemistryMask) && !chemistry.Match(bChemistryMask))
            continue;

        if (chemistry.Links == null) continue;
        foreach (var link in chemistry.Links)
        {
            if (!link.Match(aChemistryMask, bChemistryMask))
                continue;
            if (!link.SameDetector(detector))
                continue;

            return link.Interact(aChemistryMask, bChemistryMask, a, b);
        }
    }
   
    return false;

}
  1. This one is quite Date-Oriented I guess, it just change the internal value of a object, and use that value to decide whether is should burn. it seems amazingly simple… I can give everything interactive those kind of values.
    Temperature
public class Temperature : MonoBehaviour
{
    public float Value;
    private bool unchanged; // Fire is unchanged.
    private float changeSpeed;
    // TODO Min Max limit 
    private bool Change(float target)
    {
        if (unchanged) return false;
        var positive = Math.Sign(target - Value);
        Value += positive * changeSpeed * Time.deltaTime;
        return true;
    }
    private void Update()
    {
        var temperature = GetComponent<Temperature>();
        if (temperature.Value > 100)
        {
            // burn;
        }
    }

    private void Balance(Temperature other)
    {
        Change(other.Value);
    }

    private void OnTriggerStay2D(Collider2D other)
    {
        if (other.TryGetComponent<Temperature>(out var temperature))
            Change(temperature.Value);
    }
}

I want to know which one is better? From both the aspect of performance and extensible. Are there any ptifalls?
Hope I convey my question well, if anyone can shed some insight for me, I would be very appreciated! Thanks!

Kinda depends on how you’re extending it, doesn’t it?

It is impossible to predict future needs with perfect accuracy.

There are ALWAYS pitfalls.

Does your code work? Ship it, or move on to the next steps.

If you encounter issues adding more features, refactor appropriately.

DO NOT OPTIMIZE CODE JUST BECAUSE… If you don’t have a problem, DO NOT OPTIMIZE!

If you DO have a problem, always start by using the profiler:

Window → Analysis → Profiler

Failure to use the profiler means you’re just guessing, making a mess of your code for no good reason.

https://discussions.unity.com/t/841163/2

Notes on optimizing UnityEngine.UI setups:

https://discussions.unity.com/t/846847/2

2 Likes

Thanks for your teaching! I guess I’m thinking to much about design pattern and clean code…