cocos2d-x写的跨平台象棋网络版

    xiaoxiao2025-04-05  21

    改BUG改了三夜都是凌晨5点才睡,,累,,泪 目录 APP: https://yunpan.cn/Oc6iaETF6fqxdK 访问密码 64b2

    AppDelegate.cpp #include "AppDelegate.h" #include "Common.h" #include "SceneStart.h" USING_NS_CC; AppDelegate::AppDelegate() { } AppDelegate::~AppDelegate() { } bool AppDelegate::applicationDidFinishLaunching() { // initialize director CCDirector* pDirector = CCDirector::sharedDirector(); CCEGLView* pEGLView = CCEGLView::sharedOpenGLView(); pDirector->setOpenGLView(pEGLView); //设置分别率 pEGLView->setDesignResolutionSize(800, 450, kResolutionExactFit); // turn on display FPS pDirector->setDisplayStats(false); // set FPS. the default value is 1.0/60 if you don't call this pDirector->setAnimationInterval(1.0 / 60); // create a scene. it's an autorelease object CCScene *pScene = CreateScene(SceneStart::create()); // run pDirector->runWithScene(pScene); return true; } // This function will be called when the app is inactive. When comes a phone call,it's be invoked too void AppDelegate::applicationDidEnterBackground() { CCDirector::sharedDirector()->stopAnimation(); // if you use SimpleAudioEngine, it must be pause // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); } // this function will be called when the app is active again void AppDelegate::applicationWillEnterForeground() { CCDirector::sharedDirector()->startAnimation(); // if you use SimpleAudioEngine, it must resume here // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); } AppDelegate.h #ifndef _APP_DELEGATE_H_ #define _APP_DELEGATE_H_ #include "cocos2d.h" /** @brief The cocos2d Application. The reason for implement as private inheritance is to hide some interface call by CCDirector. */ class AppDelegate : private cocos2d::CCApplication { public: AppDelegate(); virtual ~AppDelegate(); /** @brief Implement CCDirector and CCScene init code here. @return true Initialize success, app continue. @return false Initialize failed, app terminate. */ virtual bool applicationDidFinishLaunching(); /** @brief The function be called when the application enter background @param the pointer of the application */ virtual void applicationDidEnterBackground(); /** @brief The function be called when the application enter foreground @param the pointer of the application */ virtual void applicationWillEnterForeground(); }; #endif // _APP_DELEGATE_H_ Common.h #ifndef __COMMON_H__ #define __COMMON_H__ #include "cocos2d.h" USING_NS_CC; #define winSize CCDirector::sharedDirector()->getWinSize() static inline CCScene* CreateScene(CCLayer* layer) { CCScene* s = CCScene::create(); s->addChild(layer); return s; } #endif Game.cpp #include "Game.h" #include "SceneGame.h" #include "Net.h" #include "backMusic.h" #include "SimpleAudioEngine.h" bool Game::_redStart;//定义 .h文件中是申明 bool Game::_redTurn; void Game::onEnter() { CCLayer::onEnter(); _steps = CCArray::create();//没有加入到任何子节点中,下一帧自动清除 _steps->retain();//所以保存 CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("backMusic.mp3", true); } void Game::onExit() { CCLayer::onExit(); _steps->release();//retain 之后一定要 release CocosDenshion::SimpleAudioEngine::sharedEngine()->stopBackgroundMusic(); } bool Game::init() { CCLayer::init(); //添加自己照片。。哈哈 CCSprite* sprite = CCSprite::create("3.jpg"); sprite->setPosition(ccp(630, 200)); sprite->setScale(0.4); addChild(sprite); //添加音乐 backMusic* music = backMusic::create(); addChild(music); //把棋盘放到Game中 _plate = Plate::create(); addChild(_plate); //摆好棋子 Stone::_myWidth = winSize.height / 10; for (size_t i = 0; i < 32; i++) { _stone[i] = Stone::create(i); addChild(_stone[i]); } //启动触摸 setTouchEnabled(true); setTouchMode(kCCTouchesOneByOne); //初始化按钮 initItem(); //设置关闭按钮 CCMenuItem* closeItem = CCMenuItemImage::create("CloseNormal.png", "CloseSelected.png", this, menu_selector(Game::closeGame)); closeItem->setPosition(ccp(370, -200)); menu->addChild(closeItem); //初始化选中时的精灵 _selectFlag = CCSprite::create("selected.png"); addChild(_selectFlag); _selectFlag->setVisible(false); //一开始没有任何棋子被选中 _selectid = -1; //悔棋标志为false bool regretFlag = false; _redTurn = true; //启动定时器 scheduleUpdate(); return true; } bool Game::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) { return true; } void Game::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) { int clickid = getClickStone(pTouch);//根据触摸点获取棋子,如果这个点没有棋子返回 -1 if (_selectid == -1)//如果还没有棋子被选中 selectStone(clickid); else//走棋 moveStone(clickid,pTouch->getLocation()); } //选择棋子 void Game::selectStone(int clickid) { if (clickid != -1 && _redTurn == _redStart && _redTurn == _stone[clickid]->_red)//如果1.点击到棋子 2.轮到这我方走 3.是我方的棋子 { doSelectStone(clickid); sendSelectStoneToNet(); } } //移动棋子 void Game::moveStone(int clickid,CCPoint pos) { if (clickid != -1)//首先判断点中的位置是不是棋子 { if (_stone[clickid]->_red == _stone[_selectid]->_red)//如果是己方的棋子再次选中 { doSelectStone(clickid); sendSelectStoneToNet(); return; } } //棋子走动 int row, col; if (!getRowColByPos(row, col,pos))//看能否获得行列 return; if (!canMove(_selectid, row, col, clickid))//每个棋子有自己固定的走法,必须经过审核才能走 return; //向对方发送信息 sendMoveStoneToNet(row, col); //走棋 注意这里两个selectid 一个是发送过来的_selectid 一个是自己方点击的 _selectid doMoveStone(_selectid, row, col);//两个功能,1.对方发信息来走棋(需要ID和目标位置)2.自己方走棋(需要选中的棋和目标位置) } //发送选棋信息 void Game::sendSelectStoneToNet() { char buf[2]; buf[0] = 1;//命令字1 表示选择棋子 buf[1] = _selectid; Net::getInstance()->Send(buf, 2); } //发送走棋信息 void Game::sendMoveStoneToNet(int row, int col) { char buf[4]; buf[0] = 2; buf[1] = _selectid; buf[2] = 9 - row; buf[3] = 8 - col; Net::getInstance()->Send(buf, 4); } //用于接收数据 void Game::update(float) { if (_redStart == _redTurn && !regretFlag)//此时不接受消息 return; char buf; char id; char row; char col; if (Net::getInstance()->Recv(&buf, 1) != 1) return; switch (buf) { case 1://接收对方选择棋子的命令 Net::getInstance()->Recv(&id, 1); doSelectStone(id);//把对方选择棋子显示出来 break; case 2: Net::getInstance()->Recv(&id, 1); Net::getInstance()->Recv(&row, 1); Net::getInstance()->Recv(&col, 1); doMoveStone(id, row, col); break; case 3: judgeRegret(); break; case 'y': waitRegretYes(); break; case 'n': waitRegretNo(); break; case 9: CCDirector::sharedDirector()->replaceScene(CreateScene(Game::create())); } } void Game::initItem() { menu = CCMenu::create(); addChild(menu); //创建悔棋按钮 initRegretButton(); //其他按钮 regretTextItem = CCMenuItemImage::create("huiqikuang.png", "huiqikuang2.png"); yesItem = CCMenuItemImage::create("tongyi.png", "tongyi2.png"); noItem = CCMenuItemImage::create("butongyi.png", "butongyi2.png"); giveMyItem = CCMenuItemImage::create("shenqing2.png", "huiqikuang2.png"); giveMyItemYes = CCMenuItemImage::create("duifangtongyi2.png", "duifangtongyi.png"); giveMyItemNo = CCMenuItemImage::create("duifangbutongyi2.png", "duifangbutongyi.png"); menu->addChild(regretTextItem); menu->addChild(yesItem); menu->addChild(noItem); menu->addChild(giveMyItem); menu->addChild(giveMyItemYes); menu->addChild(giveMyItemNo); regretTextItem->setVisible(false); yesItem->setVisible(false); noItem->setVisible(false); giveMyItem->setVisible(false); giveMyItemYes->setVisible(false); giveMyItemNo->setVisible(false); moveBy(ccp(-180, 120), regretTextItem);//设置位置 moveBy(ccp(-280, -100), yesItem);//设置位置 moveBy(ccp(-80, -100), noItem);//设置位置 moveBy(ccp(-180, 120), giveMyItem);//设置位置 moveBy(ccp(-180, 120), giveMyItemYes);//设置位置 moveBy(ccp(-180, 120), giveMyItemNo);//设置位置 yesItem->setTarget(this, menu_selector(Game::yesregret)); noItem->setTarget(this, menu_selector(Game::noregret)); giveMyItemYes->setTarget(this, menu_selector(Game::waitYes)); giveMyItemNo->setTarget(this, menu_selector(Game::waitNo)); } void Game::judgeRegret() { regretTextItem->setVisible(true); yesItem->setVisible(true); noItem->setVisible(true); } //显示出 选择的棋子 void Game::doSelectStone(int clickid) { //如果有棋子被点中,,就让【】 图形在那个位置显示 _selectFlag->setPosition(_stone[clickid]->getPosition()); _selectFlag->setVisible(true); _selectid = clickid; } //用于把一个id从当前位置,移动到 指定位置 void Game::doMoveStone(int selectid, int row, int col) { int killid = getStoneIdByRowCol(row, col); //记录走棋信息 Step* step = Step::create(selectid, _stone[selectid]->_row, _stone[selectid]->_col, row, col, killid); _steps->addObject(step);//加入到数组中 //走棋 _stone[_selectid]->move(row, col); _selectFlag->setVisible(false); _selectid = -1;//走完后一定要设置,不然会一直选中 if (killid != -1) { _stone[killid]->_dead = true; _stone[killid]->setVisible(false); //检查游戏是否结束 checkGameOver(killid); } _redTurn = !_redTurn; } void Game::checkGameOver(int killid) { Stone* s = _stone[killid]; if (s->_type == SHUAI)//游戏结束 如果这次杀死的是帅或将,那么就打印 { CCMenu* menu = CCMenu::create(); addChild(menu); if (s->_red == Game::_redStart)//开始选的棋和杀死的棋一样,,那么是输了 { CCMenuItem* item1 = CCMenuItemImage::create("shuijiemian.png", "shuijiemian.png"); menu->addChild(item1); item1->setPosition(ccp(-150, 50)); } else//赢了 { CCMenuItem* item1 = CCMenuItemImage::create("yingjiemian.png", "yingjiemian.png"); menu->addChild(item1); item1->setPosition(ccp(-150, 50)); CCMenuItem* item = CCMenuItemImage::create("new.jpg", "new2.jpg"); menu->addChild(item); item->setTarget(this, menu_selector(Game::gameOverCallback)); item->setPosition(ccp(-150, -150)); } regretItem->setEnabled(false); // 红旗黑棋位置对调 Game::_redStart = !Game::_redStart; } } void Game::gameOverCallback(CCObject*) { char buf = 9; Net::getInstance()->Send(&buf, 1); CCDirector::sharedDirector()->replaceScene(CreateScene(Game::create())); } //获取 点中 的棋子的id int Game::getClickStone(CCTouch* touch) { int row, col; if (!getRowColByPos(row, col, touch->getLocation())) return -1; return getStoneIdByRowCol(row, col); } //通过坐标转化成行列 bool Game::getRowColByPos(int &row, int &col, CCPoint pt) { for (row = 0; row < 10; row++) for (col = 0; col < 9; col++) { //获取棋盘上点的世界坐标 CCPoint ptStone = getPosByRowCol(row, col); //判断世界坐标上的点。。是否和触摸点的距离小于 棋子宽度的一半 if (ptStone.getDistance(pt) < Stone::_myWidth / 2) return true; } return false; } //通过行列。看是否能找到某个棋子 int Game::getStoneIdByRowCol(int row, int col) { for (int i = 0; i < 32; i++) { if (_stone[i]->_row == row && _stone[i]->_col == col && !_stone[i]->_dead) return i; } return -1; } //行列 转换成 世界坐标 CCPoint Game::getPosByRowCol(int row, int col) { float x = col*Stone::_myWidth; float y = row*Stone::_myWidth; return ccp(x, y) + Stone::_ptOff; } bool Game::canMove(int moveid, int row, int col, int killid) { switch (_stone[moveid]->_type) { case CHE: return canMoveChe(moveid, row, col, killid); case MA: return canMoveMa(moveid, row, col, killid); case XIANG: return canMoveXiang(moveid, row, col, killid); case SHI: return canMoveShi(moveid, row, col, killid); case SHUAI: return canMoveShuai(moveid, row, col, killid); case PAO: return canMovePao(moveid, row, col, killid); case BING: return canMoveBing(moveid, row, col, killid); default: break; } return false; } int Game::getStoneCountInLine(int row1, int col1, int row2, int col2) { if (row1 != row2 &&col1 != col2) return -1; int t = 0; if (row1 == row2) { int min = col1 > col2 ? col2 : col1; int max = col1 + col2 - min; for (int col = min + 1; col < max; col++) { int id = getStoneIdByRowCol(row1, col); if (id != -1) t++; } } else { int min = row1 > row2 ? row2 : row1; int max = row1 + row2 - min; for (int row = min + 1; row < max; row++) { int id = getStoneIdByRowCol(row,col1); if (id != -1)//如果有棋子 t++; } } return t; } bool Game::canMoveChe(int moveid, int row, int col, int killid)//moveid 是 上次选中的棋子 killid是这次点击的棋子 { if (getStoneCountInLine(row, col, _stone[moveid]->_row, _stone[moveid]->_col) == 0) return true; return false; } bool Game::canMoveMa(int moveid, int row, int col, int killid) { int r = _stone[moveid]->_row; int c = _stone[moveid]->_col; int d = abs(r - row) * 10 + abs(c - col); if (d != 12 && d != 21) return false; //判断马腿 if (d == 21)//列相差1,航相差2 { int mr = (r + row) / 2; int mc = c; if (getStoneIdByRowCol(mr, mc)!=-1)//说明这个位置有棋子 return false; } else { int mr = r; int mc = (c + col) / 2; if (getStoneIdByRowCol(mr, mc) != -1)//说明这个位置有棋子 return false; } return true; } bool Game::canMoveXiang(int moveid, int row, int col, int killid) { if (_stone[moveid]->_red == _redStart)//只要动的棋子和开局选的棋子一样,那么就可以在下方动 { if (row > 4) return false; } else { if (row < 5) return false; } int r = _stone[moveid]->_row; int c = _stone[moveid]->_col; int d = abs(r - row) * 10 + abs(c - col); if (d != 22) return false; int mr = (row + r) / 2; int mc = (col + c) / 2; int id = getStoneIdByRowCol(mr, mc); if (id != -1) return false; return true; } bool Game::canMoveShi(int moveid, int row, int col, int killid) { if (_stone[moveid]->_red == _redStart)//只要动的棋子和开局选的棋子一样,那么就可以在下方动 { if (row > 2) return false; } else { if (row < 7) return false; } if (col < 3 || col>5) return false; int r = _stone[moveid]->_row; int c = _stone[moveid]->_col; int d = abs(r - row) * 10 + abs(c - col); if (d == 11)//行相等,那么列只能差值为1 return true; return false; } bool Game::canMoveShuai(int moveid, int row, int col, int killid) { //老蒋照面 if (killid != -1 && _stone[killid]->_type == SHUAI) return canMoveChe(moveid, row, col, killid); //开局选的棋子一定在下方 if (_stone[moveid]->_red == _redStart)//只要动的棋子和开局选的棋子一样,那么就可以在下方动 { if (row > 2) return false; } else//如果动的棋子,和开局选的棋子不一样,那么就动上面的棋子 { if (row < 7) return false; } if (col < 3 || col>5) return false; int r = _stone[moveid]->_row; int c = _stone[moveid]->_col; int d = abs(r - row) * 10 + abs(c - col); if (d == 1 || d == 10)//行相等,那么列只能差值为1 return true; return false; } bool Game::canMovePao(int moveid, int row, int col, int killid) { if (killid == -1)//说明没有点中棋子 return canMoveChe(moveid, row, col, killid);//和车一样的使用 //点中棋子后,中间必须隔一个才能走 int count = getStoneCountInLine(row, col, _stone[moveid]->_row, _stone[moveid]->_col); return count == 1;//跑中间只能隔一个 } bool Game::canMoveBing(int moveid, int row, int col, int killid) { int r = _stone[moveid]->_row; int c = _stone[moveid]->_col; int d = abs(r - row) * 10 + abs(c - col); if (d != 1 && d != 10)//行相等,那么列只能差值为1 return false; if (_stone[moveid]->_red == _redStart)//只要动的棋子和开局选的棋子一样,那么就可以在下方动 { //不能后退 if (row < r)//将要走的行 大于 现在的行 return false; //没过河不能平移 if (r <= 4 && col != c) return false; } else { //不能后退 if (row > r)//将要走的行 大于 现在的行 return false; //没过河不能平移 if (r >= 5 && col != c) return false; } return true; } void Game::yesregret(CCObject*) { char buf = 'y'; Net::getInstance()->Send(&buf, 1); regret(); yesItem->setVisible(false); noItem->setVisible(false); regretTextItem->setVisible(false); } void Game::noregret(CCObject*) { char buf = 'n'; Net::getInstance()->Send(&buf, 1); yesItem->setVisible(false); noItem->setVisible(false); regretTextItem->setVisible(false); } void Game::waitRegretYes() { giveMyItem->setVisible(false); giveMyItemYes->setVisible(true); regret(); } void Game::waitRegretNo() { giveMyItem->setVisible(false); giveMyItemNo->setVisible(true); } void Game::waitYes(CCObject*) { giveMyItemYes->setVisible(false); //打开触摸 setTouchEnabled(true); //悔棋标记消失 regretFlag = false; } void Game::waitNo(CCObject*) { giveMyItemNo->setVisible(false); //打开触摸 setTouchEnabled(true); //悔棋标记消失 regretFlag = false; } //在悔棋之前进行判断,对方走时,自己不能悔棋 void Game::preregret(CCObject*) { if (Game::_redStart != Game::_redTurn) return; //发消息给对方 char buf = 3; Net::getInstance()->Send(&buf, 1); giveMyItem->setVisible(true); //屏蔽自己的触摸 setTouchEnabled(false); //可以接收消息 regretFlag = true; } void Game::regret() { for (size_t i = 0; i < 2; i++) { if (_steps->count() == 0) return; //取出上一次的棋子 的位置 CCObject* obj = _steps->lastObject(); Step* step = (Step*)obj; //将这个棋子,恢复到原来位置 Stone* sMove = _stone[step->_moveid];//获取上一个棋子的id sMove->move(step->_rowFrom, step->_colFrom); //如果有死掉的棋子,应该复活 if (step->_killid != -1) { Stone* sKill = _stone[step->_killid]; sKill->_dead = false;//不死 sKill->setVisible(true);//再显现出来 } //初始化一些和移动相关的变量 _selectid = -1; _selectFlag->setVisible(false); _redTurn = !_redTurn; //把Array里面最后一个对象删除 _steps->removeLastObject(); } } //创建悔棋按钮 void Game::initRegretButton() { regretItem = CCMenuItemImage::create("regret.png", "regret2.png"); menu->addChild(regretItem); regretItem->setTarget(this, menu_selector(Game::preregret));//设置触发函数 moveBy(ccp(120, 0), regretItem);//设置位置 } void Game::moveBy(CCPoint pt, CCNode* node) { node->setPosition(pt + node->getPosition()); } void Game::closeGame(CCObject*) { if (Net::getInstance()->isServer()) #ifdef WIN32 closesocket(Net::getInstance()->getServerSocket()); #else close(Net::getInstance()->getServerSocket()); #endif else #ifdef WIN32 closesocket(Net::getInstance()->getClientSocket()); #else close(Net::getInstance()->getClientSocket()); #endif exit(0); } Game.h #ifndef __GAME_H__ #define __GAME_H__ #include "Common.h" #include "Stone.h" #include "Plate.h" #include "Step.h" class Game:public CCLayer { int _selectid;//记录选中棋子的id bool _isOver;//游戏是否结束 bool regretFlag;//是否正确按下悔棋 CCSprite* _selectFlag;//选中时的精灵图片 CCMenu* menu; CCMenuItem* regretItem; CCMenuItem* regretTextItem; CCMenuItem* yesItem; CCMenuItem* noItem; CCMenuItem* giveMyItem; CCMenuItem* giveMyItemYes; CCMenuItem* giveMyItemNo; public: static bool _redStart;//红棋开始 static bool _redTurn;//该不该红走 (轮到谁走了) Plate* _plate;//棋盘 Stone* _stone[32];//棋子 CCArray* _steps;//记录走棋的信息 CREATE_FUNC(Game); bool init(); virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent); //如果点到空地上,返回-1 int getClickStone(CCTouch*); CCPoint getPosByRowCol(int, int); bool getRowColByPos(int &row, int &col, CCPoint pt); int getStoneIdByRowCol(int row, int col); bool canMove(int moveid, int row, int col, int killed); bool canMoveChe(int moveid, int row, int col, int killed); bool canMoveMa(int moveid, int row, int col, int killed); bool canMoveXiang(int moveid, int row, int col, int killed); bool canMoveShi(int moveid, int row, int col, int killed); bool canMoveShuai(int moveid, int row, int col, int killed); bool canMovePao(int moveid, int row, int col, int killed); bool canMoveBing(int moveid, int row, int col, int killed); int getStoneCountInLine(int row1, int col1, int row2, int col2); void onEnter(); void onExit(); void regret(); void checkGameOver(int killid); void gameOverCallback(CCObject*); void closeGame(CCObject*); void preregret(CCObject*); void yesregret(CCObject*); void noregret(CCObject*); void waitYes(CCObject*); void waitNo(CCObject*); void initItem(); void update(float); void doSelectStone(int id); void doMoveStone(int id, int row, int col); void selectStone(int id); void moveStone(int id, CCPoint pos); void sendSelectStoneToNet(); void sendMoveStoneToNet(int row,int col); void initRegretButton(); void moveBy(CCPoint pt, CCNode* node); void judgeRegret(); void waitRegretYes(); void waitRegretNo(); }; #endif Net.cpp #include "Net.h" Net* Net::theOne = NULL; Net::Net() { _server = INVALID_SOCKET;//初始化为无效的SOCKET _client = INVALID_SOCKET; _isServer = false; _isListen = false; _isConnect = false; } //作为服务器接口 bool Net::startServer(unsigned short port) { //创建socket _server = socket(AF_INET, SOCK_STREAM, 0);//1.用tcp/ip协议 2.用TCP流协议 3.默认为0 if (_server == INVALID_SOCKET) { CCLog("Socket create error"); return false; } setNonBlock(_server);//非阻塞 //填信息 struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); #ifdef WIN32 addr.sin_addr.S_un.S_addr = INADDR_ANY;//任何一个IP #else addr.sin_addr.s_addr = INADDR_ANY; #endif //将端口号和程序绑定 int ret = bind(_server, (struct sockaddr*)&addr, sizeof(addr)); if (ret == SOCKET_ERROR) { CCLog("bind socket error"); closesocket(_server); _server = INVALID_SOCKET; return false; } //设置监听 listen(_server, 10);//允许10个在这里等待监听 _isServer = true;//是服务端 _isListen = true;//是处于监听状态 _isConnect = false;//还未连接 return true; } //服务端 专门用来接收客服端 的函数 bool Net::Accept() { //判断服务端是否准备好 if (!_isServer || !_isListen || _isConnect) return false; //struct sockaddr_in peeraddr;//定义一个对方的地址 //int peerlen = sizeof(peeraddr);//定义对方的地址长度 //if ((_client = accept(_server, (struct sockaddr*)&peeraddr, &peerlen)) < 0)//从完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞 // return false; //接收一个客服端 _client = accept(_server, NULL, NULL); if (_client == INVALID_SOCKET)//看是否成功接收到客服端 return false; setNonBlock(_server);//非阻塞。毕竟是游戏,不能老阻塞在这里,这也是创建这个函数的原因 return true; } //作为客服端接口 bool Net::Connect(unsigned short port, const char* ipaddr) { //创建客服端socket _client = socket(AF_INET, SOCK_STREAM, 0); //填信息 struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); #ifdef WIN32 addr.sin_addr.S_un.S_addr = inet_addr(ipaddr);//连接上一个IP #else addr.sin_addr.s_addr = inet_addr(ipaddr); #endif //int ret = connect(_client, (struct sockaddr*)&addr, sizeof(addr));//尝试连接 //if (ret == SOCKET_ERROR)//看是否连接上了 //{ // CCLog("%s", ipaddr); // closesocket(_client); // _client = INVALID_SOCKET; // return false; //} if (connect(_client, (struct sockaddr*)&addr, sizeof(addr)) < 0) { CCLog("%s", ipaddr); closesocket(_client); return false; } setNonBlock(_client);//放在connect后面,因为connect是阻塞函数,放在上面会导致 connect 失效 _isConnect = true;//连接上了之后就不能再次连接了,相当于一个服务端只接收一个客服端 _isServer = false;//把服务器都设置为false _isListen = false; return true; } //客服端和服务器通用的端口 int Net::Recv(void* buf, int len)//需要传的东西,和长度 { #if 0 int alreadyRecvLen = 0;//已经接收到的长度 /*select()机制中提供一fd_set的数据结构,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄,建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。 */ while (1) { fd_set set; FD_ZERO(&set); /*将set清零使集合中不含任何fd*/ FD_SET(_client, &set);/*将_client加入set集合*/ struct timeval tv; tv.tv_sec = 0;//秒 tv.tv_usec = 200;//微秒 //非阻塞式I/O编程。。select只是检测是否可以发送数据,换句话说,如果你不要效率的话,不需要select函数都是可以的。 //使用Select就可以完成非阻塞。返回值:准备就绪的描述符数,若超时则返回0,若出错则返回-1。 int ret = select(-1,//本参数忽略,仅起到兼容作用。 &set,//指向一组等待可读性检查的套接口。(我们只要可读Client即可) NULL,//指向一组等待可写性检查的套接口。 NULL,//指向一组等待错误检查的套接口。 &tv);//最多等待时间,对阻塞操作则为NULL。 if (ret <= 0) return -1; ret = recv(_client, (char*)buf + alreadyRecvLen, len - alreadyRecvLen, 0);//1.指定接收端套接字描述符 4.一般置0。 if (ret > 0)//recv函数返回其实际copy的字节数 copy时出错,那么它返回SOCKET_ERROR { alreadyRecvLen += ret; if (alreadyRecvLen == len) return len; } else if (ret == 0)//网络中断了,那么它返回0。 return alreadyRecvLen; else break; } return -1; #else setNonBlock(_client); return recv(_client, (char*)buf, len, 0);//这样是偷懒的行为 #endif } // 发送 int Net::Send(char* buf, int len) { #if 0 int alreadySendLen = 0;//已经发送到的长度 while (1) { //向一个已连接的套接口发送数据。不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。 int ret = send(_client, buf + alreadySendLen, len - alreadySendLen, 0);//1.一个用于标识已连接套接口的描述字。 if (ret > 0)//recv函数返回其实际copy的字节数 { alreadySendLen += ret; if (alreadySendLen == len) return len; } else//copy时出错,网络中断,返回SOCKET_ERROR break; } return -1; #else setNonBlock(_client); return send(_client, buf, len, 0);//这样是偷懒的行为 #endif } Net.h #ifndef __NET_H__ #define __NET_H__ #ifdef WIN32 #include "WinSock2.h" #pragma comment(lib,"ws2_32.lib") #else #define SOCKET int #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket close #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h>//sockaddr_in #include <unistd.h>//close #include <arpa/inet.h> #include <netdb.h> #include <sys/ioctl.h> #include <net/if.h> #endif #include "cocos2d.h" #include "SceneGame.h" USING_NS_CC; class Net:public CCLayer { protected: Net(); bool _isServer; bool _isListen; bool _isConnect; SOCKET _server; SOCKET _client; static Net* theOne; void setNonBlock(SOCKET sock)//设置socket为非阻塞 { #ifdef WIN32 u_long arg = 1; ioctlsocket(sock, FIONBIO, &arg);//FIONBIO:允许或禁止套接口s的非阻塞模式。argp指向一个无符号长整型。如允许非阻塞模式则非零,如禁止非阻塞模式则为零。 #else int flag = fcntl(sock,F_GETFL,0); flag |= O_NONBLOCK; fcntl(sock,F_SETFL,flag); #endif } public: // 成员函数后面加cost表示这个函数不能修改成员变量 // 实质是用这个const修饰隐藏的this指针 // 常对象只能调用常函数 const Net* p; // p->isServer(); // p->StartServer(); // error:常量对象只能调用常量成员函数 bool isServer(/* Net* this */) const { return _isServer; } SOCKET getServerSocket() { return _server; } SOCKET getClientSocket() { return _client; } static Net* getInstance()//单例 { if (theOne == NULL) return theOne = new Net; return theOne; } bool startServer(unsigned short port); bool Accept(); bool Connect(unsigned short port, const char* ipaddr); int Recv(void* buf, int len); int Send(char* buf, int len); }; #endif Plate.cpp #include "Plate.h" bool Plate::init() { CCSprite::initWithFile("background.png"); setPosition(ccp(0, 0)); //设置锚点为(0,0) setAnchorPoint(ccp(0, 0)); //缩放 setScale(winSize.height / getContentSize().height); return true; } Plate.h #ifndef __PLATE_H__ #define __PLATE_H__ #include "Common.h" class Plate:public CCSprite { public: CREATE_FUNC(Plate); bool init(); }; #endif SceneGame.cpp #include "SceneGame.h" #include "Net.h" #include "Game.h" int SceneGame::_randomSelf = -1; int SceneGame::_randomOther = -1; bool SceneGame::init() { CCLayer::init(); _menu = CCMenu::create(); addChild(_menu); //开始服务器按钮 initStartServerButton(); //连接服务器按钮 initConnectButton(); //设置关闭按钮 CCMenuItem* closeItem = CCMenuItemImage::create("CloseNormal.png", "CloseSelected.png", this, menu_selector(SceneGame::closeGame)); closeItem->setPosition(ccp(370, -200)); _menu->addChild(closeItem); //开启触摸 用 box 就不需要触摸了 //setTouchEnabled(true); //setTouchMode(kCCTouchesOneByOne); //创建随机数 _randomSelf = CCRANDOM_0_1() * 100; return true; } //创建服务器开始按钮 void SceneGame::initStartServerButton() { serverItem = CCMenuItemImage::create("zhuji.png", "zhuji2.png"); _menu->addChild(serverItem); serverItem->setTarget(this, menu_selector(SceneGame::startServer));//设置触发函数 moveBy(ccp(200, 100), serverItem);//设置位置 } //创建客服端连接按钮 void SceneGame::initConnectButton() { clientItem = CCMenuItemImage::create("kefuji.png","kefuji2.png"); _menu->addChild(clientItem); clientItem->setTarget(this, menu_selector(SceneGame::ConnectServer));//设置触发函数 moveBy(ccp(200, -100), clientItem);//设置位置 } //启动服务器 void SceneGame::startServer(CCObject* sender) { //ConnectServer按钮只能点一次 serverItem->setEnabled(false);//点进来了,以后再也不能点了 clientItem->setEnabled(false); getHostIP(); if (Net::getInstance()->startServer(6543)) scheduleUpdate();//定时器 } //客服端 连接 void SceneGame::ConnectServer(CCObject* sender) { serverItem->setEnabled(false);//点进来了,以后再也不能点了 clientItem->setEnabled(false); //设置文本 我用 box 代替了这个text //_text = CCTextFieldTTF::textFieldWithPlaceHolder("Please input ip", "Arial", 30); //addChild(_text); //_text->setPosition(ccp(250, 225)); //创建一个可以编辑的 box box = CCEditBox::create(CCSize(250, 50), CCScale9Sprite::create("green_edit.png")); addChild(box); box->setPosition(ccp(240, 225)); //增加提示 box->setPlaceHolder("please input:IP"); //设置提示字体 box->setFont("Arial", 30); //设置输入的字体颜色 box->setFontColor(ccc3(0, 0, 255)); //设置可以输入的长度 box->setMaxLength(15); //弹出软键盘。最后一个参数是,回车键的样子 box->setReturnType(kKeyboardReturnTypeDone); //设置键盘弹出为数字键盘或其他 box->setInputMode(kEditBoxInputModeAny); schedule(schedule_selector(SceneGame::pingIP)); } //bool SceneGame::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) //{ // if (_text!=NULL && _text->boundingBox().containsPoint(pTouch->getLocation())) // _text->attachWithIME();//调出软键盘 // // return false; //} void SceneGame::pingIP(float) { /*if (_text == NULL) return; const char* endIP= _text->getString();*/ static char _IP[30]; const char* IP = box->getText(); if (strlen(IP) >15 || strlen(IP)<8) return; if (strcmp(_IP, IP) == 0) return; strcpy(_IP, IP); //if (Net::getInstance()->Connect(6543, "127.0.0.1"))//如果连接成功 if (Net::getInstance()->Connect(6543, IP))//如果连接成功 { if (_randomOther == -1) { Net::getInstance()->Send((char*)&_randomSelf, 4);//注意,先发,不然会挂死在这里 //再启动一个定时器,其实没有必要,就当学习一下 schedule(schedule_selector(SceneGame::RecvRadom)); } unschedule(schedule_selector(SceneGame::pingIP)); //setTouchEnabled(false); box->setVisible(false); } } //服务端 void SceneGame::update(float) { if (Net::getInstance()->Accept()) { if (_randomOther == -1)//还没有接收到客服端发过来的随机数 { Net::getInstance()->Send((char*)&_randomSelf, 4); schedule(schedule_selector(SceneGame::RecvRadom)); } unscheduleUpdate(); CCLog("Connect OK"); } } void SceneGame::RecvRadom(float) { static int recvLen = 0; char* buf = (char*)&_randomOther;//随机数 int ret = Net::getInstance()->Recv(buf + recvLen, 4 - recvLen); if (ret <= 0) return; recvLen += ret; if (recvLen != 4) return; if (_randomSelf > _randomOther || (_randomSelf == _randomOther && Net::getInstance()->isServer())) { Game::_redStart = true; } else { Game::_redStart = false; } _game = Game::create(); addChild(_game); unschedule(schedule_selector(SceneGame::RecvRadom)); } void SceneGame::moveBy(CCPoint pt, CCNode* node) { node->setPosition(pt + node->getPosition()); } void SceneGame::getHostIP() { #ifdef WIN32 char szHost[256];//存放主机名的缓冲区 gethostname(szHost, 256);//取得本地主机名称 hostent *pHost = ::gethostbyname(szHost);//通过主机名得到地址信息 //一个主机可能有多个网卡、多个IP、下面的代码输出所有的IP地址 in_addr addr; for (int i = 0;; i++) { //获得地址(网络字节) char *p = pHost->h_addr_list[i]; if (NULL == p) break;//退出循环 //将地址拷贝到in_addr结构体中 memcpy(&addr.S_un.S_addr, p, pHost->h_length); //将in_addr转换为主机字节序 char *IP = inet_ntoa(addr); CCLabelTTF* label = CCLabelTTF::create(IP, "Arial", 30); addChild(label); moveBy(ccp(250, 280 - i * 30), label);//设置位置 } #else int sockfd; struct ifconf ifconf; struct ifreq *ifreq; char buf[512];//缓冲区 //初始化ifconf ifconf.ifc_len = 512; ifconf.ifc_buf = buf; if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0) return; ioctl(sockfd, SIOCGIFCONF, &ifconf); //获取所有接口信息 //接下来一个一个的获取IP地址 ifreq = (struct ifreq*)ifconf.ifc_buf; for (int i = (ifconf.ifc_len / sizeof(struct ifreq)); i>0; i--) { if (ifreq->ifr_flags == AF_INET){ //for ipv4 char* IP = inet_ntoa(((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr); ifreq++; if(!strcmp(IP,"127.0.0.1")) continue; CCLabelTTF* label = CCLabelTTF::create(IP, "Arial", 30); addChild(label); moveBy(ccp(250, 280 - i * 30), label);//设置位置 } } #endif } void SceneGame::closeGame(CCObject*) { if (Net::getInstance()->isServer()) #ifdef WIN32 closesocket(Net::getInstance()->getServerSocket()); #else close(Net::getInstance()->getServerSocket()); #endif else #ifdef WIN32 closesocket(Net::getInstance()->getClientSocket()); #else close(Net::getInstance()->getClientSocket()); #endif exit(0); } SceneGame.h #ifndef __SceneGame_H__ #define __SceneGame_H__ #include "Game.h" #include "cocos-ext.h" USING_NS_CC_EXT; USING_NS_CC; class SceneGame :public CCLayer { public: CREATE_FUNC(SceneGame); bool init(); Game* _game; void regret(CCObject*); void moveBy(CCPoint pt, CCNode* node); void initStartServerButton(); void initConnectButton(); void startServer(CCObject*); void ConnectServer(CCObject*); void update(float); void RecvRadom(float); void initLabel(); void closeGame(CCObject*); void pingIP(float); void getHostIP(); //virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); static int _randomSelf; static int _randomOther; CCMenu* _menu; CCMenuItem* serverItem; CCMenuItem* clientItem; CCLabelTTF* _label; CCTextFieldTTF* _text; CCEditBox* box; }; #endif SceneStart.cpp #include "SceneStart.h" #include "SceneGame.h" bool SceneStart::init() { CCLayer::init(); //背景图片 CCSprite* s = CCSprite::create("desk.png"); addChild(s); s->setPosition(ccp(winSize.width / 2, winSize.height / 2)); //创建开始按钮 CCSprite* sprite1 = CCSprite::create("bkg1.png"); CCSprite* sprite2 = CCSprite::create("bkg2.png"); addChild(sprite1); addChild(sprite2); sprite2->setPosition(ccp(winSize.width/4, winSize.height / 2)); sprite1->setPosition(ccp(winSize.width - winSize.width/4, winSize.height / 2)); _black = sprite1; _red = sprite2; //使能触摸,使用单点 setTouchEnabled(true); setTouchMode(kCCTouchesOneByOne); //启动帧循环定时器(不要放在 ccTouchBegan 里面,不然有一个Bug就是:点击两次,不仅会加速,而且会启动两次定时器) scheduleUpdate(); return true; } bool SceneStart::ccTouchBegan(CCTouch* touch, CCEvent*) { //获取触摸位置 CCPoint pt = touch->getLocation(); //判断是否触摸到 黑白按钮 if (_red->boundingBox().containsPoint(pt)) { _bred = true; } else if (_black->boundingBox().containsPoint(pt)) { _bred = false; } else return 0; //点中棋子后的效果 CCMoveTo* move1 = CCMoveTo::create(0.5, ccp(winSize.width / 2 + 10, winSize.height / 2)); CCMoveTo* move2 = CCMoveTo::create(0.5, ccp(winSize.width / 2, winSize.height / 2)); CCRotateBy* rotate1 = CCRotateBy::create(2, 360); CCRotateBy* rotate2 = CCRotateBy::create(2, -360); _red->runAction(CCSpawn::createWithTwoActions(move1,rotate1)); _black->runAction(CCSpawn::createWithTwoActions(move2, rotate2)); return true; } void SceneStart::update(float dt) { //要对两个棋子进行碰撞检测 _red->getContentSize().width 是图片的宽度 //if (_red->boundingBox().intersectsRect(_black->boundingBox()))//判断矩形是否接触 intersectsRect if (_red->getPositionX() - _black->getPositionX() > 0 ) { Game::_redStart = _bred; CCDirector::sharedDirector()->replaceScene(CreateScene(SceneGame::create())); } } SceneStart.h #ifndef __SCENESTART_H__ #define __SCENESTART_H__ #include "Common.h" class SceneStart :public CCLayer { private: CCSprite* _red;//红精灵 CCSprite* _black;//黑精灵 bool _bred;//哪一方走,红为true public: CREATE_FUNC(SceneStart); bool init(); bool ccTouchBegan(CCTouch*, CCEvent*); void update(float);//帧循环 }; #endif Step.cpp #include "Step.h" Step* Step::create(int moveid, int rowFrom, int colFrom, int rowTo, int colTo, int killid) { Step* step = new Step; step->_moveid = moveid; step->_rowFrom = rowFrom; step->_colFrom = colFrom; step->_rowTo = rowTo; step->_colTo = colTo; step->_killid = killid; step->autorelease(); return step; } Step.h #ifndef __STEP_H__ #define __STEP_H__ #include "Common.h" class Step :public CCObject { public: int _moveid; int _rowFrom; int _colFrom; int _rowTo; int _colTo; int _killid; static Step* create(int moveid, int rowFrom, int colFrom, int rowTo, int colTo, int killid); }; #endif Stone.cpp #include "Stone.h" #include "Game.h" CCPoint Stone::_ptOff = ccp(45, 25);//偏移值初始化 float Stone::_myWidth;// = winSize.height / 10;//棋子宽度初始化(千万注意。这里调用winSize会出问题。调用不了winSize,只能在函数里面调用了) Stone* Stone::create(int id) { Stone* stone = new Stone; stone->init(id); stone->autorelease(); return stone; } bool Stone::init(int id) { if (Game::_redStart) { _row = getRowByID(id); _col = getColByID(id); } else { _row = getRowByID(id); _col = getColByID(id); _row = 9 - _row; _col = 8 - _col; } _id = id; _type = getTypeByID(id); _red = id < 16; _dead = false; const char* bfile[] = { "bche.png", "bma.png", "bxiang.png", "bshi.png", "bjiang.png", "bpao.png", "bzu.png" }; const char* rfile[] = { "rche.png", "rma.png", "rxiang.png", "rshi.png", "rshuai.png", "rpao.png", "rbing.png" }; if (_red) CCSprite::initWithFile(rfile[_type]); else CCSprite::initWithFile(bfile[_type]); //棋子设置自己的位置 setPosition(getPos()); //发现棋子有点大了,改小一点 setScale(.9f); return true; } //获取类型。类型只有6种{ CHE, MA, XIANG, SHI, SHUAI, PAO, BING };也就是说数字只有 0 -- 5 STONE_TYPE Stone::getTypeByID(int id) { //每一个ID 对应一个位置,对应一个type STONE_TYPE type[32] = { CHE, MA, XIANG, SHI, SHUAI, SHI, XIANG, MA, CHE, PAO, PAO, BING, BING, BING, BING, BING, BING, BING, BING, BING, BING, PAO, PAO, CHE, MA, XIANG, SHI, SHUAI, SHI, XIANG, MA, CHE }; return type[id]; } //判断每个棋子处于哪一行 int Stone::getRowByID(int id) { int row[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; return row[id]; } //判断每个棋子处于哪一列 int Stone::getColByID(int id) { int col[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 7, 0, 2, 4, 6, 8, 0, 2, 4, 6, 8, 1, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8 }; return col[id]; } //获取现在棋子的位置(世界坐标) CCPoint Stone::getPos() { float x = _col*_myWidth; float y = _row*_myWidth; return ccp(x, y) + _ptOff;//暂时不知道为什么不直接给一个特定的值,非得定义静态成员函数,然后让Game来给值???要是后面没用到就删除静态 } void Stone::move(int row, int col) { _row = row; _col = col; //this->setPosition(getPos()); CCPoint pt = getPos(); this->setPosition(pt); } Stone.h #ifndef __STONE_H__ #define __STONE_H__ #include "Common.h" enum STONE_TYPE{ CHE, MA, XIANG, SHI, SHUAI, PAO, BING }; class Stone:public CCSprite { public: int _id;//每个棋子都有一个id bool _red;//红色还是黑色 STONE_TYPE _type;//类型 int _row;//在棋盘中的行 int _col;//在棋盘中的列 bool _dead;//棋子死没死 static CCPoint _ptOff; static float _myWidth;//棋子的宽度 //创建棋子,和初始化 static Stone* create(int id); bool init(int id); void move(int row, int col); void kill(); STONE_TYPE getTypeByID(int id); int getRowByID(int); int getColByID(int); CCPoint getPos(); }; #endif backMusic.cpp #include "backMusic.h" #include "SimpleAudioEngine.h" bool backMusic::init() { CCLayer::init(); CCMenu* menu = CCMenu::create(); addChild(menu); itemOn = CCMenuItemImage::create("OnMusic.png", "OffMusic.png"); itemOff = CCMenuItemImage::create("OffMusic.png", "OnMusic.png"); itemOn->setTarget(this, menu_selector(backMusic::musicOn)); itemOff->setTarget(this, menu_selector(backMusic::musicOff)); menu->addChild(itemOn); menu->addChild(itemOff); itemOn->setPosition(150, 180); itemOff->setPosition(150, 180); itemOff->setVisible(false); CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("backMusic.mp3", true); CocosDenshion::SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(1.0); return true; } void backMusic::musicOn(CCObject*) { //CocosDenshion::SimpleAudioEngine::sharedEngine()->stopBackgroundMusic(); CocosDenshion::SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); itemOff->setVisible(true); itemOn->setVisible(false); } void backMusic::musicOff(CCObject*) { //CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("backMusic.mp3", true); CocosDenshion::SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); itemOn->setVisible(true); itemOff->setVisible(false); } backMusic.h #ifndef __backMusic_H__ #define __backMusic_H__ #include "cocos2d.h" #include "cocos-ext.h" USING_NS_CC; USING_NS_CC_EXT; class backMusic :public CCLayer { CCMenuItem* itemOn; CCMenuItem* itemOff; public: CREATE_FUNC(backMusic); bool init(); void musicOff(CCObject*); void musicOn(CCObject*); }; #endif
    转载请注明原文地址: https://ju.6miu.com/read-1297750.html
    最新回复(0)