ThingRenderer¶
Vanilla game class at Assets.Scripts.Objects.ThingRenderer. Wrapper around a Unity Renderer that unifies material access across two drawing paths (MeshRenderer and DrawData, used for batched / instanced geometry). Each Thing carries a List<ThingRenderer> in its Renderers field; mod code that needs to read or mutate the rendered material for a Thing goes through this wrapper.
Containing collection¶
Thing.Renderers at decompile Thing.cs line 534:
Populated during Thing initialization from the prefab's renderers. May be empty for Things with no visible geometry. Mod code must guard with thing.Renderers.Count > 0 before indexing.
Material accessors¶
ThingRenderer exposes three accessors for the underlying material(s):
| Accessor | Line | Returns | Notes |
|---|---|---|---|
Materials { get; } |
ThingRenderer.cs:130 | Material[] |
Clones on the DrawData path, returns the shared-material array on the MeshRenderer path. Use when the intent is "read or mutate per-instance". |
sharedMaterials { get; } |
ThingRenderer.cs:160 | Material[] |
Always returns the shared-material array (or a clone on DrawData). Can be null if the parent was destroyed. |
GetMaterial() |
ThingRenderer.cs:247 | Material |
Convenience getter returning _drawData.materials[0]. DrawData-specific. |
Callers that want to read the current emission color or shader name use Materials[0] (or sharedMaterials[0]) and then call Unity's Material.GetColor("_EmissionColor"), .shader.name, .IsKeywordEnabled("_EMISSION"), etc.
The underlying UnityRenderer field (ThingRenderer.cs:17) is private; the three accessors above are the supported entry points.
Null safety¶
Materials and sharedMaterials can return null on the DrawData path if the parent Thing has been destroyed (ThingRenderer.cs:168-169). Guard:
if (thing.Renderers.Count > 0)
{
var mat = thing.Renderers[0].Materials[0];
if (mat != null) { ... }
}
Related vanilla usage¶
Thing.SetCustomColor(int index, bool emissive = false) iterates every entry in Renderers and hands it _EmissionColor, DiffuseIndexPropertyID and SmoothnessIndexPropertyID values (327946-327951). At 0.2.6428.27798 those calls only fill two private dictionaries on the ThingRenderer: SetShaderVectorProperty (329422-329425) stores into _vectorProperties (329192), which nothing reads, and SetShaderFloatProperty (329417-329420) stores into _floatProperties (329190), which is exposed as FloatProperties (329282) and read back only by BatchInstance, for _DiffuseIndex (45118-45125). FlushShaderProperties (329427-329429) is empty and never called. None of the three values is written to a material here; the visible change comes from the CustomColorMapping material swap. See ./ColorSwatch.md "Normal vs Emissive selection" for the full body. Mods that need to inspect the shader state post-SetCustomColor read the renderer list and the material on each entry.
Verification history¶
- 2026-04-21: page created. Findings from decompile of Assembly-CSharp.dll (ThingRenderer.cs line 17 for the private UnityRenderer field, line 130 for
Materials, line 160 forsharedMaterials, line 247 forGetMaterial; Thing.cs line 534 forThing.Renderers). - 2026-09-21 (game version 0.2.6428.27798): conflict on "whether
SetCustomColor's per-renderer writes reach the shader". Previous claim, in "Related vanilla usage": "Thing.SetCustomColor(int index, bool emissive = false)iterates every entry inRenderersto write the_EmissionColor/DiffuseIndexPropertyID/SmoothnessIndexPropertyIDshader properties." New finding: the three calls only fill two private dictionaries on theThingRenderer; the vector one is never read,FlushShaderPropertiesis empty and uncalled, and onlyBatchInstancereads_DiffuseIndexback. Fresh validator verdict: B is correct, quotingSetShaderVectorPropertyand the emptyFlushShaderProperties(329422-329429), checked against the IL of the installed DLL; the full verdict is on ColorSwatch. Result: the section rewritten and restamped 0.2.6428.27798 @ 2026-09-21; the other sections keep their 0.2.6228.27061 stamps. Bumped frontmatter verified_in / verified_at and added a sources line.
Open questions¶
- What triggers
ThingRendererto select theDrawDatapath over a plainMeshRenderer? Suspected:structureRenderMode != Standard(see./Structure.md), but unconfirmed. - Whether repeated calls to
Materialsin theMeshRendererpath clone the material each time (Unity's.materialaccessor semantics) or whetherThingRenderercaches the clone internally. Needs a runtime probe.