/// <summary>Promotes the specified object into a physical connector object.</summary> /// <remarks> /// The physics will immediately start on the object. If it doesn't have a rigidbody, the one will /// be created. /// </remarks> /// <param name="ownerModule">The part's module which will control the connector.</param> /// <param name="obj">The object to be promoted.</param> /// <param name = "interactionDistance"></param> public static KASInternalPhysicalConnector Promote( PartModule ownerModule, GameObject obj, float interactionDistance = 0) { var connectorRb = obj.GetComponent <Rigidbody>() ?? obj.AddComponent <Rigidbody>(); connectorRb.useGravity = false; connectorRb.isKinematic = ownerModule.part.packed; connectorRb.velocity = ownerModule.part.rb.velocity; connectorRb.angularVelocity = ownerModule.part.rb.angularVelocity; connectorRb.ResetInertiaTensor(); connectorRb.ResetCenterOfMass(); var connectorModule = obj.AddComponent <KASInternalPhysicalConnector>(); connectorModule.ownerModule = ownerModule; // Create the interaction collider if requested. if (interactionDistance > 0) { // This mesh is placed on a special layer which is not rendered in the game. It's only // used to detect the special zones triggers, so keep it simple. var interactionTriggerObj = Meshes2.CreatePrimitive( PrimitiveType.Cube, Vector3.one, null, obj.transform); interactionTriggerObj.name = InteractionAreaCollider; var collider = interactionTriggerObj.AddComponent <SphereCollider>(); collider.isTrigger = true; collider.radius = interactionDistance; interactionTriggerObj.layer = (int)KspLayer.TriggerCollider; connectorModule._interactionTriggerObj = interactionTriggerObj; } return(connectorModule); }
/// <summary> /// Creates a mesh that represents the connecting pipe between the source and the target. /// </summary> /// <remarks>It's only called in the started state.</remarks> /// <seealso cref="CreatePipeMesh"/> /// <seealso cref="pipeTransform"/> protected virtual void CreateLinkPipe() { var colliderType = pipeColliderIsPhysical ? Colliders.PrimitiveCollider.Shape : Colliders.PrimitiveCollider.None; pipeTransform = Meshes2.CreateCylinder( pipeDiameter, 1.0f, pipeMaterial, sourceTransform, colliderType: colliderType).transform; pipeTransform.GetComponent <Renderer>().sharedMaterial = pipeMaterial; pipeMeshRenderer = pipeTransform.GetComponent <Renderer>(); // To speedup OnUpdate() handling. SetupPipe(sourceJointNode.pipeAttach, targetJointNode.pipeAttach); var extraScale = 1.0f; if (pipeTextureRescaleMode == PipeTextureRescaleMode.Stretch) { extraScale /= (sourceJointNode.pipeAttach.position - targetJointNode.pipeAttach.position).magnitude; } RescalePipeTexture( pipeTransform, pipeTransform.localScale.z * extraScale, renderer: pipeMeshRenderer); }
/// <summary>Builds a model for the joint end basing on the configuration.</summary> /// <param name="node">The node to setup.</param> /// <param name="alignTo"> /// The object to align the connector to. If it's <c>null</c>, then the model will be parked. /// </param> // ReSharper disable once VirtualMemberNeverOverridden.Global protected virtual void UpdateJointNode(ModelPipeEndNode node, Transform alignTo) { var config = node.config; var makeProceduralModels = alignTo != null || !config.parkPrefabOnly; // Return the models back to the owner part to make the search working properly. if (node.rootModel != null) { node.AlignToTransform(null); } node.parkRootObject = partModelTransform; // Create basic setup. var nodeName = modelBasename + "-pipeNode" + node.name; node.rootModel = partModelTransform.Find(nodeName) ?? new GameObject(nodeName).transform; node.rootModel.parent = partModelTransform; node.partAttach = node.rootModel.Find(PartJointTransformName) ?? new GameObject(PartJointTransformName).transform; Hierarchy.MoveToParent(node.partAttach, node.rootModel, newPosition: Vector3.zero, newRotation: Quaternion.LookRotation(Vector3.back)); node.pipeAttach = node.rootModel.Find(PipeJointTransformName) ?? new GameObject(PipeJointTransformName).transform; Hierarchy.MoveToParent(node.pipeAttach, node.rootModel, newPosition: Vector3.zero, newRotation: Quaternion.LookRotation(Vector3.forward)); // Add a pipe attachment sphere if set. const string sphereName = "pipeSphere"; var sphere = node.pipeAttach.Find(sphereName); if (config.sphereDiameter > float.Epsilon && makeProceduralModels) { if (sphere == null) { sphere = Meshes2.CreateSphere(config.sphereDiameter, pipeMaterial, node.pipeAttach, colliderType: Colliders.PrimitiveCollider.Shape).transform; sphere.name = sphereName; sphere.rotation = Quaternion.LookRotation(node.partAttach.up, node.partAttach.forward); } sphere.GetComponent <Renderer>().sharedMaterial = pipeMaterial; // For performance. RescalePipeTexture(sphere, sphere.localScale.z * config.sphereDiameter * 2.0f); } else if (sphere != null) { Hierarchy2.SafeDestroy(sphere); } // Parking position, if defined. var parkObjectName = modelBasename + "-park" + node.name; var parkAttach = partModelTransform.Find(parkObjectName); if (config.parkAttachAt != null) { node.parkAttach = parkAttach ? parkAttach : new GameObject(parkObjectName).transform; Hierarchy.MoveToParent(node.parkAttach, partModelTransform, newPosition: config.parkAttachAt.pos, newRotation: config.parkAttachAt.rot); } else if (parkAttach != null) { Hierarchy2.SafeDestroy(parkAttach); } // Place prefab between the part and the pipe if specified. if (!string.IsNullOrEmpty(config.modelPath)) { // The prefab model can move to the part's model, so make a unique name for it. const string prefabName = "prefabConnector"; var prefabModel = node.rootModel.Find(prefabName) ?? Hierarchy.FindTransformByPath(partModelTransform, config.modelPath); if (prefabModel != null) { prefabModel.gameObject.SetActive(true); prefabModel.name = prefabName; prefabModel.parent = node.rootModel; prefabModel.rotation = node.partAttach.rotation * config.modelPartAttachAt.rot.Inverse(); prefabModel.position = node.partAttach.TransformPoint(config.modelPartAttachAt.pos); node.pipeAttach.rotation = prefabModel.rotation * config.modelPipeAttachAt.rot; node.pipeAttach.position = prefabModel.TransformPoint(config.modelPipeAttachAt.pos); } else { HostedDebugLog.Error(this, "Cannot find prefab model: {0}", config.modelPath); } } // The offset is intended for the sphere/arm models only. if (makeProceduralModels) { node.pipeAttach.localPosition += new Vector3(0, 0, config.sphereOffset); } // Add arm pipe. const string armName = "sphereArm"; var arm = node.pipeAttach.Find(armName); if (config.armDiameter > float.Epsilon && config.sphereOffset > float.Epsilon && makeProceduralModels) { if (arm == null) { arm = Meshes2.CreateCylinder(config.armDiameter, config.sphereOffset, pipeMaterial, node.pipeAttach, colliderType: Colliders.PrimitiveCollider.Shape).transform; arm.name = armName; } arm.GetComponent <Renderer>().sharedMaterial = pipeMaterial; // For performance. var armTransform = arm.transform; armTransform.localPosition = new Vector3(0, 0, -config.sphereOffset / 2); armTransform.localRotation = Quaternion.LookRotation(Vector3.forward); RescalePipeTexture(armTransform, arm.localScale.z * config.sphereOffset); } else if (arm != null) { Hierarchy2.SafeDestroy(arm); } // Adjust to the new target. node.AlignToTransform(alignTo); }