メインコンテンツまでスキップ

チュートリアルジオメトリのアニメーション

📚 Source Page

JUCE アプリケーションで簡単なアニメーションを作成できます。静的なジオメトリ形状に生命を吹き込むにはAnimatedAppComponentクラスである。

レベル初心者

プラットフォーム:Windows, macOS, Linux, iOS, Android

クラス: AnimatedAppComponent,Path,Point

スタート

このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。

このステップでヘルプが必要な場合は、以下を参照してください。Tutorial: Projucer Part 1: Getting started with the Projucer.

デモ・プロジェクト

このデモ・プロジェクトが完成すると、画面上に複数の魚の連続的で滑らかなアニメーションが表示される。PathそしてPointオブジェクトがある。

デモプロジェクトのウィンドウ
デモプロジェクトのウィンドウ
注記

ここで紹介するコードは、大まかに以下のものと似ている。アニメーションの例JUCE Examplesより。

AnimatedAppComponentクラス

単純なアニメーションJUCEアプリケーションを作成する場合、継承すると便利なクラスがあります。AnimatedAppComponentクラスである。ちょうどAudioAppComponentまたはOpenGLAppComponentクラスは、それぞれオーディオ・アプリケーションとOpenGLアプリケーションに役立ちます。AnimatedAppComponentというアニメーション制作に有益な機能を提供している:

  • setFramesPerSecond():この関数を使うと、アニメーションをできるだけスムーズに実行するために、アプリケーションの開始時にFPSを設定することができます。また、この関数が呼び出されると、指定された頻度で再描画処理が開始されます。
  • update():この関数は、setFramesPerSecond() 関数で設定された割合で呼び出され、アニメーションのパラメータを段階的に進めていきます。
  • getFrameCounter():定義された FPS レートでアニメーションを開始して以来、update() 関数が呼び出された回数の合計を返します。これは、アニメーションを計算するための周期的な数学関数で有用です。
  • getMillisecondsSinceLastUpdate():フレームレートに関係なく正確なタイミングでアニメーションを作成するために、最後のupdate()関数呼び出しからの所要時間をミリ秒で返すもう1つの便利な関数です。

これらの関数と親のpaint()関数を併用することで、以下のようになる。Componentクラスを使えば、簡単なアニメーションを作ることができる。

円のアニメーション

を見ればわかるようにMainContentComponentクラスを継承しています。AnimatedAppComponent.

アニメーションを作成する最初のステップは、アニメーションのフレームレートを設定することです。これをMainContentComponentコンストラクタで次のように行います。[1]setFramesPerSecond() 関数を呼び出す:

    MainContentComponent()
{
setSize (800, 600);
setFramesPerSecond (60); // [1]
}

ここではFPSを60に設定し、アニメーションを1秒間に60回更新するために、内部的に60Hzの周波数でタイマーを呼び出します。これはほとんどのスクリーンのリフレッシュ・レートにほぼ等しく、スムーズなアニメーションになります。

まず、単純な円を円運動でアニメーションさせることから始めよう。paint()関数で、まず円を描く色を設定します。Graphicsクラス[2].次に、形状がたどる円形の経路の半径を定義します。[3]この通りである:

void paint (juce::Graphics& g) override
{
//...

g.setColour (getLookAndFeel().findColour (Slider::thumbColourId)); // [2]

int radius = 150; // [3]

juce::Point p (getWidth() / 2.0f + 1.0f * radius,
getHeight() / 2.0f + 1.0f * radius); // [4]

g.fillEllipse (p.x, p.y, 30.0f, 30.0f); // [5]
}
Slider::thumbColourId@ thumbColourIdThe colour to draw the thumb with.Definition juce_Slider.h:888

そして、次のようにする。Point与えられた時間枠における形状の中心の位置を表す[4].ここでは、円形の動きを作るために、まず画面の幅と高さを2で割って画面の中心を見つける。次に、半径の値を足して、図形のx座標とy座標をオフセットする。

最後に、fillEllipse() 関数を使って実際の円を描きます。Point座標と直径30を引数に取る。

今、アプリケーションを実行すると、円がどうなるかわかりますか?座標系が左上から始まっているため、円は半径の値だけ反対方向に押されるのだ:

スタティック・サークル
スタティック・サークル

の宣言を修正しよう。Point実際のモーションを作成する。

    void paint (juce::Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

g.setColour (getLookAndFeel().findColour (juce::Slider::thumbColourId));

int radius = 150;

juce::Point p ((float) getWidth() / 2.0f + 1.0f * (float) radius * std::sin ((float) getFrameCounter() * 0.04f),
(float) getHeight() / 2.0f + 1.0f * (float) radius * std::cos ((float) getFrameCounter() * 0.04f));

g.fillEllipse (p.x, p.y, 30.0f, 30.0f);
}

ここでは、getFrameCounter()関数を使用して、アニメーションの開始からのフレーム数のカウンターを取得し、その値を使用して、次の間の値を計算します。-1 ..1幅と高さにはそれぞれサイン関数とコサイン関数を使用する。フレームカウンターの0.04のスカラー倍は、周期関数が交互に円運動を作り出す速度を制御する。

アプリケーションを実行すると、円運動が現れるはずだ。

注記

この修正版のソースコードはAnimationTutorial_02.hファイルにある。

パスのアニメーション

円の代わりに、次は円形のパスに沿って線をアニメートしてみよう。

前のセクションと同じコードベースを使って、複数のPointに沿ったオブジェクトである。Pathが作成されます。Point.paint()関数を以下のように修正する:

    void paint (juce::Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

g.setColour (getLookAndFeel().findColour (juce::Slider::thumbColourId));

auto numberOfDots = 15; // [1]

juce::Path spinePath; // [2]

for (auto i = 0; i < numberOfDots; ++i) // [3]
{
int radius = 150;

juce::Point p ((float) getWidth() / 2.0f + 1.0f * (float) radius * std::sin ((float) getFrameCounter() * 0.04f + (float) i * 0.12f),
(float) getHeight() / 2.0f + 1.0f * (float) radius * std::cos ((float) getFrameCounter() * 0.04f + (float) i * 0.12f));

if (i == 0)
spinePath.startNewSubPath (p); // if this is the first point, start a new path..
else
spinePath.lineTo (p); // ...otherwise add the next point
}

// draw an outline around the path that we have created
g.strokePath (spinePath, juce::PathStrokeType (4.0f)); // [4]
}
  • [1]最初に、パスに沿って作成するドットの数を定義します。
  • [2]次にPathオブジェクトを使って点を結ぶ線を引く。
  • [3]今度は、すべてのドットに同じPointしかし今回は、ループの反復ごとにアニメーションをオフセットする。円形の動きの各ドットの間に0.12のオフセットが追加されていることに注目してください。反復が最初のものである場合、startNewSubPath()関数を呼び出して新しいサブパスを作成します。Pathそうでない場合は、現在のドットを以前に定義されたPath.
  • [4]最後に、作成したPathに沿っている。Pointオブジェクトがある。

アプリケーションを実行すると、円形に引かれた線を見ることができる。

円軌道
円軌道
エクササイズ

アプリケーションのFPSを変えてみて、アニメーションの滑らかさがどのように変化するかに注目してください。映画アニメーションの標準的な24FPSではどうなるでしょうか?

注記

この修正版のソースコードはAnimationTutorial_03.hファイルにある。

魚のアニメーション

の魚をアニメーション化して、もう少し面白いことをやってみよう。PathそしてPointオブジェクトを作成した。

円形パスに沿って描かれた実際の点を表示し、魚の胴体を作成するために、fillEllipse()関数を使用して円を描き、線に沿って各点の幅と高さを増加させるように指定する行をforループに追加してみましょう。[1]このように:

void paint (juce::Graphics& g) override
{
//...
g.setColour (getLookAndFeel().findColour (juce::Slider::thumbColourId));

auto fishLength = 15;

juce::Path spinePath;

for (auto i = 0; i < fishLength; ++i)
{
int radius = 150;

juce::Point p (getWidth() / 2.0f + 1.0f * radius * std::sin (getFrameCounter() * 0.04f + i * 0.12f),
getHeight() / 2.0f + 1.0f * radius * std::cos (getFrameCounter() * 0.04f + i * 0.12f));

// draw the circles along the fish
g.fillEllipse (p.x - i, p.y - i, 2.0f + 2.0f * i, 2.0f + 2.0f * i); // [1]

if (i == 0)
spinePath.startNewSubPath (p); // if this is the first point, start a new path..
else
spinePath.lineTo (p); // ...otherwise add the next point
}

// draw an outline around the path that we have created
g.strokePath (spinePath, juce::PathStrokeType (4.0f));
}

さて、アプリケーションを実行すると、魚のように見え始めるが、まだ魚のようには動かないものが見えるはずだ。

魚体
魚体

そこで、魚の動きを模倣するためにアニメーションを少し変えてみよう。

void paint (juce::Graphics& g) override
{
//...

for (auto i = 0; i < fishLength; ++i)
{
auto radius = 100 + 10 * std::sin (getFrameCounter() * 0.1f + i * 0.5f); // [2]

juce::Point p (getWidth() / 2.0f + 1.0f * radius * std::sin (getFrameCounter() * 0.04f + i * 0.12f),
getHeight() / 2.0f + 1.0f * radius * std::cos (getFrameCounter() * 0.04f + i * 0.12f));

//...
}
//...
}

ここでは、正弦関数を使用して円の半径に変調を加え、同じgetFrameCounter()関数とスカラーを使用してフレーム・カウンターに変調を加えている。[2].これでアプリケーションを実行すると、蛇のような動きが得られるはずだ。

その動きは説得力があるように見えるが、驚きがなく、最初の円軌道のまま、かなり速く繰り返されるため、少し単調な印象を受ける。

void paint (juce::Graphics& g) override
{
//...

for (auto i = 0; i < fishLength; ++i)
{
auto radius = 100 + 10 * std::sin (getFrameCounter() * 0.1f + i * 0.5f);

juce::Point p (getWidth() / 2.0f + 1.5f * radius * std::sin (getFrameCounter() * 0.02f + i * 0.12f),
getHeight() / 2.0f + 1.0f * radius * std::cos (getFrameCounter() * 0.04f + i * 0.12f)); // [3]

//...
}
//...
}

の正弦関数と余弦関数のレートをオフセットすると、次のようになる。Pointを作成し、半径の幅と高さに異なる比率を与える。[3]より説得力のある結果を得ることができる。

最後にもう一度アプリケーションを実行し、動きのランダム性が改善されていることに気づく。

エクササイズ

同様の方法で、魚の長さを変更したり、独自のアニメーション形状を作成することができます。

注記

この修正版のソースコードはAnimationTutorial_04.hファイルにある。

概要

このチュートリアルでは、JUCE アプリケーションでジオメトリ形状をアニメートする方法を学びました。特に

  • のメカニズムを探った。AnimatedAppComponentクラスである。
  • を使用して描かれた図形。Graphicsクラスである。
  • フレームごとに図形をアニメーションさせる。

参照