XXP

mai 2016

lun. mar. mer. jeu. ven. sam. dim.
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          

« Un banc de poissons WPF | Accueil | Un banc de poissons WPF »

19 juin 2007

Un banc de poissons WPF

Mon passe-temps du week-end m'a conduit à développer une animation WPF sympa : un banc de poisson chassé par un requin. Et le requin c'est vous. Pour ceux qui ont .Net 3.0 installé vous pouvez l'exécuter (application XBAP) .

Attention c'est hypnotisant, même mon chat s'est laissé prendre et tentait de trouver les bestioles derrière l'écran.

Et les sources sont .

Fishscreenshot2_2

Les particularités de cette animation sont de deux ordres. D'abord elle est gourmande en calcul. Pour donner un effet réaliste au banc de poisson il faut simuler la "vision" qu'a un poisson de ses semblables en calculant toutes les distances à ses congénères. La lourdeur de cette phase de calcul implique qu'il faut animer des objets les plus lights possibles. Donc plutôt que d'animer des poissons dérivés de Shape (Ellipse, Rectangle, etc.) via le DataBinding, il vaut mieux utiliser des classes directement dérivées de Animatable (EllipseGeometry, RectangleGeometry, etc.). En effet les classes dérivées de Shape héritent de tous les bénéfices de la classe FrameworkElement tel que styling, ressources, DataBinding of course, gestion du clavier, de la souris et du focus, etc. Pour un petit poisson à qui on ne demande que de s'afficher, c'est très lourd en calcul.

Ensuite c'est une animation sans fin, contrairement aux animations standards WPF qui ont une durée. La méthode la plus évidente (et vivement recommandée) qui consiste à créer une animation personnalisée en dérivant la classe AnimationTimeline n'est pas applicable ici.

La solution consiste à faire du "per frame rendering". Le framework WPF l'autorise en exposant l'event statique Rendering depuis la classe CompositionTarget :

CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);   

La callback passée en paramètre est appelé à chaque cycle d'affichage. Pour ce qui concerne l'animation, chaque appel est dédié à calculer les nouvelles positions et à demander le reconstruction de l'affichage via

InvalidateVisual();

Si on avait géré des objets capables de se dessiner "tous seuls" (des objets de la classe Shape), on aurait pu ne pas appeler InvalidateVisual() et définir directement dans cette callback la position des éléments par exemple sur un Canvas.

Mais nos objets de type Animatable doivent être dessinés explicitement par les primitives exposées par la Classe DrawingContext. Un objet de cette classe est passé en paramètre de la méthode virtuelle OnRender déclarée sur UIElement. Il nous suffit de surcharger cette méthode dans notre UserControl et d'invalider les visuels dans le handler de Rendering pour avoir accès à un DrawingContext.

Pour cette animation le UserControl délègue le dessin de chaque poisson à chaque poisson (quoi d'autre ?) :

public void RenderFish(DrawingContext drawingContext)

{

  drawingContext.PushTransform(new TranslateTransform(m_x, m_y));

  drawingContext.PushTransform(new RotateTransform(AngleDeg));

  drawingContext.DrawDrawing(m_drawing);

  drawingContext.Pop();

  drawingContext.Pop();

}

La donnée membre m_drawing est de type Drawing et a été chargée au démarrage à partir du XAML. Le template qui définit le dessin du poisson peut ainsi être changé à votre convenance en précisant la valeur de la DependencyProperty FishTemplate :

<local:FishAnimCtrl Grid.Column="1"  NbrFish="{Binding ElementName=NbrOfFishes, Path=Value}" 

                        MaxSpeed="{Binding ElementName=MaxSpeedCtrl, Path=Value}" 

                        MinSpeed="{Binding ElementName=MinSpeedCtrl, Path=Value}" 

                        NbrNeighbors="{Binding ElementName=NbrNeighbors, Path=Value}"

                        Nervousness="{Binding ElementName=Nervousness, Path=Value}" 

                       >

        <local:FishAnimCtrl.FishTemplate>

          <DrawingGroup>

            <GeometryDrawing Brush="Black">

              <GeometryDrawing.Pen>

                <Pen Brush="Gray" Thickness="1"/>

              </GeometryDrawing.Pen>

              <GeometryDrawing.Geometry>

                <EllipseGeometry RadiusX="10" RadiusY="1"/>

              </GeometryDrawing.Geometry>

            </GeometryDrawing>

            <GeometryDrawing Brush="Red">

              <GeometryDrawing.Pen>

                <Pen Brush="Pink" Thickness="1"/>

              </GeometryDrawing.Pen>

              <GeometryDrawing.Geometry>

                <EllipseGeometry RadiusX="2" RadiusY="2" Center="10,0"/>

              </GeometryDrawing.Geometry>

            </GeometryDrawing>

          </DrawingGroup>

        </local:FishAnimCtrl.FishTemplate>

      </local:FishAnimCtrl>

Mais si vous voulez en savoir plus, le code est à votre disposition.

TrackBack

URL TrackBack de cette note:
https://www.typepad.com/services/trackback/6a00d8341c871f53ef00e008c7969c8834

Listed below are links to weblogs that reference Un banc de poissons WPF:

» Denis Dolfus: Animation, physics model, behavior, custom cursor in WPF XBAP from WPF Team Bloggers
Denis' xbap shows more great animation, a physics model, configurable behavior. His Un banc de poissons [Lire la suite]

Commentaires

very cool,thanks for you post the stuff and the src

L'utilisation des commentaires est désactivée pour cette note.