드디어 오늘 플레이어 이동 & 카메라 구현을 완료했다. 


처음 만드는 3D게임이여서 어려웠던거 같다. 

그래도 만들면서 카메라 회전이나 회전행렬등을 이해할 수 있었다



구현 영상

조작은 마우스로도 할 수 있고 키보드 방향키로도 할 수 있다


구현 코드 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
void PlayerAirplane::InputMouse()
{
    // vGap : 전프레임과 현재 프레임의 마우스 위치의 차이.
    Vector2 vGap(INPUT.GetMouseGap());
 
    //fForwardAngle : Forward축으로 돌릴 회전값
    float fForwardAngle = -(D3DXToRadian(vGap.x) * 0.05f);
    //fRightAngle : Right축으로 돌릴 회전값
    float fRightAngle = -(D3DXToRadian(vGap.y) * 0.05f);
 
    //키보드 사용시 
    if (KEYPRESS(VK_UP))
        fRightAngle += D3DXToRadian(3.f);
    if (KEYPRESS(VK_DOWN))
        fRightAngle -= D3DXToRadian(3.f);
 
    if (KEYPRESS(VK_RIGHT))
        fForwardAngle -= D3DXToRadian(3.f);
    if (KEYPRESS(VK_LEFT))
        fForwardAngle += D3DXToRadian(3.f);
 
    // 각 축의 회전행렬과 초기화
    D3DXMATRIX matForwardRot;
    D3DXMatrixIdentity(&matForwardRot);
 
    D3DXMATRIX matRightRot;
    D3DXMatrixIdentity(&matRightRot);
 
 
#pragma region AIRPLANE ROTATION
    // 플레이어를 회전 코드
 
    if (fForwardAngle)
    {
        // Forward 축으로 회전한 Matrix를 다른축에다 적용시켜준다 
        D3DXMatrixRotationAxis(&matForwardRot, &vAxis[E_AXIS_FORWARD], fForwardAngle);
        
        D3DXVec3TransformNormal(&vAxis[E_AXIS_RIGHT], &vAxis[E_AXIS_RIGHT], &matForwardRot);
        D3DXVec3TransformNormal(&vAxis[E_AXIS_UP], &vAxis[E_AXIS_UP], &matForwardRot);
    }
 
    if (fRightAngle)
    {
        // Right 축으로 회전한 Matrix를 다른축에다 적용시켜준다 
        D3DXMatrixRotationAxis(&matRightRot, &vAxis[E_AXIS_RIGHT], fRightAngle);
        
        D3DXVec3TransformNormal(&vAxis[E_AXIS_FORWARD], &vAxis[E_AXIS_FORWARD], &matRightRot);
        D3DXVec3TransformNormal(&vAxis[E_AXIS_UP], &vAxis[E_AXIS_UP], &matRightRot);
    }
 
    if (fRightAngle != 0 || fForwardAngle != 0)
        transform->matRot = transform->matRot * matForwardRot * matRightRot;    
 
#pragma endregion AIRPLANE ROTATION
 
#pragma region CAMERA_SETTING    
    // 카메라를 회전 코드
    // 구한 회전 행렬을 플레이어와 카메라위치, 카메라LookAt위치에 대한 방향에 적용시켜준다. 
    if (fForwardAngle)
    {
        D3DXVec3TransformNormal(&vCameraLookAtDir, &vCameraLookAtDir, &matForwardRot);
        D3DXVec3TransformNormal(&vCameraDir, &vCameraDir, &matForwardRot);
    }
 
    if (fRightAngle)
    { 
        D3DXVec3TransformNormal(&vCameraLookAtDir, &vCameraLookAtDir, &matRightRot);
        D3DXVec3TransformNormal(&vCameraDir, &vCameraDir, &matRightRot);
    }
    
    //카메라 위치 셋팅
    vCameraPos = transform->pos + (vCameraDir * fCameraDistance);
    vCameraLookAt = transform->pos + (vCameraLookAtDir * fCameraLookAtDistance);
 
    CAMERA.SetCameraInfo(vCameraPos, vCameraLookAt, vAxis[E_AXIS_UP]);
#pragma endregion CAMERA_SETTING
 
}
 
void PlayerAirplane::InputKeyboard()
{
    if (KEYPRESS('W'))
        transform->pos += vAxis[E_AXIS_FORWARD] * (200 * Et);
    if (KEYPRESS('S'))
        transform->pos -= vAxis[E_AXIS_FORWARD] * (200 * Et);
 
}
 
 
 
cs

각 코드의 설명은 주석으로 작성해놨다.


알아두고 가야할 요소들.


* 값의 변화를 잘 알아두고 작업하자 


144줄과 150줄 사이에서 Forward벡터를 축으로한 회전이 이루어진다

그리고 153줄과 160줄 사이에서 Right축으로한 회전이 이루어지면서

Forward벡터가 변경되는데

여기서 입력이 동시에 이루어진다면 Forward벡터는 변경되기 전 값으로 

계속 먼저 회전되고나서 값의 변경이 이루어진다. 

이 값이 계속 누적되면 카메라가 플레이어를 똑바로 바라보지않고 오차가 생기게된다


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

만들때는 어려웠지만 만들고 보닌까 이해가 됐다. 



DirectX 에서는 벡터를 변환하는 함수인


1
2
3
4
5
D3DXVECTOR3* WINAPI D3DXVec3TransformCoord
    ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM );
 
D3DXVECTOR3* WINAPI D3DXVec3TransformNormal
    ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM );
cs

함수가 있다.


일단 이 두함수의 공통점은 2번째 인자인 벡터와 3번째 인자인 행렬을 곱하는 함수라는 것이다.


우선 3차원 벡터와 4x4 행렬은 곱해질 수 없다 왜냐하면 행렬을 곱할때는 앞 행렬의 열과 뒤 행렬의

행이 같아야지만 곱할 수 있다


그래서 이 함수는 3차원 벡터를 4차원 벡터로 변환하여 행렬과 곱해주는데 여기서 차이점이 발생한다


3차원 벡터 가 있다면


이 3차원 벡터를


D3DXVec3TransformCoord 함수는 

로 바꿔주고


D3DXVec3TranformNormal 함수는

로 바꿔준다.


둘의 차이는 4차원 벡터의 4번째 요소  가 다르다는 점인데 


 이라면 포인터(좌표) 개념이 되어 이동이 적용되고

 이라면 벡터의(방향) 개념이 되어 이동이 적용되지 않는다


이유는 


DirectX 의 4x4 행렬의 마지막 열은 이동을 담당하는것을 알고 있다면 이해하기 쉽다


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

이 식에서 


이라면

  이므로 이동이 적용되는걸 알 수 있다.


 이라면 

      이므로 이동이 적용 안되는 걸 알 수 있다.


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

그러므로


 벡터와 행렬을 곱하여 


 이동, 회전, 스케일, 을 변환하고 싶다면 D3DXVec3TransformCoord를 쓰고

 이동은 변환하고싶지 않고 회전,스케일만 변환하고 싶다면 D3DXVec3TransformNormal을 쓰면된다








'programing > directX' 카테고리의 다른 글

LPD3DXMESH 쓰는법  (0) 2018.10.13
LPD3DXFONT  (0) 2018.02.13

전체소스코드는 맨 아래있습니다.


DirectX 3D를 공부하던 중 Obj 모델링을 로드하고 그 정보를 저장하기 위해 LPD3DXMESH를 사용했다


LPD3DXMESH를 사용하기 위해 해야할 것들은...


1. LPD3DXMESH 초기화 해주기


D3DXCreateMeshFVF(

삼각형의 갯수,

정점의 갯수,

D3DXMESH_MANAGED,

FVF 포멧,

device,

LPD3DXMESH의 포인터);     위 함수를 이용하여 메쉬를 초기화 한다.


2. VertexBuffer 초기화 하기

일단 정점들을 저장한 배열이나 STL::Vector에 저장한다.

vector<CustomVertex> vVertexs;


버퍼 포인터를 받을 void 포인터 변수를 선언한다.

void * Vertices = nullptr;


락을 걸어준다.

lpD3DXMesh->LockVertexBuffer(0, Vertices);


그리고 배열이나 STL::Vector의 저장된 정점들을 복사해준다.

memcpy(Vertices, &vVertexs[0], sizeof(CustomVertex) * vVertexs.size());


락을 풀어준다.

lpD3DXMesh->UnlockVertexBuffer();


2. IndexBuffer 초기화 하기.

초기화할 인덱스 정보를 배열이나 STL:Vector에 저장한다.

vector<WORD> vIndexs;


버퍼 포인터를 받을 void 포인터 변수를 선언한다

void * Indices = nullptr;


락을 걸어준다.

lpD3DXMesh->LockIndexBuffer(0, &Indices))


그리고 배열이나 STL::Vector의 저장된 정점들을 복사해준다

memcpy(Indices, &vIndexs[0], sizeof(WORD) * vIndexs.size());


락을 풀어준다.

lpD3DXMesh->UnlockIndexBuffer();


2. AttributeBuffer 초기화 하기.

초기화할 속성 정보를 배열이나 STL:Vector에 저장한다.

vector<DWORD> vAttribute;


버퍼 포인터를 받을 DWRD 포인터 변수를 선언한다

DWRD * Attribute= nullptr;


락을 걸어준다.

lpD3DXMesh->LockAttributeBuffer(0, &dwAttribute))


그리고 배열이나 STL::Vector의 저장된 정점들을 복사해준다

memcpy(dwAttribute, &vAttribute[0], sizeof(DWORD) * vAttribute.size());


락을 풀어준다.

    lpD3DXMesh->UnlockAttributeBuffer();



이렇게 설정해주고 그릴때 lpD3DXMesh->DrawSubset(SubsetNum); 이렇게 그려주면 메쉬가 그려진다.


전체 소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
bool cMeshRenderer::CreateMesh()
{
    if (FAILED(D3DXCreateMeshFVF(
        vVertexs.size() / 3
        vVertexs.size(), 
        D3DXMESH_MANAGED, 
        CustomFVF, 
        g_device, 
        &lpD3DXMesh)))
    {
        MessageBox(nullptr, L"Failed CreateMesh ", L"Error", MB_OK);
        return false;
    }
 
    void * Vertices = nullptr;
 
    if (FAILED(lpD3DXMesh->LockVertexBuffer(0&Vertices)))
    {
        MessageBox(nullptr, L"Failed Mesh->LockVertexBuffer", L"Error", MB_OK);
        return false;
    }
    
    memcpy(Vertices, &vVertexs[0], sizeof(CustomVertex) * vVertexs.size());
 
    lpD3DXMesh->UnlockVertexBuffer();
 
    void * Indices = nullptr;
 
    if (FAILED(lpD3DXMesh->LockIndexBuffer(0&Indices)))
    {
        MessageBox(nullptr, L"Failed Mesh->LockIndexBuffer", L"Error", MB_OK);
        return false;
    }
    
    memcpy(Indices, &vIndexs[0], sizeof(WORD) * vIndexs.size());
 
    lpD3DXMesh->UnlockIndexBuffer();
 
    DWORD * dwAttribute = nullptr;
 
    if (FAILED(lpD3DXMesh->LockAttributeBuffer(0&dwAttribute)))
    {
        MessageBox(nullptr, L"FAiled Mesh->LockAttributeBuffer", L"Error", MB_OK);
        return false;
    }
 
    memcpy(dwAttribute, &vAttribute[0], sizeof(DWORD) * vAttribute.size());
 
    lpD3DXMesh->UnlockAttributeBuffer();
 
    return true;
}
 
cs



틀린 것이나 조언할것이 있다면 댓글로 해주세요 감사합니다

'programing > directX' 카테고리의 다른 글

DirectX D3DXVec3TransformCoord와 D3DXVec3TransformNormal 함수  (0) 2018.10.15
LPD3DXFONT  (0) 2018.02.13

Win32로 프로젝트를 진행하다 보면 여러가지 상태의 변경상황을 확인해야하는데 이런 것을 디버그로 확인 하다보면 정말 불편하다 멈추고 확인하고 멈추고 확인하고... 계속 반복하다 보면 정말 귀찮다 그리고 프로그램의 특정 상황(게임같이 계속 프레임이 돌아가는 경우) 에서 확인 해야 한다면 거의 불가능한 수준이다. 


그래서 Win32에서도 콘솔창을 띄울 수 있는 방법이 있다


프로그램 시작시 이런 코드를 추가해주면 된다


#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console") 


하지만 또 확인해야 할 것이 있는데 자신의 프로그램이 멀티바이트를 사용하는 프로그램인가 유니코드를 사용하는 프로그램인가에 따라서 코드가 조금 다르다


유니코드 사용시

#pragma comment(linker, "/entry:wWinMainCRTStartup /subsystem:console") 


멀티바이트 코드 사용시

#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")  


이렇게 코드를 달리 해주면 된다


근데 이걸 또 프로젝트 생성시 마다 확인해주고 다르게 작성하기 귀찮으니

c/c++인 경우 배운 전처리 지시기를 이용하면 편리하다


                                                                                                           

#ifdef UNICODE                                                                                      

      #pragma comment(linker, "/entry:wWinMainCRTStartup /subsystem:console") 

#else                                                                                                    

      #pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")   

                                                                                                           


이렇게 해주면 확인해줄 필요도없고 또 확경이 바뀌어도 알아서 해주니 편리하다


그리도 대부분 디버그 모드에서만 활용하니


#ifdef _DEBUG

                                                                                                           

#ifdef UNICODE                                                                                      

      #pragma comment(linker, "/entry:wWinMainCRTStartup /subsystem:console") 

#else                                                                                                    

      #pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")   

#endif                                                                                                   

#endif                                                                                                   



이렇게 해주면 된다 그리고 프로그램 상에서 cout이나 printf 를 사용하면된다


(개인이 공부목적으로 쓰는 글이기 때문에 틀린 부분이 있을 수 있습니다)

'programing > c_c++' 카테고리의 다른 글

STL list 원소삭제법.  (0) 2018.02.24

+ Recent posts