c# 动画_动画C#工作
c# 动画
In Unity 2018.2, the Animation C# Jobs feature extends the animation Playables with the C# Job System released with 2018.1. It gives you the freedom to create original solutions when implementing your animation system, and improve performance with safe multithreaded code at the same time. Animation C# Jobs is a low-level API that requires a solid understanding of the Playable API. It’s therefore aimed at developers who are interested in extending the Unity animation system beyond its out-of-the-box capabilities. If that sounds like you, read on to find out when is it a good idea to use it and how to get the most out of it!
在Unity 2018.2,动画C#乔布斯功能可以延长动画Playables与C#作业系统与2018.1释放。 它使您可以自由地在实施动画系统时创建原始解决方案,并同时通过安全的多线程代码提高性能。 Animation C#Jobs是一个低级API,需要对Playable API有深入的了解。 因此,它面向有兴趣将Unity动画系统扩展到其现成功能之外的开发人员。 如果听起来像您,请继续阅读以了解何时使用它是一个好主意,以及如何充分利用它!
With Animation C# Jobs, you can write C# code that will be invoked at user-defined places in the PlayableGraph, and thanks to the C# Job System, the users can harness the power of modern multicore hardware. For projects which see a significant cost in C# scripts on the main thread, some of the animation tasks can be parallelized. This unlocks valuable performance gains. The user made C# scripts can modify the animation stream that flows through the PlayableGraph.
使用Animation C#Jobs,您可以编写将在PlayableGraph中用户定义的位置调用的C#代码,并且由于有了C#Job System,用户可以利用现代多核硬件的功能。 对于在主线程上的C#脚本中花费巨大的项目,可以并行执行某些动画任务。 这可释放宝贵的性能收益。 用户制作的C#脚本可以修改流过PlayableGraph的动画流。
特征 (Features)
- New Playable node: AnimationScriptPlayable 新的Playable节点:AnimationScriptPlayable
- Control the animation data stream in the PlayableGraph 在PlayableGraph中控制动画数据流
- Multithreaded C# code 多线程C#代码
免责声明 (Disclaimer)
The Animation C# Jobs is still an experimental feature (living in UnityEngine.Experimental.Animations). The API might change a bit over time, depending on your feedback. Please join the discussion on our Animation Forum!
Animation C#Jobs仍然是一个实验性功能(存在于UnityEngine.Experimental.Animations中)。 该API可能会随时间变化,具体取决于您的反馈。 请加入我们的动画论坛中的讨论!
用例 (Use cases)
So, say, you want to have a foot-locking feature for your brand new dragon character. You could code that with a regular MonoBehaviour, but all the code would be run in the main thread, and not until the animation pass is over. With the Animation C# Jobs, you can write your algorithm and use it directly in a custom Playable node in your PlayableGraph, and the code will run during PlayableGraph processing, in a separate thread.
因此,例如,您希望为全新的龙角色具有脚锁功能。 您可以使用常规的MonoBehaviour进行编码,但是所有代码都将在主线程中运行,直到动画传递结束为止。 使用Animation C#作业,您可以编写算法并将其直接用于PlayableGraph的自定义Playable节点中,并且代码将在PlayableGraph处理期间在单独的线程中运行。
Or, if you didn’t want to animate the tail of your dragon, the Animation C# Jobs would be the perfect tool for setting up the ability to procedurally compute this movement.
或者,如果您不想为龙的尾部设置动画,Animation C#Jobs将是设置以程序方式计算此运动能力的理想工具。
Animation C# Jobs also gives you the ability to write a super-specific LookAt algorithm that would allow you to target the 10 bones in your dragon’s neck, for example.
动画C#Jobs还使您能够编写超特定的LookAt算法,例如,该算法将使您能够瞄准龙脖子上的10条骨头。
Another great example would be making your own animation mixer. Let’s say you have something very specific that you need – a node that takes positions from one input, rotations from another, scales from a third node, and mixes them all together into a single animation stream – Animation C# Jobs gives you the ability to get creative and build for your specific needs.
另一个很好的例子是制作自己的动画混合器。 假设您需要一些非常具体的东西-一个节点从一个输入获取位置,从另一个输入获取旋转,从第三个节点缩放,然后将它们全部混合到一个动画流中-Animation C#Jobs使您能够广告素材,并根据您的特定需求进行构建。
例子 (Examples)
Before getting into the meaty details of how to use the Animation C# Jobs API, let’s take a look at some examples that showcase what is possible to do with this feature.
在深入探讨如何使用Animation C#Jobs API的细节之前,让我们看一些示例,这些示例展示了可以使用此功能做什么。
All the examples are available on our Animation Jobs Samples GitHub page. To install it you can either git clone it or download the latest release. Once installed, the examples have their own scenes which are all located in the “Scenes” directory:
所有示例都可以在我们的Animation Jobs Samples GitHub页面上找到 。 要安装它,您可以git clone它或下载最新版本。 安装后,这些示例将具有自己的场景,这些场景均位于“ Scenes”目录中:
看着 (LookAt)
The LookAt is a very simple example that orients a bone (also called a joint) toward an effector. In the example below, you can see how it works on a quadruped from our 3D Game Kit package.
LookAt是一个非常简单的示例,它使骨骼(也称为关节)朝向效应器定向。 在下面的示例中,您可以看到它如何在我们的3D游戏工具包中的四足动物上工作。
TwoBoneIK (TwoBoneIK)
The TwoBoneIK implements a simple two-bone IK algorithm that can be applied to three consecutive joints (e.g. a human arm or leg). The character in this demo is made with a generic humanoid avatar.
TwoBoneIK实现了一种简单的两骨骼IK算法,该算法可以应用于三个连续的关节(例如,人的手臂或腿)。 本演示中的角色是使用通用人形头像制作的。
FullbodyIK (FullbodyIK)
The FullbodyIK example shows how to modify values in a humanoid avatar (e.g. goals, hints, look-at, body rotation, etc.). This example, in particular, uses the human implementation of the animation stream.
FullbodyIK示例显示了如何在人形化身中修改值(例如,目标,提示,注视,身体旋转等)。 特别是,此示例使用了动画流的人工实现。
减震 (Damping)
The Damping example implements a damping algorithm that can be applied to an animal tail or a human ponytail. It illustrates how to generate a procedural animation.
阻尼示例实现了一种阻尼算法,该算法可以应用于动物的尾巴或人的马尾辫。 它说明了如何生成程序动画。
SimpleMixer (SimpleMixer)
The SimpleMixer is a sort of “Hello, world!” of animation mixers. It takes two input streams (e.g. animation clips) and mixes them together based on a blending value, exactly like an AnimationMixerPlayable would do.
SimpleMixer是一种“您好,世界!” 动画混合器。 它需要两个输入流(例如动画剪辑),并根据混合值将它们混合在一起,就像AnimationMixerPlayable那样。
加权面具混合器 (WeightedMaskMixer)
The WeigthedMaskMixer example is a bit more advanced animation mixer. It takes two input streams and mixes them together based on a weight mask that defines how to blend each and every joint. For example, you can play a classic idle animation and take just the animation of the arms from another animation clip. Or you can smooth the blend of an upper-body animation by applying successively higher weights on the spine bones.
WeigthedMaskMixer示例是更高级的动画混合器。 它需要两个输入流,并根据权重掩码将它们混合在一起,该权重掩码定义了如何混合每个关节。 例如,您可以播放经典的空闲动画,并仅从另一个动画剪辑中获取手臂的动画。 或者,您可以通过对脊椎骨骼依次施加更高的权重来平滑上身动画的混合。
API (API)
The Animation C# Jobs feature is powered by the Playable API. It comes with three new structs: AnimationScriptPlayable, IAnimationJob, and AnimationStream.
动画C#作业功能由Playable API提供支持。 它带有三个新结构:AnimationScriptPlayable,IAnimationJob和AnimationStream。
AnimationScriptPlayable和IAnimationJob (AnimationScriptPlayable and the IAnimationJob)
The AnimationScriptPlayable is a new animation Playable which, like any other Playable, can be added anywhere in a PlayableGraph. The interesting thing about it is that it contains an animation job and acts as a proxy between the PlayableGraph and the job. The job is a user-defined struct that implements IAnimationJob.
AnimationScriptPlayable是一个新的动画Playable,与其他任何Playable一样,可以将其添加到PlayableGraph中的任何位置。 有趣的是,它包含一个动画作业,并充当PlayableGraph和该作业之间的代理。 作业是实现IAnimationJob的用户定义结构。
A regular job processes the Playable inputs streams and mixes the result in its stream. The animation process is separated in two passes and each pass has its own callback in IPlayableJob:
常规作业处理Playable输入流,并将结果混合到其流中。 动画过程分为两个阶段,每个阶段在IPlayableJob中都有自己的回调:
-
ProcessRootMotion handles the root transform motion, it is always called before ProcessAnimation and it determines if ProcessAnimation should be called (it depends on the Animator culling mode);
ProcessRootMotion处理根变换运动,它总是在ProcessAnimation之前调用,并确定是否应调用ProcessAnimation(这取决于Animator的剔除模式 );
- ProcessAnimation is for everything else that is not the root motion. ProcessAnimation适用于不是根源运动的所有其他内容。
The example below is like the “Hello, world!” of Animation C# Jobs. It does nothing at all, but it allows us to see how to create an AnimationScriptPlayable with an animation job:
下面的示例就像“ Hello,world!” C#作业集。 它什么也不做,但是它使我们看到了如何用动画工作创建AnimationScriptPlayable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
using UnityEngine.Experimental.Animations;
public struct AnimationJob : IAnimationJob
{
public void ProcessRootMotion(AnimationStream stream)
{
}
public void ProcessAnimation(AnimationStream stream)
{
}
}
[RequireComponent(typeof(Animator))]
public class AnimationScriptExample : MonoBehaviour
{
PlayableGraph m_Graph;
AnimationScriptPlayable m_ScriptPlayable;
void OnEnable()
{
// Create the graph.
m_Graph = PlayableGraph.Create("AnimationScriptExample");
// Create the animation job and its playable.
var animationJob = new AnimationJob();
m_ScriptPlayable = AnimationScriptPlayable.Create(m_Graph, animationJob);
// Create the output and link it to the playable.
var output = AnimationPlayableOutput.Create(m_Graph, "Output", GetComponent<Animator>());
output.SetSourcePlayable(m_ScriptPlayable);
}
void OnDisable()
{
m_Graph.Destroy();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
using UnityEngine ;
using UnityEngine . Playables ;
using UnityEngine . Animations ;
using UnityEngine . Experimental . Animations ;
public struct AnimationJob : IAnimationJob
{
public void ProcessRootMotion ( AnimationStream stream )
{
}
public void ProcessAnimation ( AnimationStream stream )
{
}
}
[ RequireComponent ( typeof ( Animator ) ) ]
public class AnimationScriptExample : MonoBehaviour
{
PlayableGraph m_Graph ;
AnimationScriptPlayable m_ScriptPlayable ;
void OnEnable ( )
{
// Create the graph.
m_Graph = PlayableGraph . Create ( "AnimationScriptExample" ) ;
// Create the animation job and its playable.
var animationJob = new AnimationJob ( ) ;
m_ScriptPlayable = AnimationScriptPlayable . Create ( m_Graph , animationJob ) ;
// Create the output and link it to the playable.
var output = AnimationPlayableOutput . Create ( m_Graph , "Output" , GetComponent < Animator > ( ) ) ;
output . SetSourcePlayable ( m_ScriptPlayable ) ;
}
void OnDisable ( )
{
m_Graph . Destroy ( ) ;
}
}
|
The stream passed as a parameter of the IAnimationJob methods is the one you will be working on during each processing pass.
作为IAnimationJob方法的参数传递的流是您将在每次处理过程中使用的流。
By default, all the AnimationScriptPlayable inputs are processed. In the case of only one input (a.k.a. a post-process job), this stream will contain the result of the processed input. In the case of multiple inputs (a.k.a. a mix job), it’s preferable to process the inputs manually. To do so, the method AnimationScriptPlayable.SetProcessInputs(bool) will enable or disable the processing passes on the inputs. To trigger the processing of an input and acquire the resulting stream in manual mode, call AnimationStream.GetInputStream().
默认情况下,将处理所有AnimationScriptPlayable输入。 如果只有一个输入(也称为后处理作业),则此流将包含已处理输入的结果。 在有多个输入(又称混合作业)的情况下,最好手动处理输入。 为此,方法AnimationScriptPlayable.SetProcessInputs(bool)将启用或禁用对输入的处理。 要触发输入的处理并以手动模式获取结果流,请调用AnimationStream.GetInputStream()。
AnimationStream和句柄 (AnimationStream and the handles)
The AnimationStream gives you access to the data that flows through the graph from one playable to another. It gives access to all the values animated by the Animator component
通过AnimationStream,您可以访问从一个可播放对象到另一个可播放对象的流经图表的数据。 它可以访问由Animator组件设置的所有动画值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public struct AnimationStream
{
public bool isValid { get; }
public float deltaTime { get; }
public Vector3 velocity { get; set; }
public Vector3 angularVelocity { get; set; }
public Vector3 rootMotionPosition { get; }
public Quaternion rootMotionRotation { get; }
public bool isHumanStream { get; }
public AnimationHumanStream AsHuman();
public int inputStreamCount { get; }
public AnimationStream GetInputStream(int index);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public struct AnimationStream
{
public bool isValid { get ; }
public float deltaTime { get ; }
public Vector3 velocity { get ; set ; }
public Vector3 angularVelocity { get ; set ; }
public Vector3 rootMotionPosition { get ; }
public Quaternion rootMotionRotation { get ; }
public bool isHumanStream { get ; }
public AnimationHumanStream AsHuman ( ) ;
public int inputStreamCount { get ; }
public AnimationStream GetInputStream ( int index ) ;
}
|
It isn’t possible to have a direct access to the stream data since the same data can be at a different offset in the stream from one frame to the other (for example, by adding or removing an AnimationClip in the graph). The data may have moved, or may not exist anymore in the stream. To ensure the safety and validity of those accesses, we’re introducing two sets of handles: the stream and the scene handles, which each have a transform and a component property handle.
无法直接访问流数据,因为相同的数据可能在流中从一帧到另一帧的偏移量不同(例如,通过在图形中添加或删除AnimationClip)。 数据可能已移动,或可能不再存在于流中。 为了确保这些访问的安全性和有效性,我们引入了两组句柄:流和场景句柄,它们分别具有转换和组件属性句柄。
流处理 (The stream handles)
The stream handles manage, in a safe way, all the accesses to the AnimationStream data. If an error occurs the system throws a C# exception. There are two types of stream handles: TransformStreamHandle and PropertyStreamHandle.
流处理以安全的方式管理对AnimationStream数据的所有访问。 如果发生错误,系统将引发C#异常。 流句柄有两种类型:TransformStreamHandle和PropertyStreamHandle。
The TransformStreamHandle manages Transform and takes care of the transform hierarchy. That means you can change the local or global transform position in the stream, and future position requests will give predictable results.
TransformStreamHandle管理Transform并负责转换层次结构。 这意味着您可以更改流中的局部或全局变换位置,将来的位置请求将提供可预测的结果。
The PropertyStreamHandle manages all other properties that the system can animate and find on the other components. For instance, it can be used to read, or write, the value of the Light.m_Intensity property.
PropertyStreamHandle管理系统可以设置动画并在其他组件上找到的所有其他属性。 例如,它可以用于读取或写入Light.m_Intensity属性的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public struct TransformStreamHandle
{
public bool IsValid(AnimationStream stream);
public bool IsResolved(AnimationStream stream);
public void Resolve(AnimationStream stream);
public void SetLocalPosition(AnimationStream stream, Vector3 position);
public Vector3 GetLocalPosition(AnimationStream stream);
public void SetLocalRotation(AnimationStream stream, Quaternion rotation);
public Quaternion GetLocalRotation(AnimationStream stream);
public void SetLocalScale(AnimationStream stream, Vector3 scale);
public Vector3 GetLocalScale(AnimationStream stream);
public void SetPosition(AnimationStream stream, Vector3 position);
public Vector3 GetPosition(AnimationStream stream);
public void SetRotation(AnimationStream stream, Quaternion rotation);
public Quaternion GetRotation(AnimationStream stream);
}
public struct PropertyStreamHandle
{
public bool IsValid(AnimationStream stream);
public bool IsResolved(AnimationStream stream);
public void Resolve(AnimationStream stream);
public void SetFloat(AnimationStream stream, float value);
public float GetFloat(AnimationStream stream);
public void SetInt(AnimationStream stream, int value);
public int GetInt(AnimationStream stream);
public void SetBool(AnimationStream stream, bool value);
public bool GetBool(AnimationStream stream);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public struct TransformStreamHandle
{
public bool IsValid ( AnimationStream stream ) ;
public bool IsResolved ( AnimationStream stream ) ;
public void Resolve ( AnimationStream stream ) ;
public void SetLocalPosition ( AnimationStream stream , Vector3 position ) ;
public Vector3 GetLocalPosition ( AnimationStream stream ) ;
public void SetLocalRotation ( AnimationStream stream , Quaternion rotation ) ;
public Quaternion GetLocalRotation ( AnimationStream stream ) ;
public void SetLocalScale ( AnimationStream stream , Vector3 scale ) ;
public Vector3 GetLocalScale ( AnimationStream stream ) ;
public void SetPosition ( AnimationStream stream , Vector3 position ) ;
public Vector3 GetPosition ( AnimationStream stream ) ;
public void SetRotation ( AnimationStream stream , Quaternion rotation ) ;
public Quaternion GetRotation ( AnimationStream stream ) ;
}
public struct PropertyStreamHandle
{
public bool IsValid ( AnimationStream stream ) ;
public bool IsResolved ( AnimationStream stream ) ;
public void Resolve ( AnimationStream stream ) ;
public void SetFloat ( AnimationStream stream , float value ) ;
public float GetFloat ( AnimationStream stream ) ;
public void SetInt ( AnimationStream stream , int value ) ;
public int GetInt ( AnimationStream stream ) ;
public void SetBool ( AnimationStream stream , bool value ) ;
public bool GetBool ( AnimationStream stream ) ;
}
|
场景处理 (The scene handles)
The scene handles are another form of safe access to any values, but from the scene rather than from the AnimationStream. As for the stream handles, there are two types of scene handles: TransformSceneHandle and PropertySceneHandle.
场景句柄是安全访问任何值的另一种形式,但是可以从场景而不是从AnimationStream进行访问。 至于流句柄,有两种类型的场景句柄:TransformSceneHandle和PropertySceneHandle。
A concrete usage of a scene handle is to implement an effector for a foot IK. The IK effector is usually a GameObject not animated by an Animator, and therefore external to the transforms modified by the animation clips in the PlayableGraph. The job needs to know the global position of the IK effector in order to calculate the desired position of the foot. Thus the IK effector is accessed through a scene handle, while stream handles are used for the leg bones.
场景手柄的具体用法是为脚IK实施效应器。 IK效果器通常是不由Animator动画的GameObject,因此在PlayableGraph中由动画剪辑修改的变换的外部。 这项工作需要知道IK效应器的整体位置,以便计算脚的所需位置。 因此,可以通过场景手柄访问IK效果器,而将流手柄用于腿部骨骼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public struct TransformSceneHandle
{
public bool IsValid(AnimationStream stream);
public void SetLocalPosition(AnimationStream stream, Vector3 position);
public Vector3 GetLocalPosition(AnimationStream stream);
public void SetLocalRotation(AnimationStream stream, Quaternion rotation);
public Quaternion GetLocalRotation(AnimationStream stream);
public void SetLocalScale(AnimationStream stream, Vector3 scale);
public Vector3 GetLocalScale(AnimationStream stream);
public void SetPosition(AnimationStream stream, Vector3 position);
public Vector3 GetPosition(AnimationStream stream);
public void SetRotation(AnimationStream stream, Quaternion rotation);
public Quaternion GetRotation(AnimationStream stream);
}
public struct PropertySceneHandle
{
public bool IsValid(AnimationStream stream);
public bool IsResolved(AnimationStream stream);
public void Resolve(AnimationStream stream);
public void SetFloat(AnimationStream stream, float value);
public float GetFloat(AnimationStream stream);
public void SetInt(AnimationStream stream, int value);
public int GetInt(AnimationStream stream);
public void SetBool(AnimationStream stream, bool value);
public bool GetBool(AnimationStream stream);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public struct TransformSceneHandle
{
public bool IsValid ( AnimationStream stream ) ;
public void SetLocalPosition ( AnimationStream stream , Vector3 position ) ;
public Vector3 GetLocalPosition ( AnimationStream stream ) ;
public void SetLocalRotation ( AnimationStream stream , Quaternion rotation ) ;
public Quaternion GetLocalRotation ( AnimationStream stream ) ;
public void SetLocalScale ( AnimationStream stream , Vector3 scale ) ;
public Vector3 GetLocalScale ( AnimationStream stream ) ;
public void SetPosition ( AnimationStream stream , Vector3 position ) ;
public Vector3 GetPosition ( AnimationStream stream ) ;
public void SetRotation ( AnimationStream stream , Quaternion rotation ) ;
public Quaternion GetRotation ( AnimationStream stream ) ;
}
public struct PropertySceneHandle
{
public bool IsValid ( AnimationStream stream ) ;
public bool IsResolved ( AnimationStream stream ) ;
public void Resolve ( AnimationStream stream ) ;
public void SetFloat ( AnimationStream stream , float value ) ;
public float GetFloat ( AnimationStream stream ) ;
public void SetInt ( AnimationStream stream , int value ) ;
public int GetInt ( AnimationStream stream ) ;
public void SetBool ( AnimationStream stream , bool value ) ;
public bool GetBool ( AnimationStream stream ) ;
}
|
AnimationJobExtensions (AnimationJobExtensions)
The last piece is the AnimationJobExtension class. It’s the glue that makes it all work. It extends the Animator to create the four handles seen above, thanks to these four methods: BindStreamTransform, BindStreamProperty, BindSceneTransform, and BindSceneProperty.
最后一块是AnimationJobExtension类。 正是胶水使所有这些都起作用。 归功于这四个方法,它扩展了Animator来创建上述四个句柄:BindStreamTransform,BindStreamProperty,BindSceneTransform和BindSceneProperty。
1
2
3
4
5
6
7
8
|
public static class AnimatorJobExtensions
{
public static TransformStreamHandle BindStreamTransform(this Animator animator, Transform transform);
public static PropertyStreamHandle BindStreamProperty(this Animator animator, Transform transform, Type type, string property);
public static TransformSceneHandle BindSceneTransform(this Animator animator, Transform transform);
public static PropertySceneHandle BindSceneProperty(this Animator animator, Transform transform, Type type, string property);
}
|
1
2
3
4
5
6
7
8
|
public static class AnimatorJobExtensions
{
public static TransformStreamHandle BindStreamTransform ( this Animator animator , Transform transform ) ;
public static PropertyStreamHandle BindStreamProperty ( this Animator animator , Transform transform , Type type , string property ) ;
public static TransformSceneHandle BindSceneTransform ( this Animator animator , Transform transform ) ;
public static PropertySceneHandle BindSceneProperty ( this Animator animator , Transform transform , Type type , string property ) ;
}
|
The “BindStream” methods can be used to create handles on already animated properties or for newly animated properties in the stream.
“ BindStream”方法可用于在流中已动画的属性上或新动画的属性上创建句柄。
也可以看看 (See also)
API documentation:
API文档:
If you encounter a bug, please file it using the Bug Reporter built in Unity.
如果遇到错误,请使用Unity内置的错误报告程序将其归档。
For any feedback on this experimental feature, please go this forum thread: Animation C# jobs in 2018.2a5
有关此实验功能的任何反馈,请访问此论坛主题: 2018.2a5中的Animation C#工作
c# 动画