Example #1
0
    // This method calculates a Vector3 representing the nearest valid snap-to target position, if there is one nearby, for placement auto-correction.
    // It then assigns that value (if it exists) to the corresponding instance variable, placementCorrectionTarget.
    // It returns a PlacementCorrectionType representing the best placement correction type available (this will be PlacementCorrectionType.SnapTo if a target position was successfully calculated)
    // param testing: indicates whether the method is being called to test a position or to actually move there (more non-local variable assignments occur in the latter case)
    private PlacementCorrectionType calculatePlacementCorrectionTarget(bool testing)
    {
        // detect up to MAX_SNAP_TO_COLLIDERS nearby snap-to objects that are relevant to this specific piece
        Collider[] colliders = new Collider[MAX_SNAP_TO_COLLIDERS];
        int        numHit    = Physics.OverlapSphereNonAlloc(transform.position, SNAP_TO_PIECE_RADIUS, colliders, 1 << snapToLayer);

        if (numHit > 0)
        {
            // there are relevant snap-to colliders nearby, so find the nearest detected one and use its position as the target

            // Helper function that returns the distance from this piece to the given collider
            Func <Collider, float> distFromThis = (collider) => Vector3.Distance(transform.position, collider.gameObject.transform.position);

            // Helper function that indicates which of two colliders is closer to this piece
            Func <Collider, Collider, bool> isFirstDistanceSmaller = (first, second) => distFromThis(first) < distFromThis(second);

            // Identify the detected collider that's closest to this piece and unoccupied
            Collider closestCollider = colliders.Aggregate((first, second) => second == null || second.gameObject.GetComponent <SnapToTargetBehaviour>().occupied || (isFirstDistanceSmaller(first, second) && !first.gameObject.GetComponent <SnapToTargetBehaviour>().occupied) ? first : second);

            if (!closestCollider.gameObject.GetComponent <SnapToTargetBehaviour>().occupied) // could be occupied if the first collider, as well as all the non-null others, was occupied
            {
                GameObject obj          = closestCollider.gameObject;
                Transform  objTransform = obj.transform;

                // If not just testing:
                // test the position to make sure it wouldn't cause overlap with another piece (besides the one it's snapping to)
                // if valid position, mark the target GameObject as occupied, and store it so it can be unmarked if this piece leaves it
                if (!testing)
                {
                    List <Collider> positionTestObjectCollidersInContact = positionTestObject.GetComponent <PiecePrefabBehaviour>().collidersInContact;
                    if (!allPartOfGameObject(positionTestObjectCollidersInContact, getCompletePiece(closestCollider.gameObject))) // the position would cause overlap with another piece (besides the one it's snapping to)
                    {
                        return(PlacementCorrectionType.ReturnToInitPosition);
                    }
                    obj.GetComponent <SnapToTargetBehaviour>().occupied = true;
                    currOccupiedSnapToTarget = obj;
                }

                // Use its position as the target
                placementCorrectionTarget = objTransform.position;
                return(PlacementCorrectionType.SnapTo);
            }
            // if we reach here, all of the detected colliders were occupied, so continue on in the placement correction options
        }

        // Okay, there are no unoccupied, designated placement correction targets for this piece nearby. So next try: is it inside another piece? If so, return it to its initial spot, given the invalid placement
        if (collidersInContact.Count > 0)
        {
            return(PlacementCorrectionType.ReturnToInitPosition);
        }

        // Okay, it isn't inside a piece either. So is it close enough to whatever's directly below it? If so, snap down
        int        layerMask = 1;
        RaycastHit hit;
        Vector3    raycastOrigin = new Vector3(transform.position.x, getBottom(), transform.position.z);

        if (Physics.Raycast(raycastOrigin, Vector3.down, out hit, SNAP_DOWN_DIST_THRESHOLD, layerMask))
        {
            placementCorrectionTarget = new Vector3(hit.point.x, convertBottomToTransformY(hit.point.y), hit.point.z);
            return(PlacementCorrectionType.SnapDown);
        }

        // Okay, there's nothing close below the piece, as detected by raycasting. Is its bottom lower than the floor
        // (or just barely above it, which occurs because of floating-point stuff if the piece was previously on the floor and was dragged across it)?
        // If so, snap to the floor, unless that would cause overlap with another piece
        if (getBottom() - floorY < 0.01f)
        {
            if (!testing && positionTestObject.GetComponent <PiecePrefabBehaviour>().collidersInContact.Count > 0) // the position would cause overlap
            {
                return(PlacementCorrectionType.ReturnToInitPosition);
            }
            placementCorrectionTarget = new Vector3(transform.position.x, convertBottomToTransformY(floorY), transform.position.z);
            return(PlacementCorrectionType.SnapTo);
        }

        // it's not close enough to any valid placement correction targets, other pieces (vertically), or the floor, so return PlacementCorrectionType.ReturnToInitPosition to indicate invalid placement
        return(PlacementCorrectionType.ReturnToInitPosition);
    }