프로젝션 행렬을 이용한 구현.

public class Test : MonoBehaviour
{
    Matrix4x4 originalProjectionMat;

    private void Start()
    {
        originalProjectionMat = Camera.main.projectionMatrix;
    }

    private void CameraShake()
    {
        Matrix4x4 pMat = originalProjectionMat;

        // roll
        pMat.m01 += Mathf.Sin(Time.time * 1.25f) * 0.4f;
        pMat.m10 += Mathf.Sin(Time.time * 1.5f) * 0.4f;

        // x
        pMat.m00 += Mathf.Sin(Time.time * 1.5f) * 0.1f;
        pMat.m11 += Mathf.Sin(Time.time * 1.5f) * 0.1f;

        Camera.main.projectionMatrix = pMat;
    }
    
    private void Update()
    {
        CameraShake();
    }
}

매트릭스 Inverse 직접 구현

 

회전행렬의 역행렬 * 이동행렬의 역행렬

    public Matrix4x4 Inverse(Matrix4x4 matrix)
    {
        // Rotate Scale Inverse
        Matrix4x4 invRot = Matrix4x4.identity;
        for(int i=0; i < 3; ++i)
        {
            Vector4 v = matrix.GetColumn(i);
            invRot.SetRow(i, v.normalized / v.magnitude);
        }

        // Translate Inverse
        Matrix4x4 invTrans = Matrix4x4.identity;
        invTrans.m03 = -matrix.m03;
        invTrans.m13 = -matrix.m13;
        invTrans.m23 = -matrix.m23;

        // Combine
        return invRot * invTrans;
    }
public float SetPixelSize(float _pixelRate)
    {
        cam= GetComponent<Camera>();
        Vector3 p1 = new Vector3(1, 0, 0);
        Vector3 p2 = new Vector3(2, 0, 0);

        p1 = cam.ScreenToWorldPoint(p1);
        p2 = cam.ScreenToWorldPoint(p2);

        float pixelSize = p2.x - p1.x;
        pixelSize *= _pixelRate;
        
        return pixelSize;
    }

 

 

Canvas 에서 Screen Space -  Camera 일 경우 마우스 클릭 위치에 UI를 옮기고 싶으면 아래와 같이 하면 됨.

 

ContextMenuEx 스크립트를 만듬.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ContextMenuEx : MonoBehaviour
{
    public RectTransform targetRectTr;
    public Camera uiCamera;
    public RectTransform menuUITr;

    private Vector2 screenPoint;
    private void Start()
    {
        targetRectTr = GetComponent<RectTransform>();
        uiCamera = Camera.main;
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Mouse0))
        {
            RectTransformUtility.ScreenPointToLocalPointInRectangle(targetRectTr, Input.mousePosition, uiCamera, out screenPoint);
            menuUITr.localPosition = screenPoint;
        }
    }
}

 

회색으로 색칠된 UI가 Panel, 빨간색으로 색칠된 UI가 menuUI이다.

마우스 클릭시 클릭된 위치에 menuUI가 잘 나온다.

 

 

패널 바깥부분 누르면 튀어나옴. Panel영역 안에서만 나오게 하고 싶으면 RectTransformUtility.RectangleContainsScreenPoint()함수를 이용하면 됨.

 

Panel영역 내에서만 나타나게 하는 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ContextMenuEx : MonoBehaviour
{
    // RectTransform 범위
    public RectTransform targetRectTr;
    public Camera uiCamera;
    public RectTransform menuUITr;

    private Vector2 screenPoint;
    private void Start()
    {
        uiCamera = Camera.main;
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Mouse0) &&
            RectTransformUtility.RectangleContainsScreenPoint(targetRectTr, Input.mousePosition, uiCamera))
        {
            RectTransformUtility.ScreenPointToLocalPointInRectangle(targetRectTr, Input.mousePosition, uiCamera, out screenPoint);
            menuUITr.localPosition = screenPoint;
        }
    }
}

 

 

간단하게 만들어봄.

InputFieldTabManager inputFieldTabMrg = new InputFieldTabManager();를 이용하여 선언 후.

inputFieldTabMrg.Add(인풋필드); 로 인풋필드를 추가해준다.

inputFieldTabMrg.SetFocus(); 를 이용하여 포커스를 맞춘 후

 

Update() 함수 안에서 inputFieldTabMrg.CheckFocus() 함수를 실행해주면 된다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// InputField에서 Tab과 Shift+Tab키를 누르는 것을 관리하는 클래스
/// </summary>
public class InputFieldTabManager
{
    private List<InputField> list;
    private int curPos;

    public InputFieldTabManager()
    {
        list = new List<InputField>();
    }

    /// <summary>
    /// Focus 할 InputField를 설정한다.
    /// </summary>
    /// <param name="idx">Focus 할 InputField의 index 번호</param>
    public void SetFocus(int idx = 0)
    {
        if (idx >= 0 && idx < list.Count)
            list[idx].Select();
    }

    /// <summary>
    /// Tab, Shift+Tab 키를 눌렀을 때 반응 할 InputField를 추가한다.
    /// </summary>
    /// <param name="inputField">추가 할 InputField</param>
    public void Add(InputField inputField)
    {
        list.Add(inputField);
    }

    /// <summary>
    /// 현재 위치를 얻는다.
    /// </summary>
    /// <returns>현재 위치의 Index</returns>
    private int GetCurerntPos()
    {
        for (int i = 0; i < list.Count; ++i)
        {
            if (list[i].isFocused == true)
            {
                curPos = i;
                break;
            }
        }
        return curPos;
    }

    /// <summary>
    /// 다음 InputField로 Focus한다.
    /// </summary>
    private void MoveNext()
    {
        GetCurerntPos();
        if (curPos < list.Count - 1)
        {
            ++curPos;
            list[curPos].Select();
        }
    }

    /// <summary>
    /// 이전 InputField로 Focus한다.
    /// </summary>
    private void MovePrev()
    {
        GetCurerntPos();
        if (curPos > 0)
        {
            --curPos;
            list[curPos].Select();
        }
    }

    /// <summary>
    /// Tab키와 Shift + Tab키를 눌렀는지 체크하여 눌렀으면 Focus를 옮긴다.
    /// </summary>
    public void CheckFocus()
    {
        if (Input.GetKeyDown(KeyCode.Tab) && !Input.GetKey(KeyCode.LeftShift))
        {
            MoveNext();
        }
        if (Input.GetKey(KeyCode.LeftShift) && Input.GetKeyDown(KeyCode.Tab))
        {
            MovePrev();
        }
    }
}

 

혹은 Selctable이용하기.

부모 오브젝트에 넣어주기만 하면 알아서 세팅해줌.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class FocusControll : MonoBehaviour
{
    [SerializeField]
    Selectable[] selectables;

    int selectedIdx = 0;
    Coroutine focusupdate = null;
    private void Awake()
    {
        selectables = GetComponentsInChildren<Selectable>(true);
    }

    private void OnEnable()
    {
        //for(int i =0; i < selectables.Length; i++)
        //{
        //    if(selectables[i].gameObject.activeSelf == true && selectables[i].IsInteractable() == true)
        //    {
        //        selectables[i
        //        break;
        //    }
        //}
        StartCoroutine(SetDefFocus());
        focusupdate = StartCoroutine(FocusUpdate());
    }
    IEnumerator SetDefFocus()
    {
        yield return new WaitForEndOfFrame();
        for (int i = 0; i < selectables.Length; i++)
        {
            if (selectables[i].gameObject.activeSelf == true && selectables[i].IsInteractable() == true)
            {
                selectables[i].Select();
                
                selectedIdx = i;
                break;
            }
        }
    }
    private void OnDisable()
    {
        StopCoroutine(focusupdate);
    }

    IEnumerator FocusUpdate()
    {
        while (true)
        {
            KeyDownBind();
            yield return new WaitForSeconds(0);
        }
    }

    private void KeyDownBind()
    {
        if (Input.GetKey(KeyCode.LeftShift) && Input.GetKeyDown(KeyCode.Tab))
        {
            Debug.Log("backward");
            int idx = 0;
            if (selectedIdx <= 0)
            {
                idx = selectables.Length - 1;
            }
            else
            {
                idx = selectedIdx - 1;
            }
            for (int i = idx; i < selectables.Length; i--)
            {
                if (selectables[i].gameObject.activeSelf == true && selectables[i].IsInteractable() == true)
                {
                    selectables[i].Select();
                    
                    selectedIdx = i;
                    break;
                }
                if (i == 0)
                    i = selectables.Length - 1;
            }
        }
        else if (Input.GetKeyDown(KeyCode.Tab))
        {
            Debug.Log("forward");
            int idx = 0;
            if (selectedIdx >= selectables.Length - 1)
            {
                idx = 0;
            }
            else
            {
                idx = selectedIdx + 1;
            }
            for (int i = idx; i < selectables.Length; i++)
            {
                if (selectables[i].gameObject.activeSelf == true && selectables[i].IsInteractable() == true)
                {
                    selectables[i].Select();
                    
                    selectedIdx = i;
                    break;
                }
            }
            
        }
        else if (Input.GetKeyDown(KeyCode.KeypadEnter)||Input.GetKeyDown(KeyCode.Return))
        {
            Button button = GetComponentInChildren<Button>(true);
            if(button != null)
                button.onClick.Invoke();
        }
    }
}

Sphere끼리 충돌 구현 실험.

 

간단하게 실험을 하기 위해 두 개의 구만 충돌이 일어나게 하였다.

닿았을 때

 

 

 

구의 충돌은 구하기 쉽다. 두 점 사이의 거리가 양쪽 구의 반지름들을 더한 값보다 작으면 충돌했다고 판정된다.

아래는 소스코드.

만드는김에 제곱근, 제곱, 두 점 사이의 거리를 구해주는 함수도 직접 만들어봄.

public class CSphereCollider : MonoBehaviour
{
    public Vector3 pivotPos;
    public float radius; // 반지름

    public GameObject otherSphere;

    private Vector3 otherPivotPos;
    private float otherRadius; // 상대방 반지름
    
    // Update is called once per frame
    void Update()
    {
        transform.localScale = new Vector3(radius*2, radius*2, radius*2);
        pivotPos = gameObject.transform.position;
        Vector3 otherPivotPos = otherSphere.gameObject.transform.position;
        otherRadius = otherSphere.gameObject.GetComponent<CSphereCollider>().radius;

            
        CheckCollider(pivotPos, otherPivotPos);
    }

    public void CheckCollider(Vector3 pos1, Vector3 pos2)
    {
        float distance = GetDistance(pos1, pos2);
        // 충돌이 일어났다.
        if (distance < radius + otherRadius)
        {
            Debug.Log("충돌이 일어남");
        }
    }

    // 유클리드 거리, 두 점 사이의 거리를 구한다.
    public float GetDistance(Vector3 currentPos, Vector3 targetPos)
    {
        float distance;
        float _x = targetPos.x - currentPos.x;
        float _y = targetPos.y - currentPos.y;
        float _z = targetPos.z - currentPos.z;
        distance = GetSQRT(GetPow(_x, 2) + GetPow(_y, 2) + GetPow(_z, 2));

        return distance;
    }

    /// <summary>
    /// 제곱을 구해준다.
    /// </summary>
    /// <param name="number">숫자</param>
    /// <param name="n">지수</param>
    /// <returns>제곱된 수</returns>
    public float GetPow(float number, int n)
    {
        float value = 1;
        for(int i=0;i  < n; ++i)
        {
            value *= number;
        }
        return value;
    }

    // 제곱근을 구해준다.
    public float GetSQRT(float value)
    {
        // 바빌로니아 법을 이용한다.
        float x = 3; // 임의의 값.
        
        // 정확도가 높아질때까지 반복
        for(int i =0;i < 10; ++i)
        {
            x = (x + (value / x)) / 2;
        }
        return x;
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(pivotPos, radius);
    }
}

 

+ Recent posts