一个粗糙的二人贪吃蛇联机版实现
联机版贪吃蛇
经过六天的艰苦(抓狂)后,终于完成了一个简单的联机版贪吃蛇制作,我将和大家分享一下制作的流程与感触。
1.题目难点
贪吃蛇是一款很经典的游戏,也是一个难度系数尚可,适宜初学c++者进行挑战的一个项目。在此过程中,主要的难点有两个:
1.如何让蛇动起来
2.如何让两台机器可以随时保持数据的交换
2.基本思路
对于电脑来说,蛇是不可能一点点蠕动的。所以在我的思考中,蛇每移动一步,就等同于在蛇头前面增加一格,同时将尾部删除。食物的产生用srand()和rand()函数来完成(虽然在最后为了保证两台机器食物同步我暂时删除了srand())。至于控制蛇,我就用了电脑的wsad四键,结合_getch()函数完成控制。至于最后的联机部分我们稍后再说。
3.对象&头文件
我总共构建了四个对象
框架
食物
蛇节点
蛇
头文件很多,在此对几个特殊的进行介绍。
1.#include <cstdlib>&&#include<ctime> :产生随机数
2.#include<WinSock2.h>&&include <WS2tcpip.h> :联机所用
3.#include<conio.h> :打印和游戏处理所用
4.详解
(因为是初学者操作,为了操作方便,所有类内成员全部为public,类与类之间也全部为友远关系)
1.框架是在控制台打印的东西,也是游戏的界面,我用全局变量二维数组作为整体的框架(42*42),以下是他的基本组成
class Frame {public: friend class snakeNode; friend class snake; void makeframe();//画出游戏的边框 void printframe();//打印 };
2.食物是贪吃蛇中必不可少的东西,食物的组成也不算复杂,如下
class Food {public: friend class Frame; friend class snake; //static int i; int fx, fy;//记录事物的坐标 void randomFood();//产生随机食物 bool havefood();//判断食物是否被吃掉 void docter();//防止两个食物随机产生在同一位置 };
3.节点相对于说是一个类,其更像是一个结构,他是蛇整体的基本组成部分,相对于蛇来说更加具体形象。
class snakeNode {public: int x, y;//坐标 snakeNode(int ix, int iy, snakeNode* n, snakeNode*p, char snakeMood = '*') :x(ix), y(iy), next(n), prec(p) { window[ix][iy] = snakeMood; }//将节点在frame中表现出来 snakeNode *next, *prec;//用链表的结构来保存蛇 };
4.蛇是最庞大的类,其内的元素是对蛇的所有操作
class snake {public: string name; friend class Frame; friend class Food; char snakeMood;//蛇的样子 snakeNode* head = new snakeNode(20, 20, nullptr, nullptr, snakeMood); //为了方便所有蛇都默认从此点出发 void move();//游戏的关键 void addHead();//为move服务的函数 void detail();//为move服务的函数 enum Direction dir;//蛇运动的方向 bool block();//判断是否相撞 bool outofFrame(int h, int w);//判断是否出界 void changeDir1(char key);//键盘操作转换 };
下面对几个最重要的函数进行详解
1.move()
void snake::move() { this->addHead();//先增加一格子 if (outofFrame(head->x, head->y) || block()) { system("cls"); cout << "Game Over!" << endl; system("pause"); exit(0); } //因为是由单机版改来的所以这里还没有修改成判断哪一方获胜的函数。。。 if (!food1.havefood()/*吃了食物*/) { food1.randomFood(); } else if (!food2.havefood()/*吃了食物*/) { food2.randomFood(); } else/*没吃则将尾部删除*/ { detail(); } }
2.randomfood()
void Food::randomFood() { // srand((unsigned)time(0) * 1000000); bool onSnake = true; while (onSnake) { onSnake = false; fx = rand() % 40 + 1; fy = rand() % 40 + 1; if (window[fx][fy] == 'm') { onSnake = true; break;//若重复了再来 } //若在蛇身上,重来 for (snakeNode *snake = qqq.head; snake; snake = snake->next) { if (fx == snake->x && fy == snake->y) { onSnake = true; break; } } for (snakeNode *snake = sss.head; snake; snake = snake->next) { if (fx == snake->x && fy == snake->y) { onSnake = true; break; } } } window[fx][fy] = 'm'; } //PS:qqq和sss为两条蛇名
5.Tcp联机实现
针对socket内容,我只是停留在理解其用法的地步,现在正在看winsock一书,希望看完后能对其产生更为深刻的理解。
基本服务器和客户端的构建
//服务器 /*定义相关变量*/ char temp = 'd'; int sock_client; struct sockaddr_in server_addr; int addr_len = sizeof(struct sockaddr_in); /*初始化*/ WSADATA wsaDate; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaDate) != 0) { cout << "加载失败!\n"; return 0; } /*创建套接字*/ if ((sock_client = socket(AF_INET, SOCK_STREAM, 0))<0) { cout << "创建套接字失败! \n"; WSACleanup(); return 0; } /*填写服务器地址*/ char IP[20]; cout << "请输入服务器地址:"; cin >> IP; memset((void *)&server_addr, 0, addr_len); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); inet_pton(AF_INET, IP, &server_addr.sin_addr.s_addr); /*与服务器建立连接*/ if (connect(sock_client, (struct sockaddr*)&server_addr, addr_len) != 0) { cout << "连接失败,错误代码:" << WSAGetLastError() << endl; closesocket(sock_client); WSACleanup(); return 0; } //客户端 char temp1; SOCKET sock_server, newsock; struct sockaddr_in addr; struct sockaddr_in client_addr; char msg[] = "Connect succeed. \n"; /*初始化*/ WSADATA wsaDate; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaDate) != 0) { cout << "加载失败!\n"; return 0; } /*创建套接字*/ if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) { cout << "创建套接字失败! \n"; WSACleanup(); return 0; } /*添加本地地址*/ int addr_len = sizeof(struct sockaddr_in); memset((void *)&addr, 0, addr_len); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock_server, (struct sockaddr*)&addr, sizeof(addr)) != 0) { cout << "地址绑定失败,错误代码:" << WSAGetLastError() << endl; WSACleanup(); return 0; } //将套接字设置为监听状态 if (listen(sock_server, 0) != 0) { cout << "listen函数调用失败,错误代码:" << WSAGetLastError() << endl; closesocket(sock_server); WSACleanup(); return 0; } while (true) { if ((newsock = accept(sock_server,(struct sockaddr*)&client_addr, &addr_len)) == INVALID_SOCKET) { cout << "accept函数调用失败,错误代码:" << WSAGetLastError() << endl; } else { cout << "成功接收到一个连接请求;\n"; break; } } //部分代码来自于winsock编程一书改编而来
相对于socket的基本构造,如何交换数据才是重中之重。windows下的recv()(接受信息函数)是阻塞的(就是在没有信息发来时会一直卡在那里不动)。所以为了保证游戏的连贯与一致,游戏中数据的交换只有蛇的方向。我们要保证信息一直在不停的发送与接收中。在编码过程中,最耗时的部分就是实现如何保证两边总能接收到信息而不发生阻塞,最后我采用了一种规则:先发送再接受,这样根据数学逻辑来讲,在缓存区中存放的数据中的send是大于等于recv的,这样就可以保证总有一端可以收到信息,而在其收到后又会再极短的时间内发出信息,这样自然能保证游戏的流畅。
system("mode con cols=100 lines=50"); Frame frame; char key; char sended[1000], geted[1000]; frame.makeframe(); food1.randomFood(); food2.randomFood(); cout << "请输入sss的方向:"; cin >> key; sended[0] = key; sss.changeDir1(key); qqq.snakeMood = '*'; sss.snakeMood = 'o'; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 0, pos.Y = 0; cout << "等待链接…" << endl; send(sock_client, sended, sizeof(sended), 0); while (true) { int size = recv(sock_client, geted, sizeof(geted), 0); if (size > 0)break; } char key1 = geted[0]; cout << key1; qqq.changeDir1(key1); while (true) { if (sss.dir != NONE&&qqq.dir != NONE) { break; } }//通过此手段,给蛇初始方向赋值,同时也能保证两台机器上游戏时间的同步 cout << "链接成功…" << "\n"; cout << "请调至英文输入进行游戏" << "\n"; cout << "游戏将在三秒后开始"; Sleep(3000); system("cls");//清屏函数 //在这里加入判定条件两边都有dir,用whiletrue循环检验是否有dir,若有则开始游戏 while (true) { while (_kbhit()) { key = _getch(); sss.changeDir1(key); sended[0] = key; send(sock_client, sended, sizeof(sended), 0); recv(sock_client, geted, sizeof(geted), 0); char key1 = geted[0]; qqq.changeDir1(key1); qqq.move(); sss.move(); determination(); food1.docter(); SetConsoleCursorPosition(hOut, pos); HideCursor(); frame.printframe(); Sleep(300); break; } while (!_kbhit()) { send(sock_client, sended, sizeof(sended), 0); recv(sock_client, geted, sizeof(geted), 0); char key1 = geted[0]; qqq.changeDir1(key1); qqq.move(); sss.move(); determination(); food1.docter(); SetConsoleCursorPosition(hOut, pos); HideCursor(); frame.printframe(); Sleep(300); } }
可以从代码中看出,其实每台机器都是独立的,他们只是同时开始,开始方向相同,每一方的玩家控制一条蛇的dir,仅此而已。这也是现在很多大型游戏所采用的方法,将大部分内容本地运行,只将玩家之间的交互数据上传到服务器上,从而使得网速要求没有那么苛刻。
6.全部代码
服务端
#include<iostream> #include<ctime> #include<conio.h> #include <cstdlib> #include<WinSock2.h> #define PORT 65432 #pragma comment(lib,"ws2_32.lib") using namespace std; enum Direction { NONE,UP, DOWN, LEFT, RIGHT}; char window[42][42]; class Frame {public: friend class snakeNode; friend class snake; void makeframe(); void printframe(); }frame; void Frame::makeframe() { for (int i = 0; i <= 41; i++) { for (int j = 0; j <= 41; j++) window[i][j] = ' '; } for (int i = 0; i <= 41; i++) { window[i][0] = '#'; window[i][41] = '#'; } for (int i = 0; i <= 41; i++) { window[0][i] = '#'; window[41][i] = '#'; } } void Frame::printframe() { for (int i = 0; i <= 41; i++) { for (int j = 0; j <= 41; j++) cout << window[i][j] << ' '; cout << endl; } } class snakeNode {public: int x, y; snakeNode(int ix, int iy, snakeNode* n, snakeNode*p, char snakeMood = '*') :x(ix), y(iy), next(n), prec(p) { window[ix][iy] = snakeMood; } snakeNode *next, *prec; }; class snake {public: string name; friend class Frame; friend class Food; char snakeMood; snakeNode* head = new snakeNode(20, 20, nullptr, nullptr, snakeMood); void move(); void addHead(); void detail(); enum Direction dir; bool block(); bool outofFrame(int h, int w); void changeDir1(char key); }; snake qqq; snake sss; class Food {public: friend class Frame; friend class snake; //static int i; int fx, fy; void randomFood(); bool havefood(); void docter(); }; Food food1, food2; void Food::docter() { if (food1.fx == food2.fx&&food2.fx == food1.fx) { food1.randomFood(); } } bool Food::havefood() { if (window[fx][fy] == 'm')return true; return false; } void Food::randomFood() {// srand((unsigned)time(0) * 1000000); bool onSnake = true; while (onSnake) { onSnake = false; fx = rand() % 40 + 1; fy = rand() % 40 + 1; if (window[fx][fy] == 'm') { onSnake = true; break; } for (snakeNode *snake = qqq.head; snake; snake = snake->next) { if (fx == snake->x && fy == snake->y) { onSnake = true; break; } } for (snakeNode *snake = sss.head; snake; snake = snake->next) { if (fx == snake->x && fy == snake->y) { onSnake = true; break; } } } window[fx][fy] = 'm'; } void snake::changeDir1(char key) { switch (key) { case 'w':if (dir != DOWN)dir = UP; break; case's':if (dir != UP)dir = DOWN; break; case 'a':if (dir != RIGHT)dir = LEFT; break; case 'd':if (dir != LEFT)dir = RIGHT; break; case'z':system("pause"); break; default: break; } } bool snake::outofFrame(int h, int w) { return h < 41 && h > 0 && w < 41 && w > 0 ? false : true; } bool snake::block() { for (snakeNode* snake1 = head; snake1; snake1 = snake1->next) { for (snakeNode* snake2 = (snake1->next); snake2; snake2 = snake2->next) { if (snake1->x == snake2->x&&snake1->y == snake2->y)return true; } } return false; } void snake::addHead() { int x1 = head->x; int y1 = head->y; switch (dir) { case UP:x1--; break; case DOWN:x1++; break; case LEFT:y1--; break; case RIGHT:y1++; break; default: break; } snakeNode* head1 = new snakeNode(x1, y1, head, nullptr, snakeMood); head->prec = head1; head = head1; } void snake::detail() { snakeNode* node = head; for (; node; node = node->next) { if (node->next == nullptr) { window[node->x][node->y] = ' '; node->prec->next = nullptr; } } } void snake::move() { this->addHead(); //若_getch还没触发一次操作是再嗯下一次getch会导致蛇回头从而gameover if (outofFrame(head->x, head->y) || block()) { system("cls"); cout << "Game Over!" << endl; system("pause"); exit(0); } if (!food1.havefood()) { food1.randomFood(); } else if (!food2.havefood()) { food2.randomFood(); } else { detail(); } } void HideCursor() { CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); } bool qqqwin() { for (snakeNode* snake1 = qqq.head; snake1->next; snake1 = snake1->next) { if (snake1->next->x == sss.head->x&&snake1->next->y == sss.head->y)return true; } return false; } bool ssswin() { for (snakeNode* snake1 = sss.head; snake1->next; snake1 = snake1->next) { if (snake1->next->x == qqq.head->x&&snake1->next->y == qqq.head->y)return true; } return false; } bool equal() { if (qqq.head->x == sss.head->x&&qqq.head->y == sss.head->y)return true; return false; } void determination() { if (qqqwin()) { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 40, pos.Y = 20; SetConsoleCursorPosition(hOut, pos); cout << "qqq win" << endl; system("pause"); exit(0); } if (ssswin()) { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 40, pos.Y = 20; SetConsoleCursorPosition(hOut, pos); cout << "sss win" << endl; system("pause"); exit(0); } if (equal()) { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 40, pos.Y = 20; SetConsoleCursorPosition(hOut, pos); cout << "Draw" << endl; system("pause"); exit(0); } } int main() { /*定义相关变量*/ char temp1; SOCKET sock_server, newsock; struct sockaddr_in addr; struct sockaddr_in client_addr; char msg[] = "Connect succeed. \n"; /*初始化*/ WSADATA wsaDate; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaDate) != 0) { cout << "加载失败!\n"; return 0; } /*创建套接字*/ if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) { cout << "创建套接字失败! \n"; WSACleanup(); return 0; } /*添加本地地址*/ int addr_len = sizeof(struct sockaddr_in); memset((void *)&addr, 0, addr_len); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock_server, (struct sockaddr*)&addr, sizeof(addr)) != 0) { cout << "地址绑定失败,错误代码:" << WSAGetLastError() << endl; WSACleanup(); return 0; } //将套接字设置为监听状态 if (listen(sock_server, 0) != 0) { cout << "listen函数调用失败,错误代码:" << WSAGetLastError() << endl; closesocket(sock_server); WSACleanup(); return 0; } while (true) { if ((newsock = accept(sock_server,(struct sockaddr*)&client_addr, &addr_len)) == INVALID_SOCKET) { cout << "accept函数调用失败,错误代码:" << WSAGetLastError() << endl; } else { cout << "成功接收到一个连接请求;\n"; break; } } system("mode con cols=100 lines=50"); Frame frame; char sended[1000], geted[1000]; frame.makeframe(); food1.randomFood(); food2.randomFood(); cout << "请输入qqq的方向:"; cin >> temp1; qqq.changeDir1(temp1); sended[0] = temp1; qqq.snakeMood = '*'; sss.snakeMood = 'o'; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 0, pos.Y = 0; cout << "等待链接…" << endl; send(newsock, sended, sizeof(sended), 0); while (true) { int size = recv(newsock, geted, sizeof(geted), 0); if (size > 0)break; } char key1 = geted[0]; sss.changeDir1(key1); while (true) { if (sss.dir != NONE&&qqq.dir != NONE) { break; } } cout << "链接成功…" << "\n"; cout << "请调至英文输入进行游戏" << "\n"; cout << "游戏将在五秒后开始"; Sleep(3000); system("cls"); //在这里加入判定条件两边都有dir,用whiletrue循环检验是否有dir,若有则开始游戏 while (true) { while (_kbhit()) { temp1 = _getch(); qqq.changeDir1(temp1); sended[0] = temp1; send(newsock, sended, sizeof(sended), 0); recv(newsock, geted, sizeof(geted), 0); char key1 = geted[0]; sss.changeDir1(key1); qqq.move(); sss.move(); determination(); food1.docter(); SetConsoleCursorPosition(hOut, pos); HideCursor(); frame.printframe(); Sleep(300); break; } while (!_kbhit()) { // key = _getch(); // sended[0] = key; // send(newsock, sended, sizeof(sended), 0); //qqq.changeDir1(key); send(newsock, sended, sizeof(sended), 0); recv(newsock, geted, sizeof(geted), 0); char key1 = geted[0]; sss.changeDir1(key1); qqq.move(); sss.move(); determination(); food1.docter(); SetConsoleCursorPosition(hOut, pos); HideCursor(); frame.printframe(); Sleep(300); } //连续getch掉头的问题还没解决= = } return 0; }
客户端
#include<iostream> #include<ctime> #include<conio.h> #include <cstdlib> #include<WinSock2.h> #include <WS2tcpip.h> #define PORT 65432 #pragma comment(lib,"ws2_32.lib") using namespace std; enum Direction { NONE, UP, DOWN, LEFT, RIGHT }; char window[42][42]; class Frame {public: friend class snakeNode; friend class snake; void makeframe(); void printframe(); }frame; void Frame::makeframe() { for (int i = 0; i <= 41; i++) { for (int j = 0; j <= 41; j++) window[i][j] = ' '; } for (int i = 0; i <= 41; i++) { window[i][0] = '#'; window[i][41] = '#'; } for (int i = 0; i <= 41; i++) { window[0][i] = '#'; window[41][i] = '#'; } } void Frame::printframe() { for (int i = 0; i <= 41; i++) { for (int j = 0; j <= 41; j++) cout << window[i][j] << ' '; cout << endl; } } class snakeNode {public: int x, y; snakeNode(int ix, int iy, snakeNode* n, snakeNode*p, char snakeMood = '*') :x(ix), y(iy), next(n), prec(p) { window[ix][iy] = snakeMood; } snakeNode *next, *prec; }; class snake {public: string name; friend class Frame; friend class Food; char snakeMood; snakeNode* head = new snakeNode(20, 20, nullptr, nullptr, snakeMood); void move(); void addHead(); void detail(); enum Direction dir; bool block(); bool outofFrame(int h, int w); void changeDir1(char key); }; snake qqq; snake sss; class Food {public: friend class Frame; friend class snake; //static int i; int fx, fy; void randomFood(); bool havefood(); void docter(); }; Food food1, food2; void Food::docter() { if (food1.fx == food2.fx&&food2.fx == food1.fx) { food1.randomFood(); } } bool Food::havefood() { if (window[fx][fy] == 'm')return true; return false; } void Food::randomFood() { // srand((unsigned)time(0) * 1000000); bool onSnake = true; while (onSnake) { onSnake = false; fx = rand() % 40 + 1; fy = rand() % 40 + 1; if (window[fx][fy] == 'm') { onSnake = true; break; } for (snakeNode *snake = qqq.head; snake; snake = snake->next) { if (fx == snake->x && fy == snake->y) { onSnake = true; break; } } for (snakeNode *snake = sss.head; snake; snake = snake->next) { if (fx == snake->x && fy == snake->y) { onSnake = true; break; } } } window[fx][fy] = 'm'; } void snake::changeDir1(char key) { switch (key) { case 'w':if (dir != DOWN)dir = UP; break; case's':if (dir != UP)dir = DOWN; break; case 'a':if (dir != RIGHT)dir = LEFT; break; case 'd':if (dir != LEFT)dir = RIGHT; break; case'z':system("pause"); break; default: break; } } bool snake::outofFrame(int h, int w) { return h < 41 && h > 0 && w < 41 && w > 0 ? false : true; } bool snake::block() { for (snakeNode* snake1 = head; snake1; snake1 = snake1->next) { for (snakeNode* snake2 = (snake1->next); snake2; snake2 = snake2->next) { if (snake1->x == snake2->x&&snake1->y == snake2->y)return true; } } return false; } void snake::addHead() { int x1 = head->x; int y1 = head->y; switch (dir) { case UP:x1--; break; case DOWN:x1++; break; case LEFT:y1--; break; case RIGHT:y1++; break; default: break; } snakeNode* head1 = new snakeNode(x1, y1, head, nullptr, snakeMood); head->prec = head1; head = head1; } void snake::detail() { snakeNode* node = head; for (; node; node = node->next) { if (node->next == nullptr) { window[node->x][node->y] = ' '; node->prec->next = nullptr; } } } void snake::move() { this->addHead(); //若_getch还没触发一次操作是再嗯下一次getch会导致蛇回头从而gameover if (outofFrame(head->x, head->y) || block()) { system("cls"); cout << "Game Over!" << endl; system("pause"); exit(0); } if (!food1.havefood()) { food1.randomFood(); } else if (!food2.havefood()) { food2.randomFood(); } else { detail(); } } void HideCursor() { CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); } bool qqqwin() {for (snakeNode* snake1 = qqq.head; snake1->next; snake1 = snake1->next) {if (snake1->next->x == sss.head->x&&snake1->next->y == sss.head->y)return true; } return false; } bool ssswin() {for (snakeNode* snake1 = sss.head; snake1->next; snake1 = snake1->next) {if (snake1->next->x == qqq.head->x&&snake1->next->y == qqq.head->y)return true; } return false; } bool equal() {if (qqq.head->x == sss.head->x&&qqq.head->y == sss.head->y)return true; return false; } void determination() {if (qqqwin()) {HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 40, pos.Y = 20; SetConsoleCursorPosition(hOut, pos); cout << "qqq win" << endl; system("pause"); exit(0); } if (ssswin()) {HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 40, pos.Y = 20; SetConsoleCursorPosition(hOut, pos); cout << "sss win" << endl; system("pause"); exit(0); } if (equal()) {HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 40, pos.Y = 20; SetConsoleCursorPosition(hOut, pos); cout << "Draw" << endl; system("pause"); exit(0); } } int main() { /*定义相关变量*/ int sock_client; struct sockaddr_in server_addr; int addr_len = sizeof(struct sockaddr_in); /*初始化*/ WSADATA wsaDate; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaDate) != 0) { cout << "加载失败!\n"; return 0; } /*创建套接字*/ if ((sock_client = socket(AF_INET, SOCK_STREAM, 0))<0) { cout << "创建套接字失败! \n"; WSACleanup(); return 0; } /*填写服务器地址*/ char IP[20]; cout << "请输入服务器地址:"; cin >> IP; memset((void *)&server_addr, 0, addr_len); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); inet_pton(AF_INET, IP, &server_addr.sin_addr.s_addr); /*与服务器建立连接*/ if (connect(sock_client, (struct sockaddr*)&server_addr, addr_len) != 0) { cout << "连接失败,错误代码:" << WSAGetLastError() << endl; closesocket(sock_client); WSACleanup(); return 0; } system("mode con cols=100 lines=50"); Frame frame; char key; char sended[1000], geted[1000]; frame.makeframe(); food1.randomFood(); food2.randomFood(); cout << "请输入sss的方向:"; cin >> key; sended[0] = key; sss.changeDir1(key); qqq.snakeMood = '*'; sss.snakeMood = 'o'; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = 0, pos.Y = 0; cout << "等待链接…" << endl; send(sock_client, sended, sizeof(sended), 0); while (true) { int size = recv(sock_client, geted, sizeof(geted), 0); if (size > 0)break; } char key1 = geted[0]; cout << key1; qqq.changeDir1(key1); while (true) { if (sss.dir != NONE&&qqq.dir != NONE) { break; } } cout << "链接成功…" << "\n"; cout << "请调至英文输入进行游戏" << "\n"; cout << "游戏将在五秒后开始"; Sleep(3000); system("cls"); //在这里加入判定条件两边都有dir,用whiletrue循环检验是否有dir,若有则开始游戏 while (true) { while (_kbhit()) { key = _getch(); sss.changeDir1(key); sended[0] = key; send(sock_client, sended, sizeof(sended), 0); recv(sock_client, geted, sizeof(geted), 0); char key1 = geted[0]; qqq.changeDir1(key1); qqq.move(); sss.move(); determination(); food1.docter(); SetConsoleCursorPosition(hOut, pos); HideCursor(); frame.printframe(); Sleep(300); break; } while (!_kbhit()) { send(sock_client, sended, sizeof(sended), 0); recv(sock_client, geted, sizeof(geted), 0); char key1 = geted[0]; qqq.changeDir1(key1); qqq.move(); sss.move(); determination(); food1.docter(); SetConsoleCursorPosition(hOut, pos); HideCursor(); frame.printframe(); Sleep(300); } //连续getch掉头的问题还没解决= = } return 0; }
总结
毕竟初学。。。可能很多地方都是在意思路多于在意代码,在其中还有很多定义了却未使用,或者重复定义的地方
在制作过程中也参考了网上的一些函数的源码,他们使得我思路更清晰明了
最近学业可能十分繁忙,没有太多时间进行代码的重编译和修改
不过在以后空闲时间,我一定会将其修改完成,顺便尝试将服务器分离,使得游戏变成多人而非二人模式
一个粗糙的二人贪吃蛇联机版实现相关推荐
- Qt基于tcp网络服务器的简易版多人贪吃蛇小游戏(多线程)
文章目录 前言 一.项目的简单介绍 二.总体步骤 1.服务器端 2 客户端 总结 前言 花费一周的时间,搭建了一个自己的破烂服务器,以此记录我那逝去的时间 一.项目的简单介绍 这是一个多人贪吃蛇游戏, ...
- 实验二、贪吃蛇的游戏开发
实验二.贪吃蛇的游戏开发 1.实验目的: 开发一个贪吃蛇游戏,吃到食物蛇身增长,蛇头撞到自身和四周墙壁死亡. 2.实验工具 通过Java运行输出. 3.实验内容 预习: 实验要求:实现贪吃蛇游戏基本功 ...
- JAVA小程序:一个基于MVC框架的贪吃蛇程序
学习JAVA也有一段时间了,之前看了翁恺老师的视频,跟着做了一个细胞自动机,粗浅地了解了一点MVC框架的知识,感觉获益匪浅.但是细胞自动机毕竟是跟着视频完成的,有很大程度上都是参考了视频里的代码,没有 ...
- 简单贪吃蛇c语言代码,一个C语言写简单贪吃蛇源代码.doc
一个C语言写简单贪吃蛇源代码 #include #include #include #include #include #include int grade=5,point=0,life=3; voi ...
- 用C语言制作一款多人贪吃蛇游戏
上学期学的C语言,寒假想用来做点好玩的东西而不是单纯应付考试,刚开始想到了贪吃蛇,最后做成了双贪吃蛇,双龙夺猪讲的是随着猪肉价格渐长,一条龙与一条蛇争夺猪肉的故事233333 代码使用了easyx图形 ...
- 汇编语言贪吃蛇、俄罗斯方块双任务设计实现详解(二)——贪吃蛇详细设计
详细设计: 2.贪吃蛇详细设计: 如下图所示左下角为主程序对贪吃蛇相关子程序的调用,程序中init_left_window子程序功能为将左侧窗口非边框部分置为空.左边上侧和右侧是贪吃蛇段中,贪吃蛇程序 ...
- 贪吃蛇简易版(C++)
导航 下一篇:贪吃蛇升级版(C++) 目录 一. 贪吃蛇简易版的实现 1. 贪吃蛇如何存储? 2. 贪吃蛇如何移动? 3. 食物如何生成? 4. 如何判断游戏结束? 二. 贪吃蛇简易版的优化 1. 添 ...
- laya游戏开发之贪吃蛇大作战(二)—— 贪吃蛇客户端
文章目录 一 功能分析 二 实现方案 1. 代码结构 2. 关键函数实现 2.1 游戏主循环(GameLoop) 2.2 数据层(Model) 2.3 画面绘制层(View) 帧同步的困难与解决方法 ...
- 实验项目二:贪吃蛇游戏开发
贪吃蛇游戏开发 一.前言 二.基本流程 三.游戏界面设计 1. 界面构造 2. 蛇的构造 3. 食物构造 四.游戏过程 1. 蛇的移动 2. 蛇吃食物与碰撞检测 五.游戏结束 1. 结束界面 六.扩展 ...
最新文章
- consul 日志配置_微服务:服务注册发现+ API 网关+配置中心+服务跟踪+服务熔断...
- php 计算字符串相邻最大重复数_php如何解决字符串中重复字符的次数并且排序输出的方法...
- Feign客户端 - 超时时间配置
- 459. Repeated Substring Pattern 重复的子字符串
- [转] 一次Ajax查错的经历
- 腾讯正式开源图计算框架Plato,十亿级节点图计算进入分钟级时代
- leetcode 796. Rotate String | 796. 旋转字符串(KMP)
- Navicat Premium 怎么安装比较快
- 3招seo技巧让你把关键词做进百度前三
- Java核心(一)线程Thread详解
- VS 编译选项如何设置以及对性能的影响1
- 神经网络中常用激活函数总结【Python实现激活函数与导函数,曲线可视化分析】
- 【物流选址】基于matlab粒子群算法求解多物流中心选址问题【含Matlab源码 1458期】
- 函数 tostring_QDate、QTime、QDateTime的相关函数说明
- php 查询逗号分隔字符串,PHP-在逗号分隔的字符串mysql中查找值
- 基于bim的施工管理平台
- 一个简易的QQ魔法卡片炼卡消耗计算器
- java blocked_Java 线程状态之 BLOCKED
- 东芝打印机共享怎么设置_东芝打印机如何共享
- DNS A记录 NS记录 CNAME记录 TXT记录 TTL值
热门文章
- PowerBI基础——第二天 最强大的引擎calculate、筛选表calculatetable、高级筛选器filter
- 用数据科学研究GameFi
- 【UDS统一诊断服务】四、诊断典型服务(5)— 功能/元件测试功能单元(例行程序功能单元0x31)
- FFMPEG 讲webm 转成MP4
- ssdb写入mysql_SSDB数据库笔记
- Microsoft Excel for Mac v2021 16.45 – 电子表格数据分析工具
- win10 下go+goland安装及环境搭建
- 2023年最新软著申请流程(一):软件著作权说明、国家版权官网的账号注册与实名认证
- 程序员的睡后收入之路 ---- 针对个人非团队
- linux如何同时执行两个命令,如何同时运行两个或者多个终端命令