C# custom attributes

Suppose you do not want always check the distance between player and some object and instead of it you just want to mark your function like [DistanceAttribute(2f)] where 2f is allowable distance and your function will be runned only if that distance between player and object will be Less or Equal of 2 and method will run automatically without any “if” over and over.

There is no too much explanation about attributes, that’s the only one reason why I’d like to share it with you. Yes it’s reflection and yes it is a bit slower, but I wish you could use caching for it, I will point you later.

Attribute can be used for Methods, Interfaces, Variables, Enums and a lot more. I’m pretty sure you know some of it [SerializeField] ,[SyncVar] ,[ClientRPC]

Let’s create our Attributes.cs script which will contain our attribute or attributes.

using System;

public enum FunctionType
{
    None,
    PickupItem,
    HandShake,
}

[AttributeUsage(AttributeTargets.Method)]
public class CustomAttribute : Attribute
{
    public readonly float AllowableDistance;
    public readonly FunctionType functionType;

    public CustomAttribute(float allowableDistance, FunctionType type)
    {
        AllowableDistance = allowableDistance;
        functionType = type;
    }
}

It’s pretty basic construction of your CustomAttribute, feel free to add some vars or change the attriubut type. AllowableDistance used for defining the value which will run the function and the functionType for output errors in case if the distance will be more than allowable.

Now lets move to our CommandExecutor

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class CommandExecutor : MonoBehaviour
{

    private void Start()
    {
        Execute("RPC_PickupItem");
        Execute("RPC_HandShake");
    }

    [Custom(8f, FunctionType.PickupItem)]
    public void RPC_PickupItem()
    {
        Debug.Log("Item has been taken");
    }

    [Custom(1f, FunctionType.HandShake)]
    public void RPC_HandShake()
    {
        Debug.Log("Hands has been shaken");
    }

    private void Execute(string functionName)
    {
        float playerDistance = 5; //Suppose we have calculated distance between player and the item or man;

        var method = GetType().GetMethod(functionName);
        var attribute = method.GetCustomAttributes(true).OfType().FirstOrDefault();

        if (playerDistance > attribute.AllowableDistance)
        {
            if (attribute.functionType == FunctionType.PickupItem)
                Debug.LogWarning("Player too far from the item");
            if (attribute.functionType == FunctionType.HandShake)
                Debug.LogWarning("Player too far from the man");
        }
        else
        {
            method.Invoke(this, null);
        }
    }
}

It’s pretty easy to use for your networking for example you’d like to call a RPC from [Command] but you wish to run it only if the Player in distance so instead of doing

[Command]
public void CmdDoAction()
{
    if (PlayerInDistance(8))
    {
      //RPC
    }
}

You can do

[Command]
public void CmdDoAction()
{
    Execute("RPC");
}

So you just simply mark your RPC with [Custom(distance, function type)].

From the code above we got 2 attributes such as [Custom(8f, FunctionType.PickupItem)] where 8 is allowable distance , and for handshaking as [Custom(1f, FunctionType.ShakeHands)] where 1 is allowable distance

 

For caching you could have a Dictionary so it won’t call GetType() over and over and you could just simply get value from Dictionary by the key which is your function name dictionary[functionName].value.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s