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;