上篇记录了main函数、game类和food类的设计,本片继续记录Snake类的设计。
四、Snake类的设计
同样从Game类中对Snake类的需求出发,Snake类用一个deque保存一系列点,每次循环需要根据其方向完成点的更新、方向更新、食物是否与蛇身重合等等一些列功能:
#pragma once
#include <deque>
#include "Point.h"
#include <conio.h>
class Snake
{
public:
Snake(initializer_list<Point> li = { {20,20},{20,21},{20,22} }, char c = 'd') :body(li), direction(c) { bodysize = body.size(); };
void paintbody(HANDLE);
bool hititself()
{
Point head = body.front();
for (auto k = --body.end(); k != (body.begin() + 2); k--)
if (head == *k)
return true;
return false;
}
bool overlap(const Point &p)//判断刷新的食物是否与蛇身重合
{
for (auto k = --body.end(); k != (body.begin() ); k--)
if (p == *k)
return true;
return false;
}
bool senddata(int &sock);
bool recvdata(int &sock);
void painthead(HANDLE hand)
{
body.front().paint(hand);
}
void tailclear(HANDLE hand)
{
body.back().clear(hand);
body.pop_back();
}
const Point& Head()
{
return body.front();
}
bool changedirection(char &k);
void update();
void reset();
bool press(char &k)
{
return k == direction;
}
private:
deque<Point> body;
char direction;
size_t bodysize;
};
inline
bool Snake::changedirection(char &k)
{
if (k == 'A' || k == 'S' || k == 'W' || k == 'D')
k = tolower(k);
if (k == 'a' || k == 's' || k == 'w' || k == 'd') {
if ((k + direction) == 234 || (k + direction) == 197 || k == direction)
return false;
direction = k;
return true;
}
return false;
}
inline
void Snake::update()
{
switch (direction) {
case 'w':
body.emplace_front(body.front().xx(), body.front().yy() - 1);
break;
case 's':
body.emplace_front(body.front().xx(), body.front().yy() + 1);
break;
case 'a':
body.emplace_front(body.front().xx() - 2, body.front().yy());
break;
case 'd':
body.emplace_front(body.front().xx() + 2, body.front().yy());
break;
}
}
蛇身绘制方面,因为整个程序中food的绘制颜色及符号与蛇身一致,因此在吃上食物后不需进行任何操作,而未吃上食物则只需在头部多绘制一个点并抹掉尾部的点即可。因为需要在Game中判定是否撞墙,因此定义一个返回头部数据的接口;reset完成数据的重置。
###五、Point类的设计
point类基本上就是一个pair加上了几个接口,需要能够完成point的绘制及清除、数据的返回即可:
#pragma once
#include <Windows.h>
#include <iostream>
using namespace std;
class Point
{
public:
Point(int a=0,int b=0):x(a),y(b){}
void paint(HANDLE hand)
{
SetOutputposition(x, y, hand);
cout << "■";
}
void clear(HANDLE hand)
{
SetOutputposition(x, y, hand);
cout << " ";
}
int& xx() {
return x;
}
const int& xx() const
{
return x;
}
int& yy() {
return y;
}
const int &yy() const
{
return y;
}
bool operator==(const Point& point) const
{
if (x == point.x&&y == point.y)
return true;
else
return false;
}
private:
int x;
int y;
};
###六、公用数据和函数
static HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
static bool chekans(const int &sock)
{
char buff[4];
int n;
while ((n = recv(sock, buff, 4, 0)) < 0)//可将此处单独顶一个为一个Recv函数代替原函数
{
if (errno == EINTR)
continue;
else
return false;
}
buff[n] = 0;
return (strcmp("LWB", buff) ? false : true);
}
static void SetOutputposition(int x, int y, HANDLE hand)
{
COORD position;
position.X = x;
position.Y = y;
SetConsoleCursorPosition(hand, position);
}
static int n;
chekanks用于check服务器在成功接受数据后返回的消息,SetOutputposition则用于设置光标位置,这个函数在程序中使用很多,主要是对SetConsoleCursorPosition进行了封装。n则用于一些函数的返回值检测。