Post List

레이블이 Unreal Engine4인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Unreal Engine4인 게시물을 표시합니다. 모든 게시물 표시

2017/12/07

Unreal4__10_Head_Tracking

게임을 보면 대기상태에서 카메라가 케릭터 앞에있을때 케릭터는 카메라를 바라보는 경우가 있지요. 오늘은 기능을 소개해보겠습니다.

이렇게 보니 약간 무섭네요 T^T... 아무튼 기술 구현을 목적으로... 

카메라의 Forward Vector 플레이어의 Forward Vector 각도를 계산해 일정범위내에 있을때에만 카메라를 바라보게 제한을 두었습니다.

스켈레톤에 목에 해당하는 본의 회전값을 제어하기 위해 저는 블루프린트로 접근해서 만들어보았습니다.

카메라와 플레이어간의 각도계산은 C++에서 이루어지며, 바라보게되는 범위내에 있으면 블루프린트로 전달할 있는 bool 변수 값을 추가하여 블루프린트에서 해당 값이 true 되면 회전값을 변경해주도록 하였습니다.

우선 플레이어의 블루프린트를 먼저 보겠습니다


노드들이 많습니다…
우선 'Follow Camera', 'Mesh', 'Capsule Component' 필요합니다.
Follow Camera  캐릭터를 바라보는 벡터를 구하기 위함입니다.
Mesh 회전할 본의 월드 좌표를 얻어 위에서 말했듯이 카메라를 바라보는 벡터를 구하기 위함입니다.
Capsule Component 실제 플레이어가 향하고 있는 Forward Vector 얻어오기 위함입니다.

여기서 Mesh Get Socket Location 노드는 스켈레톤에서 해당하는 본의 월드 공간 좌표를 반환하기때문에 Follow Camera 월드좌표와 연산을해도 문제가 없답니다.

이제 카메라를 바라보는 벡터, 플레이어의 Forward Vector 카메라를 바라보는 벡터로 이동하기 위한 회전 값을 구하는 노드를 연결해보겠습니다.

먼저 Follow Camera로부터 GetWorldLocation노드로 월드 위치 좌표를 얻어옵니다.
그리고 저는 Mesh 'neck' 해당하는 본을 회전시킬 생각이기때문에 'neck' 해당하는 본의 월드 위치 좌표를 얻어옵니다.

그리고 벡터를 빼주는데요, 순서는 [ 카메라 위치 좌표 - 'neck' 좌표 ] 입니다그래야 플레이어가 카메라를 바라보는 벡터가 만들어지지요.
만약 반대로 뺏을경우 카메라가 플레이어를 바라보는 벡터의 값이 나오게 되기때문에고려해서 연산해주시면 되겠습니다.

플레이어가 카메라를 바라보는 벡터를 Tracking Vector 라고 하겠습니다.

Tracking Vector Normalize 통해 단위벡터로 만들어줍니다.

이제  Forward Vector Tracking Vector간의 사이각도를 구해보겠습니다.

벡터사이의 각도를 구하는 방법은 벡터의 내적 값을 이용하는 방법입니다.
벡터의 내적값을 acos으로 변환하면 라디안 단위의 세타값이 나옵니다.

벡터의 내적을 구하는 공식은 두가지 입니다.
  1. A · B = Ax * Bx + Ay * By + Az * Bz
  2. A · B = ( A.length * B.length ) * cosθ

Ax * Bx + Ay * By + Az * Bz = ( A.length * B.length ) * cosθ;
이렇게 표현할 있습니다.

( Ax * Bx + Ay * By + Az * Bz  ) / ( A.length * B.length )  = cosθ;
사이각을 얻기위해 cosθ만 남기고 왼쪽으로 모두 넘겨줍니다. 그럼 위와 같은 식이 나오고,

( A · B ) / ( A.length * B.length ) = cosθ;
위와 같은 식으로도 되겠지요.  Cosθ를 각도로 바꾸게되면 acos 사용하면 됩니다.

Acos이란 cos함수의 역함수이죠, 반환되는 값은 라디안값이 됩니다.

그러면 식은,
Acos( ( A · B ) / ( A.length * B.length ) ) = radian 됩니다.

이게 여기서 식을 간단하게 계산하기위해
Tracking Vector Normalize 통해 단위벡터로 만들어줍니다Normalize 호출함으로써 백터는 단위백터로 바뀌게되고 길이가 1 백터로 되지요.

그렇게 되면
Acos( A · B ) = radian

이렇게 간단해지게 된답니다.

식을 이용해 Capsule Component Forward Vector Tracking Vector 내적하고  결과값을 Acos으로 변환하여 벡터간 사이값을 구하게 됩니다.

애님 그래프로 와서 'Neck Rotation'값을 실제 'neck_01' 해당하는 본에 회전값을 넣어주겠습니다.
'본 트랜스폼(변경)'(Transform (Modify) Bone)노드는 지정된 본의 트랜스폼을 변경하는데 사용됩니다.  저는 회전값만 변경해줄 예정이지만, 회전값외에도 이동,스케일값도 변경이 가능하답니다.

그럼 'Neck Rotation' 변수에 담긴 목의 회전값을 Rotation에 연결해줍니다.

여기서 한가지 더 짚고넘어갈 부분이 있습니다. 'Neck Rotation'은 월드공간에서의 데이터로 만들어진 회전값이였죠,
일반적으로 애니메이션 블루프린트에서 포즈 작업을 할 때, 포즈는 로컬 스페이스에 있게 됩니다. 그러나 특정 블렌드노드와 모든 SkeletalContral은 컴포넌트 스페이스에서 작동합니다. 즉, 이러한 유형의 노드에 전달하기 전 인풋 포즈를 변환해줘야 한다는 뜻입니다. 인풋 포즈가 로컬 스페이스 포즈를 출력하는 노드에서 오는 경우, 그 포즈를 올바른 스페이스로 변환해 줘야 스켈레탈 컨트롤이 그에 맞는 작업을 할 수가 있습니다. 그 후 스켈레탈 컨트롤 작업을 하고 난 결과 포즈를 다시 로컬 스페이스로 변환해 줘야 추가적인 블렌드나 Result핀에 쓸 수 있습니다.



마지막으로 Rotation에 Rotation Space를 World Space로 변경해주시고, 플레이를 하면 처음 gif와 같이 아까 지정한 범위에 카메라가 들어올 시 캐릭터의 시선은 카메라를 바라보게 됩니다. 
저는 translation과 scale은 사용하지 않기때문에 check를 해제해 비활성화 해주었답니다.

2017/11/29

Unreal4__9_2_플레이어 공격 (Trail)


오늘은 플레이어가 공격모션에 맞춰 검 궤적 이펙트를 적용한 내용을 간단하게 소개해보겠습니다. 




언리얼에서 제공하는 트레일 파티클을 사용하지 않고, 공격 애니메이션이 시작되는 순간 원형 메시를 띄워 그 위 텍스쳐의 UV값을 이동시켜 검 이동에 따라 궤적이 생기는 효과를 적용했습니다.

먼저 3D Max로 아주아주 간단한 궤적 메시를 만들어보겠습니다.


3D Max 실행시켜줍니다.




화면의 우측에 있는 커맨드 패널을 보면 + 탭이 있고탭을 누르면 생성할 Object Type 목록들이 있습니다저희는 면을 생성해야 하기때문에 Plane 선택하고아래 Parameters 보면 Width Segs 보이실겁니다수가 높을수록 자연스러운 곡선을 만들 수는 있지만 적당하게 30으로 넣어주었습니다.

그리고나서 뷰포트에 마우스로 드래그해서 그려줍니다그럼  사진처럼 Plane 생성이 됩니다.

 생성된 Plane 구부려줄 차례인데요다시 커맨트 패널을 보시면 아까 '+' 옆에 구부러진 아이콘이 있는 탭을 눌러주시면 현재 뷰포트에 있는 오브젝트를 어떤 효과를 적용할지에 대한 리스트들이 쭈욱 있습니다



  Bend 선택해줍니다.


그럼 Bend 속성이 추가되고어디 축으로 구부려줄지에 대한 값을 적용시켜주어야 합니다.

그럼  사진과 같이 구부러지게 됩니다.Bend Angle 조절하여 강도를 조절할  있습니다.






이제  플랜에 UV좌표 맵핑을  차례입니다다시 리스트에서 Unwrap UVW 선택합니다



Edit Uvs에서 Open UV Editor 선택합니다그럼 에디터창이 뜨게 됩니다.

원래 기본적으로 UV좌표는 좌상단이 0,0 되고 우상단이 1,0 됩니다.


저는 3D Max 대해 처음이고초짜이기 때문에 블로그에서 적용한 방식을 그대로 사용하기 위해 좌우를 바꿔 우상단이 0,0 되도록 변경하였습니다
상단에 Tools >Flip Horizental 선택해  좌상단이 0,0으로 지정된 UV좌표를 우상단이 0,0으로 되도록 변경해줍니다.



그럼 UV좌표가 변경이 되고  사진처럼 텍스처의 숫자 1 우상단쪽으로 이동하게 됩니다.

이제 거의  왔습니다 사진을 보면 메쉬의 피봇이 트레일 중간에 찍혀있습니다저는 게임 적용함에 있어  편리하게 작업하기 위해 피봇의 위치도 변경해주었습니다.



이번에는 커맨드 패널에서 3번째 탭을 선택해줍니다.
그럼  사진처럼 Pivot 설정에 대한 버튼들이 보이게됩니다.
피봇의 위치를 메쉬의 안쪽  공간에 설정할 생각입니다왜냐하면 캐릭터가 칼을 휘둘렀을  일반적인 상황이라면 메쉬의 생성 위치는 캐릭터의 앞부분이어야 하기 때문입니다.

오브젝트에만 적용하기 위해 Affect Object Only 선택해주고




Alignment에서 Center to Pivot 선택에 가운데로 되게끔 설정해주었습니다.






그럼 피봇의 위치는 일직선일때의 피봇위치가 아닌 구부러진 상태에서 가운데에 해당하는 위치로 Pivot 변경되었습니다.

이제 트레일 메쉬를 만드는 작업은 끝났습니다이제 3D Max에서 생성한 메쉬를 언리얼로 불러오는 작업을 해보겠습니다.






3D Max File메뉴를 선택하시면 Export 있습니다. Export버튼을 눌러주시면 위와같은 창이 생성이 됩니다저희는 언리얼에서 사용할  있는 FBX 저장을 해야하기 때문에 Save as Type *.FBX 변경해주고 Save 눌러줍니다.
FBX 저장하기전 메쉬에 대한 속성을 설정할  있는 창이 뜨게 됩니다여기가 중요한부분입니다언리얼과 호환되기위해서 언리얼에서 요구하는 옵션들을 체크해주어야 합니다. Geometry 부분을 위와 동일하게 체크해주고 Animation, Cameras, Lights안에 체크박스는 모두 해제해줍니다제가 생성한 메쉬는 스태틱메쉬이고 조명도 필요없기때문이죠.

이제 OK 누르면 무사히 저장이 된답니다.

여기까지가 트레일 메쉬를 만들고언리얼에서 사용할  있도록 저장까지 마쳤습니다.




언리얼로 불러오면 3D Max에서 보던것과 동일하게 Import 성공!
이제 트레일메쉬에 사용할 메터리얼을 하나 생성하고 노드를 만들어보겠습니다.

트레일에 적용될 텍스쳐는 3가지 입니다.
  1. UpLine_TrailTexture,
  2. Default_TrailTexture,
  3. Alpha_TrailTexture 입니다.

UpLine_TrailTexture 트레일메쉬 바깥쪽에 강한 효과를 주기위한 메터리얼 입니다.
Default_TrailTexture
Alpha_TrailTexture 트레일 메쉬에 알파를 적용할 메터리얼입니다.


먼저 텍스쳐를 적용하기전 UV좌표와 메터리얼 크기를 컨트롤할 있는 노드를 만들어보겠습니다.

Trail_U_Scale, Trail_V_Scale 메터리얼의 크기를 조절하는 노드입니다. 노드를 Append 연결하여 Trail_U_Scale값과 Trail_V_Scale값을 벡터로 만들어주었습니다.

지금 값이 1 적용된 Scalar노드를 Append하면 (1, 1) 이렇게 변환이 되는것이지요.

UV값도 변경이 가능하도록 만들어 무기의 이동에 따라 메터리얼도 움직일 있도록 해보겠습니다. Scalar 노드로 U_Move노드와 Trail_V_Move 노드 두개를 만들어 줍니다.
U_Move UV좌표중 U 해당하는 좌표를 이동시키는 노드에 해당하고,
Trail_V_Move 또한 V 해당하는 좌표를 이동시키는 노드에 해당되겠지요.

위에서 사용했던 Append 노드로 벡터로 만들어 , 위에서 메터리얼 스케일 작업을 했던 벡터와 Add 노드를 통해 벡터를 더해주어 최종 UV 좌표를 만들어 줍니다.

방금 만든 노드들은 텍스쳐의 이동및 스케일을 조정하는데 사용되는 기본적인 노드로 많이 사용될 노드들입니다.

그럼, UpLine_TrailTexture, Default_TrailTexture, Alpha_TrailTexture 사용된 노드를 보겠습니다.







Texture Sample 트레일의 끝에 자연스러움을 주기 위해 서서히 알파값이 적용되도록 도와주는 텍스쳐입니다. Alpha_Trail_Tex 실제 트레일에 적용할 알파텍스쳐가 되겠습니다. 텍스쳐의 속성이 clamp이기 때문에 바둑판처럼 반복적으로 적용되는게 아니라 UV좌표에 맞춰서 늘어난 상태로 적용이 됩니다.

Alpha_Intensity값으로 트레일의 강도를 조절할 있습니다. 투명하게 보여줄지.. 선명하게 보여줄지에 대한 강도겠지요.

머티리얼에서 노드 추가 작업은 이제 끝이 났습니다.

머티리얼 인스턴스를 생성해서 값들을 변경해 보겠습니다. 먼저, 인스턴스를 생성하기전 
머티리얼  인스턴스에 대해서 알아보겠습니다.

머티리얼 인스턴싱은 머티리얼을 바탕으로 여러가지 다양한 모양의 머티리얼을 만드는것을 말합니다. 이러한 유연성을 내기 위해 머티리얼 인스턴싱은 상속이란 개념을 사용한다고 하네요. 

머티리얼 인스턴스를 사용하는 이유는 그냥 머티리얼을 생성, 셋업하는 작업은 언리얼에서 매우 많은 시간을 잡아먹는 프로세스가 될 수 있다고합니다. 이 프로세스의 효율을 높이고 속도를 올리기 위해 머티리얼 인스턴스라는 특수 유형의 머티리얼을 제공한답니다.

머티리얼에서 값을 변경할 수 있게 만들었던 노드들을 머티리얼에서 쉽게 값을 변경하고 결과값 확인이 가능하기 때문에 머티리얼 인스턴스를 생성해서 값들을 변경해보겠습니다.

컨텐츠 브라우저에서 머티리얼을 우클릭하면 'Material Instance 생성하기가 나옵니다. 클릭해주면 해당 머티리얼에 대한 머티리얼 인스턴스 에셋이 생성이 된답니다.

이 에셋을 열어줍니다.



아까 머티리얼에서 추가했던 노드들에 수치들과 수치들을 변경할 수 있도록 디테일 패널에 추가되어있습니다.


여기서 U_Move값을 -1과 1 사이값으로 변경을 해보면 위와 같이 UV좌표가 이동되면서 나타났다가 사라지게 되는 효과를 보실 수 있습니다.


https://blog.naver.com/othiai4239/221040342597

블로그를 참고하여 만들어보았습니다. 

Unreal4__9_1_플레이어 공격 (Combo)

플레이어의 연속공격(Combo) 대해서 소개하겠습니다.  지금 플레이어는 마우스 좌클릭시 한가지 공격모션밖에 나오지 않는데연속 클릭시 4가지 공격모션이 이어져 나오도록 만들어보았습니다.

우선 콤보의 조건은
첫번째 플레이어가 공격상태일때 입니다 공격시 1콤보로 올려줍니다.
여기서 중요한 부분은 현재콤보에서 다음 콤보로 넘어가는 부분인데 공격모션인 부분과 처음 모션으로 돌아가는 모션 사이에 기준점을 두고기준점 이전에 공격 시도시 콤보로 이어지고 이전에 공격 시도없이 이후 프레임으로 넘어간다면 콤보에 대한 정보는 초기화가 이루어집니다 부분에 대해서는 밑에서 사진과 함께 다시 설명하겠습니다.


마우스   입력에 대한 처리는 C++에서 이루어지기 때문에, C++에서 먼저 작업을 진행하였습니다. Jplayer클래스에서 키입력에 따른 액션이 적용되기 때문에 Jplayer 클래스를 열어줍니다.

1
2
3
4
5
6
7
// Called to bind functionality to input
void AJPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    
    PlayerInputComponent->BindAction("Attack", IE_Pressed, this, &AJPlayer::Attack);
cs
 마우스 좌클릭시 Attack()함수가 호출이 되도록 바인딩이 되어있습니다.
Attack함수를 보면
1
2
3
4
5
6
7
8
9
10
11
12
13
void AJPlayer::Attack()
{
    if(m_isAttack == false)
    {
        m_isAttack    = true;
        m_combo        = 1;
    }
    else
    {
        m_chainCombo = true;
    }
}
cs
m_isAttack 변수와 m_combo, m_chainCombo 새로운 변수들이 보입니다.
변수에 대해 설명드리자면 m_isAttack 플레이어가 공격중인지에 대한 boolean값이며,
m_combo 현재  콤보인지, m_chainCombo 콤보를 진행할지에대한 변수입니다.

여기서 콤보 진행이 되지 않을 경우 정보를 초기화해주는 함수도 추가하겠습니다.
1
2
3
4
5
6
7
void AJPlayer::UnAttack()
{
    AJCharacter::UnAttack();
    m_chainCombo = false;
    m_combo = 0;
}
cs

UnAttack()함수는 블루프린트에서 호출되는 함수입니다.

이제 어느정도 C++에서 마우스 입력에 따른 공격상태 설정이 되었으니 에디터로 돌아와서 블루프린트와 애니메이션 몽타주를 통해 콤보를 만들어보겠습니다.

먼저 공격 애니메이션을 몽타주로 만들어주었습니다이번에는 하나의 몽타주안에 여러 개의 애니메이션이 연결되어 들어가있는게 아니라 공격모션 1개가 몽타주인샘입니다.

1. 콤보에 대한 조건 처리 다음 동작에 대한 연결
플레이어의 블루프린트를 열어줍니다이벤트그래프로 이동한 다음 사진과 같이 콤보에 대한 노드들을 연결해주었습니다.  우선 노드를 보면 Mcombo C++에서 보았던 m_combo변수로 콤보 카운트가 저장되는 변수입니다.
Mcombo == 1 이라는건 처음 공격을 시도했다는 의미입니다이때는 콤보 아닌 일반 공격에 해당됩니다


2. 공격 상태 및 콤보가 True일 때 M Combo에 따른 애니메이션 몽타주 설정과 카운트 증가


조건이 충족되었으니, Branch Condition true 되므로 switch문에서 1 해당하는 실행핀이 활성화됩니다. Combo 1 해당하는 애니메이션 몽타주가 플레이 되고동시에 combo 2 올려줍니다.

combo2부터는 마우스의 입력에 따라 콤보가 될지 말지에가 결정이 됩니다.
특정 프레임까지 마우스 입력이 없다면 현재 공격 모션에서 끝나게 되고특정 프레임전에 마우스 입력이 발생할 경우 C++ 코드에서 구현했던것처럼 m_chaincombo 값은 true 되고 콤보 공격을 진행하게 되지요.

위 1번 블루프린트 사진을 다시 보면 m_chaincombo true 되면서 branch 실행흐름은 switch 노드로 흐르게됩니다. 그 전에 m_chaincombo는 다시 false로 변경이 되지요. 다음 콤보 조건을 체크하기 위함이랍니다. 그리고 m combo 값을 selection으로 전달되기 때문에 combo2 해당하는 애니메이션 몽타주가 플레이가 됩니다

3. 선택된 애니메이션 몽타주 플레이
현재 콤보 카운트에 맞는 애니메이션 몽타주가 선택되고 플레이 시켜줍니다.
애니메이션은 한번만 플레이되어야 하기 때문에 DoOnce노드를 추가해주었습니다.
애니메이션에서 특정 프레임을 기준으로 마우스의 추가 입력에 따라 콤보의 진행여부를 판단한다고 하였습니다.

기준을 저는 공격모션에서 다시 돌아오는 모션으로 바뀌는 중으로 지정했습니다.
 부분은 만드는사람 마음이지만다음 모션으로 넘어가기에 최대한 자연스러운 프레임까지를 마지노선으로 정하였기때문에 'Reset' 이라는 노티파이를 추가해주었습니다.


애니메이션 프레임이 Reset에 도달했을 때는 더 이상의 추가 공격이 없다는 의미로 해석이 되겠지요.



Reset Notify 활성화 되면공격  콤보에 대한 정보를 초기화 해줍니다 상태에 이르게 되면 공격을 해도 다시 처음 공격모션(combo 1)으로 돌아가게 된답니다.

  


마지막 콤보 모션을 보면 회전을 하면서 공격하는 모션입니다회전하면서 앞으로 이동값을 주어 좀더 생동적인 효과를 줘보겠습니다.

우선 애니메이션 특정 프레임구간동안 이벤트를 발생시키기 위해서 저는 AnimNotifyState 기능을 사용하였습니다.
커스텀 애님노티파이스테이트를 사용하려면 AnimNotifyState 상속받는 블루프린트를 하나 생성하셔야 합니다.




마우스 우클릭  블루프린트 클래스 생성을 누른후 AnimNotifyState 찾아 클릭  생성해줍니다그럼 위와같이 생성이 되면 이름을 'MoveAnimNotifyState' 변경해 줍니다.
  
이벤트를  애니메이션을 열고 스크롤을 내리면 노티파이라는 부분이 있습니다여기에 이벤트를 주고싶은 구간을 지정해주는 부분인데요 노티파이에 좌클릭후 노티파이스 스테이트 추가를 가면 방금 추가한 MoveAnimNotifyState 있습니다클릭을 해줍니다.
  
그럼 위와같이 구간을 나타내는 노티파이 애니 스테이트가 추가가 됩니다그럼 이동값을 넣어줄 구간을 지정해   저장을 해주고 아까 생성한 MoveNotifyAnimState 블루프린트를 열어줍니다.
   



AnimNotifyState 함수오버로딩을 통해 기존에 있던 함수를 재구현할  있는데 저는 매프레임에 이동값을 주어야 하기때문에 Received Notify Tick 함수만을 오버로딩  플레이어의 포워드 벡터를 얻어와 이동값으로 넣어주었습니다.

여기서 Scale Value 이동 비율을 지정해줄수도 있답니다.


그리고 저장을   게임을 플레이해보면 아까 지정한 프레임구간에서 앞으로 이동하는 효과가 보여지게 됩니다.

https://docs.unrealengine.com/latest/KOR/Engine/Animation/Sequences/Notifies/index.html