Hide
Overview

This part is how a target is found, a simplified XML Path Language.

basic syntax: step/step/step...

for each step: axis::nodetest[predicate][predicate OR predicate]/othersteps...

Please note: powerful as the seems, there are many edge cases I have not covered yet. Report an issue if you face any.

nodetest

nodetest is like a path, use / to separate, // means any descendant

. means current object, .. means parent, * means any node.

nodetest always starts from the current object.

// `DirectChild` object under this object [GetByXPath("/DirectChild")] public GameObject directChild; // Search all children that starts with `StartsWith`, // under which, search all children ends with `Child` // and get all the direct children of that. [GetByXPath("//StartsWith*//*Child/*")] public Transform[] searchChildren;

axis

axis redirect the target point

  • ancestor::: all parents
  • ancestor-or-self::: the object itself, and all it's parents.
  • ancestor-inside-prefab::: all parents inside this prefab
  • ancestor-or-self-inside-prefab::: the object itself, and all it's parents inside this prefab
  • parent::: parent of the object
  • parent-or-self::: the object itself, and it's parent
  • parent-inside-prefab::: parent inside this prefab
  • parent-or-self-inside-prefab::: this object itself, and it's parent inside this prefab
  • scene::: root of the active scene
  • prefab::: root of the current prefab
  • resources::: Resources
  • assets::: root folder Assets
  • gameObject::: current GameObject. This will
  • field::: the decorated field/property's current value (the current element for an array/list element).
  • serialized-property::: the original SerializedProperty.
// search all parents that starts with `Sub` [GetByXPath("ancestor:://Sub*")] public Transform ancestorStartsWithSub; // search object itself, and all it's parents, which contains `This` [GetByXPath("ancestor-or-self::*This*")] public Transform[] parentsSelfWithThis; // search current scene that ends with `Camera` [GetByXPath("scene:://*Camera")] public Camera[] sceneCameras; // get the first folder starts with `Issue`, and get all the prefabs [GetByXPath("assets:://Issue*/*.prefab")] public GameObject[] prefabs;

attribute

attribute allows you to extract an attribute from a target, starting with a @ letter. Normally, {} means it can be directly executed on the target.

  • @layer: Get the layer string name

  • @{layer}: Get the layer mask (int)

  • @{tag}: Get the tag value

  • @{gameObject}: Get the gameObject (this is the default behavior)

  • @{transform}: Get the transform

  • @{rectTransform}: Get the RectTransform. This is just a shortcut ofGetComponent(RectTransform)

  • @{activeSelf}/@{gameObject.activeSelf}

  • @as(T): keep the current target when assignable to the type, including base classes and interfaces; otherwise return no match.

    e.g. @{GetMyScriptableObjects()}[*]/@as(MyEnemyConfig) returns matching scriptableObjects

  • @not-as(T): reverse of @as(T)

  • Component(s) For component(s), there are 8 forms:

    • @get-components(): get all components. The results get flatten for multiple tagets
    • @get-components(T): get all components of type T. The results get flatten for multiple tagets
    • @{GetComponents()}: get all components. The result is a list for multiple tagets thus you may need [INDEX]
    • @{GetComponents(T)}: get all components of type T. The result is a list for multiple tagets thus you may need [INDEX]
    • @get-component(): get first component.
    • @get-component(T): get first component of type T.
    • @{GetComponent()}: get first component.
    • @{GetComponent(T)}: get first component of type T.

    Namespace is allowed for T like GetComponent(MyNameSpace.MyScript). Base class/interface is allow allowed, but generic class is not supported.

  • @{CallFunction()}/@{FieldOrProperty.CallFunction()} etc.

    Please note: this is not an actual code executing, and with these limits:

    1. Parameters are not supported
    2. indexing for array/list is allowed
    3. indexing for dictionary only supports string key type, and single quote / double quote are the same Thus You can continuously chain the calling like: @{MyFunction()[-1].someField['key']}

Because some targets (like GetComponents) returns a list/array. However, XPath itself allows multiple targeting filter out. So, for @{...}, if the result is list/array, the collection will be kept. For example:

Root
|- GO1 (Component: A)
|- GO2 (Component: B)

To query Root/*@{GetComponents()} will results as [ [transform, A], [transform, B] ]

To query Root/*@get-components() will results as [ transform, A, transform, B ] because @get-components() is auto-flatten

And [*] can flatten the list/array, thus @get-components() is just an alias of @{GetComponents()}[*]

Depending on the target, how the plain NAME is down-walked is different. For example:

MyField/NextOne

If MyField is an GameObject, then we will search NextOne in it's directly children. Wildcard match like MyField/*One is supported

If MyField is an object (instance etc), then we will look at it's field/property/method named NextOne. Wildcard match like MyField//NextOne or MyField/*One is not supported.

Example:

// 1. search all the children which has `FunctionProvider` script, grab the first result // 2. call `GetTransforms()` as the results // 3. use `[*]` to flatten the list // 4. from the results, get first direct children named "ok" [GetByXPath("//*@{GetComponent(FunctionProvider)}/@{GetTransforms()}[*]/ok")] public GameObject[] getObjs; // FunctionProvider.cs public class FunctionProvider : MonoBehaviour { // Example of returning some target public Transform[] GetTransforms() => transform.Cast<Transform>().ToArray(); }

filter

filter check if the results match the expected condition. There are two types of filter:

  • index filter:

    either just use the index: child*[1] (second one), child*[last()]/child*[-1] (last one)

    or compared value: child*[index() > 3]

  • value filter: use any attribute mentioned above, can work together with >, != etc. for comparison. e.g. [@{someField.activeSelf}][@layer = "UI"]

using multiple filters means all conditions must be met. Otherwise, use the keyword OR: [@get-component(Collider) OR @get-component(MyScript)]

// find the first main camera in the scene [GetByXPath("scene:://[@{tag} = MainCamera]")] public Camera mainCamera; // find the prefabs with component "Hero" [GetByXPath("assets:://Heros/*.prefab[@GetComponent(Hero)]")] public Camera mainCamera;