How to preserve Y rotation while setting transform.up?
I'm working on a building system in which the player may drag objects and snap them together. Here is a video to show what the snapping is like: Video Showing snapping to the normal by setting transform.up Think Spore or KSP with how a raycast hit to a bindable object (an object you can place something onto) while editing lets you place the object onto its surface by setting the position and Transform.up of the dragged object to the raycast hit's normal and position.
// Set the upwards direction to the normal of the hit.
objectBeingDragged.transform.up = closestEditableHit.Value.normal;
// Update the position so it hugs the surface of the object we hit.
objectBeingDragged.transform.position = closestEditableHit.Value.point;
The issue I am facing comes in where I attempt to rotate the current object being held by the player. Since the object being dragged is setting its transform.up to the raycast hit's normal, I lose the Y-Rotation. Here is a snippet from the Player's code where I rotate an object being held.
// If this player is holding an object.
if (ObjectBeingHeld != null)
{
// A delta in the object's Y Euler angles to apply
float delta = 0.0f;
// When using snap mode for perfect angles.
if (useSnapMode)
{
// Increment in intervals of 90 degrees.
delta = positive ? 90 : -90;
}
else
{
// Otherwise, the player may make incremental rotations in free edit.
delta = (positive ? -5 : 5);
}
// Apply the rotation to the object being held around it's Y axis.
// ( I have tried both world space and local space here... )
ObjectBeingHeld.transform.Rotate(ObjectBeingHeld.transform.up, delta, Space.World);
}
The object tries to rotate for just a split second, but gets reset, likely due to the transform.up being set at nearly the same time I'm trying to rotate. I'm trying to wrap my head around how I can express what I'm trying to accomplish.
Also, you will notice in the video, as soon as I pick up the object take note that its x and z rotations are being manually frozen by setting them to 0, while preserving the y rotation.
if (isBeingDragged)
{
// Lock the x and z rotation
transform.rotation = Quaternion.Euler(0, transform.rotation.y, 0);
if (rb != null)
{
rb.velocity = Vector3.zero;
}
}
I notice that Spore does not let you rotate until you have stopped moving the object. I'm not sure if that's really the only good way to handle this.
I believe I may be on to something. I'm looking at the problem wrong. I need to simultaneously set transform.right/forward perhaps?
Answer by colinalaws · Jan 22 at 09:11 PM
Eureka! My suspicion was correct. I needed to modify both transform.forward, and transform.up simultaneously.
The solution here is to keep a variable on the object being rotated called "forward" which is a vector 3. Then, when the player clicks a rotate button on the d-pad, I modify the "forward" variable on the object being dragged. That way I keep sort of a cached state if you will of the rotation the player is trying to achieve. Simply swapping it between Vector3.up and Vector3.right seems to work pretty well.
In the end, applying the rotation on the object looks like this in my raycast:
// The variable forward here being passed in is modified by the player to "rotate" the object.
transform.rotation = Quaternion.LookRotation(forward, closestEditableHit.Value.normal);