How to Throw a UIElement Across the Screen Using C# in Windows 8 Style Apps

Last year, I wrote a blog about creating your own simple collision detection code.  I implemented this for a children's math game I created.  You can refer to my blog here:

 http://software.intel.com/en-us/blogs/2012/07/13/give-metro-ui-elements-space-writing-your-own-collision-detection-handler-in-c

 Now, I'd like to talk about another feature of the game: image kinetics.  As you can see in the aforementioned link, the math game consists of game tiles.  What I've implemented is the following.  If the user holds down and "flings" an image, it goes flying across the screen!  I also added a touch of a physics feel by implementing the following:

·      The faster the finger flicks an image, the greater its initial velocity

·      The speed of any flung object dampens over time to simulate resistance             

Surely, we could take a pure physics approach.  We could start with gravity having a magnitude of 9.81 m/s2, come up with some air resistance coefficients, and so forth.  Or….we could do things a little simpler.  I take the latter approach as this will be a "good enough" approximation to a true physics model.

Handling touch in Windows 8 UI can be as simple as using a single XAML keyword with a specified event handler or more in depth such as utilizing manipulation events.  Here, I will discuss the latter.  If you need a primer on handling touch, refer to my guide here:

http://software.intel.com/sites/default/files/m/9/0/5/0/5/44966-Enabling_Touch_in_Windows_8_with_C.pdf

 OK, so let’s get into the details of how I implemented the manipulation event handlers.  Here’s my manipulation start routine:

        //invoked when user begins a gesture on game tile

        void manip_start(object sender, ManipulationStartedRoutedEventArgs e)

        {

            //clear fling velocity components

            vel_x = 0;

            vel_y = 0;

            //grab the image (game tile) where manipulation began!!!

            img = sender as Image;

            //retain image location in canvas at start of gesture

            initial_x = Canvas.GetLeft(img);

            initial_y = Canvas.GetTop(img);

             //find array position of the image

             for (x = 0; x < images.GetLength(0); x++)

            {

                for (y = 0; y < images.GetLength(1); y++)

                {

                    if (img.Equals(images[x,y]))

                    {

                        //we found the match...update the array positions

                        img_x = x;

                        img_y = y;

                        break;

                    }

                }

            }

             //notify ImageKinetics class so that this image won't be continually moved and

            //redrawn until user has finished the gesture event

            ImageKinetics.ManipulationPending(img_x, img_y);

        }

 Figure 1: Tagging the Tile When Manipulation Begins***

This code in my case casts the UIElement (game tile) to an Image because that is specific to my implementation.  I find the array index of this image in my data structure and also note its positional coordinates from when manipulation began.  I do the latter since my images are constantly moving in space and I want the completion of the fling motion to depart from the original image location.  On that note, the ImageKinetics.ManipulationPending holds the image in place until the user has released the finger.

My manipulation delta routine is then continuously called while the user continues to drag around the selected tile (note, the tile hasn’t been flung yet).  Here, I implement feedback where the user can drag the tile away from its original position but not too far.  Here is the code:

              //invoked when user continues a gesture on game tile (eg: dragging a game tile)

        void manip_delta(object sender, ManipulationDeltaRoutedEventArgs e)

        {

            //allow user to drag the image before letting finger go, but not too far from starting

            //point...make sure that when dragged, tile stays in the vicinity dictated by

            //PERIMETER_MAX

            if (!e.IsInertial) //only update position if user hasn't yet flung game tile

            {

                pos_x = initial_x;

                if (e.Cumulative.Translation.X >= 0)

                    pos_x += Math.Min(e.Cumulative.Translation.X, PERIMETER_MAX); // keep image within perimeter

 


 

                else //moved in negative axis direction

                    pos_x -= Math.Max(e.Cumulative.Translation.X, PERIMETER_MAX); // keep image within perimeter

 

               pos_y = initial_y;

                 if (e.Cumulative.Translation.Y >= 0)

                    pos_y += Math.Min(e.Cumulative.Translation.Y, PERIMETER_MAX); // keep image within perimeter

                 else //moved in negative axis direction

                    pos_y -= Math.Max(e.Cumulative.Translation.Y, PERIMETER_MAX); // keep image within perimeter

                Canvas.SetLeft(images[img_x, img_y], pos_x);

                Canvas.SetTop(images[img_x, img_y], pos_y);

                //update sliding velocity

                vel_x = e.Velocities.Linear.X;

                vel_y = e.Velocities.Linear.Y;

            }

        }

Figure 2: Dragging the Image***

In a nutshell, the code does the following:


- Prevents the user from dragging an image that has been flung.  A flung image has velocity greater than the inertial (default velocity), and so I lock further manipulation on the image until its speed dampens to near default.  I explain this in more detail below

  - Uses min/max function to update the image position without exceeding the specified perimeter (distance) from the originating   point of the manipulation

 - Updates velocity values by reading the dragging velocity

 

The third point is important.  This one ties into when the image is finally released.  If we think about it, flinging an image at the very least requires some degree of drag (even if very small).  It’d be nice if the velocity of the object motion provides as feedback to the user something relative to the last drag just before the release.  This helps to give the natural physics feel of the fling velocity.

The last manipulation event I implement is when the manipulation is completed (finger has left the screen):

        //invoked when user ends a gesture on game tile...use final velocity for projection

        void manip_complete(object sender, ManipulationInertiaStartingRoutedEventArgs e)

        {

                      //call helper routine in ImageKinetics file for actually making the

           //selected image take on the "fling" gesture direction

           ImageKinetics.fling(img_x, img_y, vel_x, vel_y);

        }

Figure 3: Manipulation Has Completed***

ImageKinetics.fling puts the image in inertia after the user has released the finger.  Before unravelling the routine, I’ll introduce some configurable parameters I use here.

 

        //movement speed of tiles

        static int SPEED = 4;

        //adjust how fast flung object flies.....BE VERY CAREFUL..too high a value will have adverse side effects!

        private const double VEL_SCALE_FACTOR = 5;

        //upper bound on the max velocity of flung tile

        private const double MAX_VEL = 100;

        //used for slowing down a flung object

        private const double DAMPENING_FACTOR = .95;

        //threshold used where when a flung image slows down

        //velocity over time, if it's "close enough" now to

        //the inertial velocity it was at before gesture event

        private const double PROXIMITY = 1.005;

Figure 4: Configurable Kinetic Parameters***

 

The SPEED parameter dictates how far each game image moves each frame when not flung.  Remember I said that in my game, images are always moving.  VEL_SCALE_FACTOR is a multiplicative factor that scales the velocity values read from the manipulation events.  MAX_VEL naturally puts a cap on how fast the initial velocity of a flung object can be.  DAMPENING_FACTOR dampens a flung object’s velocity each frame until the velocity has slowed to near default inertial speed within a factor of PROXIMITY.  Notice that the dampening has the effect of reducing speed in a somewhat logarithmic factor.  I did this to give an approximate physics feel analogous to negative acceleration.  Now, since we got that out of the way, let me introduce my fling handling code:

 

        //at the end of a drag gesture, the user flings a game tile

        //called from GameBoard.xaml.cs

       public static void fling(int img_x, int img_y, double vel_x, double vel_y)

        {

 

/***************************************************/

/************************SECTION A******************/

 

            //based on the fling direction, we will change the directional movement

 

            //of this image accordingly

 

            if (vel_x > 0 && vel_y > 0) //SE

                move_dir[img_x, img_y] = (int) MOVE_KEY.SE;

            else if (vel_x > 0 && vel_y < 0) //NE

                move_dir[img_x, img_y] = (int)MOVE_KEY.NE;

            else if (vel_x < 0 && vel_y > 0) //SW

                move_dir[img_x, img_y] = (int)MOVE_KEY.SW;

 

            else if (vel_x < 0 && vel_y < 0) //NW

                move_dir[img_x, img_y] = (int)MOVE_KEY.NW;

  

/***************************************************/

/************************SECTION B******************/

 

            //update the fling velocity magnitude, the root of the sum of the squares

            //of velocity components

            fling_vel = Math.Sqrt(Math.Abs(Math.Pow(vel_x, 2) + Math.Pow(vel_y, 2)));

            //upper bound for velocity

            if (fling_vel > MAX_VEL)

                fling_vel = MAX_VEL;

             //now scale the velocity per build option

            fling_vel = fling_vel * VEL_SCALE_FACTOR;

  

/***************************************************/

/************************SECTION C******************/


 

 

            //retain array position of image that was flung

            fling_x = img_x;

            fling_y = img_y;

            flung = true;

            fling_pending = false; //since the user finally completed manipulation event

        }

Figure 5: Fling Code***

Here is what the code does.  Code in section A first determines if the fling direction is north-east, south-west, and so on.  Remember that this is because in my game, images move diagonally.  Then, in section B, I compute the magnitude of the fling velocity as a function of the velocity x and y components (think Pythagorean Theorem).  I cap this speed if needed and then scale it up.  Finally, in section C, I set a flag that the image is flung such that until it slows down to near inertia speed, the user is not allowed to fling this image again. 

The nice thing about these parameters is that you can play with the values.  These values I mention gave me some great results.  This covers my kinetic code.  I hope you enjoyed reading.  Let me know if you have any questions, comments, or suggestions.  Thanks!

  

***This sample source code is released under the Microsoft Limited Public License (MS-LPL) and is released under the Intel OBL Sample Source Code License (MS-LPL Compatible)


<a href="https://plus.google.com/110375266827493659950?rel=author">David Medawar on Google+</a>



如需更全面地了解编译器优化,请参阅优化注意事项