Riexn
Riexn

Reputation: 123

How to filter game objects based on a component's attribute?

enter image description here I currently have a player "sense ability" that would detect nearby enemies through Physics2D.OverlapCircleAll()

Enemies have a component that carries general properties about them:

using UnityEngine;

public class EnemyProperties : MonoBehaviour
{
    public WorldColors color;
    public bool hookable;
    public bool isLast;
}

Now I want to filter those enemies in an array based on the attribute hookable == true

What I managed to do so far is to filter them based on the component:

public static void GetObjectsWithComponent(Transform srcTransform, float checkRadius, LayerMask checkLayers, Type myType)
{
    Collider2D[] colliders = Physics2D.OverlapCircleAll(srcTransform.position, checkRadius, checkLayers);
    Collider2D[] collidersWithComponent = Array.FindAll(colliders, collider => collider.gameObject.TryGetComponent(myType, out Component component));
    // Collider2D[] collidersWithComponentAttribute = ??
}
private void Update()
{
    Sense.GetObjectsWithComponent(this.transform, checkRadius, checkMask, typeof(EnemyProperties));
}

how can I filter them further based on the bool hookable == true?

Upvotes: 1

Views: 674

Answers (2)

Tiberiu Maran
Tiberiu Maran

Reputation: 2173

You can use C# generics and Linq to make your life easier...

public static IEnumerable<TType> GetComponentsWithAttributeInCircle<TType>(Transform srcTransform, float checkRadius, LayerMask checkLayers, Func<TType, bool> filter)
{
    Collider2D[] colliders = Physics2D.OverlapCircleAll(srcTransform.position, checkRadius, checkLayers);
    IEnumerable<TType> colliderWithComponentAttribute = colliders.Select(collider => collider.gameObject.GetComponent<TType>()).Where(component => component != null).Where(filter);
    return colliderWithComponentAttribute;
}

and in Update:

private void Update()
{
    var hookableEnemies = Sense.GetComponentsWithAttributeInCircle<EnemyProperties>(this.transform, checkRadius, checkMask, (EnemyProperties => EnemyProperties.hookable));

    foreach (var enemy in hookableEnemies)
    {
        ...
    }

}

As a remark to your additional comment, Linq is already pretty generic and general purpose. If you learn to use it, you can set up simple filters like this in no time. And it works well with compile time types. If you want to do the same thing without compile time knowledge of the types you will have to use reflection.

Reflection is usually slow, and not easy to follow, so if you can stick to Linq and generics, you will probably do a lot better.

Upvotes: 4

snaipeberry
snaipeberry

Reputation: 1059

The method FindAll from the Array class is prototyped as follows :

public static T[] FindAll<T> (T[] array, Predicate<T> match);

So the second argument is actually a function, or lambda. The match parameter needs to return a boolean. If it returns true, it adds the element in the resulting array. If it returns false, it doesn't.

So you can use the && operator in order to filter the component EnemyProperties and the hookable == true.

It would look something like this :

Collider2D[] collidersWithComponent = Array.FindAll(colliders, collider => (collider.gameObject.TryGetComponent(myType, out Component component) && collider.gameObject.GetComponent<EnemyProperties>().hookable == true));

Upvotes: 0

Related Questions