Search Unity

Question ScrolLTo() method doesnt work

Discussion in 'UI Toolkit' started by sayacienes, May 23, 2023.

  1. sayacienes

    sayacienes

    Joined:
    Oct 1, 2019
    Posts:
    18
    Hi, Unity version 2022.2.6.f1 and this is my code:


    Code (CSharp):
    1. public class NewBehaviourScript : MonoBehaviour
    2. {
    3.     // Start is called before the first frame update
    4.     void Start()
    5.     {
    6.  
    7.         ScrollView sw = GetComponent<UIDocument>().rootVisualElement.Q<ScrollView>();
    8.         VisualElement element = GetComponent<UIDocument>().rootVisualElement.Q<VisualElement>("bu");
    9.  
    10.         Debug.Log("sw: " + sw.name);
    11.         Debug.Log("element: " + element.name);
    12.         sw.ScrollTo(element);
    13.     }
    14. }
    My test.uxml
    Screenshot 2023-05-23 at 20.00.59.png

    When I start the game, It doesnt scroll
    Screenshot 2023-05-23 at 20.03.29.png

    Can you help me please. I also tried 2021 LTS version
     
  2. cpalma-unity

    cpalma-unity

    Unity Technologies

    Joined:
    Nov 30, 2020
    Posts:
    110
    Hey! This probably doesn't work because the layout is not ready yet. You can try to delay the ScrollTo call after the first geometry changed event.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UIElements;
    3.  
    4. public class NewBehaviourScript : MonoBehaviour
    5. {
    6.     private ScrollView sw;
    7.  
    8.     // Start is called before the first frame update
    9.     void Start()
    10.     {
    11.         sw = GetComponent<UIDocument>().rootVisualElement.Q<ScrollView>();
    12.         VisualElement element = GetComponent<UIDocument>().rootVisualElement.Q<VisualElement>("bu");
    13.      
    14.         element.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
    15.     }
    16.  
    17.     private void OnGeometryChanged(GeometryChangedEvent evt)
    18.     {
    19.         var element = evt.target as VisualElement;
    20.         element.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
    21.         sw.ScrollTo(element);
    22.     }
    23. }
     
    sayacienes likes this.
  3. sayacienes

    sayacienes

    Joined:
    Oct 1, 2019
    Posts:
    18

    You are right, thank so much. I prefered solving it with async await. But your solution is more proper way to handle the situation. I will leave my solution too anyway:
    Code (CSharp):
    1. public class NewBehaviourScript : MonoBehaviour
    2. {
    3.     // Start is called before the first frame update
    4.     async void Start()
    5.     {
    6.         ScrollView sw = GetComponent<UIDocument>().rootVisualElement.Q<ScrollView>();
    7.         VisualElement element = GetComponent<UIDocument>().rootVisualElement.Q<VisualElement>("bu");
    8.         await Task.Delay(250); // wait 0.25s before scrolling
    9.         sw.ScrollTo(element);
    10.     }
    11. }
     
  4. TomTheMan59

    TomTheMan59

    Joined:
    Mar 8, 2021
    Posts:
    356
    Just use

    Code (CSharp):
    1. IEnumerator Start()
    2.  
    3. yield return new WaitForEndOfFrame()
     
    sayacienes likes this.
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,925
    Or use UniTask and you can just do:
    Code (CSharp):
    1. await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
    in async code.
     
    sayacienes likes this.
  6. sayacienes

    sayacienes

    Joined:
    Oct 1, 2019
    Posts:
    18
    Yes, I think this is another proper solution. But with only a handicap that GetComponent<>() needs MonoBehaviour. In my case I dont want to inherit but I still use your solution because I already keep root of visual asset source in a variable of a singleton named UIManager. So my final code is here:
    Code (CSharp):
    1. public class NewBehaviourScript
    2. {
    3.     ScrollView sw; // Start is called before the first frame update.
    4.     IEnumerator Start()
    5.     {
    6.         sw = UIManager.root.Q<ScrollView>();
    7.         //await Task.Delay(250);
    8.         yield return new VisualElement element = sw.contentContainer.Q<VisualElement>("bu");
    9.         // await Task.Delay(250); // wait 0.25s before scrolling
    10.  
    11.         // sw.ScrollTo(element); // It also works, it makes element to seem in viewport
    12.  
    13.         // I aimed to scroll center and it worked
    14.         sw.verticalScroller.value = sw.verticalScroller.highValue / 2;
    15.         sw.horizontalScroller.value = sw.horizontalScroller.highValue / 2;
    16.     }
    17.  
    18. }
    19.  
    20.  

    And my UIManager script taht is attached to the UI Document object in the scene:

    Code (CSharp):
    1. public class UIManager : MonoBehaviour
    2. {
    3.     public static VisualElement root;
    4.  
    5.     private void Start()
    6.     {
    7.         root = GetComponent<UIDocument>().rootVisualElement;
    8.     }
    9. }
     
  7. sayacienes

    sayacienes

    Joined:
    Oct 1, 2019
    Posts:
    18
    Thank you! I don't have enough knowledge of "await async" s in unity and this is an important information for me, I will keep it in mind.