From fc73d7abcd92928718547f8c0256ea8a431f299e Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 3 Oct 2026 15:37:33 +0200 Subject: [PATCH] Ship render: the hull collider carries the invisible material, so clones draw nothing forceRenderingOff is not serialized, so Instantiate and SaveAsPrefabAsset dropped it and every cloned mod ship (and every FBX-built prefab) drew its collision hull. The collider renderer now takes the template's invisible material, as the thruster emitter does. The preview smoke checks the collider on the clone and the prefab smoke checks it on the configured prefab. Co-Authored-By: Claude Opus 5.5 --- Assets/Scripts/Editor/ShipModPreview.cs | 3 +++ Assets/Scripts/Editor/ShipPrefabAuthoring.cs | 3 ++- Assets/Scripts/Editor/ShipPrefabAuthoringSmoke.cs | 2 ++ Assets/Scripts/Gameplay/ShipModShips.cs | 10 ++++++++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Editor/ShipModPreview.cs b/Assets/Scripts/Editor/ShipModPreview.cs index 64f9ea6b7..677253561 100644 --- a/Assets/Scripts/Editor/ShipModPreview.cs +++ b/Assets/Scripts/Editor/ShipModPreview.cs @@ -63,6 +63,9 @@ public static class ShipModPreview bool Inside(Component part) => part != null && part.transform.IsChildOf(ship.transform); Require(ship.HullColliders.Length == 1 && Inside(ship.HullColliders[0]) && ship.HullColliders[0].GetComponent() != null, "one hull collider with a MeshCollider"); + Require(ship.HullColliders[0].GetComponentsInChildren(true).All(renderer => + renderer.sharedMaterials.All(material => material == ship.InvisibleMaterial)), + "a hull collider that draws nothing"); Require(Inside(ship.MapIcon) && Inside(ship.Shield) && Inside(ship.TractorBeam), "map icon, shield and tractor inside the ship"); Require(ship.ThrusterHardpoints.Select(node => node.name).SequenceEqual(plan.Thrusters) && ship.ThrusterHardpoints.All(node => Inside(node) && Inside(node.Emitter) && diff --git a/Assets/Scripts/Editor/ShipPrefabAuthoring.cs b/Assets/Scripts/Editor/ShipPrefabAuthoring.cs index 8a419c6e0..5d5b7352c 100644 --- a/Assets/Scripts/Editor/ShipPrefabAuthoring.cs +++ b/Assets/Scripts/Editor/ShipPrefabAuthoring.cs @@ -160,7 +160,8 @@ public static class ShipPrefabAuthoring mapIcon.gameObject.layer = minimapLayer; hull.gameObject.layer = combatLayer; var hullRenderer = hull.GetComponent(); - if (hullRenderer != null) hullRenderer.forceRenderingOff = true; + // Serialized, so it survives SaveAsPrefabAsset and every clone; forceRenderingOff survives neither. + if (hullRenderer != null) ShipModShips.Hide(hullRenderer, invisible); var collider = AddOrGet(hull.gameObject); collider.sharedMesh = hullMesh; collider.convex = true; diff --git a/Assets/Scripts/Editor/ShipPrefabAuthoringSmoke.cs b/Assets/Scripts/Editor/ShipPrefabAuthoringSmoke.cs index 34f5abf83..9f740f557 100644 --- a/Assets/Scripts/Editor/ShipPrefabAuthoringSmoke.cs +++ b/Assets/Scripts/Editor/ShipPrefabAuthoringSmoke.cs @@ -33,6 +33,8 @@ public static class ShipPrefabAuthoringSmoke Require(ship.RadiatorHardpoints.Length == 1 && ship.RadiatorHardpoints[0].Mesh != null, "radiator mesh"); Require(ship.ArticulationPoints.Length == 1 && ship.ArticulationPoints[0].Group == 2, "articulation"); Require(ship.HullColliders.Length == 1 && ship.HullColliders[0].GetComponent().convex, "hull collision"); + Require(Array.TrueForAll(ship.HullColliders[0].GetComponent().sharedMaterials, + material => material == ship.InvisibleMaterial), "hull collider drawn with the invisible material only"); Require(ship.Shield != null && ship.Shield.GetComponent() != null, "shield envelope"); Require(ship.TractorBeam != null && ship.PingPrefab != null && ship.DestroyEffect != null, "shared assets"); Require(ship.MapIcon != null && ship.MapIcon.gameObject.layer == LayerMask.NameToLayer("Minimap"), "map icon"); diff --git a/Assets/Scripts/Gameplay/ShipModShips.cs b/Assets/Scripts/Gameplay/ShipModShips.cs index 354a1e4a6..7bc64bd37 100644 --- a/Assets/Scripts/Gameplay/ShipModShips.cs +++ b/Assets/Scripts/Gameplay/ShipModShips.cs @@ -103,8 +103,10 @@ public static class ShipModShips if (!hullMesh.isReadable) throw new InvalidOperationException($"{ship.Id}: the hull-collider mesh imported unreadable, so it cannot back a MeshCollider."); hullNode.gameObject.layer = combat; + // The collision hull is never drawn. Its renderer takes the invisible material, which is serialized and so survives + // the prototype's clone; forceRenderingOff would not survive Instantiate. var hullRenderer = hullNode.GetComponent(); - if (hullRenderer != null) hullRenderer.forceRenderingOff = true; + if (hullRenderer != null) Hide(hullRenderer, template.Invisible); var collider = AddOrGet(hullNode.gameObject); collider.sharedMesh = hullMesh; collider.convex = true; @@ -127,7 +129,7 @@ public static class ShipModShips // this renderer's visible material; forceRenderingOff would not survive Instantiate. var hardpoint = AddOrGet(Node(id).gameObject); hardpoint.Emitter = Renderer(id); - hardpoint.Emitter.sharedMaterials = Enumerable.Repeat(template.Invisible, hardpoint.Emitter.sharedMaterials.Length).ToArray(); + Hide(hardpoint.Emitter, template.Invisible); return hardpoint; }).ToArray(); var radiators = plan.Radiators.Select(id => @@ -162,6 +164,10 @@ public static class ShipModShips return instance; } + // Every material slot takes the invisible material, so the renderer draws nothing on the prototype and on every clone. + public static void Hide(Renderer renderer, Material invisible) => + renderer.sharedMaterials = Enumerable.Repeat(invisible, renderer.sharedMaterials.Length).ToArray(); + private static void Reset(Transform transform) { transform.localPosition = Vector3.zero;