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

Select Background Image and Position of a VisualElement

Discussion in 'UI Toolkit' started by UniZoja, Aug 27, 2021.

  1. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    I am searching now for almost an hour and I cannot figure out how correctly to select the background image and position of a visual element.

    The query docu is really poor, there is literally nothing:

    UQuery is a set of extension methods allowing you to select individual or collection of visualElements inside a complex hierarchy.

    All the youtube guys show the same example with a Button.

    I have this structure:

    https://imgur.com/p6qrVbP


    Each "Layer" contains a background image:

    https://imgur.com/a/6ux3Rt7


    Here is my code:

    Code (CSharp):
    1. public class ParallaxController : MonoBehaviour {
    2.  
    3.     public VisualElement layer_0;
    4.     public Image layer_0_0;
    5.     public Image layer_1;
    6.     public Image layer_1_1;
    7.     public Image layer_2;
    8.     public Image layer_2_2;
    9.     public Image layer_3;
    10.     public Image layer_3_3;
    11.     public Image layer_4;
    12.     public Image layer_4_4;
    13.  
    14.     // Start is called before the first frame update
    15.     void Start() {
    16.         var root = GetComponent<UIDocument>().rootVisualElement;
    17.  
    18.         layer_0 = root.Q<VisualElement>("Layer_0");
    19.         Debug.Log(layer_0.style.backgroundImage);
    20.         Debug.Log(layer_0.style.position);
    21.         //Debug.Log(layer_0.Q.<Image>());
    22.         //Debug.Log(layer_0.Q.<Background>());
    23.     }
    24.  
    25.     // Update is called once per frame
    26.     void Update() {
    27.      
    28.     }
    29. }
     
  2. JG-Denver

    JG-Denver

    Joined:
    Jan 4, 2013
    Posts:
    77
    I am not clear on what you are trying to accomplish, so some additional detail would be good, but I think I know what your issue is.

    The background image is not a Visual Element of type Image, so you will not select it via query nor cast it to type Image. You will only select the Visual Element with the background and the set or get the style.backgroundImage. This property is a StyleBackground struct you can use for textures, sprites, etc. For example:
    layer_0.style.backgroundImage = new StyleBackground(someTexture);

    The position is also just a property of the style, in this case, and an enum for absolute or relative positioning. You would set it like the following:
    layer_0.style.position = new StyleEnum<Position>(Position.Absolute);

    If you are trying to query these properties for some reason, I wouldn't. I would use class names on your Visual Elements to identify ones with specific properties and use the root.Query method to return collects while only setting the class name parameter.

    This reference isn't exactly the same as the StyleBackground it is very close:
    https://docs.unity3d.com/Packages/com.unity.ui@1.0/api/UnityEngine.UIElements.Background.html

    I hope this helps clarify.
     
    gareth_untether likes this.
  3. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    Okay this helps, so I cannot get the values? Thats weird. If layer_0 has position: relative and left: 100%. I cannot get this info?

    What I want to accomplish:

    Start a coroutine, which will move the different images.

    EDIT:

    This is the first thing, which does log Null, it logs "Relative", but this is wrong, layer_0 is absolute.

    Code (CSharp):
    1. layer_0.style.position.value
     
    Last edited: Aug 28, 2021
  4. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    * does not log Null.

    For some reason I cannot edit my post anymore, because its spam. Nice forum featrues...
     
  5. JG-Denver

    JG-Denver

    Joined:
    Jan 4, 2013
    Posts:
    77
    How is do you know this layer is absolute is it set inline or with a style? You could also check the resolvedStyle property, which may vary in some cases but I am not sure.

    I am not sure why it isn't logging the value you expect for that layer, but if it is laying out as absolute you can just move forward to positioning the elements with the style.left, style.top or whatever fits your layout in your coroutine.

    You can just access the visual elements or image fields of your class that you have already loaded with Q from the coroutine.

    I would try it out and then look more closely at your element styling to ensure it really is set to absolute. If you aren't using UI Builder, I would to get yourself started and make sure your elements styles are constructed correctly.
     
  6. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    The issue is probably somewhere here. If I call .style where does it search for the styles? Are there two places for styles?
    My styles are indeed inline, but to be honest I dont see an other possibility where they can be:

    https://imgur.com/OqEstm2

    I set the styles in the inspector.

    I know I can export styles to a class, is this the second place? How would I access styles within a class? I assume its not .style anymore?

    Thanks a lot for the help.
     
  7. JG-Denver

    JG-Denver

    Joined:
    Jan 4, 2013
    Posts:
    77
    Your UXML looks fine at first glance and should work fine to set the position to absolute. Just remember that absolute positions relative to the parent, not the page view, as you work through this.

    I wouldn't mess with style classes and just stick with inline for now. You should take some time to study HTML elements and CSS classes to get a background in classes and how they work before you dive into using them.

    I have never had a reason to read a style in the Start event, I just assume the UXML is coming in fine and I have had no issues with that. It is possible the style just isn't getting updated at that point in the lifecycle. I can look into this, but not until tomorrow.

    In the meantime, I would go ahead with the coroutine and updating the style to see the results you are getting, and let me know what you see.
     
  8. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    I finished the code and it works fine, except I still cannot access the values, which I set in the inspector. I wrote 2 comments, so you see what I mean.

    Thanks for all the help, here is the code:

    Code (CSharp):
    1. public class ParallaxController : MonoBehaviour {
    2.  
    3.     public VisualElement[] layers;
    4.  
    5.     public IEnumerator PlayParallax() {
    6.  
    7.         float layer_x_base = 0;
    8.         float layer_x_x_base = 100;
    9.  
    10.         float layer_0_movement = 0.01f;
    11.         float layer_1_movement = 0.05f;
    12.         float layer_2_movement = 0.09f;
    13.         float layer_3_movement = 0.13f;
    14.         float layer_4_movement = 0.17f;
    15.  
    16.         // All will return 0, eventhough half of them should return 100, because I set it in the inspector
    17.         for (int i = 0; i < layers.Length; i++) {
    18.             Debug.Log(layers[i].style.left.value.value + " NAME: " + layers[i].name);
    19.         }
    20.  
    21.         // Need this to set initial value, because I cant get the already set style, which I set in inspector, why?
    22.         for (int i = 0; i < layers.Length; i++) {
    23.             if (i == 0 || i == 2 || i == 4 || i == 6 || i == 8) {
    24.                 layers[i].style.left = new StyleLength(new Length(layer_x_base, LengthUnit.Percent));
    25.             } else {
    26.                 layers[i].style.left = new StyleLength(new Length(layer_x_x_base, LengthUnit.Percent));
    27.             }
    28.         }
    29.  
    30.         for (int i = 0; i < layers.Length; i++) {
    31.             Debug.Log(layers[i].style.left.value.value + " NAME: " + layers[i].name);
    32.         }
    33.  
    34.         while (true) {
    35.  
    36.             for (int i = 0; i < layers.Length; i++) {
    37.                 if (i == 0 || i == 1) {
    38.                     layers[i].style.left = new StyleLength(new Length(layers[i].style.left.value.value - layer_0_movement, LengthUnit.Percent));
    39.                 } else if (i == 2 || i == 3) {
    40.                     layers[i].style.left = new StyleLength(new Length(layers[i].style.left.value.value - layer_1_movement, LengthUnit.Percent));
    41.                 } else if (i == 4 || i == 5) {
    42.                     layers[i].style.left = new StyleLength(new Length(layers[i].style.left.value.value - layer_2_movement, LengthUnit.Percent));
    43.                 } else if (i == 6 || i == 7) {
    44.                     layers[i].style.left = new StyleLength(new Length(layers[i].style.left.value.value - layer_3_movement, LengthUnit.Percent));
    45.                 } else if (i == 8 || i == 9) {
    46.                     layers[i].style.left = new StyleLength(new Length(layers[i].style.left.value.value - layer_4_movement, LengthUnit.Percent));
    47.                 }
    48.  
    49.                 if (i == 0 || i == 2 || i == 4 || i == 6 || i == 8) {
    50.                     if (layers[i].style.left.value.value <= -100) {
    51.                         layers[i].style.left = new StyleLength(new Length(layer_x_base, LengthUnit.Percent));
    52.                     }
    53.                 } else {
    54.                     if (layers[i].style.left.value.value <= 0) {
    55.                         layers[i].style.left = new StyleLength(new Length(layer_x_x_base, LengthUnit.Percent));
    56.                     }
    57.                 }
    58.             }
    59.  
    60.             yield return new WaitForSeconds(0.015f);
    61.         }
    62.     }
    63.  
    64.     // Start is called before the first frame update
    65.     void Start() {
    66.         var root = GetComponent<UIDocument>().rootVisualElement;
    67.         layers = new VisualElement[10];
    68.  
    69.         layers[0] = root.Q<VisualElement>("Layer_0");
    70.         layers[1] = root.Q<VisualElement>("Layer_0_0");
    71.         layers[2] = root.Q<VisualElement>("Layer_1");
    72.         layers[3] = root.Q<VisualElement>("Layer_1_1");
    73.         layers[4] = root.Q<VisualElement>("Layer_2");
    74.         layers[5] = root.Q<VisualElement>("Layer_2_2");
    75.         layers[6] = root.Q<VisualElement>("Layer_3");
    76.         layers[7] = root.Q<VisualElement>("Layer_3_3");
    77.         layers[8] = root.Q<VisualElement>("Layer_4");
    78.         layers[9] = root.Q<VisualElement>("Layer_4_4");
    79.  
    80.         StartCoroutine(PlayParallax());
    81.     }
    82.  
    83.     // Update is called once per frame
    84.     void Update() {
    85.        
    86.     }
    87. }
     
  9. JG-Denver

    JG-Denver

    Joined:
    Jan 4, 2013
    Posts:
    77
    I did my own testing and have confirmed what you are seeing. It appears to me that the style object remains empty until you change it in code and does not load with the inline style. It also does not load after Start has completed. Once you set the value in code it will be readable.

    This definitely doesn't match the behavior of HTML elements. Unity, is this by design or something to be resolved in the future?

    In the meantime, I suggest you either set the initial value in code or better yet build your own objects to represent these entities and just reference the visual elements they control. The latter is a bit more the way most design games. So you might have a class with the coordinates of each element, a member that is VisualElement or Image which you set in Start, and the other properties or behaviors of these entities.

    Good catch and good luck!
     
    sol-erides likes this.
  10. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    Thanks for confir
    Thanks a lot for confirming and helping me.

    Looks like something the guys from Unity should maybe fix or clarify.
     
    JG-Denver likes this.
  11. JG-Denver

    JG-Denver

    Joined:
    Jan 4, 2013
    Posts:
    77
    One last little thing with you objects for your entities. A simple way I like to work with these is to create a Component and make a List of the entity class in the component. You will want set the [Serializable] attribute on the entity class. Then you can manage the list of your objects in the inspector and set their coordinates, etc. in your scene.

    And I agree, Unity needs to resolve this or clarify it in the documentation.
     
    Last edited: Aug 29, 2021
  12. v-strelchenko

    v-strelchenko

    Joined:
    Aug 2, 2019
    Posts:
    13
    I'm facing the same issue, as you guys, when trying to get a hold of a backgroundImage though the style of property of a VisualElement.

    Is there any update on how to properly get hold of a backgroundImage in UI Toolkit? Other than setting it via code in the first place, so the style is no longer null, as suggested in this thread.