Scroll Rect inertia on touch screen improve

A few years ago, I wrote a small fix to the standard ScrollRect, and now I found the time to contribute it. It turns out that during this time the open source repository was closed, so I will send my improvement here.

One of the problems with all mobile Unity games is the small of scroll inertia. Open any Unity game on you smartphone and you will realize that the scrolling process causes you negative emotions. This is due to the fact that on phones, people scroll most of the content not due to the drag gesture, but due to the inertia after this gesture.

The standard Unity’s ScrollRect contains a hack to damping inertia starting velocity, and this is justified for the mouse, but disgusting for the touch. I suggest detecting if a scroll is a touch then disable the hack. This small fix makes the Unity scroll look as same as native scroll on iOS and Android and leave it the same on computers

You should add to ugui/Runtime/UI/Core/ScrollRect.cs:

public class ScrollRect : ... {
    ...
    private bool m_DraggingByTouch;
    ...

    public virtual void OnBeginDrag(PointerEventData eventData) {
        ...
        m_DraggingByTouch = eventData.pointerId != -1;
    }

    protected virtual void LateUpdate() {
        ...
        // replace this line: https://github.com/Unity-Technologies/uGUI/blob/5ab4c0fee7cd5b3267672d877ec4051da525913c/UnityEngine.UI/UI/Core/ScrollRect.cs#L849
        m_Velocity = m_DraggingByTouch ? newVelocity : Vector3.Lerp(m_Velocity, newVelocity, deltaTime * 10);
        ...
    }
}

ps. I use links to an deprecated repository, but this is all relevant for the current version
I am attaching a fully functional code for version ugui@1.0.0

8365002–1101774–ScrollRect.cs (54.8 KB)

2 Likes

Bump!!!
Used in many projects. Makes apps feel very native.

1 Like

Wow feels so much better on iOS. I really wish Unity could consider implementing this… or at least give an option for it.

1 Like

The built-in ScrollRect is barely production-ready in terms of UX, the performance is also (understandably) limited once you surpass a few hundred items.

Yes, this is really cool, unfortunately I don’t know where to write so that the Unity team will pay attention.
I still use this hack in all my Unity mobile projects.
And I am triggered by terrible scrolling in many big games, but this is how you can detect their game engine)

Its also true, but it many case you don’t need 100+ items in scroll. Many reusable scrolls anyway use built-in ScrollRect under the hood

You can use something like that for base ScrollRect.
I don’t have checks to ensure that all scroll fields are set, if u need them, just modify this code.

public class InertialScrollRect : ScrollRect
    {
        private bool _draggingByTouch;
        private bool _isDragging;
        private Vector2 _prevPosition = Vector2.zero;
           
        protected override void OnDestroy()
        {
            base.OnDestroy();
               
            StopAllCoroutines();
        }
           
        public override void OnBeginDrag(PointerEventData eventData)
        {
            base.OnBeginDrag(eventData);
               
            _draggingByTouch = eventData.pointerId != -1;
            _isDragging = true;
        }
   
        public override void OnEndDrag(PointerEventData eventData)
        {
            base.OnEndDrag(eventData);
               
            _isDragging = false;
        }
   
        protected override void OnDisable()
        {
            base.OnDisable();
               
            _isDragging = false;
        }
           
        public override void Rebuild(CanvasUpdate executing)
        {
            base.Rebuild(executing);
               
            if (executing == CanvasUpdate.PostLayout)
            {
                _prevPosition = content.anchoredPosition;
            }
        }
    
        protected override void LateUpdate()
        {
            base.LateUpdate();
               
            var deltaTime = Time.unscaledDeltaTime;
            if (deltaTime > 0.0f && _isDragging && inertia)
            {
                Vector3 newVelocity = (content.anchoredPosition - _prevPosition) / deltaTime;
                velocity = _draggingByTouch ? newVelocity : Vector3.Lerp(velocity, newVelocity, deltaTime * 10f);
            }
               
            _prevPosition = content.anchoredPosition;
        }
1 Like

ofc

many, but not all

this works, can I add it to the ScrollView Adapter (free asset)?

Sure!

1 Like