User Tools

Site Tools


manual:dealing_with_dpi

Dealing With Different Pixel Densities

Not all screens are created equal, and that's particularly true for mobile devices with their crazy range of different screen resolutions and pixel densities. Apple started all the rage with their Retina Display screen technology, creating screens that pack much more pixels per inches (aka Dots-Per-Inches or DPI) than their previous iterations.

This is great for marketing and end-users, but it also means additional work and coffee to properly support these various specifications. Dealing with different pixel densities is especially important for handling gestures as you do not want your game's input to behave differently when it's running on a high DPI device than on a lower DPI one. For instance, you probably want a 2cm drag to the right to always result in the camera moving by 10 world units to the left, regardless of whether the user is running on an iPad Mini 1 (non-retina screen) or an iPad Mini 2 (retina screen).

A strategy to deal with this in a sane way is to write input handling code that operates on distance/speed values expressed in physical units (cm or inches) instead of raw pixel values.

Gesture Recognizers Configuration

When configuring your GestureRecognizers, you can specify the various distance thresholds and speed properties in either pixel, centimeters or inches by using the drop-down list next to screen-distance based properties (they show up in teal):

The distance unit selected is global for that gesture recognizer to prevent mixing them up. This unit is only used for the recognition settings, and will not affect the output values provided in the gesture event data. The gesture event data is always returned in raw pixel values, regardless of the unit used to configure the gesture recognizer.

To make your input handling logic code independent of pixel-density, you must manually convert the pixel values returned by the gesture data in either centimeters or inches.

Distance Unit Conversion API

To assist you with this, FingerGestures (3.1+) exposes several functions you can use to easily convert screen distance values to/from pixels/centimeters/inches, based on the current device's screen DPI.

FingerGestures.Convert

The main conversion function is the Convert() static method on the FingerGestures singleton:

// convert the "distance" value expressed in "fromUnit" to "toUnit"
float FingerGestures.Convert(float distance, DistanceUnit fromUnit, DistanceUnit toUnit)

DistanceUnit is an enum with the following values:

  • DistanceUnit.Pixels
  • DistanceUnit.Inches
  • DistanceUnit.Centimeters

Some sample code on how to use the Convert() method:

// distance in raw pixels
float distanceInPixels = 340;
 
// convert from pixels to centimeters
float distanceInCentimeters = FingerGestures.Convert( distanceInPixels, DistanceUnit.Pixels, DistanceUnit.Centimeters );
 
// convert from pixels to inches
float distanceInInches = FingerGestures.Convert( distanceInPixels, DistanceUnit.Pixels, DistanceUnit.Inches );
 
// convert inches value back to pixels
float inchesToPixels = FingerGestures.Convert( distanceInInches, DistanceUnit.Inches, DistanceUnit.Pixels );

Extension Methods

The syntax can be a bit verbose, so FingerGestures also provides a bunch of extension methods for the float and Vector2 type to streamline unit conversion:

// distance in raw screen pixels
float distanceInPixels = 340;
 
// convert from pixels to centimeters using extension method
float distanceInCentimeters = distanceInPixels.Convert( DistanceUnit.Pixels, DistanceUnit.Centimeters );
 
// move vector in raw screen pixels
Vector2 moveInPixels = new Vector2( 120, -30 );
 
// convert vector2 from pixels to centimeters using extension method
Vector2 moveInCentimeters = moveInPixels.Convert( DistanceUnit.Pixels, DistanceUnit.Centimeters );

Since the data provided by gesture events is always expressed in raw pixel values, you will spend most of your time converting from pixel values to either centimeter or inches. That's why FingerGestures also provides two other extension methods to make this even easier:

// distance in raw screen pixels
float distanceInPixels = 340;
 
// convert from pixels to centimeters using extension method
float distanceInCentimeters = distanceInPixels.Centimeters();
float distanceInInches = distanceInPixels.Inches();
 
// alternatively, you may want to use the In() method:
float distanceInCentimetersAlt = distanceInPixels.In( DistanceUnit.Centimeters );  // same as Centimeters()
float distanceInInchesAlt = distanceInPixels.In( DistanceUnit.Inches );  // same as Inches()
 
// move vector in raw screen pixels
Vector2 moveInPixels = new Vector2( 120, -30 );
 
// convert vector2 from pixels to centimeters using extension method
Vector2 moveInCentimeters = moveInPixels.Centimeters();
Vector2 moveInInches = moveInPixels.In( DistanceUnit.Inches );

Example: DPI-independent Pinch

If you take a look at PinchTwistSample.cs, you can see a practical use of this system to make the object-scaling pinch gesture work in a consistent way regardless of the type of device you're running it on. Pinching by one centimeter will scale the object the same way on all devices.

void OnPinch( PinchGesture gesture )
{
    if( gesture.Phase == ContinuousGesturePhase.Started )
    {
        Pinching = true;
    }
    else if( gesture.Phase == ContinuousGesturePhase.Updated )
    {
        if( Pinching )
        {
            // change the scale of the target based on the pinch delta value
            target.transform.localScale += gesture.Delta.Centimeters() * pinchScaleFactor * Vector3.one;
        }
    }
    else
    {
        ...
    }
}

Here we convert the pinch gesture's Delta value (float) from pixels to centimeters using the float.Centimeters() extension method, before we let it modify the object's scale.

manual/dealing_with_dpi.txt · Last modified: 2013/12/11 06:34 by wravaine