// WATTetrisView.cpp : implementation of the CWATTetrisView class
//
#include "stdafx.h"
#include "WATTetris.h"
#include "WATTetrisDoc.h"
#include "WATTetrisView.h"
#include "LogWnd.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern int g_NowBlockInfo[7][4][4][4]; // WATOneTetris.cpp에 선언
/////////////////////////////////////////////////////////////////////////////
// CWATTetrisView
IMPLEMENT_DYNCREATE(CWATTetrisView, CFormView)
BEGIN_MESSAGE_MAP(CWATTetrisView, CFormView)
//{{AFX_MSG_MAP(CWATTetrisView)
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWATTetrisView construction/destruction
CWATTetrisView::CWATTetrisView()
: CFormView(CWATTetrisView::IDD)
{
//{{AFX_DATA_INIT(CWATTetrisView)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// TODO: add construction code here
// m_iNowBlockDir = 0;
// m_iNowBlockX = 5;
// m_iNowBlockY = 0;
// m_iOneBlockSize = 15;
//
//
// // 보드의 좌측 벽
// for(int i=0;i<BOARD_Y_SIZE;i++){
// g_BoardInfo[i][0]=50;
// }
//
// // 보드의 우측 벽
// for( i=0;i<BOARD_Y_SIZE;i++){
// g_BoardInfo[i][BOARD_X_SIZE+1]=50;
// }
//
// // 보드의 하단 벽
// for( i=0;i<BOARD_X_SIZE+2;i++){
// g_BoardInfo[BOARD_Y_SIZE][i]=50;
// }
// 테스트용 코드
// for( i=3;i<BOARD_X_SIZE+2;i++){
// g_BoardInfo[BOARD_Y_SIZE-1][i]=50;
// }
// g_BoardInfo[BOARD_Y_SIZE/2][5]=50;
}
CWATTetrisView::~CWATTetrisView()
{
}
void CWATTetrisView::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CWATTetrisView)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BOOL CWATTetrisView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CFormView::PreCreateWindow(cs);
}
void CWATTetrisView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
GameStart();
}
/////////////////////////////////////////////////////////////////////////////
// CWATTetrisView printing
BOOL CWATTetrisView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CWATTetrisView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CWATTetrisView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
void CWATTetrisView::OnPrint(CDC* pDC, CPrintInfo* /*pInfo*/)
{
// TODO: add customized printing code here
}
/////////////////////////////////////////////////////////////////////////////
// CWATTetrisView diagnostics
#ifdef _DEBUG
void CWATTetrisView::AssertValid() const
{
CFormView::AssertValid();
}
void CWATTetrisView::Dump(CDumpContext& dc) const
{
CFormView::Dump(dc);
}
CWATTetrisDoc* CWATTetrisView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CWATTetrisDoc)));
return (CWATTetrisDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CWATTetrisView message handlers
void CWATTetrisView::OnPaint()
{
CPaintDC dc(this); // device context for painting
m_oneTetris[0].DrawBoard();
m_oneTetris[1].DrawBoard();
m_oneTetris[2].DrawBoard();
}
BOOL CWATTetrisView::PreTranslateMessage(MSG* pMsg)
{
if (WM_KEYDOWN ==pMsg->message)
{
m_oneTetris[0].OnKey(pMsg->wParam);
m_oneTetris[1].OnKey(pMsg->wParam);
m_oneTetris[2].OnKey(pMsg->wParam);
}
return CFormView::PreTranslateMessage(pMsg);
}
void CWATTetrisView::OnTimer(UINT nIDEvent)
{
if (nIDEvent>=TIMER_FIST_NUM) // 타이머 번호 10 이상인 것만 사용
{
m_oneTetris[nIDEvent-TIMER_FIST_NUM].OnTimer();
}
CFormView::OnTimer(nIDEvent);
}
BOOL CWATTetrisView::IsGameEnd(){
// 나중에 필요함
// for(int tempY = 0;tempY<2 ;tempY++){
// for(int tempX=3; tempX<7; tempX++)
// {
// if(0 != m_BoardInfo[tempY][tempX]){
// return TRUE;
// }
// }
// }
return FALSE;
}
void CWATTetrisView::GameStart()
{
srand( (unsigned)time( NULL ) );
m_oneTetris[0].SetGameID(0);
m_oneTetris[1].SetGameID(1);
m_oneTetris[2].SetGameID(2);
m_oneTetris[0].SetBoardXY(0,0);
m_oneTetris[1].SetBoardXY(180,0);
m_oneTetris[2].SetBoardXY(330,0);
m_oneTetris[0].SetBlockSize(13);
m_oneTetris[1].SetBlockSize(10);
m_oneTetris[2].SetBlockSize(13);
m_oneTetris[0].GameStart(1000);
m_oneTetris[1].GameStart(900);
m_oneTetris[2].GameStart(700);
m_oneTetris[0].SetDefineKey('A','D','W','S');
m_oneTetris[1].SetDefineKey('G','J','Y','H');
m_oneTetris[2].SetDefineKey(VK_LEFT,VK_RIGHT,VK_UP,VK_DOWN);
}
키값 표시하기
각 게임의 컨트롤 키값을 표시해 준다.
CString CWATOneTetris::KeyValue2String(int iValue)
{
CString strReturnValue;
if ('A'<=iValue && iValue<='Z')
{
strReturnValue.Format("%c",iValue);
}else if (VK_LEFT==iValue) strReturnValue = "LEFT KEY";
else if (VK_RIGHT==iValue) strReturnValue = "RIGHT KEY";
else if (VK_UP==iValue) strReturnValue = "UP KEY";
else if (VK_DOWN==iValue) strReturnValue = "DOWN KEY";
else{
strReturnValue = "UNDEFINED";
}
return strReturnValue;
}
// 키값 표시
CString strKeyValue;
strKeyValue.Format("LEFT : %s",KeyValue2String(m_iDefineLeftKey));
pDC.TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize,strKeyValue);
strKeyValue.Format("RIGHT : %s",KeyValue2String(m_iDefineRightKey));
pDC.TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize+20,strKeyValue);
strKeyValue.Format("ROTA : %s",KeyValue2String(m_iDefineRotateKey));
pDC.TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize+40,strKeyValue);
strKeyValue.Format("DOWN : %s",KeyValue2String(m_iDefineDownKey));
pDC.TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize+60,strKeyValue);
화면떨림 방지
테트리스 동작에는 큰 문제가 없지만 화면처리에 약간의 문제가 있다.
벽돌이 움직이거나 일정 시간마다 화면이 떨리는 문제가 있을 것이다.
벽돌 하나가 변경될때나 일정 시간(1초)이 경과되면 전체 화면을 새로 그리기 때문에 발생하는 문제이다.
이 문제를 더블버퍼링을 이용해 해결할 것이다.
더블버퍼링을 사용하는 방법은 그리 어렵지 않다.
일단 출력할 내용을 메모리에 넣었다가 그것을 모니터에 뿌려주면 된다
CDC dc;
CDC* pDC = pView->GetDC(); // 모니터에 출력할 정보
CDC* pDrawDC=pDC; // 모니터에 출력하기 전에 사용할 메모리
CBitmap bitmap;
CBitmap* pOldBitmap;
CRect m_rectDraw; // 갱신할 구역
m_rectDraw = CRect(m_iBoardX,m_iBoardY,BOARD_X_SIZE*m_iOneBlockSize+m_iBoardX,BOARD_Y_SIZE*m_iOneBlockSize+m_iBoardY);
CRect client;
pDC->GetClipBox(client);
CRect rect=client;
if(dc.CreateCompatibleDC(pDC))
{
if(bitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height()))
{
pDrawDC=&dc;
dc.OffsetViewportOrg(-rect.left,-rect.top);
pOldBitmap = dc.SelectObject(&bitmap);
dc.SetBrushOrg(0, 0);
}
}
CBrush brush;
// 배경색
if(!brush.CreateSolidBrush(RGB(200,200,200)))
return;
brush.UnrealizeObject();
pDrawDC->FillRect(client,&brush);
DrawOneBlockbyCDC( m_iNowBlockY,m_iNowBlockX,m_iOneBlockSize,pDrawDC,RGB(1,255,255));
pDrawDC->TextOut(1,1,"글씨 쓰기");
// 화면 갱신
if(pDrawDC!=pDC)
{
pDC->SetViewportOrg(0,0);
pDC->SetWindowOrg(0,0);
pDC->SetMapMode(MM_TEXT);
dc.SetViewportOrg(0,0);
dc.SetMapMode(MM_TEXT);
pDC->BitBlt(m_rectDraw.left,m_rectDraw.top,m_rectDraw.Width(),m_rectDraw.Height(),pDrawDC,m_iBoardX,m_iBoardY,SRCCOPY);
dc.SelectObject(pOldBitmap);
}
// 메모리 해제해야 메모리 누수를 막을수 있다.
pDC.DeleteDC(); // 메모리 해제
pDrawDC->DeleteDC(); // 메모리 해제
bitmap.DeleteDC(); // 메모리 해제
pOldBitmap->DeleteDC(); // 메모리 해제
}
void CWATOneTetris::DrawBoard()
{
int tempX,tempY;
CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
CWATTetrisView *pView = (CWATTetrisView *)pFrame->GetActiveView();
CDC dc;
CDC* pDC = pView->GetDC(); // 모니터에 출력할 정보
CDC* pDrawDC=pDC; // 모니터에 출력하기 전에 사용할 메모리
CBitmap bitmap;
CBitmap* pOldBitmap;
CRect m_rectDraw; // 갱신할 구역
m_rectDraw = CRect(m_iBoardX,m_iBoardY,(BOARD_X_SIZE+2)*m_iOneBlockSize+m_iBoardX,(BOARD_Y_SIZE+1)*m_iOneBlockSize+m_iBoardY+80);
CRect client;
pDC->GetClipBox(client);
CRect rect=client;
if(dc.CreateCompatibleDC(pDC))
{
if(bitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height()))
{
pDrawDC=&dc;
dc.OffsetViewportOrg(-rect.left,-rect.top);
pOldBitmap = dc.SelectObject(&bitmap);
dc.SetBrushOrg(0, 0);
}
}
CBrush brush;
// 배경색
if(!brush.CreateSolidBrush(RGB(200,200,200)))
return;
brush.UnrealizeObject();
pDrawDC->FillRect(client,&brush);
// 현재 벽돌 그림
for(tempY=0;tempY<4;tempY++)
{
for( tempX=0; tempX<4; tempX++)
{
if (0 != g_NowBlockInfo[m_iNowBlockShape][m_iNowBlockDir][tempY][tempX])
{
DrawOneBlockbyCDC( m_iNowBlockY+tempY, m_iNowBlockX+tempX,m_iOneBlockSize,pDrawDC,RGB(250,050,250));
}
}
}
// 보드 그림
for(tempY = 0;tempY<BOARD_Y_SIZE+1;tempY++){
for(tempX=0; tempX<BOARD_X_SIZE+2; tempX++)
{
if(0 != m_BoardInfo[tempY][tempX]){
DrawOneBlockbyCDC((tempY),tempX,m_iOneBlockSize,pDrawDC,RGB(200,200,200));
}
}
}
CString strKeyValue;
strKeyValue.Format("LEFT : %s",KeyValue2String(m_iDefineLeftKey));
pDrawDC->TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize,strKeyValue);
strKeyValue.Format("RIGHT : %s",KeyValue2String(m_iDefineRightKey));
pDrawDC->TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize+20,strKeyValue);
strKeyValue.Format("ROTA : %s",KeyValue2String(m_iDefineRotateKey));
pDrawDC->TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize+40,strKeyValue);
strKeyValue.Format("DOWN : %s",KeyValue2String(m_iDefineDownKey));
pDrawDC->TextOut(m_iBoardX,m_iBoardY+(BOARD_Y_SIZE+1)*m_iOneBlockSize+60,strKeyValue);
// 화면 갱신
if(pDrawDC!=pDC)
{
pDC->SetViewportOrg(0,0);
pDC->SetWindowOrg(0,0);
pDC->SetMapMode(MM_TEXT);
dc.SetViewportOrg(0,0);
dc.SetMapMode(MM_TEXT);
pDC->BitBlt(m_rectDraw.left,m_rectDraw.top,m_rectDraw.Width(),m_rectDraw.Height(),pDrawDC,m_iBoardX,m_iBoardY,SRCCOPY);
dc.SelectObject(pOldBitmap);
}
}
BOOL CWATOneTetris::MoveLeft()
{
// CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
// CWATTetrisView *pView = (CWATTetrisView *)pFrame->GetActiveView();
if(CanMove(m_iNowBlockDir,m_iNowBlockY,m_iNowBlockX-1)){
m_iNowBlockX -= 1;
DrawBoard();
// pView->Invalidate(TRUE);
}
return TRUE;
}
더블버퍼링을 사용하기 전과 사용한 후의 차이를 느낄 것이다.