반응형

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();
        }
    }

반응형
반응형

 

 

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

 

 

구현 코드 내용입니다

 

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 가 발견되지 않는것을 확인할수 있습니다.

 

반응형
반응형

 

 

3D 구체(공을) 벽이나 바닥에 닿을시 계속 튕기게 (바운딩) 되게 해 보았습니다.

 

- 공에적용한 컴포넌트 

 1. Rigidbody 컴포넌트

 2. Sphere Collider 컴포넌트

 

 

 

- 설명

 

일반적으로 물리가 적용되어 공이 바운딩을 하긴 하나 여러차례 지날수록 바운딩 되지 않고 제자리에 잇거나 굴러 가기만 하죠. 

 

이번에는 공을 계속 튕기도록 해보았습니다. 

 

 

우선 아래 예시 움짤입니다

 

 

 

 

 

 

원리는 아래 그림처럼 공이 벽이나 바닥을 충돌했을때 충돌 접점을 알아낸후 
공과 충돌체의 역방향으로 Rigidbody.Addforce 하는겁니다

 

 

이제 공이 충돌되었을떄 처리 되는 코드 입니다.

void OnCollisionEnter(Collision collision)
    {
        
        
        
        ExcecuteReBounding();
    }
    
    
     void ExcecuteReBounding(Collision collision)
    {
      

        ContactPoint cp = collision.GetContact(0);
        Vector3 dir = Tr.position - cp.point; // 접촉지점에서부터 탄위치 의 방향
        rigidbody.AddForce((dir).normalized * 300f);
    }

 

Collision.GetContact 함수는 충돌시 충돌되는 접점의 정보를 반환해주는 함수 입니다.
반환되는 타입은 ContactPoint 이며  ContactPoint의 멤버변수인 Vector3 point 가

접촉된 지점을 의미합니다.

 

공의위치 - 접촉지점 = 밀어낼 방향 이 되므로 이방향으로 Addforce를 가해주면 공이 계속 튕기는 느낌을 줄수 있습니다.

 

 

이상 공 계속 튕기기 였습니다.

 

반응형
반응형

3D 유도탄만들기.

 

벡터 내적외적 이용해서 할려고 하는데 적용하기에는 시간이 걸려서 

이번 포스트에는 타겟 방향벡터 가지고 유도탄 만들기를 하겠습니다.

 

추후에 이 포스트에 벡터 내적외적 이용해서 구현한 방법도 추가해서 올릴게요~

 

 

 

우선 구현 움짤입니다

 

6개 로켓이 한방향으로 나가는 유도탄(유도체는 앞쪽에 회색기둥)

 

 

 

 

 

 

구현 코드내용입니다

private float dis;
private float speed;
private float waitTime;
public Transform TargetTr;


void Start()
{
   dis = Vector3.Distance(Tr.position, TargetTr.position);
   
   //포탄생성후 초반에 포탄이 벌어지듯이 연출하기위해
   //포탄의 회전을 캐릭터위치에서 포탄의 위치의 방향으로 놓습니다
   transform.rotation = Quaternion.LookRotation(transform.position - 캐릭터의위치);

}


void Update()
{
	DiffusionMissile_Move_Operation();
}

void DiffusionMissile_Move_Operation()
    {
        if (TargetTr == null) return;

        
        waitTime += Time.deltaTime;
        //1.5초 동안 천천히 forward 방향으로 전진합니다
        if (waitTime < 1.5f)
        {
            speed = Time.deltaTime;
            transform.Translate(Tr.forward * speed, Space.World);
        }
        else
        {
        // 1.5초 이후 타겟방향으로 lerp위치이동 합니다

            speed += Time.deltaTime;
            float t = speed / dis;

            Tr.position = Vector3.LerpUnclamped(Tr.position, TargetTr.position,  t   );

        }


		// 매프레임마다 타겟방향으로 포탄이 방향을바꿉니다
        //타겟위치 - 포탄위치 = 포탄이 타겟한테서의 방향
        Vector3 directionVec = TargetTr.position - Tr.position;
        Quaternion qua = Quaternion.LookRotation(directionVec);
        Tr.rotation = Quaternion.Slerp(Tr.rotation, qua, Time.deltaTime * 2f);
        


    }

 

포탄생성시 위치값은 랜덤으로 해줬습니다.

이후에 transform.rotation = Quaternion.LookRotation(transform.position - 캐릭터의위치); 에서

각 포탄의 방향을 공작날개같이 펼치듯이 하기 위해 방향을 변경합니다.

 

경과시간 1.5초 전 까지는 공작날개같이 펼쳐 놓도록한 포탄의 방향에서 forward 방향으로 전진 시켜줍니다.

 

경과시간 1.5이후에는 타겟으로 위치이동을합니다.

 

매프레임마다 포탄의 방향은 타겟방향으로 향하게 Slerp 처리 해줍니다.

 

위 유도탄 만들기 코드 중 DiffusionMissile_Move_Operation함수 만 보시면 될거같네요 

 

이상 유도탄 만들기 였습니다.

반응형
반응형

충돌범위 안에 있는 모든 충돌체를 가져와 참조 하고 싶다, 충돌범위안 모든 콜리더가 잇는 오브젝트를 알고 싶다

 

그러면 Physics.OverlapXXX 함수들을 사용하면 되는데요 .

Overlap함수 종류로는, OverlapBox , OverlapSphere , OverlapCapsule 가 있습니다.

 

이중에서 OverlapCapsule 함수 사용법을 알아보겟습니다 .

 

OverlapCapsule 함수 정의를 찾아보면 아래 와같습니다 

 

 

 

----------------------------------------------------------------------------------------------------------------------------------

public static Collider [] OverlapCapsule ( Vector3 point0 , Vector3 point1 , float radius , int layerMask = AllLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);

 

매개 변수

point0 start캡슐 의 구의 중심 .
point1 end캡슐 의 구의 중심 .
radius 캡슐의 반경입니다.
layerMask 레이어 마스크 캡슐을 전송할 때 선택적 colliders을 무시하는데 사용된다.
queryTriggerInteraction 이 쿼리가 트리거에 도달해야하는지 여부를 지정합니다.

----------------------------------------------------------------------------------------------------------------------------------

 

OverlapCapsule 함수에 들어가는 매개변수중에서 point0, point1 두 인자가 어떻게 값을 넣어야하는지 궁금햇습니다.

구글링해도 딱히 사용법도 안올라와서 제가 해봣습니다.

 

start캡슐 의 구의 중심 ? end캡슐 의 구의 중심? 무슨 말인가 했엇죠 

 

end캡슐 의 구의 중심 / start캡슐 의 구의 중심 . 아래 사진과같은 의미입니다.

 

사진에 명칭을 달리햇지만 (Pos1, Pos2)

 

결론적의미는 캡슐의 맨위쪽 위치와  맨아래쪽 위치를 말하는겁니다.

 

 

아래는 OverlapCapsule 함수 사용법 예시 입니다.

  void Update()
    {
    
    	//반경
	float radius = 5f;
        
        //캡슐의 맨아래 위치
        Vector3 pos1 = new Vector3(Tr.position.x, Tr.position.y-ConstHeight, Tr.position.z);
        //캡슐의 맨위 위치
        Vector3 pos2 = new Vector3(Tr.position.x, Tr.position.y + ConstHeight, Tr.position.z);

        Collider[] colls = Physics.OverlapCapsule(pos2, pos1, radius);

        for (int i = 0; i < colls.Length; i++)
        {

            _player = colls[i].GetComponent<Player_Script>();

            if(_player != null)
            {
               //...
            }
        }


        
    }

 

ConstHeight  변수는 임의지정한 높이 값입니다. 정해준 값에 따라 캡슐의 높이가 설정됩니다.

 

 

 

 

 

* 추가적으로 OverlapCapsuleNonAlloc 함수 사용법도 위와 동일합니다 .

OverlapCapsuleNonAlloc는 할당한 충돌체들을 반환하지않고 충돌한 갯수를 반환합니다. 그러므로 OverlapCapsule 사용할때보다 메모리 누적이 덜되기 때문에 게임부하에 덜주게 됩니다. 반대로 OverlapCapsule는 반환되는 충돌체들을이메모리할당하기 때문에 메모리 누적이 발생합니다.

 

 

 

이상  Physics.OverlapCapsule 함수 사용법 이었습니다.

반응형

+ Recent posts