数据定义:
2048游戏界面如下,界面为一个4乘4的方格,在游戏设计中定义一个16个元素的数组即可,因为每个格子有多个游戏属性,比如数值,颜色,方格坐标中心位置等,方格行列编号,这里定义了以下的数据结构:
class CGameNumBlock
{
public:
int m_iBlockID;
int m_iGameNumShow;
CRect m_RectBlock;
int m_iFlashCount;
};
游戏方块颜色值的映射表,每种数字对应一个颜色:
map<int,COLORREF> g_mapColor;
g_mapColor[0] = RGB(205,194,181);
g_mapColor[2] = RGB(238,226,213);
g_mapColor[4] = RGB(238,222,197);
g_mapColor[8] = RGB(238,178,115);
g_mapColor[16] = RGB(246,149,98);
g_mapColor[32] = RGB(246,125,90);
g_mapColor[64] = RGB(246,93,49);
g_mapColor[128] = RGB(238,206,106);
游戏启动时方块坐标及数值的初始化:
CRect rectCore;
m_wndCore.GetClientRect(rectCore);
m_BlockGapX = 10;
m_BlockGapY = 8;
m_BlockWidth = (rectCore.Width() - m_BlockGapX*5)/4;
m_BlockHeight = (rectCore.Height() -m_BlockGapY*5)/4;
int iLeftX(0),iLeftY(0);
for (int i=0;i<4;i++) // 行
{
iLeftY = ((1+i)*m_BlockGapY + i*m_BlockHeight);
iLeftX = 0;
for (int j=0;j<4;j++) // 列
{
iLeftX = ((j+1)*m_BlockGapX + j*m_BlockWidth);
m_RectBlocks[i*4+j].m_RectBlock = CRect(CPoint(iLeftX,iLeftY),CSize(m_BlockWidth,m_BlockHeight));
m_RectBlocks[i*4+j].m_iBlockID = i*4+j;
m_RectBlocks[i*4+j].m_iGameNumShow = 0;
m_RectBlocks[i*4+j].m_iFlashCount = 0;
}
}
void CGame2048Dlg::IntilizeGame()
{
for (int i=0;i<16;i++)
{
m_RectBlocks[i].m_iGameNumShow = 0;
}
// 随机选两块数字重置为2
static int iIDArry[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
vector<int> vIds(16,0);
std::copy(iIDArry,iIDArry+16,vIds.begin());
vector<int>::iterator iter = vIds.begin();
int iBlockID[2] = {0,0};
iBlockID[0] = vIds[rand()%16];
iter += iBlockID[0];
vIds.erase(iter);
iBlockID[1] = vIds[rand()%15];
m_RectBlocks[iBlockID[0]].m_iGameNumShow = 2;
m_RectBlocks[iBlockID[1]].m_iGameNumShow = 2;
GetDlgItem(IDC_STATIC_ADD_SCORE)->SetWindowText("");
m_GameCtrler.SetSumScore(0);
GetDlgItem(IDC_STATIC_SUM_SCORE)->SetWindowText("当前总分:0");
}
上下左右移动的事件处理:
BOOL CGame2048Dlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if (pMsg->message == WM_KEYDOWN)
{
if(pMsg-> wParam == VK_LEFT|| pMsg-> wParam == 'A')
m_GameCtrler.MoveLeft();
else if(pMsg-> wParam == VK_RIGHT|| pMsg-> wParam == 'D')
m_GameCtrler.MoveRight();
else if(pMsg-> wParam == VK_UP || pMsg-> wParam == 'W')
m_GameCtrler.MoveUp();
else if(pMsg-> wParam == VK_DOWN|| pMsg-> wParam == 'S')
m_GameCtrler.MoveDown();
else if (pMsg->wParam == VK_ESCAPE)
TRUE;
else
return CBCGPDialog::PreTranslateMessage(pMsg);
CString strAddScore;
strAddScore.Format("+%d",m_GameCtrler.GetAddScore());
GetDlgItem(IDC_STATIC_ADD_SCORE)->SetWindowText(strAddScore);
CString strSumSocre;
strSumSocre.Format("当前总分:%d",m_GameCtrler.GetSumScore());
GetDlgItem(IDC_STATIC_SUM_SCORE)->SetWindowText(strSumSocre);
return TRUE;
}
return CBCGPDialog::PreTranslateMessage(pMsg);
}
控制器完整源码:
CGameCtrler::CGameCtrler(CGame2048Dlg * pMainWnd)
{
m_pMainWnd = pMainWnd;
m_iSumScore = 0;
m_iCurScore = 0;
srand(time(NULL));
}
CGameCtrler::~CGameCtrler(void)
{
}
void CGameCtrler::MoveUp()
{
RestoreStatus();
m_iCurScore = 0;
for (int j=0;j<4;j++)
ProcessOneColumToUp(j);
MakeNewBlockInEmptyErea();
m_pMainWnd->Invalidate(FALSE);
}
void CGameCtrler::MoveRight()
{
RestoreStatus();
m_iCurScore = 0;
for (int j=0;j<4;j++)
ProcessOneRowToRight(j);
MakeNewBlockInEmptyErea();
m_pMainWnd->Invalidate(FALSE);
}
void CGameCtrler::MoveLeft()
{
RestoreStatus();
m_iCurScore = 0;
for (int j=0;j<4;j++)
ProcessOneRowToLeft(j);
MakeNewBlockInEmptyErea();
m_pMainWnd->Invalidate(FALSE);
}
void CGameCtrler::MoveDown()
{
RestoreStatus();
m_iCurScore = 0;
for (int j=0;j<4;j++)
ProcessOneColumToDown(j);
MakeNewBlockInEmptyErea();
m_pMainWnd->Invalidate(FALSE);
}
void CGameCtrler::MakeNewBlockInEmptyErea()
{
vector<int> vEmptyID;
for (int i=0;i<16;i++)
{
if (m_pMainWnd->m_RectBlocks[i].m_iGameNumShow ==0)
vEmptyID.push_back(i);
}
if (vEmptyID.empty())
{
//if (IsGameOver()) // 不能产生空白
// MessageBox(m_pMainWnd->m_hWnd,"游戏结束!","提示",MB_OK|MB_ICONINFORMATION|MB_TOPMOST);
return;
}
if (!StatusChanged())
return;
int iIndex = rand()%vEmptyID.size();
int iNewID = vEmptyID[iIndex]; // 的到新数字块的id
int iNewNum = (rand()%8)==7 ? 4:2; // 新生成2或者4
m_pMainWnd->m_RectBlocks[iNewID].m_iGameNumShow = iNewNum;
m_pMainWnd->m_RectBlocks[iNewID].m_iFlashCount = FLASH_COUNT;
m_iFlashBlockID = iNewID;
m_pMainWnd->SetTimer(TIMER_FLASH_BLOCK,TIMER_SPACE_FLASH,NULL);
if (IsGameOver()) // 不能产生空白
MessageBox(m_pMainWnd->m_hWnd,"游戏结束!","提示",MB_OK|MB_ICONINFORMATION|MB_TOPMOST);
}
void CGameCtrler::ProcessOneColumToDown(int iColumId)
{
BlockNode node[4];
for (int i=0;i<4;i++)
{
int iId =(3-i)*4 + iColumId;
int iNum = m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow;
node[i].m_iID = iId;
node[i].m_iShowNum = iNum;
if (i<3)
node[i].m_pNextBlock = &node[i+1];
}
ProcessDataListNode(node);
for (int i=0;i<4;i++)
{
int iId =(3-i)*4 + iColumId;
m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow = node[i].m_iShowNum;
}
}
void CGameCtrler::ProcessOneColumToUp(int iColumId)
{
BlockNode node[4];
for (int i=0;i<4;i++)
{
int iId = i*4 + iColumId;
int iNum = m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow;
node[i].m_iID = iId;
node[i].m_iShowNum = iNum;
if (i<3)
node[i].m_pNextBlock = &node[i+1];
}
ProcessDataListNode(node);
for (int i=0;i<4;i++)
{
int iId = i*4 + iColumId;
m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow = node[i].m_iShowNum;
}
}
void CGameCtrler::ProcessOneRowToLeft(int iRowId)
{
BlockNode node[4];
for (int i=0;i<4;i++) // 列循环
{
int iId = iRowId*4 + i;
int iNum = m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow;
node[i].m_iID = iId;
node[i].m_iShowNum = iNum;
if (i<3)
node[i].m_pNextBlock = &node[i+1];
}
ProcessDataListNode(node);
for (int i=0;i<4;i++) // 列循环
{
int iId = iRowId*4 + i;
m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow = node[i].m_iShowNum;
}
}
void CGameCtrler::ProcessOneRowToRight(int iRowId)
{
BlockNode node[4];
for (int i=0;i<4;i++) // 列循环
{
int iId = iRowId*4 +(3-i);
int iNum = m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow;
node[i].m_iID = iId;
node[i].m_iShowNum = iNum;
if (i<3)
node[i].m_pNextBlock = &node[i+1];
}
ProcessDataListNode(node);
for (int i=0;i<4;i++) // 列循环
{
int iId = iRowId*4 +(3-i);
m_pMainWnd->m_RectBlocks[iId].m_iGameNumShow = node[i].m_iShowNum;
}
}
void CGameCtrler::ProcessDataListNode(BlockNode * pNodeHeader)
{
// 移动紧促
BlockNode *pNodeCur = pNodeHeader;
vector<int> vNums(4,0);
int iIndexNoneZero = 0;
while(pNodeCur)
{
if (pNodeCur->m_iShowNum!=0)
{
vNums[iIndexNoneZero] = pNodeCur->m_iShowNum;
iIndexNoneZero++;
}
pNodeCur = pNodeCur->m_pNextBlock;
}
pNodeCur = pNodeHeader;
int i = 0;
while(pNodeCur)
{
pNodeCur->m_iShowNum = vNums[i];
pNodeCur = pNodeCur->m_pNextBlock;
i++;
}
// 检查相加
BOOL bAddSuccess = FALSE;
for (int i=0;i<3;i++)
{
if (vNums[i] == vNums[i+1] && vNums[i]!=0)
{
vNums[i] += vNums[i+1];
vNums[i+1] = 0;
m_iCurScore += vNums[i];
m_iSumScore += vNums[i];
bAddSuccess = TRUE;
i++;
}
}
if (bAddSuccess) // 相邻的相加成功
{
vector<int> vNumTmp(4,0);
iIndexNoneZero = 0;
for (int i=0;i<4;i++)
{
if (vNums[i]!=0)
vNumTmp[iIndexNoneZero++] = vNums[i];
}
vNums.clear();
vNums = vNumTmp;
pNodeCur = pNodeHeader;
int i = 0;
while(pNodeCur)
{
pNodeCur->m_iShowNum = vNums[i];
pNodeCur = pNodeCur->m_pNextBlock;
i++;
}
}
}
BOOL CGameCtrler::IsGameOver()
{
// 检查每一行是否有可相加的或者有空白位置
for (int i=0;i<4;i++)
{
int iPreNum = m_pMainWnd->m_RectBlocks[i*4].m_iGameNumShow;
if (iPreNum==0)
return FALSE;
for (int j=1;j<4;j++)
{
int iCurNum = m_pMainWnd->m_RectBlocks[i*4 + j].m_iGameNumShow;
if (iCurNum==0 || iPreNum==iCurNum)
return FALSE;
iPreNum = iCurNum;
}
}
// 检查每一列是否有可相加的
for (int j=0;j<4;j++)
{
int iPreNum = m_pMainWnd->m_RectBlocks[j].m_iGameNumShow;
if (iPreNum==0)
return FALSE;
for (int i=1;i<4;i++)
{
int iCurNum = m_pMainWnd->m_RectBlocks[i*4 + j].m_iGameNumShow;
if (iCurNum==0 || iPreNum==iCurNum)
return FALSE;
iPreNum = iCurNum;
}
}
return TRUE;
}
void CGameCtrler::RestoreStatus()
{
for(int i=0;i<16;i++)
m_iRestoreStatus[i] = m_pMainWnd->m_RectBlocks[i].m_iGameNumShow;
}
BOOL CGameCtrler::StatusChanged()
{
for(int i=0;i<16;i++)
if(m_iRestoreStatus[i] != m_pMainWnd->m_RectBlocks[i].m_iGameNumShow)
return TRUE;
return FALSE;
}
void CGameCtrler::UpdateFlashTimeCount()
{
m_pMainWnd->m_RectBlocks[m_iFlashBlockID].m_iFlashCount --;
if (m_pMainWnd->m_RectBlocks[m_iFlashBlockID].m_iFlashCount==0)
{
m_pMainWnd->KillTimer(TIMER_FLASH_BLOCK);
}
}
主窗口MainGlg中需要定义游戏数据和控制器:
CGameNumBlock m_RectBlocks[16];
CGameCtrler m_GameCtrler;
视图更新及绘制:
void CGame2048Dlg::DrawBlocks(CDC & dc)
{
for (int i=0;i<16;i++)
{
DrawBlock(dc,i);
}
}
void CGame2048Dlg::DrawBlock(CDC & dc,const int & iBlockID)
{
CRect rectBlock = m_RectBlocks[iBlockID].m_RectBlock;
int iShowNum = m_RectBlocks[iBlockID].m_iGameNumShow;
if (iShowNum ==0)
{
CBrush brush(g_ColorNullBlock);
dc.FillRect(rectBlock,&brush);
return;
}
if (m_RectBlocks[iBlockID].m_iFlashCount > 0)
{
int iW = m_RectBlocks[iBlockID].m_iFlashCount*FLASH_INFALTE_PIXEL;
int iH = m_RectBlocks[iBlockID].m_iFlashCount*FLASH_INFALTE_PIXEL;
rectBlock.InflateRect(-iW,-iH,-iW,-iH);
}
int iTemp =iShowNum > 128 ? 128:iShowNum;
CBrush brush(g_mapColor[iTemp]);
dc.FillRect(rectBlock,&brush);
}
// paint 函数中调用DrawBlocks
CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
CRect rectCore;
m_wndCore.GetClientRect(rectCore);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc,rectCore.Width(),rectCore.Height());
MemDC.SelectObject(&bitmap);
CBrush brush(g_ColorBackGround);
MemDC.FillRect(rectCore,&brush);
DrawBlocks(MemDC);