2015-07-31 12 views
8

Znalazłem grę w wężową grę SFML w języku C++, a ja się z tym zmierzyłem i zmieniłem kilka rzeczy, ale jedną z rzeczy, których nie potrafię wymyślić, jest to, jak sprawić, by było bardziej płynne/responsywne za pomocą klawiszy strzałek. Teraz jest za pomocą enum Direction {Up, Down, Left, Right}; zCzy gra SFML Snake lepiej reaguje na naciskanie klawiszy?

while (window.isOpen()) 
{ 

    sf::Vector2f lastPosition(snakeBody[0].getPosition().x, snakeBody[0].getPosition().y); 

    // Event 
    sf::Event event; 
    while (window.pollEvent(event)) 
    { 
     //..... 

     if (event.type == sf::Event::KeyPressed && event.key.code 
       == sf::Keyboard::Return) 
     { 
      //clock.restart; 
      if (!currentlyPlaying) 
       currentlyPlaying = true; 
      move = Down; 

      New_Game(snakeBody, window_width, window_height, engine, apple, score, scoreText, lowBounds); 
      mode = 1; 
      moveClock.restart(); 
      //start game 
     } 
      if(inputClock.getElapsedTime().asSeconds() >= 0.07) 
      { 
       if(event.key.code == sf::Keyboard::Up && move != Down) 
        move = Up; 
       inputClock.restart(); 
       if(event.key.code == sf::Keyboard::Down && move != Up) 
        move = Down; 
       inputClock.restart(); 
       if(event.key.code == sf::Keyboard::Left && move != Right) 
        move = Left; 
       inputClock.restart(); 
       if(event.key.code == sf::Keyboard::Right && move != Left) 
        move = Right; 
       inputClock.restart(); 
      } 
} 

To frustrujące odgrywać obecnie, bo nie może się poruszać tak dokładnie, jak chciałbym. Czy istnieje sposób na szybsze reagowanie na polecenia lub czy już reaguje na naciśnięcia klawiszy tak szybko, jak to możliwe na moim sprzęcie?

Jestem kompletnym początkującym dla OOP i SFML, więc mam trochę problemów ze zrozumieniem zegara, a także z grą w węża w innych językach, aby przetłumaczyć to na. W razie potrzeby mogę opublikować cały kod.

Tak wiem, że to nie jest ładna, ale tu jest cały kod:

#include <iostream> 
#include <SFML/Graphics.hpp> 
#include <vector> 
#include <string> 
#include <fstream> 
#include <sstream> 


// Global directions 
enum Direction {Up, Down, Left, Right}; 

// Reads high scores 
void High_Scores(std::vector<int> &top10scores) 
{ 
    top10scores.clear(); 
    std::string line; 
    std::ifstream highscores("highscores.txt"); 
    while (std::getline(highscores, line)) 
     top10scores.push_back(stoi(line)); 
    highscores.close(); 
} 

// Checks new score against high scores 
void New_High(std::vector<int> newScores) 
{ 
    std::ofstream fileoutput; 
    fileoutput.open("highscores.txt"); 
    for (int i = 0; i < 10; i++) 
     fileoutput << newScores[i] << "\n"; 
    fileoutput.close(); 
} 

// Writes over highscores.txt file 
int Update_Scores(int &score, std::vector<int> &top10scores) 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     if (score >= top10scores[i]) 
     { 
      for (int j = 9; j >= 0+i; j--) 
      { 
       top10scores[j] = top10scores[j-1]; 
      } 

      top10scores[i] = score; 

      New_High(top10scores); 

      High_Scores(top10scores); 
      return i; 
     } 
    } 
    return 11; 
} 

// Start menu 
void Welcome_Screen(sf::RenderWindow &window) 
{ 
    sf::Texture texture; 
    texture.loadFromFile("welcome2.png"); 
    sf::Sprite background(texture); 
    // Create welcome text 
    sf::Font font; 
    if (!font.loadFromFile("bonzai.ttf")) 
     std::cout << "Can't find the font file 'bonzai.ttf'" << std::endl; 
    sf::Text text("\n\n\n\n \t\t\t Welcome!\n\t Press 'Enter' to begin.", font, 50); 
    text.setColor(sf::Color::White); 
    sf::Text quitText(" 'Esc' to quit", font, 17); 
    quitText.setColor(sf::Color::Black); 
    window.clear(); 
    window.draw(background); 
    window.draw(text); 
    window.draw(quitText); 
    window.display(); 
} 

// Basic collision check for apple placement 
bool Collision_Detect(std::vector<sf::RectangleShape> &snakeBody, sf::CircleShape &apple) 
{ 
    for (int i = 0; i != snakeBody.size(); i++) 
    { 
     if (snakeBody[i].getPosition() == apple.getPosition()) 
      return true; 
    } 
    return false; 
} 

// Sets up starting values for game 
void New_Game(std::vector<sf::RectangleShape> &snakeBody, int window_width, int window_height, 
       std::default_random_engine &engine, sf::CircleShape &apple, int score, sf::Text &scoreText, 
       int lowBounds) 
{ 
    score = 0; 
    scoreText.setString("Score: 0"); 
    snakeBody.clear(); 
    snakeBody.push_back(sf::RectangleShape(sf::Vector2f(20,20))); // one square 
    snakeBody[0].setPosition(window_width/2, window_height/2 - 120); 
    snakeBody[0].setFillColor(sf::Color(200,255,200)); 
    snakeBody[0].setOutlineThickness(-1); 
    snakeBody[0].setOutlineColor(sf::Color::Black); 
    std::uniform_int_distribution<int> xPosition(lowBounds, 39); 
    auto randX = std::bind(xPosition, std::ref(engine)); 
    std::uniform_int_distribution<int> yPosition(lowBounds, 23);  // path length of 20 pixels I think 
    auto randY = std::bind(yPosition, std::ref(engine)); 
    do 
     apple.setPosition(randX()*20+10, randY()*20+10); 
    while (Collision_Detect(snakeBody, apple)); 

    for (int i = 0; i < 2; i++) 
    { 
     snakeBody.push_back(sf::RectangleShape(sf::Vector2f(20,20))); 
     snakeBody[snakeBody.size()-1].setFillColor(sf::Color(100,150,100)); 
     snakeBody[snakeBody.size()-1].setOutlineThickness(-1); 
     snakeBody[snakeBody.size()-1].setOutlineColor(sf::Color::Black); 
     snakeBody.back().setPosition(snakeBody.begin()->getPosition().x, 
            snakeBody.begin()->getPosition().y); 
    } 

} 

//Display all blocks of snake 
void Draw_Snake(sf::RenderWindow &window, std::vector<sf::RectangleShape> &snakeBody) 
{ 
    for (int i = 0; i != snakeBody.size(); i++) 
     window.draw(snakeBody[i]); 
} 

// Moves snake's head and tail 
void Move_Snake(std::vector<sf::RectangleShape> &snakeBody, sf::Vector2f &lastPosition, int move) 
{ 
    switch (move) 
    { 
    case Up: 
     snakeBody[0].move(0, -20); 
     break; 
    case Down: 
     snakeBody[0].move(0, 20); 
     break; 
    case Left: 
     snakeBody[0].move(-20, 0); 
     break; 
    case Right: 
     snakeBody[0].move(20, 0); 
     break; 
    default: 
     break; 
    } 
    sf::Vector2f newPosition(lastPosition); 
    if (snakeBody.size() > 1) 
    { 
     for (int i = 1; i != snakeBody.size(); i++) 
     { 
      lastPosition = snakeBody[i].getPosition(); 
      snakeBody[i].setPosition(newPosition); 
      newPosition = lastPosition; 
     } 
    } 
} 

// Apple placement 
bool Apple_Placement(int lowBounds, std::default_random_engine &engine, 
        std::vector<sf::RectangleShape> &snakeBody, sf::CircleShape &apple, sf::Clock &immuneTimer) 
{ 
    std::uniform_int_distribution<int> xPosition(lowBounds, 39); 
    auto randX = std::bind(xPosition, std::ref(engine)); 
    std::uniform_int_distribution<int> yPosition(lowBounds, 23); 
    auto randY = std::bind(yPosition, std::ref(engine)); 

    if ((apple.getPosition().x == snakeBody[0].getPosition().x + 10) && 
      (apple.getPosition().y == snakeBody[0].getPosition().y + 10)) 
    { 
     // for (int i = 0; i < 2; i++) 
     // { 
     snakeBody.push_back(sf::RectangleShape(sf::Vector2f(20,20))); 
     snakeBody[snakeBody.size()-1].setFillColor(sf::Color(100,150,100)); 
     snakeBody[snakeBody.size()-1].setOutlineThickness(-1); 
     snakeBody[snakeBody.size()-1].setOutlineColor(sf::Color::Black); 
     snakeBody.back().setPosition(snakeBody.begin()->getPosition().x, snakeBody.begin()->getPosition().y); 
     // } 
     do 
      apple.setPosition(randX()*20+10, randY()*20+10); 
     while (Collision_Detect(snakeBody, apple)); 

     immuneTimer.restart(); 
     return true; 
    } 
    else 
     return false; 
} 






// Checks body collision and out of bounds 
bool Snake_Alive(std::vector<sf::RectangleShape> &snakeBody, sf::Clock &immuneTimer) 
{ 
    if (snakeBody[0].getPosition().x < 0 || snakeBody[0].getPosition().x > 790 
      || snakeBody[0].getPosition().y < 0 || snakeBody[0].getPosition().y > 460) 
    { 
     // snakeBody[0].setFillColor(sf::Color::Yellow); 
     return false; 
    } 


    if(immuneTimer.getElapsedTime().asSeconds() >= .15) 
    { 
     for (int i = 1; i != snakeBody.size(); i++) 
     { 
      if (snakeBody[0].getPosition() == snakeBody[i].getPosition()) 
      { 
       // snakeBody[i].setFillColor(sf::Color::Yellow); 
       return false; 
      } 
     } 
    } 
    return true; 
} 

int main() 
{ 
    int window_width = 800, window_height = 600; 
    int bitsPerPixel = 24, start = 0, mode = 0, score = 0, difficulty = 2, lowBounds = 0; 
    std::vector<int> top10scores; 
    std::vector<sf::RectangleShape> snakeBody; 
    int move; 
    bool currentlyPlaying = false; 


    // Create main window 
    sf::RenderWindow window(sf::VideoMode(window_width, window_height, 
              bitsPerPixel), "Snake!", sf::Style::Close); 
    window.setVerticalSyncEnabled(true); 

    // Set the icon 
    sf::Image icon; 
    if (!icon.loadFromFile("icon.png")) 
     return EXIT_FAILURE; 
    window.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr()); 

    // Game board 
    sf::Texture texture; 
    texture.loadFromFile("grass.png"); //replace with game board 
    sf::Sprite grass(texture); 

    // Apple 
    sf::CircleShape apple(10); 
    apple.setOutlineThickness(-1); // should be diameter of 20 
    apple.setOutlineColor(sf::Color::Black); 
    apple.setFillColor(sf::Color::Red); 
    apple.setOrigin(apple.getRadius(), apple.getRadius()); 

    // Random generator 
    std::random_device seed_device; 
    std::default_random_engine engine(seed_device()); 

    // Clocks 
    sf::Clock moveClock; 
    sf::Clock inputClock; 
    sf::Clock immuneTimer; 

    // Score box 
    sf::RectangleShape scoreBox(sf::Vector2f(window_width, window_height - 480)); 
    scoreBox.setFillColor(sf::Color(0,200,0)); 
    scoreBox.setOutlineColor(sf::Color::Black); 
    scoreBox.setOutlineThickness(-3.f); 
    scoreBox.setPosition(0, 480); 

    sf::Font font; 
    if (!font.loadFromFile("bonzai.ttf")) 
     std::cout << "Can't find the font file 'bonzai.ttf'" << std::endl; 
    sf::Text scoreText("Score: ", font, 60); 
    scoreText.setColor(sf::Color::White); 
    scoreText.setPosition(314, 497); 

    sf::Text pauseText("GAME PAUSED", font, 80); 
    pauseText.setColor(sf::Color::Black); 
    pauseText.setPosition(174, 185); 

    sf::Text overText(" GAME OVER", font, 78); 
    overText.setColor(sf::Color(150,0,0)); 
    overText.setPosition(174, 185); 

    sf::Text newquitText("Pause: 'P'\nNew game: 'Enter'\nQuit to main menu: 'Q'", font, 20); 
    newquitText.setColor(sf::Color::Black); 
    newquitText.setPosition(5, 525); 

    sf::Text highScoreText("", font, 85); 
    highScoreText.setColor(sf::Color::Black); 

    //High scores 
    High_Scores(top10scores); 

    if (difficulty == 2) 
     lowBounds = 0; 

    New_Game(snakeBody, window_width, window_height, engine, apple, score, scoreText, lowBounds); 
    // Main game loop 
    while (window.isOpen()) 
    { 
     sf::Vector2f lastPosition(snakeBody[0].getPosition().x, snakeBody[0].getPosition().y); 
     // Event 
     sf::Event event; 
     while (window.pollEvent(event)) 
     { 
      // Welcome screen 
      if (start <= 1) 
      { 
       Welcome_Screen(window); 
       start++; 
      } 
      // Close window: Exit 
      if (event.type == sf::Event::Closed) 
       window.close(); 
      // Esc pressed: Exit 
      if (event.type == sf::Event::KeyPressed && event.key.code 
        == sf::Keyboard::Escape) 
       window.close(); 
      // Q pressed: Exit to main menu 
      if (event.type == sf::Event::KeyPressed && event.key.code 
        == sf::Keyboard::Q) 
      { 
       start = 1; 
       mode = 0; 
      } 
      if (event.type == sf::Event::KeyPressed && event.key.code 
        == sf::Keyboard::Return) 
      { 
       if (!currentlyPlaying) 
        currentlyPlaying = true; 
       move = Down; 

       New_Game(snakeBody, window_width, window_height, engine, apple, score, scoreText, lowBounds); 
       mode = 1; 
       score = 0; 
       moveClock.restart(); 
       inputClock.restart(); 
       immuneTimer.restart(); 
      } 
      if(event.type == sf::Event::KeyPressed && inputClock.getElapsedTime().asSeconds() >= 0.06) //0.07 
      { 
       if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Up && move != Down) 
       { 
        move = Up; 
        inputClock.restart(); 
       } 
       if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Down && move != Up) 
       { 
        move = Down; 
        inputClock.restart(); 
       } 
       if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Left && move != Right) 
       { 
        move = Left; 
        inputClock.restart(); 
       } 
       if(event.key.code == sf::Keyboard::Right && move != Left) 
       { 
        move = Right; 
        inputClock.restart(); 
       } 
      } 
      // P pressed: Pause simulation 
      if (event.type == sf::Event::KeyPressed && event.key.code 
        == sf::Keyboard::P) 
      { 
       if (mode == 1) 
       { 
        window.draw(pauseText); 
        window.display(); 
       } 
       mode *= -1; 
       moveClock.restart(); 
       immuneTimer.restart(); 
       inputClock.restart(); 
      } 
     } 
     if (mode == 1) 
     { 
      window.clear(); 
      window.draw(grass); 
      window.draw(apple); 
      Draw_Snake(window, snakeBody); 
      window.draw(scoreBox); 
      window.draw(scoreText); 
      window.draw(newquitText); 

      if(moveClock.getElapsedTime().asSeconds() >= .075)   // change back to 0.09 
      { 
       Move_Snake(snakeBody, lastPosition, move); 
       moveClock.restart(); 
      } 
      if(Apple_Placement(lowBounds, engine, snakeBody, apple, immuneTimer)) 
      { 
       if (difficulty == 1) 
        score += 5; 
       else if (difficulty == 2) 
        score += 10; 
       else 
        score += 20; 
       std::string newScore = std::to_string(score); 
       scoreText.setString("Score: " + newScore); 
      } 
      if(!Snake_Alive(snakeBody, immuneTimer)) 
      { 
       window.draw(overText); 
       int scorePlacement = Update_Scores(score, top10scores); 
       if (scorePlacement != 11) 
       { 
        std::string newHighText = std::to_string(scorePlacement+1); 
        highScoreText.setString("#" + newHighText 
              + " out of top 10 scores!"); 
        if (scorePlacement == 9) 
         highScoreText.setPosition(15, 50); 
        else 
         highScoreText.setPosition(30, 50); 
        window.draw(highScoreText); 
       } 
       window.display(); 
       mode = 0; 
       window.display(); 
      } 

      window.display(); 
     } 
    } 
    return EXIT_SUCCESS; 
} 
+0

'if (inputClock.getElapsedTime(). AsSeconds()> = 0.07)' cóż, to jest trochę podejrzane ... – Manu343726

+0

Co masz na myśli mówiąc o płynności? Gra Snake nie jest płynna z definicji, segmenty są po prostu przyciągane z jednego miejsca do drugiego. –

+0

Mam na myśli kontrolowanie głowy węża bardziej płynnie. Na przykład bardzo trudno wykonać konsekwentnie ciasny zawrót (gdy nie ma dodatkowego miejsca). – Austin

Odpowiedz

4

Więc skończyło się na tym, że usunąłem kontrolki z pętli taktowania, ale sprawiłem, że zamiast tego kontrolują zmienną drugiego ruchu. Oryginalny ruch jest następnie ustawiony tak, aby był równy drugiej zmiennej ruchu w pętli synchronizacji. W rezultacie usunięto błąd i utrzymano dobrą reakcję.

6

Jeśli czytam poprawnie czytać, twoim celem (s) są:

Czy istnieje sposób, aby kontrolki bardziej responsywne

Na przykład bardzo trudno wykonać konsekwentnie ciasny zawrót (gdy nie ma dodatkowego miejsca).

(1) co następuje: (umieścić w funkcji aktualizacji o nazwie każdej ramki)

if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) 
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) 
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) 
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) 

będzie stanowić gładki ruch zaznaczono here. Również tutaj jest trochę article, który dostarcza interesujących informacji na temat "sprawdzania zdarzeń" vs. "isKeyPressed()".

(2) Jeśli to nie zadziała, to mogłoby być coś z:

if(inputClock.getElapsedTime().asSeconds() >= 0.07) 

Jeśli „inputClock” opóźnia swoją zdolność do poruszania się ponownie, a następnie każdą barierę (nawet mały inkrement 0,07) może spowodować niepożądane wyjście.

W przeciwnym razie podaj więcej kodu, aby przetestować go samodzielnie lub dać mi znać, co uzyskasz z moich sugestii.

2

Masz dwa błędy w kodzie, które można zobaczyć w oryginalnej próbki kodu:

  1. nie płacą uwagę, czy zdarzenie jest keyPressed lub KeyReleased zdarzenie przy podejmowaniu decyzji, jak się poruszać.
  2. Używasz timera do obejścia tego, ignorując inne zdarzenia przez określony czas po naciśnięciu klawisza ruchu.

Należy wykonać tylko zdarzenia KeyPressed, a następnie całkowicie usunąć logikę synchronizacji.

Przeglądając pozostałe szlaki \ odpowiedzi, nie sądzę, aby oba błędy zostały naprawione na raz ...

Po prostu usunięcie logiki czasowej (jak uwzględniono w komentarzach) spowodowałoby pozornie dziwaczne zachowanie, gdy zwolnienie klawiszy ruchu może zmienić kierunek węża, w zależności od kolejności naciśnięć/zwolnień wielu klawiszy.

Sprawdzanie isKeyPressed (zgodnie z odpowiedzią @ Donalda) spowoduje rozwiązanie problemu press \ release, ale musisz także zdecydować, co zrobić, gdy zostanie naciśnięty więcej niż jeden raz. To prawdopodobnie nadmiernie skomplikuje twój kod, gdy zdarzenia rozwiążą to wszystko dla ciebie, zwracając chronologiczną kolejność tego, co zostało naciśnięte.

Wygląda na to, że Twój obecny pełny program naprawił problem związany z klawiszem KeyPress, ale nie usunął timera. Zapobiegnie to działaniu klawiszy tak szybko, jak potrzebujesz.