1
\$\begingroup\$

How can I implement snapping via code? I have this 2d circle that the players rotate via drag and it works like a rotating dial. The best way to describe what I wanted to achieve is similar to a clock hand snapping to the hour numbers. I'm a total beginner and haven't actually learned anything about quaternions.

Image of dial with 0 at the top and 7 increment marks evenly spaced around the rest

This is the my code , im using a raycast to detect if the dial is clicked/dragged.

Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition); RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector3.forward); if (Input.GetMouseButtonDown(0)) { if (hit.collider && hit.collider.GetComponent<Rotate2d>()) { deltaRotation = 0f; previousRotation = angleBetweenPoints(transform.position, Camera.main.ScreenToWorldPoint(Input.mousePosition)); } } else if (Input.GetMouseButton(0)) { if (hit.collider && hit.collider.GetComponent<Rotate2d>()) { currentRotation = angleBetweenPoints(transform.position, Camera.main.ScreenToWorldPoint(Input.mousePosition)); deltaRotation = Mathf.DeltaAngle(currentRotation, previousRotation); if (Mathf.Abs(deltaRotation) > deltaLimit) { deltaRotation = deltaLimit * Mathf.Sign(deltaRotation); } previousRotation = currentRotation; hit.collider.gameObject.transform.Rotate(Vector3.back * Time.deltaTime, deltaRotation); } } 

I forgot to add, here is my angle between points method.

 float angleBetweenPoints(Vector2 position1, Vector2 position2) { Vector2 fromLine = position2 - position1; Vector2 toLine = new Vector2(1, 0); float angle = Vector2.Angle(fromLine, toLine); Vector3 cross = Vector3.Cross(fromLine, toLine); // did we wrap around? if (cross.z > 0) { angle = 360f - angle; } return angle; } 
\$\endgroup\$
1
  • 1
    \$\begingroup\$ hit.collider.gameObject.transform.Rotate(Vector3.back * Time.deltaTime it's not meaningful to multiply a direction vector by time here. You do not want time in this expression at all. \$\endgroup\$ Commented Jun 20, 2022 at 11:48

1 Answer 1

1
\$\begingroup\$

First, you can simplify your angle method:

public static float DegreesCCWFromRight(Vector2 center, Vector2 pointer) { var direction = pointer - center; return Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; } 

Then just round the result to your desired number of increments.

if (hit.collider && hit.collider.GetComponent<Rotate2d>()) { Vector2 mouseWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); float angle = DegreesCCWFromRight(transform.position, mouseWorld); const float incrementCount = 6; angle = Mathf.Round(currentRotation * incrementCount/360) * 360/incrementCount; transform.localEulerAngles = new Vector3(0, 0, angle); } 
\$\endgroup\$
3
  • \$\begingroup\$ Thanks a lot! Finally got it to work! You made it look easy. Will probably spent sometime in the weekend learning some of those math functions! \$\endgroup\$ Commented Jun 20, 2022 at 16:19
  • \$\begingroup\$ By the way I managed to make it work but somehow I can't figure out something. I changed the increments to 4/8 since this works perfectly and I wanted it to rotate by quarters or half a quarter. It works on one dial, but when I try to rotate the other dials, it rotates by a quarter and stops totally. What am I doing wrong? \$\endgroup\$ Commented Jun 20, 2022 at 17:51
  • \$\begingroup\$ @PeytonFarquhar you can always ask a new question with the updated code that is causing problems (and post the working solution for other amounts). It can be as simple as forgetting to change one variable, having a wrong cast or a simple typo. But when saying works on one and not the others, without showing us whats the difference between your dials, we can't help \$\endgroup\$ Commented Jun 21, 2022 at 8:17

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.