Пример #1
0
    /// <summary>
    /// Align all transforms position with one Transform, or on mini/max values of boundaries
    /// </summary>
    /// <param name="referenceTransform">
    /// A <see cref="Transform"/> which all other transforms will be aligned with, if alignType is "mean"
    /// </param>
    /// <param name="transformList">
    /// The <see cref="Transform[]"/> of all objects to be aligned
    /// </param>
    /// <param name="axis">
    /// The axis to align on : <see cref="Vector3.one"/> to align all axis, <see cref="Vector3.right"/> to align on the X axis, <see cref="Vector3.up"/> to align on the Y axis, <see cref="Vector3.forward"/> to align on the Z axis
    /// </param>
    /// <param name="alignType">
    /// Witch type of alignement we do, from the <see cref="Landmark"/> enum
    /// </param>
    public static void AlignPosition(Transform referenceTransform, Transform[] transformList, Vector3 axis, Landmark alignType, ExtentsGetter.BoundType boundType)
    {
        //bool useBounds = (alignType != Landmark.distributed && alignType != Landmark.mean);
        // Get the position from the active selected object
        Vector3 markPosition = referenceTransform.position;

        // If alignment is not the mean one, search the min or max positioned object
        if (alignType == Landmark.minimum)
        {
            markPosition = ExtentsGetter.GetMinMarkPosition(referenceTransform.position, transformList, boundType);
        }
        else if (alignType == Landmark.maximum)
        {
            markPosition = ExtentsGetter.GetMaxMarkPosition(referenceTransform.position, transformList, boundType);
        }
        Vector3 activePosition = markPosition;

        foreach (Transform nextTransform in transformList)
        {
            Vector3 newPos;
            Vector3 delta = Vector3.zero;
            if (alignType == Landmark.maximum)
            {
                if (nextTransform.GetComponent <Renderer>())
                {
                    delta = -nextTransform.GetComponent <Renderer>().bounds.extents;
                }
                else if (nextTransform.GetComponent <Collider>())
                {
                    delta = -nextTransform.GetComponent <Collider>().bounds.extents;
                }
            }
            else if (alignType == Landmark.minimum)
            {
                if (nextTransform.GetComponent <Renderer>())
                {
                    delta = nextTransform.GetComponent <Renderer>().bounds.extents;
                }
                else if (nextTransform.GetComponent <Collider>())
                {
                    delta = nextTransform.GetComponent <Collider>().bounds.extents;
                }
            }
            // refers to axisList : None, X, Y, Z, All -> 0, 1, 2, 3, 4
            newPos.x = (axis == Vector3.one || axis == Vector3.right) ? activePosition.x + delta.x : nextTransform.position.x;
            newPos.y = (axis == Vector3.one || axis == Vector3.up) ? activePosition.y + delta.y : nextTransform.position.y;
            newPos.z = (axis == Vector3.one || axis == Vector3.forward) ? activePosition.z + delta.z : nextTransform.position.z;
            nextTransform.position = newPos;
        }
    }
Пример #2
0
    // <summary>
    /// Raycast down from the object bounds so it appears to have fallen on the colliders below (from the down to the top)
    /// Beware : due to approximation in Transform values, it's not 100% accurate
    /// </summary>
    /// <param name="fallingTransform">
    /// The <see cref="Transform"/> of the object to be aligned
    /// </param>
    /// <param name="rotateToTerrainAngle">
    /// <see cref="true"/> to rotate <see cref="Transform"/> to the normal from the raycast hit
    /// </param>
    public static void FallOnTerrain(Transform fallingTransform, bool rotateToTerrainAngle, ExtentsGetter.BoundType boundType)
    {
        Bounds activeObjectBounds = ExtentsGetter.GetHierarchyBounds(fallingTransform.gameObject, boundType);
        // TODO : rewrite so it use a bounding box to get objects inside and then the upper one, then use Collider.ClosestPointOnBounds to get the closest hit point

        // If we apply a rotation to the object when falling, make it fall as if it was upside
        Quaternion wasRotation = fallingTransform.rotation;

        if (rotateToTerrainAngle)
        {
            fallingTransform.rotation = Quaternion.identity;
        }

        float distance     = System.Single.PositiveInfinity;
        bool  hitSomething = false;
        // The radius is calculated from the bound size, taking the greatest value of width and depth
        float sphereRadius = Mathf.Max(activeObjectBounds.extents.x, activeObjectBounds.extents.z);

        // Set the default new position to the current position
        Vector3 newPosition = fallingTransform.position;
        Vector3 normal      = fallingTransform.up;

        // New from 2.1 : the Raycast has been replaced by a SphereCastAll method so every single object in the sphere sweep returns a hit
        // SphereCast down to check all hits
        RaycastHit[] hits;
        // TIPS  : the cast can return the current Transform so we must ensure to not take it into account
        hits = Physics.SphereCastAll(activeObjectBounds.center + Vector3.up * sphereRadius, sphereRadius, Vector3.down);
        if (hits.Length > 0)
        {
            for (int i = 0; i < hits.Length; i++)
            {
                // Somehow, the hits[i].distance is not equal to (raycast.origin - hits[i].point) ?
                float hitDistance = activeObjectBounds.center.y - hits[i].point.y;
                // v2.2 Fix : do not take into account any hit from inside the boundaries (use the y extents)
                if (!hits[i].collider.transform.Equals(fallingTransform) && hitDistance <distance && hitDistance> activeObjectBounds.extents.y)
                {
                    hitSomething = true;
                    distance     = hitDistance;
                    normal       = hits[i].normal;
                }
            }
        }

        if (hitSomething)
        {
            if (distance > 0)
            {
                // Do not move the object if the distance is 0 or below
                if (rotateToTerrainAngle)
                {
                    // Assume a well oriented mesh (Vector3.up means the up side of the mesh
                    Quaternion rotation = Quaternion.FromToRotation(Vector3.up, normal);
                    wasRotation = rotation;
                }

                // Move to new position : nearest hit distance - half size of the falling object
                newPosition.y             = newPosition.y - distance + activeObjectBounds.extents.y;
                fallingTransform.position = newPosition;
            }
            fallingTransform.rotation = wasRotation;
        }
    }
    void OnGUI()
    {
        if (AlignEditor.editorPath == null)
        {
            AlignEditor.editorPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this)));
        }

        title   = "Align Editor v" + AlignEditor.VERSION + " Settings";
        maxSize = new Vector2(450, 200);
        minSize = maxSize;

        if (GUILayout.Button("Click here for web site & Documentation", EditorStyles.boldLabel))
        {
            Application.OpenURL("http://www.equilibregames.com/align+editor");
        }
        GUILayout.FlexibleSpace();

        // Settings
        GUILayout.Label("Global settings", EditorStyles.toolbar, GUILayout.ExpandWidth(true));

        // Prefer to use boundaries / extents
        ExtentsGetter.BoundType newBound = (ExtentsGetter.BoundType)EditorGUILayout.EnumPopup("Extents to use", AlignEditor.PreferredBounds);
        if (newBound != AlignEditor.PreferredBounds)
        {
            Undo.RegisterUndo(this, "extents to use");
            AlignEditor.PreferredBounds = newBound;
        }
        // Prefer to align gameobject to collider angle or not
        bool rotateToTerrainAngle = EditorGUILayout.Toggle("Align to collider angle", AlignEditor.rotateToTerrainAngle);

        if (rotateToTerrainAngle != AlignEditor.rotateToTerrainAngle)
        {
            Undo.RegisterUndo(this, "Align to collider angle setting");
            AlignEditor.rotateToTerrainAngle = rotateToTerrainAngle;
        }

        // Layout
        GUILayout.Label("Editor layout", EditorStyles.toolbar, GUILayout.ExpandWidth(true));

        // Button display : skin and style

        // Icon Set / Skin
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.BeginVertical();
        EditorGUILayout.BeginHorizontal();
        // show immediately one icon to the user
        EditorGUILayout.PrefixLabel("Icon Set");
        AlignEditor.IconSet newSkin = (AlignEditor.IconSet)EditorGUILayout.EnumPopup(AlignEditor.Skin);
        if (newSkin != AlignEditor.Skin)
        {
            Undo.RegisterUndo(this, "change icon set");
            AlignEditor.Skin = newSkin;
        }
        EditorGUILayout.EndHorizontal();

        // Button Display
        EditorGUILayout.BeginHorizontal();
        // show immediately a sample button to the user
        EditorGUILayout.PrefixLabel("Button display");
        ImagePosition newStyle = (ImagePosition)EditorGUILayout.EnumPopup(AlignEditor.ButtonStyle);

        if (newStyle != AlignEditor.ButtonStyle)
        {
            Undo.RegisterUndo(this, "change button style");
            AlignEditor.ButtonStyle = newStyle;
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
        Texture icon = AssetDatabase.LoadAssetAtPath(AlignEditor.editorPath + "/Icons/" + AlignEditor.Skin + "/alignMin.png", typeof(Texture)) as Texture;

        GUIContent myButtonContent = new GUIContent();

        myButtonContent.image   = icon;
        myButtonContent.text    = "Click";
        myButtonContent.tooltip = "This is a sample";

        ImagePosition wasPosition = GUI.skin.button.imagePosition;

        GUI.skin.button.imagePosition = AlignEditor.ButtonStyle;
        GUILayout.Label(myButtonContent, "Button");
        EditorGUILayout.EndHorizontal();

        GUI.skin.button.imagePosition = wasPosition;

        GUILayout.FlexibleSpace();
        if (GUILayout.Button("Close"))
        {
            Close();
        }
    }
Пример #4
0
    // <summary>
    /// Raycast down from the object bounds so it appears to have fallen on the colliders below (from the down to the top)
    /// </summary>
    /// <param name="transformList">
    /// The <see cref="Transform[]"/> of all objects to be aligned
    /// </param>
    /// <param name="rotateToTerrainAngle">
    /// <see cref="true"/> to rotate <see cref="Transform"/> to the normal from the raycast hit
    /// </param>
    public static void FallOnTerrain(Transform[] transformList, bool rotateToTerrainAngle, ExtentsGetter.BoundType boundType)
    {
        // List of selected objects, to sort from the min position to the max position
        List <Transform> list = new List <Transform>(transformList);

        // Sort the selected objects from the down to the top, so the lowest 'falls' first
        list.Sort(DistributionManager.ByVector3PositionY);

        foreach (Transform fallingTransform in list)
        {
            FallOnTerrain(fallingTransform, rotateToTerrainAngle, boundType);
        }
    }
    /// <summary>
    /// Duplicate a Prefab or a GameObject in a grid
    /// </summary>
    /// <param name="prefabObject">
    /// The <see cref="GameObject"/> prefab if there is one
    /// </param>
    /// <param name="referenceObject">
    /// The <see cref="GameObject"/> to duplicate
    /// </param>
    /// <param name="transformList">
    /// All <see cref="Transform[]"/> to be distributed
    /// </param>
    /// <param name="sortBy">
    /// The axis which is used to sort the transform list, <see cref="SortAxis"/>
    /// </param>
    /// <param name="axis">
    /// The axis to distribute the transforms on, using the same <see cref="Vector3"/> format
    /// </param>
    public static void DuplicateInGrid(GameObject prefabObject, GameObject referenceObject, Vector3 gridSize, Vector3 offsets, ExtentsGetter.BoundType type)
    {
        // Get the min and max marks
        Vector3 minMarkPosition = referenceObject.transform.position;

        // Total grid object instantiation count
        int gridCount = 0;
        // The steps are here to be sure that we start after the selected object, on the right axis
        int cellX = 0;
        int cellY = 0;
        int cellZ = 0;

        // Calculate the count of elements to create
        gridCount = Mathf.RoundToInt(Mathf.Max(gridSize.x, 1) * Mathf.Max(gridSize.y, 1) * Mathf.Max(gridSize.z, 1));

        // Interval between parts (do not use prefab, as its extents are not calculated by Unity before instantiation)
        Vector3 distanceBetween = ExtentsGetter.GetHierarchyBounds(referenceObject, type).size;

        Vector3 newPos = minMarkPosition;

        // First step, to avoid creating in the same position of the original object
        if (gridSize.x > 1)
        {
            cellX = 1;
        }
        else if (gridSize.y > 1)
        {
            cellY = 1;
        }
        else
        {
            cellZ = 1;
        }

        // ...
        GameObject duplicatedObj = null;
        string     baseName      = "";

        // Starts @ 1 to avoid duplicate the first object (@ the current location)
        for (int i = 1; i < gridCount; i++)
        {
            // Keep the link to the prefab, if there is one
            if (prefabObject)
            {
                duplicatedObj = PrefabUtility.InstantiatePrefab(prefabObject) as GameObject;
                baseName      = prefabObject.name;
            }
            else
            {
                duplicatedObj = (GameObject)GameObject.Instantiate(referenceObject);
                baseName      = referenceObject.name;
            }
            // Name the new object from its position in the grid
            duplicatedObj.name = baseName;
            if (gridSize.x > 0)
            {
                duplicatedObj.name += "_" + cellX;
            }
            if (gridSize.y > 0)
            {
                duplicatedObj.name += "_" + cellY;
            }
            if (gridSize.z > 0)
            {
                duplicatedObj.name += "_" + cellZ;
            }

            // Compute object position
            newPos.x = minMarkPosition.x + (distanceBetween.x + offsets.x) * cellX;
            newPos.y = minMarkPosition.y + (distanceBetween.y + offsets.y) * cellY;
            newPos.z = minMarkPosition.z + (distanceBetween.z + offsets.z) * cellZ;
            duplicatedObj.transform.position = newPos;

            // Update the grid indexes
            cellX++;
            if (cellX >= gridSize.x || gridSize.x == 0)
            {
                cellX = 0;
                cellY++;
                if (cellY >= gridSize.y || gridSize.y == 0)
                {
                    cellY = 0;
                    cellZ++;
                }
            }
        }
    }