Giving Personality to Procedural Animations using Math
Summary
TLDRこのビデオスクリプトでは、伝統的なベクターアニメーションにおける補間曲線の概念から始まり、プロシージャルアニメーションにおけるキーフレームなしの柔軟性を紹介します。ゲームにおけるキャラクターの動きを制御する際に、逆運動学を使用し、リアルタイムの要求に応じて応答する例を説明します。さらに、補間曲線と同様の直感的な抽象化を用いて、動的な反応性を失わずに慣性とエネルギーを加える方法を提案し、数学的モデルを使って動きの特性を調整する方法を解説します。UnityのSmoothDamp関数を含む実践的な例を通じて、リアルタイムでの方程式の解決方法と、安定性を確保するための技術的な分析を紹介しています。
Takeaways
- 🎨 伝統的なベクターアニメーションでは、補間曲線という概念があり、これはアニメーションのキーフレーム間の動きを制御するものです。
- 🛠️ プロシージャルアニメーションでは、キーフレームの概念を排除することで、状況に応じた動きが可能になります。これはゲームにプロシージャルアニメーションを組み込む主な理由の一つです。
- 🤖 ゲームにおけるキャラクターの動きは、逆運動学を使用してリアルタイムの移動と向き変更の要求に応答するように制御できます。
- 🔗 補間曲線を使わずに動的な反応性を維持しながら、慣性のようなものを得る方法を探求しています。
- 📉 動的な入力xに対して、特徴的な動きをもたせた動的な出力をyとして生成する問題を定義しています。
- 🔄 第二階の微分方程式を用いて、動きの特性を定義し、k1, k2, k3というパラメータを用いて様々な動作を作り出します。
- 📏 f, zeta, rという3つの新しい変数を定義し、それらをシステムの動きの設計パラメータとして使用します。
- 🎚️ fは自然周波数、zetaは減衰係数、rは初期応答を制御する値です。これらを使ってキャラクターの動きの特性を調整できます。
- 👾 実時間でこの方程式を解決し、プロシージャルアニメーションの動きを実践的に使用するアプローチを探求しています。
- 📚 セミインPLICIT Euler法という、Euler法のバリエーションを使って、微分方程式を数値的に解決する方法を紹介しています。
- 🚫 周波数がフレームレートに比べて高すぎると、システムが不安定になる問題があります。これはより技術的な分析によって解決可能です。
- 🔍 状態空間表現を使用して、システムの安定性を解析し、時間ステップTが安定性閾値以下であることを保証する制約を導出します。
- 🛑 時間ステップが大きすぎる場合、安定性閾値に基づいて小さなステップに分割することで、計算を制御します。
- 🎮 ゲームエンジンでは、安定性を確保するためにk2を制限するか、極-零点一致法を使用してフレームごとにk1とk2の値を計算することができます。
- 🌐 f, zeta, rのようなシンプルなパラメトリック_motionモデルを使用することで、ゲームプレイ情報を伝えるための動きのバリエーションを簡単に実装できます。
Q & A
どのような概念が「補間曲線」として伝統的なベクターアニメーションに存在していますか?
-「補間曲線」はアニメーションのキーフレーム間の動きを記述する概念で、その形状に応じて動きをゆっくりと開始したり、ゆっくりと終了したり、キーフレームを予測したり、オーバーシュートさせたりすることができます。
アニメーション曲線の選択はどのように動作の感覚に影響を与えることがありますか?
-アニメーション曲線の選択は、運動の慣性とエネルギーの感覚に大きな影響を与え、アニメーターには運動を表現する上で多くの芸術的な表現力を与えます。
プロシージャルアニメーションにはどのような特徴がありますか?
-プロシージャルアニメーションはキーフレームの概念を全く排除し、システムが状況に応じて動くように自由にします。この柔軟性はゲームにプロシージャルアニメーションを組み込む主な理由の一つです。
逆運動学とは何であり、どのように使われますか?
-逆運動学は、キャラクターがリアルタイムの移動と向きを変える要求に応じて体と脚を動かす技術です。マウスで制御され、オンラインで実装方法に関するチュートリアルが見つけられます。
プロシージャルアニメーションでは補間曲線を使わずに慣性のような感覚をどのように表現できますか?
-プロシージャルアニメーションでは、補間曲線に類似する直感的な抽象化を使用して慣性のような感覚を実現しつつ、キーフレームなしシステムの動的な応答性を失わずにすることができる。
ゲームにおける動的な入力xに対して、どのように動的な出力をyとして追跡するようにシステムを設計しますか?
-ゲームからの動的な入力xに対して、入力xを追跡する一方で、運動の感じを伝える特質を持つ動的な出力をyとしてシステムを設計します。最も単純なケースでは、y=xであり、システムは与えられた入力に固く従います。
第二オーダーシステムとは何であり、どのように現実世界の力学的運動と関連していますか?
-第二オーダーシステムとは、最高到達する時間微分の次数が2である関係のことを指し、現実世界の力学的運動は第二オーダーであり、力を加えると加速度の変化、すなわち位置の二階微分に影響を与えるという事実に関連しています。
f, zeta, rという3つの新しい変数を定義するにあたり、それぞれがシステムの動きのどの部分を制御するのに使われますか?
-fはシステムの自然周波数で、入力の変化への応答速度と振動する傾向の周波数を記述しますが、運動の形状には影響しません。zetaは減衰係数で、システムが目標に達する様子を記述し、rはシステムの初期応答を制御します。
UnityのSmoothDamp関数とは何であり、どのように使われますか?
-UnityのSmoothDamp関数は、減衰係数が正確に1であることを特徴とする関数で、これはクリティカルダンピングと呼ばれ、目標値に向かって徐々に安定する動きを実現します。
半インPLICIT Euler法とはどのようなアルゴリズムで、どのように運動方程式をリアルタイムに解決するのに使われますか?
-半インPLICIT Euler法は、Euler法の変種であり、システムの運動方程式をリアルタイムに解決するために使用されます。位置と速度の推定値を更新し、最後の推定値を使って加速度の計算を行います。
物理ソルバーが不安定になる原因は何であり、どのように解決策を見つけるに至りますか?
-物理ソルバーが不安定になる主な原因は、大きなタイムステップとパラメータの比較により、エラーが蓄積され、そのエラーが自己増殖し始めることです。問題を標準形式に整理し、状態空間表現を使用して状態変数の影響を分析することで、解決策を見つけることができます。
ゲームエンジンで不都合な事態を防ぐためには、どのようなアプローチを取ることができますか?
-ゲームエンジンで不都合な事態を防ぐためには、最大安定タイムステップを計算し、実行時にタイムステップをそれに比較することができます。タイムステップが大きければ、安定性しきい値を下回る小さなステップに分割することができます。
f, zeta, rパラメータを使用してゲームプレイ情報を伝える方法とはどのようなものですか?
-f, zeta, rパラメータを使用してキャラクターの動きに異なるパラメータを割り当て、その状態の変化をプレイヤーに伝えることができます。これにより、意識、健康状態、ステータス効果などに基づいてキャラクターの動きが変化し、プレイヤーに状況を伝えます。
極-零_matching方法とは何であり、どのような利点がありますか?
-極-零_matching方法は、タイムステップに基づいてk1とk2の値をフレームごとに計算する手法で、非常に高速な動きに対してより正確な結果を生成する利点がありますが、追加の計算コストがかかるという欠点もあります。
Outlines
🎨 アニメーションの表現力:補間曲線と手続き型アニメーション
第1段落では、伝統的なベクターアニメーションにおける補間曲線の概念とその重要性が説明されています。補間曲線はアニメーションのキーフレーム間の動きを制御し、運動の慣性とエネルギーを表現するのに役立ちます。手続き型アニメーションではキーフレームを排除し、状況に応じた動きを実現できます。これはゲームに組み込む主な理由の一つです。例えば、ゲーム内の小さなロボットキャラクターが、リアルタイムの移動と向きの変更に応じて逆運動学を使用して体と脚を動かす様子が紹介されています。手続き型アニメーションでは補間曲線を使わずに動的な反応性を失わずに慣性のようなものを得る方法を探求しています。
🔧 動的な出力の実現:第二階数システムとパラメータの調整
第2段落では、動的に変化する入力xに対して、運動の感じを伝える特性を持つ動的な出力yを生成する方法について詳細に説明されています。単純なケースから始めて、入力に応じてシステムがどのように動くかを数学的に表現しています。第二階数システムとして知られる関係式に速度と加速度の項を加えることで、さまざまな興味深い動作を作り出します。しかし、これらのパラメータk1, k2, k3がどのように動作に影響を与えるかを直感的に理解することは難しいです。そこで、ステップ応答を使ってシステムの動的な特性を可視化し、新しい変数f, zeta, rを定義して、これらのパラメータを直感的にコントロールする方法を提案しています。
🛠 実時間での方程式の解決:半顕式オイラー法と安定性の確保
第3段落では、実時間で方程式を解決し、それをゲームに実践的に適用する方法について探求しています。オイラー法の変種である半顕式オイラー法を紹介し、その精度と複雑なベルレ積分と同等であることについて説明しています。状態変数として位置と速度を割り当て、フレームごとの更新ループを作成する方法を説明しています。しかし、共振周波数fがフレームレートに比較して高すぎるとシステムが不安定になる問題があります。この問題を解決するために、数学的な分析を行い、状態空間表現を使用して安定性閾値を計算し、大きなタイムステップが原因でエラーが蓄積されるのを防ぐ方法を提案しています。
Mindmap
Keywords
💡補間曲線
💡プロシージャルアニメーション
💡逆運動学
💡第二秩序システム
💡自然周波数
💡減衰係数
💡初期応答
💡イーラメーター法
💡状態空間表現
💡極座標匹配
Highlights
在传统矢量动画中,插值曲线的概念用于描述动画关键帧之间的运动方式,影响动画的惯性感和能量感。
程序化动画通过完全摆脱关键帧的概念,使系统能够根据情况需求自由移动,增加了适应性。
通过使用逆动力学,游戏中的角色能够实时响应移动和改变方向的请求。
在没有关键帧输入的情况下,无法使用插值曲线增加惯性感,需要类似插值曲线的直观抽象。
动态输入x与动态输出y之间的关系,以及如何通过方程引入速度和加速度项来引入有趣的动态。
第二阶系统的概念,以及如何通过牛顿第二定律理解现实世界中的机械运动。
通过调整k1, k2, 和 k3的值,可以产生多种不同的动态行为。
定义了f(自然频率)、zeta(阻尼系数)和r(初始响应)三个新变量,作为系统运动的设计参数。
f, zeta, 和 r各自控制系统响应的不同方面,提供了对动态行为的直观理解。
使用f, zeta, 和 r参数,可以调整角色动画的运动特性,如身体倾斜和头部滞后。
介绍了半隐式欧拉方法,用于实时解决微分方程,保持动态响应性。
通过状态变量(位置和速度)的更新,展示了半隐式欧拉方法的迭代过程。
当共振频率f相对于帧率过高时,系统可能变得不稳定,导致无限增长。
通过分析状态转移矩阵A,可以确定系统稳定性的条件。
系统稳定时,状态转移矩阵A的特征值的模必须小于1。
通过计算最大稳定时间步长T critical,可以在运行时避免系统不稳定。
介绍了极零匹配方法,用于根据时间步长计算k1和k2的值,以提高快速移动的准确性。
使用简单的参数运动模型,如f, zeta, r模型,可以为游戏角色的状态(如警觉、健康、状态效果)提供不同的运动参数。
通过这些参数的调节,可以向玩家传达游戏角色的情况,这是一种成本低廉且易于迭代的方法。
Transcripts
In traditional vector animation, there is a concept called interpolation curves,
which describes the way we move between one animation keyframe and the next
Depending on the shape of this curve, we can control motion to slowly ease in,
to slowly ease out, or even to anticipate and overshoot the keyframe
The choice of animation curves can greatly affect the feeling of inertia and energy in the motion
This grants the animator a good deal of artistic expression in describing motion
while keeping the number of keyframes economical
In procedural animation, however, it can be very powerful to do away with the concept of
keyframes entirely, freeing the system to move however the situation demands
This adaptability is one of the main reasons to incorporate procedural animation in games
For example, in the game I’m working on, I have this little robot character which moves its body
and legs using inverse kinematics in response to realtime requests to move and change orientation
I’m controlling it with my mouse here, and you can find tutorials for how to
and you can find tutorials for how to implement this sort of thing online
In game, this same flexibility allows the character to immediately respond
to changing gameplay situations
This is pretty nice, but because we don’t have keyframes for these inputs,
we can't really use interpolation curves to add that sense of inertia
What we’d really like is the ability to achieve something like this,
using an intuitive abstraction similar to interpolation curves,
but without losing out on the dynamic responsiveness of a keyframe-free system
Let’s start off by describing the problem we want to solve a bit more precisely
Given some dynamically changing input x from our game world,
we want to produce some dynamic output y which tracks the input x, but is imbued with
some sort of characteristic that conveys the way that we want the motion to feel
In the simplest case, y=x, and our system rigidly conforms to whatever input we give it
This isn’t very useful on its own, but it is a good starting point
Whatever changes we make to this equation, we’ll want to ensure that in the long term,
when x isn’t changing, y should eventually settle at x
To introduce the possibility for interesting dynamics,
let’s add some velocity and acceleration terms, represented here as
first and second order derivatives with respect to time, scaled by some values k1, k2, and k3
The dot notation here is just a shorthand way of writing time derivatives, which
you may have also seen written like this, or this
This type of relationship involving up to a second order time derivative
is known as a second order system
Mechanical motions in the real world tend to be second order, which you can think of as
being related to the fact that forces act on acceleration, the second derivative of
position, as described by Newton’s second law These forces usually arise out of stiffness,
where they are a function of displacement, or out of friction, where they are a function of
velocity, hence our choice of terms
For example, this is the equation of motion of a rigid body
being dragged along one axis through a linear damped spring
And this is the equation of motion for the SmoothDamp function in Unity
By playing around with these values k1, k2, and k3, we can generate a whole host of different
interesting behaviors, but as an artist, it might not be very intuitive
how these values map to those different behaviors
First, let’s simplify the visualization to look at the response to a single sudden change in the input
The resulting output is known as the step response, which is a standard way
of visualizing the dynamics of a system, and captures the essence of the system’s behavior
Next, using k1 k2 and k3, we’ll define 3 new variables: f, zeta, and r
We’ll use f, zeta, and r (which I’ll explain in a moment)
as the design parameters for the system’s motion
Under the hood, we can use them to compute k1, k2, and k3
In this new parameterization of f, zeta, and r, each term controls something
that we can develop some reasonable intuition about
f is the natural frequency of the system measured in Hz, or cycles per second
It describes the speed at which the system will respond to changes in the input, as well as the
frequency the system will tend to vibrate at, but does not affect the shape of the resulting motion
zeta is known as the damping coefficient
It describes how the system comes to settle at the target
When zeta is 0, vibration never dies down, and the system is undamped
Between values of 0 and 1, the system is underdamped, and will vibrate more,
or less, depending on the magnitude of zeta
When zeta is greater than 1, the system will not vibrate,
and instead slowly settle toward the target x, depending on the magnitude of zeta
The SmoothDamp function in Unity uses a zeta equal to exactly 1 which is known as critical damping
r is a value which controls the initial response of the system
When r is 0, the system takes time to begin accelerating from rest
When r is positive, the system reacts immediately to movement in x
When r is greater than 1, the system will overshoot the target
When r is negative, the system will anticipate the motion
When modeling a mechanical connection, a value of 2 for r is typical
Now, as artists, we have all we need to start playing with the
characteristics of the motion of our procedurally animated object
If we want to control the settling behavior of the system, we can play with zeta
If we want to control the initial response of the system, we can play with r
If we want to control how fast or slowly the system behaves, we can play with f
Coming back to this robot character, you can see I used an underdamped movement for the
body with zeta of 0.5, and r of -2 to give it some anticipation before fast movements
For a bit of extra flavor, I tilt the body towards the target position,
to further hint at the intent behind the motion I want the turning to be smooth, so for the body
orientation I use a zeta of 1 and r of 0 I do the same for the head orientation,
but with a smaller f, to make it lag behind the body as a sort of secondary motion
The cables are animated with a little trick that I explained over on twitter a while ago
Let’s now look at an approach for solving this equation in real-time,
so that we can begin using it in practice
There are many ways of numerically solving differential equations
such as our second order one, but a particularly straightforward one is called Euler’s method
Today, I will present a slight variation known as the semi-implicit Euler method,
which, for our system, happens to have the same accuracy as the more complex Verlet integration
First, we need to allocate some state variables: position and velocity
These are our estimates of y and its first derivative
At timestep 0, they need to be initialized to some initial values
Now, when the game is running, we’ll want to iteratively update these variables each frame
Let’s call the amount of time that passes between frames T
First, we update the position estimate by taking the previous iteration’s position
and adding T times the velocity
Next, we update the velocity by taking the previous iteration’s velocity
and adding T times the acceleration
To compute this acceleration, recall our equation of motion
We can solve for acceleration here
And substitute in our most recent estimates of position and velocity
Note that in semi-implicit Euler, we are using the updated position to compute velocity,
which is slightly different from the regular Euler method where we use the previous frame’s position
In the case that the input velocity, x dot, is unknown, we can also estimate it using historical
measurements, for example, by approximating it as the average velocity since the previous sample
This algorithm naturally translates almost line by line into code
If we were to implement this in-game, we would see that it does work as designed
I hooked up the code to allow for f, zeta, and r to be changed in real time,
and the system responds appropriately
There is one big issue that we’ll run into after a bit of experimentation though
Set the resonant frequency f too high relative to the frame rate, and the system becomes
completely unstable, launching itself to infinity
While it’s possible to simply keep f low enough such that this type of glitch is unlikely,
in a game engine, we would really like to have a stronger guarantee that something like
this doesn’t happen in corner cases, such as a lag spike
causing the time step to be much larger than anticipated
This is a problem that can be solved, but to understand it properly, we’ll have to
get a bit more technical with our analysis
The reason why physics solvers like Euler’s method can become unstable is that fundamentally,
they are feedback systems whose outputs from one iteration, here y and its derivative,
are fed back into later iterations of the computation
When the time step between frames is too large compared to the parameters,
it will start accumulating errors over time
Beyond a critical threshold, these errors will start to compound on themselves,
quickly leading to catastrophic failure
To compute this threshold, we’ll need to organize the problem into a more standard form
What we want to do is write the values of our state variables at frame n+1
in terms of our state variables at frame n
We can do this by substituting the first equation into the second, so now both our equations
have y and its derivative at frame n on the right-hand side
Now let’s expand this and collect like terms
This is a system of linear equations, which we can write in matrix notation
The system, written this way, is known as the state-space representation
This matrix A, known as the state transition matrix,
describes how each iteration of the state variables influence the next iteration
Intuitively, you can imagine that the feedback will be stable
If A doesn’t cause the state variables to grow
if A was a number instead of a matrix,
it would be easier to reason about the effect that it has on the system
For example, in this simpler system with only a single state variable and no external forces,
each iteration is equal to the previous iteration scaled by A
When the magnitude of A is less than 1, the magnitude of y at each iteration
will be smaller than the previous iteration, causing the system to stabilize over time
When the magnitude of A is greater than 1, the magnitude of y at each iteration will
be larger than the previous iteration, so the system will quickly grow beyond control
This is what we’d like to avoid
In our system, A is a 2x2 matrix, which doesn’t have
a magnitude that can directly be compared to 1
Instead, it has what we can think of as two separate magnitudes, called eigenvalues
We won’t get into it here, but suffice to say that both of these eigenvalues z1 and z2,
which are computed by finding the two solutions to this equation, must have a magnitude less than 1
for the system to settle over time
This is computed like this
Which expands to this quadratic polynomial of z
Which we can solve using the quadratic formula
Our system will be stable when the magnitude of this expression is less than 1
Solving this inequality gives us the following constraint,
that the time step T must be less than the square root of 4 times k2 plus k1 squared, subtract k1
In our code, we can compute this maximum stable time step, T critical,
and when running in-game, compare the time step against it
If the time step is too large,
we can divide it up into smaller steps that are below the stability threshold
Taking smaller steps requires performing more computations per frame
To avoid this, we might be able to get away with slowing down the dynamics instead
In this case, rather than constraining T, we can solve for k2 and constrain that
Clamping k2 to be above this value is not physically correct, but recall that our goal
here was just to prevent catastrophic failure, not to produce an accurate physics simulation
There are, of course, much more esoteric places you can take this analysis,
and in a previous draft of this video, I did go a bit further
For example, this frame-to-frame jittering behavior that sneaks in when the frequency
is sufficiently high, is caused by negative eigenvalues, so if we also take that into account
during our analysis, we can impose additional restrictions to ensure it doesn’t happen
In my own implementation, in case accuracy is needed, I opt to use a method known as
pole-zero matching to compute values for k1 and k2 per-frame based on the time step
This produces generally more accurate results for very fast movement,
but comes with this additional computation cost that might not suit every application
The idea behind using simple parametric motion models
like our f zeta r one extends some interesting possibilities
Just as an example, we could modulate the values of these parameters
to convey gameplay information, so characters could move with different parameters
depending on their state of awareness, health, status effects, and so on
This would help communicate the situation to players through motion
in a way that’s cheap and easy to iterate on
In any case, I’ve spent long enough working on this video instead of my game,
so
hopefully this has been an approachable but substantial introduction to
some interesting concepts, or maybe a presentation of some applications of
mathematics in an unusual but interesting context
As always, thanks for watching
5.0 / 5 (0 votes)