没有WPF的XAML - 动画

时间:2010-03-05 14:37:41

标签: wpf xaml xna animation

我试图在WPF之外完全使用XAML,特别是在XNA应用程序中。到目前为止,我已经管理(很容易,我很惊讶地承认)从XAML文件加载我的XNA应用程序中的一些数据。当我决定要为我班级的一个属性制作动画时,问题就开始了......没有任何反应:(

这是我从XAML文件加载的主要类:

[ContentPropertyAttribute("Animation")]
public class Test : FrameworkContentElement
{
  public string Text { get; set; }
  public Vector2 Position { get; set; }
  public Color Color { get; set; }
  public Storyboard Animation { get; set; }

  public static DependencyProperty RotationProperty = 
    DependencyProperty.Register("Rotation", typeof(double), typeof(Test), new PropertyMetadata(0.0));
  public double Rotation { get { return (double)GetValue(RotationProperty); } set { SetValue(RotationProperty, value); } }
}

这是XAML文件:

<l:Test xmlns:l="clr-namespace:XAMLAndXNA;assembly=XAMLAndXNA"
      xmlns:a1="clr-namespace:System.Windows.Media.Animation;assembly=PresentationFramework"
      xmlns:a2="clr-namespace:System.Windows.Media.Animation;assembly=PresentationCore"
  Text="Testo" Position="55,60" Color="0,255,255,255">
  <a1:Storyboard>
    <a2:DoubleAnimation a1:Storyboard.TargetProperty="Rotation"
                              From="0"
                              To="360"
                              Duration="00:00:10.0"/>
  </a1:Storyboard>
</l:Test>

这是加载和动画启动(尝试):

Test test = XamlReader.Load(new XmlTextReader("SpriteBatchStuff.xaml")) as Test;
test.Animation.Begin(test);

死于好奇心:)

4 个答案:

答案 0 :(得分:6)

虽然XAML独立于WPF,但视觉元素却不是。特别是,动画和布局 WPF的一部分,并依赖于WPF管道 - 通过Application对象,一个PresentationSource,如HwndSource,XBAP PresentationHost.exe等。

因此,您可以读取XAML并获取具有子Storyboard对象的Test对象的对象图,但该Test对象在放置在WPF上下文中之前不会连接到动画或布局引擎。 XAML给你的所有东西都是一个愚蠢的内存对象图:它是WPF,而不是XAML,使对象“活着”。

正如Ben所说,你可能最终需要自己“推动或刺激”动画。我不知道有关如何执行此操作的任何文档,但是通过在Reflector中进行讨论,看起来关键API是Storyboard.SeekAlignedToLastTick,其中文档说:

  

值会立即更新为   反映由于的变化   SeekAlignedToLastTick,即使是   屏幕不反映这些变化   直到屏幕更新。

注意第二个条款。通常,WPF在视觉对象值改变时处理屏幕的更新。如果您没有使用WPF,那么您可以自行阅读更改后的值并重新绘制屏幕:您没有WPF布局管理器来为您处理。

最后,请注意我没有测试SeekAlignedToLastTick是否可以在没有加载WPF管道的环境中工作。它应该是声音,因为它不关心它是WPF还是驱动时钟的用户代码,但我不能做出任何承诺......虽然我承认你已经拥有了我好奇!


更新:我快速给了它,它似乎确实有效。这是一个在Windows窗体中托管动画的演示(在这种情况下使用普通的'Windows窗体计时器,但在XNA中我想框架将为您提供一个游戏计时器 - 没试过,因为我不知道XNA)。假设您有一个带有计时器(timer1)和标签(label1)的vanilla窗体,并且该项目引用了WPF程序集。

首先,我的班级简化版:

[ContentProperty("Animation")]
public class Fie : DependencyObject
{
  public double Test
  {
    get { return (double)GetValue(TestProperty); }
    set { SetValue(TestProperty, value); }
  }

  public static readonly DependencyProperty TestProperty =
    DependencyProperty.Register("Test", typeof(double), typeof(Fie),
    new FrameworkPropertyMetadata(0.0));

  public Storyboard Animation { get; set; }
}

现在,WPF代码从XAML加载其中一个婴儿并开始动画:

private Fie _f;
private DateTime _startTime;

public Form1()
{
  InitializeComponent();

  string xaml =
@"<local:Fie xmlns:local=""clr-namespace:AnimationsOutsideWpf;assembly=AnimationsOutsideWpf""
             xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
             xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
             >
  <Storyboard>
    <DoubleAnimation Storyboard.TargetProperty=""Test""
                     From=""0""
                     To=""360""
                     Duration=""00:00:10.0""/>
  </Storyboard>
</local:Fie>";

  _f = (Fie)XamlReader.Load(XmlReader.Create(new StringReader(xaml)));
  Storyboard.SetTarget(_f.Animation, _f);
  _f.Animation.Begin();

  _startTime = DateTime.Now;
  timer1.Enabled = true;
}

请注意,我必须将storyboard的目标设置为我刚刚加载的XAML对象。这不会自动发生。我尝试在XAML中使用Storyboard.TargetName执行此操作,但这似乎不起作用 - 您可能会有更多运气。

最后一行只是为定时器回调设置:

private void timer1_Tick(object sender, EventArgs e)
{
  TimeSpan sinceStart = DateTime.Now - _startTime;
  _f.Animation.SeekAlignedToLastTick(sinceStart);

  label1.Text = _f.Test.ToString();
}

我已经存储了动画的开始时间,并使用它来计算动画的距离。 WinForms定时器有点粗糙,但这足以证明概念;毫无疑问,XNA会有更好的东西。然后我调用Storyboard.SeekAlignedToLastTick,它会更新动画值。没有任何东西会自动显示,因为我的XAML对象没有连接显示,但我可以检查它的Test属性并验证它确实是动画。实际上,我会用它来更新XAML对象所代表的任何XNA视觉元素的位置或方向。

答案 1 :(得分:4)

仅供参考,我现在将记录如何使用XNA进行此操作。感谢itowlson提供缺少的链接:否则我不得不创建一个带有隐形窗口的空应用程序......

我们在XAML中定义具有动画的类(注意xmlns指令):

<l:Test 
      xmlns:l="clr-namespace:XAMLAndXNA;assembly=XAMLAndXNA"
      xmlns:a1="clr-namespace:System.Windows.Media.Animation;assembly=PresentationFramework"
      xmlns:a2="clr-namespace:System.Windows.Media.Animation;assembly=PresentationCore"
  Text="Testo" Position="55,60" Color="0,255,255,255">
  <a1:Storyboard>
    <a2:DoubleAnimation a1:Storyboard.TargetProperty="Rotation"
                              From="0"
                              To="6.28"
                              Duration="00:00:2.0"
                              RepeatBehavior="Forever"/>
  </a1:Storyboard>
</l:Test>

“代码隐藏”类测试如下:

[ContentPropertyAttribute("Animation")]
public class Test : DependencyObject
{
  public string Text { get; set; }
  public Vector2 Position { get; set; }
  public Color Color { get; set; }
  public Storyboard Animation { get; set; }

  public static DependencyProperty RotationProperty =
    DependencyProperty.Register("Rotation", typeof(double), typeof(Test), new PropertyMetadata(0.0));
  public double Rotation { get { return (double)GetValue(RotationProperty); } set { SetValue(RotationProperty, value); } }
}

在XNA Game类的Initialize函数中,我们反序列化我们的xaml文件并启动动画:

test = XamlReader.Load(new XmlTextReader("SpriteBatchStuff.xaml")) as Test;
Storyboard.SetTarget(test.Animation, test);
test.Animation.Begin();

Update函数将GameTime作为输入,它提供TotalGameTime字段,用于存储自应用程序启动以来经过的时间量的TimeSpan:这正是Storyboard需要勾选的内容:

protected override void Update(GameTime gameTime)
{
  // Allows the game to exit
  if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
    this.Exit();

  test.Animation.SeekAlignedToLastTick(gameTime.TotalGameTime);

  base.Update(gameTime);
}

在draw方法中,我们可以使用Rotation属性绘制一些文本,现在可以正确设置动画:

protected override void Draw(GameTime gameTime)
{
  GraphicsDevice.Clear(Color.CornflowerBlue);

  spriteBatch.Begin();
  spriteBatch.DrawString(Content.Load<SpriteFont>("font"), test.Text, test.Position, test.Color, (float)test.Rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f);
  spriteBatch.End();

  base.Draw(gameTime);
}

答案 2 :(得分:0)

在WPF应用程序的正常循环之外,我怀疑是否有任何方法可以驱动动画。可能会有一些课程可以推动或刺激它们,但它很可能是密封的。

您可能最终会在另一个线程上构建自己的动画执行引擎,并确保在您的UI线程上进行更新,这意味着要么找到重用Dispatcher的方法,要么重新创建类似的东西。

This MSDN article may provide some useful information in this endeavor

这是一个有趣的项目......我很想知道你是否成功了!

答案 3 :(得分:0)

哇,这太棒了!不幸的是,它可能会归结为在某些内部API中进行的一些“更新”类型的调用。如果你不调用它,动画将不会动画......就像XNA游戏没有调用Update方法一样。

我非常希望了解您如何做到这一点以及您找到的成功程度。你应该写一篇博客文章/文章: - )