Clolent

※ 유니티 상에서 Read Only 파일에 어떠한 수정을 가해도 껏다키면 사라진다. 주의하자


※ 애니메이션 파일에서 Inspector - Anmation 에서 클립 추가하고, 몇프레임에서 몇프레임까지 주는 식으로 클립 주기 가능


※ 애니메이션은 하나의 파일에서프레임 단위로 찢어서 여러 모션들을 넣는경우가 있고, 각 모션마다 모두 따로만들어서 관리하는 케이스가 있다.


※ 후자의 치명적 단점 : 모델이 각각의 파일마다 필요하므로, 코스트가 굉장히 크다 ;; 

해결 : 클립파일만 뽑아내서 사용하고 모델링 파일을 하나만 남겨둔다. 


※ 전자의 치명적 단점 : 중간에 어떤 애니메이션의 프레임이 증가하거나 감소할 경우 나머지 애니메이션에도 모든 영향을 끼침


※ 애니메이션 이름 줄때 @ 를 사용하자, 모델명@클립이름 이 규칙을 사용하면 자동으로 포함을 한다.


※ Transform 혹은 GameObject 에 접근 할수 있다면 모든 컴퍼넌트에 접근할 수 있다.


※ GameObject.Contains( " " ) 로 느슨한 체크 가능 


※ Destroy ( ) 안에 this 등 다른 class나 컴퍼넌트를 넣는 경우가 있는데 이렇게 할 경우 해당 class 혹은 컴퍼넌트만 사라지게 된다. 그런데 나중에 이 컴퍼넌트 등이 필요해지면 또 AddCompoent 를 해서 추가해줘야하는데, 좋지않은 케이스다.


※ GetComponentInChildren< > 은 얼마나 하위에있건 상관없이 다 찾아서 컴퍼넌트를 가져오는데 가까운 순으로 탐색한다.


※ 계층 구조 어디에있던 해당 컴퍼넌트를 가져올수 있다. 

바로 Find 함수 사용법은 GameObject.Fine("이름").원하는컴퍼넌트 

 // 하지만 Find 는 굉장히 느리다 사용하지마!


※ 더욱더 좋은 방법이 있으니 바로 태그를 사용하는 것!

GameObject.FindGameObjectWithTag("태그명").컴퍼넌트    태그를 씁시다 태그를 ! 


※ 이런 실시간 링크가 필요한 이유는 서로 링크시켜야할 대상이 처음엔 Scene 에 존재하지 않을경우

 직접링크가 불가능하기 때문이다.


※ .magnitude 벡터의 성질중 하나로 한점에서 한점을 빼면 벡터가 나오는데, 그 거리를 구하는 방식으로 두 점의 

실제 거리를 반환 시켜준다. 같은 방법으로 Vector3.Distance ( 포지션, 포지션 ) 이 있다.


※ 벡터의 성질을 이해하자 A 에서 B 를 빼면 B에서 A를 향하는 벡터가 나온다. 


※ Normalize 정규화 그 벡터의 크기를 1로 만들어 주는것 


※ Quaternion.LookRotation ( 벡터 ) 벡터값을 기준으로 사원소값을 만들어준다 ! 


※ SimpleMove : 심플무브는 y값에 대한 모든 값을 무시해 버리고 이동을 하는데, 경사면오르기나 계단 등은 잘 오르지만점프만 안하는 것이다.




충돌 대상 구분하기


충돌한 대상이 무엇이냐에 따라서 서로 다른일이 일어나게 하고싶다라면 충돌이 발생했을때,구분하는 방법은 두가지가 있다.

하나는 충돌 대상의 Layer 을 구분하는 방법이고 하나는, 그냥 오브젝트의 이름을 비교하는 방법이다.


Layer 를 이용하여 비교하는 코드 


        if( other.gameObject.layer == LayerMask.NameToLayer("Player"))

        {

            other.gameObject.transform.position = new Vector3(0, 50, 0);

        }


혹은

        if( other.gameObject.layer == 12 )

        {

            other.gameObject.transform.position = new Vector3(0, 50, 0);

        }


슬슬 입질이 오고있다. Layer 의 형태를 기본적으로 int 형이다. Edit - ProjectSetting 에 들어가서 Layer 들을 보면,

숫자로 구분이 되어 있는 것을 알수있다. 그렇다면 위의 LayerMask.NameToLayer( "String" ) 이라는 것이 있다.

이는 저 스트링에 해당하는 친구의 레이어를 찾아서, int 값으로 반환해준다. 

반대로 LayerToName 을 하면 인트값을 주면 해당 Layer 의 String 값을 반환해준다. 

LayerMask.NameToLayer 을 이용하도록 하자, 왜냐하면 나주에 Layer 의 위치를 변경해서 기존 12가 아닌 15가 되어도 

스트링을 넘겨줬었다면 코드를 수정하지 않아도 문제가 일어나지 않기 때문이다.


이름으로 구분하는 방법은


other.gameObject.name.Contatins( "String" ) 이라는 방법이 있다. == 을 쓰는 것보다 Contains 를 쓰는 이유는, 

새로 만들어지는 경우 이름뒤에 Clone 이 붙기도 하고 해서 인식이 안되는 경우가 있으므로 Contains 를 통해서, 

true 조건을 조금 느슨하게 해주는 것이다.


        if( other.gameObject.name.contains( "Player" )

        {

            other.gameObject.transform.position = new Vector3(0, 50, 0);

        }



Tag 를 이용하여 비교하는 코드 

if (other.gameObject.tag == "Wall")

 태그는 gameObject 에서 불러올수 있으며 그냥 스트링 비교로 바로 체크가 가능하다. 굉장히 깔끔한것 같다. 

※ 레이어는 10번부터 사용하도록 하자 < 권장 사항 > 





Trigger 트리거

is Trigger 이라는 것이 Collider 안에 있고 체크가 해제되어있다. 트리거 라는 것은 방아쇠 라는 뜻으로 총을 한번쏘면 되돌릴수 없듯이 트리거는 이벤트를 발생시키는 일종의 방아쇠 역할을 한다. 그럼 Collider 과 같은 역할을 하는거 아닌가 라고 생각할수도 있지만, 큰 차이가 있다. 바로 물리적인 힘이 존재하지 않다는것이다. 


일상생활에서 예로 자동문을 생가해볼수 있다. 분명 우리가 어떤 부분의 트리거를 건드렸기 때문에 자동문이 열리는 것일텐데 우리가 뭔가에 부딪히거나 하는 느낌은 전혀 들지 않는다.


이 트리거에 대한 함수로는 OnTriggerEnter( Collider other ) 과 같은 식으로 사용한다. 


Animations

Anim Compression 애니메이션 압축할거냐 ? Keyframe Reduction : 지워도 큰문제 없을것 같은 값들을 지워서 용량 절약

Clips 일종의 배열로 관리 / Start End 로 시작프레임과 끝프레임 정해줄수 있음 ( 실수 값 ! )

Add Loop Frame 루프가 아닌 것을 루프인것처럼 만들어 주는 것 / 첫프레임과 끝프레임이 같아야하는것 

Wrap Mode : 애니메이션을 어떻게 재생 할것인가.

Default : Once 와 같음

Once  : 한번만 수행함

Loop : 무한 반복

Ping Pong : 끝 찍고, 다시 처음으로 돌아오서 다시 수행

Clamp Forever : 마지막 프레임을 계속 반복함 -> 부드럽게 모션을 만들기 위해서 ( 애니메이션 블랜딩을 위해 )

블랜딩 -> 두 애니메이션이 섞여서 부드럽게 이어지게 만드는것

모든 모션에 다 블랜딩이 무조건 좋다 ? 아니다 -> 블랜디을 안줌으로서 역동성고 속도감을 줄수 있다.

어디에 넣고, 어디에는 뺄꺼야 ? 눈으로 보고 판단하자.


Clip 미리보기 에서 No model is 어쩌고 나오면 드래그 해서 될만한 애를 넣고 재생시켜봄으로서 잘 되는건지 알수 있다.



※ Shift , 원하는 클립까지 클릭하고 그대로 누른상태로 끌어서 Animations 에 넣으면 순서대로 들어간다.


※ 재생 전에 속도를 조절해줘야, 애니메이션에 적용된다.


※ 애니메이션에 Play 는 그냥 재생시키고 Cossfade 는 애니메이션 블랜딩을 하면서 플레이 시킨다.



Lerp 함수

일종의 보관함수로서 인자는 From, To, Percent 라고 생각하면 된다.

ex )            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(dir), 

 rotationSpeed * Time.deltaTime);


어떤 값을 퍼센트로 받아오기때문에 확확 움직이지않고 천천히 돌거나 하는 등에 사용 가능 하다 ! 


데드존 코드분석

public class DeadZone : MonoBehaviour {

    void OnTriggerEnter(Collider other)  ※ 트리거에 들어간 순간 발동

    {

        if( other.gameObject.layer == LayerMask.NameToLayer("Player")) ※ 레이어가 Player 면

        {

            other.gameObject.transform.position = new Vector3(0, 50, 0);  ※ 위치를 옮겨버린다.

        }

        else if(other.gameObject.layer == LayerMask.NameToLayer("Bomb")) ※ 레어어가 Bomb 이면

        {

            Destroy(other.gameObject);    ※ 부딪힌 대상을 부숴버린다! 

           // Debug.Log("BOOM");

        }

    }

}


Enemy 코드분석

public class Enemy : MonoBehaviour {

    enum ENEMYSTATE          ※열거형으로 이제 적의 상태를 관리

    {

        IDLE = 0,

        MOVE,

        ATTACK,

        DAMAGE,

        DEAD

    }

    ENEMYSTATE enemyState = ENEMYSTATE.IDLE;   ※ 열거형 변수선언

    float stateTime = 0.0f;  ※ 상태에 대한 시간체크를 위해

    public float idleStateMaxTime = 2.0f;   ※ 시작 대기값

    public Animation anim;    ※ 애니메이션을 컨트롤 하기 위함, public 이므로 직접 링크 필요! 

    public Transform target = null;

    CharacterController characterController = null;  


    public float moveSpeed = 5.0f;

    public float rotationSpeed = 10.0f;

    public float attackRandge = 5f;

    public float attackStateMaxTime = 2.0f;


    void Start() {

      //  target = GameObject.Find("Player").transform;

        target = GameObject.FindGameObjectWithTag("Player").transform; 

 ※ 왜 public 인데 직접 링크를 안했는지 알수 있는 부분이다. 만약 게임 타이틀 화면등에 플레이어가 

존재안하면? 프리팹에 직접링크를 시켜려해도 플레이어는 Scene에서 존재하지않으므로 링크가 불가능이다.

따라서 이런식으로 실시간 링크를 시켜주어야 하는 것이다.

        characterController = GetComponent<CharacterController>();

        }


    void Awake() {  ※ Awake 함수가 일단은 Start 보다 빠르다고 알고는 있다. 중요한건 단 한번 수행된다는것

        Init();

    }



    void Update()

    {

        switch (enemyState)  ※ enemyState 함수에 의해 매 프레임 스위치 문을 수행한다.

        {

            case ENEMYSTATE.IDLE:

                      Idle();

                      break;

               

            case ENEMYSTATE.MOVE:

                Move();

                    break;


            case ENEMYSTATE.ATTACK:

                Attack();

                break;

            case ENEMYSTATE.DAMAGE:

                Damage();

                break;


            case ENEMYSTATE.DEAD:

                Dead();

                break;

        }

    } // END UPDATE


    void Idle()

    {

        stateTime += Time.deltaTime;  ※  매 프레임과 프레임 사이 시간을 더해주는 것으로 시간체크가가능하다.

        if(stateTime > idleStateMaxTime)     위에서 설정해줬던 시작대기시간을 넘어가면 

        {

            stateTime = 0.0f;

            enemyState = ENEMYSTATE.MOVE;  ※ 움직임으로 상태변환 ! 

        }

    }

    void Move() {

        anim["Run"].speed = 1.5f;  ※ Run 애니메이션의 수행속도를 1.5 배 해준다.

        anim.CrossFade("Run");     ※ 이전 단계의 애니메이션에서 블랜딩하면서 Run 을 실행 시켜준다 ! 

        float distance = (target.position - transform.position).magnitude;  ※ target(Player) 와 자기자신의 거리값 구하기

        if( distance < attackRandge)   ※ 만약 현재 타겟과의 거리가 공격범위보다 작다면

        {

            enemyState = ENEMYSTATE.ATTACK;   ※ 공격 상태로 넘어간다.

            stateTime = 2.0f;   ※ 넘어가서는 바보같이 멀뚱멀뚱 보지않고 바로때리기위한 설정

        }

        else

        {

            Vector3 dir = target.position - transform.position;   ※ 만약 공격범위 안에 없다면 ?

            dir.y = 0.0f;

            dir.Normalize();   ※ 거리를 정규화 시켜준다 아니면 멀때는 빨리다가오고 가까우면 천천히 다가올것이다.

            characterController.SimpleMove(dir * moveSpeed);  ※ 심플 무드 : 점프 빼고 다됨


            transform.rotation = Quaternion.Lerp(transform.rotation, ※ Lerp함수 위의 참조

                Quaternion.LookRotation(dir),

                rotationSpeed * Time.deltaTime);

        }




    }

    void Attack() {

        stateTime += Time.deltaTime;

        float distance = (target.position - transform.position).magnitude;

        if (stateTime > attackStateMaxTime && distance < attackRandge)

  ※ 공격범위 안에 있으면 공격하되, 쿨타임이 존재

        {

            stateTime = 0.0f;

            anim.Play("Attack");

            anim.PlayQueued("Idle", QueueMode.CompleteOthers);

        }

        else 

        {

                enemyState = ENEMYSTATE.MOVE;          


        }

    }

    void Damage() { }

    void Dead() { }

    void Init()

    {

        enemyState = ENEMYSTATE.IDLE;

        anim["Idle"].speed = 1.5f;

        anim.Play("Idle");

    }

    

}


댓글 로드 중…

블로그 정보

Clolent - 커피물조절달인

최근에 게시된 글