Rotate Door Object Away from Player

First some context. My map is a tile based system with randomly generated walls and doors. All of these pieces are prefabs instantiated by script and placed in their proper location and orientation.

Right now I have doors. On an input key it fires a raycast that checks for the collision with objects on the Interactable layer. When one is found it runs a script on the door GameObject for it to rotate open.

They are currently rotating using a Quaternion.Slerp with a From of the current transform.rotation of the GameObject and the To of a transform.rotation of a public GameObject assigned to the tile prior to building.

This works fine, however it’s a hardcoded direction of rotation. What I am trying to do is determine a way to programatically create the destination Quaternion/Transform so that the door always rotates AWAY from the player. Right now it has a 50/50 chance of hitting the player in the face.

The info I have available is a RayCastHit, Player GameObject, Door GameObject. I have tried doing things like taking the ray.normal and flipping it 180 degrees, which gives a “forward” but that forward isn’t oriented up so it rotates the door correctly while also rotating it down into the floor.

I feel like this should be a lot simpler than I am making it. Anyone have thoughts?

private enum ActionStates {
	CLOSED,
	OPEN,		
	CLOSING,
	OPENING,
}
private ActionStates actionState;
private float timer = 0.0f;
public Transform transOpen;
private Transform transStart;
public bool isUsable = true;
public float timeMult = 0.5f;

void Start ()
{
	actionState = ActionStates.CLOSED;
	transStart = transform;		
}

void Update ()
{
	if (actionState == ActionStates.OPENING) {
		timer += Time.deltaTime * timeMult;
		transform.rotation = Quaternion.Slerp(transStart.rotation,transOpen.rotation, timer);
		if (timer >= 1.0f) {
			actionState = ActionStates.OPEN;
	}
}

public void DoUse()
{
	if (actionState == ActionStates.CLOSED) {
		DoOpen();
	}
	if (actionState == ActionStates.OPEN) {
		DoClose();
	}
}

public void DoOpen()
{
	actionState = ActionStates.OPENING;
	timer = 0.0f;
}
	
public void DoClose()
{
	actionState = ActionStates.CLOSING;
	timer = 0.0f;
}

I would do things in a different manner:

1- Do a raycast from the player (I suppose that’s what you’re already doing);

2- If the object hit is a door, use SendMessage to call a door function that opens the door - pass the player position as the parameter;

3- Based on the player position, this routine decides to which side rotate;

4- The door rotates a fixed angle (say, -120 or 120 degrees) around the Y axis with Rotate, or by setting eulerAngles.y;

In practical terms, the whole thing would be like this:

  • In the player script:
...
if (Physics.Raycast(transform.position, transform.forward, out hit)){
  if (hit.transform.tag == "Door"){
    hit.transform.SendMessage("OpenDoor", transform.position);
  }
}
...
  • In the door script:
public float speed = 90; // door speed in degrees per second
public float openAngle = 120; // opening angle in degrees

Vector3 curAngle;
float startAngle;
float targetAngle;

void Start(){
  curAngle = transform.eulerAngles;
  startAngle = curAngle.y; // save the startAngle (closed)
  targetAngle = startAngle;
}

void OpenDoor(playerPos: Vector3){
  if (actionState == ActionState.CLOSED){ // only open a closed door!
    Vector3 dirDoor = transform.position - playerPos; 
    float dot = Vector3.Dot(dirDoor, transform.right);
    if (dot > 0){ // if door opens to the wrong side, use dot < 0
      targetAngle = startAngle + openAngle;
    } else {
      targetAngle = startAngle - openAngle;
    }
    actionState = ActionState.OPEN;
  }
}

void Update(){
  // rotate gradually door to targetAngle, if different of curAngle:
  curAngle.y = Mathf.MoveTowards(curAngle.y, targetAngle, speed * Time.deltaTime);
  transform.eulerAngles = curAngle;
}

void CloseDoor(){ // closing the door is much easier!
  targetAngle = startAngle;
  actionState = ActionState.CLOSED;
}

There are some things to consider:

1- This code assumes that the door’s Y passes through the hinges and Z points to the handle; if X points to the handle side, use Dot((dirDoor, transform.forward).

2- If the door is opening to the wrong side, invert the dot comparison (dot < 0).