Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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:
    90
    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:
    298
    Just use

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

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    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.