Ship exhaust: wiring an exhaust to an emitter turns Use Mesh Colors off

The emitter carries the invisible material, whose alpha is 0, so an exhaust
prefab left at Unity's default (Use Mesh Colors on) emitted nothing. The
shipped thruster prefabs only worked because theirs is off. ThrusterHardpoint
.WireExhaust now owns the wiring and turns it off; ShipInstance calls it. The
preview smoke wires a default particle system to each emitter on the clone and
requires every particle emitted and none transparent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-03 15:39:14 +02:00
commit 239f359990
3 changed files with 50 additions and 7 deletions

View file

@ -51,9 +51,9 @@ public static class ShipModPreview
// Instantiate clones the assembled root, and Unity must remap every array reference into the clone.
var clone = UnityEngine.Object.Instantiate(ship.gameObject).GetComponent<ShipInstance>();
try { CheckShip(clone, plan, "clone"); }
try { CheckShip(clone, plan, "clone"); CheckExhaust(clone); }
finally { UnityEngine.Object.DestroyImmediate(clone.gameObject); }
Debug.Log($"SHIP_MOD_ASSEMBLY_SMOKE thrusters={plan.Thrusters.Length} " +
Debug.Log($"SHIP_MOD_ASSEMBLY_SMOKE thrusters={plan.Thrusters.Length} exhaust=ok collider=hidden " +
$"weapons={plan.Weapons.Length} radiators={plan.Radiators.Length}");
}
@ -79,6 +79,42 @@ public static class ShipModPreview
node.FiringPoint.All(Inside)).All(ok => ok), "weapon hardpoints with their muzzles in order");
}
// Exhaust from each hidden emitter, wired as ShipInstance wires it, from a particle system left at Unity's defaults
// (Use Mesh Colors on). The emitter's invisible material has alpha 0, so mesh colours would make every particle
// transparent; the wiring must turn them off.
private static void CheckExhaust(ShipInstance ship)
{
const int count = 200;
foreach (var hardpoint in ship.ThrusterHardpoints)
{
var exhaust = new GameObject("Smoke Exhaust").AddComponent<ParticleSystem>();
try
{
exhaust.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
var main = exhaust.main;
main.playOnAwake = false;
main.startSpeed = 0;
main.startLifetime = 10;
main.maxParticles = count * 2;
var emission = exhaust.emission;
emission.enabled = false;
var shape = exhaust.shape;
shape.enabled = true;
shape.shapeType = ParticleSystemShapeType.MeshRenderer;
if (!shape.useMeshColors) throw new InvalidOperationException("Unity's default Use Mesh Colors is off; the exhaust check proves nothing.");
ThrusterHardpoint.WireExhaust(exhaust, hardpoint);
exhaust.Emit(count);
if (exhaust.particleCount != count)
throw new InvalidOperationException($"clone: exhaust from {hardpoint.name} emitted {exhaust.particleCount} of {count} particles.");
var alive = new ParticleSystem.Particle[count];
exhaust.GetParticles(alive);
if (alive.Any(particle => particle.GetCurrentColor(exhaust).a == 0))
throw new InvalidOperationException($"clone: exhaust from {hardpoint.name} has transparent particles.");
}
finally { UnityEngine.Object.DestroyImmediate(exhaust.gameObject); }
}
}
[MenuItem("Aetheria/Preview Mod Ship Package")]
private static async void Open()
{

View file

@ -65,10 +65,9 @@ public class ShipInstance : EntityInstance
{
var effectData = (ThrusterData) thruster.Data;
var particles = Instantiate(EngineAssets.Load<ParticleSystem>(effectData.ParticlesPrefab), transform, false);
var particlesShape = particles.shape;
var thrusterHardpoint = ThrusterHardpoints
.FirstOrDefault(t => t.name == ship.Hardpoints[thruster.Item.Position.x, thruster.Item.Position.y].Transform);
particlesShape.meshRenderer = thrusterHardpoint?.Emitter;
ThrusterHardpoint.WireExhaust(particles, thrusterHardpoint);
// if (!string.IsNullOrEmpty(thruster.Item.Data.SoundEffectTrigger) && thrusterHardpoint != null)
// {
// AkSoundEngine.RegisterGameObj(thrusterHardpoint.gameObject);

View file

@ -1,8 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine;
public class ThrusterHardpoint : MonoBehaviour
{
public MeshRenderer Emitter;
// Points an exhaust's shape at a hardpoint's emitter, or at nothing when the ship has no such hardpoint. The emitter
// carries the invisible material (alpha 0), so the particles never take their colour from its mesh: with Use Mesh
// Colors on, Unity's default, they would all be born transparent.
public static void WireExhaust(ParticleSystem exhaust, ThrusterHardpoint hardpoint)
{
var shape = exhaust.shape;
shape.useMeshColors = false;
shape.meshRenderer = hardpoint != null ? hardpoint.Emitter : null;
}
}