Get it on Google Play


Wm뮤 :: 'Unity' 카테고리의 글 목록 (21 Page)

블로그 이미지
가끔 그림그리거나 3D모델링하거나
취미로 로봇만드는
퇴직한 전자과 게임프로그래머
2020.3.48f1 , 2022.3.6f1 주로 사용
모카쨩
@Ahzkwid

Recent Comment

Archive


2020. 7. 9. 15:27 Unity/C#

 

 

뭔가 유용한데 정리하기 애매한것들

나중에 정리하자

 

 

 

 

 

 

씬 어딘가에 있는 컴포넌트를 불러옴. 매니저컴포넌트같은걸 다룰때 좋음. 할당 안해도 되니까

부하가 크니 사용시 주의

자주 호출할땐 아래의 '싱글톤에서 최상위 부모를 불러올때' 항목 참고

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
posted by 모카쨩
2020. 7. 8. 15:11 Unity/C#

 

 

//인스펙터에 script표시
GUI.enabled = false;
{
    var script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
    EditorGUILayout.ObjectField("Script", script, typeof(MonoScript), false);
}
GUI.enabled = true;

중괄호를 if문이나 for문같은게 없더라도 구획을 나누는데 쓰면 좋다

이렇게 만들어두면 필요할때 함수로 묶기도 편하다

region따위보다 가독성이 훨씬 좋다

 

그리고 아래는 아웃풋이 있을경우 예시


//worldPosition 2 screenPosition
Vector2[] screenPositions;
Vector2 screenHandlePosition;
{
    screenPositions = System.Array.ConvertAll(worldPositions, wp => RectTransformUtility.WorldToScreenPoint(cam, wp));
    screenHandlePosition = RectTransformUtility.WorldToScreenPoint(cam, worldHandlePoint);
}

 

 

 

 

 

foreach (var reloadEndSound in reloadEndSounds)
{
    reloadEndSound.Play();
}

foreach는 무조건 var를 쓰자. 어차피 용도는 한정되어 있으니까

참조변수는 s만 빼서 쓰면 식별도 편하고 좋다

 

 

 

 

switch (humanBodyBones)
{
    case HumanBodyBones.LeftUpperLeg:
    case HumanBodyBones.RightUpperLeg:
    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:
    case HumanBodyBones.LeftLowerArm:
    case HumanBodyBones.RightLowerArm:
        partDamage = 0.7f;
        break;
}

 

책이나 교수들이 스위치문 이상하게 알려주는데

조건마다 break 달 필요없다.

애초에 그럴거면 다중if 쓰지 뭐라러 스위치문을 쓰겠는가

 

 

 

public void PressButton(string enumName, int index)
{
    Debug.Log($"PressButton({enumName}, {index})");
    if (enumName == nameof(MainCategory))
    {
        mainCategory = (MainCategory)index;
    }
    if (enumName == nameof(CostumeCategory))
    {
        costumeCategory = (CostumeCategory)index;
    }
    if (enumName == nameof(AppearanceCategory))
    {
        appearanceCategory = (AppearanceCategory)index;
    }
}

else if 대신 다중if를 쓰자.

가끔 switch문이 제약조건으로 안 먹는 경우 다중if를 많이 쓴다

오잉? '처리량이 늘어나는데 왜 else if를 안 쓰지?' 하고 의아해 할것이다.

보통 이렇게 수동으로 분기처리하는 데이터는 양이 적기 때문에

처리량이 늘어나도 거의 영향을 안 주는데

else if는 맨 윗열이 삭제되거나 하면 일일히 else를 지워가면서 해야한다

몰론 엄청나게 무거운 로직을 처리할땐 else if를 써야겠지만 그럴일은 별로 없다

 

아래는 또다른 예시

public void Skill(int index)
{
    if (hp<=0)
    {
        return;
    }
    if (IsMine)
    {
        if (CurrentSkillDelay(index) > 0)
        {
            return;
        }
        for (int i = 0; i < 2; i++)
        {
            if (CurrentSkillTime(i) > 0)
            {
                return;
            }
        }
    }
    //스킬실행
}

만약 위 코드에 if else를 쓰고 작동부를 if블록 안쪽에 두었다면 엄청나게 복잡해졌을것이다

 

 

 

 

다중루프 탈출

변수둬서 if체크하며 탈출하지 말고

goto로 탈출하자

for (int i = 0; i < players.Length; i++)
{
    for (int j = 0; j < bullets.Length; j++)
    {
        if (CollisionCheck(players[i], bullets[j]))
        {
            goto LOOP_OUT;
        }
    }
}
LOOP_OUT:;

'Unity > C#' 카테고리의 다른 글

자주 쓰는 유니티 코드 모음  (0) 2020.07.09
스파인 관련함수  (0) 2020.07.02
Parent of RectTransform is being set with parent property  (0) 2020.06.29
posted by 모카쨩
2020. 7. 2. 17:50 Unity/C#

스파인 컴포넌트 써오면서 깨달은점은 스파인 만든놈들은 유니티를 존나 못한단것이다

 

 

 

스파인 애니메이션 변경관련

 

 

using계열

using Spine.Unity

 

-스파인 애니메이션 변경시 해당 오브젝트 하위에 SkeletonAnimation이 있을경우

public SkeletonAnimation skeletonAnimation;

[SpineAnimation(dataField: "skeletonAnimation")]
public String spineAnim;
    
    
 SkelAnim.AnimationName = spineAnim;

-스파인 애니메이션 변경시 다른 오브젝트에서 SkeletonAnimation를 호출할경우

(SkeletonAnimation타입)

public SkeletonAnimation skeletonAnimation;
public AnimationReferenceAsset spineAnim;

SkelAnim.loop = false; //루프는 반드시 애니메이션 설정전에 실행되어야함
SkelAnim.AnimationName = spineAnim.name;

(SkeletonGraphic타입)

public SkeletonGraphic skeletonGraphic;
public AnimationReferenceAsset spineAnim;

skeletonGraphic.AnimationState.SetAnimation(trackIndex:0, spineAnim.name, loop:false); //false는 루프, 0은 인덱스 번호

 

 

-현재 SkeletonAnimation의 애니메이션의 지속시간

(SkeletonAnimation타입)

SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
float Duration=skeletonAnimation.skeleton.Data.FindAnimation(skeletonAnimation.AnimationName).Duration;

(SkeletonGraphic타입)

SkeletonGraphic skelGrap = GetComponent<SkeletonGraphic>();
string animName = skelGrap.AnimationState.Tracks.Items[0].ToString();
float duration=skelGrap.SkeletonData.FindAnimation(animName).Duration;

 

스파인 특정 본의 y축 좌표 추적

skeletonGraphic.Skeleton.FindBone("본이름").Y * 100f;

스파인 스킨 변경

GetComponent<Spine.Unity.SkeletonGraphic>().Skeleton.SetSkin("스킨 이름");

스파인 스킨을 번호로 가져올때

var skeletonGraphic = GetComponent<Spine.Unity.SkeletonGraphic>();
var skins = skeletonGraphic.AnimationState.Data.SkeletonData.Skins.ToArray();
skeletonGraphic.Skeleton.SetSkin(skins[번호]);

 

 

 

해당 skeletonGraphic의 애니메이션 리스트 가져오기

skeletonGraphic.AnimationState.Data.SkeletonData.Animations.ToArray()

해당 skeletonGraphic의 스킨 리스트 가져오기

skeletonGraphic.AnimationState.Data.SkeletonData.Skins.ToArray()

 

SkeletonDataAsset을 가져올때

public Spine.Unity.SkeletonDataAsset skeletonDataAsset;

 

SkeletonDataAsset 변경

public Spine.Unity.SkeletonDataAsset skeletonDataAsset;

var skeletonGraphic = GetComponent<Spine.Unity.SkeletonGraphic>();
if(skeletonGraphic.skeletonDataAsset!= SkeletonDataAsset)
{
    skeletonGraphic.skeletonDataAsset = SkeletonDataAsset;
    skeletonGraphic.Initialize(true);
}

 

현재 애니메이션의 최대시간

SkeletonGraphic skeletonGraphic = GetComponent<SkeletonGraphic>();
if (skeletonGraphic.gameObject.activeInHierarchy && skeletonGraphic.IsActive())
{
    float duration = skeletonGraphic.AnimationState.Tracks.Items[0].AnimationTime;
}

 

 

정지

skeletonGraphic.timeScale = 0;
if (skeletonGraphic.gameObject.activeInHierarchy && skeletonGraphic.IsActive())
{
    skeletonGraphic.AnimationState.Tracks.Items[0].TrackTime = 0;
}

 

일시정지

skeletonGraphic.timeScale = 0;

 

 

재생


var entry = skeletonGraphic.AnimationState.SetAnimation(0, animation.Name, loop);
entry.MixDuration = 0f;

 

되감기

SkeletonGraphic skeletonGraphic = curtainNormal.GetComponent<SkeletonGraphic>();
if (skeletonGraphic.gameObject.activeInHierarchy && skeletonGraphic.IsActive())
{
    float duration = skeletonGraphic.AnimationState.Tracks.Items[0].AnimationTime;
    skeletonGraphic.unscaledTime = true;
    skeletonGraphic.AnimationState.Tracks.Items[0].TrackTime = duration;
    skeletonGraphic.timeScale = -1;
}

 

 

 

애니메이션이 끝났을때에만 재생(Update에 넣어야함)

if (skeletonGraphic.AnimationState.Tracks.Items.Length == 0)
{
    var entry = skeletonGraphic.AnimationState.SetAnimation(0, animation.Name, loop);
    entry.MixDuration = 0f;
    skeletonGraphic.Initialize(true);
    skeletonGraphic.Update(0f);
}
else
{
    Spine.TrackEntry trackItemNow = skeletonGraphic.AnimationState.Tracks.Items[0];
    trackItemNow.Loop = loop;
    float duration = skeletonGraphic.SkeletonData.FindAnimation(trackItemNow.ToString()).Duration;
    float trackTime = trackItemNow.TrackTime;
    if (trackTime > duration-0.1f)
    {
        var entry = skeletonGraphic.AnimationState.SetAnimation(0, animation.Name, loop);
        entry.MixDuration = 0f;
        skeletonGraphic.Initialize(true);
    }
}

 

 

애니메이션 종료 콜

단점은 루프가 꺼져있어도 애니메이션이 타임만 증가하며 영원히 돌아가는거로 나오기 때문에 작동을 제대로 안함

void Awake()
{
	//애니메이션 루틴이 끝났을경우 호출(루프중이면 주기마다 호출됨)
    GetComponent<SkeletonGraphic>().AnimationState.Complete -= SpineEndEvent;
    GetComponent<SkeletonGraphic>().AnimationState.Complete += SpineEndEvent;
	//애니메이션이 종료되었을경우 호출(제대로 작동 안 함)
    GetComponent<SkeletonGraphic>().AnimationState.End -= SpineEndEvent;
    GetComponent<SkeletonGraphic>().AnimationState.End += SpineEndEvent;
}
void SpineEndEvent(Spine.TrackEntry te)
{
//동작할 코드
}

 

 

스켈레톤 그래픽 사이즈

skeletonGraphic.SkeletonData.Width //가로
skeletonGraphic.SkeletonData.Height //세로

 

 

 

특정폴더의 스켈레톤 데이터를 전부 불러옴


#if UNITY_EDITOR
    public static Spine.Unity.SkeletonDataAsset[] GetFolderToSkeletonDatas(UnityEngine.Object folder)
    {
        var folderPath = UnityEditor.AssetDatabase.GetAssetPath(folder);
        var filePathSkeletonDatas = UnityEditor.AssetDatabase.FindAssets($"t:{typeof(Spine.Unity.SkeletonDataAsset).Name}", new string[] { folderPath });
        var skeletonDatas = new Spine.Unity.SkeletonDataAsset[filePathSkeletonDatas.Length];
        for (int i = 0; i < skeletonDatas.Length; i++)
        {
            skeletonDatas[i] = UnityEditor.AssetDatabase.LoadAssetAtPath<Spine.Unity.SkeletonDataAsset>(UnityEditor.AssetDatabase.GUIDToAssetPath(filePathSkeletonDatas[i]));
        }
        return skeletonDatas;
    }
#endif

 

 

'Unity > C#' 카테고리의 다른 글

사소한 c# 테크닉  (0) 2020.07.08
Parent of RectTransform is being set with parent property  (0) 2020.06.29
SQLite 유니티 사용방법  (0) 2020.05.24
posted by 모카쨩
2020. 6. 29. 18:47 Unity/C#

new_go.transform.parent=target.transform.parent;

를 아래처럼 고침

new_go.transform.SetParent(target.transform.parent);

'Unity > C#' 카테고리의 다른 글

스파인 관련함수  (0) 2020.07.02
SQLite 유니티 사용방법  (0) 2020.05.24
유니티 커스텀 인스펙터  (0) 2020.05.19
posted by 모카쨩
2020. 6. 29. 10:26 Unity

 

안드로이드 스튜디오 안 깔아도 됨

 

 

 

'Unity' 카테고리의 다른 글

유니티 스파인 오류 모음  (0) 2020.07.24
스파인 사용법  (0) 2020.06.25
인풋필드 엔터누르면 초기화  (0) 2020.05.31
posted by 모카쨩
2020. 6. 29. 10:11 Unity/shader

스프라이트 쉐이더 작성시에 볼수 있는 에러 문구이다

 

 

         _StencilComp ("Stencil Comparison", Float) = 8
         _Stencil ("Stencil ID", Float) = 0
         _StencilOp ("Stencil Operation", Float) = 0
         _StencilWriteMask ("Stencil Write Mask", Float) = 255
         _StencilReadMask ("Stencil Read Mask", Float) = 255
         _ColorMask ("Color Mask", Float) = 15

프로퍼티에 이걸 넣는다

 

 


		Stencil
		{
			Ref[_Stencil]
			Comp[_StencilComp]
			Pass[_StencilOp]
			ReadMask[_StencilReadMask]
			WriteMask[_StencilWriteMask]
		}
		ColorMask[_ColorMask]

에러를 지우는 것뿐만이 아닌, 스텐실 기능도 활성화 하려면 이 코드도 서브쉐이더에 넣는다

 

 

 

참조

https://answers.unity.com/questions/980924/ui-mask-with-shader.html

'Unity > shader' 카테고리의 다른 글

유니티 쉐이더 인스펙터  (0) 2021.02.21
스텐실  (0) 2021.01.20
자주쓰는 유니티 쉐이더 코드모음  (0) 2020.11.04
posted by 모카쨩
2020. 6. 25. 10:43 Unity

http://ko.esotericsoftware.com/spine-unity-download

위 링크에서 플러그인을 다운받는다

 

 

 

필요한파일

캐릭터.atlas.txt

캐릭터.json

캐릭터.png

 

없으면 기본랩터를 써도 전혀 문제없다

더보기

랩터위치

 

 

 

마테리얼을 먼저 만든다

 

그다음 우클릭해서 두개를 만든다

스파인 아틀라스는 아틀라스 파일과 방금 만든 매터리얼

 

 

스켈레톤 데이터는 json과 아틀라스를 넣는다

 

하이라키에서 스켈레톤 애니메이션을 넣는다

 

 

애니메이션 체크하고 루프를 넣는다

 

 

빌드해보면 잘된다

'Unity' 카테고리의 다른 글

유니티 로그캣  (0) 2020.06.29
인풋필드 엔터누르면 초기화  (0) 2020.05.31
SQLlite 브라우저 사용방법  (0) 2020.05.24
posted by 모카쨩
2020. 5. 31. 22:02 Unity

 

'Unity' 카테고리의 다른 글

스파인 사용법  (0) 2020.06.25
SQLlite 브라우저 사용방법  (0) 2020.05.24
유용한 에셋 모음  (0) 2020.05.20
posted by 모카쨩

  • total
  • today
  • yesterday

Recent Post

저사양 유저용 블로그 진입