2011-09-09 47 views
12

我試圖在代碼中模擬動畫效果(幾乎任何語言都會這樣做,因爲它看起來像是數學而不是語言)。本質上,它是對質量彈簧系統的仿真。我一直在尋找WPF/Silverlight的ElasticEase,這看起來和我正在尋找的東西非常接近,但並不完全。彈簧質量系統的阻尼效果(或者這是ElasticEase?)

首先,這裏是我正在尋找的東西 - 一個物體,行進一定的秒數,擊中一個位置,然後立即放慢並振盪一段時間,以便在阻尼是在同一點休息一段時間應用。所以想象這個,假設我有一個600瓦/ 900小時的畫布,並且我有一個正方形,開始在一個TranslateTransform.Y中從900px到150px進行動畫處理。需要4秒才能達到150像素高度(每秒187.5像素),在此階段它會被阻擋,並且在0.4秒(每秒87.5像素)到115像素高度之間只會傳輸約35像素,然後反彈1秒到163像素高度(每秒48像素和48像素),然後反彈回146像素(每秒17像素和17像素),依此類推,直到眩暈減慢到最終的150像素。熬夜時間是16秒。

我上述的例子是左上方藍色長方形這裏: enter image description here

這裏是什麼,我會提前知道 - 秒的像素間距和數量需要從A點到達B點,曇花一現的秒數。像羣衆這樣的事情似乎並不重要。

我試過ElasticEase,這個問題似乎是我無法讓物體在沒有緩動4秒的情況下行駛,然後在接下來的16秒內「跳動」。該.Springiness也總是太多,即使我將其設置爲像20

ILSpy展示的其功能實在是高數:

protected override double EaseInCore(double normalizedTime) 
     { 
      double num = Math.Max(0.0, (double)this.Oscillations); 
      double num2 = Math.Max(0.0, this.Springiness); 
      double num3; 
      if (DoubleUtil.IsZero(num2)) 
      { 
       num3 = normalizedTime; 
      } 
      else 
      { 
       num3 = (Math.Exp(num2 * normalizedTime) - 1.0)/(Math.Exp(num2) - 1.0); 
      } 
      return num3 * Math.Sin((6.2831853071795862 * num + 1.5707963267948966) * normalizedTime); 
     } 

我已經包括2個視頻,以及一個Excel壓縮文件夾中的文件DropBox。我想這個問題將更多的是一個工作正在進行中,人們會問更多的澄清問題。

(免責聲明:我不知道我說的是當它涉及到很多這方面的東西)

+0

你的問題是(如你所懷疑的)數學/物理學。這是一個標準的第一次物理過程阻尼振盪問題,但它需要幾百個單詞(以及一些數學公式,這在SO中非常糟糕)才能完全解釋。基本問題是阻尼是指數的,所以當你離散時,尾部振盪就會丟失。你確定你想要一個完整的解釋,而不僅僅是http://en.wikipedia.org/wiki/Damping? :) –

+0

謝謝@belisarius。這個數學/物理學真的超越了我,特別是考慮到上面的例子似乎沒有考慮到質量。您可以提供的任何解釋和/或代碼示例可能會幫助我開始使用已知的變量,這些已知變量對我來說是最有幫助的。 –

+0

我會嘗試,但是正如我預見的那樣,需要我花很多時間才能做得更好,也許一些物理學家/學生追逐賞金可能會讓我期待:)。順便說一下,質量是在那裏,但問題的所有其他常數(彈性常數和阻尼係數)除以質量得到質量獨立的問題(因爲Fm * a == 0等於零,您可以分成所有的術語,無論你想要什麼常數)。 F是兩個力的組合,彈性力與彈性常數k和位置成比例,阻尼力與速度和阻尼常數成比例。 –

回答

8

跳過物理,只是直奔方程。

參數: 「這是什麼,我會提前知道 - 像素距離[d]和秒數[T0]它從A點到達B點,秒振盪數[T1 ]。「另外,我將添加一個自由參數:振盪的最大尺寸,Amax,阻尼時間常數Tc和幀頻Rf,即在什麼時候需要一個新的位置值。我想你不希望永遠計算這個,所以我就做10秒,TTOTAL,但也有各種各樣的合理的止損條件...

代碼: 下面的代碼(在Python )。最主要的是該方程中,在def Y(t)發現:

from numpy import pi, arange, sin, exp 

Ystart, D = 900., 900.-150. # all time units in seconds, distance in pixels, Rf in frames/second 
T0, T1, Tc, Amax, Rf, Ttotal = 5., 2., 2., 90., 30., 10. 

A0 = Amax*(D/T0)*(4./(900-150)) # basically a momentum... scales the size of the oscillation with the speed 

def Y(t): 
    if t<T0: # linear part 
     y = Ystart-(D/T0)*t 
    else: # decaying oscillations 
     y = Ystart-D-A0*sin((2*pi/T1)*(t-T0))*exp(-abs(T0-t)/Tc) 
    return y 

y_result = [] 
for t in arange(0, Ttotal, 1./Rf): # or one could do "for i in range(int(Ttotal*Rf))" to stick with ints  
    y = Y(t) 
    y_result.append(y) 

的想法是直線運動直到點,隨後是衰減振盪。振盪由sin提供,衰減值乘以exp。當然,改變參數以獲得任何距離,振盪大小等等,你想要的。

enter image description here

筆記

  1. 的大多數人的意見所提出的建議物理方法。我沒有使用它們,因爲如果一個動作指定了某個動作,它有點過度 - 從物理開始,進入微分方程,然後計算動作,並調整參數以得到最終的結果。不妨直接走向最後的東西。除非,就是說,一個人對他們想從事的物理學有一個直覺。
  2. 通常在像這樣的問題想要保持一個連續的速度(一階導數),但你說「立即放慢」,所以我沒有這樣做。
  3. 請注意,振盪的週期和振幅不會完全符合應用阻尼時的規定,但這可能比您關心的更詳細。
  4. 如果您需要將其表示爲一個單一的等式,您可以使用「Heaviside函數」來打開和關閉貢獻。

在作出這一過長的風險,我意識到我可以做在GIMP的GIF,所以這是什麼樣子:

enter image description here

我可以張貼的完整代碼,使這些情節如果有興趣,但基本上我只是用每個時間步的不同D和T0來調用Y.如果我再次這樣做,我可以增加阻尼(即減少Tc),但這有點麻煩,所以我現在就離開它。

+0

同意,你其實不需要質量。擁有速度和加速度就足夠了。在進行彈簧圖佈局時發現這一點。天真的做法是以速度,質量,力量等方式進行全面的物理模擬。然後發現你只是在煩人的副作用下做雙重工作。 – gjvdkamp

+0

多麼輝煌的解釋!謝謝!我將在接下來的一兩天內處理這個問題,看看我能否實現它。 –

+0

@Otaku看起來在D的派生是不連續的。當人體第一次達到靜息位置時,您可能會感覺到速度的突然變化 –

5

我在@ tom10的思路。 (我也考慮過一個IEasingFunction,其中採用了IList<IEasingFunction>,但是從現有的行爲中篡改想要的行爲會很棘手)。

// Based on the example at 
// http://msdn.microsoft.com/en-us/library/system.windows.media.animation.easingfunctionbase.aspx 
namespace Org.CheddarMonk 
{ 
    public class OtakuEasingFunction : EasingFunctionBase 
    { 
     // The time proportion at which the cutoff from linear movement to 
     // bounce occurs. E.g. for a 4 second movement followed by a 16 
     // second bounce this would be 4/(4 + 16) = 0.2. 
     private double _CutoffPoint; 
     public double CutoffPoint { 
      get { return _CutoffPoint; } 
      set { 
       if (value <= 0 || value => 1 || double.IsNaN(value)) { 
        throw new ArgumentException(); 
       } 
       _CutoffPoint = value; 
      } 
     } 

     // The size of the initial bounce envelope, as a proportion of the 
     // animation distance. E.g. if the animation moves from 900 to 150 
     // and you want the maximum bounce to be no more than 35 you would 
     // set this to 35/(900 - 150) ~= 0.0467. 
     private double _EnvelopeHeight; 
     public double EnvelopeHeight { 
      get { return _EnvelopeHeight; } 
      set { 
       if (value <= 0 || double.IsNaN(value)) { 
        throw new ArgumentException(); 
       } 
       _EnvelopeHeight = value; 
      } 
     } 

     // A parameter controlling how fast the bounce height should decay. 
     // The higher the decay, the sooner the bounce becomes negligible. 
     private double _EnvelopeDecay; 
     public double EnvelopeDecay { 
      get { return _EnvelopeDecay; } 
      set { 
       if (value <= 0 || double.IsNaN(value)) { 
        throw new ArgumentException(); 
       } 
       _EnvelopeDecay = value; 
      } 
     } 

     // The number of half-bounces. 
     private int _Oscillations; 
     public int Oscillations { 
      get { return _Oscillations; } 
      set { 
       if (value <= 0) { 
        throw new ArgumentException(); 
       } 
       _Oscillations = value; 
      } 
     } 

     public OtakuEasingFunction() { 
      // Sensible default values. 
      CutoffPoint = 0.7; 
      EnvelopeHeight = 0.3; 
      EnvelopeDecay = 1; 
      Oscillations = 3; 
     } 

     protected override double EaseInCore(double normalizedTime) { 
      // If we get an out-of-bounds value, be nice. 
      if (normalizedTime < 0) return 0; 
      if (normalizedTime > 1) return 1; 

      if (normalizedTime < _CutoffPoint) { 
       return normalizedTime/_CutoffPoint; 
      } 

      // Renormalise the time. 
      double t = (normalizedTime - _CutoffPoint)/(1 - _CutoffPoint); 
      double envelope = EnvelopeHeight * Math.Exp(-t * EnvelopeDecay); 
      double bounce = Math.Sin(t * Oscillations * Math.PI); 
      return envelope * bounce; 
     } 

     protected override Freezable CreateInstanceCore() { 
      return new OtakuEasingFunction(); 
     } 
    } 
} 

這是未經測試的代碼,但是如果出現問題,調試應該不會太壞。我不確定哪些屬性(如果有)需要添加到XAML編輯器的屬性中才能正確處理它們。

+0

哇,彼得!這真太了不起了!我會在接下來的一兩天內試用,並告訴你。 –

+0

彼得,你提供的東西很棒,和湯姆一樣好。我會在此開一筆賞金,並在兩天內給你點數。 –