Search Unity

Optimized ScrollView Adapter + Playmaker support

Discussion in 'Assets and Asset Store' started by xucian, Apr 1, 2016.

  1. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Hi,
    This asset seem to be very optimized while having a lot of features, which is good.
    But the tutorial link points to an old video, which is using ScrollRect while the latest version doesn't use it anymore.
    Is an updated tutorial planned?

    I understand that there's a way to create an empty optimized listView with a few clicks, but in order to understand fully how something works I've always found that it was easier to do it from scratch, step by step, in order to go through everything which is needed and see directly why it is needed.

    Speaking about what is needed and what is not, the package contains several directories, but it's not very clear what can be safely removed and what cannot.
    In most packages there is a 'Demo' directory, which is optional.
    Here it's not so clear, do I need to import the 'img' directory for example? Or is it only used in the examples? Same thing for some other directories such as 'Fonts', 'Resources', etc.
    Are all the source files in 'Scripts' mandatory (except for the ones in Demos I presume)?

    Also, what is the use of 'Frame8'? It seem to be some sort of tools-bag, but the name isn't very explicit.
     
  2. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    Since the view layouting is done behind the scenes for you, it doesn't make much of a difference whether there is a ScrollRect or not - you would've never interacted with it manually, anyway. That tutorial still covers at least 90% of what you need to know to get started - the basics are the same. So don't get discouraged just because it's for an older version. Also, the manual doubles the video in presenting the fundamental workflow - they present the same thing, but from different perspectives.

    You brought something important into discussion. You're right, what's essential and what's not should be definitely explained in the manual's dedicated section. I'll do that now. Take a look there in 1h from now.
    I'm still in doubts on whether to move all the demo code in a separate package or not. The main downside is that a surprising amount of users don't read the documentation and would probably thing there are no demos - similar scenarios have already happened.

    About frame8, someone before asked this, so it was included it in the Manual's FAQ section at that moment. You're right, it's our internal set of tools we use for all of the projects. Here, it's just a sub-set of the original library. "frame8" it's just a name. I remember the other person that asked about frame8, also complained that it's not very clear what it is, and even left a negative review. I fail to understand to this day why a name can be a disputable thing. "Frame8" is like "John".
    If you think something needs to be done regarding this, I'll be more than willing to listen to your suggestions. The only thing I can't do is change its name (for historical reasons).

    Thank you for the pointing these things. Helps improving the documentation

    --Lucian

    Edit: Actually, it's easier to first use the OSA wizard to get the thing up and running and just have an overview on the main components (the prefab, the scrollbar, the content, the viewport, the ScrollView GameObject containing the OSA implementation, the implementation itself, which is extensively commented with explanations). After that, if you wan to do everything by hand, from scratch, that's piece of cake - you have something on which to look to "cheat" if you get stuck
     
    Last edited: Feb 4, 2019
  3. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Thanks, I'll follow the old tutorial then.

    I didn't meant another package, just putting everything related to the example to a specific directory. For example, if the content of 'Fonts' and 'img' is only used by the examples, they should be placed near the examples (maybe an 'Examples' directory containing 'img, 'Fonts', 'Scene', etc.).
    Currently, it's not clear what is linked exclusively to the examples, the folder 'ExampleItemPrefabs' seem to be in that case, but are the other folders in the 'Templates' folder also linked to the examples or not? I do not know for sure without trying to remove them and see what happen.

    In most packages I have bought, there is a 'Demo' folder (or a similar name), and you need to import everything except that directory in the final project.
    It's just an easy way to remove what is not necessary for production.

    I usually import the whole package in a test project in order to take a look at the examples/demos, and to play a bit with the asset until I know how it works.
    And after that, I import only the important part into my project, which usually mean not importing the demos/examples folders, so I do not clutter my project with unused assets.


    Naming is very important.
    In the context of a ScrollView, the word 'Frame' may mean something very specific, such as the container of the ScrollView or something like that, so it's misleading.
    In fact, I spent some time trying to figure out what the '8' could mean, was it a word-game with the sound 'Eight' (like some people do with 'Set2Display()' for example), but I failed to find any such meaning, but as English s not my native language I am still not sure I haven't missed something very subtle about the meaning of the '8' in 'Frame8'.

    If 'Frame8' is a collection of internal tools you use, why not just call it 'InternalTools'?


    For the record, I worked with someone who didn't understood the need to name things in a code (he could code assembly directly in hexadecimal...).
    He was using variables names 'a', 'b', 'c', etc. same for the methods. It could go over 'm' in some algorithms...
    He had no problem understanding his code, but it was a real pain for about anybody else, and most of what he did had to be ditched when he stopped working with us, it would have cost more to understand it rather than just re-coding it from scratch.

    I know that this is an extreme example (but unfortunately very real), and your code is not nearly like that, but it does prove that good naming is important, if not for you, then for your customers.
     
  4. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I know. I just mentioned something that I was thinking of lately

    The code in there is not specific to this project(*), and thus the namespaces can't change (what I'll change is the Frame8 folder to something like "InternalTools" like you mentioned and change the MenuItem from "frame8->OSA" to simply "OSA").
    Do you think keeping only the namespace "frame8.**" would still be something a bit undesirable or would the above changes I mentioned be enough?

    Thanks again for taking the time to write all these things!

    (*)In the original project, the source files included in this project under the Frame8 folder are symbolic links to files inside a bigger Visual Studio project (which we compile to a dll for use in big Unity projects, but here it'd be too heavy to use it, so just the necessary files are included directly).

    --Lucian
     
  5. chambino

    chambino

    Joined:
    Jun 4, 2018
    Posts:
    12
    Can you have the Hierarchy list with the sticky header? ie the header always stays at the top until next header is up to the top?
     
  6. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    518
    I can't find a best solution for my case - I got multiple sizes data but using same prefab. Content size fitter example doesn't fit to my case because a) I got too complex UI item structure b) I got predefined item size (keeped in Data)

    So, what is the best way to setup adapter in that case? I used another asset before, where was pretty easy implemented method in a CellItem - GetCellSize where I could run the calculations or just return predefined size. Any similar approach here?

    My Adapter structure is:
    Data (keeping size, texture url, title, info etc)
    UiItem (keeps Data)

    Standard holder
    MyItemViewsHolder : BaseItemViewsHolder
    got public property UiItem, which is initialised on CollectViews() method.

    MyParams : BaseParamsWithPrefabAndData<Data>
     
  7. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Replied to your email. See you there :D
    PS for others reading the thread: this will be available there in the next version, which will come in a week or two
     
  8. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    What you describe was our approach at the very beginning of the asset's development, which turned out to not be flexible enough for the all of the demos we wanted to support.
    The closest you can get now is to override CollectItemsSizes, and set the size for all items, from index 0 to N-1.

    If you know exactly which items have a constant size, you can set it in inspector beforehand (DefaultItemSize property) and only set a custom size for all the others.

    You can see this used in the IncrementalItemFetchExample script.

    For example, if you have(bolded have an unknown size): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    you'd do in CollectItemsSizes():
    Code (CSharp):
    1. BeginChangingItemsSizes(1);
    2. itemsDesc[1] = ..
    3. itemsDesc[2] = ..
    4. EndChangingItemsSizes();
    5.  
    6. BeginChangingItemsSizes(5)
    7. itemsDesc[5] = ..
    8. itemsDesc[6] = ..
    9. itemsDesc[7] = ..
    10. EndChangingItemsSizes();
    This allows us to do a lot of optimizations behind the scenes. Of course, the downside is that the time it takes to change the items count is O(N) (where N is the number of items of different sizes), which is a problem if you have like 50k-100k items. Item complexity doesn't matter, just their count. Also, if you need the views to already be constructed before you calculate the size, this is almost always a sign you need to use the CSF approach, which is also the way to go if you have much more than 50k items.

    In this case, take a look at the ContentSizeFitter example. I know you said your items are too complex for the CSF, but don't worry, you won't see any drop in performance if you'll use it. Just try it and come back here or email me if something goes wrong.

    Edit: if you already know the exact sizes, you could skip the CSF and use RectTransformExtensions.SetSizeFromParentEdgeWithCurrentAnchors(..) to change the RectTransform's size directly, as a CSF component would've done, so the only difference between your case and the CSF example is that instead of doing CSF.enabled = true, you'd call the aforementioned method in the exact same place (see the CSF example for the exact approach)

    --Lucian
     
    Last edited: Feb 16, 2019
  9. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    So, if I understand well, it means that if I have one million elements in the scrollView and if I enlarge the first one with an animation over 20 frames, one million elements will be recomputed every frame for 20 frames?


    So, there isn't a system of buckets or something like that?
    I mean, the elements could be in buckets (and the buckets in larger buckets and so on), about 100 elements per bucket of level 1, and about 100 buckets level 1 in a bucket level 2.
    That way, when the size of an element is changed, all the algorithm would have to do is to update the elements in the same bucket, and then update all the buckets headers.
    With such a system about 200 elements/buckets updates are enough, and only about 300 updates for 100 million elements.
    Of course it wouldn't be that ideal because elements can be moved/removed/inserted, so the buckets may not contain exactly 100 elements each, but the gain would still be of several orders of magnitude.

    And even the fact that it would add some code to manage the buckets wouldn't be a problem, I'm pretty sure it wouldn't add too much compared to looping over the '_Keys and '_SizesCumulative' dictionaries using the keys to access each element instead of an iterator.
    Speaking about these 2 dictionaries, is it really necessary to use twice the key to access their data? Wouldn't it be possible to put a struct (or class, I don't care) containing the size and the cumulated sizes in order to have only one dictionary access each time?
    I have given a fast look at it and it seems that each time '_SizesCumulative' is accessed, '_Keys' is also accessed either just one line before or one line after.
     
  10. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    I'm sorry, but I can't really get a main idea about your post, apart from the fact that keys and cummulativesizes dicts should be merged (which it's not possible). Keys are not stored for each item. It a bit more complicated (but efficient).
    You mention multiple subjects and I don't know really which one to follow.
    I also don't see to which post are you exactly replying when saying "So, if I understand correctly, [...]"
    What I can guarantee you is that you can have around 2billion items, in every scenario provided in the package(the scenes that limit the number of items to less are the ones that use random sizes, where it didn't make sense to use the ContentSizeFitter approach because the sizes can be generated randomly, directly in the code), at at least 60fps and the only variable between different scenarios it'd be the time in takes to implement them.
    However, I'm curious about each of your points. Can you elaborate on them, one by one?

    Also, what is that you're trying to achieve in the current project? Or you're just curious about the implementation?
    The core details of the implementation are not for public access, so if you want me to give you more info about any aspect of it, I'd be glad to do so via email. I can explain you any individual piece of the code.

    --Lucian
     
    Last edited: Feb 15, 2019
  11. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Sorry, copy/paste error from my side.
    I meant merging '_SizesCumulative' and '_Sizes' (not '_Keys', which is the list of keys in the dictionaries...) dictionaries.
    I just checked:
    - each time an element is added to one dictionary, it is also added to the other one
    - each time an element is removed from one dictionary, it is also removed from the other
    - each time one of the dictionary is cleared, the other is also cleared
    - even when the '_Sizes' dictionary is rebuilt from scratch (in 'RotateItemsSizesOnScrollViewLooped()'), then the '_SizesCumulative' is rebuilt using the same keys
    I'd say that merging them will work without any other modification, except if you use reflection or code injection to add/remove elements from one dictionary only.


    I am curious about the implementation, I always take a look before I integrate something in my project.

    As for what I want to achieve, there are several goals.
    I know that I will have to manage a lot of logs (probably a few hundreds thousands), but I may need to be able to click on the title of a log in order to display the details of the log.
    And I'd like that to be smooth, as in your examples where it is possible to expand an element, except that the enlarged size depends on the content of the log, and there will be a lot of different possible sizes.
    So, I need to ensure that even resizing an element will not cause lag/GC.

    Another need I have is to display orders/information, which can also be expanded.
    The difference here is that the computer may be the one expanding the information in order to attract the eye of the user (meaning that there could be a lot of elements expanded after a long use of the project).

    The other needs shouldn't require any large number of elements, so they won't be a problem.



    In your previous message (#308), you spoke about O(N) complexity, which is what has attracted my attention.
    I need to know if 'N' is the number of displayed items, the total number of items, or the number of items which had their size modified.

    From what I have seen, the '_Keys' list seem to contain the indexes of the elements whose size is not 'standard' (but it may be a bit more complex when elements are removed/moved, I only looked into the code for 30mn).
    As there are a lot of dictionary accesses over the elements of the '_Keys' list, I'm a bit afraid for the performances when expanding one of the first elements if a very large number of elements have been previously expanded.

    In the 'IncrementalItemFetchExample' example, new data are fetched and their sizes set using an override of 'CollectItemsSizes()'.
    In that case (and if the sizes of the items are random), all items are added to the '_Keys' list, does that mean that in that case the 'N' is the total number of loaded items?
     
  12. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey, you almost got me convinced :D

    Thanks for observing this. I don't remember if I ever though about this, but I also realized now that this is still not possible (I really would've liked to, since it would've simplified the code a bit):
    I went more in depth through it and also tested the looping behavior. RotateItemsSizesOnScrollViewLooped requires them to be 2 separate dictionaries. I also ran some tests scrolling through 2B items with CSF attached and after 2 minutes of frantic big jumps (using the scrollbar), their total memory was around 1MB. Given the extreme scenario, this proves memory no concern. I understand the need to merge them (I'd really liked them to be merged too), but when performance and code aesthetics can't coexist - like in this case -, we're stuck with repetitive/duplicate code, and we're forced to like it :D

    That's very good.

    This was done before, although I don't know if the user needing this had posted in this thread. There's no demo that shows this (but it's on the to-do list for some time). You could do a search here to find it. But we can also re-iterate it when the time will come (via email or here).
    The basic approach includes using a variation of the CSF approach. The only difference is that you won't need a csf on your items, only on an external instance of your prefab, which you keep off-screen, and each time a views holder needs to be updated, you'd first update that external instance instead, just so you could then immediately retrieve its size and manually resize the real item and call "ScheduleComputeVisibilityTwinPass", which would re-position all of the views again after the current pass. But we can expand this at the moment you'll need it. You can just start with having a fixed expanded size for all, and from there it's a piece of cake to complete the implementation.

    No issue with this. You can have as many expanded items as you want

    Right. I edited that so others would also know what I was referring with "N". It's the number of items with a size different than DefaultItemSize (declared in BaseParams, which you can set via inspector, or it can be set for you form the prefab at initialization, if you use BaseParamsWithPrefab or any of its derived classes).

    It's sounds reasonable, but since the CSF example - where every item has a different size and each size is computed the first time the item is seen - works smoothly, I think that's a proof that you should be able to expand as many items as you want and still expand others that come before them in the list. There are some tweaks that make this possible, that I can't describe publicly

    You're right, but in a real case you won't use CollectItemsSizes(), unless you really have more than, let's say, 10k items, or if you don't mind the bigger hiccup when inserting/removing/resetting items. In the case of very numerous items, the CSF approach is recommended (which uses a much faster way of managing sizes, even if all 2B items have different ones)

    --Lucian
     
    Last edited: Feb 16, 2019
  13. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    I saw that method, but I fail to understand why it would prevent to merge the 2 dictionaries.
    I know that I'm a bit stubborn, but it's the only way I found to improve my own code, by always trying to find a way to do things in a better way.

    Currently 'RotateItemsSizesOnScrollViewLooped' recreates the dictionaries and the list from scratch, using 2 loops. The first one fills '_Keys' and '_Sizes', and the second loop fills '_SizesCumulative'.
    But from what I see, the second loop will loop over all the newly created '_Sizes' elements and add a corresponding '_SizesCumulative' element using the exact same key.
    Meaning that in the end there will be the same numbers of elements in '_Sizes' and '_SizesCumulative', and the 2 dictionaries will also have the exact same keys.
    So, I think we could have that:
    - the first loop were to fill '_Keys' and the merged dictionary, only initializing the first part of the merged elements
    - the second loop wouldn't create any dictionary element, but it would loop over the merged dictionary (filled in the first loop) and initialize the second part of the merged elements (the cumulative size)

    I'm not sure it could be done in only one loop as the elements aren't added to the list, but inserted using a specific ordering rule.

    The thing is, accessing (reading or changing) a value in the 2 dictionaries is irrelevant to the merging, only the following actions must be synced:
    - adding an element
    - removing an element
    - clearing a dictionary
    If only one of the data is read it's not a problem, and if only one of the data is written (not added to the dictionary, but have its value modified) it's not a problem either and will not interfere with the fact that the data itself is within a class containing other data or if it is alone.


    I agree, I also have quite a few things like that and I have to live with it. But every time I go over them, I try again to find a better way, and sometimes (rarely unfortunately...) I find one.


    Thank you for the info, I'll implement that when I'll have some time.
    The important thing for me was to ensure that there was a viable solution, and there is one, thanks again!


    Thanks, that's good enough for me.
     
  14. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Nope :)
    I know it seems that way. But what it does (the second loop) is looping on the first same number of keys. Keys could be added or not in the first loop, so no way to know where they'll be added, until a full loop is done.
    I left some code commented intentionally so I won't be tempted to merge the 2 loops.

    You're welcome :)

    --Lucian
     
  15. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    I agree that the 2 loops cannot be merged.
    But the 2 dictionaries can. The resulting dictionary would have to be filled half in the first loop, and half in the second loop, that's all.

    The second loop get the 'current' key in 'newKeyWithCurSize'. Then it adds a new element in the '_SizesCumulative' dictionary using the 'newKeyWithCurSize' key, and the 'newKeyWithCurSize' key does exists in the '_Sizes' dictionary (it comes from the '_Keys' list, and all the keyrs from the '_Keys' list have been added to the '_Sizes' dictionary in the first loop).
    That means that it is possible to have a dictionary (called for example 'SizesAndCumulativeSizes') containing objects of type 'SizeAndCumulativeSize' containing each 2 values, 'Size' and 'CumulativeSize'.

    The order in which the elements are added in the dictionary is not important (because it is a dictionary), only the order in which the values are initialized is important.
    Which is why it is important to keep the 2 loops, the first loop adding the elements in the 'SizesAndCumulativeSizes' dictionary and initializing only the 'Size' part of the elements.
    The second loop will only initialize the 'CumulativeSize' part of the elements, probably in another order compared to the first loop, which is why the 2 loops cannot be merged.

    The only goal of that would be to have twice less dictionary accesses.
    It's a shame that it's not possible to also merge the 2 loops, but we can't always have it all.
     
  16. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    518
    I have followed that example. Here is a code
    Code (CSharp):
    1.  
    2. base.CollectItemsSizes(changeMode, count, indexIfInsertingOrRemoving, itemsDesc);
    3. if (changeMode == ItemCountChangeMode.REMOVE || count == 0)
    4.     return;
    5.  
    6. itemsDesc.BeginChangingItemsSizes(0);
    7. for (int i = 0; i < _Params.Data.Count; ++i)
    8. {
    9.     Debug.Log($"For index: {i} size is: {_Params.Data[i].size.y}");
    10.     itemsDesc[i] = _Params.Data[i].size.y;
    11. }
    12. itemsDesc.EndChangingItemsSizes();
    13.  
    Items size set correctly, but initially cell items position is not correct - https://take.ms/36L3A .
    It's set correctly only after I scroll down till the end
     
    Last edited: Feb 18, 2019
  17. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I looked again and the method in the first loop BinaryAddKeyToSortedListIfDoesntExist actually re-populates the keys list with the same keys. I didn't pay attention to the fact that the keys list are cleaned completely, and thought new ones will be added (t'was a long time ago I made this method).
    Thank you for helping with this! I ended up merging the 2 dictionaries :D

    -- Lucian
     
  18. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    I'm assuming you use the InsertItems method?
    Your example works only if you use ResetItems.
    To support them both, do something similar to what the IncrementalItemFetchExample script does:

    Code (CSharp):
    1.         protected override void CollectItemsSizes(ItemCountChangeMode changeMode, int count, int indexIfInsertingOrRemoving, ItemsDescriptor itemsDesc)
    2.         {
    3.             base.CollectItemsSizes(changeMode, count, indexIfInsertingOrRemoving, itemsDesc);
    4.  
    5.             if (changeMode == ItemCountChangeMode.REMOVE || count == 0)
    6.                 return;
    7.  
    8.             int indexOfFirstItemThatWillChangeSize;
    9.             if (changeMode == ItemCountChangeMode.RESET)
    10.                 indexOfFirstItemThatWillChangeSize = 0;
    11.             else
    12.                 indexOfFirstItemThatWillChangeSize = indexIfInsertingOrRemoving;
    13.  
    14.             int end = indexOfFirstItemThatWillChangeSize + count;
    15.  
    16.             itemsDesc.BeginChangingItemsSizes(indexOfFirstItemThatWillChangeSize);
    17.             for (int i = indexOfFirstItemThatWillChangeSize; i < end; ++i)
    18.                 itemsDesc[i] = <size at i>;
    19.             itemsDesc.EndChangingItemsSizes();
    20.         }
    However, if you only use the ResetItems method to change the items, you should get them correctly aligned.
    ItemDraggingExample and IncrementalItemFetchExample already use this and it's not an issue. In this case, the issue should be somewhere else. I think I'll need to see your project and track down the issue manually. Let me know

    --Lucian
     
  19. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    518
    Checked again, and nothing worked. I used ResetItems in my case.

    Code (CSharp):
    1. public void SetData(List<AnalyzeData> datas)
    2. {
    3.             _Params.Data = datas;
    4.             ResetItems(datas.Count);
    5.             //InsertItems(0, datas.Count);
    6. }
    Just sent an email (thefallengamesstudio@gmail.com) attaching scripts I use. (Note, I used InsertItems just now, to test only, before I used ResetItems only)

    UPDATED
    Finally found the problem - for cell item I used DoozyUI asset UIToggle and it runs some position initialisation logic on Start. There was a conflict between 2 systems
     
    Last edited: Feb 20, 2019
  20. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    If you foster a community where you post huge screenshots about your arguments with other people, this qualifies as harassment. I have removed post from public view. Contact mods to resolve it without personal stress. Posting huge screenshots makes your own thread a candidate for deletion or being locked. Don't do it.
    Do not respond to this mod note.
     
  21. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi there! How i should customize distance between digits in datetime_picker example?
     
  22. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hmm, I think that'd mean to change the font with one that already has a built-in wider space between chars (easiest approach) or replace the Text components with TMPro components altogether and use that to customize the characters however you want. In the second case, you also need to change the code to expect a TMPro UGUI text component instead of the simple Text.

    If you, however, ask about the space in the formatted text that's generated based on the selected day, hour etc. look into DateTimePickerDialog.cs and in Update you'll see what format it's used to display it. You can also retrieve the DateTime structure for the current selected values and display it however you want

    -- Lucian
     
    justtime likes this.
  23. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Also, when i make prefab from datepicker example and try to create it in runtime (outside of canvas) Editor just freezes :( So if you just drag datetime prefab on the scene, not under cavas, it freezes.


    I found that if call instead of
    Code (CSharp):
    1. if (_AutoInit)
    2.    ExecuteAfter(.2f, AutoInit);
    this
    Code (CSharp):
    1. _AutoInit()
    it will not freeze, but we get a warning that OSA is not initialized yet, and we must setup data again.
     
    Last edited: Feb 21, 2019
  24. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Thanks for reporting this. I now imagine why sometimes you'd want to instantiate the adapter in an empty/disabled game object and enable it after.
    The problem was that if you don't instantiate it inside a Canvas, it reports zero or negative size, which is bad and messes up the internals. This should be prevented and here's what I did:

    1. I added the following in the BaseParams.cs, method InitIfNeeded():
    Code (CSharp):
    1.         public virtual void InitIfNeeded(IOSA iAdapter)
    2.         {
    3.             _ScrollViewRT = iAdapter.AsMonoBehaviour.transform as RectTransform;
    4.             LayoutRebuilder.ForceRebuildLayoutImmediate(ScrollViewRT);
    5.  
    6. // Start of added code
    7.             var rectSize = ScrollViewRT.rect.size;
    8.             string widthOfHeightErr = null;
    9.             float sizErr;
    10.             if ((sizErr = rectSize.x) < 1f)
    11.                 widthOfHeightErr = "width";
    12.             else if ((sizErr = rectSize.y) < 1f)
    13.                 widthOfHeightErr = "height";
    14.             if (widthOfHeightErr != null)
    15.                 throw new UnityException("OSA: '" + ScrollViewRT.name + "' reports a zero or negative " + widthOfHeightErr + "(" + sizErr + "). " +
    16.                     "\nThis can happen if you don't have a Canvas component in the OSA's parents or if you accidentally set an invalid size in editor. " +
    17.                     "\nIf you instantiate the ScrollView at runtime, make sure you use the version of Object.Instantiate(..) that also takes the parent so it can be directly instantiated in it. The parent should be a Canvas or a descendant of a Canvas"
    18.                     );
    19. // End
    20.         }
    2. For even more freeze-proofing, I added this method in ItemsDescriptor.cs:
    Code (CSharp):
    1.  
    2.         void AssureValidDefaultItemSizeOrThrow()
    3.         {
    4.             if (_DefaultSize <= 0)
    5.                 throw new InvalidOperationException("ItemsDescriptor.AssureValidDefaultItemSizeOrThrow: Item prefab's size is zero or negative(" + _DefaultSize + "). This is not allowed. Is your prefab badly resized at initialization?");
    6.         }
    3. Called it inside the ReinitializeSizes method:
    Code (CSharp):
    1.             if (newDefaultSize != null)
    2.             {
    3.                 if (newDefaultSize != _DefaultSize)
    4.                 {
    5.                     if (changeMode != ItemCountChangeMode.RESET)
    6.                         throw new UnityException("Cannot preserve old sizes if the newDefaultITemSize is different!");
    7.  
    8.                     _DefaultSize = newDefaultSize.Value;
    9.                     //_AverageSize = _DefaultSize = newDefaultSize.Value;
    10.                 }
    11.  
    12. // Start of added code
    13.                 AssureValidDefaultItemSizeOrThrow();
    14. // End
    15.             }
    This just prevents the freezing. Of course, to solve the problem entirely, you still need to make sure you instantiate the datetime picker inside a Canvas. You can create an empty object inside your main canvas. Or, if there's no canvas whatsoever in the scene, just add one. Let me know if for some reason you can't do that and we'll discuss from there.
     
  25. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Thanks! I'll check this solution, but it would be nice to have opportunity create this from outside of the canvas, cause i have some kind of dialog's pool with canvas, but firstly i create objects from prefabs and only then parent them to canvas parent.
     
  26. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    No one can stop you from having multiple canvases. Having 2 instead of one shouldn't incur performance problems. The 2nd canvas can be in screen-space overlay, for simplicity
     
    justtime likes this.
  27. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    I agree with you. I meant that it would be more convenient to stick with old logic and not make another one for this particular case =)
     
  28. themalakarraja

    themalakarraja

    Joined:
    Nov 26, 2017
    Posts:
    3
    I want to implement a scroll list view, when the user searches something it will display item list a with some texts and image just like Youtube search. First and every time it will fetch 20 items data and download images from the server.

    Please tell me how can I do this.
     
  29. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I understand. I really want to help, but I just couldn't come up with any viable solution for this. I made it work at some point, but I'm sure it would've broken somewhere else shortly. Because everywhere in the code it assumes there's a canvas already.
    Let me know if you managed to solve this in the new way

    --Lucian
     
  30. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hi,
    Well, your requirements are pretty general and you should take a look at the manual first, but here are some directions:
    - see the OSA wizard section in the manual + maybe the video on YouTube about it. This really is the quickest way to get started with a minimum amount of code and get a feel about how it works.
    - next, you should check the IncrementalItemFetchExample, since it simulates incrementalidata retrievals from a server. You should skip overriding the CollectItemsSizes callback, if your sizes are constant (specifically, items' heights, assuming it's a vertical scroll view).
    -next, look about how the RemoteImageBehaviour and\or SimpleImageDownloader utility scripts are used in different places. You can look at the "grid_plus_async" for their correct usage. This is, of course, if you don't already have your own image downloading\caching scripts.

    Hope this was useful
    --Lucian
     
  31. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Sorry, I replied without the Reply button. See the post above

    --Lucian
     
  32. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    In the 'Example scenes & utilities' chapter of the manual, the directories to remove when we do not want the demos are indicated.
    Unfortunately it doesn't seem to be detailed enough.
    It is only about the 'root' directories, not about the children directories.

    For example, in the 'Scripts' directories I shouldn't need to keep the 'Demos' sub-directory, should I?
    And what about 'DataHelpers'? Is that directory mandatory? Are we forced to use 'LazyDataHelper', 'LazyList' or 'SimpleDataHelper'?
     
  33. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi! I'm trying to adjust design for datetime picker and when i set height twice less then it was (layout element + content size fitter on the root parent), picker becomes consist of 5 numbers, not 3, and font becomes very small. Where i can setup this kind of logic?
     
  34. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    This is just a transitory phase, so not every sub-folder was examined closely. The next release will have them drastically separated to the bone :) specifically, there will be 3 components: core files (unpackaged), demos (package), utilities(package).
    I verified now and the Scripts/Demos can also be removed. Updated the manual. Thanks.

    And no, you aren't forced to use datahelpers at all. They just allow you to write less code when you need to insert/remove/reset items on your list, because otherwise you need to store your models in a separate list, and each time you insert/remove you need to:
    1. insert/remove/reset on your list;
    2. notify OSA about the insert/remove/reset (and the exact indices, like where did you remove from or where you added etc.)
    The data helpers do this for you in one unified call. You can also choose not to notify the adapter about the changes in the data by taking the List property and modify it directly, then calling NotifyListChangedExternally when you're ready to update the view. This is available for SimpleDataHelper.
    For LazyDataHelper, you can set the SkipNotifyingAdapterForNextEvent property to true before each modification and call its NotifyListChangedExternally after, the same way.
    This is what makes the datahelpers appropriate in almost all scenarios and why it's the default choice in the demos.
    Of course the the part with the LazyDataHelper has no use if you always want to modify your list externally, then notify OSA manually, in which case you're better off with no datahelper at all.


    Hey,

    This would happen if you modify the DateTimePickerDialog/Adapters game object as opposed to the root (DateTimePickerDialog).
    If you want to modify the layouting, see the DateTimePickerDialog prefab in OSA/Resources/OSA. Take note that this will change all DateTimePickerDialog prefabs you'll use when you call DateTimePickerDialog.Show(...).
    You can re-arrange the UI elements in there however you want. What you're looking for is making the item prefab's height scale with its parent height. Check the anchoring

    --Lucian
     
  35. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    That's nice, I like it!
    Do you have an estimate for the release?
    Not a precise date, but it would help my planning to know if it's a matter of weeks or months.


    Thanks.


    Thank you, I'll look into them.
     
  36. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Weeks :) Let's say around 15th this month

    --Lucian
     
  37. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Good!
    I'll continue to play with OSA in my test project and wait for the new release to integrate it into my project to directly have the proper folders architecture.

    Will you use the assembly definitions?
     
  38. doctorpangloss

    doctorpangloss

    Joined:
    Feb 20, 2013
    Posts:
    270
    Solved: When using OnItemHieghtChangedPreTwinPass, it should be noted that the prefab should have its ContentSizeFitter set to disabled in its prefab. Additionally, it is considerably simpler to always mark the views holder for rebuild (this was the only way I could get the sizes correct no matter how I manipulated the underlying collection). The "hasPendingVisualSizeChange" view-model optimization is very finnicky.

    When recreating the chat example and calling Refresh(), this loop in OSASubComponents hangs the application until nextPotentialIndex underflows I think:

    Code (CSharp):
    1. // Go down/right until a visible item is found
    2.                 while (negRealInsetStart_posRealInsetEnd <= -itemSize)
    3.                 {
    4.                     int nextPotentialIndex = index + neg1_posMinus1;
    5.                     if (nextPotentialIndex == firstOutsideBoundsIndex)
    6.                         break;
    7.  
    8.                     index = nextPotentialIndex;
    9.                     itemSize = _ItemsDesc.GetItemSizeOrDefault(index);
    10.                     negRealInsetStart_posRealInsetEnd += itemSize + _InternalState.spacing;
    11.                 }
    This seems to occur when the prefab's ContentSizeFitter is enabled.
     
    Last edited: Mar 3, 2019
  39. pistoleta

    pistoleta

    Joined:
    Sep 14, 2017
    Posts:
    539
    I cant find what I should use, I want to create a vertical grid but I need to manage to insert some kind of separators, like families, how would you do this?

    PS:This great asset should have an own website/forum where the users can share ideas and examples. The documentation is a little bit complex and not easy for begginer-medium users.
    Can't believe the only way to interact is the unity forum u_u .
     
  40. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I'll see if that'll fit in the schedule for the next version. Can you share some of the advantages this would bring?
    Thanks

    Glad you found a solution!
    The HasPendingVisualSizeChange optimization is definitely the recommended way of doing this, especially if you experience frame drops. It requires you to think of all the cases your item's model changes in a way that also changes the visual size of your item and I know it's a time consumer, but we needed to provide the most efficient approach to the users and it's their choice whether to use the full version or a stripped-down one, depending on the needs.
    If you're building for mobile, can you check your framerates using the simpler method and report them here?
    Thanks

    I was thinking about creating a channel on discord. Should be more useful than a forum, yes. Thanks for reminding me of this. Should be done in a few days and I'll include links to it everywhere the asset is advertised.

    About the separators in a grid, you can achieve that using an enclosing List OSA and the "item" (from OSA's perspective) will consist of a simple GridLayout having multiple children. That way, you can style it however you want. This will work wonderfully if you have a lot of groups (as you called them "families") and relatively few children in those groups.
    If, however, you have just a few groups (5-10) with a lot of items in them, you'd need to do the exact opposite: optimize the scrolling of the children and wrap them inside a regular ScrollRect. Here's a quick video I made showing this (if you have a variable number of groups, make the group into a prefab - which is a good idea anyway - and instantiate it programmatically).

    --Lucian
     
  41. doctorpangloss

    doctorpangloss

    Joined:
    Feb 20, 2013
    Posts:
    270
    For users who get a callstack size exceeded "RangeError" exception when building with High stripping for WebGL, you should make sure to include the following in your link.xml:

    Code (CSharp):
    1. <linker>
    2.     <assembly fullname="Assembly-CSharp">
    3.         <type fullname="Com.TheFallenGames.OSA.Core.*" preserve="all"/>
    4.         <type fullname="Com.TheFallenGames.OSA.CustomParams.*" preserve="all"/>
    5.     </assembly>
    6. </linker>
    This was also another problem solved. I use assembly definitions, so my link.xml differs slightly from this one, and assembly definitions themselves may be responsible for causing the stripping issue.
     
  42. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    - Having different assemblies is a way to ensure that the modules in a project aren't using any circular references.
    - For assets, the main advantage is to ensure that there's no conflicts, if each asset is using its own assembly.
    - Also, it seems to be the future toward which Unity is going, for example TextMeshPro now has its own assembly.
    - Most importantly, if a project is using assemblies, then every asset must be in an assembly or else the project's assemblies will not be able to access them.

    The main problem is that you need 2 assemblies, a runtime one and an editor one, which is a real pain for a full project, but the pain is limited for an asset.


    Anyway, I will put OSA in its own assembly because my project is using assemblies, which force me to put every asset I have in its own assembly.
    I am quite used to put an asset in an assembly as currently there's not a lot of assets doing that.
     
  43. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Thank you. I'll include this in the manual's FAQ section and will also read more about the exact causes for this.

    Thanks! This is very useful. I'll look more into it and it'll surely be included

    --Lucian
     
  44. themalakarraja

    themalakarraja

    Joined:
    Nov 26, 2017
    Posts:
    3
    I'm trying to display images using IncrementalItemFetchExample. I changed MyItemViewsHolder and ExampleItemModel according to my data and changed below code in IncrementalItemFetchExample.

    Code (CSharp):
    1. protected override void UpdateViewsHolder(MyItemViewsHolder newOrRecycled)
    2. {
    3.     ExampleItemModel model = Data[newOrRecycled.ItemIndex];
    4.    
    5.     newOrRecycled.id.text = model.id;
    6.     newOrRecycled.ss_id.text = model.ss_id;
    7.     newOrRecycled.user_id.text = model.user_id;
    8.     newOrRecycled.sso_title.text = model.sso_title;
    9.  
    10.     var imageURLAtRequest = "http://www.sooperpop.com/images/" + model.ss_id + "/default.jpg";
    11.     newOrRecycled.iconRemoteImageBehaviour.Load(imageURLAtRequest, true, (fromCache, success) =>
    12.     {
    13.         //if (!IsRequestStillValid(viewsHolder.ItemIndex, itemIndexAtRequest, imageURLAtRequest))
    14.         //    return;
    15.     });
    16. }
    17.  
    18. IEnumerator FetchItemModelsFromServer(int count, Action<ExampleItemModel[]> onDone)
    19. {
    20.     _Params.statusText.text = "Fetching "+ count + " from server...";
    21.     Debug.Log("offset " + offset);
    22.     WWWForm form = new WWWForm();
    23.     form.AddField("offset", offset);
    24.     form.AddField("limit", _Params.preFetchedItemsCount);
    25.     UnityWebRequest www = UnityWebRequest.Post(URL, form);
    26.     yield return www.SendWebRequest();
    27.  
    28.     JSONNode jsonData = JSONNode.Parse(www.downloadHandler.text);
    29.     var results = new ExampleItemModel[count];
    30.  
    31.     for (int i = 0; i < count; ++i)
    32.     {
    33.         results[i] = new ExampleItemModel();
    34.  
    35.         results[i].id = jsonData[i]["id"].Value;
    36.         results[i].ss_id = jsonData[i]["ss_id"].Value;
    37.         results[i].user_id = jsonData[i]["user_id"].Value;
    38.         results[i].sso_title = jsonData[i]["sso_title"].Value;
    39.     }
    40.     onDone(results);
    41. }
    It works fine. But It downloaded the images every time. I think images are not loading from the cache when I scroll up, because every time it calls network. Am I doing anything wrong?
     
  45. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hi,

    The RemoteImageBehaviour is just a very basic script that was initially intended for use in demos.
    The loadCachedIfAvailable parameter actually means that it'll skip the download if the same image is already in the Image component. Providing a full caching solution was a bit outside of the scope at the moment the script was created, and each project has very specific caching requirements and limits, which would mean we'll need a separate script (or interface to be implemented directly by your OSA script, for simplicity) "ImagePool" that holds all of the cache's configuration (most importantly, the max number of images it should cache, and the caching method - LRU/FIFO). I agree this would be very useful to have and I'll add it in the next version.
    If you want the pooling script sooner, email me with invoice number, unity version and OSA version,

    -- Lucian
     
  46. Player7

    Player7

    Joined:
    Oct 21, 2015
    Posts:
    1,533
    Will the update have some more example demo's that would be good.
     
  47. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    For sure: HierarchyWithStickyHeaders

    Not sure:
    1. ExpandItemToOwnContentSize (currently, when you expand an item's size, you need to specify a size, but with this it'll work as a content size fitter by inferring the item's full expanded size so you won't need to calculate it yourself)

    2. FlexibleGridExample




    3. Animated removal of items: Animate the item's size changing towards 0, then remove it.


    --Lucian
     
  48. kiaxseventh

    kiaxseventh

    Joined:
    Jan 24, 2015
    Posts:
    6
    hello
    can you one example code on GridView with infinity object pulling ?

    thank you lot of
     
  49. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    I don't really know what "infinity object pulling" means. The plugin already supports 2B items (2,147,483,645, more exactly, the maximum value of an integer).
    If you mean "incrementally fetch items indefinitely", then we have an example scene for that called "IncrementalItemFetching", where items will be added into the list almost indefinitely, until the memory fills up (and that takes a looong time).
    It does so by adding items only when you scroll near the end. And it does it without a limit in theory.

    But there's no infinity in computers, only the illusion of that. And while the aforementioned example is in theory finite, you can still go "infinite" by removing items from the opposite direction in which you are scrolling to make room for the new ones that appear, which is just a line of code.

    Still, I'm curious what people usually think about when they say "infinite scrolling" or "infinite object pooling", since in theory this is impossible. Can you fully describe the behavior of such a Scroll View?

    Thanks,

    Lucian

    Edit: Do you mean looping spinners, as you see in date-time pickers? If yes, that's already implemented
     
    Last edited: Mar 18, 2019
  50. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi there! I am trying to implement Grid, but can i use it with BaseParamsWithPrefabAndData (for access to Data), or just with GridParams?