기본 콘텐츠로 건너뛰기

[Unity] Tip, Error, Warning

    목차
찾기 귀찮아 쓰는 내용


Matrix4x4

  • Unity의 행렬은 Column Major 이다.
    • [0] ~ [15]의 1차원 인덱스를 이용하여 프로퍼티를 접근 하는 경우, Column 순서대로 순차적으로 참고하게 된다.
    • Vector4 4개를 입력받는 생성자를 사용하는 경우. Column순으로 입력한다. (아래 이미지에서 왼쪽부터 세로 줄부터 입력한다고 생각하면된다.)
  • m00 ~ m33 의 프로퍼티에서 숫자 부분은 (row, col) 의 해당하는 인덱스라고 생각하면 된다.
    • 자료 유형은 float
    • ToString 함수를 사용하는 경우 아래 이미지처럼 프로퍼티 값을 출력한다.
    
        //예: Row2, Col3 요소
        Matrix4x4 temp; // 그냥 아무렇게 선언한 임시 변수
        temp.m23; //방법 1
        temp[14]; //방법 2 (인덱스 계산은 row + col * 4 = 2 + 3 * 4)
        
        // ////////////////////////////////
        
        Row 0 : Vector4(m00, m01, m02, m03)
        Row 1 : Vector4(m10, m11, m12, m13)
        Row 2 : Vector4(m20, m21, m22, m23)
        Row 3 : Vector4(m30, m31, m32, m33)
        
        Column 0 : Vector4(m00, m10, m20, m30)
        Column 1 : Vector4(m01, m11, m21, m31)
        Column 2 : Vector4(m02, m12, m22, m32)
        Column 3 : Vector4(m03, m13, m23, m33)
        
        // ////////////////////////////////////////////
        // 하단 함수는 Matrix에서 TRS 각 요소를 가져오는 함수 (검증하지 않음)
        // https://forum.unity.com/threads/how-to-assign-matrix4x4-to-transform.121966/
        
        public static Quaternion ExtractRotation(this Matrix4x4 matrix)
        {
            Vector3 forward;
            forward.x = matrix.m02;
            forward.y = matrix.m12;
            forward.z = matrix.m22;
        
            Vector3 upwards;
            upwards.x = matrix.m01;
            upwards.y = matrix.m11;
            upwards.z = matrix.m21;
        
            return Quaternion.LookRotation(forward, upwards);
        }
        
        public static Vector3 ExtractPosition(this Matrix4x4 matrix)
        {
            Vector3 position;
            position.x = matrix.m03;
            position.y = matrix.m13;
            position.z = matrix.m23;
            return position;
        }
        
        public static Vector3 ExtractScale(this Matrix4x4 matrix)
        {
            Vector3 scale;
            scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
            scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
            scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
            return scale;
        }
        
        // /////////////////////////////////////////////////////
        // 하단 함수는 TRS 수동 제작 관련 코드 (검증하지는 않음) /////////////////////////
        // https://www.reddit.com/r/Unity3D/comments/flwreg/comment/fl1lvii/?utm_source=share&utm_medium=web2x&context=3
        
        public static float ConvertDegToRad(float degrees)
        {
            return ((float)Math.PI / (float) 180) * degrees;
        }
        
        public static Matrix4x4 GetTranslationMatrix(Vector3 position)
        {
            return new Matrix4x4(new Vector4(1, 0, 0, 0),
                                 new Vector4(0, 1, 0, 0),
                                 new Vector4(0, 0, 1, 0),
                                 new Vector4(position.x, position.y, position.z, 1));
        }
        
        public static Matrix4x4 GetRotationMatrix(Vector3 anglesDeg)
        {
            anglesDeg = new Vector3(ConvertDegToRad(anglesDeg[0]), ConvertDegToRad(anglesDeg[1]), ConvertDegToRad(anglesDeg[2]));
        
            Matrix4x4 rotationX = new Matrix4x4(new Vector4(1, 0, 0, 0), 
                                                new Vector4(0, Mathf.Cos(anglesDeg[0]), Mathf.Sin(anglesDeg[0]), 0), 
                                                new Vector4(0, -Mathf.Sin(anglesDeg[0]), Mathf.Cos(anglesDeg[0]), 0),
                                                new Vector4(0, 0, 0, 1));
        
            Matrix4x4 rotationY = new Matrix4x4(new Vector4(Mathf.Cos(anglesDeg[1]), 0, -Mathf.Sin(anglesDeg[1]), 0),
                                                new Vector4(0, 1, 0, 0),
                                                new Vector4(Mathf.Sin(anglesDeg[1]), 0, Mathf.Cos(anglesDeg[1]), 0),
                                                new Vector4(0, 0, 0, 1));
        
            Matrix4x4 rotationZ = new Matrix4x4(new Vector4(Mathf.Cos(anglesDeg[2]), Mathf.Sin(anglesDeg[2]), 0, 0),
                                                new Vector4(-Mathf.Sin(anglesDeg[2]), Mathf.Cos(anglesDeg[2]), 0, 0),
                                                new Vector4(0, 0, 1, 0),
                                                new Vector4(0, 0, 0, 1));
        
            return rotationX * rotationY * rotationZ;
        }
        
        public static Matrix4x4 GetScaleMatrix(Vector3 scale)
        {
            return new Matrix4x4(new Vector4(scale.x, 0, 0, 0),
                                 new Vector4(0, scale.y, 0, 0),
                                 new Vector4(0, 0, scale.z, 0),
                                 new Vector4(0, 0, 0, 1));
        }
        
        public static Matrix4x4 Get_TRS_Matrix(Vector3 position, Vector3 rotationAngles, Vector3 scale) 
        {
            return GetTranslationMatrix(position) * GetRotationMatrix(rotationAngles) * GetScaleMatrix(scale);
        }
        
        
        

    Screen 회전 행렬 (Android) - 검증 필요

    • Portrait : Matrix4x4.Rotate(Quaternion.Euler(0, 0, -90))
      0.0000 1.0000 0.0000 0.0000
      -1.0000 0.0000 0.0000 0.0000
      0.0000 0.0000 1.0000 0.0000
      0.0000 0.0000 0.0000 1.0000

    • LandscapeLeft : Matrix4x4.Rotate(Quaternion.identity)
      1.0000 0.0000 0.0000 0.0000
      0.0000 1.0000 0.0000 0.0000
      0.0000 0.0000 1.0000 0.0000
      0.0000 0.0000 0.0000 1.0000

    • PortraitUpsideDown : Matrix4x4.Rotate(Quaternion.Euler(0, 0, 90))
      0.0000 -1.0000 0.0000 0.0000
      1.0000 0.0000 0.0000 0.0000
      0.0000 0.0000 1.0000 0.0000
      0.0000 0.0000 0.0000 1.0000

    • LandscapeRight : Matrix4x4.Rotate(Quaternion.Euler(0, 0, 180))
      -1.0000 0.0000 0.0000 0.0000
      0.0000 -1.0000 0.0000 0.0000
      0.0000 0.0000 1.0000 0.0000
      0.0000 0.0000 0.0000 1.0000




    빌드 오류

    • Editor 코드 확인 해보기
    • apk 파일 삭제하고 재시도

    Unity → Android 빌드 오류


    오류 예시 
    No Android Devices Connected (무선 디버깅 실행 중이면, 무선 디버깅이 해제되면서 메시지를 띄움)

      Unity Editor의 Preferences 설정에서 Android SDK를 내장 SDK(Unity Editor 설치 시 제공하는 파일)를 사용하는 것으로 설정했다면, 현재 작업 관리자에서 실행되고 있는 adb.exe 프로세스가 Unity에서 제공하는 adb인지 확인해봐야 한다.
    Android Studio의 adb.exe가 Unity의 adb.exe와 같이 실행 또는 대신 실행될 수도 있다.

    해결 방법 
    • Android 기능을 지원하는 모든 IDE 종료
      • Unity도 같이 종료
      • Visual Studio, Rider 등
    • adb.exe 강제 종료
      • "작업 관리자" → "프로세스" 또는 "작업 관리자" → "자세히"
    • Unity Editor 먼저 실행
    • 실행되고 있는 adb.exe 확인
      • "작업 관리자" → "프로세스" 또는 "작업 관리자" → "자세히"
      • adb.exe 우클릭 → 파일 위치 열기
      • 탐색기 경로가 Unity가 제공하는 adb.exe면 성공

    Android gradle build failed

    • 프로젝트 경로에 한글이 있으면 안된다.
    • Unity Editor에서 “Edit” → “Preferences” → “External Tools” → “Android” 부분에서 각 체크 박스의 체크를 해제했다가 다시 체크 (Unity Editor에서 경로 설정 관련 기능이 반영안되는 버그가 간혹있음)



    Layer, LayerMask

    두 녀석 모두 int32로 표현하기 때문에 구분을 잘해야한다.

    • Layer : 말그대로 Layer. 단일 Layer 번호만 의미한다.
      • GameObject Layer 설정시 활용
    • LayerMask : Bit 연산. 여러개의 Layer를 넣을 수 있다.
      • Raycast 파라미터로 들어간다.

    <예시>

    LayerName Layer LayerMask
    ABC 9 1 << 9
    DEF 10 1 << 10
    GHI 11 1 << 11
    int layerMask1
        = 1 << LayerMask.NameToLayer("DEF"); // NameToLayer 는 Layer 번호만 가져온다.
        = 1 << 10
        = LayerMask.GetMask("DEF");
        
        //임의의 Layer를 이용하여 마스크 만들기
        int layerMask2
        = 1 << LayerMask.NameToLayer("ABC") | 1 << LayerMask.NameToLayer("GHI");
        = 1 << 9 | 1 << 11
        = LayerMask.GetMask("ABC","GHI");
        

    값을 잘못 넣으면 아래와 같은 오류가 뜨니 조심.

    Unity A game object can only be in one layer. The layer needs to be in the range [0…31]

    <참고>

    https://penguinofdev.tistory.com/57
    https://m.blog.naver.com/pxkey/221324326124




    cannot resolve symbol ‘UnityEngine’

    본인의 경우 재설치한 PC에서 프로젝트를 열면서 발생.

    1. 프로젝트 파일이 있는 곳으로 이동
    2. .csproj 파일 및 .sln 파일 삭제
      • 필요에 따라서 다른 폴더도 삭제해도 된다. (Temp, obj, Logs, Library 등)
    3. Unity 실행
    4. Edit → Preferences → External Tools → External Script Editor 에서 자신이 사용하는 편집 툴로 변경
    5. 아래쪽에 있는 “Regenerate project files” 버튼 또는 Assets → Open C# Project

    https://forum.unity.com/threads/solved-unity-not-generating-sln-file-from-assets-open-c-project.538487/
    https://forum.unity.com/threads/cannot-resolve-symbol-rider-windows-resolve-symbols-between-editor-and-runtime-assemblies.866317/



    Quaternion 차이 계산, 적용

    
        /// <summary>
        /// 순서 주의!!!
        /// Get FROM : to * Quaternion.Inverse(diff)<br/>
        /// Get TO : from * diff
        /// </summary>
        /// <param name="from">시작 기준점. Global rotation</param>
        /// <param name="to">목표 지점. Local rotaton</param>
        /// <returns>"diff"</returns>
        public static Quaternion GetQuaternionDiff(Quaternion from, Quaternion to)
        {
            //Diff
            return Quaternion.Inverse(from) * to;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="from"></param>
        /// <param name="diff"></param>
        /// <returns>"to"</returns>
        public static Quaternion ApplyQuaternion(Quaternion from, Quaternion diff)
        {
            return from * diff;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="to"></param>
        /// <param name="diff"></param>
        /// <returns>"from"</returns>
        public static Quaternion RevertQuaternion(Quaternion to, Quaternion diff)
        {
            return to * Quaternion.Inverse(diff);
        }
        
        

    https://forum.unity.com/threads/get-the-difference-between-two-quaternions-and-add-it-to-another-quaternion.513187/#post-8187513



    (Mesh용) 삼각형 인덱스 생성

    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="clockwise">false : counterclockwise</param>
        /// <returns>인덱스 3개씩 묶으면 삼각형, 6개를 묶으면 사각형이 된다. Mesh.SetTriangles 함수에 적용하면 된다.</returns>
        public static int[] GenerateTriangles(int width, int height, bool clockwise = true)
        {
            var widthTemp = width - 1;
            var heightTemp = height - 1;
            var indices = new int[heightTemp * widthTemp * 6];
            var idx = 0;
        
            if (clockwise)
            {
                for (var y = 0; y < heightTemp; y++)
                for (var x = 0; x < widthTemp; x++)
                {
                    //// Unity has a clockwise triangle winding order.
                    //// Upper quad triangle
                    //// Top left
                    int idx0 = (y * width) + x;
                    //// Top right
                    int idx1 = idx0 + 1;
                    //// Bottom left
                    int idx2 = idx0 + width;
        
                    //// Lower quad triangle
                    //// Top right
                    int idx3 = idx1;
                    //// Bottom right
                    int idx4 = idx2 + 1;
                    //// Bottom left
                    int idx5 = idx2;
        
                    indices[idx++] = idx0;
                    indices[idx++] = idx1;
                    indices[idx++] = idx2;
                    indices[idx++] = idx3;
                    indices[idx++] = idx4;
                    indices[idx++] = idx5;
                }
            }
            else
            {
                for (var y = 0; y < heightTemp; y++)
                for (var x = 0; x < widthTemp; x++)
                {
                    //// Counter - clockwise triangle winding order.
        
                    int idx0 = (y * width) + x;
                    int idx1 = idx0 + 1;
                    int idx2 = idx0 + width;
        
                    int idx3 = idx1;
                    int idx4 = idx2 + 1;
                    int idx5 = idx2;
        
                    indices[idx++] = idx0;
                    indices[idx++] = idx2;
                    indices[idx++] = idx1;
                    indices[idx++] = idx3;
                    indices[idx++] = idx5;
                    indices[idx++] = idx4;
                }
            }
        
            return indices;
        }
        
        


    UnityWebRequest “Https” 검사 우회

    
        /// <summary>
        /// To ignore certification of https
        /// </summary>
        public sealed class BypassCertificate : CertificateHandler
        {
            protected override bool ValidateCertificate(byte[] certificateData)
            {
                //Simply return true no matter what
                return true;
            }
        }
        
        ...
        // 사용방법 예시
        
        var req = UnityWebRequest.Get("원하는 URL");
        req.certificateHandler = new BypassCertificate();
        
        



    float 비교 (Vector 비교)

    Mathf.approximately

    https://discussions.unity.com/t/vector3-comparison-efficiency-and-float-precision/62649



    자식 오브젝트의 방향 축을 기준으로 부모, 자식 오브젝트 동시 이동

    부모와 자식 오브젝트가 있고… 자식 오브젝트는 로테이션이 살짝 돌아가 있다고 가정하자…
    나는 키보드를 이용하여 부모와 자식 오브젝트를 동시 이동하는 것을 설계해야하는데,
    이동하는 방향 축을 자식 오브젝트의 것을 사용해야한다…

    부모 오브젝트를 포커스로 설정하고 오브젝트 이동을 하면, 자식도 같이 이동이 되는 것 까지는 좋다…
    근데 이동축을 부모의 것을 써서 문제지…

    이 경우 부모의 translate에 사용되는 벡터에다가 자식 오브젝트의 로테이션을 곱하면 된다.
    parent.transform.Translate(child.transform.rotation * 이동 방향 벡터)



    Exe로 추출한 Unity 프로그램의 텍스쳐가 로드 되지 않는 현상 (분홍색)

    • Unity Editor에서 Edit → Project Settings → Graphics
    • 프로그램에서 사용하는 Shader 등록

    https://stackoverflow.com/a/36909798/7017299



    -180 ~ 180 → 0 ~ 360

    그냥 음수 값일 때 +360을 하자…

    https://forum.unity.com/threads/solved-360-angle.596185/



    Prefab

    Prefabricated Object의 약어

    • Prefabricate : 미리 제조하다, 조립식으로 만들다.
    • Prefabricated : 조립식인
      Prefab에서의 SerializeField는 Prefab 그룹 안에 정의된 녀석만 지정이 가능하고 외부의 컴포넌트는 지정 불가. (관련 링크). FindObjectOfType 함수나 GetComponents 함수를 활용할 것.



    Camera

    Sprite 크기에 딱 맞게 Orthographic 카메라를 화면크기와 맞게 보이게 size 값을 설정하는 방법

    // https://youtu.be/3xXlnSetHPM?t=351
        public void SetCameraSize()
        {
            SpriteRenderer rink = GetComponent<SpriteRenderer>();
            
            float screenRatio = (float) Screen.width / (float) Screen.height;
            float targetRatio = rink.bounds.size.x / rink.bounds.size.y;
            
            float size = rink.bounds.size.y / 2;	
                
            if(screenRatio < targetRatio){
                size *= targetRatio / screenRatio; 
            }
            
            // 사이즈를 맞출 원하는 카메라로 지정하면 된다. 여기서는 Main 카메라로 설정
            Cameara.main.orthographicSize = size;	
        }
        
        


    Refactoring 시 SerializeField 유지

    프로퍼티 위에 [FormerlySerializedAs(oldname)] 사용 
    (Rider Refactoring시에 자동으로 붙여준다.)

    gitignore

    • unity의 경우 meta 파일이 무시되지 않도록 주의!
      • gitignore 자동 생성 사이트에서 Unity의 경우 meta 파일이 무시되지 않지만, Visual studio의 경우 meta 파일이 무시된다. meta 파일 정보가 없다면, missing prefab 에러가 뜰 수 있다.

    UI 요소 (Button, Toggle, Slider)들이 터치에 반응이 없는 경우

    • Event System GameObject를 생성한다.
      • UI 부분에 있음
    • Canvas 컴포넌트가 있는 부분에 Canvas Raycaster, Canvas Group 컴포넌트를 추가하여 Raycast를 조정한다.
    • GameObject(버튼이나 토글)의 Layer를 "UI"로 바꿔본다.

    "UnityEngine.Object" 관련 객체들의 Null Check


      Unity는 "UnityEngine.Object"를 기반으로 한 클래스(예 : MonoBehaviour)에 대해 null 오버로딩을 제공한다.
      Unity는 C#의 기본 null과 다르게 파괴된(Destroyed) 객체를 null로 간주한다.
      주의할 점이 "Destroy(A)"를 실행하더라도 C# 입장에서 객체 참조가 남아있을 수도 있다.

    • A != null : Unity의 null 오버로딩을 따르며, Unity 메모리 상태를 고려한 null 체크. 엔진에서 객체의 파괴 상태를 null로 간주.
    • A is not null : C#의 일반적인 null 체크. 다만 객체 참조가 메모리에 남아있으면 true을 반환.
    "UnityEngine.Object" 관련 클래스의 null check는 "A != null" 형식이 권장된다. (퍼포먼스 때문에 ReferenceEquals를 써는 경우도 있다...)
    물론 일반적인 클래스의 "A != null" 와 "A is not null"은 동일하게 동작한다.



    Destroy VS DestroyImmediate


    DestroyImmediate

    • 즉시 파괴 : 대상 객체를 메모리에서 즉시 제거.
    • 에디터 스크립트에서 활용. (런타임에 사용하기에는 위험)

    Destroy

    • 지연 파괴 : 다음 프레임이 시작되기 전에 파괴
    • 대상이 GameObject : 해당 GameObject (붙어 있는 Component) 및 하위 자식 객체들도 제거
    • 대상이 특정 Component : 해당 Component 제거
    • 하위 자식을 선택적으로 제거하고 싶으면? : gameobject.transform을 이용하여 찾아 들어가서 제거

    Destory가 되지 않는 경우

    • DontDestroyOnLoad 처리된 GameObject인 경우에는 명시적으로 호출해야 Destory 동작.
    • 다른 곳에서 참조 및 의존성 (시스템, 이벤트, Coroutine)
    • 비활성화 된 부모 객체
    • 이미 파괴됨

    주의!

    위에 Null 관련 내용에도 언급된 내용에 따 Destroy를 한 이후에도 Null 처리를 해야 된다.


    PlayerPrefs

    • MainThread에서 실행할 것
    • 값 설정 후에는 반드시 Save 함수 호출
    • 로드한 값은 별도의 변수에 저장한 후, 해당 변수를 활용하자. UI 같은 곳에 다이렉트로 사용하는 경우 간혹 값이 적용되지 않는 문제가 있다.

      1
      2
      3
      4
      5
      // 별도 변수에 저장
       
      var intValue = PlayerPrefs.GetInt("IntKey"0);
       
      // intValue를 활용
      cs

    컴포넌트 초기화

    GetComponent 같이 컴포넌트를 가져오는 함수는 가능한 Start 함수에서 호출 할 것
    Awake 함수에서 해당 컴포넌트가 초기화 되지 않을 수도 있다.






















    댓글