- 목차
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 빌드 오류
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에서 프로젝트를 열면서 발생.
- 프로젝트 파일이 있는 곳으로 이동
-
.csproj 파일 및 .sln 파일 삭제
- 필요에 따라서 다른 폴더도 삭제해도 된다. (Temp, obj, Logs, Library 등)
- Unity 실행
- Edit → Preferences → External Tools → External Script Editor 에서 자신이 사용하는 편집 툴로 변경
- 아래쪽에 있는 “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);
}
(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 비교)
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 등록
-180 ~ 180 → 0 ~ 360
그냥 음수 값일 때 +360을 하자…
Prefab
Prefabricated Object의 약어
- Prefabricate : 미리 제조하다, 조립식으로 만들다.
- Prefabricated : 조립식인
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 유지
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
- A != null : Unity의 null 오버로딩을 따르며, Unity 메모리 상태를 고려한 null 체크. 엔진에서 객체의 파괴 상태를 null로 간주.
- A is not null : C#의 일반적인 null 체크. 다만 객체 참조가 메모리에 남아있으면 true을 반환.
Unity Object Null 비교 관련 참고 문서
참고 링크
- https://discussions.unity.com/t/bool-gameobject-vs-gameobject-null/686968
- https://discussions.unity.com/t/is-null-returns-false-on-null-objects/821933
- https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Scripting/UnityEngineObject.bindings.cs#L118-L135
- https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Scripting/UnityEngineObject.bindings.cs#L615-L617
- https://velog.io/@bedshanty/%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90%EC%84%9C-.-%EB%A5%BC-%EC%93%B0%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0
Destroy VS DestroyImmediate
DestroyImmediate
- 즉시 파괴 : 대상 객체를 메모리에서 즉시 제거.
- 에디터 스크립트에서 활용. (런타임에 사용하기에는 위험)
Destroy
- 지연 파괴 : 다음 프레임이 시작되기 전에 파괴
- 대상이 GameObject : 해당 GameObject (붙어 있는 Component) 및 하위 자식 객체들도 제거
- 대상이 특정 Component : 해당 Component만 제거
- 하위 자식을 선택적으로 제거하고 싶으면? : gameobject.transform을 이용하여 찾아 들어가서 제거
Destory가 되지 않는 경우
- DontDestroyOnLoad 처리된 GameObject인 경우에는 명시적으로 호출해야 Destory 동작.
- 다른 곳에서 참조 및 의존성 (시스템, 이벤트, Coroutine)
- 비활성화 된 부모 객체
- 이미 파괴됨
주의!
위에 Null 관련 내용에도 언급된 내용에 따 Destroy를 한 이후에도 Null 처리를 해야 된다.PlayerPrefs
- MainThread에서 실행할 것
- 값 설정 후에는 반드시 Save 함수 호출
-
로드한 값은 별도의 변수에 저장한 후, 해당 변수를 활용하자. UI 같은 곳에 다이렉트로 사용하는 경우 간혹 값이 적용되지 않는 문제가 있다.
12345// 별도 변수에 저장var intValue = PlayerPrefs.GetInt("IntKey", 0);// intValue를 활용cs
컴포넌트 초기화
GetComponent 같이 컴포넌트를 가져오는 함수는 가능한 Start 함수에서 호출 할
것
Awake 함수에서 해당 컴포넌트가 초기화 되지 않을 수도 있다.
댓글
댓글 쓰기