간단한 턴제 게임으로 던전 탈출하는 게임.
2016년 1월 15일 금요일
2015년 5월 24일 일요일
더블버퍼링
mfc의 경우에 onDraw나 onPaint에 그림을 그리거나
혹은 비트맵등의 이미지 파일을 호출할 텐데 처음 윈도우프로그래밍을 배우는
사람들이라면 api를 쓰던지 뭘 하던지 많은 이미지를 여러번 호출하거나
그리게되면 자주 화면 깜빡거림을 보게되어 거슬린다고 짜증내게 될 것이다.
그 이유는 반복문으로 한 그림을 여러번 그린다고 가정했을때를 예를 들겠다.
onDraw나 onPaint에 그냥 CDC와 BITMAP를 생성해서
바닥과 벽이라는 이미지를 33픽셀 단위로 그린다고 하였을때
//셋팅된 맵 배열 읽고 이미지 삽입
for (int x = 0; x < 1279; x += 33){
for (int x = 0; x < 1279; x += 33){
for (int y = 0; y < 719; y += 33){
if (mainRoop->cMap[mainRoop->
player->playerMapCount].mapSet[x][y] == 0){
//벽
pDC->BitBlt(x, y, 33, 33, &wallDC, 0, 0, SRCCOPY);
}
else{
//바닥
pDC->BitBlt(x, y, 33, 33, &floorDC, 0, 0, SRCCOPY);
}
}
}
이 코드는 몇번 그리게 되고 그럼 몇번이나 깜빡일까?
Sleep를 걸어보면 알겠지만 이미지한번 그릴때마다 깜빡일것이다.
물론 이렇게 한번 쫙 그리고 그만둔다면 OnEraseBkgnd 함수를 찾아서
리턴값을 수정하면 하얗게 깜빡이는 현상이 사라지겠지만 게임같은 경우는
맵외에도 몬스터도 실시간으로 움직일텐데 그냥 onDraw나 onPaint에 다 그리면
매우 심각하게 반짝인다. 한번 그릴때마다 갱신하니까 장난아니게 되는것이다.
실 예로 영상을 첨부하겠다.
영상을 보면 알겠지만 깜빡임이 장난 아닐것이다. 맵 비트맵 33픽셀단위로 한번씩 그리고 다 그린다음에는 각 몬스터를 그리고 플레이어를 그리고.. 게임이니까 움직일때마다 다시 그려야하므로 깜빡임이 맵그리기, 몬스터 각각 이미지 뿌리기, 플레이어 뿌리기 하면 와우..
하지만 걱정말자 해결 방법이 있다.
예를 조금 더 들어보겠다.
화가가 그림을 그리는건 직접 실시간으로 볼 수 있다. 어디가 그려지는지,
무엇을 지우는지 실시간으로 관찰이 가능하고 눈에 보인다.
그러나 카메라는 기기에서 모두 처리하고 한번에 출력해버린다.
즉 더블버퍼링이라는 것은 카메라 처럼 뒤에서 모두 그린뒤 한번에
onDraw나 onPaint에 그림을 그려주는것이다.
어느 프로그래밍을 하던지 개념은 같다. 그러니 코드가 다르더라도 개념만 이해하면
api던 mfc건 뭐건 그냥 적용이 가능하니 나는 그냥 mfc로 설명하겠다.
//화면 크기와 그릴 공간을 담당할 rect
CRect rect;
GetClientRect(&rect);
// 메모리 DC 선언
CDC dubleBfDC;
CBitmap bitmap;
CBitmap* pOldBitmap;
// 화면 DC와 호환되는 메모리 DC 객체를 생성
dubleBfDC.CreateCompatibleDC(pDC);
// 화면 DC와 호환되는 Bitmap 생성
bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
pOldBitmap = memDC.SelectObject(&bitmap);
//현재 화면 해상도를 가져와서 검은색으로 모두 채움
dubleBfDC.PatBlt(0, 0, rect.Width(), rect.Height(), BLACKNESS);
//이미지셋 함수에 파라메터값으로 &dubleBfDC를 넘김
ImageSet(&dubleBfDC);
// 메모리 DC를 화면 DC에 고속 복사
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dubleBfDC, 0, 0, SRCCOPY);
dubleBfDC.SelectObject(pOldBitmap);
dubleBfDC.DeleteDC();
bitmap.DeleteObject();
}
void CGameProjectView::ImageSet(CDC* pDC){
// onDraw나 onPaint에 그렸던 것을 여기다가 옮겨준다.
}
/*소스코드 참고 : http://warmz.tistory.com/865 */
코드를 보면 onDraw에서 dubleBfDC를 생성해서 뒤에서 그림 그릴 도화지를 생성했다.
그 다음 ImageSet에서 그림을 모두 dubleBfDC에 그린다음 onDraw에서는 다 그려진
dubleBfDC를 출력한다.
카메라처럼 뒤에서 모두 이미지를 그린다음 출력만 onDraw에서 하는것이다.
그렇게 하면 깜빡임이 거의 없다고 보면된다.
아래 영상은 더블버퍼링을 사용한 영상이다.
2015년 5월 11일 월요일
c, c++ 랜덤함수 사용하여 중복되지 않게 몬스터 배치하기
학교 과제로 mfc 프로그래밍하는데 주제가 2D 로크라이크 게임만들기를 하면서
몬스터 좌표가 겹치지않게 하는 코드를 작성해 보았습니다.
일단 변수와 함수를 선언
//몬스터 랜덤 좌표세팅 함수
void MonsterRandomSet(void);
//몬스터 랜덤 좌표 함수
int MonsterRandomX(void);
int MonsterRandomY(void);
//몬스터 랜덤 좌표 넣기위한 저장 변수
int mrX[18];
int mrY[18];
//랜덤 좌표 중복을 방지하기위한 체크 변수(전체 x값은 0~38, y값은 0~22)
bool mrXCheck[37];
bool mrYCheck[22];
몬스터 좌표 xy 값을 랜덤하게 넣기 위해 함수로 각각 분할했고
//몬스터 x좌표 랜덤 함수
int CMainRoop::MonsterRandomX(void)
{
int x=0;
//좌표 범위는 각 테두리 33픽셀위치에 생성하면 안되므로 랜덤최대값+1
x=rand()%37 + 1;
return x;
}
//몬스터 y좌표 랜덤 함수
int CMainRoop::MonsterRandomY(void)
{
int y=0;
//좌표 범위는 각 테두리 33픽셀위치에 생성하면 안되므로 랜덤최대값+1
y=rand()%20 + 1;
return y;
}
좌표가 중복이 되는지 확인하여 중복되지않게 하기 위해 랜덤세팅 함수를 만들었어요.
//몬스터 좌표 랜덤 세팅함수
void CMainRoop::MonsterRandomSet(void)
{
for(int i = 0 ; i<18 ; i++){
//몬스터 좌표 넣기위한 저장 변수 초기화
mrX[i] = 0;
mrY[i] = 0;
}
for(int i = 0 ; i<38 ; i++){
//좌표 중복을 방지하기위한 체크 변수(전체 x값은 0~38, y값은 0~22) 초기화
mrXCheck[i] = false;
}
for(int i = 0 ; i<22 ; i++){
//좌표 중복을 방지하기위한 체크 변수(전체 x값은 0~38, y값은 0~22)
mrYCheck[i] = false;
}
for(int mrc = 0; mrc<18 ;){
//랜덤으로 좌표값을 구해 임시 변수에 저장한다.
int nTempX = MonsterRandomX();
int nTempY = MonsterRandomY();
//중복 검사 시작 x와 y 값이 false이면
if(mrXCheck[nTempX] == false && mrYCheck[nTempY] == false){
//해당하는 좌표값을 true로 바꾼다.
mrXCheck[nTempX] = true;
mrYCheck[nTempY] = true;
//그리고 몬스터 임시 좌표값 변수에 넣어준다.
mrX[mrc] = nTempX;
mrY[mrc] = nTempY;
//좌표값을 모두 넣기위해 여기서 증감연산을 함.
mrc++;
}
}
}
실제 좌표 넣는 코드
//해당 층의 몬스터가 세팅 되지 않았을때 몬스터 좌표 세팅
if(cMap[player->playerMapCount].mapMonsterSetCheck == false){
//몬스터 좌표세팅 함수 호출
MonsterRandomSet();
for(int i = 0 ; i<18; i++){
//몬스터 좌표가 셋팅되지 않았을 경우
if(cMap[player->playerMapCount].monster[i].monsterSets == FALSE){
//몬스터 좌표세팅 여부 true로 변경
cMap[player->playerMapCount].monster[i].monsterSets = TRUE;
//Sleep(1000);
//랜덤으로 생성한 값에 *33하여 좌표지정. 이미지가 33픽셀이므 로 랜덤 숫자*33하면 해당 좌표값이됨
cMap[player->playerMapCount].monster[i].Point.x = mrX[i]*33;
cMap[player->playerMapCount].monster[i].Point.y = mrY[i]*33;
}
}
//해당 맵의 몬스터 세팅이 완료되었다고 값 변환
cMap[player->playerMapCount].mapMonsterSetCheck = true;
}
몬스터 좌표가 겹치지않게 하는 코드를 작성해 보았습니다.
일단 변수와 함수를 선언
//몬스터 랜덤 좌표세팅 함수
void MonsterRandomSet(void);
//몬스터 랜덤 좌표 함수
int MonsterRandomX(void);
int MonsterRandomY(void);
//몬스터 랜덤 좌표 넣기위한 저장 변수
int mrX[18];
int mrY[18];
//랜덤 좌표 중복을 방지하기위한 체크 변수(전체 x값은 0~38, y값은 0~22)
bool mrXCheck[37];
bool mrYCheck[22];
몬스터 좌표 xy 값을 랜덤하게 넣기 위해 함수로 각각 분할했고
//몬스터 x좌표 랜덤 함수
int CMainRoop::MonsterRandomX(void)
{
int x=0;
//좌표 범위는 각 테두리 33픽셀위치에 생성하면 안되므로 랜덤최대값+1
x=rand()%37 + 1;
return x;
}
//몬스터 y좌표 랜덤 함수
int CMainRoop::MonsterRandomY(void)
{
int y=0;
//좌표 범위는 각 테두리 33픽셀위치에 생성하면 안되므로 랜덤최대값+1
y=rand()%20 + 1;
return y;
}
좌표가 중복이 되는지 확인하여 중복되지않게 하기 위해 랜덤세팅 함수를 만들었어요.
//몬스터 좌표 랜덤 세팅함수
void CMainRoop::MonsterRandomSet(void)
{
for(int i = 0 ; i<18 ; i++){
//몬스터 좌표 넣기위한 저장 변수 초기화
mrX[i] = 0;
mrY[i] = 0;
}
for(int i = 0 ; i<38 ; i++){
//좌표 중복을 방지하기위한 체크 변수(전체 x값은 0~38, y값은 0~22) 초기화
mrXCheck[i] = false;
}
for(int i = 0 ; i<22 ; i++){
//좌표 중복을 방지하기위한 체크 변수(전체 x값은 0~38, y값은 0~22)
mrYCheck[i] = false;
}
for(int mrc = 0; mrc<18 ;){
//랜덤으로 좌표값을 구해 임시 변수에 저장한다.
int nTempX = MonsterRandomX();
int nTempY = MonsterRandomY();
//중복 검사 시작 x와 y 값이 false이면
if(mrXCheck[nTempX] == false && mrYCheck[nTempY] == false){
//해당하는 좌표값을 true로 바꾼다.
mrXCheck[nTempX] = true;
mrYCheck[nTempY] = true;
//그리고 몬스터 임시 좌표값 변수에 넣어준다.
mrX[mrc] = nTempX;
mrY[mrc] = nTempY;
//좌표값을 모두 넣기위해 여기서 증감연산을 함.
mrc++;
}
}
}
실제 좌표 넣는 코드
//해당 층의 몬스터가 세팅 되지 않았을때 몬스터 좌표 세팅
if(cMap[player->playerMapCount].mapMonsterSetCheck == false){
//몬스터 좌표세팅 함수 호출
MonsterRandomSet();
for(int i = 0 ; i<18; i++){
//몬스터 좌표가 셋팅되지 않았을 경우
if(cMap[player->playerMapCount].monster[i].monsterSets == FALSE){
//몬스터 좌표세팅 여부 true로 변경
cMap[player->playerMapCount].monster[i].monsterSets = TRUE;
//Sleep(1000);
//랜덤으로 생성한 값에 *33하여 좌표지정. 이미지가 33픽셀이므 로 랜덤 숫자*33하면 해당 좌표값이됨
cMap[player->playerMapCount].monster[i].Point.x = mrX[i]*33;
cMap[player->playerMapCount].monster[i].Point.y = mrY[i]*33;
}
}
//해당 맵의 몬스터 세팅이 완료되었다고 값 변환
cMap[player->playerMapCount].mapMonsterSetCheck = true;
}
2015년 5월 2일 토요일
MFC MainFrame, App, Doc,View의 포인터 얻기
(***에 자신이 만든 프로젝트 클래스 이름을 넣으면됨)
메인프레임과 도큐먼트, 뷰 세가지 모두다 포인터 값을 얻어오려면
헤더파일 순서는 이렇게 해주어야한다.
#include "MainFrm.h"
#include "***Doc.h"
#include "***View.h"
메인프레임만 포인터값 가져오려면 메인프레임 헤더만 불러와도 되지만
도큐먼트나 뷰 클래스의 포인터값을 받아오려면 위 순서대로 헤더파일을 불러야한다.
메인프레임->도큐먼트->뷰 순서
순서가 틀리면 에러가 나므로 주의가 필요하다.
헤더파일을 선언 다 했으면
아래와 같이 포인터를 선언하여 지정해주자
MainFrame
CMainFrame *pFrame = (CMainFrame *) AfxGetMainWnd();
App
***App *pApp = (***App *) AfxGetApp();
Document
CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
***Doc *pDoc = (***Doc *)pFrame->GetActiveDocument();
***Doc *pDoc = ((CMainFrame *)AfxGetMainWnd())->GetActiveDocument();
View 포인터 얻기
CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
***View *pView = (***View *)pFrame->GetActiveView();
***View *pView = ((CMainFrame *)AfxGetMainWnd())->GetActiveView();
메인프레임과 도큐먼트, 뷰 세가지 모두다 포인터 값을 얻어오려면
헤더파일 순서는 이렇게 해주어야한다.
#include "MainFrm.h"
#include "***Doc.h"
#include "***View.h"
메인프레임만 포인터값 가져오려면 메인프레임 헤더만 불러와도 되지만
도큐먼트나 뷰 클래스의 포인터값을 받아오려면 위 순서대로 헤더파일을 불러야한다.
메인프레임->도큐먼트->뷰 순서
순서가 틀리면 에러가 나므로 주의가 필요하다.
헤더파일을 선언 다 했으면
아래와 같이 포인터를 선언하여 지정해주자
MainFrame
CMainFrame *pFrame = (CMainFrame *) AfxGetMainWnd();
App
***App *pApp = (***App *) AfxGetApp();
Document
CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
***Doc *pDoc = (***Doc *)pFrame->GetActiveDocument();
***Doc *pDoc = ((CMainFrame *)AfxGetMainWnd())->GetActiveDocument();
View 포인터 얻기
CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
***View *pView = (***View *)pFrame->GetActiveView();
***View *pView = ((CMainFrame *)AfxGetMainWnd())->GetActiveView();
2015년 4월 28일 화요일
더블버퍼링을 사용하지 않고 화면 깜빡임 방지하기
mfc를 이용하여 Invalidate();를 호출할 때가 있을건데
화면을 갱신하게 하다보니 흰 색깔과 함께 깜빡깜빡거린다.
원인은 화면을 새로 호출하는데 한번 배경을 싸그리 다 지우고 새로그리는 이유떄문인데
즉 싹 다 지우니 한번 흰 화면이 나오는거고 그다음 다시 OnDraw를 호출하면서
새로 그리는 현상때문인듯 하다.
이 경우 Invalidate(false); 이렇게 써주면 깜빡임이 사라진다.
아래 방식도 비슷하게 깜빡임을 없엘 수 있긴 한데 왠만하면 사용하지 않는게 좋다.
BOOL CGameProjectView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
//return CView::OnEraseBkgnd(pDC);
return FALSE;
}
InvalidateRect()을 사용하여 일부 이미지를 지워야 할 경우도 있을텐데 위 방식처럼
할 경우 일부 이미지가 지워지지않고 그대로인것 처럼 보인다.
일단 지워지는것은 맞는데 새로 그려버림..
화면을 갱신하게 하다보니 흰 색깔과 함께 깜빡깜빡거린다.
원인은 화면을 새로 호출하는데 한번 배경을 싸그리 다 지우고 새로그리는 이유떄문인데
즉 싹 다 지우니 한번 흰 화면이 나오는거고 그다음 다시 OnDraw를 호출하면서
새로 그리는 현상때문인듯 하다.
이 경우 Invalidate(false); 이렇게 써주면 깜빡임이 사라진다.
아래 방식도 비슷하게 깜빡임을 없엘 수 있긴 한데 왠만하면 사용하지 않는게 좋다.
BOOL CGameProjectView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
//return CView::OnEraseBkgnd(pDC);
return FALSE;
}
InvalidateRect()을 사용하여 일부 이미지를 지워야 할 경우도 있을텐데 위 방식처럼
할 경우 일부 이미지가 지워지지않고 그대로인것 처럼 보인다.
일단 지워지는것은 맞는데 새로 그려버림..
2015년 4월 26일 일요일
라디오버튼 우선 체크 설정 및 체크값 확인 방법
프로그램이나 다이얼로그창이 켜지면 사용하는 라디오버튼중에서
체크가 우선 되있으면 하는 경우가 있을텐데 그럴경우 설정 방법.
CPlayerNameDlg 이라는 다이얼로그를 생성하여 사용한다면 CPlayerNameDlg.cpp에서
void CPlayerNameDlg::DoDataExchange(CDataExchange* pDX)
함수를 찾아 내부에 아래처럼 입력하면 된다.
((CButton*)GetDlgItem(IDC_RADIO2))->SetCheck(1);
IDC_RADIO2 부분에 라디오버튼 ID값을 넣어주면된다.
SetCheck(1)에서 1이 체크표시.
라디오버튼이 체크되었는지 확인하는 방법은
((CButton*)GetDlgItem(IDC_RADIO2))->GetCheck()
를 사용하여 확인하면된다.
체크가 우선 되있으면 하는 경우가 있을텐데 그럴경우 설정 방법.
CPlayerNameDlg 이라는 다이얼로그를 생성하여 사용한다면 CPlayerNameDlg.cpp에서
void CPlayerNameDlg::DoDataExchange(CDataExchange* pDX)
함수를 찾아 내부에 아래처럼 입력하면 된다.
((CButton*)GetDlgItem(IDC_RADIO2))->SetCheck(1);
IDC_RADIO2 부분에 라디오버튼 ID값을 넣어주면된다.
SetCheck(1)에서 1이 체크표시.
라디오버튼이 체크되었는지 확인하는 방법은
((CButton*)GetDlgItem(IDC_RADIO2))->GetCheck()
를 사용하여 확인하면된다.
다이얼로그에서 메인 윈도우 해상도 조절 방법
우선 사용할 다이얼로그에 MainFrm.h 를 include 한다.
MainFrm 클래스의 포인터값을 지정하여 해상도 조절을 하면된다.
만약 라디오 버튼을 사용 할 경우를 가정하여 예를 들면 아래와 같다.
CPlayerNameDlg 다이얼로그의 라디오버튼 1이 체크될 경우
void CPlayerNameDlg::OnBnClickedRadio1()
{
//해상도 크기800,600
CMainFrame *frame = (CMainFrame*)AfxGetMainWnd();
frame->SetWindowPos(NULL,200,300,800,600,SWP_NOMOVE);
}
하위 다이얼로그에서 메인 윈도우 끄는 방법(전체 종료)
메인 윈도우 화면이 있고 다이얼로그를 추가 호출하여 사용도중
다이얼로그에서 종료버튼 조작시 프로그램이 종료되게 하는 방법은
우선 추가한 다이얼로그.cpp 파일에서 MainFrm.h 파일을 include 해주어야한다.
그 다음 include 한 MainFrm 클래스의 포인터값을 가져와서
프로그램 호출 종료를 시도하면된다.
CPlayerNameDlg 이라는 다이얼로그를 만들고 다이얼로그에서 취소버튼을 눌러
프로그램이 종료되게 하려면 아래처럼 입력하면된다.
아래 함수는 다이얼로그의 취소버튼 입력 이벤트 처리함수이다.
void CPlayerNameDlg::OnBnClickedCancel()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CDialogEx::OnCancel();
//메인프레임 포인터를 가져와서 프로그램 종료 호출
CMainFrame *frame = (CMainFrame*)AfxGetMainWnd();
frame->PostMessage(WM_CLOSE, NULL, NULL);
}
다이얼로그에서 종료버튼 조작시 프로그램이 종료되게 하는 방법은
우선 추가한 다이얼로그.cpp 파일에서 MainFrm.h 파일을 include 해주어야한다.
그 다음 include 한 MainFrm 클래스의 포인터값을 가져와서
프로그램 호출 종료를 시도하면된다.
CPlayerNameDlg 이라는 다이얼로그를 만들고 다이얼로그에서 취소버튼을 눌러
프로그램이 종료되게 하려면 아래처럼 입력하면된다.
아래 함수는 다이얼로그의 취소버튼 입력 이벤트 처리함수이다.
void CPlayerNameDlg::OnBnClickedCancel()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CDialogEx::OnCancel();
//메인프레임 포인터를 가져와서 프로그램 종료 호출
CMainFrame *frame = (CMainFrame*)AfxGetMainWnd();
frame->PostMessage(WM_CLOSE, NULL, NULL);
}
MFC 윈도우창 해상도 설정 방법
MainFrm.cpp에서 조절.
아래 함수를 찾아서 입력해주면 된다.
그럼 프로그램이 처음 실행될때 해상도가 조절이됨.
해상도 조절을 사용자가 임의로 테두리 클릭, 드래그로 조절하지 못하게
고정으로 하는 방법도 아래에 주석으로 처리되어 있는 부분을 입력하면됨.
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
// TODO: CREATESTRUCT cs를 수정하여 여기에서
// Window 클래스 또는 스타일을 수정합니다.
//초기 해상도 크기조정
cs.cx = 1587;
cs.cy = 829;
//해상도 자체조정 불가능하게 하는 방법
//cs.style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
return TRUE;
}
아래 함수를 찾아서 입력해주면 된다.
그럼 프로그램이 처음 실행될때 해상도가 조절이됨.
해상도 조절을 사용자가 임의로 테두리 클릭, 드래그로 조절하지 못하게
고정으로 하는 방법도 아래에 주석으로 처리되어 있는 부분을 입력하면됨.
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
// TODO: CREATESTRUCT cs를 수정하여 여기에서
// Window 클래스 또는 스타일을 수정합니다.
//초기 해상도 크기조정
cs.cx = 1587;
cs.cy = 829;
//해상도 자체조정 불가능하게 하는 방법
//cs.style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
return TRUE;
}
피드 구독하기:
글 (Atom)