뭔가 유용한데 정리하기 애매한것들
나중에 정리하자
씬 어딘가에 있는 컴포넌트를 불러옴. 매니저컴포넌트같은걸 다룰때 좋음. 할당 안해도 되니까
부하가 크니 사용시 주의
자주 호출할땐 아래의 '싱글톤에서 최상위 부모를 불러올때' 항목 참고
FindObjectOfType<클래스명>();
FindObjectsOfType을 대체하는 함수
2021.3.18에 추가됨
차이점은 FindObjectsByType은 정렬을 할지 안할지 정할수 있어서 정렬을 안 하면 속도가 더 빠르다는정도
var targets = FindObjectsByType<Renderer>(FindObjectsSortMode.None);
모든 자식들중에 해당 게임오브젝트가 존재하는지 검색함
단 Transform이 달려있어야함
System.Array.Find(transform.GetComponentsInChildren<Transform>(), x => x.name == "게임오브젝트명")
태그검색
GameObject.FindGameObjectsWithTag("태그명");
문자열로 레이어 체크
gameObject.layer == LayerMask.NameToLayer("레이어명")
카메라 레이어 토글
var layerIndex=LayerMask.NameToLayer("레이어명");
camera.cullingMask ^= 1 << layerIndex;
레이어마스크 추가 삭제
//추가
layerMask |= 1 << layer;
//삭제
layerMask &= ~(1 << layer);
컴포넌트 달린 오브젝트 생성
var gameObject = new GameObject(nameof(SampleClass), typeof(SampleClass));
컴포넌트를 활성화 하거나 비활성화할때
GetComponent<Image>().enabled = false;
유니티 에셋 파일 변경시 호출
#if UNITY_EDITOR
클래스명()
{
EditorApplication.projectChanged += GetFileData;
}
void GetFileData()
{
//실행문
}
#endif
프리팹 저장
var target_ClassName = target as ClassName;
if (PrefabUtility.GetPrefabAssetType(target_ClassName.gameObject) != PrefabAssetType.NotAPrefab)
{
PrefabUtility.SavePrefabAsset(target_ClassName.gameObject);
}
텍스트 일부분만 컬러주기, 리치텍스트
Rich Text가 적용되어 있어야 하는데 일반적으론 적용되어있으니 상관없음
텍스트컴포넌트.richText = true;
텍스트컴포넌트.text=
"<color=#ff0000>빨간색</color>
<color=green>초록색</color>
<color=#0000FFc0>반투명파란색</color>
<b>굵게</b>
<i>기울임</i>
<size=50>사이즈50</size>
<material=2>매터리얼2번</material>
"
;
버튼을 누르면 URL실행
if (GUILayout.Button("URL실행"))
{
Application.OpenURL("https://wmmu.tistory.com/");
}
인스펙터의 값을 글로벌로 저장할때 ScriptableObjects를 사용
스크립트테이블오브젝트
using UnityEngine;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
public string prefabName;
public int numberOfPrefabsToCreate;
public Vector3[] spawnPoints;
}
string.IsNullOrEmpty("문자열")
폴더 이름 반환
var folderPath=AssetDatabase.GetAssetPath(object형폴더);
string[] fileNames = AssetDatabase.GetSubFolders(folderPath);
폴더 아래의 Sprite이름들 반환
{
var folderPath=AssetDatabase.GetAssetPath(object형폴더);
string[] guidFilePathSprites = AssetDatabase.FindAssets($"t:{typeof(Sprite).Name}", new string[] { folderPath });
Sprite[] sprites = new Sprite[guidFilePathSprites.Length];
for (int i = 0; i < sprites.Length; i++)
{
var path=AssetDatabase.GUIDToAssetPath(sprites[i]));//Assets/FileName.Sprite 같은식으로 반환
sprites[i]=AssetDatabase.LoadAssetAtPath<Sprite>(path);
}
}
정적 업데이트
[InitializeOnLoad]
public class MyWindow : EditorWindow
{
static MyWindow()
{
EditorApplication.update -= StaticUpdate;
EditorApplication.update += StaticUpdate;
}
static long lastTime = 0;
public static void StaticUpdate()
{
if ((System.DateTime.Now.Ticks-lastTime)/10000000 >= 3)//3초마다 동작
{
//실행문
lastTime = System.DateTime.Now.Ticks;
}
}
}
Time.time 타이머 예제
가끔 지속체크 해야해서 코루틴 쓰기 곤란한거 쓸때 쓰면 좋음
public Text respawnTimeText;
public Image respawnProgress;
float lastDeathTime = float.MinValue;
/// <summary>
/// update에서 지속 호출
/// </summary>
public void RespawnCheck()
{
var nowTime = Time.time;
var delay = 10;
if (nowTime < lastDeathTime + delay)
{
//부활대기중
var respawnTime = (lastDeathTime + delay) - nowTime;
respawnTimeText.text = respawnTime.ToString("N0"); //10부터 시작
respawnProgress.fillAmount = 1f - respawnTime / delay; //0부터 시작
respawnTimeText.gameObject.SetActive(true);
respawnProgress.gameObject.SetActive(true);
}
else
{
if (lastDeathTime > 0)
{
//리스폰
Respawn();
lastDeathTime = float.MinValue;
}
//생존중
respawnTimeText.gameObject.SetActive(false);
respawnProgress.gameObject.SetActive(false);
}
}
public void Respawn()
{
(var poses, var rot) = GameSystem.GetInstance().GetSpawnPoints(GetComponent<PhotonCharacter>().team);
transform.position = poses[0];
transform.rotation = rot;
GetComponent<PhotonCharacter>().hp = 100;
GetAnimator().GetComponent<DynamicRagdoll>().Deactive();
}
public void Death()
{
lastDeathTime = Time.time;
}
모든 씬에서 단 한번만 자동생성되는 오브젝트
public class GameManager : MonoBehaviour
{
static GameObject gameManager;
static void CreateInstance()
{
if (gameManager == null)
{
DontDestroyOnLoad(new GameObject(nameof(GameManager), typeof(GameManager)));
//nameof가 오브젝트명이고 typeof가 addcomponant
}
}
static GameManager()
{
if(Application.isEditor==false)
{
CreateInstance();
}
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlaying)
{
CreateInstance();
}
#endif
}
열닫 가능한 코드
#region 주석
#endregion
웹컬러 변환
출력은 #이 빠져나오고 입력은 #이 붙어야 하는 미쿠친구 함수니까 조심하자
string webColor = $"#{ColorUtility.ToHtmlStringRGB(Color.red)}";
//webColor = #FF0000
string webColorRGBA = $"#{ColorUtility.ToHtmlStringRGBA(Color.red)}";
//webColor = #FF0000FF
Color color;
ColorUtility.TryParseHtmlString(webColor, out color);
//color = RGBA(1.000, 0.000, 0.000, 1.000)
에디터 모드에서도 MonoBehaviour를 동작하게 함
update나 start이런 함수도 작동
[ExecuteInEditMode]
데이터 타입 이름 구한거
Debug.Log($"bool: {typeof(bool).Name}"); //Boolean
Debug.Log($"char: {typeof(char).Name}"); //Char
Debug.Log($"string: {typeof(string).Name}"); //String
Debug.Log($"short: {typeof(short).Name}"); //Int16
Debug.Log($"int: {typeof(int).Name}"); //Int32
Debug.Log($"long: {typeof(long).Name}"); //Int64
Debug.Log($"float: {typeof(float).Name}"); //Single
Debug.Log($"double: {typeof(double).Name}"); //Double
배열을 콤마가 붙은 문자열로 반환
string strData = $"({string.Join(",", 배열데이터)})";
string형이 아닌 배열일때
string strData = $"({string.Join(",", Array.ConvertAll(배열데이터, x => x.ToString()))})";
하이어라키의 해당 오브젝트의 자식계층에서 최하위로 설정함, 부모는 변하지 않음
transform.SetAsLastSibling();
리플렉션으로 애니메이션 윈도우 접근
var animationWindowType = System.Type.GetType("UnityEditor.AnimationWindow,UnityEditor");
var animationWindow = Resources.FindObjectsOfTypeAll(animationWindowType)[0];
마우스에 대고있는 윈도우 이름
var mouseOverWindow = EditorWindow.mouseOverWindow;
if (mouseOverWindow != null)
{
Debug.Log("mouseOverWindow: " + mouseOverWindow.GetType().ToString());
}
폰이 스스로 종료되지 않도록
Screen.sleepTimeout = SleepTimeout.NeverSleep; //네버슬립모드
백그라운드에서도 동작함
Application.runInBackground=true;
TextUI의 적절한 가로 사이즈, 캐릭터(문자)의 위치를 구할때 가아아아끔씩 쓴다
GetComponent<Text>().preferredWidth
씬에디터상에 박스를 표시
//에디터상에서 박스위치 표시할때 사용
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(borderCenter, borderSize);
}
public Vector3 borderCenter = Vector3.zero;
public Vector3 borderSize = new Vector3(200,100,200);
배속설정, 디버깅할때 유용하더라
Time.timeScale=2;
싱글톤에서 최상위 부모를 불러올때
v1
static MainSystem mainSystemPrivate;
public static MainSystem mainSystem
{
get
{
if (mainSystemPrivate == null)
{
mainSystemPrivate = FindObjectOfType<MainSystem>();
}
return mainSystemPrivate;
}
}
v2
네이밍만 바뀜
static GameSystem gameSystem = null;
public static GameSystem GetInstance()
{
if (gameSystem == null)
{
gameSystem = FindObjectOfType<GameSystem>();
}
return gameSystem;
}
팝업창을 띄움, 에디터 전용
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoad]
public class Class : MonoBehaviour {
static Class()
{
UnityEditor.EditorUtility.DisplayDialog("제목", "내용", "네", "아니요");
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
#endif
인스턴트 SE
public void InstantiateSE(AudioClip clip)
{
if (clip==null)
{
Debug.Log("clip==null");
return;
}
var gameObject = new GameObject($"SoundEffect({clip.name})");
var audioSource = gameObject.AddComponent<AudioSource>();
audioSource.clip = clip;
audioSource.Play();
Destroy(gameObject, audioSource.clip.length + 3f);
DontDestroyOnLoad (gameObject);
}
유니티 버전
Application.version
Predicate 샘플
public static class Array
{
public static int[] FindIndexAll<T>(T[] array, System.Predicate<T> match)
{
var indexList = new List<int>();
for (int i = 0; i < array.Length; i++)
{
if (match.Invoke(array[i]))
{
indexList.Add(i);
}
}
return indexList.ToArray();
}
}
// Start is called before the first frame update
void Start()
{
var strTest = new string[] { "A", "B", "C", "A", "B" };
var indexes = Array.FindIndexAll(strTest, x => x == "A");
Debug.Log(string.Join(",",indexes)); //0,3을 반환
}
현재 언어가 영어인지
if(Application.systemLanguage == SystemLanguage.English)
인풋필드가 활성화 되어있는지
if((InputField!=null)&& (InputField.isFocused))
{
}
스크린사이즈 변경
private void Awake()
{
if (Debug.isDebugBuild)
{
var hei = 480;
Screen.SetResolution(Screen.width * hei / Screen.height, hei, true);
}
}
fps측정
float fps = 60;
private void OnGUI()
{
fps = Mathf.Lerp(fps, (1 / Time.deltaTime), 0.05f);
GUI.Box(new Rect(100, 0, 200, 24), "FPS: " + (int)fps);
}
스크롤뷰의 콘텐츠 위치를 항상 아래쪽으로 잡을때
scrollRect.verticalScrollbar.value = 0;
이름으로 블렌드 쉐이프 설정
var skinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
var blendShapeIndex=skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex("ShapeKeyName");
skinnedMeshRenderer.SetBlendShapeWeight(blendShapeIndex, weight);
마우스 위치 표시
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
Debug.DrawLine(ray.origin, hit.point,Color.red);
}
else
{
Debug.DrawLine(ray.origin, ray.origin + ray.direction * 5);
}
자식리스트를 불러옴
//켜진것만
var childList = new List<GameObject>();
for (int i = 0; i < transform.childCount; i++)
{
var child = transform.GetChild(i).gameObject;
if (child.activeInHierarchy)
{
childList.Add(child);
}
}
//전부
var childList = new List<GameObject>();
for (int i = 0; i < transform.childCount; i++)
{
childList.Add(transform.GetChild(i).gameObject);
}
uuid
빌드조건에 따라 쉽게 바뀌니 주의
안드로이드면 키스토어만 바뀌어도 기준값이 바뀌고
애플이면 xcode에서 직접 빌드했는지 아닌지에 따라서도 바뀐다
SystemInfo.deviceUniqueIdentifier
자주쓰는 애니메이션 이벤트
public void InstantiateSE(AudioClip clip)
{
if (clip==null)
{
Debug.Log("clip==null");
return;
}
var gameObject = new GameObject($"SoundEffect({clip.name})");
var audioSource = gameObject.AddComponent<AudioSource>();
audioSource.clip = clip;
audioSource.Play();
Destroy(gameObject, audioSource.clip.length + 3f);
}
scrollRect(Scroll Rect) 스크롤좌표 초기화
scrollRect.horizontalNormalizedPosition = 0f;
scrollRect.verticalNormalizedPosition = 0f;
GPGS토큰
string googleIdToken = null;
string googleAccessToken = null;
if (PlayGamesPlatform.Instance.IsAuthenticated())
{
googleIdToken = PlayGamesPlatform.Instance.GetIdToken();
//googleIdToken = ((PlayGamesLocalUser)Social.localUser).GetIdToken(); 이거도 된다고는 함
googleAccessToken = PlayGamesPlatform.Instance.GetServerAuthCode();
}
else
{
Debug.LogError("GPGS에 연결되어있지않아 토큰을 받아올수 없음");
}
//ID토큰은 공개적인거고 Access토큰은 비공개적인것인듯하다(불확실)
최대값을 지정 해 줌으로서 min값을 구할때 쓸 수 있다
long minScore = long.MaxValue;
게임종료
일반적인 상황에서는 쓸일이 없지만
번들로더같은곳에서 다운로드 취소버튼을 누른다던지 PC용게임을 만들때 사용함
public void Exit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
Enumerable To List
IEnumerable<DataSnapshot> childrens;
List<DataSnapshot> snapshots= Enumerable.ToList(childrens);
Enumerable To Array
IEnumerable<DataSnapshot> childrens;
DataSnapshot[] snapshots= Enumerable.ToArray(childrens);
레이캐스트2D
유니티4버전이라서 추가 업뎃 해야할듯
RaycastHit2D ray;
Vector3 pos = Camera.main.ScreenToWorldPoint (Input.mousePosition);
ray = Physics2D.CircleCast ((Vector2)pos,0.1f,Vector2.zero);
Debug.DrawRay (mpos,ray.direction,Color.red);
if(ray.transform != null) //빔을 쏴서 존재하는지
{
Debug.Log (ray.point);
Debug.DrawLine(mpos, ray.point, Color.blue);
Debug.Log (ray.transform.GetInstanceID ().ToString());
}
레이캐스트 everything
var everything = ~0;
레이캐스트 샘플
유니티 공식문서 코드에서 조금만 수정했다
주의 : hit.transform은 절대 절대 절대 절대 절대 쓰지말자
float maxDistance = 1000;
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, maxDistance >= 0 ? maxDistance : Mathf.Infinity))
{
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * hit.distance, Color.yellow);
}
else
{
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * maxDistance, Color.white);
}
레이캐스트 2점쇼바
public class Piston : MonoBehaviour
{
public Transform pistonStart;
public Transform pistonEnd;
void Update()
{
var forward = (pistonEnd.position - pistonStart.position).normalized;
var distance = Vector3.Distance(pistonStart.position, pistonEnd.position);
if (Physics.Raycast(pistonStart.position, forward, out RaycastHit hit, distance,1))
{
transform.position = pistonEnd.position - forward*(distance - hit.distance);
}
else
{
transform.position= pistonEnd.position;
}
}
}
레이캐스트 화면터치
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray))
{
//클릭시 실행
}
레이캐스트 화면기준 발사 3D
//var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var screenCenter = new Vector3(Screen.width / 2, Screen.height / 2, 0);
var ray = Camera.main.ScreenPointToRay(screenCenter);
float maxDistance = 1000;
if (Physics.Raycast(ray, out RaycastHit hit, maxDistance >= 0 ? maxDistance : Mathf.Infinity))
{
Debug.DrawLine(ray.origin, ray.GetPoint(hit.distance), Color.green);
}
else
{
Debug.DrawLine(ray.origin, ray.GetPoint(1000), Color.yellow);
}
디버그 로그 비활성
v1
디버그모드일때만 작동
public class SampleClass : MonoBehaviour
{
static class Debug
{
public static void Log(string log)
{
#if UNITY_EDITOR
if (UnityEditor.EditorUserBuildSettings.development)
#else
if (Debug.isDebugBuild)
#endif
{
UnityEngine.Debug.Log(log);
}
}
public static void LogError(string log)
{
#if UNITY_EDITOR
if (UnityEditor.EditorUserBuildSettings.development)
#else
if (Debug.isDebugBuild)
#endif
{
UnityEngine.Debug.LogError(log);
}
}
}
v2
맨 처음에 한번만 적어주면 됨
#if UNITY_EDITOR
if (UnityEditor.EditorUserBuildSettings.development)
#else
if (Debug.isDebugBuild)
#endif
{
Debug.logger.logEnabled=false
}
Define 전역설정
특정 패키지가 임포트 되었을때만 활성화 된다던지가 가능
충돌반사
public float damageSpeed = 3;
public void OnCollisionEnter(Collision collision)
{
if (collision.relativeVelocity.magnitude > damageSpeed)
{
explosionRot = Vector3.Reflect(-collision.relativeVelocity, collision.contacts[0].normal);
explosionPower = collision.relativeVelocity.magnitude * 0.5f;
}
}
충돌반사 네트워킹
https://ahzkwid.booth.pm/items/4471500 에서 사용중이다
public void OnCollisionEnter(Collision collision)
{
//https://ahzkwid.booth.pm/items/4471500
if (GetComponent<VRC_Pickup>() == null)
{
return;
}
if (Networking.IsOwner(gameObject))
{
if (collision.relativeVelocity.magnitude > damageSpeed* DamageSpeedMultiple)
{
explosionRot = Vector3.Reflect(-collision.relativeVelocity, collision.contacts[0].normal);
explosionPower = collision.relativeVelocity.magnitude * 0.5f;
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(ExplosionEffect));
Delete();
}
}
}
충돌파워에 따른 사운드
void OnCollisionEnter(Collision collision)
{
if (impactSound.isPlaying)
{
return;
}
var volume = collision.relativeVelocity.magnitude;
volume = Mathf.Clamp(volume, 0.1f, 1f);
impactSound.volume = volume;
impactSound.Play();
}
일정시간뒤 자동 삭제
using UnityEngine;
public class DelayDestroy : MonoBehaviour
{
public float delay = 3;
void Start()
{
Destroy(gameObject, delay);
}
}
부위데미지
var partDamage = 1f;
switch (humanBodyBones)
{
case HumanBodyBones.LeftUpperLeg:
case HumanBodyBones.RightUpperLeg:
partDamage = 0.9f;
break;
case HumanBodyBones.LeftLowerLeg:
case HumanBodyBones.RightLowerLeg:
partDamage = 0.7f;
break;
case HumanBodyBones.Spine:
case HumanBodyBones.Chest:
case HumanBodyBones.UpperChest:
case HumanBodyBones.Hips:
partDamage = 1f;
break;
case HumanBodyBones.Neck:
case HumanBodyBones.Head:
partDamage = 2f;
break;
case HumanBodyBones.LeftShoulder:
case HumanBodyBones.RightShoulder:
case HumanBodyBones.LeftUpperArm:
case HumanBodyBones.RightUpperArm:
partDamage = 0.8f;
break;
case HumanBodyBones.LeftLowerArm:
case HumanBodyBones.RightLowerArm:
partDamage = 0.7f;
break;
case HumanBodyBones.LeftHand:
case HumanBodyBones.RightHand:
case HumanBodyBones.LeftFoot:
case HumanBodyBones.RightFoot:
case HumanBodyBones.LeftToes:
case HumanBodyBones.RightToes:
partDamage = 0.5f;
break;
}
Enum1에서 Enum2로 변환
var enum2 = (Enum2)System.Enum.Parse(typeof(Enum2), enum1.ToString());
해당 게임오브젝트의 최상위루트 반환
v1
적용예제 : https://github.com/ahzkwid/AhzkwidAvatarTools
Transform GetRoot(Transform transform)
{
var parents = transform.GetComponentsInParent<Transform>(true);
Transform root = null;
if (parents.Length == 1)
{
root = transform;
}
else
{
root = System.Array.Find(parents, parent => parent.GetComponentsInParent<Transform>(true).Length == 1);
}
return root;
}
근데 내장함수 있었음;;
v2
transform.root
'Unity > C#' 카테고리의 다른 글
유니티 버그, 에러, 오류모음 (0) | 2020.07.20 |
---|---|
사소한 c# 테크닉 (0) | 2020.07.08 |
스파인 관련함수 (0) | 2020.07.02 |