AnimationClock を使ってアニメーション動画を作る(6)

トップページへ

 AnimationClock を使用したアニメーションの紹介

  1.DoubleAnimationUsingKeyFrames を使用したアニメーション(2D)
  2.Transform を使った円のアニメーション(2D)
  3.指定された経路上で円を運動させるアニメーション(2D)
  4.TileMode で描画された画像のアニメーション(2D)
  5.自公転する立方体のアニメーション(3D)
  6.立方体の表示にタイムラグを設けるアニメーション(3D) (このページ)
  7.ClockGroup を使ったアニメーション−基本編(2D)
  8.ClockGroup を使ったアニメーション−応用編(2D)
  9.残像を形成するアニメーション(2D)

なお、これらの動画は、「ソフト関連Trial−No.109」で紹介しています。

 立方体の表示にタイムラグを設けるアニメーション動画の作成方法(3D)

1.作成するアニメーションの内容
Viewport3D 内に配置した 3 個の立方体に、以下のアニメーションを設定します。
 (1) 各立方体の VisibilityProperty に ObjectAnimationUsingKeyFrames を設定して、
   時間差をつけて立方体の表示を行います。
 (2) 各立方体は、通常の RotateTransform3D を使った DoubleAnimation で、自転させます。
 (3) 左側の立方体が縦方向に 3 倍、中央の立方体が縦方向に 2 倍 伸長されるように、
   左側と中央の立方体の ScaleYProperty にDoubleAnimationを設定します。

アニメーション作成の画面は以下のようになります。


2.使用した開発環境
第1例 と同じく、以下の開発環境を使用しました。
開発環境:Visual Studio 2010 Professional Edition
.NET Framework:.NET Framework 4 Client Profile
古いバージョンを使いましたが、最新の開発環境でも問題なく使用できると思います。

3.XAMLコード
XAMLコードは、Camera の部分を除いて、前例 と同じです。
Camera の部分のコードは、以下のようになります。
    <!-- Camera -->
    <Viewport3D.Camera>
     <OrthographicCamera Position="0,200,1000" LookDirection="0,-2,-10" Width="20" />
    </Viewport3D.Camera>

4.C#コード
  // usingによって名前空間を参照するコードの部分は省略
  
  public partial class MainWindow : Window
  {
    // コンストラクター
    public MainWindow()
    {
      InitializeComponent();
      InitializeWindow();
    }

    private const double TIME = 4;     // Cube自転の周期
    private const int NUM_C = 3;      // Cubeの数
    private AnimationClock[] _clockSR = new AnimationClock[NUM_C];  // Cube自転
    private AnimationClock[] _clockVis = new AnimationClock[NUM_C];  // CubeのVisibility
    private AnimationClock _clockScY0;
    private AnimationClock _clockScY1;

    // [Avi]ボタンクリックイベント
    private void btnAvi_Click(object sender, RoutedEventArgs e)
    {
      var filepath = GetAviFilePath();
      if (0 < filepath.Length)
      {
        SaveAsAvi(filepath);
        MessageBox.Show("OK");
      }
    }

    // [開始]ボタンクリックイベント
    private void btnStart_Click(object sender, RoutedEventArgs e)
    {
      for (int i = 0; i < NUM_C; i++)
      {
        _clockSR[i].Controller.Begin();
        _clockVis[i].Controller.Begin();
      }
      _clockScY0.Controller.Begin();
      _clockScY1.Controller.Begin();
    }

    // [停止]ボタンクリックイベント
    private void btnStop_Click(object sender, RoutedEventArgs e)
    {
      for (int i = 0; i < NUM_C; i++)
      {
        _clockSR[i].Controller.Stop();
        _clockVis[i].Controller.Stop();
      }
      _clockScY0.Controller.Stop();
      _clockScY1.Controller.Stop();
    }


    // Cube各面のGeometryModel3Dを返すメソッド
    private GeometryModel3D GModel3D(Brush brush, Point3DCollection vp3col)
    {
      // 省略(前例 と同じ)
    }

    // CubeのVisibilityに関するObjectAnimationUsingKeyFramesを返すメソッド
    private ObjectAnimationUsingKeyFrames OaUkfVis(double time)
    {
      var frames = new ObjectAnimationUsingKeyFrames();

      var frame0 = new DiscreteObjectKeyFrame
      {
        KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)),
        Value = Visibility.Collapsed
      };
      frames.KeyFrames.Add(frame0);

      var frame1 = new DiscreteObjectKeyFrame
      {
        KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(time)),
        Value = Visibility.Visible
      };
      frames.KeyFrames.Add(frame1);

      return frames;
    }

    // Aviファイル保存パス取得
    private string GetAviFilePath()
    {
      // 省略
    }

    // Cubeを生成するメソッド
    private void CreateCube()
    {
      // Cube六面の形状指定、
      // 省略(前例 と同じ)

      // Cube六面の色指定
      // 省略(前例 と同じ)

      // 3個のCubeを生成する
      ModelUIElement3D[] cubes = new ModelUIElement3D[NUM_C];

      for (int i = 0; i < NUM_C; i++)
      {
        var groupM = new Model3DGroup();
        for (int i1 = 0; i1 < 6; i1++)
        {
          var gmodel = GModel3D(new SolidColorBrush(color[(i * 2 + i1) % 6]), p3col[i1]);
          groupM.Children.Add(gmodel);
        }

        cubes[i] = new ModelUIElement3D();
        cubes[i].Model = groupM;

        // CubeのTransform設定
        var groupT = new Transform3DGroup();
        cubes[i].Transform = groupT;

        // Cube自転のTransformとAnimation
        var axis3d = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
        groupT.Children.Add(new RotateTransform3D(axis3d));
        var da1 = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(TIME)));
        da1.RepeatBehavior = RepeatBehavior.Forever;
        _clockSR[i] = da1.CreateClock();
        axis3d.ApplyAnimationClock(AxisAngleRotation3D.AngleProperty, _clockSR[i]);
        _clockSR[i].Controller.Stop();

        // Cubeの配置
        var tr3d = new TranslateTransform3D((i - 1) * 6, 0, 0);
        groupT.Children.Add(tr3d);

        // CubeのScaleTransform3D設定(No.2 Cubeには設定しない)
        if (i < 2)
        {
          var sc3d = new ScaleTransform3D();
          groupT.Children.Add(sc3d);
          
          if (i == 0) // No.0 Cube
          {
            var dasc0 = new DoubleAnimation(3,
                   new Duration(TimeSpan.FromSeconds(TIME / 2)));
            dasc0.AutoReverse = true;
            dasc0.RepeatBehavior = RepeatBehavior.Forever;
            _clockScY0 = dasc0.CreateClock();
            sc3d.ApplyAnimationClock(ScaleTransform3D.ScaleYProperty, _clockScY0);
            _clockScY0.Controller.Stop();
          }
          else     // No.1 Cube
          {
            var dasc1 = new DoubleAnimation(2,
                   new Duration(TimeSpan.FromSeconds(TIME / 2)));
            dasc1.AutoReverse = true;
            dasc1.RepeatBehavior = RepeatBehavior.Forever;
            _clockScY1 = dasc1.CreateClock();
            sc3d.ApplyAnimationClock(ScaleTransform3D.ScaleYProperty, _clockScY1);
            _clockScY1.Controller.Stop();
          }
        }

        // CubeのVisibilityのAnimation設定
        cubes[i].Visibility = Visibility.Collapsed;
        if (i == 0)
          _clockVis[i] = OaUkfVis(TIME).CreateClock();
        else if (i == 1)
          _clockVis[i] = OaUkfVis(0).CreateClock();
        else
          _clockVis[i] = OaUkfVis(TIME * 2).CreateClock();
        cubes[i].ApplyAnimationClock(UIElement.VisibilityProperty, _clockVis[i]);
        _clockVis[i].Controller.Stop();

        // ViewportへのCube配置
        myViewport.Children.Add(cubes[i]);
      }
    }

    private void InitializeWindow()
    {
      CreateCube();
    }

    // Aviファイル作成
    private void SaveAsAvi(string filepath)
    {
      var fps = 48;                // フレームレート
      var totalFrames = (int)(fps * TIME * 3);   // 全フレーム数
      var secs = Enumerable.Range(0, totalFrames).Select(t => (((double)t) / fps));
      var aviManager = new AviFile.AviManager(filepath, false);
      AviFile.VideoStream aviStream = null;
      var tempFile = "frame.png";

      foreach (var sec in secs)
      {
        for (int i = 0; i < NUM_C; i++)
        {
          _clockSR[i].Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                                 TimeSeekOrigin.BeginTime);
          _clockVis[i].Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                                 TimeSeekOrigin.BeginTime);
        }
        _clockScY0.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        _clockScY1.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        cvs1.UpdateLayout();
        cvs1.SaveImage(tempFile);      // cvs1の画像をファイルに保存する
        var bmp = new System.Drawing.Bitmap(tempFile);
        if (aviStream == null)
          aviStream = aviManager.AddVideoStream(true, fps, bmp);
        else
          aviStream.AddFrame(bmp);
        bmp.Dispose();
      }
      aviManager.Close();
      
      for (int i = 0; i < NUM_C; i++)
      {
        _clockSR[i].Controller.Stop();
        _clockVis[i].Controller.Stop();
      }
      _clockScY0.Controller.Stop();
      _clockScY1.Controller.Stop();
      
      _clock.Controller.Stop();
      System.IO.File.Delete(tempFile);
    }
  }

立方体とそのアニメーションの生成は、[CreateCube]メソッドで行います。
アニメーションの開始は、[開始]ボタンクリックイベント内の、各 AnimationClock の Begin メソッドで行います。
アニメーションの停止は、[停止]ボタンクリックイベント内の、各 AnimationClock の Stop メソッドで行います。
Aviファイル作成のためにフレーム画像を取り出す操作は、[SaveAsAvi]メソッド内の、各 AnimationClock の SeekAlignedToLastTick メソッドで行います。
[SaveAsAvi]メソッドの中間付近にある cvs1 の SaveImage 拡張メソッドについては、第1例 の記載を参照してください。

 ダウンロード

ここで紹介したアニメーションを作成するプロジェクトファイルは、以下のリンクからダウンロードすることができます。
「Example06_3DAnimation02」プロジェクトファイルのダウンロード
なお、このプロジェクトでは、AVIファイル化用クラスライブラリとして、Code Project の「 A Simple C# Wrapper for the AviFile Library 」からダウンロードして入手した[AviFile.dll]を使用しています。

ダウンロードしたファイルの利用は、全て利用される方の責任で行っていただきます。
作者は十分な注意を払って本ファイルを作成していますが、もし万一、本ファイルの内容に誤りがあっても、 またその利用によって使用パソコンに問題が発生しても、作者は一切責任を負いません。

 公開・更新履歴

 2017/09/01 ページを公開しました。

 ご質問・ご意見・ご感想

ご質問、ご意見、ご感想等の連絡は、 こちらへ

トップページへ