2010-03-05 18 views
7

Estoy tratando de usar XAML completamente fuera de WPF, en particular dentro de una aplicación XNA. Hasta ahora, he logrado (bastante fácil, me sorprende admitir) cargar algunos datos dentro de mi aplicación XNA desde un archivo XAML. Los problemas comienzan cuando decidí que quería animar una de las propiedades de mi clase ... No pasa nada :(XAML sin WPF - Animaciones

Aquí es la clase principal me carga desde el archivo 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); } } 
} 

Aquí es el archivo 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> 

y aquí está la carga y la animación de lanzamiento (intento):

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

Estoy muriendo de curiosidad :)

+0

¡Qué idea tan genial! ¿Has probado la depuración de animaciones en las aplicaciones normales de WPF para ver qué componentes activan las actualizaciones? –

+0

Sé que esta es una vieja pregunta, pero tengo curiosidad sobre qué tan lejos llegaste con esto? He estado jugando con la idea de alojar XAML dentro de XNA yo mismo. Incluso esta pregunta que muestra cómo agregar animaciones básicas es un gran comienzo. –

Respuesta

4

Solo como referencia, ahora documentaré cómo logré hacer que esto funcione con XNA. Gracias a itowlson por proporcionar el enlace que faltaba; de lo contrario, tuve que crear una aplicación vacía con una ventana invisible ...

Definimos la clase con su animación en XAML (nótese las directivas 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> 

La clase Test "código subyacente" es la siguiente:

[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); } } 
} 

En la función de inicialización de la clase XNA Game deserializamos nuestro archivo xaml y comenzamos la animación:

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

La función de actualización toma como entrada un GameTime, que ofrece el campo TotalGameTime que almacena el intervalo de tiempo de la cantidad de tiempo pasado desde el lanzamiento de aplicaciones: eso es exactamente lo que un guión gráfico tiene que marcar:

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); 
} 

En el método de sorteo simplemente podemos dibujar texto usando la propiedad Rotación, que ahora estará animada correctamente:

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

Fuera del bucle normal de una aplicación WPF, dudo que haya alguna manera de conducir la animación. Puede haber alguna clase que pueda empujar o empujar para conducirlos, pero es probable que esté sellada.

Probablemente terminará construyendo su propio motor de ejecución de animaciones ejecutándose en otro subproceso y asegurando que las actualizaciones sucedan en su subproceso de interfaz de usuario, lo que significa encontrar una forma de reutilizar el Dispatcher o volver a crear algo similar.

This MSDN article may provide some useful information in this endeavor

Es un proyecto interesante ... Sería curioso saber si tiene éxito!

0

¡Guau, esto es bastante increíble! Desafortunadamente, es posible que se deba a algún tipo de llamada de "actualización" que se realiza en alguna API interna. Y si no lo llamas, la animación no se animará ... al igual que si un juego XNA no tiene el método de actualización llamado.

Me gustaría obtener más información sobre cómo lo está haciendo y qué nivel de éxito está encontrando. Debe escribir una publicación/artículo en algún lugar del blog :-)

6

Aunque XAML es independiente de WPF, los elementos visuales no lo son. En particular, la animación y el diseño son parte de WPF, y depende de la instalación de cañerías de WPF estar presente - a través de un objeto de aplicación, un PresentationSource tales como HwndSource, el XBAP PresentationHost.exe, etc.

Por lo que puede lee en tu XAML y obtén un gráfico de objeto de un objeto Test con un objeto Child Storyboard, pero ese objeto Test no está conectado a los motores de animación o diseño hasta que se coloca en un contexto WPF. Todo lo que obtiene XAML es un gráfico de objetos en memoria: es WPF, no XAML, lo que hace que los objetos "vivan".

Así que, como dice Ben, probablemente termines necesitando "empujar o pinchar" la animación tú mismo. No estoy al tanto de ninguna documentación sobre cómo hacer esto, pero a partir de hurgar en el reflector, parece que la API clave es Storyboard.SeekAlignedToLastTick, de los cuales los documentos dicen:

Los valores se actualizan de inmediato a reflejan la cambios debido a SeekAlignedToLastTick, aunque la pantalla no refleja estos cambios hasta que la pantalla se actualice.

Observe esa segunda cláusula. Normalmente, WPF maneja la actualización de la pantalla a medida que cambian los valores del objeto visual. Si no está utilizando WPF, le corresponde a usted leer los valores modificados y volver a dibujar la pantalla en consecuencia: no tiene el administrador de diseño WPF para manejarlo por usted.

Finalmente, tenga en cuenta que no he probado si SeekAlignedToLastTick funcionará en un entorno sin la tubería WPF cargada. Es suena como debería, porque no importa si es WPF o código de usuario el que está manejando los relojes, pero no puedo hacer ninguna promesa ... ¡aunque admito que me tienes curiosidad!


ACTUALIZACIÓN: le he dado un ir rápido, y parece que no funciona. Aquí hay una demo de alojar una animación dentro de Windows Forms (en este caso usando un simple temporizador de Windows Forms, pero en XNA supongo que el marco proporcionará un temporizador de juego para ti, no lo intenté porque no sé XNA). Supongamos que tiene un Windows Form de vanilla con un temporizador (timer1) y una etiqueta (label1), y que el proyecto hace referencia a los ensamblajes de WPF.

En primer lugar, mi versión simplificada de su clase:

[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; } 
} 

Ahora, el código de WPF para cargar uno de estos bebés de XAML y comenzar la animación:

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; 
} 

Tenga en cuenta que tenía que establecer el objetivo del guión gráfico para que sea el objeto XAML que acababa de cargar. Esto no sucede automáticamente Intenté hacer esto con Storyboard.TargetName en el XAML, pero eso no pareció funcionar; es posible que tengas más suerte.

Las líneas finales son sólo para la configuración de la devolución de llamada de temporizador:

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

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

me he guardado la hora de inicio de la animación, y se utiliza para el cálculo de que hasta qué punto en la animación que somos. Los temporizadores de WinForms son un tanto toscos, pero esto es suficiente como prueba de concepto; sin dudas, XNA tendrá algo mejor. Luego llamo Storyboard.SeekAlignedToLastTick, que actualiza los valores animados. Nada se muestra automáticamente porque mi objeto XAML no está conectado para su visualización, pero puedo verificar su propiedad de prueba y verificar que de hecho esté animando. En realidad, usaría esto para actualizar la posición u orientación del elemento visual XNA que representa el objeto XAML.

+0

¡Bien hecho! Estoy sinceramente sorprendido de que fuera tan directo. Esperaba que hubiera muchas clases selladas en el camino. –

Cuestiones relacionadas