지난번 UnitTool Part1에서 이어서 진행을 하려고한다.

 

오브젝트한테 필요한 정보는 어떤 것이 있을까?

우선 오브젝트의 이름이 필요하다.

그리고 공격력 방어력 등의 수치와 현재 어떤 아이템을 가지고 있는지 등이 필요할 수도 있다.

 

그래서 다음과 같이 리소스 뷰를 설정 해놓는다.(리소스 뷰를 꾸미는 것은 자유!!)

변수 추가를 통해서 이름, 공격력, 방어력 등의 변수를 추가해준다.

Radio Button과 CheckBox는 따로 배열로 만들어 주었다.

 

여기까지 진행이 완료되었으면, 이제 위의 정보를 담을 구조체가 하나 필요하므로,

추가한 변수를 넣을 구조체를 하나 만들어준다.

typedef struct tagUnitInfo
{
	CString strName;
	int	iAttack; 
	int	iDef;
	BYTE	byJop;
	BYTE	byItem; 
}UNITINFO;

구조체까지 정의하였으면, 이제 UnitInfo의 정보를 담을 공간이 필요하다.

UnitInfo를 확인해보면 오브젝트 각각의 이름과 공격력 방어력 등이 있다.

여기서 오브젝트 각각의 이름은 Key 값 그 외 정보들(UnitInfo)는 Value라고 생각하면된다.

여기까지 생각해보았을 때 Key와 Value를 담을 공간, 즉 map 컨테이너를 사용하는 것이다.

 

다음과 같이 map 컨테이너의 변수를 하나 만들어준다.

private:
	map<CString, UNITINFO*> m_mapUnitInfo;

그리고 Add 버튼에 클릭 이벤트를 추가해준다.

추가를 하고나서 해당 이벤트 함수에 다음과 같이 코드를 작성한다.

UpdateData(TRUE);
auto& iter_find = m_mapUnitInfo.find(m_strName);

if (iter_find != m_mapUnitInfo.end())
	return;

UNITINFO* pUnit = new UNITINFO;
pUnit->strName = m_strName;
pUnit->iAttack = m_iAttack;
pUnit->iDef = m_iDef;
pUnit->byJop = 0;
pUnit->byItem = 0;

for (int i = 0; i < 3; ++i)
{
	if (m_Jop[i].GetCheck())
	{
		pUnit->byJop = i;
		break;
	}
}


if (m_CheckBox[0].GetCheck())
	pUnit->byItem |= SWORD;	
if (m_CheckBox[1].GetCheck())
	pUnit->byItem |= BOW;
if (m_CheckBox[2].GetCheck())
	pUnit->byItem |= SPEAR;


m_mapUnitInfo.emplace(pUnit->strName, pUnit);
m_ListBox.AddString(m_strName);
UpdateData(FALSE);

간단하게 설명하자면,

Add 버튼을 클릭하였을 때 리스트 박스에 있는 오브젝트 이름과 같은 이름이 있을 경우

새로운 정보 추가가 되지않도록 예외 처리를 해주었다.

 

만약 같은 이름이 추가가 되면 나중에 저장을 하였을 때 혼동이 찾아올 수도 있기 때문이다.

아래 GetCheck()라는 함수는 UnitTool에서 해당 체크박스를 체크하였는지 안했는지 여부를 가져오는 함수이다.

 

여기까지 하였으면 정보를 입력하고 Add 버튼을 눌렀을 때 다음과 같이 ListBox에 추가가 된 것을 볼 수 있다.

하지만 리스트에서 추가된 오브젝트를 클릭하였을 때 저장된 정보가 확인되지 않는다.

그래서 이제 리스트에 추가된 오브젝트를 클릭하면 이전에 만들었을때의 정보를 불러오게 하려고 한다.

 

우선 리소스뷰에서 리스트 박스를 클릭하고 다음과 같이 이벤트 처리기를 추가해준다.

 

추가한 이벤트 함수에 다음과 같이 정의한다.

UpdateData(TRUE);

int iIndex = m_ListBox.GetCurSel();
if (LB_ERR == iIndex)
	return;

CString strfindName;
m_ListBox.GetText(iIndex, strfindName);

auto& iter_Find = m_mapUnitInfo.find(strfindName);
if (iter_Find == m_mapUnitInfo.end())
	return;

for (int i = 0; i < 3; ++i)
{
	m_Jop[i].SetCheck(FALSE);
	m_CheckBox[i].SetCheck(FALSE);
}

m_strName = iter_Find->second->strName;
m_iAttack = iter_Find->second->iAttack;
m_iDef = iter_Find->second->iDef;
m_Jop[iter_Find->second->byJop].SetCheck(TRUE);

if (iter_Find->second->byItem & SWORD)
	m_CheckBox[0].SetCheck(TRUE);

if (iter_Find->second->byItem & BOW)
	m_CheckBox[1].SetCheck(TRUE);

if (iter_Find->second->byItem & SPEAR)
	m_CheckBox[2].SetCheck(TRUE);

UpdateData(FALSE);

위와 같이 정의한 후 실행을 시켜보면 다음과 같이 추가된 정보를 확인할 수 있게된다.

여기서 오브젝트를 추가할 때 아무런 직업도 선택을 안할경우,

제일 첫번째에 있는 직업으로 자동으로 저장이 되도록 하였다.

 

이제 오브젝트 추가에 대한 것을 끝났으니 리스트에 있는 오브젝트를 삭제하는 것을 추가해보자.

이전과 같이 버튼을 추가하고 클릭 이벤트 처리기 함수를 하나 만들어준다.

그 다음 해당 이벤트 함수에 다음과 같이 정의한다.

UpdateData(TRUE); 
int iIndex = m_ListBox.GetCurSel(); 
if (LB_ERR == iIndex)
	return; 
CString wstrName;
m_ListBox.GetText(iIndex, wstrName); 

auto& iter_find = m_mapUnitInfo.find(wstrName); 
if (m_mapUnitInfo.end() == iter_find)
	return; 
Safe_Delete(iter_find->second); 
m_mapUnitInfo.erase(wstrName); 
m_ListBox.DeleteString(iIndex); 

UpdateData(FALSE); 

 

위와같이 정의하였으면 다음과 같이 리스트에 있는 오브젝트의 정보를 Delete버튼을 클릭하여 삭제가 가능하게된다.

 

마지막으로 오브젝트를 추가한 상태로 프로그램을 종료시켰을 때 메모리 누수가 생기게될 것이다.

이유는 map 컨테이너에 원소를 추가만 한 상태로 종료를 해서 그렇다.

그러므로 소멸자 부분에 다음과 같이 메모리 누수가 생기지 않도록 map 컨테이너를 비워줘야한다.

for (auto& rPair : m_mapUnitInfo)
{
    if (rPair)
    {
   	delete rPair; 
	rpair = nullptr; 
    }
}
m_mapUnitInfo.clear();

여기까지 오브젝트의 정보 추가와 삭제를 해보았고,

다음에는 ListBox에 추가한 오브젝트를 저장 및 불러오기를 해보려고 한다.

'Programming > MFC' 카테고리의 다른 글

MFC - 프로젝트 세팅  (0) 2020.12.09
MFC - UnitTool Part3(저장 및 불러오기)  (0) 2020.12.05
MFC - 미니맵  (1) 2020.11.29
MFC - UnitTool Part1(기본 구성), 이벤트 처리기  (0) 2020.11.29
MFC - 기본 이론 , 계층 구조  (0) 2020.11.29

미니맵은 다음과 같이 여러 게임에서도 흔히 확인해 볼 수가 있다.

이전에 MFC에서 타일을 배치해보았다.

이번에는 위에 사진처럼 배치한 타일들을 띄워줄 미니맵을 만들어보려고 한다.

 

우선 미니맵을 띄워주기 위해 View클래스를 하나 만들어 준다.

기본 클래스는 CView로 해준다.

클래스를 생성한 다음, 생성한 클래스를 클릭하고 클래스 뷰를 누른다음 재정의를 클릭한다.

그리고 스크롤을 쭈욱 내리다 보면 OnDraw라는 함수가 보일 것이다.

클릭하여 재정의 해준다.

재정의를 하고나서 헤더파일을 가게되면 다음과 같이 나올 것이다.

OnDraw 함수까지 재정의를 완료하였으면 이제 다음과 같이 창을 분리해야 한다.

창을 분리하기 위해서 MFC에서 제공해주는 CSplitterWnd 클래스를 사용한다.

 

CSplitterWnd

-> 여러 개의 창이 포함된 창인 분할자 창 기능을 제공

docs.microsoft.com/ko-kr/cpp/mfc/reference/csplitterwnd-class?f1url=%3FappId%3DDev14IDEF1%26l%3DKO-KR%26k%3Dk(AFXEXT%252FCSplitterWnd);k(CSplitterWnd);k(DevLang-C%252B%252B);k(TargetOS-Windows)%26rd%3Dtrue&view=msvc-160

 

CSplitterWnd 클래스

CSplitterWnd 클래스CSplitterWnd Class 이 문서의 내용 --> 여러 개의 창이 포함된 창인 분할자 창 기능을 제공합니다.Provides the functionality of a splitter window, which is a window that contains multiple panes. 구문Syntax cl

docs.microsoft.com

CSplitterWnd는 메인 프레임 클래스에 정의하면 된다.

창을 분리하더라도 기존의 ToolView가 사용할 창이 필요하므로,

Main Splitter와 미니맵으로 사용할 SecondSplitter 두 개를 선언한다.

 

그 다음 MainFrame 클래스에서 OnCreateClient라는 함수를 재정의 해준다.

함수 재정의 후에 다음과 같이 MainSplitter와 SecondSplitter를 통해서 창을 분할해주면 된다.

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
	// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.

	/*
	0,0		0,1
	1,0		1,1
	*/
    
 	m_MainSplitter.CreateStatic(this, 1, 2);
 	m_MainSplitter.CreateView(0, 1, RUNTIME_CLASS(CMFCToolView), CSize(800 , 600 ), pContext);
	
	m_SecondSplitter.CreateStatic(&m_MainSplitter, 2, 1, WS_VISIBLE | WS_CHILD, m_MainSplitter.IdFromRowCol(0, 0));
	m_SecondSplitter.CreateView(0, 0, RUNTIME_CLASS(CMiniView), CSize(300, 300), pContext);
	m_SecondSplitter.CreateView(1, 0, RUNTIME_CLASS(CMyFormView), CSize(300, 300), pContext); 
	m_MainSplitter.SetColumnInfo(0, 300, 10);
	return TRUE;
}

분할된 창을 만들려면 CreateStatic 함수를 먼저 선언하여야 한다.

그래야지 이후에 CreateView 함수를 통해 창 분할을 시도한다.

 

CSplitterWnd 내부 함수는 MSDN에서 확인 해볼 수 있다.

docs.microsoft.com/ko-kr/cpp/mfc/reference/csplitterwnd-class?f1url=%3FappId%3DDev14IDEF1%26l%3DKO-KR%26k%3Dk(AFXEXT%252FCSplitterWnd%253A%253ACreateView);k(CSplitterWnd%253A%253ACreateView);k(CreateView);k(DevLang-C%252B%252B);k(TargetOS-Windows)%26rd%3Dtrue&view=msvc-160

 

CSplitterWnd 클래스

CSplitterWnd 클래스CSplitterWnd Class 이 문서의 내용 --> 여러 개의 창이 포함된 창인 분할자 창 기능을 제공합니다.Provides the functionality of a splitter window, which is a window that contains multiple panes. 구문Syntax cl

docs.microsoft.com

위와 같이 정의하였으면 결과는 다음과 같다.

이제 미니맵 클래스에서 OnDraw 함수 부분으로 이동하여 다음과 같이 정의하면 된다.

void CMiniView::OnDraw(CDC* pDC)
{
	CDocument* pDoc = GetDocument();	
	CGraphic_Device::Get_Instance()->Render_Begin(); 
    
	// winApp -> MainFrame 얻어옴. 
	CMainFrame* pMain = dynamic_cast<CMainFrame*>(::AfxGetApp()->GetMainWnd());
    
	//MainFrame -> MainSplitter -> MFCToolView를 얻어옴. 
	CMFCToolView* pView = dynamic_cast<CMFCToolView*>(pMain->m_MainSplitter.GetPane(0, 1)); 
    
	// MFCToolView -> CTerrain 얻어옴. 
	CTerrain* pTerrain = pView->m_pTerrain;
    
	pTerrain->MiniRender_Terrain();
	CGraphic_Device::Get_Instance()->Render_End(m_hWnd); 
}

결과

먼저 UnitTool(유닛의 정보가 들어가있는)로 사용할 클래스를 하나와

UnitTool 창을 띄워줄 클래스(MyFormView) 하나를 만들어준다.

 

MyFormView의 기본 클래스는 CFormView로 지정하고,

UnitTool의 기본 클래스는 CDialog로 지정해준다.

 

 

클래스를 만들고나서 Ctrl + Alt + E를 눌러서 리소스 뷰를 띄워준다.

그 다음 Dialog 폴더에 있는 IDD_UNITTOOL을 더블클릭하면 다음과 같은 화면이 나온다.

 

여기까지 완료하였으면 Ctrl + Alt + X를 눌러서 도구 상자를 띄우고

도구 상자 안에있는 Button을 드래그해서 UNITTOOL 뷰에 옮긴다.

우리가 도구 상자에서 사용할 것은 몇개 되지 않기때문에 사용할 도구들의 의미에 대해서만 설명하려고한다.

 

Button : 버튼

Check Box : 체크 박스

Edit Control : 입력란

List Box : 정보를 표시해줄 공간

Radio Button : 동그란 체크 박스

Static Text

-> 아래에 Static으로 적혀있는 Text 도구인데 우측에 있는 입력란에 어떤 정보가 들어갈지 모른다.

-> 우측에 어떠한 정보가 들어가 있는지 알려주기 위해 표시하는 역할

도구의 이름은 한 번만 클릭하고 타자를 입력해서 변경하면 된다.

여기서 주의해야 할 점은 만약 더블 클릭을 하게 되면, 버튼에 대한 이벤트 처리기 함수가 자동으로 생성되기 때문에

원하지 않은 이벤트 처리기 함수가 추가될 경우 해당 함수가 추가된 클래스에 찾아가서

관련된 함수들을 전부 지워줘야되는 번거로움이 생기므로 주의하는 것이 좋다.

 

일단 Button1로 되어있는 이름을 Add라고 지어준다.

 

그 다음 해당 버튼이 클릭되어 있는 상태에서 오른쪽 마우스를 클릭하여 이벤트 처리기 추가를 클릭한다.

그럼 다음과 같은 창이 나올 것이다.

여기서 메시지 형식에 BN_CLIKED라고 나와있는데 이것은 버튼을 클릭 했을 때 이벤트를 발생 시킨다는 의미이다.

클래스 목록은 이벤트 처리기 함수를 추가하고싶은 클래스를 선택하는 곳이다.

함수 처리기 이름은 개인이 변경하고 싶은 함수명이 없으면 자동으로 기본 값으로 세팅이 된다.

변경할 것이 없다면 위에 상태에서 추가를 눌러준다.

 

추가를 누르게되면 다음과 같이 UnitTool 클래스에 함수가 추가된 것을 볼 수 있다.

버튼이 제대로 동작하는지 테스트를 해보자

정상적으로 나온 것을 확인할 수 있다.

 

이제 UnitTool 창을 MyFormView의 버튼을 통해서 나오도록 해보려고 한다.

 

우선 MyFormView의 버튼이 화면에 나오게 해야한다.

이전에 미니맵을 생성했을 때 사용한 Splitter를 이용해서 창 분할을 하였는데,

똑같은 방식으로 분할하여 그 위치에 표시되도록 한다.

 

MainFrame 클래스 -> OnCreateClient 함수

여기까지 하였으면 다음과 같이 정상적으로 버튼이 화면에 표시되는 것을 볼 수 있다.

 

이제 클래스뷰에서 MyFormView 클래스를 클릭하고 Alt + Enter를 눌러서 속성에 들어간 후

OnInitialUpdate 함수를 재정의해준다.

그리고 MyFormView 헤더로 가서 CUnitTool 변수를 만들어준다.

 

그 후에 만들어준 이벤트 처리기 함수에 다음과 같이 정의한다.

여기서 ShowWindow는 MyFormView에서 만든 버튼을 클릭했을 때 UnitTool의 창을 띄워주기 위해 사용하는 함수이다.

OnInitialUpdate 함수에서 Create함수는 인자에 들어간 ID로 만들어져 있는 클래스의 창을 만들어주는 역할을 한다.

아이디는 리소스 뷰에서 다음과 같이 확인할 수 있다.

여기까지 했으면 MyFormView에서 만든 버튼을 클릭 했을 때 정상적으로 ToolView의 창이 나오는 것을 확인할 수 있다.

이 다음은 UnitTool Part2에서 이어서 작성하도록하겠다.

'Programming > MFC' 카테고리의 다른 글

MFC - UnitTool Part2(리스트에 정보 추가 및 삭제)  (0) 2020.12.05
MFC - 미니맵  (1) 2020.11.29
MFC - 기본 이론 , 계층 구조  (0) 2020.11.29
MFC - 타일 픽킹(내적)  (0) 2020.11.29
MFC - 타일 픽킹(직선의 방정식)  (0) 2020.11.25

 

1. MFC란 ?

MFC(Microsoft Foundation Class)

윈도우 어플리케이션(Window Application)을 생성하기 위해 만들어진 C++ 클래스 라이브러리이다.

대부분의 클래스들은 C++ 언어를 확장(wrapper)하여 만들어졌으며,

이는 개발자로 하여금 손쉬운 GUI 기반의 프로그램 제작 환경을 만들어 주었다.

 

MFC를 이용하여 프로그램을 개발함으로써 얻을 수 있는 이점으로는 크게 두 가지로 생각해 볼 수 있다.

첫 번째로 프로그램 개발 시간을 크게 단축시켜 줌으로써 기존의 Win32 API를 이용한 프로그램 제작할 때,

개발자 스스로가 도맡아 해오던 수많은 실행 함수에 대한 코딩의 번거로움을 개선해주었다.

두 번째로는 ActiveX, OLE 등 다양한 인터페이스의 기본 제공으로  손쉬운 프로그래밍을 가능하게 해주었다는 점이다.

 

MFC 프로그램은

크게 단일 윈도우(SDI : Single Document Interface) 와 다중 윈도우(MDI : Multiple Document Interface)로 나눌 수 있다.

아래 그림은 SDI에 대한 기본 구조로써,

MFC는 기본적으로 네 개의 클래스가 모여 하나의 윈도우를 생성함을 알 수 있다.

#1. CFrameWnd

-> 윈도우의 외곽 경계를 담당

-> 메뉴, 툴바, 상태바를 가지고 있음

 

#2. CVIew

-> 실제 화면 처리를 담당

-> 문자 출력, 그래픽 출력 등

 

#3. CDocument

->디스크에서 데이터를 읽고 저장

 

여기까지 MFC 이론에 대해서 살짝 정리를 해보았고,

 

다음은 MFC 계층 구조이다.

 

계층 구조

#1. CObject 클래스(최상위 클래스)

메모리에 클래스를 설정하는 기능

클래스를 할당하기 위해 new 연산자가 오버로딩 된다.

/MFC/Include/Afx.h 설정

클래스의 기능과 종류를   있는 함수가 있다.

- IsSerializable() : 현재 클래스가 데이터를 디스크에 저장할  있는 기능을 자지고 있는지 없는지를 확인하는 함수

- AssertValid( ) : 현재 클래스가 유효한 클래스인가를 확인하는 함수

- Dump() : 현재 클래스의 상태를 확인하는 함수

디버깅할   함수를 이용하여 데이터의 상태를 확인하고 오류를 정리할  있음

 

#2. CCmdTarget클래스

메시지 전송을 담당하는 클래스

실질적으로 메시지를 처리하는 것이 아니라WM_COMMAND OLE 메시지만 담당

 

#3. CWnd 클래스

화면에 보이는 윈도우들은 모두 CWnd 에서 상속 받음

가장 많이 사용되는 클래스

윈도우의 최상위 클래스

상속해서 사용하지 직접 CWnd 클래스를 사용하지는 않는다.

 

#4. CWndThread 클래스

윈도우가 스레드로 돌아갈  있도록 구동 되는 클래스

- 스레드는 독립적인 형태로 구동 되는 하나의 모듈

 개의 프로그램을 독립적으로 움직이려면  개의 프로그램은   이상의 CWinThread 포함해야 한다

- Multi-tasking 가능

 

#5. CWndApp 클래스

 개의 프로그램을 포함하고 관장하는 클래스

 

#6. CDocument 클래스

데이터를 디스크에서 읽어 들이거나 디스크에 저장하는 부분을 담당하는 클래스

주로 알고리즘을 저장

나중에 다른 프로그램에서 재사용이 용이

 

#7. CWnd를 상속 받은 기타 클래스

CFrameWnd : 프레임 형태의 윈도우

CControlBar : 컨트롤바

CPrepertySheet : 프로퍼티 시트

CDialog : 대화 상자

CView : View 윈도우

Control Class : 각종 컨트롤들

'Programming > MFC' 카테고리의 다른 글

MFC - 미니맵  (1) 2020.11.29
MFC - UnitTool Part1(기본 구성), 이벤트 처리기  (0) 2020.11.29
MFC - 타일 픽킹(내적)  (0) 2020.11.29
MFC - 타일 픽킹(직선의 방정식)  (0) 2020.11.25
MFC - 타일 배치  (0) 2020.11.25

+ Recent posts