Player walking inside a cube -- how to rotate cube as player nears facing wall?

Hi, folks. Noob here with first post. Thanks in advance for any guidance on offer.

I have a large cube with the Player inside.

The cube is comprised of six planes set as children of a parent empty gameobject named “Cubicle.”

I want the cube to rotate forward when the Player gets close (raycasting) to the Player-facing wall, so that the Player-facing wall becomes the floor. I’ve got it working with a poor implementation I hope can be improved. In its current form, as the Player I can approach any side wall and have it flip around to the floor. But instead of maintaining strict 90-degree intervals of rotation, variations creep in and accumulate to throw off the Player rotation – which should stay horizontal at all times.

Here’s a video showing the results of what I have so far.

You can see how if I (the Player) approach a wall at an angle, causing it to rotate to become the floor, the cube rotation goes askew. I need to somehow lock the rotation to multiples of 90 degrees to maintain the Player horizontal. I’ve scoured the net and forums for the right Vector/Quaternion approach but regretfully could not piece it together.

Here’s my rotation script that is attached to the Cubicle gameobject. (Ignore the nonsensical class name, which hasn’t changed since early testing.)

using UnityEngine;
using System.Collections;

public class CheckPosition : MonoBehaviour {

	public float rayForwardDistance = 2;
	public float lerpDuration = 1f;

	private float currentLerpTime = 999;
	private Quaternion startPosR, endPosR;

	private GameObject cubicle, player;
	private bool gotHitForward = false;

	// Use this for initialization
	void Start () {

		cubicle = GameObject.Find("Cubicle");
		player = GameObject.FindGameObjectWithTag("MainCamera");

		if (player == null)
		{
			Debug.Log("Failed to locate object tagged 'MainCamera'");
		}

		InvokeRepeating("RayCastCheck", 0, 0.1f);
	}
	
	// Update is called once per frame
	void Update () {
		if (gotHitForward && currentLerpTime < lerpDuration)
		{
			currentLerpTime += Time.deltaTime;
			//lerp!
			float perc = currentLerpTime / lerpDuration;
			transform.rotation = Quaternion.Slerp(startPosR, endPosR, perc);
		}
	}

	void RayCastCheck ()
	{

		if (currentLerpTime < lerpDuration)
		{
			return; // currently lerping, so do nothing
		}

		RaycastHit hitForward;


		gotHitForward = Physics.Raycast(player.transform.position, player.transform.forward, out hitForward, rayForwardDistance);
		if (gotHitForward)
		{
			currentLerpTime = 0f;
			GameObject theHitObject = hitForward.collider.gameObject;
			Debug.Log("hitForward = " + theHitObject.name);

			startPosR = transform.rotation;
			endPosR = Quaternion.AngleAxis(90f, player.transform.right) * transform.rotation;

		}
	}
}

i would put the lerp code into a coroutine that’s started after the raycast success, stop the raycastcheck and at the end of the lerp routine restart it; that way you can also debug it easier with Debug logs to see if something cancels the lerp and why.

I thought about how i would make something like this work, so i created a little test scene. It’s not a clean way, but it works and is similar to what you do, i hope it can help you (attached unitypackage).

2560622–178314–WallWalk.unitypackage (32.5 KB)

Thanks, ValooFX.

I solved it after thinking it through some more, and it was a delight to find your reply and test package when I returned to update this thread. Having now seen your attached wall-walking demo and code, I appreciate how clunky my solution is, but I’m encouraged at being mostly on the right track.

This was my thinking…

The cube gameobject transform is at the center of the cube. All the transforms for the wall planes similarly have their transforms centered. So what I needed to do was get the vector between the cube center and the raycast-returned wall center (i.e. the wall the Player approached). That gives me the player’s cube-centered forward-facing direction. The axis to rotate around is not this forward vector – which would result in a lefthand rotation for the cube – but the x/right vector, to spin the cube forward. That meant I needed to get the vector perpendicular to the forward and up vectors. That was achieved through the CROSS function (which you also used).

Here’s the modified script section:

        gotHitForward = Physics.Raycast(player.transform.position, player.transform.forward, out hitForward, rayForwardDistance);
        if (gotHitForward)
        {
            GameObject theHitObject = hitForward.collider.gameObject;
            Debug.Log("hitForward = " + theHitObject.name);

            startPosR = transform.rotation;
           // get vector between cube center and wall center
            Vector3 cubeCenterVectorForward = transform.position - theHitObject.transform.position;
           // now get the axis to rotate around, which is perpendicular to the forward and up
            Vector3 adjustedVector = Vector3.Cross(Vector3.up, cubeCenterVectorForward);
            endPosR = Quaternion.AngleAxis(90.00f, -adjustedVector) * transform.rotation;

            currentLerpTime = 0f;

        }

With this adjustment, no matter what angle the Player approaches a wall, the cube will rotate 90 degrees and maintain the Player’s relative zero z rotation/horizon.

Here’s the result:

Of course, now I’m going to replace my code with your streamlined version. Thanks so much for taking the time to produce a working demo. Seeing something working then reading the code behind it is the best lesson possible.