How do i rotate a cube least amount to align faces with another?
Hi,
My first post... I'm trying to make a basic interaction where the player can pick up blocks on the ground and attach them to others. It's not like a m*craft grid thing, the blocks can be in any orientation. When player picks up a block A they can rotate it. When the block is placed near a face of another block B, A will snap into place to align with the face of B (see pics). The orientation matters because there will be symbols on the block faces (not shown), so block A need A to rotate only the smallest/nearest amount to align with B. I'm stuck with getting the rotation right.
okay alignment:
bad alignment:
Here's my Snap function where my understanding of rotation geometry is failing short and could use advice on how to get the desired effect. Variables mentioned here are holdingPart - the block the player is holding (transparent in the pic); nearPointingAtPart - the block on the ground; and pointerHit - the raycast hitting the block face that the player is pointing at.
void SnapHeldPart()
{
// rotate the held object a little bit to align with the pointed-at part
var vec = holdingPart.transform.eulerAngles - nearPointingAtPart.transform.eulerAngles;
vec.Set(Mathf.Round(vec.x / 90f) * 90f, Mathf.Round(vec.y / 90f) * 90f, Mathf.Round(vec.z / 90f) * 90f);
holdingPart.transform.eulerAngles = vec + nearPointingAtPart.transform.eulerAngles;
// position the held part by the selected surface normal on another cube.
Vector3 placePoint = (nearPointingAtPart.transform.position + pointerHit.normal * 0.51f);
holdingPart.transform.position = placePoint;
}
Thanks
Answer by ElijahShadbolt · Nov 18, 2016 at 07:36 AM
I figured out a solution which works for all rotations of the block and/or camera. It requires the desired forward and upward vectors of your holdingPart block, i.e. pointerRay.direction
(forwards, like say, the direction of your raycast) and Camera.main.transform.up
(upwards).
using UnityEngine;
public class BlockRotation : MonoBehaviour
{
public GameObject holdingPart;
private GameObject nearPointingAtPart;
private RaycastHit pointerHit;
private Ray pointerRay;
public float blockDistance = 0.51f;
void Update()
{
pointerRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(pointerRay, out pointerHit))
{
nearPointingAtPart = pointerHit.transform.gameObject;
SnapHeldPart();
}
}
void SnapHeldPart()
{
// other-block directions
Vector3 f = nearPointingAtPart.transform.forward,
r = nearPointingAtPart.transform.right,
u = nearPointingAtPart.transform.up;
// placing-block directions
Vector3 forw = GetDirectionVector(f, r, u, pointerRay.direction);
Vector3 up = GetDirectionVector(f, r, u, Camera.main.transform.up);
// set rotation
holdingPart.transform.rotation = Quaternion.LookRotation(forw, up);
// set position
holdingPart.transform.position = nearPointingAtPart.transform.position + pointerHit.normal * blockDistance;
}
// returns the face normal of a cube (defined by normals f,r,u) which is the closest angle to dir.
private Vector3 GetDirectionVector(Vector3 f, Vector3 r, Vector3 u, Vector3 dir)
{
float af = Vector3.Angle(dir, f);
float ar = Vector3.Angle(dir, r);
float au = Vector3.Angle(dir, u);
if (af <= 45f)
return f;
else if (af >= 135f)
return -f;
else if (ar <= 45f)
return r;
else if (ar >= 135f)
return -r;
else if (au <= 45f)
return u;
else// if (au >= 135f)
return -u;
}
}
Answer by JavaMcGee · Dec 18, 2016 at 12:28 AM
Appreciate the suggestion but I couldn't get that working quite the way I wanted. It resets the rotation of the held cube after it is snapped, but what I wanted was to keep the rotation as close to the current as possible, however it was previously rotated by the player, rounding it to the closest that aligns with the fixed cube. I also had problems fixing a behavior where it flipped rotation by 45deg depending on which side of cube's face (across a diagonal I guess) that the ray was pointing at.
I found that my originally posted code was closer than I thought if I just don't subtract rotations. Change to the following - although this doesn't behave the way I wanted if the base cube is rotated too much.
preSnapPartRotation = holdingPart.transform.rotation;
holdingPart.transform.rotation = ModRotation(nearPointingAtPart.transform.rotation, 90f) * RoundRotation(preSnapPartRotation, 90f);
Anyway, the point of this was to see if handling and snapping blocks together like this would be useful (and fun) for a game I had in mind, but unfortunately it feels like an awkward way to make the player manipulate objects. Going to try to think up something different.
Your answer
![](https://koobas.hobune.stream/wayback/20220612100912im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Rotate around local Y axis using Joystick Vector3 0 Answers
Smooth snapping rotation 1 Answer
align object with collision normal then look at an object on one axis 0 Answers
How to make rotating objects with snapping to grid while the object contains children 0 Answers
3D rotation with snap 0 Answers