我的基于文本的游戏显示一帧后 我正在研究蛇的经典游戏,蛇在屏幕上到处吃东西,在C++中长时间。我在Youtube上看到了一个例子,但我决定让它更加面向对象,并且我正在使用ncurses(Linux中)使它更加精致
我一直在一个时间点编程,在这一点上,还没有设置食物。它只是一条蛇,当按下箭头键时开始移动,一旦碰到墙就会崩溃。让我无法继续下去的是,游戏似乎在蛇的实际位置后面显示了一个画面。当它移动时,我按下箭头键来改变方向,它在转向之前在同一方向上再移动一次。(我将帧速率调低到每秒一帧,以使这一点变得明显。)此外,如果我让它直接进入墙,它会从墙移动到第二个位置,然后触发一场游戏并退出。然而,如果我在第二个位置推动一个垂直方向,它会靠着墙向上移动,然后开始沿着墙移动。q(退出)按钮没有这种犹豫,一旦usleep功能完成,游戏就会退出。看起来输入函数和逻辑函数都像预期的那样工作,但是draw函数在某种程度上是滞后的,尽管它紧跟在其他两个函数之后 下面是代码,我将把我使用的命令放在最上面,以防任何使用Linux或类似环境的人想自己尝试。我这样做是为了在屏幕底部打印蛇的坐标,但它们与蛇的绘制位置相匹配。(例如,如果向左走,你可以看到x变为2,然后游戏结束,尽管它的程序很清楚,一旦蛇变为0,就可以杀死它,你可以通过转动它使它沿着第1列移动。)我花了两天的时间试图解决这个问题,即使使用gdb,我也一直在寻找其他我想要改变的东西,但我似乎无法理解这一点。请把我从这种疯狂中解救出来我的基于文本的游戏显示一帧后 我正在研究蛇的经典游戏,蛇在屏幕上到处吃东西,在C++中长时间。我在Youtube上看到了一个例子,但我决定让它更加面向对象,并且我正在使用ncurses(Linux中)使它更加精致,c++,ncurses,C++,Ncurses,我一直在一个时间点编程,在这一点上,还没有设置食物。它只是一条蛇,当按下箭头键时开始移动,一旦碰到墙就会崩溃。让我无法继续下去的是,游戏似乎在蛇的实际位置后面显示了一个画面。当它移动时,我按下箭头键来改变方向,它在转向之前在同一方向上再移动一次。(我将帧速率调低到每秒一帧,以使这一点变得明显。)此外,如果我让它直接进入墙,它会从墙移动到第二个位置,然后触发一场游戏并退出。然而,如果我在第二个位置推动一个垂直方向,它会靠着墙向上移动,然后开始沿着墙移动。q(退出)按钮没有这种犹豫,一旦usleep
g++-g snakes.cpp-lncurses-o snakes
// draw is one frame behind
#include <iostream>
#include <unistd.h>
#include <ncurses.h>
#define WIDTH 30
#define HEIGHT 30
#define MS_PER_FRAME 750
#define START_LENGTH 1
using namespace std;
bool gameOver;
enum eDirection { STOP, LEFT, RIGHT, UP, DOWN };
class Snake
{
public:
Snake(int i, int j);
bool isHead(int i, int j);
bool isTail(int i, int j);
void moveSnake();
bool collisionOccurred();
void drawSnake();
void setDirection(eDirection newDir);
private:
short int x, y, length;
short int tailx[20], taily[20];
eDirection dir;
} snake (WIDTH / 2, HEIGHT / 2);
Snake::Snake(int i, int j)
{
x = i;
y = j;
length = START_LENGTH;
dir = STOP;
}
bool Snake::isHead(int i, int j)
{
if (i == x && j == y)
return true;
else
return false;
}
bool Snake::isTail(int i, int j)
{
for (short int r = 0; r < length; r++)
if (i == tailx[r] && j == taily[r])
return true;
return false;
}
void Snake::moveSnake()
{
for (short int r = length; r >= 1; r--)
{
tailx[r] = tailx[r-1];
taily[r] = taily[r-1];
}
tailx[0] = x;
taily[0] = y;
switch (dir)
{
case LEFT:
x--;
break;
case RIGHT:
x++;
break;
case UP:
y--;
break;
case DOWN:
y++;
break;
}
}
bool Snake::collisionOccurred()
{
if (x == 0 || x == WIDTH+1)
return true;
if (y == 0 || y == HEIGHT+1)
return true;
for (int l = 0; l < length; l++)
if (dir != STOP && (x == tailx[l] && y == taily[l]))
return true;
return false;
}
void Snake::drawSnake()
{
mvaddch(y, x, 'O' | COLOR_PAIR(2));
for (int l = 0; l < length; l++)
mvaddch(taily[l], tailx[l], 'o' | COLOR_PAIR(2));
mvprintw(HEIGHT+2, 0, "Snake: %d, %d\n", x, y);
}
void Snake::setDirection(eDirection newDir)
{
dir = newDir;
}
void drawBorder()
{
for (int i = 0; i <= WIDTH+1; i++)
mvaddch(0, i, '#' | COLOR_PAIR(1));
for (int i = 0; i <= WIDTH+1; i++)
mvaddch(HEIGHT+1, i, '#' | COLOR_PAIR(1));
for (int i = 0; i <= WIDTH+1; i++)
mvaddch(i, 0, '#' | COLOR_PAIR(1));
for (int i = 0; i <= WIDTH+1; i++)
mvaddch(i, WIDTH+1, '#' | COLOR_PAIR(1));
}
void init()
{
gameOver = false;
initscr();
noecho();
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
curs_set(0);
start_color();
init_pair(1, COLOR_YELLOW, COLOR_BLUE); // border
init_pair(2, COLOR_GREEN, COLOR_BLACK); // snake
init_pair(3, COLOR_RED, COLOR_BLACK); // fruit
drawBorder();
}
void deinit()
{
endwin();
}
void draw()
{
for (int j = 1; j <= HEIGHT; j++)
{
move(j, 0);
for (int i = 1; i <= WIDTH; i++)
{
mvaddch(j, i, ' ');
}
}
snake.drawSnake();
refresh;
}
void input()
{
switch (getch())
{
case KEY_UP:
snake.setDirection(UP);
break;
case KEY_DOWN:
snake.setDirection(DOWN);
break;
case KEY_LEFT:
snake.setDirection(LEFT);
break;
case KEY_RIGHT:
snake.setDirection(RIGHT);
break;
case 'q':
gameOver = true;
break;
}
}
void logic()
{
if (!gameOver)
snake.moveSnake();
if (snake.collisionOccurred())
gameOver = true;
}
int main()
{
init();
draw();
while (!gameOver)
{
usleep(MS_PER_FRAME * 1000);
input();
logic();
draw();
}
deinit();
return 0;
}
//绘图落后一帧
#包括
#包括
#包括
#定义宽度30
#定义高度30
#每帧750定义MS_
#定义起始长度1
使用名称空间std;
布尔·加莫弗;
枚举eDirection{停止、左、右、上、下};
蛇类
{
公众:
Snake(inti,intj);
bool-ishad(inti,intj);
bool isTail(int i,int j);
void moveSnake();
布尔碰撞发生();
void drawSnake();
void setDirection(edidirection newDir);
私人:
短整数x,y,长度;
短int tailx[20],taily[20];
编辑主任;
}蛇(宽/2,高/2);
Snake::Snake(inti,intj)
{
x=i;
y=j;
长度=起始长度;
dir=停止;
}
布尔蛇::isHead(inti,intj)
{
如果(i==x&&j==y)
返回true;
其他的
返回false;
}
bool Snake::isTail(inti,intj)
{
for(短int r=0;r=1;r--)
{
tailx[r]=tailx[r-1];
taily[r]=taily[r-1];
}
tailx[0]=x;
taily[0]=y;
交换机(dir)
{
案例左:
x--;
打破
案例权利:
x++;
打破
个案:
y--;
打破
按大小写:
y++;
打破
}
}
bool Snake::碰撞发生()
{
如果(x==0 | | x==WIDTH+1)
返回true;
如果(y==0 | | y==高度+1)
返回true;
对于(int l=0;l 对于(inti=0;i噢,天哪,我终于想出了这个办法,我很惊讶编译器没有抓住它。关于getch进行刷新的响应引导我朝着正确的方向前进,尽管这本身并不是问题
简单明了,我忘记了绘图例程中刷新后的括号,所以当时它没有刷新。我不知道getch会进行刷新,尽管这并不重要,因为在几毫秒后重新计算蛇的位置后会再次进行刷新,如果是这样的话。但是,去掉括号意味着它没有刷新然后,只有格奇在蛇被移动之前,解释了为什么它看起来像是背后的一个框架
现在我只想知道为什么编译器没有在没有括号的情况下出错,以及刷新会做什么(如果有的话)。尝试将其解析为一个最小的可复制示例,您可能会找到您的答案。如果没有,我们将在这里。getch
与箭头键一起使用时可能会返回两个键码。这似乎不是问题我修改了它,所以getch将其返回值转换为int,然后基于该int调用switch语句。在这两者之间,它将在按下的键的蛇坐标下打印。它保持为-11(我假设错误)除非我按下一个键。箭头是258到261,只在一帧上显示,但在我按下它后,它们仍然会在两帧上显示。通常是在下一帧上显示-11,然后是相应的代码。这支持了我的想法,即在读取输入之前绘制屏幕,即使它不是这样编码的。getch代码>进行刷新。如果您使用的是snake的原始版本,则情况会有所不同,因为BSD curses没有在getch中进行刷新。