Introduction
TypeSafe is a tool for reflecting changes made in the Unity Editor to your code. By scanning the project and generating static classes from your Resources, Layers, Tags and Scenes you no longer have to rely on hard-coded ‘naked-strings’ littered around your codebase. This has a number of benefits that improve the robustness of your code:
- Any changes made in the Editor that might introduce bugs in your code will cause compiler errors. (e.g. renaming a resource, which would break a hard-coded path at runtime, shows up as a compiler error immediately)
- Misspelling layers, scenes, etc is no longer possible.
- It is possible to iterate over the resources in your project. More on that later.
Resources
In Unity, a Resource is any asset that is contained within a folder called Resources. Usually to load such a resource you would pass a path into Resources.Load
, for example:
Traditional Method
This has a number of drawbacks.
- If the path to the resource changes, or is typed incorrectly, this code will fail at runtime.
- The type of the resource may change in the future and cause this code to fail at runtime.
TypeSafe is designed to relocate this kind of error to compile-time instead of runtime, ensuring that you find out about bugs as soon as they are introduced.
TypeSafe Method
Resource Wrapper
Each resource in your project is wrapped in a simple Resource
object. This object contains the path to the resource, a cached copy of the resource once it has been loaded, and helper methods for asynchronous loading. Usually you won’t need any of this, as the object implicitly converts to the type of the resource when assigned to a field.
For example, a Material resource will be wrapped in a Resource<Material>
object. When this wrapper object is assigned to a renderer.sharedMaterial
field it will automatically load the resource and assign the result to the field.
The resource wrapper object automatically loads and caches the resource when assigned to a field. After the object is first assigned, any subsequent uses will return the cached object instead of loading it again. You don’t need to cache a copy in your scripts like you do with the traditional Resources.Load
method.
Class Overview
Folders
The static class for resources generated by TypeSafe (named SRResources
by default, but this can be customized) has nested classes for each folder. For example, a resource located at Assets/Resources/Materials/Player/PlayerAlt can be accessed by SRResources.Materials.Player.PlayerAlt
.
Each folder has methods for getting a list of the contents, contents of a certain type, and matching recursive variants (which include resources in sub-folders).
Class Overview
Prefabs
TypeSafe handles prefabs as a special case. Instead of being wrapped in the generic Resource
object, they are wrapped in a specialized PrefabResource
object. This provides some syntax sugar for instantiating to save you some time.
Class Overview
Unloading Resources
Resource folders have an UnloadAll and an UnloadAllRecursive method, that calls Unload() on each resource wrapper in the folder or folder & sub-folders respectively.
The resource wrapper holds the cached reference to the resource as a Weak Reference, which means that it does not prevent Unity’s Resources.UnloadUnusedResources()
method from working correctly on TypeSafe-loaded resources.
Layers and Tags
Both Layers and Sorting Layers in TypeSafe are wrapped in a struct which contains the layer ID and name. The Layer wrapper also contains the bitmask for the layer.
Layers
TypeSafe makes it simple to access and use layers. By using the SRLayers
class to reference layers you can be certain that if your layer configuration changes later you’ll discover any problems immediately. The Layer object implicitly converts to an integer, so can be assigned to a gameObject’s layer field automatically.
Class Overview
The SRLayers
object also has an All
property that returns a list of Layer
objects containing all the layers in your project.
SortingLayers
By default, SortingLayers are accessed through the SRSortingLayers
class. The SortingLayer object implicitly converts to an integer or string, so can be assigned to a renderer’s sortingLayerID/sortingLayerName fields automatically.
Class Overview
The SRSortingLayers
object also has an All
property that returns a list of SortingLayer
objects containing all the sorting layers in your project.
Tags
Tags are accessed via the SRTags
class (by default. See Customize). It contains string properties generated from your tags, and an All
property that returns a list of strings with all your tags.
Scenes
Scenes are accessed via the SRScenes
class (by default. See Customize). This class has properties that return a Scene
wrapper object, that exposes the scene name and index. You can pass this object directly to Application.LoadScene(...)
or use one of the helper methods. Only Scenes listed in the Build Settings window will be included.
Class Overview
Input
Your Input axes (as defined in the Unity Input Manager) are accessed via the SRInput
class (by default. See Customize). This class has properties that return an InputAxis
wrapper object, that exposes the input name and state.
Class Overview
Audio Mixers
In the settings window, drag Audio Mixer assets to the Audio Mixers section on the Assets tab.
The parameters and snapshots of mixers in this list will be available in the SRAudioMixers
class (by default).
Parameters will be under SRAudioMixers.__MixerName__.Parameters
, and snapshots will be under SRAudioMixers.__MixerName__.Snapshots
Animators
In the settings window, drag Animator Controller assets to the Animator section on the Assets tab.
The parameters and layers of animators in this list will be available in the SRAnimators
class (by default).
Parameters
Parameters will be under SRAnimators.__AnimatorName__.Parameters
.
Parameters are integers, and correspond to the equavelent call to Animator.StringToHash.
Layers
Layers are under SRAnimators.__AnimatorName__.Layers
. Layers are stored as an integer, and correspond to the equavelent call to Animator.GetLayerIndex.
Customize
Automatic Rebuild
TypeSafe automatically regenerates the classes whenever it detects a change in your project that might impact your code. This can be disabled if you want to only trigger scans manually. If no changes are detected it won’t trigger a script refresh.
The Automatic Rebuild Delay setting forces a minimum build time. This is helpful when you are rearranging resources as triggering a script refresh when you are busy rearranging can be frustrating. If another change is detected during a scan the scan is cancelled and re-queued.
Naming Scheme
The class naming scheme can be adjusted from the Settings panel. The namespace, prefix and suffix of the class can be changed.
- The namespace is the C# namespace that the generated classes will be contained in. By default this is empty and the classes are in the root namespace.
- The prefix is a string of characters added to the beginning of the class name (e.g., the default prefix SR is added to Resources to make SRResources.
- The suffix is the same as above, but added to the end of the class name.
The settings panel will provide a preview of your changes and ensure that the generated names don’t conflict with any built-in Unity classes or your own code.
Limitations
Conflicting Resources
Two resources cannot share the same path and name. If TypeSafe detects this, one will be discarded and an error printed to the console (clicking on this error will take you to the conflicting resources).
Nested Folder Names
Nested folders cannot have the same name as the parent folder. If TypeSafe detects this it will prefix the offending name with an underscore to prevent compiler errors and print a warning to the console. The same applies to resource names.
Unicode
TypeSafe allows most Unicode letter characters in resource names, except for non-ASCII numbers. This is due to a bug with Unity’s Mono compiler that incorrectly handles these characters.
Reserved Names
TypeSafe reserves a number of names that are used internally, or are part of the API. These names cannot be used for resources, layers or any other item. TypeSafe will ignore any resources with these names. A truncated list of these names is below:
- All
- None
- GetContents
- GetContentsRecursive
API
TypeSafeApi
The TypeSafeApi static class has a few exposed methods for invoking the TypeSafe compile process via script.
Member Name | Description |
---|---|
QueueRefresh() | Queue a new scan/compile process. Will do nothing and print a warning if IsBusy is true. |
Cancel() | If a TypeSafe scan is currently in progress, cancel it. |
IsBusy | True if TypeSafe is currently scanning or compiling. |
FormatTypeName(string) | Format a string with the class prefix/suffix specified in the Settings window. Useful when using Custom Data Sources |
TypeSafeApi is located in the TypeSafe.Editor namespace.
Custom Data Source
TypeSafe supports integrating into your own code to provide custom generated classes based on your own data. This is accomplished by creating a class that implements the ITypeSafeDataSource
interface.
This interface has a single method, GetTypeSafeDataUnit()
, that returns a TypeSafeDataUnit
object. Any class that implements this interface will be automatically instantated and this method invoked during the TypeSafe scan process.
TypeSafeDataUnit
Property Name | Description |
---|---|
ClassName | The name of the generated class. You can use TypeSafeApi.FormatTypeName(…) to conform to the naming scheme specified in the Settings window. |
FileName | Used to create the generated file name ({0}.Generated.cs, placed in the TypeSafe usr folder). Uses ClassName by default. |
DataType | System.Type used for each data entry in the generated class. See type limitations. |
Data | Collection of TypeSafeDataEntry objects used to populate the generated class. |
NestedUnits | Collection of TypeSafeDataUnit objects. These will be added as nested classes inside this data unit. |
EnableAllProperty | Setting to true will enable the generation of a property named ‘All’ that returns a list of all entries in the data set. |
TypeSafeDataEntry
Property Name | Description |
---|---|
PropertyName | String used as property name of the generated entry. Will be filtered through reserved name and C# compliance checks. |
Parameters | The array of parameters passed to the data type constructor. If the data type is a primitive or string, this should have one entry of the exact same type. |
Type Limitations
The type of generated fields must fulfil one of the following criteria:
- Is a primitive (int, uint, byte, etc)
- Is a string
- Is a class with a public constuctor, with parameters matching the objects passed into the
TypeSafeDataEntry.Parameters
array.
FAQ
What’s up with global::?
In all code generated by TypeSafe classes are referenced by their absolute path, starting with global::
. This makes the code a bit bulky, but it ensures that there are no naming conflicts with existing code in your project. Users finding that a class in their project is interfering with code in an asset is a problem we’ve had reported in the past, so we took steps to ensure that this didn’t happen with TypeSafe. This is the same reason why we use a .NET assembly for all non-generated code.
Contact
If you have a problem which isn’t answered by this documentation or the FAQ, or you find an error in these docs, feel free to open an issue on the GitHub repository or post in the Unity Forum Thread.