2010-03-05 73 views
7

我想在WPF外部完全使用XAML,特别是在XNA应用程序中。到目前为止,我已经管理(很容易,我惊讶地承认)从XAML文件加载我的XNA应用程序中的一些数据。这些问题开始的时候,我决定,我想我的动画类的属性之一......什么也没有发生:(XAML没有WPF - 动画

这里是主类我从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); 

我是临死好奇:)

+0

真是个好主意!您是否尝试过在普通WPF应用程序中调试动画以查看哪些组件触发更新? – 2010-03-05 14:51:44

+0

我知道这是一个老问题,但我很好奇你有多远?我一直在玩弄自己在XNA内部托管XAML的想法。即使这个问题显示如何添加基本的动画是一个很好的开始。 – 2012-05-05 05:48:28

回答

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游戏类,我们反序列化我们的xaml文件并启动动画:

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

该更新功能需要输入一个GameTime,它提供存储,因为该应用程序推出经过的时间量的时间跨度的TotalGameTime场:这正是一个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); 
} 
0

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

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

This MSDN article may provide some useful information in this endeavor

这是一个有趣的项目。我很好奇,如果你成功的来信!

0

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

我非常想知道更多关于你如何做这件事的信息以及你找到的成功程度。你应该在某处写一篇博客帖子/文章:-)

6

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

这样你就可以读入您的XAML并获取包含子Storyboard对象的Test对象的对象图,但该Test对象不会连接到动画或布局引擎,直到将其放置在WPF上下文中。 XAML带给你的所有内容都是一个愚蠢的内存对象图:它是WPF,而不是XAML,它使对象“活着”。按照Ben的说法,你可能最终需要自己“推动或制作”动画。我不知道如何做到这一点的任何文档,但在反射闲逛,看起来关键API是Storyboard.SeekAlignedToLastTick,它的文档说:

值立即更新到 反映由于 SeekAlignedToLastTick的变化,即使 屏幕没有反映这些变化 直到屏幕更新。

请注意,第二条款。通常,WPF会在可视对象值发生更改时处理屏幕更新。如果你不使用WPF,那么你需要阅读已更改的值并相应地重新绘制屏幕:你没有WPF布局管理器来为你处理它。

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


更新:我考虑了一个快速去,和它似乎工作。这是一个在Windows窗体中托管动画的演示(在这种情况下,使用普通的Windows窗体计时器,但在XNA中,我猜框架会为您提供一个游戏计时器 - 没有尝试,因为我不知道XNA)。假设你有一个带定时器(timer1)和标签(label1)的vanilla Windows窗体,并且该项目引用了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; 
} 

注意,我不得不将故事板的目标设置为我刚刚加载的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可视元素的位置或方向。

+0

做得很好!我真的很震惊,这是直截了当的。我预计会有很多密封课程。 – 2010-03-07 14:29:57