Skip to content

Instantly share code, notes, and snippets.

@Curookie
Last active February 11, 2025 03:49
Show Gist options
  • Save Curookie/42c979a7de7d656ec8cf6b8c01ea0457 to your computer and use it in GitHub Desktop.
Save Curookie/42c979a7de7d656ec8cf6b8c01ea0457 to your computer and use it in GitHub Desktop.
Unity 유니티 실무2
유니티 실무1 - https://gist.github.com/Curookie/5de19e581eb54cff7d7b643408ba930c
----- 진짜 정말 많이 쓰는 3가지 프레임워크 코드 공유 -----
1. 트랜스 폼 아래에 모든 게임오브젝트 제거
public static void DestroyAllChildren(this Transform tran) {
var children = new List<GameObject>();
for (var i = 0; i < tran.childCount; i++) {
children.Add(tran.GetChild(i).gameObject);
}
var failsafe = 0;
while (children.Count > 0 && failsafe < 200) {
var child = children[0];
GameObject.DestroyImmediate(child);
if (children[0] == child) {
children.RemoveAt(0);
}
failsafe++;
}
}
2. 해당 트랜스폼의 가장 가까운 부모 캔버스 찾는 거
public static Canvas GetTopmostCanvas(this Transform tran) {
Canvas[] parentCanvases = tran.GetComponentsInParent<Canvas>();
if (parentCanvases != null && parentCanvases.Length > 0) {
return parentCanvases[parentCanvases.Length - 1];
}
return null;
}
3. 스크린 값 (마우스 포인터) Vector2가 있을경우 World 좌표로 자동변환 단 변환할 좌표 트랜스폼을 제시
public static Vector3? ScreenToWorld(this Vector2 screenPos_, Transform targetTrans_) {
var targetCanvas_ = targetTrans_.GetTopmostCanvas();
Vector3? worldCursorPos_ = null;
if(targetCanvas_) {
if(targetCanvas_.renderMode == RenderMode.ScreenSpaceOverlay) {
worldCursorPos_ = screenPos_;
} else {
worldCursorPos_ = targetCanvas_.worldCamera.ScreenToWorldPoint(new Vector3(screenPos_.x, screenPos_.y, targetCanvas_.planeDistance));
}
} else { }
return worldCursorPos_;
}
----- URP 2D, Overlay Camera에서 2D Light가 적용이 안되는 이슈 (2022.3.34f1)-----
이슈 :
메인 Base 캠은 1,2,3 레이어 Culling
Overlay 캠은 4 레이어를 Culling 했는데
1번 레이어에 있는 2D Light - Global 효과가
SortingLayer설정과 상관없이 Overlay캠에 적용된 4번 레이어에 적용이 안됨.
해결법 : 2D Light를 하나더 만들어서 2D Light 오브젝트 레이어를 같은 레이어(4번 레이어)로 변경시 됨.
----- 코드로 게임 오브젝트 생성시 ------
new GameObject("Contents", typeof(CanvasRenderer), typeof(RectTransform))
이런식으로 생성하면 컴포넌트도 쉽게 붙일 수 있다.
------ RectTransform 가져오는 다른 방법 ------
보통 transform.GetComponent<RectTransform>() 으로 가져오는데
transform as RectTransform 으로 가져올수도 있다.
(transform as RectTransform).블라블라 이런식도 가능
------ 커스텀 디버거 Custom Debug ------
[HideInCallstack] 콜백에 커스텀 디버깅 코드가 스태킹되서 콘솔 볼때 스트레스 받을 때 쓰는 어트리뷰트가 있었는데 2022.3 에서 지원 안한다.
그대신 콘솔 창에 햄버거 아이콘 누르고 Strip logging stacking 체크해주면 자동으로 커스텀 디버깅 스태킹이 사라짐.
[예시]
Common : AAAA
UnityEngine.Debug:Log (object)
GameFrameworks.DBug:<Log>g___Log|6_0 (GameFrameworks.DBug/<>c__DisplayClass6_0&) (at Assets/Plugins/GameFrameworks/Utility/DBug.cs:53)
GameFrameworks.DBug:Log (string,GameFrameworks.DBug/PrintLogType) (at Assets/Plugins/GameFrameworks/Utility/DBug.cs:42)
TokaTonTon.GameSceneManager:OnEnable () (at Assets/02_Script/Manager/GameSceneManager.cs:54)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
Common : AAAA
UnityEngine.Debug:Log (object)
TokaTonTon.GameSceneManager:OnEnable () (at Assets/02_Script/Manager/GameSceneManager.cs:54)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
------ Tilemap에서 반 칸짜리 타일(Half Tile) 구분할때 유용한 방법 ------
이름을 텍스쳐 이름을 HALF_ 이런식으로 넣고 이름으로 구분하는거 유용하다. TilemapCollider2D 개별 체크안됨.
foreach (var position in bounds.allPositionsWithin)
{
Vector3Int tilePosition = new Vector3Int(position.x, position.y, 0);
if (tilemap.HasTile(tilePosition))
{
bool isHalf = false;
Tile tile = tilemap.GetTile<Tile>(position);
if (tile)
{
if (tile.name != null && tile.name.Contains("HALF"))
{
isHalf = true;
} else if (tile.sprite != null && tile.sprite.name.Contains("HALF"))
{
isHalf = true;
}
}
Vector3Int aboveTilePosition = new Vector3Int(tilePosition.x, tilePosition.y + (isHalf ? 0 : 1), 0);
if (!tilemap.HasTile(aboveTilePosition)||isHalf)
{
Vector3 worldPosition = tilemap.CellToWorld(aboveTilePosition);
Vector3Int localPosition = tilemap.layoutGrid.WorldToCell(worldPosition);
walkableTiles.Add((Vector2Int)localPosition);
}
}
------ Post Processing 스크립트로 제어할 때 주의할 점 ------
1. _dOF.focalLength.value = 0;
2. _dOF.focalLength = new ClampedFloatParameter(0, 1, 300, true);
두 가지 방안이 있는데
이렇게 제어할 때는 반드시 min max 값이 inspector의 min max값과 동일해야한다.
아래방식대로 적용할때는 override가 필요할경우 즉시 반영해야할경우 아래 방식으로 true해서 처리해야함.
종류마다 1, 2 스크립팅을 다르게 해야할 필요가 있어보임. Depth of Field는 1번방식으로만 적용됨. (2022.3.34f1 기준)
------ Timeline Pasue 할때 이미 완료되서 Hold된 애니메이션 초기화 되는 현상 막는법 -----
pD_Director.Pause();
pD_Director.RebuildGraph();
정지하고 직후 Graph를 Rebuild시켜준다.
------ CinemachineVirutalCamera Noise 안먹을 때! ----
컴포넌트 달려있는 오브젝트를 복제해서 생성할 경우 Noise가 안먹는 상황이 발생했다. (2022.3.34f1 버전 기준)
Body 컴포넌트 None , Noise 컴포넌트 None으로 바꾼다음 다시 설정하면 제대로 먹힘.
------ 픽셀 게임 만들때 Texture 설정 -------
PPU - 32나 64
Filter Mode - Point
Compression - None
------ area.bounds.Contains() 2DCollider의 bounds.Contains 함수가 제대로 작동안될때 ------
2DCollider.bounds.Contains(Vector3) 로 체크할때 분명히 범위안에 있는데 false가 뜨는경우
z값을 2DCollider의 z값으로 설정하면 제대로 먹힘
ex)
잘못된 예 - area&&target&&area.bounds.Contains(target.transform.position);
옳은 예 - area&&target&&area.bounds.Contains(new Vector3(target.transform.position.x, target.transform.position.y, area.transform.position.z));
----- Awake보다 빨리 실행시키는 방법 ------
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void PreAwake() {
}
스태틱함수로 만들어야함.
----- 컴파일 제외 꿀팁 ------
안쓰는 스크립트를 컴파일 제외 시키고싶을때 폴더 명 앞에 ~ 를 붙여주면 완전히 무시할 수 있다.
----- Master Audio BGM 팁 -----
var track1Playing = PlaylistController.InstanceByName("BGM_Track1")?.ActiveAudioSource?.clip?.name ?? "";
BGM 켜져 있는지 체크
----- Assembly Definition 사용 시 매우매우 주의할점 ------
기본적으로 Auto Referecnce 라는 체크박스가 켜져있는데 내가 일반적으로 컴파일안할거면 절대 꺼놔야한다!!! 켜놓으면
그냥 기존에 컴파일하던대로 자동으로 컴파일 과정에 포함되서 쓰는 의미가 없다.
ex) example 붙은 Assembly Definition - Auto Reference 반드시 끄기
+ Define Constraints에 UNITY_INCLUDE_TESTS 넣어서 제외 시키기
+ Include Platforms에 QNX 이런거만 체크해서 제외 시키기
----- 에디터에서 isPlaying이 아닐때 PlayMode 아닐때 tranform 을 코드로 바꿀경우 적용이 안될 수 있는데 이때 ----
delayCall 로 변경하면 적용됨
ex)
#if UNITY_EDITOR
EditorApplication.delayCall += () => {
sR_Main.transform.localPosition = new Vector3(Building.GetOffset.x, Building.GetOffset.y, 0);
};
if(!Application.isPlaying) {
EditorUtility.SetDirty(sR_Main.gameObject);
SceneView.RepaintAll();
}
#else
sR_Main.transform.localPosition = new Vector3(Building.GetOffset.x, Building.GetOffset.y, 0);
#endif
----- 유니티 프로젝트 에디터 버전 체크하는 법 ------
ProjectSettings/ProjectVersion.txt 에서 확인 가능하다.
----- 에디터 코드 꿀팁 HelpBox 잘 활용하면 매우 쉽고 빠르게 인스펙터 이쁘게 꾸밀 수 있다. (기본 디자인이 둥근 사각형이라 좋음)------
예시) Editor 코드
protected void InitializeStyles()
{
foldoutStyle = new GUIStyle(EditorStyles.foldout)
{
fontStyle = FontStyle.Bold,
margin = new RectOffset(15, 0, 4, 4),
border = new RectOffset(1, 1, 1, 1),
};
headerStyle = new GUIStyle(EditorStyles.helpBox)
{
padding = new RectOffset(10, 10, 8, 8),
margin = new RectOffset(4, 4, 4, 4),
border = new RectOffset(1, 1, 1, 1),
};
contentBoxStyle = new GUIStyle(EditorStyles.helpBox)
{
padding = new RectOffset(10, 10, 5, 5),
margin = new RectOffset(0, 0, 4, 4),
border = new RectOffset(1, 1, 1, 1),
};
vector2Style = new GUIStyle(EditorStyles.numberField)
{
fixedWidth = 150,
fontSize = 12,
fontStyle = FontStyle.Bold
};
originalBGColor = GUI.backgroundColor;
}
protected virtual void MainSettings() {
if (!stylesInitialized) {
InitializeStyles();
stylesInitialized = true;
}
EditorGUILayout.PropertyField(_SO_Creature, new GUIContent("프리셋"));
DrawSpritePreview();
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f);
EditorGUILayout.BeginVertical(headerStyle);
GUI.backgroundColor = originalBGColor;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("현재 속도:", GUILayout.Width(70));
GUILayoutOption[] options = { GUILayout.Width(180) };
velocity.vector2Value = EditorGUILayout.Vector2Field("", velocity.vector2Value, options);
GUILayout.FlexibleSpace();
if (GUILayout.Button(isAllHide ? "Show All" : "Hide All", GUILayout.Width(70))) {
isAllHide = !isAllHide;
ToggleFoldouts(isAllHide);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f);
EditorGUILayout.BeginVertical(headerStyle);
GUI.backgroundColor = originalBGColor;
showBaseSettings = EditorGUILayout.Foldout(showBaseSettings, "기본", true, foldoutStyle);
if(showBaseSettings) {
EditorGUILayout.BeginVertical(contentBoxStyle);
DrawOptionalProperty(applyMass, mass, "무게", "1 - 10");
DrawOptionalProperty(applyFriction, friction, "마찰력", "0 - 1");
DrawOptionalProperty(applyBounce, bounciness, "반동력", "0 - 1");
DrawOptionalProperty(applyPush, null, "밀림 여부", "False or True");
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f);
EditorGUILayout.BeginVertical(headerStyle);
GUI.backgroundColor = originalBGColor;
showDetailSettings = EditorGUILayout.Foldout(showDetailSettings, "세부 설정", true, foldoutStyle);
if(showDetailSettings) {
EditorGUILayout.BeginVertical(contentBoxStyle);
EditorGUILayout.PropertyField(serializedObject.FindProperty("gravityModifier"), new GUIContent("중력 가산치"));
EditorGUILayout.Space(10);
EditorGUILayout.PropertyField(serializedObject.FindProperty("minGroundNormalY"), new GUIContent("지면착지 판정각"));
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
----- 태그 비교 최적화 -----
transform.gameObject.tag == "AA" 하는 것보다 transform.CompareTag("AA") 하는게 조금 더 이점이 있다.
----- collider.Cast 함수 -----
collider의 가장자리에서 특정 방향 및 거리에 떨어진 Collider를 찾을 때 사용하면 유용하다.
ex) 콜라이더의 오른쪽 shellRadius 만큼 떨어진 영역 안에 있는 모든 콜라이더를 찾는 예제
var colliders = body.GetComponents<Collider2D>();
var hitBufferPush = new RaycastHit2D[16];
int count = 0;
foreach (var collider in colliders)
{
if (!collider.isTrigger)
{
count += collider.Cast(Vector2.right, contactFilter, hitBufferPush, shellRadius);
}
}
----- 스크립트를 에디터에서 혹은 빌드 후 가장 먼저 실행시키는 법 (PlayMode 아닐때에도 실행) -----
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] 태그로 빌드 이후에도 가장 먼저 실행가능
에디터에서만 하려면
#if UNITY_EDITOR
[InitializeOnLoadMethod]
#endif
ex) 현재 씬에 매니저씬이 없으면 자동으로 불러오려는 상황 예시
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
using UnityEngine;
using UnityEngine.SceneManagement;
public class AutoLoadManagerScene : MonoBehaviour
{
private const string ManagerSceneName = "Manager";
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void LoadManagerSceneInPlayMode()
{
if (!IsSceneLoaded(ManagerSceneName))
{
SceneManager.LoadSceneAsync(ManagerSceneName, LoadSceneMode.Additive);
Debug.Log($"Manager scene '{ManagerSceneName}' automatically loaded in Play Mode.");
}
}
#if UNITY_EDITOR
[InitializeOnLoadMethod]
private static void LoadManagerSceneInEditMode()
{
EditorApplication.update += () =>
{
if (!Application.isPlaying && !IsSceneLoaded(ManagerSceneName))
{
EditorSceneManager.OpenScene($"Assets/01_Scene/{ManagerSceneName}.unity", OpenSceneMode.Additive);
Debug.Log($"Manager scene '{ManagerSceneName}' automatically loaded in Edit Mode.");
}
};
}
#endif
private static bool IsSceneLoaded(string sceneName)
{
for (int i = 0; i < SceneManager.sceneCount; i++)
{
if (SceneManager.GetSceneAt(i).name == sceneName)
return true;
}
return false;
}
}
----- UI Toolkit 으로 .uss 스타일 시트를 만들 수 있다. -----
Unity UI Toolkit 패키지로
웹에서 css 처럼 에디터 상 커스텀 스타일을 쉽게 만들 수 있다.
----- Easy Save 3 파일 저장 -----
ES3File은 Easy Save 3에서 제공하는 기능 중 하나로, 메모리 내에서 작동하는 저장 파일 객체
이 객체는 파일을 직접적으로 다루기보다, 메모리 상에서 파일의 내용을 관리하고 수정한 후에 최종적으로 디스크에 저장하거나 로드하는 방식을 사용
성능 최적화: 파일을 자주 읽고 쓰지 않고 메모리에서 작업을 처리함으로써 성능이 향상
버퍼링 기능: 데이터를 수집한 뒤 한 번에 저장하거나 로드할 수 있어 효율적
안정성: 데이터를 메모리에서 작업한 후에 문제가 발생할 경우 디스크로 저장되지 않으므로 안정성 높음
ES3File _es3File = new ES3File(filePath, es3Settings);
_es3File.Save<string>(key, data);
_es3File.Sync();
// 비동기로 데이터를 파일에 저장하기도 가능
await _es3File.SyncAsync();
------ uGUI Slider 키보드 제어 막기 -----
Slider가 좌우 키제어(Horizontal and Vertical axes)를 받아서 예상치 못한 Slider Value값이 변경되어 UI가 바뀌는 현상이 있을 수 있다.
키보드 제어를 막으려면 Navigation 옵션을 None으로 두면 됨.
코드로만 제어하려면 (안전하게 마우스 제어도 막으려면) Interactable 도 false로 하자.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment