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 ページを公開しました。
ご質問・ご意見・ご感想 |
ご質問、ご意見、ご感想等の連絡は、
こちらへ