반응형

public static void BundleSoundMoveToAddressableFolder(bool isRefreshWhenEnd)
    {
        FileInfo curF = null;
        try
        {
            DirectoryInfo dir = new DirectoryInfo("Assets/Editor/FMOD/BundleSound");
            FileInfo[] info = dir.GetFiles("*.*");
            foreach (FileInfo f in info)
            {
                curF = f;
                int idx = f.Name.IndexOf('.');
                string name = f.Name;
                string nameWithOutExtention = name.Substring(0, idx);
                string[] nameSplit = nameWithOutExtention.Split('_');

                int packNumber = 0;
                for (int i = 0; i < nameSplit.Length; i++)
                {
                    string nameSplitWithOutExtention = Path.GetFileNameWithoutExtension(nameSplit[i]);
                    int.TryParse(nameSplitWithOutExtention, out packNumber);
                }

                bool isMoveToCommon = false;

                //팩에 해당한는 리소스
                if (packNumber > 0)
                {
                    string packNumberFolderName = string.Format("Pack_{0:D2}", packNumber);
                    string destPackFolderPath = string.Format(destinationPackFolderPath, packNumber);
                    if (f.Name.Contains("Character_Pack"))
                    {
                        //(임시)Character_Pack이름 적힌 리소스는 Addressable폴더에 Common 폴더로
                        isMoveToCommon = true;
                    }
                    else if (Directory.Exists(destPackFolderPath) == false)
                    {
                        //pack폴더 없다면 옮기지않음
                        continue;
                        //pack폴더 없다면 Addressable폴더에 Common 폴더로
                        //isMoveToCommon = true;
                        
                    }
                    else if (f.Name.Contains(packNumberFolderName))
                    {
                        //팩에 해당하는 파일이면 Addressable폴더의 Pack 폴더로
                        FileUtil.ReplaceFile($"{sourceFolderPath}/{name}", $"{string.Format(destinationPackFolderPath, packNumber)}/{name}");

                    }
                    else // 해당되지 않은 것들은 Addressable폴더에 Common 폴더로
                    {
                        isMoveToCommon = true;

                    }



                }
                else //팩이아닌 리소스
                {
                    if (soundListBelongToResourcesFolder.Contains(nameWithOutExtention))
                    {
                        //Resouces 파일에 해당하는 것은 Resources 폴더로
                        FileUtil.ReplaceFile($"{sourceFolderPath}/{name}", $"{destinationSoundMasterFolderPath}/{name}");
                    }
                    else // 해당되지 않은 것들은 Addressable폴더에 Common 폴더로
                        isMoveToCommon = true;
                }

                if (isMoveToCommon)
                {
                    FileUtil.ReplaceFile($"{sourceFolderPath}/{name}", $"{destinationCommonFolderPath}/{name}");
                }
            }
        }
        catch(System.Exception e)
        {
            Debug.LogError($"ex :  {curF.Name} / {curF.FullName} / {curF.DirectoryName} ");
        }
        if (isRefreshWhenEnd)
        {
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
    }

반응형
반응형

 

 [MenuItem("GameObject/FieldObject/CopyComponet (Ctrl+Alt+Shift+C) %#&c")]
    static void CopyComponent()
    {
        _copyObject = Selection.activeObject as GameObject;
    }

    [MenuItem("GameObject/FieldObject/PasteComponet (Ctrl+Alt+Shift+V) %#&v")]
    static void PasteComponet()
    {
        if (_copyObject == null)
        {
            Debug.LogError("복사된 오브젝트가 없습니다.");
            return;
        }

        Component[] componentList = _copyObject.GetComponents<Component>();

        GameObject obj = Selection.activeObject as GameObject;
        obj.name = _copyObject.name;
        for (int i = 0, count = componentList.Length; i < count; ++i)
        {
            Component targetObjectComponent = obj.GetComponent(componentList[i].GetType());
            UnityEditorInternal.ComponentUtility.CopyComponent(componentList[i]);
            if (targetObjectComponent != null)
            {
                UnityEditorInternal.ComponentUtility.PasteComponentValues(targetObjectComponent);
            }
            else
            {
                UnityEditorInternal.ComponentUtility.PasteComponentAsNew(obj);
            }
        }
    }
반응형
반응형

Unity 2018 버전 부터 프리팹모드 적용되 해당 프리팹만 따로 관리 할수 있는 Scene을 만들어 작업할수 있게 기능으 두었습니다.

 

unity 5 버전 unity 2017 버전 등 사용하다 프리팹모드 경험하면 좀 익숙지 않다 생각할수 있습니다.

 

겪엇던 문제로는 

1. 현재 사용하는 씬에서 프리팹을 하이라키에 끌어와 작업을 할때 프리팹내의 오브젝트 레이어 변경이나 오브젝트 삭제시 "Cannot Restructure Prefab Instance" 팝업이 뜨면서 오브젝트 삭제나 레이어 변경을 프리팹모드에서 강제로 할수 밖에 없는문제. => 너무 번거러움... 뭐 삭제하려고해도 프리팹모드 들어가서 삭제하고 Apply 하고 나와서 다시 작업하게됨...

 

2. UI 프리팹 경우 현재 사용하는 씬의 Canvas 값이랑 프리팹모드에 Canvas 값이랑 달라서  프리팹모드에서 UI 가 깨지는 문제 => 디테일하게 이미지 보면서 작업이 불가능함

 

 

 

두문제중 가장 짜증나는건 2번문제...

아래사진과같이... ㅠㅠ

 

 

 

 

 

 

 

하지만 최근알게된것이 프리팹모드 (프리팹씬) 을 자기가 커스텀하게 설정 할수 있다는겁니다.

 

아래 내용 Prefab Editing Environment 입니다

 

경로 : Edit - Project Setting - Editor 카테고리 - Prefab Editing Environments 

 

 - Regular Environment 는 일반 오브젝트 프리팹이 됫을경우 사용될 씬입니다 (ex. 보스, 캐릭터, 나무상자 등등)

 - UI Environment 는 UI 전반적인 적이 프리팹됫을경우 사용될 씬입니다 (ex. 로비UI, 팝업UI, 등등)

    Environment가 구분되는 기준은 제 생각엔 Transform 형태에 따라 달라지는거같습니다

    (RectTransform 과 Transfrom)

 

 

 

 

 

 

아래 사진과같이 UI 프리팹일때 사용될 씬을 넣습니다

 

 

 

 

 

 

이제 적용하고 UI프리팹을 열어 프리팹모드로 하게 되면 사용햇던 씬의 Canvas가 적용되어있다는 것을 확인할수 있습니다.

Scene 뷰의 UI가 깨지지 않는것도 확인할수 있죠~

 

 

 

 

이상 프리팹 에디터 환경의 Scene 변경하기 였습니다

 

반응형
반응형

 

 

오브젝트반투명 처리한 움짤입니다

 

 

구현 코드 내용입니다

 

public struct St_ObstacleRendererInfo
 {
        public int InstanceId;
        public MeshRenderer Mesh_Renderer;
        public Shader OrinShader;
 }


    private Dictionary<int, St_ObstacleRendererInfo> Dic_SavedObstaclesRendererInfo = new Dictionary<int, St_ObstacleRendererInfo>();
    private List<St_ObstacleRendererInfo> Lst_TransparentedRenderer = new List<St_ObstacleRendererInfo>();
    private Color ColorTransparent = new Color(1f, 1f, 1f, 0.2f);
    private Color ColorOrin = new Color(1f, 1f, 1f, 1f);
    private string ShaderColorParamName = "_Color";
    private Shader TransparentShader;
    private RaycastHit[] TransparentHits;
    private LayerMask TransparentRayLayer;
    
 void Init()
{
    	TransparentRayLayer =  1<<LayerMask.NameToLayer(DefineKey.LayerObstacle);
        TransparentShader = Shader.Find("Legacy Shaders/Transparent/Diffuse");
}
    
    void Update()
    {
       Camera_TransparentProcess_Operation();
    }

    void Camera_TransparentProcess_Operation()
    {
        if (MY_Char_Transform == null) return;

       

        // 반투명했던거 다시 월래 쉐이더로 복귀
        if (Lst_TransparentedRenderer.Count > 0)
        {
            for (int i = 0; i < Lst_TransparentedRenderer.Count; i++)
            {
                Lst_TransparentedRenderer[i].Mesh_Renderer.material.shader = Lst_TransparentedRenderer[i].OrinShader;
            }

            Lst_TransparentedRenderer.Clear();
        }



        Vector3 CharPos = MY_Char_Transform.position + MY_Char_Transform.TransformDirection(0, 1.5f, 0);
        float Distance = (Camera_Move_OJ.position - CharPos).magnitude;

        Vector3 DirToCam = (Camera_Move_OJ.position - CharPos).normalized;
        Vector3 DirToCharbehind = -MY_Char_Transform.forward;


        //플레이어몸에서 몸뒤 체크해서 걸리는오브젝트 반투명
        HitRayTransparentObject(CharPos, DirToCharbehind, Distance);
        //플레이어 몸에어 카메라까지 체크해서 걸리는오브젝트 반투명
        HitRayTransparentObject(CharPos, DirToCam, Distance);


        

    }


    void HitRayTransparentObject(Vector3 start, Vector3 direction, float distance)
    {
        TransparentHits = Physics.RaycastAll(start, direction, distance, TransparentRayLayer);

        for (int i = 0; i < TransparentHits.Length; i++)
        {
            int instanceid = TransparentHits[i].collider.GetInstanceID();
			
            //레이에 걸린 장애물이 컬렉션에 없으면 저장하기
            if (!Dic_SavedObstaclesRendererInfo.ContainsKey(instanceid))
            {
                MeshRenderer obsRenderer = TransparentHits[i].collider.gameObject.GetComponent<MeshRenderer>();
                St_ObstacleRendererInfo rendererInfo = new St_ObstacleRendererInfo();
                rendererInfo.InstanceId = instanceid; // 고유 인스턴스아이디
                rendererInfo.Mesh_Renderer = obsRenderer; // 메시렌더러
                rendererInfo.OrinShader = obsRenderer.material.shader; // 장애물의쉐이더

                Dic_SavedObstaclesRendererInfo[instanceid] = rendererInfo;
            }

			// 쉐이더 반투명으로 변경
            Dic_SavedObstaclesRendererInfo[instanceid].Mesh_Renderer.material.shader = TransparentShader;
            //알파값 줄인 쉐이더 색 변경
			Dic_SavedObstaclesRendererInfo[instanceid].Mesh_Renderer.material.SetColor(ShaderColorParamName, ColorTransparent);

            Lst_TransparentedRenderer.Add(Dic_SavedObstaclesRendererInfo[instanceid]);
        }
    }

 

 

- Init 함수 

TransparentRayLayer =  1<<LayerMask.NameToLayer(DefineKey.LayerObstacle);  //레이캐스트 시 장애물 오브젝트만 감지하기 위해 레이어설정
 TransparentShader = Shader.Find("Legacy Shaders/Transparent/Diffuse"); // 반투명쉐이더로 교체할 쉐이더
(사용한 쉐이더는 유니티 기본 쉐이더이더 입니다, Transparent가 있는 다른 쉐이더로 사용하셔도 됩니다

 

 

 

- Camera_TransparentProcess_Operation 함수

  반투명을 했엇던 오브젝트를 담은 리스트 (Lst_TransparentedRenderer) 를 다 검사하여 원래 쉐이더로 바꿔줍니다.

  

  두군데를 레이를 쏠 준비를 합니다. 첫번쨰는 몸뒤에 오브젝트가 걸리는지 확인하기위해 몸뒤 방향, 거리 , 캐릭터위치 를 구합니다,

두번쨰는 캐릭터에서부터 카메라까지 걸리는 오브젝트가 있는지 확인하기 위해 캐릭터에서 카메라의 방향, 거리, 캐릭터 위치를 구합니다

 

 

- HitRayTransparentObject 함수

 시작위치, 방향, 거리 를 변수를 가지고 Physics.RaycastAll 를 사용하여 해당방향에 걸리는 장애물오브젝트를 다 가져옵니다.

 

ray 에 걸린 정보로 오브젝트의 인스턴스Id, renderer 정보를 찾아 구조체에 담아 
오브젝트 인스턴스Id 키값으로 컬렉션에 담아둡니다(Dic_SavedObstaclesRendererInfo)

 

컬렉션(Dic_SavedObstaclesRendererInfo)에 담아두는 이유는 매프레임마다 같은오브젝트가 레이에 걸리게 된다면 
 같은 오브젝트의 정보를 계속 찾고 구조체에 담아 두고하는일이 비용적으로 크고 부담이기 때문에 

컬렉션에 담아 다음프레임에 같은 오브젝트가 걸리더라도 컬렉션에 담은 거가지고 활용하기위해서 입니다

 

결론적으로 부하를 줄이기위함입니다.

 

렌더정보를 찾으 장애물 오브젝트의 쉐이더를 반투명쉐이더로 바꾸고 반투명쉐이더의 _Color파라미터의 색 alpha 값을 을 변경해줍니다. 

 

* 쉐이더마다 선언된 color 파라미터 이름이 다를수 있습니다. 사용하실 쉐이더 Properties를 확인하여 주세요

 

 

변경된 오브젝트의 구조체정보를 리스트에 담습니다

 

이상 오브젝트반투명 처리였습니다

 

 

반응형
반응형

RectTransform item;

float width = LayoutUtility.GetPreferredWidth(item);

 

item 오브젝트가 비활성화 되어 잇거나 item 오브젝트의 부모 오브젝트가 비활성화 되어 있으면

 

LayoutUtility.GetPreferredWidth 은 0 으로 반환됩니다 .

 

활성이 되야 LayoutElement 에 지정한 PreferredWidth 값이 잘 반환됩니다

 

LayoutUtility.GetPreferredHeight( ) 도 마찬가지 입니다!!

반응형
반응형

 

float SightAngle = 70f; //시야각 범위
Transform  AttackTargetPlayer;
    bool IsTargetInSight()
    {
       
       	//타겟의 방향 
         Vector3 targetDir = (AttackTargetPlayer.position - Tr.position).normalized;
         float dot = Vector3.Dot(Tr.forward, targetDir);
         
         //내적을 이용한 각 계산하기
         // thetha = cos^-1( a dot b / |a||b|)
         float theta = Mathf.Acos(dot) * Mathf.Rad2Deg;

         //Debug.Log("타겟과 AI의 각도 : " + theta);
         if (theta <= SightAngle) return true;
         else return false;

        
         return false;
        
    }

 

두벡터 사이각도  즉, 자신이보는 시선 방향이랑 자신과 타겟의 방향 사이각도를 구하는것 !

 

자신이 보는 시선 => Tr.forward

자신과 타겟의 방향  Vector3 targetDir = (AttackTargetPlayer.position - Tr.position).normalized; 

 

내적을 이용하여 두사이각을 알아낸다

A dot B = |A||B|cos@  이다

cos@ =  A dot B / |A||B|

@ = cos^-1(A dot B / |A||B|)

 

 

위에 유도된 식으로 @ 각을 알수 있다 ( 참고로 @ 는 라디안 이다)

 

코사인의 역함수는 아크코사인 이다.

 

cos^-1 => 코드에서 mathf.Acos 를 활용하면된다.

 

A dot B 는 Vector3.Dot(a,b) 를 활용하면된다

 

|A||B| 는 A벡터의 크기, B 벡터의 크기이다. 

 

A = Tr.forward 

B =  Vector3 targetDir = (AttackTargetPlayer.position - Tr.position).normalized; 

 

Tr.foward 는 오브젝트로컬 기준으로 z방향으 노말벡터를 의미한다

B =  Vector3 targetDir  는 적방향의 노말벡터를 의미한다

 

노말벡터의 크기는 1이기에 

| 1 || 1 | = 1 이다 

 

그래서 위 코드에 |A||B|이 생략 되었다. 5/1 = 5 아니겟는가

 

 

라디인값인 Mathf.Acos(dot) 는 각도를 구하려면 변환해야한다. 그래서 mathf.Rad2Deg 를 곱해준다

 float theta = Mathf.Acos(dot) * Mathf.Rad2Deg; 

 

계산된 theta가 원하는 제한각도 보다 작으면 시야범위안에 있는것이다.

 

 

 

 

 

반응형
반응형

- 문제

 

아래 사진에 표시된 것 같이 Profiler 돌려봤을떄 Physics.SyncRigidbodyTransfom 및 Physics.SyncColliderTransform Cpu 점유율이 높거나 Selfms(지연시간) 높을떄가 있다. (부하현상) 
제 프로젝트에서는 Physics.SyncRigidbodyTransfomPhysics.SyncColliderTransform 의해 부하가 많이 커져 문제점을 파악하려고 하였습니다.

모바일Device 에서 Profiler 돌려보았을때는 점유율이 4~5퍼 정도 먹고 있어 프레임드랍 현상이 심했습니다.

현재 제프로젝트에서는 여러 캐릭터 오브젝트가 NaviMeshAgent를 활용하여 움직을 구현하고 있는데

이로 인해  Physics.SyncRigidbodyTransfom 가 크게 발생하고 있엇습니다.

 

 

 

 

-문제 파악

우선 Physics.SyncRigidbodyTransfom 및 Physics.SyncColliderTransform Cpu 점유를 하고 있는 이유는 

Unity 물리연산에 의한 점유 입니다 .

ProjectSetting -> Physics -> Auto Sync Transform 을 On 하면 Physics.SyncRigidbodyTransfom 및 Physics.SyncColliderTransform 가 발생하게 됩니다.

Auto Sync Transform의 기능은 Rigidbody와 Collider 간의 물리 충돌 연산시 현재 프레임에 에서 물리연산의값이 나타 나도록 하는 기능입니다.

Unity 2017.2 버전 이후부터  Auto Sync Transform On/Off 기능을 추가 된것이고 

이전 버전에서는 항상 Auto Sync Transform가 이루어 졋습니다.

하지만 프로젝트마다 항상 물리적인 연산에 의해 sync가 맞출 이유가 없는데에 서는 부하만 잡으니 위 옵션을 넣은거 같습니다.

 

Auto Sync Transform 을 off 하게 되면 물리적용된 대상이 이전프레임에서 Transform이 유지 되는것을 확인 할수 있습니다. (ex. 두 박스 콜리더가 서로 붙딪힐떄 두 박스간 거리가 완전 0 일때 붙딪히는게 보이는게 아니라 

 붙딪히고 서로 약간씩 뚫고나가고 나서야 붙딪힌 모습을 볼수 잇습니다.)

 

 

어쩃든... Auto Sync Transform을 On하니 부하현상이 발생해서.. Off를 해야겟다고 생각햇습니다.

하지만 Off를 하면 NaviMeshAgent 사용하는 오브젝트는 NaviMeshAgent.SetDestination()에의해움직이지가

않습니다 .... 이유는 찾지는 못햇습니다....

 

 

 

 

 

 

 

- Auto Sync Transform Off 하면서 네비 움직이기 해결하기

Auto Sync Transform Off 하면서 네비 움직이게 하는 방법은 아래 코드와 같이 해결하였습니다

 

 

publice NavMeshAgent nav;

void Start()
{
   nav.updateRotation = false; //네비의해 자동 회전을 끕니다
   nav.updatePosition = false; //네비의해 자동 이동을 끕니다
}


void Move(vector3 moveDestination)
{
  nav.SetDestination(MoveDestination);
  
  //네비의 nav.nextposition 을 현재 오브젝트 위치에 할당
  transform.position = nav.nextposition;

}

 

 

  nav.updateRotation = false , nav.updatePosition = false 로 회전과 이동을 네비에의해 행해지는것을 꺼주고 

수동으로 Position 이동을 해줍니다.

 

 

 

 

 

 

아래 사진처럼 Auto Sync Transform Off후 네비로 수동으로 위치,회전 설정하였을떄 Profiler 에서 

Physics.SyncRigidbodyTransfom  Physics.SyncColliderTransform 가 발견되지 않는것을 확인할수 있습니다.

 

반응형
반응형

 

Rendertexture 기능을 이용해 UI에 캐릭터 모델을 나타 내는걸 진행해보았습니다.

 

 

기존에는 canvas 에서 해당 UI씬 마다 필요 캐릭터모델을 로드하고 위치하게 했엇는데,

Rendertexture 를 사용하면 한 캐릭터모델만 로드하여 사용할수 잇어 클라이언트 측면에서 이점이 있는걸로 생각이 됩니다.

 

 

 

-사용방법

 

1. 프로젝트에 Create / Rendertexure 로 렌더텍스쳐 파일을 생성합니다.

 

 

 

 

 

 

 

 

2. Size를 설정합니다. 사이즈가 크면 클수록 화질이 선명하게 나옵니다.

 Render 될 오브젝트(캐릭터)가 크게 비추게 원하시면 1024/1024 정도 사이즈로 설정해두면 좋습니다.

사이즈 값은 2제곱수로 해야합니다 (ex. 256/256 , 512/512  , ...)

 

 

 

 

3. 씬내에 카메라를 생성합니다

 - 카메라 TargetTexture에 생성한 RenderTexture를 할당합니다.

 - 카메라에 CullingMask는 선택 유무 입니다. 캐릭터만 보이고 싶다하면 캐릭터에 설정한 layer를 선택하게 되면

   Render 될때에 캐릭터만 보이게 될것입니다.

 - 카메라 Projection 은 Orthographic으로 합니다. UI 에 캐릭터를 나타낼것이므로 원근감이 필요없어서 그렇습니다. 

   만약 Render 타입이 3D 형태 라면 원근감이 느껴지는 Perspective를 사용하세요

 

 

 

 

 

 

 

4. 해당 UI에 RawImage를 만듭니다. ( 씬내 우클릭 -> UI -> RawImage )

 - 생성된 RawImage 에 생성한 RenderTexure를 할당합니다.

 - RawImage 사이즈 (width,height)로 렌더될 사이즈를 결정해줍니다. 

   (RawImage 사이즈 가 작게 표시할경우 위 2번에 Rendertexture의 size 를 256/256 정도로 해줘도 좋습니다,

    하지만 RawImage 사이즈가 UI 전역에해당하는 크기이다 하면 size는 1024/1024 이상으로 잡아 줘야, Render 시

    캐릭터가 꺠지지 않게 보입니다)

 - RawImage 사이즈 설정하게 되면 그림의 초록 부분에 오브젝트(캐릭터)가 보이게 될것입니다.

 

 

 

 

 

5. 씬에 생성한 카메라 바라보는 방향에 캐릭터를 생성합니다.

 - play 를 하면 UI에 RawImage 부분에 캐릭터가 보이는것을 확인할수 있습니다.

반응형

+ Recent posts