2015-07-31 11 views
8

Ho trovato un gioco serpente SFML C++ e ho lavorato con esso e cambiando alcune cose, ma una delle cose che non riesco a capire è come renderlo più fluido/reattivo con le pressioni dei tasti freccia. In questo momento sta usando enum Direction {Up, Down, Left, Right}; conRendi il gioco SFML Snake più reattivo ai tasti premuti?

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(); 
      } 
} 

E 'frustrante giocare momento perché non riesco a muovere nel modo più preciso come vorrei. C'è un modo per rendere i comandi più reattivi o risponde già alle pressioni dei tasti così rapidamente come è in grado di fare con il mio hardware?

Sono un principiante assoluto di OOP e SFML, quindi ho un po 'di problemi a capire l'orologio e guardare i giochi di serpenti in altre lingue per tradurlo in questo. Posso postare l'intero codice, se necessario.

Quindi so che non è abbastanza, ma qui è l'intero codice:

#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)' beh, questo è un po 'sospetto .... – Manu343726

+0

Cosa intendi con "smooth"? Il gioco Snake non è fluido per la sua definizione, i segmenti si agganciano da un posto all'altro. –

+0

Intendo controllare la testa del serpente in modo più fluido. Ad esempio, è molto difficile effettuare una inversione a U stretta (dove non viene utilizzato spazio aggiuntivo) in modo coerente. – Austin

risposta

4

Quindi quello che ho finito è stato rimuovere i controlli dal ciclo di temporizzazione, ma facendo loro controllare invece una seconda variabile di movimento. La mossa originale viene quindi impostata per uguagliare la seconda variabile di movimento all'interno del ciclo di temporizzazione. Questo ha finito per rimuovere il bug e mantenere una buona reattività.

6

se leggo leggo correttamente, il vostro obiettivo (s) sono:

C'è un modo per rendere il controlli più reattivi

Ad esempio, è molto difficile effettuare una inversione a U stretta (dove non viene utilizzato spazio aggiuntivo) in modo coerente.

(1) la seguente: (Mettere in una funzione di aggiornamento che viene chiamato ogni fotogramma)

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) 

attuerà movimento regolare ha indicato here. Inoltre, ecco un piccolo numero article che fornisce alcune informazioni interessanti su "event-checking" e "isKeyPressed()".

(2) Se questo non funziona, allora potrebbe essere qualcosa con:

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

Se "inputClock" sta ritardando la sua capacità di muoversi di nuovo, quindi ogni barriera (anche il piccolo incremento di 0,07) potrebbe causare un output non desiderato.

Altrimenti, fornire più codice in modo da poter testarlo da solo, o fammi sapere cosa ottieni dai miei suggerimenti.

2

Hai due bug nel codice che posso vedere nel codice di esempio originale:

  1. non si paga l'attenzione sul fatto che l'evento è un keyPressed o KeyReleased evento al momento di decidere come muoversi.
  2. Si sta quindi utilizzando un timer per risolvere il problema ignorando altri eventi per un tempo impostato dopo aver premuto un tasto di movimento.

È necessario agire solo sugli eventi KeyPressed e quindi rimuovere completamente la logica di temporizzazione.

Guardando attraverso il resto delle tracce \ risposte, non penso che entrambi i bug siano stati risolti contemporaneamente ...

La semplice rimozione della logica di temporizzazione (come descritta nei commenti) comporterebbe un comportamento apparentemente bizzarro in cui il rilascio dei tasti di movimento potrebbe cambiare la direzione del serpente, in base all'ordine del tasto multiplo che preme i rilasci.

Il controllo èKeyPressato (come per la risposta di @Donald) invece risolverà il problema di stampa \ rilascio, ma sarà necessario anche decidere cosa fare quando più di uno viene premuto contemporaneamente. Ciò probabilmente complicherà eccessivamente il codice quando gli eventi risolveranno tutto ciò per te restituendo un ordinamento cronologico di ciò che è stato premuto.

Il tuo programma completo sembra aver risolto il problema KeyPressed, ma non rimosso il timer. Ciò impedirà alle chiavi di funzionare in modo reattivo quanto necessario.