文章目录
前言
扫雷是Windows内置经典游戏之一!它是一个简单的数字逻辑推理游戏!
一、游戏规则分析?
游戏规则很简单,用户通过鼠标点击分布的格子,点击的结果有三种:雷,数字,空白!如果是雷则本局失败,数字则表明此格子周围八个格子的雷数量,空白则自动打开附近的区域直到没有连续空白格子!此外游戏还提供一个右键标注功能,通过右击可以标注推测结果避免误点和帮助分析!
二、使用SOUI创建基本界面
1.创建一个类CMine代表一个格子
代码如下(示例):
#include <core/SWnd.h>
#include <vector>
#include <layout/SGridLayout.h>
#include <sstream>
/// <summary>
/// 标记格子状态
/// </summary>
enum class MINE_STATE {
normal,//常规
flag,//标记
open//打开
};
#define ICON_MAX_INDEX 4
/// <summary>
/// 代表一个雷区格子
/// </summary>
class CMine
{
MINE_STATE m_mineState = MINE_STATE::normal;
private:
SWindow* _pWnd = nullptr;
int _iiconIndex = 0;
size_t _uminecount = 0;
public:
CMine()
{
}
SWindow* getWnd()
{
return _pWnd;
}
void reset()
{
m_mineState = MINE_STATE::normal;
_iiconIndex = 0;
_pWnd->EnableWindow(TRUE);
}
void attackWnd(SWindow* pWnd)
{
SASSERT(pWnd);
SASSERT(!_pWnd);
_pWnd = pWnd;
}
~CMine()
{
}
//获取状态
MINE_STATE getState()const {
return m_mineState;
}
//是否点开
bool isOpen()const {
return m_mineState == MINE_STATE::open;
}
//是否可以左键点击
bool canLClick()const {
return m_mineState == MINE_STATE::normal;
}
//是否可以右键点击
bool canRClick()const {
return m_mineState != MINE_STATE::open;
}
//获取图标绘制状态
int getCurIndex()const {
return _iiconIndex;
}
//获取图标绘制状态
int getNextIndex() {
++_iiconIndex;
SASSERT(_iiconIndex >= 0);
if (_iiconIndex >= ICON_MAX_INDEX)
_iiconIndex = 0;
return _iiconIndex;
}
public:
#define MAX_COL 16
#define MAX_ROW 30
#define MIN_COL 9
#define MIN_ROW 9
/// <summary>
/// 创建一个雷区
/// </summary>
/// <param name="parent">雷区窗口模板</param>
/// <param name="strName">雷区窗口模板</param>
/// <param name="mines">雷区数据</param>
/// <param name="row">行</param>
/// <param name="col">列</param>
/// <returns></returns>
static std::vector<std::vector<CMine>>& createMine(
SWindow* parent,
const SStringT& strName,
std::vector<std::vector<CMine>>& mines,
const size_t col,
const size_t row)
{
SASSERT(parent);
if (!parent)
return mines;
{
SGridLayout* param = sobj_cast<SGridLayout>(parent->GetLayout());
SASSERT(param);
std::wstringstream os;
os << col;
param->SetAttribute(L"columnCount", os.str().c_str());
}
for (auto& ite1 : mines)
{
for (auto& ite2 : ite1)
{
SWindow* _pWnd = ite2.getWnd();
if (_pWnd)
{
SWindow* _Parent = _pWnd->GetParent();
if (_Parent)
{
_Parent->DestroyChild(_pWnd);
}
}
}
}
mines.clear();
mines.resize(row);
for (int r = 0; r < row; r++)
{
mines[r].resize(col);
}
SStringW strXml = GETTEMPLATEPOOLMR->GetTemplateString(strName);
SASSERT(!strXml.IsEmpty());
if (!strXml.IsEmpty())
{
pugi::xml_document xmlDoc;
if (xmlDoc.load_buffer_inplace(strXml.GetBuffer(strXml.GetLength()), strXml.GetLength() * sizeof(WCHAR), 116, pugi::encoding_utf16))
{
pugi::xml_node xmlTemp = xmlDoc.first_child();
SASSERT(xmlTemp);
for (int r = 0; r < row; r++)
for (int c = 0; c < col; c++)
{
SWindow* pChild = SApplication::getSingleton().CreateWindowByName(xmlTemp.name());
if (pChild)
{
parent->InsertChild(pChild);
pChild->InitFromXml(xmlTemp);
mines[r][c].attackWnd(pChild);
}
}
}
}
return mines;
}
};
这里使用createMine创建一个二维数据,每次创建都会销毁前面的窗口,暂不考虑优化问题。
2.对应XML
template.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<template>
<mine>
<window skin="mine_bkskin" size="26,26" columnWeight="1" rowWeight="1">
<img name="flag" pos="|,|" show="0" offset="-0.5,-0.5" size="-1,-1"/>
<text name="minecount" pos="|,|" show="0" offset="-0.5,-0.5" size="-1,-1" text="1"/>
</window>
</mine>
</template>
uires.idx的values节点下添加:
<values>
<file name="string" path="values\string.xml"/>
<file name="color" path="values\color.xml"/>
<file name="skin" path="values\skin.xml"/>
<file name="template" path="values\template.xml"/>
</values>
init.xml 在节点添加template节点下添加:
<template src="values:template"/>
dlgmain.xml修改如下,添加了一个combobox用于选择难度。
<SOUI name="mainWindow" title="@string/title" bigIcon="ICON_LOGO:32" smallIcon="ICON_LOGO:16" margin="5,5,5,5" resizable="0" wndType="appMain"
appWnd="1"
translucent="1"
>
<root skin="_skin.sys.wnd.bkgnd" cache="1" size="-1,-1" layout="vbox">
<caption size="-2,30" font="adding:0">
<icon pos="10,8" src="ICON_LOGO:16"/>
<text pos="29,9">@string/title</text>
<imgbtn name="btn_close" skin="_skin.sys.btn.close" pos="-45,0" tip="close" animate="1"/>
<imgbtn name="btn_min" skin="_skin.sys.btn.minimize" pos="-83,0" animate="1" />
</caption>
<combobox name="cbx_lv" size="200,30" dropDown="1" dropHeight="300" dotted="0" animateTime="200" curSel="0">
<liststyle itemHeight="30" colorText="#000000" colorSelText="#FFFFFF" colorItemBkgnd="#FFFFFF" colorItemSelBkgnd="#000088" />
<editstyle inset="5,0,5,0" margin="0" colorText="#000000" align="left" colorBkgnd="#FFFFFF" />
<items>
<item text="初级" icon="1" data="0" />
<item text="中级" icon="2" data="1" />
<item text="高级" icon="3" data="2" />
</items>
</combobox>
<window name="parent_mine" size="-1,-1" padding="6,6,6,6" layout="gridLayout" columnCount="4" xGravity="fill" yGravity="fill"/>
</root>
</SOUI>
3.调用createMine动态创建格子
BOOL CMainDlg::OnInitDialog(HWND hWnd, LPARAM lParam)
{
CMine::createMine(FindChildByName(L"parent_mine"), L"mine", m_mines, 9, 9);
bInit = true;
return 0;
}
其中两个变量定义为
bool bInit = false;
std::vector<std::vector<CMine>> m_mines;
4.响应EventCBSelChange动态生成界面
添加事件映射
EVENT_NAME_HANDLER(L"cbx_lv", EventCBSelChange::EventID, OnCbxLvChange)
OnCbxLvChange函数实现如下:
void CMainDlg::OnCbxLvChange(EventArgs* e)
{
if (!bInit)
return;
EventCBSelChange* e2 = sobj_cast<EventCBSelChange>(e);
SComboBox* pCbx = sobj_cast<SComboBox>(e2->sender);
if (e2->nCurSel != -1)
{
LPARAM lv = pCbx->GetItemData(e2->nCurSel);
switch (lv)
{
case 0:
CMine::createMine(FindChildByName(L"parent_mine"), L"mine", m_mines, 9, 9);
break;
case 1:
CMine::createMine(FindChildByName(L"parent_mine"), L"mine", m_mines, 20, 12);
break;
case 2:
CMine::createMine(FindChildByName(L"parent_mine"), L"mine", m_mines, 30, 16);
break;
}
}
}
最后运行效果如下:
总结
SOUI是一个强大的UI库,可以方便实现静态XML或动态创建工作。