我的文字游戏在后面显示了一帧

问题描述

我正在研究经典的蛇游戏,其中蛇绕着屏幕进食,并且在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是否会刷新,尽管这并不重要,因为如果在几毫秒后重新计算了蛇的位置,它将再次执行。但是,省略括号就意味着它并没有刷新,只是在蛇被移动之前通过抓地力,解释了为什么它看起来落后一帧。

现在,我只想知道为什么编译器没有为此出错,以及没有括号的情况下进行了什么刷新(如果有的话)。