Selectable items in Unity

Image credit: Artur Pachałko

Selectable items in Unity

Hi guys! Since I decided to postpone the deadline for my master thesis project onto the next session, which takes place in September, I can take some time to focus on minor elements of the project. My aim is to create a little GUI-helper library, that I will use in my future game projects – for example to boost my productivity on game jams. That is also why I’m trying to write scripts that are maximally potentially reusable. The first component that I created was DraggableItem, now it is time for Selectable Items. I needed them for a handy entities creation mechanism, which you can see below.

1

So, the concept is simple – similar to components named radio buttons. When we activate one, the others get deactivated. Since it is unity, we will probably want to do something when the button gets marked.

One to rule them all

First of all, we want our elements to find their parent. The parent will have a SelectablesContainer script attached that will provide the method to register the child. The container will keep track of its children and will allow them to communicate by exposing an event – ActiveElementChanged.

public class SelectablesContainter: MonoBehaviour
{
    public event Action<int> ActiveElementChanged = delegate { };
    private int elemCounter = 0;
    public void ChangeActive(int id)
    {
        ActiveElementChanged(id);
    }

    public int Register()
    {
        return elemCounter++;
    }
}

When a child registers, it obtains its own id and the counter is incremented. When active element is about to change, it calls the ChangeActive method passing its id as an argument. Then, the container sends an event to all selectable items. The ones with non-matching id will get disabled.

The element

The first thing selectable items do while appearing on the scene, is to find the parent container. When it is done, they sign up for the ActiveElementChanged event and request an id.

protected virtual void OnEnable()
{
    container = gameObject.FindInParents<SelectablesContainter>();
    if (container != null) {
        container.ActiveElementChanged += Container_ActiveElementChanged;
        _id = container.Register();
    }
}
private void Container_ActiveElementChanged(int id)
{
    IsActive = _id == id && !IsActive;
}

Selectable items obviously will be selected by clicking on them, so they also need to implement IPointerClickHandler interface. They will have only one public readonly property – IsActive. When it is set, the virtual method OnActiveChanged is called.

public void OnPointerClick(PointerEventData eventData)
{
    if (container != null) {
        container.ChangeActive(_id);
    }
}

protected virtual void OnActiveChanged() { }

It does nothing right now, but is left here for other scripts that will inherit from this class. OnEnable is also virtual, in case future components would need some additional initialization.

The example

This is it, little simple scripts that make entities creation handier. I created also a script that inherits from SelectableItem – ColorSelectableItem, which changes its color when marked. That is when the “core” part ends. Then, my EntityElement could inherit from this class and in the overridden OnActiveChanged method it turns the input field on and off. The only thing left was a handy AttachElementButton – I will need that in a lot more places:

[RequireComponent(typeof(Button))]
public class AttachElementButton: MonoBehaviour
{
    [SerializeField]
    protected Transform     _container;
    [SerializeField]
    protected GameObject    _prefab;

    public bool StayAtEnd;

    public void OnClick()
    {
        var obj = Instantiate(_prefab);
        obj.transform.SetParent(_container);
        obj.transform.SetAsLastSibling();
        if (StayAtEnd) {
            transform.SetAsLastSibling();
        }
    }
}

RequireComponent

In this script I used the RequireComponent attribute, to ensure that it will be placed only on objects that contain the Button script. This attribute is really useful, because:

• If you already have required component attached to the object it will do nothing,

• If you add this script to a game object that doesn’t have this component attached, it will be added automatically,

• If adding this component would collide with some other (for example – you already have an Image component attached and would want to add a script that requires the Text component) – you will get an error message and the script won’t be added.

I hope that this boring UI part is slowly coming to an end and I will be able to put some spirit into these panels and buttons, but first things first – there’s Easter coming! So, happy Easter!

And if you liked this post and would like to get notified on the following ones - like my fan page or follow me on Twitter. :)

comments powered by Disqus