원블록 테트리스 만들기
VC++ (MFC) 공부하면서 SDI(싱글 도큐먼트 인터페이스)에서 Drawing(그리기) 예제를 이용하여 1블록 테트리스를 만들었습니다. 저는 예전에도 테트리스를 만들어봐서 즉흥적으로 만들었습니다. 다시 프로그래밍을 공부중이어서 옛날 생각을 더듬어 만들었는데 허접하네요.
// SDrawingView.cpp : implementation of the CSDrawingView class
// 즉흥적으로 만들어본 한칸짜리 테트리스
#include "stdafx.h"
// SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail
// and search filter handlers and allows sharing of document code with that project.
#ifndef SHARED_HANDLERS
#include "SDrawing.h"
#endif
#include "SDrawingDoc.h"
#include "SDrawingView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
int x = 160; // 초기 블럭 나올때 좌표
int y = 100;
int w = 200;
int h = 120;
int test = 0;
UINT htimer; // 타이머
int gap = 20; // 간격
int xy[7][10]; // 테트리스 칸
int mvblock[7][10]; //움직이는 블럭
int copyblock[7][10]; // 블럭 복사하기, 블럭 칸 완성되면 위에칸이 아래칸이 내려오게 보이기
int mbblockX=3; // 최초 블록 위치 지정
int mbblockY=0;
bool m_bNewblock = 0;
bool bMoveLeft=1; // 왼쪽 움직임 가능여부 1이면 가능 0이면 불가
bool bMoveRight=1; // 오른쪽 움직임 가능여부
bool bMoveDown=1; //아래쪽 움직임 가능여부
int gamePoint = 0; // 게임 포인트 - 몇줄 삭제한지 기록
// CSDrawingView
IMPLEMENT_DYNCREATE(CSDrawingView, CView)
BEGIN_MESSAGE_MAP(CSDrawingView, CView)
ON_WM_CONTEXTMENU()
ON_WM_KEYDOWN()
ON_WM_TIMER()
END_MESSAGE_MAP()
// CSDrawingView construction/destruction
CSDrawingView::CSDrawingView()
: m_ptX(0)
, m_ptY(0)
, m_crColor(0)
,m_reRect(0,0,3000,3000)
{
// TODO: add construction code here
m_crColor = BLACK_BRUSH;
}
CSDrawingView::~CSDrawingView()
{
}
BOOL CSDrawingView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CView::PreCreateWindow(cs);
}
// CSDrawingView drawing
void CSDrawingView::OnDraw(CDC* pDC)
{
CSDrawingDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
int i, j;
int tempi, tempj;
//htimer = SetTimer(1, 500, NULL);//0.5초마다 블록이 떨어진다.
CPen redpen(PS_SOLID, 1, RGB(255, 0, 0)); // 벽 그리기
pDC->SelectObject(&redpen);
for (i = 0; i < 10; i++) //좌,우 벽 =3
{
xy[0][i] = 3;
pDC->Rectangle(100, 100 + (gap*i), 120, 120 + (gap*i)); //좌측 벽
xy[6][i] = 3;
pDC->Rectangle(220, 100 + (gap*i), 240, 120 + (gap*i)); //우측 벽
}
for (j = 0; j < 7; j++) //하단 벽 =3
{
xy[j][9] = 3;
pDC->Rectangle(100 + (gap*j), 280, 120 + (gap*j), 300); //하단 벽
}
if (m_bNewblock == 0)// 최초 빈공간 초기화
{
for (i = 1; i < 9; i++) //빈공간
{
for (j = 1; j < 6; j++)
{
xy[j][i] = 0; //빈공간 = 0
}
}
m_bNewblock = 1;
}
CPen greenpen(PS_SOLID, 1, RGB(0, 255, 0)); // 쌓인 벽돌 그리기
pDC->SelectObject(&greenpen);
for (i = 0; i < 9; i++)
{
for (j = 0; j < 6; j++)
{
if (xy[j][i] == 2)
{
pDC->Rectangle(100 + (gap*j), 100 + (gap*i), 120 + (gap*j), 120 + (gap*i));
}
}
}
// 배열 값으로 보여주기
CClientDC dc(this);
CString strPoint;
for (i = 0; i < 7; i++)
{
for (j = 0; j < 10; j++)
{
strPoint.Format(_T("%d"), xy[i][j]);
dc.TextOutW(280 + (gap * i), 100 + (gap * j),strPoint);
}
}
tempi = mbblockX;
tempj = mbblockY;
CString strPoint2, strPoint3, strPoint4, strgamePoint, strmovePoint;
strPoint2.Format(_T("좌표 [%d][%d]=%d test=%d"),tempi,tempj, xy[tempi][tempj],test);
dc.TextOutW(0, 0, strPoint2);
strgamePoint.Format(_T("게임 포인트: %d"), gamePoint);
dc.TextOutW(400, 0, strgamePoint);
strmovePoint.Format(_T("bmoveLeft = %d, bmoveRigh = %d, bmoveDown = %d"), bMoveLeft, bMoveRight, bMoveDown);
dc.TextOutW(600, 0, strmovePoint);
NewBlock(); // 새로운 블록 생성
}
void CSDrawingView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}
// CSDrawingView diagnostics
#ifdef _DEBUG
void CSDrawingView::AssertValid() const
{
CView::AssertValid();
}
void CSDrawingView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CSDrawingDoc* CSDrawingView::GetDocument() const // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSDrawingDoc)));
return (CSDrawingDoc*)m_pDocument;
}
#endif //_DEBUG
// CSDrawingView message handlers
void CSDrawingView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handwler code here and/or call default
int tempi, tempj, tempk;
switch (nChar)
{
case VK_LEFT: //방향키 왼쪽이 눌러지면..
tempi = mbblockX;
tempj = mbblockY;
SerchAround();
if ((bMoveLeft == 0 || bMoveRight ==0) && bMoveDown==0)
{
xy[tempi][tempj] = 2;
x = 160, y = 100;
mbblockX = 3, mbblockY = 0;
LineCheck();
NewBlock();
}
else if (bMoveLeft == 1)
{
mbblockX -= 1;
mvblock[mbblockX][mbblockY] = 1;
xy[tempi][tempj] = 0;
xy[mbblockX][mbblockY] = mvblock[mbblockX][mbblockY];
x -= gap;
w -= gap;
}
Invalidate();
break;
case VK_RIGHT: // 방향키 오른쪽이 눌러지면
tempi = mbblockX;
tempj = mbblockY;
SerchAround();
if ((bMoveLeft == 0 || bMoveRight == 0) && bMoveDown == 0)
{
xy[tempi][tempj] = 2;
x = 160, y = 100;
mbblockX = 3, mbblockY = 0;
LineCheck();
NewBlock();
}
else if (bMoveRight == 1)
{
mbblockX += 1;
mvblock[mbblockX][mbblockY] = 1;
xy[tempi][tempj] = 0;
xy[mbblockX][mbblockY] = mvblock[mbblockX][mbblockY];
x += gap;
w += gap;
}
Invalidate();
break;
case VK_DOWN: //방향키 아래 누르면
tempi = mbblockX;
tempj = mbblockY;
gameOver(); // 게임오버 검사
SerchAround();
if (bMoveDown == 0)
{
xy[tempi][tempj] = 2;
x = 160, y = 100;
mbblockX = 3, mbblockY = 0;
LineCheck();
NewBlock();
}
else if (bMoveDown == 1)
{
mbblockY += 1;
mvblock[mbblockX][mbblockY] = 1;
xy[tempi][tempj] = 0;
xy[mbblockX][mbblockY] = mvblock[mbblockX][mbblockY];
y += gap;
h += gap;
}
Invalidate();
break;
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CSDrawingView::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
// 타이머는 VK_DOWM 복사
int tempi, tempj, tempk;
tempi = mbblockX;
tempj = mbblockY;
SerchAround();
if (bMoveDown == 0)
{
xy[tempi][tempj] = 2;
x = 160, y = 100;
mbblockX = 3, mbblockY = 0;
LineCheck();
NewBlock();
}
else if (bMoveDown == 1)
{
mbblockY += 1;
mvblock[mbblockX][mbblockY] = 1;
xy[tempi][tempj] = 0;
xy[mbblockX][mbblockY] = mvblock[mbblockX][mbblockY];
y += gap;
h += gap;
}
Invalidate();
CView::OnTimer(nIDEvent);
}
void CSDrawingView::NewBlock() // 새로운 블록 생성
{
CClientDC dc(this);
CPen blackpen(PS_SOLID, 1, RGB(0, 0, 0)); // 블록 그림
dc.SelectObject(&blackpen);
dc.Rectangle(x, y, x + gap, y + gap);
mvblock[mbblockX][mbblockY] = 1; // 최초 위치 [3][0]
htimer = SetTimer(1, 500, NULL);//0.5초마다 블록이 떨어진다.
}
void CSDrawingView::Game(int xPos, int yPos, int wPos, int hPos) //게임판에 쌓인 벽돌 그리기 갱신
{
int i, j;
CClientDC dc(this);
CPen bluepen(PS_SOLID, 1, RGB(0, 0, 255)); // 벽 그리기
dc.SelectObject(&bluepen);
for (i = 0; i < 9; i++)
{
for (j = 0; j < 6; j++)
{
if (xy[j][i] == 2)
{
dc.Rectangle(xPos, yPos, wPos, hPos);
}
}
}
}
void CSDrawingView::SerchAround()//주변을 탐색한다. 벽인지 쌓인 벽돌인지 탐색.
{
int tempLeft, tempRight, tempDown; //임시 저장 장소
int serchLeft, serchRight, serchDown; // 주변 검색 값 저장
tempLeft = mbblockX - 1; //왼쪽검색
tempRight = mbblockX + 1; //오른쪽검색
tempDown = mbblockY + 1; //아래 검색
serchLeft = xy[tempLeft][mbblockY]; //왼쪽 검색값
serchRight = xy[tempRight][mbblockY]; //오른쪽 검색값
serchDown = xy[mbblockX][tempDown]; //아래 검색값
if (serchLeft == 3 || (serchDown == 3|| serchDown == 2)) //왼쪽 검색값이 3이면 못가 3은 벽
{
bMoveLeft = 0;
}
else if (serchLeft == 2 || (serchDown == 3 || serchDown == 2)) //왼쪽검색값이 2여도 못가 2는 쌓인벽돌
{
bMoveLeft = 0;
}
else if (serchLeft == 0) // 왼쪽 검색값이 0이면 갈수 있다.
{
bMoveLeft = 1;
}
// 오른쪽 검색값 움직임 여부
if (serchRight == 3 || (serchDown == 3 || serchDown == 2))
{
bMoveRight = 0;
}
else if (serchRight == 2 || (serchDown == 3 || serchDown == 2))
{
bMoveRight = 0;
}
else if (serchRight == 0)
{
bMoveRight = 1;
}
// 아래쪽 검색값 움직임 여부
if (serchDown == 3)
{
bMoveDown = 0;
}
else if (serchDown == 2)
{
bMoveDown = 0;
}
else if (serchDown == 0)
{
bMoveDown = 1;
}
}
void CSDrawingView::LineCheck() // 블록이 5개가 차면 없어지고 위에칸이 아래칸으로 값을 복사한다.
{
// 라인체크 구성하기
int count[9],i,j;
for (j = 8; j > 0; j--)
{
count[j] = 0;
for (i = 1; i < 6; i++)
{
if (xy[i][j] == 2)
{
count[j]++;
if (count[j] == 5)
{
gamePoint++; // 게임 포인트 올리기
for (j = 8; j > 0; j--)
{
for (i = 1; i < 6; i++)
{
copyblock[i][j] = xy[i][j - 1];
xy[i][j] = copyblock[i][j];
}
}
}
}
}
}
}
void CSDrawingView::gameOver()
{
if (xy[3][0]) //시작점에서 움직일수 없다면 게임 오버처리
{
if (bMoveDown == 0 && bMoveLeft == 0 && bMoveRight == 0)
{
AfxMessageBox(_T("게임 오버"), (MB_YESNO | MB_ICONEXCLAMATION));
KillTimer(htimer);
}
}
}
// 즉흥적으로 만들어본 한칸짜리 테트리스