Змея в C ++ не станет дважды подряд - PullRequest
0 голосов
/ 23 октября 2018

Я только недавно начал изучать C ++.Я решил запрограммировать небольшую игру Snake, которая запускается в консоли.Он относительно простой и не выглядит потрясающе, но он делает то, что должен.
Единственная проблема, с которой я столкнулся, это то, что моя Снейк не поворачивается дважды подряд.Другими словами, вы не можете делать крутые развороты с этим.Однако он повернется сразу после нажатия кнопки.(Если только вы не включили это).
Мой код имеет длину 120 строк, поэтому вот оно:

Первые мои включения и пространство имен:

#include <iostream>
#include <vector>
#include <conio.h>
#include <windows.h>
#include <random>

using namespace std;

Эта функция рисует все полев консоли:

void drawGrid(vector<vector<char>> &g, int height, int width, int score, int time)
{
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), { 0,0 });
    for (int r = 0; r < height; r++)
    {
        for (int c = 0; c < width; c++) std::cout << g[c][r] << " ";
        std::cout << '|' << endl;
    }
    std::cout << "Current score: " << score << "     ";
    std::cout << "\nCurrent speed: " << time << "      ";
}

Эта функция проверяет, находится ли еда под змеей:

bool foodSnake(vector<int> &f, vector<vector<int>> &t, int l)
{
    for (int i = 0; i < l; i++) 
        if (f[0] == t[i][0] && f[1] == t[i][1]) return true;
    return false;
}

А это большая пооба:

int main(void)
{
    int sleeptime = 1000;    // how long the break is between each frame
    bool foodExists = 0;
    int width = 20;    //width and height of the field
    int height = 15;
    mt19937_64 engine;    //random distributions for food generation
    uniform_int_distribution<int> heightDist(0, height - 1);
    uniform_int_distribution<int> widthDist(0, width - 1);
    int tailLengthstart = 4;
    int tailLength = tailLengthstart;
    char movementDirection = 'u';    //starts moving upwards
    char input;
    bool alive = 1;   //keeps the program running on death = 0
    vector<int> pos = { (width - 1) / 2,(height - 1) / 2 };    //starts in the middle of field
    vector<int> foodPos = pos;  // so that the food generates at the beginning
    vector<vector<int>> tail(tailLength, pos);
    vector<vector<char>> emptyGrid(width, vector<char>(height, ' '));
    vector<vector<char>> grid = emptyGrid;
    while (alive)  //runs main program until alive == 0
    {
        grid = emptyGrid; // clear grid
        grid[pos[0]][pos[1]] = 'Q';  //place head in grid
        if (!foodExists) //generates food if it was eaten
        {
            while (foodSnake(foodPos, tail, tailLength) || foodPos == pos)
            { // keeps regenerating until it isn't under the snake
                foodPos[0] = widthDist(engine);
                foodPos[1] = heightDist(engine);
            }
            foodExists = 1;
        }
        grid[foodPos[0]][foodPos[1]] = 'X'; //place food in grid
        for (int i = 0; i < tailLength; i++) grid[tail[i][0]][tail[i][1]] = 'O'; // place tail in grid
        drawGrid(grid, height, width, tailLength - tailLengthstart, sleeptime); //call above function to draw the grid
        input = '_';
        Sleep(sleeptime);
        if (_kbhit()) { //this was the best way I found to wait for input
            input = _getch();
            switch (input)
            { //disallows moving in opposite direction otherwise changes direction
                case 'w':
                    if (movementDirection == 'd') break;
                    movementDirection = 'u';
                    break;
                case 'a':
                    if (movementDirection == 'r') break;
                    movementDirection = 'l';
                    break;
                case 's':
                    if (movementDirection == 'u') break;
                    movementDirection = 'd';
                    break;
                case 'd':
                    if (movementDirection == 'l') break;
                    movementDirection = 'r';
                    break;
                case '_':
                    break;
            }
        }
        for (int i = tailLength - 1; i > 0; i--)
            tail[i] = tail[i - 1]; 
        tail[0] = pos; //move the tail along
        if (movementDirection == 'u') pos[1]--;
        if (movementDirection == 'l') pos[0]--;
        if (movementDirection == 'r') pos[0]++;
        if (movementDirection == 'd') pos[1]++; // move the head
        if (pos[0] < 0 || pos[0] > width - 1 || pos[1] < 0 || pos[1] > height - 1) 
            alive = 0; // if head is out of bounds -> dead
        for (int i = 0; i < tailLength; i++) 
            if (pos == tail[i]) 
                alive = 0; // if head is on tail -> dead
        if (foodPos == pos)
        { // if head is on food -> eat
            foodExists = 0; // food needs to be generated
            tail.push_back(tail[tailLength - 1]); //tail adds a link
            tailLength++; // tail is now longer
            if (tailLength % 5 == 0) sleeptime *= 0.75; // at certain lengths game speeds up
        }
    }

thisследующая часть происходит, когда вы мертвы или живы == 0

    std::system("cls");
    std::cout << endl << endl << endl << endl << "\tYou have died" << endl << endl << endl << endl;

    std::cout << endl;
    std::system("pause");
    return 0;
}

Так что если у кого-то есть идея, почему он не поворачивается быстро, пожалуйста, помогите.Или любые другие идеи по улучшению также приветствуются.

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

Есть небольшая проблема, которую вы ждете sleeptime даже при нажатии клавиши

Sleep(sleeptime);

Основной цикл может быть похож на этот псевдокод

while (IsAlive())
{
   currtime = 0;
   DrawState();
   while (currtime++ < sleeptime)
   {
      if (CheckAndProcessKeyboardInput())
         break;
      SleepOneMilliSecond();
   }
}
0 голосов
/ 23 октября 2018

Проблема с движением вызвана тем, что вы продвигаете хвост, и изменение направления немедленно вызывает движение головы.Это означает, что всегда есть шаг, прежде чем змея действительно развернется.

Состояние, установленное так, как я его вижу:

  1. Настройка сцены.
  2. Проверка ключа.
  3. Изменить направление движения, если нажата клавиша, и оно не противоположно текущему направлению.
  4. Сделать старую голову хвостом и добавить элемент головы в заданном направлении от старой головы.
  5. Проверка на смерть
  6. Проверка на еду.
  7. Если найдена еда, отрастите хвост, изменив TailLength.
  8. Если длина змеи больше, чем TailLength, удаляйте элементы хвоста, пока они не станут равными.
  9. Визуализация сцены
  10. Сон и переход к 2.

Лучше представлять змею списком, а не вектором.Он не будет перераспределять память при изменении размера или если вы вырежете первые элементы.

Если ваша платформа Windows (очевидно, вы используете функцию Sleep () вместо usleep), ответ на этот вопроспредлагает лучшее решение для обнаружения ключей. Получить нажатие клавиши в консоли Windows

Аналогичные решения существуют для платформ POSIX.

...