The array's only reader was the dead SoundBank block in EntityInstance, which looked up a hardpoint transform and did nothing with it. ShipModPlan.Equipment, ShipModShips' assignment, the preview check, the FBX builder's HP.Equipment case and its smoke assertion all fed that one dead reader. An HP.Equipment node is now refused by the builder as an unknown hardpoint kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.9 KiB
Ship collection authoring
Status: FBX-to-Unity-prefab contract for ships using the current catalog path. Longinus and Djinni predate this contract and keep their current prefabs. The parallel mod-owned .cc path is specified in moddable-ship-authoring.md.
Objective and ownership
One Blender collection exports one FBX model. Its hierarchy names the ship's presentation anchors. Unity builds
Assets/Content/Prefabs/Ships/<Name>.prefab from that model and assigns the components and references that the
runtime currently requires on a hand-built ship prefab. GameData/Aetheria.cc remains the owner of HullData,
hardpoint cells and types, gameplay stats, products, and the prefab's Addressables key. The model never decides a
hit, thruster force, weapon arc, or shield capacity.
Today ShipInstance reads manually filled arrays on each ship prefab. It matches their transform names against
HullData.Hardpoints[].Transform. Its shared ping, invisible material, explosion, shield, and tractor references
also live on each prefab. The builder derives the arrays from the FBX hierarchy and supplies the shared Unity
assets once, from their existing project paths. The old prefabs are left alone.
Blender collection and export
- Name the collection
Ship.<Name>and configure its collection exporter as FBX. Export toAssets/Models/Ships/<Name>.fbx. The filename is the generated prefab name. Blender's collection exporter can export a collection directly and repeat that export after edits. - Export meshes and empties, with animation disabled. Use Blender's
-Z Forward,Y Upaxis conversion for Unity. Keep the collection origin at the ship's simulation centre, with the nose pointing toward the exported +Z axis. Apply object scales on visual and collision meshes; theShieldempty's scale is the deliberate exception. - The importer owns every prefab under
Assets/Content/Prefabs/Ships/whose matching model is inAssets/Models/Ships/. Editing generated components by hand is temporary; the next export rebuilds them. Put appearance in the FBX and gameplay values in the catalog.
The exported hierarchy uses exact, case-sensitive names. Names beginning HP., Pivot., or SHIP. are
reserved. IDs after a role prefix must be unique across the model. Other visual objects may have any names.
<Name> FBX root
Body visible meshes, any nesting
SHIP.MapIcon one mesh; assigned the shared map material and Minimap layer
SHIP.HullCollider one low-poly closed convex mesh; rendered invisible, given a MeshCollider
SHIP.Shield one empty; its position and scale set the shield envelope
SHIP.TractorBeam one empty; its pose sets the tractor effect origin
HP.Thruster.PortAft empty; exact name copied to HullData.Hardpoints[].Transform
Emitter one mesh; thruster particle emission shape
HP.Weapon.PortGun empty; exact name copied to the catalog
Muzzle.0 empty; +Z is firing direction
Muzzle.1 optional additional barrel
HP.Radiator.Port empty; exact name copied to the catalog
RadiatorMesh one mesh; temperature emission surface
Pivot.0.-90.90.-80.80.60 optional empty parent of articulated mounts
HP.Weapon covers ballistic, energy, and launcher mounts; the catalog chooses which. Only thrusters, weapons and
radiators are visible and get a node; internal hardpoints have none. Each role gets its matching component and array.
Muzzle.<nonnegative integer> children are sorted numerically; their transforms become FiringPoint. A weapon
mount requires at least one. A Pivot name carries Group.YawMin.YawMax.PitchMin.PitchMax.Speed as signed
integers in degrees and degrees per second. It gets ArticulationPoint; child mounts follow its transform. Pivots are
presentation only: the simulation's catalog firing arc remains authoritative.
SHIP.Shield receives the existing Shield prefab as a child. That prefab's effective unscaled radius is one
Unity unit; the marker's three scale components are the intended envelope radii in world units. The builder adds
ShieldEnvelope to the Shield prefab root if absent. SHIP.TractorBeam receives the existing Tractor Beam
prefab. The four SHIP.* markers are required exactly once. Ship-specific mesh materials remain authored in
Blender/Unity's material import mapping; the builder only assigns the shared map-icon material.
Build, validation, and failure
Re-exporting an FBX in Assets/Models/Ships/ schedules a prefab rebuild after Unity finishes importing it.
Aetheria/Build Ship Prefab From Selected Model performs the same build on demand. The builder validates all
reserved names, required anchors, component meshes, muzzle numbering, and shared asset references before
replacing a prefab. A bad export reports an error and leaves the last valid prefab in place. The output is a
Prefab Variant of the FBX model, so mesh and transform identities remain tied to the source export.
After adding a ship, author its HullData and product in the catalog, set HullData.Prefab to the generated
prefab's GUID, and set every HardpointData.Transform to an exact HP.* name. Run the catalog asset check and
the Unity compile. A visual play check still owns size, materials, shield fit, barrel orientation, and thrust
appearance. The builder cannot infer the 2D schematic from a 3D mesh.
Cut and build budget
This adds an Editor-only builder, its focused smoke, and this contract. It adds no runtime component, persistent field, package, service, or build target. It replaces per-ship array and shared-reference authoring for newly exported ships. The focused verification target is Unity Editor script compilation plus a synthetic named-hierarchy check; a real Blender collection export is required before claiming end-to-end import fidelity.