Rendering & effects¶
Change how things look at runtime — tint renderers, swap material properties, drive lights and particle systems, and manage 2D sprites. Get the component with gameObject.GetComponent<T>() (or a public field) and drive it.
Visuals are local
Color, light, and particle changes render on each player's client. A script that runs everywhere produces the same look for everyone — good for deterministic effects (a button that glows when pressed). Don't use a one‑client visual change to communicate authoritative state.
Renderer — tint & material¶
Renderer is the mesh's draw component. The quickest tint is SetColor/GetColor:
Renderer r = gameObject.GetComponent<Renderer>();
r.SetColor(Color.red); // PER-OBJECT tint (_Color/_BaseColor) — affects only this object
Color c = r.GetColor();
r.enabled = false; // stop drawing (object still exists)
Material m = r.material; // ⚠ the SHARED material (see the warning below) — NOT a per-object copy
r.material is the SHARED material here — use SetColor for per-object color
Unlike plain Unity, r.material does not clone — it returns the shared material asset (the runtime redirects .material → sharedMaterial to stop per‑access material leaks). So writing r.material.SetColor(...) or r.material.color = … re‑colors every object using that material — recolor one placed copy of a prefab and you recolor them all. For a per‑object color use r.SetColor(color) (a MaterialPropertyBlock writing _Color+_BaseColor, no clone). There is no runtime material clone/instancing API and no per‑object path for named/emissive properties: for a distinct emissive glow per placed copy, assign a distinct material asset to each copy at author time. (_EmissionColor also renders nothing unless the material's emission is enabled at author time — there is no runtime shader‑keyword API.)
MeshFilter — swap the mesh¶
Change an object's mesh at runtime (e.g. a high‑ vs low‑detail model) via its MeshFilter:
public Mesh hiMesh;
public Mesh loMesh;
void UseHighDetail(bool hi)
{
MeshFilter mf = gameObject.GetComponent<MeshFilter>();
if (mf != null) mf.mesh = hi ? hiMesh : loMesh; // or mf.sharedMesh
}
Material — shader properties¶
Drive any material property by name (matching the shader's property names):
Material m = gameObject.GetComponent<Renderer>().material;
m.color = new Color(0.2f, 0.6f, 1f);
m.SetFloat("_Metallic", 0.8f);
m.SetColor("_EmissionColor", Color.cyan * 2f); // glow
float v = m.GetFloat("_Glossiness");
m.mainTexture = otherMat.mainTexture;
color · mainTexture · GetColor/SetColor(name, c) · GetFloat/SetFloat(name, f) · GetInt/SetInt(name, i).
These write the SHARED material
Every Material here is the shared asset (Renderer.material is redirected to sharedMaterial), so m.color / m.SetColor("_EmissionColor", …) changes every object using that material. For a per‑object main color use Renderer.SetColor(color) instead; per‑object emissive/named‑property color isn't possible at runtime — assign a distinct material asset per placed copy at author time.
Light¶
Light lamp = gameObject.GetComponent<Light>();
lamp.enabled = true;
lamp.color = Color.yellow;
lamp.intensity = 2.5f;
lamp.range = 12f; // point/spot falloff distance
lamp.spotAngle = 45f; // spot cone
A pulsing lamp:
protected override void Update()
{
lamp.intensity = 1.5f + Mathf.Sin(Time.time * 3f) * 0.5f; // 1.0..2.0
}
enabled · color · intensity · range · spotAngle · type.
ParticleSystem¶
ParticleSystem fx = gameObject.GetComponent<ParticleSystem>();
fx.Play();
fx.Emit(20); // burst of 20 right now
fx.Stop();
fx.Clear(); // remove live particles
bool done = fx.isStopped && fx.particleCount == 0;
Play · Stop · Pause · Clear · Emit(count) · isPlaying · isStopped · particleCount · time.
Lines & trails — pens, lasers, drawn paths¶
LineRenderer draws a connected series of points. AddPosition is the one‑call pen stroke:
LineRenderer line = strokeObject.GetComponent<LineRenderer>();
line.startWidth = 0.01f;
line.endWidth = 0.01f;
line.startColor = inkColor;
line.useWorldSpace = true;
// Each frame the pen tip moves far enough, append a point:
if (Vector3.Distance(lastPoint, tip.position) > 0.02f)
{
bool added = line.AddPosition(tip.position); // false once the per-line cap is hit
if (!added) StartNewStroke(); // 2048 points per line — start a fresh stroke object
lastPoint = tip.position;
}
Also: positionCount, SetPosition(i, v) / GetPosition(i), loop, enabled. The point cap logs one
console warning when first clamped, so a runaway loop is visible, never silent.
Every line comes out the material's colour, not startColor
startColor / endColor are applied as vertex colours, and the standard Universal Render Pipeline/Unlit
shader never reads them — so every stroke renders in whatever colour the material is, and no ink setting you
change will do anything. The tell is that the lines are uniform, not the wrong shade.
Use Universal Render Pipeline/Particles/Unlit with its Color Mode set to Multiply on the line's
material. That one does read vertex colour, and startColor / endColor start working as written.
TrailRenderer is the no‑bookkeeping alternative — a ribbon that follows its object:
TrailRenderer trail = gameObject.GetComponent<TrailRenderer>();
trail.emitting = isDrawing; // toggle while the pen is pressed
trail.time = 2f; // seconds of history
trail.Clear(); // cut the ribbon instantly
Atmosphere — fog, ambient light, skybox¶
Environment.* drives the world's RenderSettings — day‑night cycles, weather, horror reveals. It's
visual‑only and world‑scoped: run the same call on every client (e.g. from a [ClientRpc], or in
every client's Update) and everyone sees the same sky.
// Rolling fog at dusk:
Environment.SetFogEnabled(true);
Environment.SetFogColor(new Color(0.35f, 0.3f, 0.4f, 1f));
Environment.SetFogDensity(0.015f);
Environment.SetAmbientIntensity(0.4f);
// Skybox: pass a PRE-AUTHORED material from a serialized field, and spin it for a day cycle:
public Material nightSky; // assign in the Inspector
Environment.SetSkybox(nightSky);
Environment.SetSkyboxRotation(sunAngle); // degrees; false if the skybox shader has no _Rotation
Rotation writes go to a runtime clone of the skybox material, so the authored asset is never modified.
Sprites (2D)¶
SpriteRenderer draws a sprite in the world:
SpriteRenderer sr = gameObject.GetComponent<SpriteRenderer>();
sr.color = Color.white;
sr.flipX = true;
sr.sortingOrder = 5; // draw order
sr.enabled = true;
SpriteRegistry is a helper for sprite‑indexed UI Images — card games, slot reels, inventory icons. Register sprites by index once, then swap an Image to any index:
SpriteRegistry.Clear();
SpriteRegistry.LoadFromResources(0, "Cards/ace_spades"); // index -> sprite from Resources
SpriteRegistry.LoadFromResources(1, "Cards/king_hearts");
SpriteRegistry.SetBack(cardImage); // show the "back" sprite (face down)
SpriteRegistry.SetImage(cardImage, 0); // reveal index 0 (ace of spades)
SpriteRegistry.HideImage(cardImage); // transparent, object stays active
SpriteRegistry.ShowImage(cardImage); // opaque again
LoadFromResources(index, path) |
register a sprite from Resources/ at an index |
SetImage(image, index) |
show that sprite on a UI Image |
SetBack(image) · HideImage · ShowImage |
back sprite / transparent / opaque |
Clear() |
drop all registrations (before re‑populating) |
Quick reference¶
| Group | Calls |
|---|---|
| Renderer | SetColor · GetColor · enabled · material · sharedMaterial |
| Material | color · mainTexture · SetColor/Float/Int(name,…) · GetColor/Float/Int(name) |
| Light | enabled · color · intensity · range · spotAngle · type |
| Particles | Play · Stop · Pause · Clear · Emit · isPlaying · particleCount |
| Sprites | SpriteRenderer.color/flipX/flipY/sortingOrder · SpriteRegistry.LoadFromResources/SetImage/SetBack/Hide/Show |
→ Use Math & vectors for the colors here, UI for screen images, and see SlotMachineGame.cs for SpriteRegistry in action.