Giter Club home page Giter Club logo

unity-bitmasktypes's Introduction

openupm

The idea with this project is having a way of creating custom enums in editor, by Game Designers for example, and use them internally for fast comparisons as bitmasks.

Motivation

When working on Iron Marines at Ironhide, we used bitmasks to speed up units' abilities targeting logic. To do that, we were using c# Enums with Flags Attribute, like this:

[Flags]
public enum TargetType
{
    Unit = 1 << 0,
    Structure = 1 << 1,
    Vehicle = 1 << 2
}

Using this approach, we can configure them in Unity's Editor by selecting the flags we want, and then we just compare using bitwise operations to check if a target matches the ability targeting.

This works pretty well internally but it has one limitation, it is not so easy for the Game Designers to extend it since they have to know it is used internally as bitmask and they don't care about it.

There is also another problem, extending it by code couples the Enum definition to a specific game instead of keeping the core engine decoupled to be easily reused.

So the focus here is to keep the base concepts coupled to the core engine while leaving the values to the Game Designers, and if they want, different on each game.

Main approach: custom property to override maskfield names

The main option is to just define your types in a generic way in your code and then use property attribute to override the names shown in the inspector. This is a clean an simpler way.

For example, for the following enum:

[Flags]
public enum DamageType
{
    Nothing = 0,
    Everything = -1,
    Type0 = 1 << 0,
    Type1 = 1 << 1,
    Type2 = 1 << 2,
    Type3 = 1 << 3,
    Type4 = 1 << 4,
    Type5 = 1 << 5,
    Type6 = 1 << 6,
    Type7 = 1 << 7
}

Will be draw in the inspector as:

drawing

In this case, by creating an EnumNameTypeAsset asset as it follow:

drawing

And then, configuring a field attribute:

[EnumName("damages")]
public DamageType type1;

Then, the inspector will show this:

drawing

One good thing about enums approach is you can have type checks in compilation time, so you can't misplace a check between ArmorType and DamageType unless you want it and you have to explicitly do it by casting to the other enum.

Using it with int fields

This approach can be used with int fields too:

[EnumName("damages")]
public int type3;

One good thing of using ints is you don't have to worry about defining all the enum entries, while the enums mode will fail if you define more types in the asset than the enum.

One drawback is you don't any type in code, you just work with ints, so you delegate the config to the editor all the time.

Code generation

In the case of using ints, there is an option to generate class with static int fields with each value, so for the previous example, activating the code generation as it follows:

drawing

Will generate something like this:

namespace Gemserk.Examples
{
    public class DamageTypes
    {
        public static int Fire = 1 << 0;
        public static int Ice = 1 << 1;
        public static int Lightning = 1 << 2;
    }
}

Which helps a lot if you need to check for specific value directly in code.

Preconfigured bit masks

There is also an option of preconfigure some bit masks for some groups of types, for example, all cold related damages, like Ice, Cold, Frost, etc. In order to do that, you can do the following:

drawing

This will allow you to select from a list these preconfigured values:

drawing

And by doing so, it will override the serialized value with the bit mask, and it will also auto generate the following code inside the previous autogenerated code:

namespace Gemserk.Examples
{
    public class DamageTypes
    {
        public static int Fire = 1 << 0;
        public static int Ice = 1 << 1;
        public static int Cold = 1 << 2;
        public static int Lightning = 1 << 3;
        public static int Shocking = 1 << 4;
        public static int Electric = 1 << 5;
        
        public static int BaseDamages = Fire | Ice | Lightning;        
        public static int IceDamages = Ice | Cold | Lightning;
        public static int ElectricDamages = Shocking | Electric;
    }
}

Another approach is using Unity assets directly in code (deprecated)

Right now, each time a new type is created, the asset importer auto assigns a bitmask to that asset. The bitmask itself isn't important in editor, could change and nothing happens since the users of that asset should referencing the asset itself.

To use it, you have to create a subclass of BaseTypeAsset and start creating your asset instances for each kind of type you need. For example, for a game with different kind of damages:

[CreateAssetMenu(menuName="Gemserk/Example/Damage Type")]
public class DamageTypeAsset : BaseTypeAsset {}

Then, in a class you want a bitmask of that type, you can do the following:

public class UnitWithDamage : MonoBehaviour
{
    [TypeMask(typeof(DamageTypeAsset))]
    public BaseTypeMask damageTypes;
}

Here you declare a field of type BaseTypeMask, and say the type you want is of type DamageTypeAsset so the custom property drawer will allow you select the bits you want using an enum mask (internally saves a list of asset references, not the bitmask).

In editor, it looks like this:

drawing

Code autogeneration

For each custom type, a file with an enum with bitmasks is autogenerated when reimporting new type instances, for example:

namespace Gemserk.Examples
{
    public enum DamageTypeAssetEnum
    {       
        ElectricDamage = 1 << 0,   
        FireDamage = 1 << 1,
        IceDamage = 1 << 2,
        PoisonDamage = 1 << 3,
    }
}

So this can be used from code directly. This normally isn't needed since we try to configure everything to depend on assets and then use an int, but sometimes this could be handy, maybe for unit tests, or for editor stuff.

The drawbacks of this approach is we have to depend a lot on Unity assets in code, and also having the iteration to calculate the bitmask.

unity-bitmasktypes's People

Contributors

acoppes avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.