Canvas Scaler and moving 2D items with touch

Hello,
I am developing a game and built my own infinite scroller that would lock onto a touch and follow the user’s finger. It worked great until we added a canvas scaler to help scale some of our difficult UI elements for all devices. Here are the settings for the Canvas Scaler:

UI Scale Mode: Scale With Screen Size
Reference Resolution: x: 750 y:1334
Screen Match Mode: Match width Or height
Match: 0.5
Reference pixels per unit: 100

What i’m struggling with is trying to get an item that is within the canvas to follow your finger with every screen size. I have a panel inside the canvas that detects your touch using ‘OnDrag’ and i am setting the item’s position inside the panel to eventdata.position. On the IPhone tall it seems to move slower then my finger, yet it works great on the IPhone5. Here is a little snippet of my code:

public void OnDrag(PointerEventData eventData)
{
_item.transform.position = eventData.position;
}

This bug can be seen in the editor and on the phone. Please help!

I’ve seen this a bit. There does appear to be a certain amount of lag between user / pointer input and Canvas Drawing.
Might be worth logging with an example project

Thanks for the response. Without the Canvas Scaler though, it works great, there does not seem to be any lagging and the app seems to be fully responsive. In my editor I can change the preset to the different phone sizes and depending on the size it scrolls faster or slower then my finger or perfectly if the phone is the same size as the Canvas Scaler’s Reference Resolution: I think it has to do with the scaling of the UI that the canvas scaler does on ‘Scale With Screen Size’ mode. Any other ideas?

Interesting, I’ve not seen any cases where the Canvas Scaler would cause performance issues.
Any idea @phil-Unity @Adam-Buckner_1 ?

Here is an example project if this helps. If its not already, change to IOS in your build settings and set the screen size to iPhone Tall. I am currently in Unity 4.6.1 to see it just click on the button to begin dragging and drag it up and down you will see your mouse pass it up because its moving slower. Then try iphone 5 and you can see it move with your mouse. Maybe i need to compensate for something?

1952865–126289–CanvasScalerIssue.unitypackage (6.69 KB)

_scaler = GetComponentInParent<CanvasScaler>();
...
if (_scaler != null)
    Drag(e.delta.x * _scaler.referenceResolution.x / Screen.width);
else
    Drag(e.delta.x);
4 Likes

Hi, Thank you soo much rakkarage!! It works great!

1 Like

The above is not entirely correct - it assumes that the reference resolution is matched to the actual screen by linear scaling in both directions which, depending on the MatchMode and Match width/height slider, may not be true.

If you wish to avoid the headache of figuring that out, Unity has already done if for you in the Canvas transforms’ “localScale” property (What I don’t understand is why Unity doesn’t do this before sending the event in the first place but it’s just one of those things, I guess…)

float dx = event.delta.x/_scaler.transform.localScale.x;
float dy = event.delta.y/_scaler.transform.localScale.y;

2 Likes

I found that in some cases, using localScale wasn’t working, and frustratingly the problem only turned up on the target platform.

CanvasScaler _canvasScaler;
...
void Awake()
{
_canvasScaler = GetComponent<CanvasScaler>() ?? GetComponentInParent<CanvasScaler>();//anyone know if ?? is redundant here?
}
...
public Vector3 UnscaleEventDelta(Vector3 vec)
    {
        Vector2 referenceResolution = _canvasScaler.referenceResolution;
        Vector2 currentResolution = new Vector2(Screen.width, Screen.height);

        float widthRatio = currentResolution.x / referenceResolution.x;
        float heightRatio = currentResolution.y / referenceResolution.y;
        float ratio = Mathf.Lerp(widthRatio, heightRatio, _CanvasScaler.matchWidthOrHeight);

        return vec /ratio;
    }

This code takes into account the “Match” slider as well.

7 Likes

I know this is an old thread, but since I got here by Google, others might as well. This answer worked perfectly for me: c# - Unity3D UI, calculation for position dragging an item? - Stack Overflow

Bumping for Vatio’s response above. That also worked for me and was the easiest solution I found by far

The UnscaleEventDelta(..) method mainly works but it doesn’t work properly when using a Canvas Scaler set to Match Width Or Height = 0.5 (slider in the middle) while having a small Screen.width and a big Screen.height. Any idea why the calculated mouse position is slightly off? I am trying to create a selection area using a blank image so there’s no element to click to use PointerEventData.

Big thanks for this! :slight_smile: