[Unity] 6일차 마우스 회전 수학 계산

김소연·2025년 4월 14일
public class CookingMillMiniGame : MonoBehaviour,ICookingMiniGameHandler
{
    float timer = 15; // 15초
    float elapsedTimer; // 누적 시간
    
    // 기준
    public float direction; // 시계 or 반시계
    public bool isClockWise; // 시계방향인지 아닌지
    public float angle; 
    public float speed;

    bool isGameOver = false;

    Vector2 curPos; // 현재 마우스 위치
    Vector2 previousPos;
    Vector2 targetPos;

    Vector2 centerPos;
    [SerializeField]private Transform centerTransform;

    private float curAngle;
    private float previousAngle;

    // 큐 (최단각도, 시간) 구조 
    public Queue<(float deltaAngle, float time)> angleQueue = new();

    float CurAngleFromCenter(Vector2 pos, Vector2 center)
    {
        Vector2 dir = pos - center; // 센터->pos 방향
        return Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
    }

    #region 리팩토링 이후 사용
    public void StartGame()
    {
    }

    public void StopGame()
    {

    }

    public void UpdateGame()
    {
    }
#endregion

    // 테스트용 생명주기함수

    void Start()
    {

    }

    void Update()
    {

        if (isGameOver) return; 

        timer -= Time.deltaTime;

        curPos = Input.mousePosition; // 현 마우스의 위치
        Vector2 centerPos = Camera.main.WorldToScreenPoint(centerTransform.position); // 맷돌 정가운데 (중심점)

        // 현재 마우스와 중심 사이의 각도를 구한다
        CurAngleFromCenter(curPos, centerPos);

        // 1. 프레임마다의 각도의 차이를 구한다
        float deltaAngle = Mathf.DeltaAngle(curAngle, previousAngle);



        // 2. 마우스 회전방향 
        // (현재 프레임 - 이전 프레임)의 최단각도가 플러스면 시계, 마이너스면 반시계 방향
        if(deltaAngle > 0) { isClockWise = true; }

        // 3. (현재 프레임 - 이전 프레임)의 각도를 계속해서 큐에 넣어준다
        angleQueue.Enqueue((deltaAngle, Time.time));

        // * 0.75초마다 계산하기 위해, 큐에 있는 가장 오래된 데이터의 시간값이 0.75초를 넘으면 제거한다
        if (angleQueue.Peek().time > 0.75f)
        {
            angleQueue.Dequeue();
        }

        // 4. 누적된 회전 각도는 큐에 존재하는 데이터들의 절댓값의 합
        float totalAngle = angleQueue.Sum(x => Mathf.Abs(x.deltaAngle));

        // 5. 그것을 0.75초로 나누어주면 마우스의 평균 속도  
        float mouseSpeed = totalAngle / 0.75f;

        Debug.Log($"{mouseSpeed}");

        // 6. mouseSpeed (180f~250f) 사이를 유지한 시간도 구해야 판정 기준에 쓸 수 있음
        if (mouseSpeed >= 180f && mouseSpeed <= 250f) 
        {
            elapsedTimer += Time.deltaTime;
        }

        // 7.. 모든 처리 이후 초기화
        previousAngle = curAngle;

        Debug.Log($"deltaAngle: {deltaAngle}, mouseSpeed: {mouseSpeed}, curAngle: {curAngle}, prevAngle: {previousAngle}");

        if (timer <= 0f)
        {
            isGameOver = true;
            // 8. 마우스속도와 유지시간을 판정 함수로 넘김
            Judge(mouseSpeed, elapsedTimer);
            Time.timeScale = 0f;
        }
    }

    /// <summary>
    /// 게임종료 후 등급 판정 
    /// (mouseSpeed를 180f~250f 사이로 유지한 시간을 기준으로 함)
    /// </summary>
    /// <param name="speed"></param>
    /// <param name="time"></param>
    void Judge(float speed, float time)
    {
        // 12초이상 : 상 (perfect)
        if (time >= 12f) 
        {
            Debug.Log("Perfect");
        }
        if (time >= 8f && time <= 11f)
        {
            Debug.Log("Good");

        }
        if (time >= 5f && time <= 7f)
        {
            Debug.Log("Bad");

        }
        else
        {
            Debug.Log("Fail");
        }

            // 8~11초 : 중 (Good)

            // 5~7초 : 하 (Bad)

            // 5초 미만 || 반대방향 지속 입력 : 실패 (Miss)

    }
  1. 메인 카메라에서 screen 좌표계의 마우스 위치를 입력 받아 월드 좌표로 변환하고 이를 curPos에 대입한다

15초간 진행되는 이 미니게임은 마우스를 원형범위내에서 특정한 회전방향(시계or반시계 랜덤)으로 회전하면서, 목표 속도를 유지 하는 게임이다.
따라서

  1. 현재 마우스의 회전방향 (이전 프레임과 현 프레임 간의 각도 차이)
  2. 마우스 위치를 기반으로 회전각도를 계산
  3. 마우스 회전 속도 (누적된 회전각으로 평균 속도 계산)
  4. 목표 범위 (회전속도목표는 초당 평균 회전각도 180~250도)에 들어오는지 확인
  1. Mathf.Atan2을 통해 탄젠트 값으로 y/x값을 가지는 radian을 반환한다.

  2. Mathf.Rad2Deg는 radian to degree로, 우리가 일반적으로 사용하는 각도로 변환해주는 과정이다

마우스 커서의 이동에 따라 오브젝트를 움직
https://gnaseel.tistory.com/17
ex)
Quarternion.AngleAxis(30, Vector3.up);
Sets the transform's current rotation to a new rotation that rotates 30 degrees around the y-axis(Vector3.up)

트러블슈팅
Vector2 centerPos = Camera.main.WorldToScreenPoint(centerTransform.position);

->

Vector2 centerPos = RectTransformUtility.WorldToScreenPoint(null, centerTransform.position);

ui는 rectTransform을 가지기 때문에..

0개의 댓글