Jump to content

Autostruts and moving parts


Recommended Posts

Here's what I learned on the way while developing DockRotate. It took me a while to figure it out, so I'm collecting it all in one place, hoping it will be useful to other modders.

Autostruts prevent part relative motion. This can be annoying if you WANT parts to move.

Autostruts can be placed by the user. We could say that if a user places autostruts that cross moving joints it's their fault, and they should be more careful in the future. :sticktongue:

Landing legs are always autostrutted to heaviest part. The user has no way to disable it. So, if there's a moving joint between a landing leg and the heaviest part of a craft, the joint won't be able to move. The user should design the ship in a way that prevents such autostrut. This can be practically impossible.

 

The quick-and-dirty solution: remove all autostruts

The Part class has a ReleaseAutoStruts() method. If you call it on every part of a vessel, all autostruts will be gone, and every moving part will behave as expected. When the movement is done, a call to Vessel.CycleAllAutostrut() will restore them all.

See releaseAllAutoStruts() and secureAllAutoStruts() here for an example.

This works pretty well. But the autostrut free craft can break easier.

 

The official solution: IJointLockState

There actually is a moving part in stock KSP: the Advanced Grabbing Unit (AKA the Klaw) pivot joint can be unlocked to fix center of mass alignment.

So, @SQUAD added a way to prevent autostruts from locking the joint. If a PartModule implements the IJointLockState interface, it will have a IsJointUnlocked() method. This must return true if the part has a moving joint: Vessel.CycleAllAutostrut() method will call it to know where NOT to put autostruts. (Many thanks to @sarbian and @Rudolf Meier for giving me pointers at this).

If you want a part to move, you have to add a PartModule that implements IJointLockState. When it starts moving, you have to make IsJointUnlocked() return true and call Vessel.CycleAllAutoStrut(). When the movement is finished, make IsJointUnlocked() return false and call Vessel.CycleAllAutoStrut() again.

Unfortunately, Vessel.CycleAllAutostrut() appears to have a bug: in a few cases IsJointUnlocked() is not called, and autostruts can lock moving joints anyway. I filed a bug report here: please upvote it if you like writing (and using) mods with moving parts! (and please @SQUAD fix it! We'll love you even more! :kiss:)

 

The workaround: Smart Autostruts

There's a way I found to make things work anyway: this!

It's a hack, really. It iterates on all PartJoint objects in the physics bubble, and destroys joints across moving parts. It's not very efficient, but it works. It has a problem currently: it disables crossing regular struts too. But the user has full control over them, so it's not a big problem.

That's the solution that DockRotate implements by default, until the IJointLockState bug is fixed.

Please feel free to use my Smart Autostruts hack for your moving parts mods. If needed, I can refactor it in a separate file, to make using it easier.

Happy hacking!

Edited by peteletroll
Link to comment
Share on other sites

  • 1 month later...

Hi @peteletroll,

i need your help, please. I try to do a module that aligns the port if it does not fit exactly. I rotate the part, but it does not turn, just jump one.

This may be the solution you have written, but it does not work for me.

                        float axisAngle;
                        Vector3 eulerAngle = new Vector3();

                        axisAngle = baseDockingPort.eulerAngles.x - partDockingPort.eulerAngles.x;
                        eulerAngle.x = part.transform.eulerAngles.x + eulerAxisDifferent(axisAngle);

                        axisAngle = baseDockingPort.eulerAngles.y - partDockingPort.eulerAngles.y;
                        eulerAngle.y = part.transform.eulerAngles.y + eulerAxisDifferent(axisAngle);

                        axisAngle = baseDockingPort.eulerAngles.z - partDockingPort.eulerAngles.z;
                        eulerAngle.z = part.transform.eulerAngles.z + eulerAxisDifferent(axisAngle);

                        releaseAllAutoStruts(part.vessel);

                        part.transform.eulerAngles = eulerAngle;

                        part.vessel.CycleAllAutoStrut();

What could be the problem?

I try connect HPGT to the QUEST.

jb6qIb9.png

Link to comment
Share on other sites

@Trufiadok, you're changing directly a part transform, its position, so the part jumps. You have to move it slowly step by step. It should happen if you disable autostruts too.

I think it's better to move parts by acting on the joints that connect them. Be patient, Unity Configurable joints are a complex beast.

Edited by peteletroll
Link to comment
Share on other sites

Nice!

Here's another work-around - disables all auto-struts on the ship while something is moving, then sets them back to their default value. 

Spoiler


/* 
 * Stock auto-strut from wheels cause issues by not implementing the IJointLockState properly.
 * Work-around this by temporarily disabling all auto-struts when something is moving (here 
 * defined as any animation is running).
 */
[KSPAddon(KSPAddon.Startup.Flight, false)]
public class AutoStrutUpdater: MonoBehaviour
{
    bool wasMoving;

    // Collect info about all the parts in the vessel and their earlier auto strut mode
    class PartInfo
    {
        public Part part;
        public Part.AutoStrutMode autoStrutMode;
    }

    PartInfo[] partInfos;

    private static void printf(string format, params object[] a)
    {
        int i = 0;
        string s = (format is string) ? System.Text.RegularExpressions.Regex.Replace((string)format, "%[sdi%]",
          match => match.Value == "%%" ? "%" : i < a.Length ? (a[i++] != null ? a[i - 1].ToString() : "null") : match.Value) : format.ToString();
        Debug.Log(s);
    }

    // Retrieve the active vessel and its parts
    public static List<Part> GetParts()
    {
        List<Part> parts = null;

        if (FlightGlobals.ActiveVessel)
            parts = FlightGlobals.ActiveVessel.parts;
        else
            parts = EditorLogic.fetch.ship.parts;

        return parts;
    }

    void FixedUpdate()
    {
        bool isMoving = AnyAnimationMoving();

        if (isMoving == wasMoving)
            return;
        wasMoving = isMoving;

        printf(isMoving ? "Started moving" : "Stopped moving");

        List<Part> parts = AnimatedAttachmentUpdater.GetParts();

        if (isMoving)
        {
            partInfos = new PartInfo[parts.Count];

            // If any part is moving, we need to de-strut any wheels
            foreach (Part part in parts)
            {
                // Ignore parts that don't have struting
                if (part.autoStrutMode == Part.AutoStrutMode.Off)
                    continue;

                // Create a record to keep track of the part and the current mode
                PartInfo partInfo = new PartInfo();
                partInfos[parts.IndexOf(part)] = partInfo;

                partInfo.part = part;
                partInfo.autoStrutMode = part.autoStrutMode;

                printf("Changing auto strut of %s from %s to %s",
                    part.name,
                    part.autoStrutMode,
                    Part.AutoStrutMode.Off);

                // Remove the struting
                part.autoStrutMode = Part.AutoStrutMode.Off;
                part.ReleaseAutoStruts();
            }
        }
        else
        {
            // Go through our list of de-strutted parts and put their original strutting back again
            foreach (PartInfo partInfo in partInfos)
            {
                if (partInfo == null)
                    continue;

                printf("Changing auto strut of %s from %s to %s",
                    partInfo.part.name,
                    partInfo.part.autoStrutMode,
                    partInfo.autoStrutMode);

                // Bring struty back
                partInfo.part.autoStrutMode = partInfo.autoStrutMode;
            }
        }
    }

    // Check if any animation is moving
    public static bool AnyAnimationMoving()
    {
        List<Part> parts = GetParts();
        foreach (Part part in parts)
            foreach (PartModule partModule in part.Modules)
                if (partModule.moduleName == "ModuleAnimateGeneric")
                    if (((ModuleAnimateGeneric)partModule).aniState == ModuleAnimateGeneric.animationStates.MOVING)
                        return true;
        return false;
    }
}

 

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...