ScrollView has a few bugs

Hi,

The UIElements ScrollView has a few issues when operated with touch:

Issue #1: Elasticity ignores ScrollView mode.

Regardless of whether a ScrollView is only Horizontal or Vertical, in elastic dragging mode, you can pull in all directions, including horizontally in a vertical scroll view, basically pulling the scroll view off the rails.

Issue #2: Flick velocity is calculated incorrectly

Flick velocity is calculated incorrectly. Try to scroll up or down without letting go, wait 2 seconds, and then let go. The scroller suddenly flies off. The scroller isn’t actually calculating a velocity (distance over time), it’s only measuring the delta between the last two events, even if there are 10 seconds between them. The correct behavior would be to take the time between the two events into account to calculate an actual velocity. The scroller should also do nothing if the time between the last two deltas is more than a second, because this is likely not intended as a scroll, but a user changing their mind about scrolling at all. Currently, a decision to not scroll results in a scroll.

Issue #3: Scroll animation frame rate

Scroll animation has a different framerate than actual scrolling. In an app that runs that 60 fps per second, you’ll have the full frame rate while the finger is on the scroller, but the flicking frame rate immediately drops to 30 fps when you flick and let go. I think the timer that’s running the scroller animation is only being sampled at 30 fps.

Issue #4: Deceleration curve is extremely non-linear

The deceleration curve is based on multiplying a number with the speed, so 0 means immediately stopping, and 1 means going forever. However, the values in between don’t result in smooth deceleration, and the view hits a minimum speed you can’t get under regardless of deceleration value, unless you set it to zero and stop all scrolling. A value of 0.1, results in an abrupt deceleration to about 30% speed, and over the next 10 seconds, it only decelerates a little bit more. It feels like there’s a decimal error and the calculation is done in integers, because the curve quickly hits a floor of a minimum speed it can’t get under unless you disable flicking completely.

Also, multiplication isn’t a great way to decelerate, because it has an extremely long tail, and mathematically goes on literally forever (at a forceful 0.3x rate, it still takes 8 seconds to stop). You should consider subtracting a value from the speed instead of multiplying the speed with something. This creates a much more natural deceleration. The current curve is extremely exponential.

Issue #5: Elasticity is hard-coded when dragging

The current elasticity value only affects how a scroller hitting the end during a scroll is handled. It’s completely ignored when you drag over the edges. Please see how Apple does this. They apportion about 20% of the pointer movement to dragging of the view, indicating a sluggishness. This value is hard-coded in UIElements, and is close to 100%, which does not feel like any other elasticity implementation.

8 Likes

I have also found and reported a few scrollview bugs. The only one I think I also found in your list is:

Scrollview needs a ton of work. I hope its on the short list :frowning:

Hi perholmes,

Could you let us know on which version of the Editor you are? And the version of the UI Toolkit package if you are using it?

Thank you!

Hi,

I’m on 2021.2.0b7.3246. The built-in package version is just showing up as 1.0.0. But I’ve been asked this before, and I don’t know if there’s a more detailed version number somewhere else? It has been 1.0.0 for several editor betas.

This is kind of a placeholder version, the version to provide is the main Unity version like you did.
Thank you.

The toughest of these issues is the velocity not being calculated correctly, because it’s actually impossible to scroll to a place in a list and then tap on it. When you stop over the item you want to tap, it actually jumps out of view before you have a chance to click it. This means that it’s not just cosmetic, it actually doesn’t work, and I can’t release anything to users in this state. So I hope you’ll give this the highest priority.

Also a heads up, scroll views interact poorly with the device simulator. After one good scroll, they routinely jump to random places or to the top of the list, or become unresponsive. I can’t spot the pattern yet. It’s only in the device simulator. In the regular game view or on actual devices, you don’t have this randomness.

I would suspect it’s due to the tricks the device simulator does to manipulate coordinates to emulate resolutions, and UIElements using the true coordinates. In the latest device simulator, it’s necessary to go via UnityEngine.Device.SystemInfo, and the other classes under UnityEngine.Device to get device info.

+1
For all of the mentioned points. The ScrollView behaviour has a lot of room for improvement. Besides the mentioned points I also reported two ScrollView related Bugs:

  • Having a rotated element inside a vertical ScrollView (regardless its shape and size) makes the ScrollView scroll horizontal.
  • Absolute positioned objects inside of ScrollViews expand the content field thus make the whole ScrollView scrollable even though absolute objects shouldn’t influence the size of parent objects.

To be completely honest, we’ve been reimplementing all of this from scratch, and it’s much, much nicer. I was going to post a video at some point, because it’s kind of a pity if we’re the only ones that use this, and maybe it should become an alternative UI control framework.

The core issue is that all of what UI Elements provides is touch hostile. If what you’re putting inside of the scroll view is a menu, or controls, you need a hand-off between the controls and the scroll view itself. For example, you press a menu item, but as you drag up and down, the menu item lets go of the mouse capture and hands it to the outer scroll view, which then starts scrolling. The same for check boxes, buttons, sliders, text edits. Every phone does this. You initially interact with the control, but if your interaction is too vertical, the scroll view gets the events instead.

This is hard to do with the current UI Elements out of the box, because the controls and the scroll view aren’t aware of each other. So we’ve basically made what we consider “proper” versions of everything, and the only Unity controls we use are labels and text edits. Everything else is made from scratch.

And then for the scroll view, the Unity implementation of a scroll view is really poor. The whole flicking and rubberbanding and deceleration, is just not very pretty. Unity does deceleration with a multiplier so it continues forever after a steep braking, it can start moving after a pure press and release, and it just feels very homemade. Our scroll view has linear deceleration, rubberbanding, bounce, smooth scrolling with mouse wheel.

Our scroll view also slides horizontally for submenus, so you have submenus exactly like on your phone.

I’ll post a video when I have time to make one. Since I was only building this for us, some things might be too custom for general use. But it gives us app menus that feel professional, and I’d hope to at least raise the bar.

But for all the ranting, as a core HTML/CSS thing, UXML/USS works really well. I’m very happy how easy it is to build in it. What’s bad is only that the Unity-provided controls are crummy, are full of weird behaviors and unstylable sub-components, and no rhyme or reason for how to access this. So we gave up.

Here’s a screenshot:


2 Likes

And then same thing on a smaller device, the whole thing becomes scrollable, and the controls automatically arbitrate between interaction and letting the outer container scroll.

1 Like

I totally agree. Your UI looks awesome!
Reimplementing seems to be the best method for now. In the past hour i was able to hack some of the functionality into the existing systems by making a copy of the original ScrollView from the disassembly and changing the necessary parts. Most of the weird flicking and slow scroll behaviour was just connected to the animation update set to fixed 30ms(?!) setting this to 60fps (16ms) solved most of the scroll speed/acceleration issues. Similarly clamping one axis and having one axis elastic was just a small change.

If you could publish your system that would be awesome! Though I am guessing this is a lot of work. For now i can work with my hackish solutions until a better Unity implementation is published.

The overall UI Toolkit is awesome. It’s a total game changer but currently needs a lot of workarounds.

Looking forward to further updates!

1 Like

Could you share your solution? I spent most of the week on this issue and I didn’t find a fix yet. I changed the FPS to 60 and some issues are gone but the flickering when you press->drag->release is still there and I can’t find how to fix it.

Sorry for the late reply @mChapuis , i missed your post.
I don’t know if I can share the whole thing since its unity source code. The diff between my ConfigurableScrollView and the original ScrollView is attached. The changes are minor, as I wrote earlier most of the flicking was related to the framerate. Other than that I removed resetting the scroll if items were added and elastic scroll only in vertical direction. That was good enough for us for now. Waiting for a better Unity native version.

The Unity source code is (officially) available on Github, so no disassembling necessary:

https://github.com/Unity-Technologies/UnityCsReference/blob/master/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs

1 Like

When looking at the source, we see that the PostPointerUpAnimation is refreshed arbitrarily 30 times per second.
m_PostPointerUpAnimation = schedule.Execute(PostPointerUpAnimation).Every(30);
https://github.com/Unity-Technologies/UnityCsReference/blob/master/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs#L1245

It would be nice if the PostPointerUpAnimation was refreshed according to the application target framerate, for example.
This might explain the jittery animation raised on other topics ?

1 Like

I am having this issue on mobile.

I am wondering why the developers decided to go in this direction with the scrollview. Any insights?

It is absolutely bizarre to me that not only have people been posting bugs regarding the scrollview’s function for MONTHS, they have even posted some SOLUTIONS and yet Unity has not implemented these and fixed the package.

I just starting working with UI Toolkit and I notice several of the same problems which I posted here:

https://forum.unity.com/threads/bug-scrollview-allows-both-horizontal-and-vertical-drag-regardless-of-setting-clamped-also-broken.1217157/

  • Clamped doesn’t work with touch and drag (it is always elastic)
  • Touch and drag pulls the screen in any direction even if you only have it set to vertical.
  • The motion is unbelievably poor and abnormal/jittery looking.

Why on earth does every single other application (including my web browser) have a perfectly smooth working scroll that updates on every screen refresh with no trouble but Unity doesn’t? Very strange.

Even on the old GameObject based UI I couldn’t get a perfectly smooth scroll. It was always jittery. I thought the problem would be fixed by going to UI toolkit now that I’m just scrolling basic CSS/code with nothing fancier than this web page but it’s actually worse by default.

I will try Cery_'s fixes for now.

I have had smooth scrolling in Windows since the early 90s. I never thought that was a miracle until now. Who knew making something scroll properly is actually too taxing even for a $1000+ 2021 edition smartphone.

It’s about to be 2022. We almost have photorealistic 3D rendering, but still can’t scroll a basic text window smoothly anywhere in Unity.

1 Like

I tried applying this but I did not know how. I tried copying and pasting the entire ScrollView.cs into a new document with a new class name “MyUIScrollView” matching the new file name, but I got 42 errors so that didn’t work. How do you create a new scroll view class with these modifications?

Thanks

In the ScrollView class there are a couple of references to internal properties of the VisualElements class. The VisualElement is public so you can inherit from it normally but its subproperties like for example boundingBox are internal. This means if you define a new MyUIScrollView it will normally be put into your current assembly but the VisualElements definition is in the UnityEngine.UI assembly. Since they are in different assemblies internal properties can’t be accessed even though you inherit VisualElement.
My solution was to create a new Folder “UnityElementsExtensions” that contains an assembly definition reference to UnityEngine.UI and contains your new script (you can create a new Assembly reference with right click). An assembly definition reference basically puts all scripts inside its folder or any subfolders into the referenced assembly. That way your new MyUIScrollView is now in the UnityEngine.UI assembly thus is able to use the internal properties of VisualElement.
-Not sure if this is the best way to do this. Its all just a temporary hack.

Hope that helps!

2 Likes

Thanks Cery, that was very helpful. I was having trouble getting programmatic access to the UI Toolkit as well and this helped me understand how to fix it. I needed to add an Assembly file to my script folder with the asmdef files from UI Toolkit dragged onto it. Then it worked.

After reading all the problems with click management and scrollviews in UI Toolkit here and elsewhere by guys like midiphony-Panda and perholmes, I have decided to abandon the scrollview completely.

I was planning on developing most of my UI Toolkit elements programmatically anyway in C# (since they need to be generated on the fly). This just made me decide to do everything 100% in C#. I have a blank UXML document on my UI Toolkit game object and then a C# script that generates all my UI elements within that blank UXML document via GetComponent().

This allows me to do things like monitoring for clicks in my “Update()” of my C# script and I can do click and drag behavior by modifing “visualElementToDrag.style.top.value.value” based on the click drag behavior.

A scrollview is just a movable object behind a mask. So I am just recreating that in the simple way I need. In the simplest sense this is:

void Update() {

        if (Input.GetMouseButtonDown(0)) { //if mouse click;
            mouseDown = true;
            mouseDownPos = Input.mousePosition;
            mouseDownPos_1 = mouseDownPos;
            print("click started");
        }
        if (Input.GetMouseButtonUp(0)) { //if mouse release
            mouseDown = false;
            print("click released");
        }
        if (mouseDown) {
            if (Input.mousePosition != mouseDownPos_1) {
                rootVE.style.top = rootVE.style.top.value.value - (Input.mousePosition.y - mouseDownPos_1.y);
                mouseDownPos_1 = Input.mousePosition;
            }
        }
    }
}

According to 2022.2 source code, the ScrollView still executes a 30 fps elastic spring animation :face_with_spiral_eyes:
https://github.com/Unity-Technologies/UnityCsReference/blob/master/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs#L1374 (link doesn’t work anymore)
m_PostPointerUpAnimation = schedule.Execute(PostPointerUpAnimation).Every(30);

It would be very nice if we could modify the refresh rate of the ScrollView elastic animation (or maybe it could take Application.targetFrameRate into account).

EDIT : still the case on master branch of UnityCsReference, on 2023.2 at the time of writing :

7 Likes