Duilib之挑战2048
网上很流行的一款很火爆的手机游戏,并由此衍生出好多2048版的游戏,具体可以百度就知晓了,自己学习duilib后觉得可以实现,就动手写了一个,算法估计很挫,不过界面是百分百像哦,一天就搞定了。
界面展示:
XML界面布局:
<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<Window size="359,580">
<Font name="微软雅黑" size="20" bold="true" italic="false" />
<Font name="微软雅黑" size="53" bold="true" italic="false" />
<Font name="新宋体" size="20" bold="false" italic="false" />
<Font name="微软雅黑" size="25" bold="true" italic="false" />
<VerticalLayout width="359" height="580" bkcolor="#FFFAF8EF">
<HorizontalLayout width="363" height="23" />
<HorizontalLayout width="359" height="177">
<VerticalLayout width="247" height="71">
<Label text="2048" float="true" pos="19,0,0,0" width="143" height="70" textcolor="#00776E65" disabledtextcolor="#FFA7A6AA" font="1" />
</VerticalLayout>
<VerticalLayout name="vertical" width="67" height="67" bkcolor="#FFBBADA0">
<Label text="得分" float="true" pos="13,4,0,0" width="53" height="25" textcolor="#00FFFBF0" disabledtextcolor="#FFA7A6AA" font="2" />
<Text name="scores" text="0" float="true" pos="2,29,0,0" width="63" height="33" textpadding="2,0,2,0" textcolor="#00FFFBF0" disabledtextcolor="#FFA7A6AA" font="3" align="centerwrap" />
</VerticalLayout>
<Label text="玩法:" float="true" pos="14,74,0,0" width="57" height="27" textcolor="#00776E65" disabledtextcolor="#FFA7A6AA" font="0" />
<Label text="1.使用方向键移动数字" float="true" pos="14,106,0,0" width="215" height="22" textcolor="#00656E77" disabledtextcolor="#FFA7A6AA" font="2" />
<Label text="2.相邻数字相同则并成1个数字" float="true" pos="13,136,0,0" width="346" height="24" textcolor="#00656E77" disabledtextcolor="#FFA7A6AA" font="2" />
</HorizontalLayout>
<HorizontalLayout width="372" height="385">
<VerticalLayout width="9" height="26" />
<VerticalLayout width="340" height="340" bkcolor="#FFBBADA0">
<Button name="bt1" float="true" pos="12,12,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt4" float="true" pos="258,12,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt3" float="true" pos="176,12,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt2" float="true" pos="94,12,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt5" float="true" pos="12,94,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt6" float="true" pos="94,94,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt7" float="true" pos="176,94,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt8" float="true" pos="258,94,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt9" float="true" pos="12,176,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt10" float="true" pos="94,176,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt11" float="true" pos="176,176,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt12" float="true" pos="258,176,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt13" float="true" pos="12,258,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt14" float="true" pos="94,258,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt15" float="true" pos="176,258,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
<Button name="bt16" float="true" pos="258,258,0,0" width="70" height="70" bkcolor="#FFCCC0B3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="3" align="center" />
</VerticalLayout>
</HorizontalLayout>
</VerticalLayout>
</Window></span><span style="font-size:24px;">
</span>
此时的界面全是平面的,还没有圆角,要实现圆角设计,需要在加载的程序里加入
SIZE si;
si.cx = si.cy = 4;
bt->SetBorderRound(si);//bt为一个空间的指针
对于Duilib控件的获取可以用
CTextUI* scores = static_cast<CTextUI*>(m_PaintManager.FindControl(_T("scores")));
如上即是获取显示分数的TEXT控件的方式。
此次XML没有使用任何图片,完全是原始控件绘制,比较简单,所以如果没有代码里的圆角去美化的话,界面就显得很抽象了。
键盘响应:
LRESULT CGameWnd::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if(wParam == VK_UP)
{
pMove->_up();
pMove->Print();
}
else if(wParam == VK_RIGHT)
{
pMove->_right();
pMove->Print();
}
else if(wParam == VK_DOWN)
{
pMove->_down();
pMove->Print();
}
else if(wParam == VK_LEFT)
{
pMove->_left();
pMove->Print();
}
else if(wParam == VK_SPACE)
{
pMove->Init();
pMove->Print();
}
return TRUE;
}
Duilib的 WindowImplBase类有键盘响应函数,直接重写即可。至于键盘的虚拟码,可以去看下面这个:
移动控制源码:
#include "Move.h"
#define random_2_4 (rand()%5==4 ? 4:2)
#define random_x(x) (rand()%x)
CMove::CMove(void)
{
data = CData_2048::GetInstance();
}
CMove::~CMove(void)
{
}
void CMove::Init()
{
data->_count = 0;
data->score = 0;
memset(data->_matrix,0,sizeof(data->_matrix));
srand((int)time(0));
for (int i = 0; i < 2; i++)
random_data_one();
}
void CMove::AddButton(CButtonUI* bt)
{
data->buttons.push_back(bt);
}
bool CMove::b_up()
{
for (int k = 0; k < 4; k++)
{
bool flag = false;
for (int i = 0; i < 4 - 1; i++)
{
if (data->_matrix[i][k] == 0)
flag = true;
else
{
int j = i + 1;
while (j < 4)
{
if (data->_matrix[j][k])
{
if (data->_matrix[i][k] == data->_matrix[j][k])
return true;
else
break;
}
else{
++j;
}
}
}
}
if (flag)
{
int i = 0, j = 4 - 1;
while (i < 4)
{
if (data->_matrix[i][k])
++i;
else
break;
}
while (j >= 0)
{
if (data->_matrix[j][k] == 0)
--j;
else
break;
}
if (i < j)
return true;
}
}
return false;
}
bool CMove::b_left()
{
for (int k = 0; k < 4; k++)
{
bool flag = false;
for (int i = 0; i < 4 - 1; i++)
{
if (data->_matrix[k][i] == 0)
flag = true;
else
{
int j = i + 1;
while (j < 4)
{
if (data->_matrix[k][j])
{
if (data->_matrix[k][i] == data->_matrix[k][j])
return true;
else
break;
}
else{
++j;
}
}
}
}
if (flag)
{
int i = 0, j = 4 - 1;
while (i < 4)
{
if (data->_matrix[k][i])
++i;
else
break;
}
while (j >= 0)
{
if (data->_matrix[k][j] == 0)
--j;
else
break;
}
if (i < j)
return true;
}
}
return false;
}
bool CMove::b_down()
{
for (int k = 0; k < 4; k++)
{
bool flag = false;
for (int i = 4 - 1; i > 0; i--)
{
if (data->_matrix[i][k] == 0)
flag = true;
else
{
int j = i - 1;
while (j >= 0)
{
if (data->_matrix[j][k])
{
if (data->_matrix[i][k] == data->_matrix[j][k])
return true;
else
break;
}
else{
--j;
}
}
}
}
if (flag)
{
int i = 0, j = 4 - 1;
while (i < 4)
{
if (data->_matrix[i][k] == 0)
++i;
else
break;
}
while (j >= 0)
{
if (data->_matrix[j][k])
--j;
else
break;
}
if (i < j)
return true;
}
}
return false;
}
bool CMove::b_right()
{
for (int k = 0; k < 4; k++)
{
bool flag = false;
for (int i = 4 - 1; i > 0; i--)
{
if (data->_matrix[k][i] == 0)
flag = true;
else
{
int j = i - 1;
while (j >= 0)
{
if (data->_matrix[k][j])
{
if (data->_matrix[k][i] == data->_matrix[k][j])
return true;
else
break;
}
else{
--j;
}
}
}
}
if (flag)
{
int i = 0, j = 4 - 1;
while (i < 4)
{
if (data->_matrix[k][i] == 0)
++i;
else
break;
}
while (j >= 0)
{
if (data->_matrix[k][j])
--j;
else
break;
}
if (i < j)
return true;
}
}
return false;
}
void CMove::_up()
{
if (b_up())
{
for (int i = 0; i < 4; i++)
{
memset(data->current, 0, sizeof(int)*4);
int ii = 0;
for (int j = 0; j < 4; j++)
{
if (data->_matrix[j][i])
data->current[ii++] = data->_matrix[j][i];
}
for (int k = 0; k < ii - 1; k++)
{
if (data->current[k] == data->current[k + 1])
{
data->current[k] *= 2;
data->score += data->current[k];
data->current[k + 1] = 0;
++k;
--data->_count;
}
}
ii = 0;
for (int j = 0; j < 4; j++)
{
if (data->current[j])
data->_matrix[ii++][i] = data->current[j];
}
for (; ii < 4; ii++)
data->_matrix[ii][i] = 0;
}
random_data_one();
}
}
void CMove::_down()
{
if (b_down())
{
for (int i = 0; i < 4; i++)
{
memset(data->current, 0, sizeof(int)*4);
int ii = 0;
for (int j = 4 - 1; j >= 0; j--)
{
if (data->_matrix[j][i])
data->current[ii++] = data->_matrix[j][i];
}
for (int k = 0; k < ii - 1; k++)
{
if (data->current[k] == data->current[k + 1])
{
data->current[k] *= 2;
data->score += data->current[k];
data->current[k + 1] = 0;
++k;
--data->_count;
}
}
ii = 4 - 1;
for (int j = 0; j < 4; j++)
{
if (data->current[j])
data->_matrix[ii--][i] = data->current[j];
}
for (; ii >= 0; ii--)
data->_matrix[ii][i] = 0;
}
random_data_one();
}
}
void CMove::_left()
{
if (b_left())
{
for (int i = 0; i < 4; i++)
{
memset(data->current, 0, sizeof(int)*4);
int ii = 0;
for (int j = 0; j < 4; j++)
{
if (data->_matrix[i][j])
data->current[ii++] = data->_matrix[i][j];
}
for (int k = 0; k < ii - 1; k++)
{
if (data->current[k] == data->current[k + 1])
{
data->current[k] *= 2;
data->score += data->current[k];
data->current[k + 1] = 0;
++k;
--data->_count;
}
}
ii = 0;
for (int j = 0; j < 4; j++)
{
if (data->current[j])
data->_matrix[i][ii++] = data->current[j];
}
for (; ii < 4; ii++)
data->_matrix[i][ii] = 0;
}
random_data_one();
}
}
void CMove::_right()
{
if (b_right())
{
for (int i = 0; i < 4; i++)
{
memset(data->current, 0, sizeof(int)*4);
int ii = 0;
for (int j = 4 - 1; j >= 0; j--)
{
if (data->_matrix[i][j])
data->current[ii++] =data->_matrix[i][j];
}
for (int k = 0; k < ii - 1; k++)
{
if (data->current[k] == data->current[k + 1])
{
data->current[k] *= 2;
data->score += data->current[k];
data->current[k + 1] = 0;
++k;
--data->_count;
}
}
ii = 4 - 1;
for (int j = 0; j < 4; j++)
{
if (data->current[j])
data->_matrix[i][ii--] =data-> current[j];
}
for (; ii >= 0; ii--)
data->_matrix[i][ii] = 0;
}
random_data_one();
}
}
bool CMove::random_data_one()
{
if (data->_count == 16)
return false;
int left = 16 - data->_count;
int tmp = random_x(left);
int num = random_2_4;
int k = 0;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (data->_matrix[i][j] == 0)
{
if (k++ == tmp)
{
data->_matrix[i][j] = num;
break;
}
}
}
}
++data->_count;
return true;
}
上述代码中data是个单例模式的对象,如下定义:
class CData_2048
{
public:
int _matrix[4][4];
int current[4];
int _count;
int score;
CTextUI* scores;
vector<CButtonUI*>buttons;
static CData_2048* GetInstance();
static void Release();
private:
CData_2048();
static CData_2048* t_;
};
关于移动类的方法没有做优化,有点挫了。
图形显示:
void CPrintArray::Print()
{
for(int k=0;k<16;k++)
{
data->buttons[k]->SetText(_T(""));
data->buttons[k]->SetAttribute(_T("bkcolor"),_T("#FFCCC0B3"));
}
for (int i = 0; i<4;i++)
{
for (int j=0;j<4;j++)
{
if(data->_matrix[i][j]!=0)
showbutton(data->buttons[i*4+j],data->_matrix[i][j]);
}
}
TCHAR str[5];
sprintf(str,"%d",data->score);
data->scores->SetText(str);
}
void CPrintArray::showbutton(CButtonUI* bt,int num)
{
TCHAR str[10];
sprintf(str,"%d",num);
if(num==2)
{
bt->SetAttribute(_T("bkcolor"),_T("#ffeee4da"));
bt->SetAttribute(_T("textcolor"),_T("776e65"));
}
else if(num==4)
{
bt->SetAttribute(_T("bkcolor"),_T("#ffede0c8"));
bt->SetAttribute(_T("textcolor"),_T("ff776e65"));
}
else if(num==8)
{
bt->SetAttribute(_T("bkcolor"),_T("#fff2b179"));
bt->SetAttribute(_T("textcolor"),_T("fff9f6f2"));
}
else if(num==16)
{
bt->SetAttribute(_T("bkcolor"),_T("#fff59563"));
bt->SetAttribute(_T("textcolor"),_T("fff9f6f2"));
}
else if(num==32)
{
bt->SetAttribute(_T("bkcolor"),_T("#fff67c5f"));
bt->SetAttribute(_T("textcolor"),_T("fff9f6f2"));
}
else if(num==64)
{
bt->SetAttribute(_T("bkcolor"),_T("#fff65e3b"));
bt->SetAttribute(_T("textcolor"),_T("fff9f6f2"));
}
else if(num>=128)
{
bt->SetAttribute(_T("bkcolor"),_T("#ffedcf72"));
bt->SetAttribute(_T("textcolor"),_T("fff9f6f2"));
}
<span style="white-space:pre"> </span>//......
bt->SetText(str);
}
这个显示方式比较笨拙吧,其实套用一个装饰模式可以很好的实现,如下:
class _Button
{
public:
CButtonUI* m_ButtonUI;
int m_num;
_Button(int num,CButtonUI* button):m_num(num),m_ButtonUI(button){}
virtual ~_Button() {}
virtual void UpButtonData() {}
};
class Num_Button:public _Button
{
public:
Num_Button(int num,CButtonUI* button):_Button(num,button){}
void UpButtonData()
{
switch(m_num)
{
case 2:
case 4:m_ButtonUI->SetAttribute(_T("textcolor"),_T("776e65"));break;
default:
m_ButtonUI->SetAttribute(_T("textcolor"),_T("fff9f6f2"));
}
TCHAR str[10];
sprintf(str,"%d",m_num);
m_ButtonUI->SetText(str);
}
};
class SetButton:public _Button
{
protected:
_Button* m_BkButton;
public:
SetButton(_Button* bkbutton):m_BkButton(bkbutton),_Button(bkbutton->m_num,bkbutton->m_ButtonUI){
}
virtual void UpButtonData()
{
m_BkButton->UpButtonData();
}
};
class Color_Button:public SetButton
{
public:
Color_Button(_Button* bkbutton):SetButton(bkbutton){}
void UpButtonData()
{
SetButton::UpButtonData();
AddSetButton();
}
private:
void AddSetButton()
{
switch (m_num)
{
case 0: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#FFCCC0B3"));break;
case 2: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#ffeee4da"));break;
case 4: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#ffede0c8"));break;
case 8: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#fff2b179"));break;
case 16: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#fff59563"));break;
case 32: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#fff67c5f"));break;
case 64: m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#fff65e3b"));break;
default:
m_ButtonUI->SetAttribute(_T("bkcolor"),_T("#ffedcf72"));
}
}
};
class Form_Button:public SetButton
{
public:
Form_Button(_Button* bkbutton):SetButton(bkbutton){}
void UpButtonData()
{
SetButton::UpButtonData();
AddSetButton();
}
private:
void AddSetButton()
{
SIZE si;
si.cx = si.cy =4;
m_ButtonUI->SetBorderRound(si);
}
};
所以那个CPrintArray::showbutton函数可以这么写了:
_Button* button1 = new Num_Button(num,bt);
_Button* button2= new Form_Button(button1);
_Button* button3 = new Color_Button(button2);
button3->UpButtonData();
资源下载
实现比较简单,源码几乎全贴了,自己尝试一次,肯定可以搞定,以后再放资源!