Smooth Control with Touch

By on 4/5/2010

The new touch capabilities that are present in XNA 4.0/windows phone 7 present new opportunities for letting the user take control of the games that we can write. Unlike the input from the xbox 360 gamepad, or the mouse though, the user is not restricted to incremental analog adjustments (ie. moving the mouse by 5 pixels per frame, or having the left stick pushed up to a value of .5). So it is completely possible for the user to press his finger in the bottom left of the screen, drag it 20 pixels, lift for 5 seconds, and then press it down somewhere in the upper right of the screen.

In a naive implementation of handling touch input, you could just take the touch point as fact, and the entity would snap directly over to the user's finger in the scenario described above. This may be visually jarring, which of course may be ok for your game. However, if you want your entity to have more of a realistic presence in the game world, to feel more "real", the you probably don't want it teleporting around the screen; At the same time, you want the user's input not to feel laggy or non-responsive.

So how do we handle this fundamental difference in user input techniques?

The problem of incomplete or inconsistent input reminded me of the network prediction sample, which is available on the XNA Creator's Club site (http://creators.xna.com/en-US/sample/networkprediction). In that sample, they present two solutions to deal with the issues brought on by network latency. Particularly, I was interested in their smoothing solution because it solves my problem.
Smoothing is a simple concept. When a network packet is received, rather than teleporting immediately to the new position, we can interpolate gradually from the previous position toward this new location, giving the illusion of continuous motion ...
I took that smoothing concept and applied it here so that when the user re-touches in another location, the onscreen entity will smoothly accelerate towards his current touch point. And because it's using interpolation, it will always catch up to the current location after half a second regardless of where the user is moving his finger. The entity class from the project is listed below. The important bits are the "isCatchingUp" and "currentSmoothing" variables. When the input handling code realizes that it needs to catch up, it starts to decay the "currentSmoothing" variable, which is then used in the interpolation to smoothly transition to the current location (via Vector2.Lerp).
class Entity
{
    private Vector2 lastKnownPosition;
    private Vector2 touchPosition;
    private float currentSmoothing;

public Vector2 Position; public Texture2D Texture;

public void Update() { var touches = TouchPanel.GetState(); float decay = 1f / 15f;

bool isTouching = touches.Count > 0; bool isCatchingUp = currentSmoothing > 0;

if (isTouching) { if (isCatchingUp) { currentSmoothing -= decay; }

foreach (var touch in touches) { touchPosition = touch.Position; } } else if (!isCatchingUp) { currentSmoothing = 1; }

if (!isCatchingUp) { lastKnownPosition = touchPosition; }

Vector2 positionToDraw = lastKnownPosition; if (isCatchingUp) { Vector2.Lerp( ref touchPosition, ref lastKnownPosition, currentSmoothing, out positionToDraw); }

this.Position = new Vector2( positionToDraw.X - Texture.Width / 2, positionToDraw.Y - Texture.Height / 2); } }
Little touches like this add an extra level of polish that players will appreciate ... and yes, I intended the pun ;-)

See more in the archives