예제 #1
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;
        }
    }
    /// <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++;
                }
            }
        }
    }