Give Page Feedback | API

Chunk Streamers - Custom Streamers

Custom Chunk Streamers can be used to add support for new asset types, such as procedurally generated assets, or to provide alternative streaming techniques for existing supported asset types (such as prefabs or scenes).

In the end, the responsibility of the Streamer is to provide valid Asset Chunks (i.e., System.Object intances) for all input World Cells, and to attach those Chunks to the World Cells. How it does this is entirely up to you. You can load Assets from the user's computer, from an online database, or any other source; you can even create the Asset Chunks purely with code!

Creating The Script

In order to create a custom Chunk Streamer, create a new MonoBehaviour script that derives from the ChunkStreamer base abstract class. This abstract class defines several abstract methods which need to be overridden in order to implement the logic required by the Streamable Assets Manager to stream in Asset Chunks.

You should place your class inside of the DeepSpaceLabs.SAM namespace, which will make the ChunkStreamer class available and also help avoid naming conflicts with 3rd party assets or your own code.

--Special Note--
An easy way to start with this class is to hover over your custom class name and choose the option Show potential fixes -> Implement abstract class. This will provide default overrides for all abstract class members.

In addition, we recommend adding the following attribute above your class, which will ensure the custom streamer is shown with other Chunk Streamers in menus:

[AddComponentMenu(GlobalValues.COMPONENT_ROOT_PATH + "Chunk Streamers/Custom Chunk Streamer Name")]

Awake Logic

The base Chunk Streamer class makes use of Awake in order to configure some things that are dependent on global Chunk Streamer settings. As such, if you need to carry out code that would normally go in Awake, you will need to override the AwakeExtended method instead.

IsSingleFrameAttachmentPreloadRequired (Property - Implementation Required)

If your Custom Chunk Streamer is capable of single or double frame loading, the value returned here will determine which is used.

If you override the property to return true, the custom chunk streamer's PerformSingleFrameAttachmentPreload method will be called in one frame, and then LoadAndAttachChunksToCellsInSingleFrame will be called in the next.

If you override the property to return false, only {BoldTagOpen}LoadAndAttachChunksToCellsInSingleFrame{BoldTagClose} will be called.

Note, this property and the methods mentioned are only used when the Component Manager's Initialize method is used. If you are strictly using the InitializeGradually method, you can leave the default implementations as is.

If your custom chunk streamer is incapable of single or double frame loading, the value returned does not matter; you should override the methods mentioned to throw exceptions and avoid using the Initialize method of the Component Manager (which may mean disabling the Initialize On Awake setting on the Manager).

ExtraDataToPrependKey (Property - Implementation Optional)

Override this property if you'd like to prepend extra data taken from each LOD Group to the beginning of the string used to load each Asset Chunk. This data can be found in each LOD's Extra Data store on the Streamable Grid it belongs to, or the Global Extra Data store if the key does not exist in the LOD specific Extra Data store. The string returned should be a valid key in at least one LOD Group's Extra Data store, or a valid key in a Global Extra Data store.

If an LOD Group does not have any data associated with the key (either in the LOD specific Extra Data store or the Streamable Grid's Global Extra Data store), nothing is prepended to the Load String, so there is no issue if only some of the LOD Groups using the Streamer contain valid extra data using the returned Key.

If you do not override this property, null is returned. If you need to manually access Extra Data using the ExtraDataToPrependKey, you should first check whether it is null and only try to access the extra data if it is not null.

ExtraDataToAppendKey (Property - Implementation Optional)

Override this property if you'd like to append extra data taken from each LOD Group to the end of the string used to load each Asset Chunk. This data can be found in each LOD's Extra Data store on the Streamable Grid it belongs to, or the Global Extra Data store if the key does not exist in the LOD specific Extra Data store). The string returned should be a valid key in at least one LOD Group's Extra Data store, or a valid key in a Global Extra Data store.

If an LOD Group does not have any data associated the key (either in the LOD specific Extra Data store or the Streamable Grid's Global Extra Data store), nothing is appended to the Load String, so there is no issue if only some of the LOD Groups using the Streamer contain valid extra data using the returned Key.

If you do not override this property, null is returned. If you need to manually access Extra Data using the ExtraDataToAppendKey, you should first check whether it is null and only try to access the extra data if it is not null.

ConstantDataToAppend (Property - Implementation Optional)

Override this property if you'd like to append an extra literal string to the end of the string used to load each Asset Chunk. The data is added to the very end of the string, after any extra data appended using the ExtraDataToAppendKey.

If you do not override this property, null is returned. If you need to manually access this string, you should first check if it is null and only use it if it is not null.

AwakeExtended (Method - Implementation Optional)

Override this to execute logic that would normally go in an Awake method.

CreateNewUser (Method - Implementation Optional)

A custom user object needs to be created for each LOD Group that will be used with a Chunk Streamer, in order to store LOD Group specific data that is used to carry out the loading/unloading logic.

This method is called automatically whenever an LOD Group registers with the Chunk Streamer. By default, it creates and returns an instance of the default ChunkStreamerUser class, which simply stores the Chunk Streamer that created it, the ILODGroup object (which has all information about the registered LOD Group), and a Cell String object which can be used to generate the Asset Chunk and Asset Chunk Load Key strings needed to load/unload Asset Chunks.

If you want to store additional information or provide per LOD Group methods/properties, you can create a custom class that derives from ChunkStreamerUser, then override the CreateNewUser method to return an instance of this new custom ChunkStreamerUser class.

Note, while this ChunkStreamerUser class does not have a presence in this editor guide or the online API, short Summary and param code comments have been provided for it, so you can explore the class and its members further using Visual Studio or whatever Script Editor you are using.

PerformSingleFrameAttachmentPreload (Method - Implementation Optional)

This method only needs to be overridden if you want to support the Component Manager's Initialize method and you have overridden IsSingleFrameAttachmentPreloadRequired to return true.

The World will call this method in one frame, and then LoadAndAttachChunksToCellsInSingleFrame in the immediate frame following it. With it, you should perform pre-loading or other necessary steps to enable the loading of Asset Chunks when LoadAndAttachChunksToCellsInSingleFrame is called in the following frame.

As an example, when using Non Addressable Scene Loading, scenes take a frame to 'load in', so the Scene Chunk Streamer actually loads scenes using SceneManager.LoadScene using this method, and then finds those loaded scenes and attaches them to the World Cells within LoadAndAttachChunksToCellsInSingleFrame.

If your custom Chunk Streamer is capable of single frame loading, you only need to override the LoadAndAttachChunksToCellsInSingleFrame method (and return false for IsSingleFrameAttachmentPreloadRequired).

For each LOD Group user, the cells passed in are the same cells that will be passed to the LoadAndAttachChunksToCellsInSingleFrame method.

The userID is the ID of the LOD Group user, which was assigned to it when it registered with the Chunk Streamer. You can use it to access the ChunkStreamerUser object associated with the LOD Group, via the RegisteredUsers property, which contains useful information about the LOD Group.

LoadAndAttachChunksToCellsInSingleFrame (Method - Implementation Required)

Provide a valid implementation of this method if you want to support the Component Manager's Initialize method.

It can be used to support single (when IsSingleFrameAttachmentPreloadRequired returns false) or double (when IsSingleFrameAttachmentPreloadRequired returns true) frame loading. The World will call this method either in the frame after PerformSingleFrameAttachmentPreload is called (assuming IsSingleFrameAttachmentPreloadRequired returns true), or by itself. All Chunk Streamers have it called in the same frame. With it, you should complete the process of loading and/or attaching all Asset Chunks to the passed in World Cells.

As an example, when using Non Addressable Scene Loading, scenes take a frame to 'load in', so the Scene Chunk Streamer actually loads scenes using SceneManager.LoadScene within the PerformSingleFrameAttachmentPreload method, and then finds those loaded scenes and attaches them to the World Cells within this method.

If your custom Chunk Streamer is capable of single frame loading, you only need to provide a valid implementation for this method (and return false for IsSingleFrameAttachmentPreloadRequired). You do not need to override PerformSingleFrameAttachmentPreload.

For each LOD Group/user, the cells passed in are the same cells that were passed to the PerformSingleFrameAttachmentPreload method (if used).

The userID is the ID of the LOD Group user, which was assigned to it when it registered with the Chunk Streamer. You can use it to access the ChunkStreamerUser object associated with the LOD Group, via the RegisteredUsers property, which contains useful information about the LOD Group.

LoadAndAttachChunksToCells (Method - Implementation Required)

This is used in situations where single/double frame loading is not required, such as when the Component Manager's InitializeGradually method is called or during normal World Updates that occur as a result of the player moving.

With it, you should load all Asset Chunks for all of the input World Cells, and attach them to the World Cells using the WorldCell.AttachChunkToCell method. Generally you will wish to do this over multiple frames to reduce the per-frame performance impact of the load operations.

The cells passed in are always guaranteed to come from the same LOD Group.

The userID is the ID of that LOD Group user, which was assigned to it when it registered with the Chunk Streamer. You can use it to access the ChunkStreamerUser object associated with the LOD Group, via the RegisteredUsers property, which contains useful information about the LOD Group.

If you wish to use pooling, that should be done via the Chunk Manager, so as to ensure the Chunk Streamer is only responsible for streaming in new Asset Chunks when they are required, and removing Chunks when they are no longer needed.

DetachAndUnloadChunksFromCells (Method - Implementation Required)

With this method, you should detach all Asset Chunks from all of the input World Cells using the WorldCell.DetachChunksFromCell method, and then remove/unload those Asset Chunks from the Application so they are no longer consuming memory. Generally you will wish to do this over multiple frames to reduce the per-frame performance impact of the unload/remove operations.

The cells passed in are always guaranteed to come from the same LOD Group.

The userID is the ID of that LOD Group user, which was assigned to it when it registered with the Chunk Streamer. You can use it to access the ChunkStreamerUser object associated with the LOD Group, via the RegisteredUsers property, which contains useful information about the LOD Group.

If you wish to use pooling, that should be done via the Chunk Manager, so as to ensure the Chunk Streamer is only responsible for streaming in new Asset Chunks when they are required, and removing Chunks when they are no longer needed.

--Special Note--
The return type of the LoadAndAttachChunksToCells and DetachAndUnloadChunksFromCells methods is IEnumerator. This return type is special as it allows the method to be iterated over, possibly over multiple frames. This allows the execution's performance impact to be spread out over multiple frames, which is awesome! There are two strategies for implementing methods with this return type:

1) Simply include yield return statements within the method's body where the returned object is either null or an instance of a YieldInstruction.

The compiler will auto generate a state machine class, which will be used to iterate the method's logic. The drawback of this technique is every time the method is called, a new instance of the auto generated class is created, resulting in garbage generation throughout the lifetime of the game.

2) Implement one of the reusable enumerator classes found within the API.

This strategy is harder to implement as you will need to implement the state machine logic yourself, as well as perform some other code related task necessary to use the reusable enumerators. It is recommended for experienced coders only!

If you elect to go with option 2, you can find detailed information on how to use the Reusable Enumerator classes in the Yield Enumerator Section within the Secondary Non Components Chapter.

Yield Enumerator Classes

Accessing Registered Users/LOD Groups

The registration of new LOD Group users is handled automatically by the base Chunk Streamer class. You can access the ChunkStreamerUser object of any registered LOD Group via the RegisteredUsers property, using a simple Indexer, where the index is the userID passed into each of the loading/unloading methods.

--Code Example--
ChunkStreamerUser user = RegisteredUsers[userID];

Do note that when using this Indexer, the returned object is always of type ChunkStreamerUser, so if you have created a custom class deriving from ChunkStreamerUser and have overridden the CreateNewUser method to return an instance of this custom class, you will need to cast the returned user object to your custom type in order to access any custom data stored on the custom user object.

You can also iterate through all registered LOD Group users in a garbage free way using the RegistrantEntries method, which returns an enumerable struct object (we recommend using a simple foreach statement to perform the iteration).

While these should be the only members of the RegistrationHandler class you need to utilize, you are free to take a look at this class in more detail by viewing the Registration Handler Section within the Secondary Non Components Chapter.

Learn More About The Registration Handler

Tracking Load Progress

The Chunk Streamer class has a LoadProgress property which can only be set by classes deriving from it. This property is currently only used with the Component Manager when it's InitializeGradually method is called, however in the future it may be used in other situations. For the time being, however, if you do not plan on tracking the progress of the Component Manager's InitializeGradually method (to display a progress bar when loading the game world, for instance), you do not need to worry about setting the property.

If you do wish to track the progress of InitializeGradually, you should implement load progress tracking for the LoadAndAttachChunksToCells method.

To start, when this method is called, immediately reset the LoadProgress value to 0f.

As you load in Asset Chunks, increment the LoadProgress value until all Asset Chunks are loaded (set LoadProgress to 1f once this is true).

The progress tracking can be as fine-tuned as you wish, tracking the progress of individual load operations, or simply incrementing the progress by a fixed amount each time an Asset Chunk is loaded and attached to a World Cell.

A simle way of calculating by how much the LoadProgress should be incremented for each Asset Chunk is (at the start of each LoadAndAttachChunksToCells call) to iterate through all input World Cells, adding up the number of Asset Chunks each cell has using its NumChunks property, then dividing 1f by the calculated sum.

The LoadProgress value should only track the progress (from 0f to 1f) of loading/attaching the current World Cells from the current LoadAndAttachChunksToCells method call.

Custom Inspectors

When creating custom Chunk Streamers, you will need to create a custom Editor class so that the Global Chunk Streamer settings can be drawn/utilized properly. To do so:

1) Create a new script file with whatever name you wish (though we recommend CustomStreamerClassNameEditor.cs).

2) Place the class definition inside of the DeepSpaceLabs.EditorSAM namespace.

3) Add using UnityEditor and using DeepSpaceLabs.SAM statements above your class definition.

4) Right above your class definition, add the following line:
[CustomEditor(typeof(CustomStreamerClass))]

5) Add a field of type ChunkStreamerBaseEditor, like so:
ChunkStreamerBaseEditor baseEditor;

6) Add a public override method called OnInspectorGUI:
public override void OnInspectorGUI(){}

7) Within the OnInspectorGUI method, check if baseEditor is null, and if it is, create a new instance of it, passing in serializedObject:
if (baseEditor == null) baseEditor = new ChunkStreamerBaseEditor(serializedObject)

8) Finally, call baseEditor.OnInspectorGUI:
baseEditor.OnInspectorGUI();

This will make the Global Chunk Streamer fields show, however if you need to add additional drawing or perform other stuff, feel free to add to the class's OnInspectorGUI method by drawing additional properties before and/or after the call to baseEditor.OnInspectorGUI().