Ejemplo n.º 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.renderer) {
					delta = -nextTransform.renderer.bounds.extents;
				} else if (nextTransform.collider) {
					delta = -nextTransform.collider.bounds.extents;
				}
			} else if (alignType == Landmark.minimum) {
				if (nextTransform.renderer) {
					delta = nextTransform.renderer.bounds.extents;
				} else if (nextTransform.collider) {
					delta = nextTransform.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;
		}
	}
Ejemplo n.º 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;
        }
    }
    /// <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++;
                }
            }
        }
    }
Ejemplo n.º 4
0
	/// <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++;
				}
			}
		}
	}
Ejemplo n.º 5
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;
		}
	}
Ejemplo n.º 6
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);
		}
	}