游戏先容
下围棋的程序,实现了界面切换,选择路数,和围棋规则,也实现了点目功能,不过只有当所有棋子都被提走才能点目,不然不准确
操作方法鼠标操作
游戏截图
编译环境
VisualStudio2019,EasyX_20211109
文件描述用广度寻路探求周围所有相同棋子,直到四处碰钉子了,得到包围住自己的所有点,看看这些点是空地的数量,空地的数量便是气的数量,气为 0 这些子全部提掉,设为空地。每下一步棋记录下这步棋的位置,悔棋时把这些点提掉。打劫时在存悔棋的点的地方找到劫争的地方,只吃了一颗子并且落在劫争的地方就不能下。
点目便是找到一块空地,看看围住它的是不是都是同一个颜色的子,是的话这块空地有多大这个颜色的子的目数就加多大。
如果围住空地的是不同颜色的子,那么这块空地多大,这两个子的气就加这块空地的目数的一半。
其他功能便是设计模式的问题了,我借鉴了 cocos2d 的设计模式,在 director 里运行全体程序,在 scene 里写这个层运行的功能,director 的 runwithscene 写切换场景。
详解:
1、公共工具MyTool
这个文件里包含了广度寻路和围棋舆图的类,个中围棋舆图通过广度寻路实现了吃子,提子,点目的功能。还有一些之前做七巧板的项目时保存下来的类,暂且用不到,但是大概能方便未来开拓,以是放到一起。
DealArray处理数组的头文件,目前有三个函数,浸染分别是:将 vector 首尾颠倒、判断一个元素是否在 vector 里面,判断两个 vector 是否相等(每个元素都相等便是两个 vector 相等),函数实现为
// 这个实现 vector 首尾颠倒template<typename Vector_Reverse>// 这个是函数模板void Reserve_Vector(vector<Vector_Reverse>& arr){for (int i = 0;i < (arr.size() >> 1);i++){Vector_Reverse temp = arr[i];arr[i] = arr[arr.size() - i-1];arr[arr.size() - i - 1] = temp;}}// 这个实现判断一个元素是否在 vector 里面template<typename VectorInclude>bool ifNotInVector(vector<VectorInclude>arr, VectorInclude num){for (VectorInclude i : arr){if (i == num)return false;}return true;}// 这个实现判断两个 vector 是否相等template<typename VectorEqual>bool ifTwoVectorEqual(vector<VectorEqual> arr, vector<VectorEqual>ano){if (arr.size() != ano.size())return false;for (int i = 0;i < arr.size();i++){if (arr[i] != ano[i])return false;}return true;}
MapPoint舆图点的类,由 indexX 存放列数,indexY 存放行数,有 PathDir 列举类型列举四个方向,能通过 MapPoint getDirPoint(PathDir turn) 这个函数得到四个方向的点,这个函数长这样
MapPoint getDirPoint(PathDir turn){switch (turn){case path_up:return MapPoint(this->indexX,this->indexY-1);break;case path_down:return MapPoint(this->indexX, this->indexY + 1);break;case path_left:return MapPoint(this->indexX-1, this->indexY);break;case path_right:return MapPoint(this->indexX+1, this->indexY);break;default:break;}}
同时这个类也用于保存 BoundingBox 类的坐标,由于 easyx 里的每个点都是整型,以是保存的坐标也是整型。
PathNode广度寻路的节点类,也便是树的数据构造,一个父节点,多个子节点由
MapPoint pos;PathNode parent;vector<PathNode> child;
这三个数据组成,pos 是这个节点所在的位置,parent 是这个节点的父节点,child 是这个节点的子节点们
为方便清理 PathNode 节点,这个类里还供应了静态函数
static void clearNode(PathNode p_head){if (p_head->child.empty()){if (p_head->parent != nullptr){PathNode temp = p_head->parent;for (int i = 0;i < temp->child.size();i++){if (temp->child[i] == p_head){temp->child.erase(temp->child.begin() + i);break;}}}delete p_head;}else{// vector 的遍历不足好,直接这么打消子节点while (!p_head->child.empty()){clearNode(p_head->child[0]);}}}
要清理掉一全体 PathNode phead 树只需
PathNode::clearNode(phead);
BFS广度寻路的类,包含
function<bool(MapPoint)> ifCanThrough;// 判断是否可以通畅的函数指针unsigned int size_Width, size_Height;// 舆图尺寸,width 为宽度,height 为高度bool AuxiliaryMaps;// 赞助舆图,判断某个点是否走过
四个数据,用
void BFS::setMap(unsigned int size_Width, unsigned int size_Height, function<bool(MapPoint)> CallBack){this->size_Height = size_Height;this->size_Width = size_Width;ifCanThrough = CallBack;}
设置舆图尺寸和判断是否可以通畅的函数指针,setMap 用法如下
int map[3][3] ={0,1,0,0,1,0,0,0,0};BFS bfs;bfs.setMap(3, 3, [&](MapPoint num){if (map[num.indexY][num.indexX] == 0)return true;return false;});// 这是用 lambda 表达式写的// 也可以用函数指针如bool ifCanThrough(MapPoint num){if (map[num.indexY][num.indexX] == 0)return true;return false;}bfs.setMap(3, 3, ifCanThrough);// 或者bool (p)(MapPoint)=ifCanThrough;bfs.setMap(3,3,p);
初始化 AuxiliaryMap 用
void initAuxiliaryMaps(){AuxiliaryMaps = new bool[size_Height size_Width];memset(AuxiliaryMaps, false, sizeof(bool)size_Heightsize_Width);}
清理 AuxiliaryMap 用
void clearAuxiliaryMaps(){if (AuxiliaryMaps != nullptr)delete AuxiliaryMaps;AuxiliaryMaps = nullptr;}
AuxiliaryMap(赞助舆图)的浸染是每次遍历一个广度寻路的节点就把该节点的位置的 bool 值设为 true 表示这个点探求过了,避免重复探求同一个位置,寻路完就把赞助舆图清理掉。
由于不知道 ifCanThrough 是否判断点是否在舆图内,以是要多写一个判断点是否在舆图内的函数,避免访问 AuxiliaryMap 时数组越界,这个函数为
bool ifInMap(MapPoint num){if (num.indexX >= 0 && num.indexX < size_Width && num.indexY >= 0 && num.indexY<size_Height)return true;return false;}
现在赞助舆图有了,广度寻路的节点有了,是否可以通畅的判断也有了,可以根据广度寻路的算法用出发点和终点的值找到可以通畅的路径了,探求路径的函数为
vector<MapPoint> getThroughPath(MapPoint star, MapPoint end);
函数过长,就不贴出来了,广度寻路的步骤是
1、将出发点放进 PathNode phead
2、将 phead->pos 在 AuxiliaryMap 对应的点的 bool 设为 true,即 AuxiliaryMap[phead->pos.indexYsize_Width+phead->pos.indexX]=true;
3、判断 phead->pos 高下旁边四个方向的点是否找寻过,是否可以通畅,未找寻过可以通畅则把这个点放入 phead 的子节点,phead->addchild(new PathNode(MapPoint(phead->pos.getDirPoint(path_up / 或者 path_down path_left path_rght /)))); 并且放进 vector<PathNode>child; 里
4、遍历 child,看看有没有点到达终点,没有进入步骤 5,有进入步骤 8
5、令 vector<PathNode>parent=child;child.clear(); 遍历 parnet 里的每个 PathNode,对每个 PathNode 单独实行步骤 3
6、如果 child 为空,进入步骤 7,如果 child 不为空,进入步骤 4
7、返回空的 vector<MapPoint>result;
8、把找到的 PathNode 节点保存下来,一直找 pathNode 的父节点,把每个父节点的 pos 值 push_back 进 vector<MapPoint> result; 里面返回 result.
详细函数实现看 BFS 里的 vector<MapPoint> getThroughPath(MapPoint star, MapPoint end);
实现这个功能实在对围棋这个项目没有帮助,但是都封装出了这个类,不实现一下这个功能总归有点缺憾,围棋要判断所有能走的点,只须要在广度寻路的八个步骤中去掉对是否到达终点的判断就行了,得到包围这块区域的点只须要在探求所有能走的点时碰着 ifCanThrough 为 false 的点时把该点所在 AuxiliaryMap 的 bool 值设为 true 并存进 vector<MapPoint> result; 里就行,终极返回的便是碰着的所有不能走的点,在 BFS 的函数实现为
vector<MapPoint> getAllCanThrough(MapPoint star);vector<MapPoint> getEnclosedPoint(MapPoint star);
BFS 中还实现了单步寻路的功能
vector<MapPoint> SingleSearch(PathNode begin);
这个的用法是
int map[3][3] ={0,1,0,0,1,0,0,0,0};bool ifCanThrough(MapPoint num){if (map[num.indexY][num.indexX] == 0)return true;return false;}BFS bfs;PathNode begin = new PathNode(MapPoint(0, 0));bfs.setMap(3, 3, ifCanThrough);bfs.initAuxiliaryMaps();vector<MapPoint> reslt=bfs.SingleSearch(begin);while(!reslt.empty()){// .....这里写每步寻路后的操作reslt = bfs.SingleSearch(begin);}bfs.clearAuxiliaryMaps();PathNode::clearNode(begin);
MapNode舆图节点,我试图用图的数据构造来写围棋的舆图,这样舆图上的每个点都是指针,加上 Map 是个单例模式,得到的每个点,点每个点的处理都会反应到真实的舆图上,不用重复传参。
这个头文件有 Piece 列举类型
enum Piece {Black,White,Space};
表示围棋的黑子,白子,空地三种类型
这个类有
// 高下旁边四个节点MapNode Node_Left;MapNode Node_Right;MapNode Node_Up;MapNode Node_Down;// 这个点的棋子Piece Node_Piece;// 这个点原来的棋子Piece original_Piece;// 这个点的坐标int indexX, indexY;// 这个点棋子被改变的次数unsigned int changeTimes;
9 个数据
要清理全体舆图调用
// 在图中的任何一个点都可以用于打消全体图void MapNode::DeleteChild(){// 从父节点到子节点猖獗扩散来清理子节点vector<MapNode> parent;vector<MapNode> child;if (this->Node_Down){child.push_back(this->Node_Down);this->Node_Down->Node_Up = nullptr;this->Node_Down = nullptr;}if (this->Node_Up){child.push_back(this->Node_Up);this->Node_Up->Node_Down = nullptr;this->Node_Up = nullptr;}if (this->Node_Left){child.push_back(this->Node_Left);this->Node_Left->Node_Right = nullptr;this->Node_Left = nullptr;}if (this->Node_Right){child.push_back(this->Node_Right);this->Node_Right->Node_Left = nullptr;this->Node_Right = nullptr;}while (!child.empty()){parent = child;child.clear();for (MapNode parent_Node : parent){if (parent_Node->Node_Down){if(ifNotInVector(child, parent_Node->Node_Down))child.push_back(parent_Node->Node_Down);parent_Node->Node_Down->Node_Up = nullptr;parent_Node->Node_Down = nullptr;}if (parent_Node->Node_Up){if(ifNotInVector(child, parent_Node->Node_Up))child.push_back(parent_Node->Node_Up);parent_Node->Node_Up->Node_Down = nullptr;parent_Node->Node_Up = nullptr;}if (parent_Node->Node_Left){if(ifNotInVector(child, parent_Node->Node_Left))child.push_back(parent_Node->Node_Left);parent_Node->Node_Left->Node_Right = nullptr;parent_Node->Node_Left = nullptr;}if (parent_Node->Node_Right){if(ifNotInVector(child, parent_Node->Node_Right))child.push_back(parent_Node->Node_Right);parent_Node->Node_Right->Node_Left = nullptr;parent_Node->Node_Right = nullptr;}delete parent_Node;}}}
这个函数。这个函数不会把自己清理掉,只会把自己周围的所有节点设为 nullptr,以是可以放心在析构函数里用它。
悔棋时把这个点设为某个棋子用
void MapNode::UndoSetPiece(Piece num){changeTimes--;if (changeTimes == 1)original_Piece = Space;else if (num == original_Piece){switch (num){case White:original_Piece = Black;break;case Black:original_Piece = White;break;default:break;}}Node_Piece =num;}
悔棋时这个点如果棋子改变次数大于 2,设为与原来相同的子时原来的子就要设为的这个子的相反面,这点有一点小逻辑在里面,当然如果改变次数为 2,要设为任何子,原来的子都会是空地。有闲心的可以自己推一下。
不悔了和落子时把这个点设为某个棋子时用
void MapNode::setPiece(Piece num){if(num==Space&&Node_Piece!=Space)original_Piece = Node_Piece;Node_Piece = num;changeTimes++;}
StepPoint每一步的点,用于存每一步落子的地方和每一步悔棋的地方,还有每一步劫争的 MapNode,用于实现悔棋和不悔了的功能,共有
int indexX, indexY;// 下的位置bool ifUpBeEated;// 上边有没有被吃bool ifDownBeEated;// 下边有没有被吃bool ifLeftBeEated;// 左边有没有被吃bool ifRightBeEated;// 右边有没有被吃Piece Step_Piece;// 这一步是什么棋子MapNode kozai;// 这一步劫争的地方
八个数据,如果上边有被吃,就把上边的所有空地找到,设为与这一步棋子相反的棋子,下,左,右亦然,四个方向判断完后再把这颗子提掉,这便是悔棋的逻辑,不用存下被吃掉的所有点,用四个 bool 值就省去了很多内存。
Map,舆图的所有数据及数据的处理都在 Map 这个类里。
这是个单例模式的类,单例模式便是任何人不能 new 出一个工具,只有这个类自己才能给出自己的样子容貌,详细写法为
class A{public:~A() {}// 析构函数一定假如公有的static A getInstance()// getInstance 一定假如静态的{if (p_Ins == nullptr)p_Ins = new A;return p_Ins;}private:A() {};// 布局函数一定假如私有的static A p_Ins;// 这个不能在布局函数里初始化};A A::p_Ins = nullptr;// 这个不能漏
详细用法你得多多实践才能理解透彻,例如写一个回合制对战游戏,一个英雄一个怪物,一回合轮一个人发动攻击或者防御什么的,调度每个人的攻击力,防御力,暴击率,看看末了是谁赢了这个小项目,你用单例模式试着做一下差不多就能理解了。之后要说的仿照 cocos 就用到了一个单例模式,也是至关主要的单例模式。
Map 共有
MapNode Entity;// 实体int sizeX, sizeY;stack<StepPoint> everyStep;stack<StepPoint> everyUndoStep;function<void(int indexX, int indexY, Piece num)> drawPiece;
这六个数据,且这六个数据都是私有的
drawPiece 是个函数指针,由于舆图的不同,drawPiece 函数也会不同,以是详细情形详细赋值,这个 drawPiece 相称于一个虚函数。
为 drawPiece 赋值的接口为
void setDrawPiece(function<void(int indexX, int indexY, Piece num)> num){drawPiece = num;}
Entity 是舆图数据的实体,通过不断地访问
MapNode Node_Left;MapNode Node_Right;MapNode Node_Up;MapNode Node_Down;
这四个节点来到达舆图上的任何一个地方。详细函数为
MapNode Map::getMapNode(int indexX, int indexY){if (!ifInMap(indexX, indexY))return nullptr;MapNode result=Entity;for (int xx = 0;xx < indexX;xx++)result = result->Node_Right;for (int yy = 0;yy < indexY;yy++)result = result->Node_Down;return result;}
sizeX,sizeY 是舆图尺寸,用于广度寻路。
everyStep 储存每一步子落在的地方,everyUndoStep 储存每一步悔棋提掉的子所在的地方,都是 stack 构造来存的。
一开始棋盘是空的,以是通过
void Map::setBlankMap(int width, int height){sizeX = width;sizeY = height;if (Entity != nullptr){Entity->DeleteChild();delete Entity;Entity = nullptr;}Entity = new MapNode;Entity->indexX = 0;Entity->indexY = 0;MapNode currentY = Entity;MapNode currentX = Entity;for (int indexY = 0;indexY < height;indexY++){currentX = currentY;if (indexY != height - 1){currentY->Node_Down = new MapNode;currentY->Node_Down->Node_Up = currentY;currentY = currentY->Node_Down;currentY->indexX = 0;currentY->indexY = indexY + 1;}for (int indexX = 0;indexX < width-1;indexX++){currentX->Node_Right = new MapNode;currentX->Node_Right->Node_Left = currentX;if (currentX->Node_Up && currentX->Node_Up->Node_Right){currentX->Node_Right->Node_Up = currentX->Node_Up->Node_Right;currentX->Node_Up->Node_Right->Node_Down = currentX->Node_Right;}currentX = currentX->Node_Right;currentX->indexX = indexX + 1;currentX->indexY = indexY;}}while (!everyStep.empty())everyStep.pop();while (!everyUndoStep.empty())everyUndoStep.pop();}
来初始化 Entity,sizeX,sizeY。
围棋的流程为一个人下一颗子,判断这颗子吃了几颗子,把吃掉的子提掉,判断能不能下在这里(提掉的子大于一或提掉的子为一且不在 everyStep.top().kozai 的地方,没有提掉的子且自身的气不为 0),能下在这里就下在这里,不能下在这里就重新下,下完轮到另一个人。吃掉子,判断在不在劫争的位置,判断自身的气是否为 0 都要判断气,以是首先要实现判断一个区域的气的功能。
在 Map 里判断一个区域气的功能我写为两个函数
vector<MapNode> Map::getEnclosedPiece(int indexX, int indexY){vector<MapNode> result;MapNode num = getMapNode(indexX, indexY);BFS calc;calc.setMap(sizeX, sizeY, [&](MapPoint val){if (getMapNode(val.indexX, val.indexY)->Node_Piece != num->Node_Piece)return false;return true;});vector<MapPoint>enclose_point=calc.getEnclosedPoint(MapPoint(indexX, indexY));for (MapPoint i : enclose_point){result.push_back(getMapNode(i.indexX, i.indexY));}return result;}int Map::getZoneQi(int indexX, int indexY){int result = 0;vector<MapNode> enclose_point = getEnclosedPiece(indexX, indexY);for (MapNode i : enclose_point){if (i->Node_Piece == Space)result++;}return result;}
getZoneQi 便是判断一个区域气的函数。
判断一个区域的气为 0,那就要把这块区域设为空地,这个须要得到这块区域所有的点,然后把这块区域所有点设为空地,实现这个功能须要两个函数
vector<MapNode> Map::getAllSimplePiece(int indexX, int indexY){vector<MapNode> result;MapNode num = getMapNode(indexX, indexY);BFS calc;calc.setMap(sizeX, sizeY, [&](MapPoint val){if (getMapNode(val.indexX, val.indexY)->Node_Piece != num->Node_Piece)return false;return true;});vector<MapPoint>next_point = calc.getAllCanThrough(MapPoint(indexX, indexY));for (MapPoint i : next_point){result.push_back(getMapNode(i.indexX, i.indexY));}return result;} void Map::setZoneSpace(int indexX, int indexY){vector<MapNode> next_point = getAllSimplePiece(indexX, indexY);for (MapNode i : next_point){i->setPiece(Space);}}
能吃子,能提子,然后才能落子,落子的功能比较繁芜,函数也比较长,总的来说便是
bool putOnePiece(int indexX, int indexY,Piece num);
这个函数,如果这个点能落子返回 true,不能落子返回 false。详细实现看 gitee 上的源码
悔棋功能写在
bool Undo();
不悔了的功能写在
bool UnUnDo();
之以是有返回值是由于有可能没落子就有人按悔棋,或者没悔过棋就有人按不悔了,返回的 bool 值是悔棋和不悔了是否成功。
代码没什么好说的,看源码便是了,有点长。
点目功能写在
double getMesh(Piece num);
里,有点长,看源码去。
至此围棋这个游戏的逻辑已经全部实现了,接着便是界面的切换
2、SimulationCocos(仿照 Cocos)
仿照 Cocos 有三个模块,Menu,Scene,Director
Menu 菜单,用于保存每个按钮的类,每个场景里只有一个菜单,菜单里有 MenuItem (菜单项)
MenuItem菜单项,是一个双向链表,每个菜单里只有一个 MenuItem 链表,每个 MenuItem 里包含一个 Button
Button包含三个函数指针
function<void(BoundingBox num)> ResponseFunction;// 相应function<bool(BoundingBox num)> Call_Back;// 回调function<void(BoundingBox num)> Restore;// 规复
和一个 BoundingBox 类。
BoundingBox边框,包含
int size_width, size_height;// 尺寸MapPoint Place;// 左上角位置
三个数据,判断某个点是否在 BoundingBox 里面调用
bool BoundingBox::ifInBoundingBox(MapPoint num){int heightest, lowest, leftest, rightest;heightest = Place.indexY;lowest = Place.indexY + size_height;leftest = Place.indexX;rightest = Place.indexX + size_width;if (num.indexX >= leftest && num.indexX <= rightest && num.indexY >= heightest && num.indexY <= lowest)return true;return false;}
当一个场景里发生了点击反应,只需在场景的 Menu 里调用
MenuItem Menu::IfHappendEvent(int xx, int yy){MenuItem current = head;bool ifFind = false;while (current != nullptr){if (current->ifThisIsCalling(xx, yy)){ifFind = true;break;}current = current->child;}if(ifFind)return current;return nullptr;}
就能判断是否按到了某个按钮以及得到那个按钮的 MenuItem 值,然后调用 MenuItem 的按钮的 ResponseFunc
当点击反应结束时调用相应中的按钮的 Restore 然后判断鼠标所在的位置还在不在按钮里面,在的话调用按钮的 Call_Back 函数,函数里面传的参是按钮的边框,用于绘制按钮。
Scene场景,继续自 GameNode 类,
GameNode是一个双向链表,有
virtual bool initMySelf() { return true; }virtual bool operation() { return true; }virtual void EndOperation(){}
三个虚函数,operation 是场景运行时的函数,EndOperation 是令场景结束运行的函数,initMySelf 是初始化场景的函数
同时还有
bool GameNode::ifInRace(GameNode num){GameNode current = this;while (current!=nullptr){if (current == num)return true;current = current->child;}current = this;while (current!=nullptr){if (current == num)return true;current = current->parent;}return false;}
判断某个场景是否和自己有血缘关系。有血缘关系返回 true,无血缘关系返回 false
在 Scene 里有
function<void()> Operat_Func;
这个函数指针,也算是个虚函数,交由子类实现,子类必须实现这个函数指针,不然一定会报错,以是也可以称作不会报错的纯虚函数吧。
还有
bool ifExit;
是否退出场景的判断
在 Scene 里实现了
bool Scene::operation(){ifExit = false;while (true){Operat_Func();if (ifExit)break;}return true;}
void EndOperation() { ifExit = true; };
这两个函数,operation 里面真正的精华是 Operat_Func(); 这个函数,这个函数交由 Scene 的子类实现。Scene 的子类可以通过调用 this->EndOperation(); 这个函数退出场景。
Director,单例模式,程序运行的核心,每个 Scene 都在 Director 里运行。只有两个数据
bool ifExit; // 是否退出的判断GameNode IsRunning;// 当前运行的场景
Director 里紧张通过两个函数来实现 Scene 的运行和场景的切换
void Director::RunWithScene(GameNode scene){if (IsRunning != nullptr){IsRunning->EndOperation();}IsRunning = scene;}
void Director::Operation(){ifExit = false;GameNode temp = IsRunning;while (true){if (temp == nullptr)break;temp->initMySelf();if (temp->operation())// 场景一律在这个判断里运行,退出场景时进入判断{if(!IsRunning->ifInRace(temp))// 此时 IsRunning 已经通过 Director::getInstance()->RunWithScene(...); 改变了自己delete temp;temp = IsRunning;}if (ifExit)break;}}
IsRunning 变了,temp 不变,原来的场景能运行至结束然后才跳出,开释掉原来场景的内存接着才运行新的场景,这便是 Director 的核心逻辑,Director 须要和 Scene 相互引用,Scene 通过访问 Director 类直接访问当前正在运行的程序,如果 Director 不是单例模式,那么 Scene 就不能通过直接访问类的办法访问到当前的 Director,Director 还得传参给 Scene,这就造成了 Scene 和 Director 相互引用,也便是未定义类型的问题。以是 Director 用单例模式会很方便。
当然,这只是我利用 Cocos2d-x 根据 Cocos 的特性推测着写的,Cocos2d-x 里有自动开释池,写起来估计比我这种山寨版的要好,但是我这个在 Scene 里引用了 graphics.h 头文件,也便是可以在 Scene 里重新定义图形界面的大小,某种意义上会比 Cocos2d 方便。
3、GameScene,LoadScene这两个类都继续自 Scene,都须要实现 initMySelf 函数,不过如果要实现两个场景之间的切换不能通过相互引用的方法或者分成两个文件,一个头文件,一个 .cpp 文件来实现,头一种会造成创造一个多次重定义的标识符,和未定义标识符的报错,后一种会多出 140 个报错说是什么什么字符已经定义了。总之两个文件不能相互引用,那么便是一个知道另一个,一个不知道另一个,在这种情形下要实现场景的切换就用到了 GameNode 的特性双向链表,比如我是让 LoadScene 文件里引用了 GameScene 的头文件,然后在 LoadScene 的类里包含了 GameScene scene; 在布局函数的时候
scene = new GameScene;scene->addChild(this);
把自己设为 scene 的子节点,开始游戏时
Director::getInstance()->RunWithScene(scene);
进入 GameScene
在 GameScene 里要变回 LoadScene 只需
Director::getInstance()->RunWithScene(this->getChild());
就行了。Director 里假如 IsRunning 和 temp 有血缘关系它是不会 delete 掉 temp 的。以是切换场景时这两个场景都不会被清理掉。
完全的 VC 项目在 gitee 上:https://gitee.com/ProtagonistMan/weiqi
以上便是围棋的所有逻辑了,至于代码部分,很长,逻辑都有了就剩搬砖把大楼盖起来,看不下去我的源码也可以根据我的描述写一份自己的了,我相信我描述的够清楚了。
此外,我也给大家分享我网络的其他资源,从最零根本开始的C措辞C++教程,帮助大家在学习C措辞的道路上披荆斩棘!
编程学习书天职享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目条记,根本入门教程)最主要的是你可以在群里面互换提问编程问题哦!
对付C/C++感兴趣可以关注小编在后台私信我:【编程互换】一起来学习哦!
可以领取一些C/C++的项目学习视频资料哦!
已经设置好了关键词自动回答,自动领取就好了!