问题描述
我正在研究经典的蛇游戏,其中蛇绕着屏幕进食,并且在C ++中变得越来越长。我看到了一个在Youtube上制作的示例,但是我决定要使其更加面向对象,而且我正在使用ncurses(在Linux中)使其更加精致。
我一次编程了一段时间,此时,还没有准备好食物。它只是一条蛇,当按下箭头键时会开始移动,一旦撞到墙壁就会崩溃。让我无法继续的是,游戏似乎在蛇实际所在的位置后面显示了一个帧。当它移动时,我按箭头键更改方向时,在转向之前,它又朝同一方向移动了一次。 (我将帧速率调低为每秒一帧,以使其变得明显。)此外,如果我让它直接进入墙壁,它会从墙壁移至第二位置,然后触发游戏并退出。但是,如果我在第二个位置推动垂直方向,它将向上靠墙向上移动,然后开始沿着它移动。 q(退出)按钮没有这种犹豫,一旦完成睡眠功能,就会导致游戏退出。输入和逻辑功能似乎按预期方式工作,但是绘制功能在某种程度上滞后,即使它紧随其他两个。
这是代码,如果使用Linux或类似环境的任何人都想自己尝试一下,则将我使用的命令放在顶部。我这样做是为了在屏幕底部打印蛇的坐标,但它们与蛇的绘制位置匹配。 (例如,如果向左移动,则可以看到x变为2,然后游戏结束,即使它已明确编程为杀死到达0的蛇,也可以通过旋转使其沿第1列移动。 )即使是使用gdb,我也一直在努力解决这一问题,但我一直在寻找其他我想更改的东西,但似乎无法解决这一问题。请让我摆脱这种疯狂。
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,"Snake: %d,%d\n",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,'#' | 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,' ');
}
}
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;
}
解决方法
哦,我终于弄明白了这一点,我很惊讶编译器没有抓住它。关于getch进行刷新的响应将我引向了正确的方向,尽管这本身并不是问题。
简单明了,在绘制例程中刷新后我忘记了括号,因此当时它没有刷新。我不知道getch是否会刷新,尽管这并不重要,因为如果在几毫秒后重新计算了蛇的位置,它将再次执行。但是,省略括号就意味着它并没有刷新,只是在蛇被移动之前通过抓地力,解释了为什么它看起来落后一帧。
现在,我只想知道为什么编译器没有为此出错,以及没有括号的情况下进行了什么刷新(如果有的话)。