Search Unity

Topics covered
Share

Is this article helpful for you?

Thank you for your feedback!

In order to implement project Carte Blanche’s card system, we developed an extensible framework for creating dynamic, scrollable lists of objects. This article discusses the structure and contents of this framework. The code and sample scenes are available as a Unity Asset Store package and a public repository for the developer community.

Project Carte Blanche (PCB) is Unity Lab's research initiative on VR-in-VR authoring tools for non-technical users. As illustrated in the concept video, its user interface is based on a playing cards metaphor. Central to the design of Carte Blanche is the idea that objects and actions are represented by virtual cards which users grab and place on a virtual table. The user will physically interact with cards by using tracked motion controllers.

PCB’s cards are a more complex version of conventional scrollable lists. List Views are common widgets provided in many GUI toolkits. Unity’s UI system includes several layout components and customizable controls to make dynamic lists which are scrollable and have hover states. A GridLayout or VerticalLayout will get most of the work done, and there’s even a handful of packages on the Asset Store just for lists. However, the existing solutions we found required the lists to be inside of a canvas and exist within the UI system. PCB requires cards to animate in and out of existence and to let the user touch them. Furthermore, performance in VR is critical. One common drawback of "classic" UI list views is that the full list is represented by scene objects which are only masked. If the list structure changes, or perhaps an item is expanded to take up more space, the system must re-evaluate all of the items in the list. We also want to avoid instantiating and destroying scene objects if possible, because this can be costly. Finally, for reusability and consistency of look and feel, we need an extensible solution that allows us to create similar behavior in other UI elements.

We developed a general framework for creating list views, which serves as the foundation of PCB’s card system. Since there are also broad use-cases for such a framework, we decided to release it as an Asset Store package for the community.Scrollable Lists Beyond the Canvas

Decouple Model and View

One goal of this framework is to follow the MVC and MVVM design patterns, decoupling the logic which displays the underlying data (the view) from the state of the data itself (the model). In any given frame, the framework should automatically handle displaying the current state of the list. This way we only need to take into account the current state of the data without worrying about how to trigger updates to the view. Likewise, we don’t need to worry about synchronization issues when user actions are performed quickly or frames are taking too long to render. As an added bonus, the CPU overhead for the list will stay relatively consistent, since it will be doing the same amount of work all the time. Performance will not degrade significantly when the list size increases or states change in unexpected ways.

The idea to represent a list this way is borrowed from the Android and iOS UI frameworks. Both use slightly different implementations, but essentially take the same approach. A container element controls the position of a number of child elements, which are pooled in memory to avoid the cost of allocating and freeing them each time they are scrolled on/off screen. Separately, an interface is defined for how the view gets the information for each list item from the data source. The developer writes code for the data source and designs the list items, and the framework takes care of the rest. Both SDKs provide helpful guides on how to implement a list following the same design pattern of pooling rows and establishing an interface with a data source. iOS calls theirs a UITableView, and Android just calls it a ListView with an associated ListAdapter which talks to the data source.

Both implementations set up a framework for laying out scrollable UI elements, and allow developers to define custom functions for how to define what goes on those elements based on rows in a database, lines in a text file, or any data source they desire. The view needs to know the total number of elements in the data set, as well as a method for getting the information to display such as a movie title and rating. Generally there is a default template for just displaying text, but developers can also customize the design of each list row with custom UI layouts. The framework itself takes care of allocating memory for these list rows, and re-using them after they are scrolled offscreen to display upcoming list rows. If each row is using the same template, the system should never have to allocate more than one full-screen’s worth of rows, plus one additional row if the list can scroll smoothly. That last row exists in order to display the first and last row only part-way on screen. We can think of it as having extra “bleed” space at each end of the list.

The Package

The List View Framework is available as an Asset Store package, as well as an open-source git repository on Unity’s BitBucket account. We hope that this package, and others that we publish in the future, will live on to be used, improved, and re-used by the community. Feel free to fork this repository into your own project with its own fixes or enhancements. The code is released under the MIT/X11 license, which basically means that you can do whatever you want with it as long as you keep the disclaimer. In the future, we at Unity want to release more of our original content as modular open-source packages that can be valuable and improved by the community. This is the first of many modules from Project Carte Blanche which will be released in this way.

The framework boils down to three C# classes: ListViewController (split in two), ListViewItem, and ListViewItemData:

public abstract class ListViewControllerBase : MonoBehaviour
{
        public float scrollOffset;
        public GameObject[] templates;
        ...
}

public abstract class ListViewController<DataType, ItemType> : ListViewControllerBase
        where DataType : ListViewItemData
        where ItemType : ListViewItem
{
        public DataType[] data;
        ...
}

public class ListViewItem<DataType> : ListViewItemBase where DataType : ListViewItemData
{
        public DataType data;

        public virtual void Setup(DataType data)
        {
            this.data = data;
            data.item = this;
        }
}

public class ListViewItemData
{
        public string template;
        public MonoBehaviour item;
}

The reason for splitting ListViewController in two is so that we can access properties which are not dependent on the data type without knowing what implementation of the list we are using. In this way, InputHandler scripts can scroll any kind of list, regardless of what class is actually implementing ListViewController. The framework includes a very basic implementation of ListViewController which accepts items with no data (other than a template) which can be set up in the inspector:

public class ListViewController : ListViewController {}

The simplest possible list (Example 0) uses all of the base classes and just allows the user to scroll a set of objects based on template prefabs and a data array set up in the Inspector. To display meaningful data, users are expected to extend ListViewItemData and ListViewController into classes that describe their particular data, as illustrated in further examples.

Template SetupThe ListViewInputHandler class sets up a base for scrolling or click behavior based on mouse input, for example. And of course, the ListViewScroller sub-class sets up some useful patterns for scrolling behavior. Mouse and Touch input are easy to handle at the same time, but these classes could also conceivably handle gamepad, UI, gestural input or VR devices as well. In the case of PCB , the list views in are manipulated via hand-tracked motion controllers.

Conclusion

We hope that this article helps to explain the framework, examples, and how to get started including list views into your next project. Consult the wiki for further reading and an in-depth description of the Core Classes and Examples. Even if you don’t end up using any of this code directly, the concept of decoupling model and view code is a powerful one, and leads to more efficient code that is easy to maintain as your project grows. Game systems often benefit from stateless designs that make very few assumptions, and constantly re-evaluate as much available information as possible, within reason. Our goal was to come up with a system to create a scrollable list that is highly customizable and performs well given a potentially infinite set of data. We now have a robust toolkit of features to support asynchronous caching, non-uniform template sizes, nested data, and complex animation behavior. As should be clear by the diversity of example code, one size does not fit all, and every implementation will have its own caveats, many of which have not been covered.

The intent with this package is that it becomes property of the community. It was created at Unity, but the source is publicly available on BitBucket, and we encourage users to fork the repo and share their improvements. We can’t wait to see what you come up with!

We'll leave you with some eye candy:

This content is hosted by a third party provider that does not allow video views without acceptance of Targeting Cookies. Please set your cookie preferences for Targeting Cookies to yes if you wish to view videos from these providers.

Cover image: Timoni West, Unity Labs Principal Designer.
Article images: Dennis DeRyke, Unity Graphics Software Development Engineer in Test
Matt Schoen & Dio Gonzalez work at Unity Labs; Schoen is a Senior Software Engineer and Dio is a VR Principal Engineer.

August 5, 2016 in Technology | 8 min. read

Is this article helpful for you?

Thank you for your feedback!

Topics covered