Skip to content

chonky-gator/gator-logic

Repository files navigation

GatOR Logic

Unity package to help with the development of games and apps. It features interface references on the inspector, a button to create a ScriptableObject without the need of [CreateAssetMenu] and a Dependency Injection pattern for unity objects.

Note: If enough people require only one of the features, they will be eventually moved to their own separated package

Features

ReferenceOf<T> - Serializing and displaying interface types on the inspector

Since we cannot serialize a interface reference by itself and show it on the inspector with [SerializeField] private IExample example;, this package includes a generic struct ReferenceOf<T> that uses Unity's 2020.3 features like generic serialization and polyphormic serialization.

/*
* Any of these fields will be serialized and shown into the inspector, the same way it would
* happen with an UnityEngine.Object reference
* Of course, it does works as well if you replace "[SerializeField] private" with "public"
*/
[SerializeField] private ReferenceOf<IHealth> health;
private IHealth Health { get => health.Value; set => health.Value = value; }
[SerializeField] private ReferenceOf<IWeapon> weapon;

private void AttackWithWeapon()
{
  // The only downside is that you need to dereference using .Value
  weapon.Value.Attack();
}

public void EquipWeapon(IWeapon weapon)
{
  // You can update the reference value as well
  // It works even if the weapon is a UnityEngine.Object or a serializable/non-serializable value
  this.weapon.Value = weapon;
}

public void Damage(int amount)
{
  // You can create a get/set property to avoid writing .Value every time
  Health.Damage(amount)
}

The value in the inspector is shown with the type <ITest> in this case and we can select the concrete type that we want to assign to.

ReferenceOf example with Unity Objects

Alternatively, it will also accept any class that implements that interface and has [Serializable]

ReferenceOf example with custom serializable class

Any reference that implements that interface can be assigned through that interface seamlessly. It's really useful when you want to unit test with a mock object.

[CreateAssetButton] - Easily create new ScriptableObjects when needed

This utility property drawer allows us to add the [CreateAssetButton] attribute to a field that references any ScriptableObject, this allows us to skip adding [CreateAssetMenu] to the ScriptableObject class to avoid dealing with too many assets cluttering the Assets>Create menu and creating the assets the moment we need them.

CreateAssetButton on the inspector

[CreateAssetButton] public TestSettings settings;

IConstructable - Define dependency injection functions with Unity objects

Dependency Injection is a common pattern to help us decouple code and explicitly define dependencies. Normally the DI would be found in the constructor, but because Unity objects can't be constructed because they are already instantiated, we have to pass the dependencies to a method that initializes everything.

Luckily, this package has some interfaces and exceptions to help with this. You can add IConstructable<...> and this.ThrowIfAlreadyConstructed() which help us repeat this design pattern.

// IConstructable 
public class Enemy : MonoBehaviour, IConstructable<IHealth, DifficultySettings>
{
  // I would reccommend to use private serialized fields to only allow the constructor
  // to modify these values.
  [SerializeField] private ReferenceOf<IHealth> health;
  [SerializeField] private DifficultySettings difficulty;

  // Setting this IConstructable property with a private set is the reccommended way
  // so we can set it to "true" when we construct the object
  public bool IsConstructed { get; private set; }
  
  public void Construct(IHealth health, DifficultySettings difficulty)
  {
    // This safeguards when IsConstructed is true. It will throw an
    // AlreadyConstructedException to avoid executing the code below.
    this.ThrowIfAlreadyConstructed();
    
    // We can also detect errors early, like null values
    this.health.Value = health ?? throw new ArgumentNullException(nameof(health));
    // With unity objects we can use the utility method .OrThrowNullArgument(argumentName)
    this.difficulty = difficulty.OrThrowNullArgument(nameof(difficulty));
    
    IsConstructed = true; // Do NOT forget to set IsConstructed to true
  }
  
  public void Damage(int amount)
  {
    // We can safeguard with a Debug.Assert to detect bugs related to the object not
    // being constructed yet.
    this.AssertAlreadyConstructed();
    this.ThrowIfNotConstructed(); // Or you can safeguard with throw if you prefer it that way
    health.Value.Damage(amount);
  }
}

Installation

Inside your unity project go to Window > Package manager > + > Add from git URL and add: https://github.com/chonky-gator/gator-logic.git

Or modify your manifest.json to include this repo URL:

{
  "dependencies": {
    "com.chonkygator.logic": "https://github.com/chonky-gator/gator-logic.git",
  }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages