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 parentsancestor-or-self::: the object itself, and all it's parents.ancestor-inside-prefab::: all parents inside this prefabancestor-or-self-inside-prefab::: the object itself, and all it's parents inside this prefabparent::: parent of the objectparent-or-self::: the object itself, and it's parentparent-inside-prefab::: parent inside this prefabparent-or-self-inside-prefab::: this object itself, and it's parent inside this prefabscene::: root of the active sceneprefab::: root of the current prefabresources:::Resourcesassets::: root folderAssetsgameObject::: current GameObject. This willfield::: the decorated field/property's current value (the current element for an array/list element).serialized-property::: the originalSerializedProperty.
// 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 thegameObject(this is the default behavior) -
@{transform}: Get thetransform -
@{rectTransform}: Get theRectTransform. 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 typeT. 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 typeT. 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 typeT.@{GetComponent()}: get first component.@{GetComponent(T)}: get first component of typeT.
Namespace is allowed for
TlikeGetComponent(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:
- Parameters are not supported
- indexing for array/list is allowed
- indexing for dictionary only supports
stringkey 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
attributementioned 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;