본문 바로가기

Unity/3D

Unity 3D 벽점프를 위한 반사벡터 구하기 [C#] / Unity VS

반응형

자바스크립트를 쓰다가 c#쓰니까 너무 편하다..

한글을 쓰고싶으면 비쥬얼스튜디오 for unity를 설치하면 됩니다.


적용법은... 그냥 설치하시고

유니티 프로젝트를 하나 여시고


Edit -> Preperence -> Externel Tools 에서 스크립트 에디터를 비쥬얼 스튜디오로 변경하신다음 껐다키시면 됩니다.


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


초기엔 온갖 뻘짓을 했다.


카메라의 위치를 노말값으로 해서 법선벡터랑 반사각 연산을 해본다거나... 그러다가

카메라의 로테이션을 벡터로 변경해서 노말값으로 법선벡터랑 반사각을 연산 해본다거나..

이런식의 연산을 하다보니 한쪽 방향으로만 힘이 가해지는 문제가 발생하였고.

또 한,  Z축에 대한 힘이 전혀 없음을 알았다.


수학이 짧아 아직도 벡터와 쿼터니온에 대해 이해가 부족한데.


http://sharkmino.tistory.com/1436 다른분의 정보를 보면서 출발지점이란게 대체 무엇일까 하며 고민을 해본 결과.

난 이미 이 부분을 구했음을 깨달았다.


바로, 캐릭터를 움직이게 하기위해 연산했던, moveDirection (dr) 변수였다.

앞 글을 안읽어 보신 분들을 위해 짧게 설명하자면.


키 입력에 따라서 캐릭터가 이동할 벡터를 구한 변수를 뜻한다.

또한, 필자는 캐릭터컨트롤러를 쓴다.


캐릭터가 벽에 부딪혀 움직이지 못하더라도, 연산은 분명 이행되고 있었다. 따라서, 벽을 향해서 나아가면 당연히 이 벡터는 벽을 향한 출발지점(벡터)가 된다.



이렇게 하면 X축은 물론 Z축에 대한 값도 나오며, 자동으로 +-가 연산된다.

즉, 많은 방향으로 연산이 가능해진다는 점이다.



#추가로#

http://sjcy.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%98%EC%82%AC-%EB%B2%A1%ED%84%B0-%EA%B5%AC%ED%95%98%EA%B8%B0

이 분의 연산을 참고하여 반사벡터를 구했다.

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

필자는 디버깅을 위해 점프 제한 같은 제약을 걸지 않고 값을 확인하기 위해 충돌동안 매 프레임 호출되는 

void OnCollisionStay(Collision col); 함수를 사용했다.


매프레임 호출된다길레 당연히 FixedUpdate() 에서 연산된 dr의 값을 불러오면 충돌함수에서도 쓸 수 있을 줄 알았는데

FixedUpdate 다음에 호출 되는 바람에 dr은 이미 초기화가 되버린 후라서 0,0,0 값이 나왔다.

따라서, FixedUpdate에서 dr을 저장해두고 가져왔다.


ex) 

Vector3 walltmp; //전역 변수, 이동벡터를 저장해둔다.

void FixedUpdate(){

이동 연산이 끝난 끝난 후, 

walltmp = dr;

}


그 다음, 이 값을 이용해서 충돌 함수에서 처리를 하도록 가져왔다.

 void OnCollisionStay(Collision col)

    {

        ContactPoint cp = col.contacts[0]; //첫번째 충돌한 벽에 대한 충돌자

//cp.normal은 그 충돌자에 대한 법선벡터를 뜻한다.

        float dot = Mathf.Abs(Vector3.Dot(Camera.main.transform.forward, cp.normal)); 

// 카메라의 정면벡터와 충돌부의 법선벡터를 내적하여 각을 구한다.


       // Debug.Log(dot); 

        if (dot >  0.149f) //필자는 각이 0.149f 보다 크면 벽을 바라본다고 판정을 갖게 되었다. 아슬아슬하게 카메라가 벽을 바라보고 있는 정도다.

        {

            if(!ctrler.isGrounded && Input.GetButtonDown("Jump") && Y != 0.0f) 

            {

                wallJumpV=Vector3.Reflect(walltmp.normalized, cp.normal).normalized * speed;  //이동속도를 곱해서 힘을 좀더 강하게 한다.

                Debug.Log(walltmp.normalized + " --- " + Camera.main.transform.rotation.eulerAngles.normalized + " --- "+ cp.point + " --- " + cp.normal + " --- " +wallJumpV); //디버깅

                tmpJumpPower = wallJumpV.y * 2; //y의 힘을 더 강하게 해준다.

                wallJumpV.Set(wallJumpV.x, 0, wallJumpV.z);

                

            }

        }


    }




#코드에 대한 설명#

ContactPoint 란, 충돌점을 뜻 합니다. 

col.contacts[] 는, 여러 충돌점을 배열로저장되어있는 것인데, 벽점프는 첫번째 충돌벽에서 할 것이기에 [0] 인덱스의 충돌점을 저장합니다.


Mathf.Abs() 함수는 절대값을 구합니다. 부호가 바뀌면 if문을 작성하는데 피곤하기에 절대값을 주었습니다.

제가 수학이 짧아서.. 내적하는데 -가 나올일이 있는진 잘 모르겠는데...  


Vector3.Dot() 함수는 두 벡터에 대해 내적을 하는 것입니다.

카메라의 정면부 (바라보고 있는 방향), 충돌점의 법선벡터를 내적합니다.


법선벡터란, 면에 대해서 수직으로 나오는 벡터를 뜻합니다. 쉽게 충돌점에대해 nomal값을 구하면 법선벡터라고 합니다.


디버깅으로 카메라를 돌려보며 값을 비교해보았을때, 내적값이 0.149f 값보다 컸을때 (약 0.15f 이상) 벽을 간신히 바라보고 있는 정도의 값이었습니다. 


그다음 비교문은 캐릭터컨트롤러를 통해 땅이 아니며, 점프키를 눌렀고, 앞뒤키를 누르고 있었을 때 에 대해 벽점프를 하겠다. 라고 한것입니다. 굳이 if문을 분할 한것은 가독성을 위해 잠시 나누엇으며... 저는 최적화 라던가는 일단 만들고 하는 스타일이기에 이렇게 했습니다.


그다음 벽점프에 대한 벡터를 구해야합니다.

Vector3.Reflect() 함수는 두 벡터에 대해 반사각을 구합니다.

반사각이란 입사각과 법선벡터를 통해 구하는 값입니다.


입사각은 FixedUpdate()에서 구한 캐릭터의 이동방향 벡터를 nomal값으로 한 벡터입니다.


그리고, Reflect()의 값을 nomal값으로 변경하여줍니다. 따라서 균등한 반사벡터를 구하게 됩니다.

반사벡터의 구함은 여기서 끝이며, 이후의 이 값의 가공은 원하시는데로 하시면 됩니다.



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


20170928 추가글


하지만, 이 반사벡터는 캐릭터가 이동하고자 한 방향에서 법선벡터와 함께 반사벡터를 구하기에

결과적으로 하단을 쳐다보고 벽점프를 하려하면 하단으로 힘이 가해져 마치 벽을 박차고 빠르게 떨어지는 듯 작동합니다.

이러한 부분에 대해서 적용을 하시려면, Y벡터가 마이너스가 되었음을 인식하고 +로 전환하는 수식을 넣으면 될것이라 생각합니다.

따라서 하단 대각선이든 하단을 쳐다보고 점프를해도 상단 벽점프로만 이어지도록 구현할 수 있습니다.



반응형