Add Introduction
This commit is contained in:
@@ -1,4 +1,559 @@
|
||||
## Animator As Code (AacV0)
|
||||
# Animator As Code
|
||||
|
||||
**Animator As Code** is a small Unity Editor facility to generate Avatars 3.0 Animator layers and animations from a [fluent builder](https://en.wikipedia.org/wiki/Fluent_interface) syntax written in C#.
|
||||
|
||||
Describing your animators as code provides the following advantages:
|
||||
|
||||
- you do not need to edit your animations by hand every time you add remove or change the location of a component in your hierarchy
|
||||
- you will not need to edit a hundred transitions by hand if you need to rectify your animator
|
||||
|
||||
It is written with VRChat Avatars 3.0 use cases in mind; the API is opinionated to facilitate writing such animators in a concise way, hopefully requiring as little additional tweaking.
|
||||
|
||||
# Interested? Join my Discord Server
|
||||
|
||||
This is a work in progress, I am looking for feedback!
|
||||
|
||||
[Join the Invitation Discord Server!](https://discord.com/invite/58fWAUTYF8)
|
||||
|
||||
# Install
|
||||
|
||||
There are currently no releases.
|
||||
|
||||
Clone the repository within a subfolder of your Unity project, or download the source code and install in any subfolder of your project.
|
||||
|
||||
The project can be located within `Assets/AnimatorAsCodeFramework` but you can choose any location.
|
||||
|
||||
# Examples
|
||||
|
||||
## #0 Toggle a GameObject
|
||||
|
||||
This example shows:
|
||||
|
||||
- Creating a FX layer
|
||||
- Creating states
|
||||
- Creating animations
|
||||
- Declaring a Bool parameter
|
||||
- Creating transitions with conditions
|
||||
-
|
||||

|
||||
|
||||
```csharp
|
||||
public class GenExample0_ToggleGo : MonoBehaviour
|
||||
{
|
||||
public VRCAvatarDescriptor avatar;
|
||||
public AnimatorController assetContainer;
|
||||
public string assetKey;
|
||||
public GameObject item;
|
||||
}
|
||||
|
||||
private void Create()
|
||||
{
|
||||
var my = (GenExample0_ToggleGo) target;
|
||||
// The avatar is used here:
|
||||
// - to find the FX playable layer animator, where a new layer will be created.
|
||||
// - to resolve the relative animation path to the item.
|
||||
// The generated animation files are stored in the asset container.
|
||||
var aac = AacExample.AnimatorAsCode("Example 0", my.avatar, my.assetContainer, my.assetKey, AacExample.Options().WriteDefaultsOff());
|
||||
|
||||
// Create a layer in the FX animator.
|
||||
// Additional layers can be created in the FX animator (see later in the manual).
|
||||
var fx = aac.CreateMainFxLayer();
|
||||
|
||||
// The first created state is the default one connected to the "Entry" node.
|
||||
// States are automatically placed on the grid (see later in the manual).
|
||||
var hidden = fx.NewState("Hidden")
|
||||
// Animation assets are generated as sub-assets of the asset container.
|
||||
// The animation path to my.skinnedMesh is relative to my.avatar
|
||||
.WithAnimation(aac.NewClip().Toggling(my.item, false));
|
||||
var shown = fx.NewState("Shown")
|
||||
.WithAnimation(aac.NewClip().Toggling(my.item, true));
|
||||
|
||||
// Creates a Bool parameter in the FX layer.
|
||||
// Parameters are added to the Animator if a parameter with the same name
|
||||
// does not exist yet.
|
||||
var itemParam = fx.BoolParameter("EnableItem");
|
||||
|
||||
// Transitions are created with a set of default values
|
||||
// That can be changed in the Generator settings (see later in the manual).
|
||||
hidden.TransitionsTo(shown).When(itemParam.IsTrue());
|
||||
shown.TransitionsTo(hidden).When(itemParam.IsFalse());
|
||||
}
|
||||
```
|
||||
|
||||
## #1 Toggle a SkinnedMeshRenderer with two conditions
|
||||
|
||||
This example shows:
|
||||
|
||||
- Simple toggle animations
|
||||
- Groups of Bool parameters
|
||||
- Simple transitions
|
||||
|
||||

|
||||
|
||||
```csharp
|
||||
public class GenExample1_ToggleSmr : MonoBehaviour
|
||||
{
|
||||
public VRCAvatarDescriptor avatar;
|
||||
public AnimatorController assetContainer;
|
||||
public string assetKey;
|
||||
public SkinnedMeshRenderer skinnedMesh;
|
||||
}
|
||||
|
||||
private void Create()
|
||||
{
|
||||
var my = (GenExample1_ToggleSmr) target;
|
||||
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
|
||||
|
||||
var fx = aac.CreateMainFxLayer();
|
||||
var hidden = fx.NewState("Hidden")
|
||||
// The runtime type of my.skinnedMesh is used within the animation.
|
||||
// In this case, the "SkinnedMeshRenderer" component is disabled.
|
||||
.WithAnimation(aac.NewClip().TogglingComponent(my.skinnedMesh, false));
|
||||
var shown = fx.NewState("Shown")
|
||||
.WithAnimation(aac.NewClip().TogglingComponent(my.skinnedMesh, true));
|
||||
|
||||
// This creates two Bool parameters in the animator.
|
||||
// The resulting value can be used in conditions.
|
||||
var accessoriesParams = fx.BoolParameters("EnableAccessories", "AccessoryThing");
|
||||
|
||||
// The following line creates one transition.
|
||||
// The conditions are "EnableAccessories is true" and "AccessoryThing is true"
|
||||
hidden.TransitionsTo(shown).When(accessoriesParams.AreTrue());
|
||||
|
||||
// The following line creates two transitions:
|
||||
// - The first transition is "EnableAccessories is false"
|
||||
// - The second transition is "AccessoryThing is false"
|
||||
shown.TransitionsTo(hidden).When(accessoriesParams.IsAnyFalse());
|
||||
}
|
||||
```
|
||||
|
||||
## #2 Animate a SkinnedMesh with Motion time
|
||||
|
||||
This example shows:
|
||||
|
||||
- Describing animations with simplified keyframes
|
||||
- Animating a state with Motion Time (formerly known as Normalized Time)
|
||||
|
||||

|
||||
|
||||
|
||||
```csharp
|
||||
public class GenExample2_Animate : MonoBehaviour
|
||||
{
|
||||
public VRCAvatarDescriptor avatar;
|
||||
public AnimatorController assetContainer;
|
||||
public string assetKey;
|
||||
public SkinnedMeshRenderer wedgeMesh;
|
||||
}
|
||||
|
||||
private void Create()
|
||||
{
|
||||
var my = (GenExample2_Animate) target;
|
||||
var aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
|
||||
|
||||
var fx = aac.CreateMainFxLayer();
|
||||
|
||||
fx.NewState("Motion")
|
||||
.WithAnimation(aac.NewClip().Animating(clip =>
|
||||
{
|
||||
clip.Animates(my.wedgeMesh, "blendShape.Wedge").WithFrameCountUnit(keyframes =>
|
||||
keyframes.Easing(0, 100f).Easing(28, 0).Easing(29, 0).Easing(30, 0).Easing(31, 0).Easing(32, 0).Easing(60, 100f)
|
||||
);
|
||||
clip.Animates(my.wedgeMesh, "material._Metallic").WithFrameCountUnit(keyframes =>
|
||||
keyframes.Constant(0, 1f).Constant(28, 0).Constant(60, 0)
|
||||
);
|
||||
}))
|
||||
.MotionTime(fx.FloatParameter("WedgeAmount"));
|
||||
}
|
||||
```
|
||||
|
||||
## #3 Using Parameter Drivers
|
||||
|
||||
This example shows:
|
||||
|
||||
- Driving parameters using VRC Parameter Driver behavior
|
||||
- VRChat Parameters
|
||||
- Creating systems with multiple FX layers
|
||||
- Complex transitions
|
||||
- *Show code...*
|
||||
|
||||
```csharp
|
||||
public class GenExample3_Gesturing : MonoBehaviour
|
||||
{
|
||||
public VRCAvatarDescriptor avatar;
|
||||
public AnimatorController assetContainer;
|
||||
public string assetKey;
|
||||
public SkinnedMeshRenderer iconMesh;
|
||||
}
|
||||
|
||||
private GenExample3_Gesturing my;
|
||||
private AacFlBase aac;
|
||||
|
||||
private void Create()
|
||||
{
|
||||
my = (GenExample3_Gesturing) target;
|
||||
aac = AacExample.AnimatorAsCode(SystemName, my.avatar, my.assetContainer, my.assetKey);
|
||||
|
||||
CreateMainLayer();
|
||||
CreateSupportingLayer();
|
||||
}
|
||||
|
||||
private void CreateMainLayer()
|
||||
{
|
||||
var layer = aac.CreateMainFxLayer();
|
||||
|
||||
var dirtyCheckParameter = layer.BoolParameter("AAC_INTERNAL_GesturingIcon_DirtyCheck");
|
||||
|
||||
// ### Create states
|
||||
var lackOfChangeDetected = layer.NewState("Animate To NoChange")
|
||||
.WithAnimation(IconAppears());
|
||||
|
||||
// By default, states have an animation that animates a dummy object for 1 frame.
|
||||
var noChange = layer.NewState("NoChange", 1, 0).RightOf();
|
||||
|
||||
var changeDetected = layer.NewState("Animate To Changing").Under()
|
||||
.WithAnimation(IconDisappears());
|
||||
|
||||
// This creates a clip that animates a dummy object for 1.5f seconds.
|
||||
var changing = layer.NewState("Changing", 0, 1).LeftOf()
|
||||
.WithAnimation(aac.DummyClipLasting(1.5f, AacFlUnit.Seconds));
|
||||
|
||||
// When this state is entered, the parameter is driven to the value of false.
|
||||
var stillChanging = layer.NewState("Still Changing", 0, 2).Under()
|
||||
.Drives(dirtyCheckParameter, false);
|
||||
|
||||
// ------
|
||||
|
||||
// ### Create transitions
|
||||
lackOfChangeDetected.TransitionsTo(changeDetected).AfterAnimationIsAtLeastAtPercent(0.7f).When(dirtyCheckParameter.IsTrue());
|
||||
|
||||
// The transition duration is 30% of the animation duration.
|
||||
lackOfChangeDetected.TransitionsTo(noChange).AfterAnimationFinishes().WithTransitionDurationPercent(0.3f);
|
||||
|
||||
noChange.TransitionsTo(changeDetected).When(dirtyCheckParameter.IsTrue());
|
||||
|
||||
// By using AfterAnimationFinishes, the transition will trigger after the animation
|
||||
// for the icon appearing finishes.
|
||||
changeDetected.TransitionsTo(changing).AfterAnimationFinishes();
|
||||
|
||||
changing.TransitionsTo(stillChanging).When(dirtyCheckParameter.IsTrue());
|
||||
// By using AfterAnimationFinishes, the transition will trigger after 1.5 seconds,
|
||||
// which is the length of the animation in Changing.
|
||||
changing.TransitionsTo(lackOfChangeDetected).AfterAnimationFinishes();
|
||||
|
||||
// The transition will immediately happen upon entering, by using Exit time set to 0.
|
||||
stillChanging.AutomaticallyMovesTo(changing);
|
||||
}
|
||||
|
||||
private void CreateSupportingLayer()
|
||||
{
|
||||
// Create an additional FX layer.
|
||||
var layer = aac.CreateSupportingFxLayer("Detection");
|
||||
var reevaluating = layer.NewState("Reevaluating", -1, 0);
|
||||
|
||||
foreach (var left in Enumerable.Range(0, 8))
|
||||
{
|
||||
foreach (var right in Enumerable.Range(0, 8))
|
||||
{
|
||||
var state = layer.NewState($"Gesture {left} {right}", left, right)
|
||||
// When this state is entered, the parameter is driven to the value of true.
|
||||
.Drives(layer.BoolParameter("AAC_INTERNAL_GesturingIcon_DirtyCheck"), true);
|
||||
|
||||
reevaluating.TransitionsTo(state)
|
||||
// Use ".Av3" to access VRChat standard parameters.
|
||||
// Accessing these parameters will create the corresponding parameter in the animator.
|
||||
.When(layer.Av3().GestureLeft.IsEqualTo(left))
|
||||
.And(layer.Av3().GestureRight.IsEqualTo(right));
|
||||
state.TransitionsTo(reevaluating)
|
||||
.When(layer.Av3().GestureLeft.IsNotEqualTo(left))
|
||||
.Or()
|
||||
.When(layer.Av3().GestureRight.IsNotEqualTo(right));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AacFlClip IconAppears()
|
||||
{
|
||||
return aac.NewClip().Animating(clip =>
|
||||
{
|
||||
clip.Animates(my.iconMesh, "blendShape.Wedge")
|
||||
.WithFrameCountUnit(keyframes => keyframes.Easing(0, 0f).Easing(10, 100f));
|
||||
});
|
||||
}
|
||||
|
||||
private AacFlClip IconDisappears()
|
||||
{
|
||||
return aac.NewClip().Animating(clip =>
|
||||
{
|
||||
clip.Animates(my.iconMesh, "blendShape.Wedge")
|
||||
.WithFrameCountUnit(keyframes => keyframes.Easing(0f, 100f).Easing(10, 0f));
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
# Reference
|
||||
|
||||
## Typical animator creation steps
|
||||
|
||||
Animator As Code is generally used in the following steps:
|
||||
|
||||
- Declare an Animator As Code
|
||||
- Create one or multiple layers
|
||||
- Create the states
|
||||
- Create the animations at the same time
|
||||
- Create the transitions
|
||||
|
||||
## Declare an Animator As Code (AAC)
|
||||
|
||||
In order to use Animator As Code (AAC), first, declare it with a configuration.
|
||||
|
||||
The AAC configuration requires the following:
|
||||
|
||||
- A system name.
|
||||
- Animator As Code describes systems. A system can have multiple layers, not only across playable layer animators, but also within a single playable layer animator. All created layers will be prefixed with this system name.
|
||||
- It is up to you to decide where the boundaries of the system lies.
|
||||
- An avatar descriptor, animator root, and default value root.
|
||||
- This is used to select the playable layer animators to use.
|
||||
- This is also used to select the root transform that animations will use for relative paths.
|
||||
- This is used to collect the default values for some animations.
|
||||
- *In general, all three of them are the same object, but this is not mandatory.*
|
||||
- An asset container.
|
||||
- Animations are generated by Animator As Code, and the large quantity of generated assets can be quite messy. To limit littering your project, such assets will be generated as sub-assets of a container. The container is of type Animator Controller, but it doesn’t need to have any layers within it.
|
||||
- An asset key.
|
||||
- The asset key a prefix that all generated assets will use. When creating the layers, all generated assets that uses that prefix will be removed upon invocation of `aac.ClearPreviousAssets()`.
|
||||
- A provider of defaults.
|
||||
- Animator As Code is opinionated, and sometimes you want to tweak the default values. The provider of defaults is executed when a state is created, an animation is created, a transition is created. This will let you tweak the generation process.
|
||||
|
||||
To declare it:
|
||||
|
||||
```csharp
|
||||
string systemName;
|
||||
VRCAvatarDescriptor avatar;
|
||||
AnimatorController assetContainer;
|
||||
string assetKey;
|
||||
|
||||
var aac = AacV0.Create(new AacConfiguration
|
||||
{
|
||||
SystemName = systemName,
|
||||
AvatarDescriptor = avatar,
|
||||
AnimatorRoot = avatar.transform,
|
||||
DefaultValueRoot = avatar.transform,
|
||||
AssetContainer = assetContainer,
|
||||
AssetKey = assetKey,
|
||||
DefaultsProvider = new AacDefaultsProvider(writeDefaults: false)
|
||||
});
|
||||
// Remove all previously generated assets from the asset container
|
||||
// that match the asset key.
|
||||
aac.ClearPreviousAssets();
|
||||
```
|
||||
|
||||
## Create one or multiple layers
|
||||
|
||||
Using AAC, create layers in your animators. A system can have multiple layers across animators.
|
||||
|
||||
There is one main layer, and multiple supporting layers, per animator.
|
||||
|
||||
- The main layer will be named exactly after your system name.
|
||||
- The supporting layers will be prefixed by the system name, and appended with a suffix of your choice.
|
||||
|
||||
You are in no obligation to create a main layer. If you think several layers of the same animators are of equal importance, you can declare them as being supporting layers.
|
||||
|
||||
```csharp
|
||||
var fx = aac.CreateMainFxLayer();
|
||||
var detection = aac.CreateSupportingFxLayer("Detection");
|
||||
```
|
||||
|
||||
## Create states
|
||||
|
||||
Using your layer, you may now create states. Your state will be configured with defaults based on your AAC configuration, most notably, the Write Defaults setting.
|
||||
|
||||
By default, states have a dummy animation that lasts one frame (1/60th of a second).
|
||||
|
||||
It is intended to create the animation clip directly while declaring the state, this will be explained later on.
|
||||
|
||||
```csharp
|
||||
var hidden = fx.NewState("Hidden")
|
||||
.WithAnimation(aac.NewClip().Toggling(my.item, false));
|
||||
```
|
||||
|
||||
## Creating parameters for use within layers
|
||||
|
||||
Using your layer, you can create parameters.
|
||||
|
||||
The parameter will be added to the animator the layer belongs to.
|
||||
|
||||
If you need to reuse a parameter across multiple layers, you need to invoke it on all relevant layers.
|
||||
|
||||
*(Once done, you are not forced to reuse the resulting parameter instance on the same layer it was created from)*
|
||||
|
||||
```csharp
|
||||
|
||||
fx.NewState("Motion")
|
||||
// This creates a Float parameter on the FX layer.
|
||||
.MotionTime(fx.FloatParameter("WedgeAmount"))
|
||||
// ...
|
||||
```
|
||||
|
||||
If a parameter with the same name already exists in the animator, it will not be created again.
|
||||
|
||||
*This is also true if the animator already has a parameter with the same name but with a different type, and no error will be raised.*
|
||||
|
||||
There are also dedicated functions to obtain Avatars 3.0 parameters, such as.
|
||||
|
||||
```csharp
|
||||
var gestureLeftWeight = fx.Av3().GestureLeftWeight
|
||||
```
|
||||
|
||||
## Forcing the value of parameters
|
||||
|
||||
In some rare cases you may wish to override the default value of animator parameters, for example in use for special conditions, for use in blend trees as a constant value.
|
||||
|
||||
Using your layer:
|
||||
|
||||
```csharp
|
||||
fx.OverrideValue(fx.FloatParameter("SmoothingAmount"), 0.7f)
|
||||
```
|
||||
|
||||
## Visually organize your states
|
||||
|
||||
By default, a newly created state will be visually placed under the previously created state.
|
||||
|
||||
For more control, states have `LeftOf`, `RightOf`, `Over`, `Under`, `Shift` operators, which let you move a state to be visually next to another state.
|
||||
|
||||
The value is in grid units.
|
||||
|
||||

|
||||
|
||||
```csharp
|
||||
var init = fx.NewState("Init"); // This is the first state. By default it is at (0, 0)
|
||||
var a = fx.NewState("A"); // This will be placed under Init.
|
||||
var b = fx.NewState("B"); // This will be placed under A.
|
||||
var c = fx.NewState("C").RightOf(a); // This will be placed right of A.
|
||||
var d = fx.NewState("D"); // This will be placed under C.
|
||||
var e = fx.NewState("E").RightOf(); // This will be placed right of D.
|
||||
var alternate = fx.NewState("Alternate").Over(c); // This will be placed over C.
|
||||
|
||||
// This will be placed relative to Alternate: 2 blocks over, and 1 to the right.
|
||||
var reset = fx.NewState("Reset").Shift(alternate, 1, -2);
|
||||
```
|
||||
|
||||
## Create an animation
|
||||
|
||||
By default, states have a dummy animation that lasts one frame (1/60th of a second). If you want the state to play an animation of your choice, there is usually an invocation to the `.WithAnimation(...)` function:
|
||||
|
||||
```csharp
|
||||
var hidden = fx.NewState("Hidden")
|
||||
.WithAnimation(aac.NewClip().Toggling(my.item, false));
|
||||
```
|
||||
|
||||
The invocation uses AAC to create a new clip inside the asset container. You don’t need to specify a name for this clip, it is irrelevant.
|
||||
|
||||
Animations use object references instead of paths. They are converted to paths in the animation clip asset by resolving the relative path to the animator root of the AAC configuration.
|
||||
|
||||
Most of the functions lets you create single-frame constant animations.
|
||||
|
||||
For more complex animations, use the `.Animating(...)` function.
|
||||
|
||||
```csharp
|
||||
fx.NewState("Motion")
|
||||
.WithAnimation(aac.NewClip().Animating(clip =>
|
||||
{
|
||||
clip.Animates(my.wedgeMesh, "blendShape.Wedge").WithFrameCountUnit(keyframes =>
|
||||
keyframes.Easing(0, 100f).Easing(28, 0).Easing(29, 0).Easing(30, 0).Easing(31, 0).Easing(32, 0).Easing(60, 100f)
|
||||
);
|
||||
clip.Animates(my.wedgeMesh, "material._Metallic").WithFrameCountUnit(keyframes =>
|
||||
keyframes.Constant(0, 1f).Constant(28, 0).Constant(60, 0)
|
||||
);
|
||||
}))
|
||||
```
|
||||
|
||||
## Create transitions and define conditions
|
||||
|
||||
Using the states, you can create transitions between states. Your transition will be configured with defaults based on your AAC configuration, defined within the DefaultsProvider.
|
||||
|
||||
To create a transition from Any, Exit, or Entry, there are some functions in the states like `.TransitionsFromAny()` or `.Exits()`.
|
||||
|
||||
Start defining conditions for that transition using the `.When(...)` operator.
|
||||
|
||||
Parameters have functions that generate conditions once invoked.
|
||||
|
||||
```csharp
|
||||
reevaluating.TransitionsTo(state)
|
||||
// Use ".Av3" to access VRChat standard parameters.
|
||||
// Accessing these parameters will create the corresponding parameter in the animator.
|
||||
.When(layer.Av3().GestureLeft.IsEqualTo(left))
|
||||
.And(layer.Av3().GestureRight.IsEqualTo(right));
|
||||
state.TransitionsTo(reevaluating)
|
||||
.When(layer.Av3().GestureLeft.IsNotEqualTo(left))
|
||||
.Or()
|
||||
.When(layer.Av3().GestureRight.IsNotEqualTo(right));
|
||||
```
|
||||
|
||||
## When(), And(), and Or() operators
|
||||
|
||||
In animator transitions, all conditions must be verified for the transition to occur; this is effectively a “AND” of all of the conditions (A && B && C).
|
||||
|
||||
This is done by using the `.And(...)` operator: `.When(A).And(B).And(C)`
|
||||
|
||||
To represent “OR”, new transitions need to be created; ((A && B && C) || (D && E)) results in:
|
||||
|
||||
- Transition 1: (A && B && C)
|
||||
- Transition 2: (D && E)
|
||||
|
||||
This is done by using the `.Or(...)` operator: `.When(A).And(B).And(C).Or().When(D).And(E)`
|
||||
|
||||
From this limitation, conditions with nested OR cannot be expressed easily, such as:
|
||||
|
||||
- 🚫 (F && (G || H) && (J || K))
|
||||
|
||||
## Conditions that generate multiple transitions
|
||||
|
||||
Some conditions generate multiple transitions, such as `boolParameters.IsAnyFalse()`
|
||||
|
||||
In order to still let you express these expressions easily, the following applies:
|
||||
|
||||
- These conditions can only be used in a `.When(...)` operator, and not within a `.And(...)`
|
||||
- If such a condition is used, calling `.And(...)` will apply the condition to all transitions generated by the `.When(...)` operator up until the next `Or(...)` operator if any exists.
|
||||
- For example:
|
||||
`.When(fx.BoolParameters(I, J).IsAnyFalse()).And(K.IsTrue())`
|
||||
- Is equivalent to:
|
||||
`.When(I.IsFalse()).And(K.IsTrue()).Or().When(J.IsFalse()).And(K.IsTrue())`
|
||||
|
||||
## Use the WhenConditions() operator to build in a `for` loop
|
||||
|
||||
The presence of the `.When(...)` operator can make it difficult to build conditions iteratively in a `for` loop.
|
||||
|
||||
For this purpose, use the `.WhenConditions()` operator. This will let you build conditions using the `.And(...)` operator.
|
||||
|
||||
```csharp
|
||||
var conditions = state.TransitionsFromEntry().WhenConditions();
|
||||
for (var i = 0; i < numberOfBits; i++)
|
||||
{
|
||||
conditions.And(parameter[i].IsEqualTo(bitMask[i]));
|
||||
}
|
||||
```
|
||||
|
||||
## Create many-to-one transitions using a `foreach` loop
|
||||
|
||||
There is no facility to create multiple identical transitions from multiple states to another state.
|
||||
|
||||
Use a regular `foreach` loop to achieve this.
|
||||
|
||||
```csharp
|
||||
foreach (var cancelWhenNotAllowed in new[] {auto, reverse, manual, custom, done})
|
||||
{
|
||||
cancelWhenNotAllowed.TransitionsTo(idle).When(allowSystemParameter.IsFalse());
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Reference manual
|
||||
|
||||
## Animator As Code (AacV0)
|
||||
- `static AacFlBase Create(AacConfiguration configuration)` /// Create an Animator As Code (AAC) base.
|
||||
|
||||
# Base (AacFlBase)
|
||||
|
||||
Reference in New Issue
Block a user