// 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); }