让 SFML Snake 游戏对按键反应更灵敏?
Make SFML Snake game more responsive to key presses?
我找到了一个 SFML C++ 贪吃蛇游戏,我一直在摆弄它并改变了一些东西,但我想不通的一件事是如何让它变得更 smooth/responsive箭头键按下。现在它正在使用 enum Direction {Up, Down, Left, Right};
和
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();
}
}
目前的比赛令人沮丧,因为我不能像我想的那样精确地移动。有没有办法让控件响应更快,或者它是否已经像我的硬件一样快速响应按键?
我完全是 OOP 和 SFML 的初学者,所以我在理解时钟以及查看其他语言的蛇游戏以将其翻译成这个时遇到了一些麻烦。如果需要,我可以 post 完整的代码。
所以我知道它并不漂亮,但这是完整的代码:
#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;
}
如果我没看错,你的目标是:
Is there a way to make the controls more responsive
For instance it's very difficult to make a tight U-turn (where no additional space is used) consistently.
(1) 如下:(放入每帧调用的更新函数)
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)
将实施平滑移动已表明 here. Also, here is a little article 提供了一些关于 "event-checking" 与 "isKeyPressed()" 的有趣见解。
(2) 如果这不起作用,则可能是:
if(inputClock.getElapsedTime().asSeconds() >= 0.07)
如果 "inputClock" 延迟了您再次移动的能力,那么任何障碍(即使是 0.07 的小增量)都可能导致非预期的输出。
否则,请提供更多代码以便我自己测试,或者让我知道您从我的建议中得到了什么。
你的代码中有两个错误,我可以在你的原始示例代码中看到:
- 您在决定如何移动时没有注意事件是 KeyPressed 还是 KeyReleased 事件。
- 然后,您将使用计时器通过在按下移动键后忽略其他事件一段时间来解决此问题。
您只需对 KeyPressed 事件进行操作,然后完全删除计时逻辑。
查看 trails\answers 的其余部分,我认为这两个错误都没有立即修复...
仅删除计时逻辑(如评论中所述)会导致明显奇怪的行为,释放移动键可能会改变蛇的方向,具体取决于您的多键顺序 presses\releases。
检查 isKeyPressed(根据@Donald 的回答)将解决 press\release 问题,但您还需要决定一次按下多个键时要做什么。这可能会使您的代码过于复杂,因为事件将通过返回所按内容的时间顺序为您解决所有问题。
您当前的完整程序似乎已解决 KeyPressed 问题,但未删除计时器。这将阻止按键按照您的需要响应地工作。
所以我最终做的是从计时循环中删除控件,而是让它们控制第二个运动变量。然后将原始移动设置为等于计时循环内的第二个移动变量。这最终消除了错误并保持了良好的响应能力。
我找到了一个 SFML C++ 贪吃蛇游戏,我一直在摆弄它并改变了一些东西,但我想不通的一件事是如何让它变得更 smooth/responsive箭头键按下。现在它正在使用 enum Direction {Up, Down, Left, Right};
和
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();
}
}
目前的比赛令人沮丧,因为我不能像我想的那样精确地移动。有没有办法让控件响应更快,或者它是否已经像我的硬件一样快速响应按键?
我完全是 OOP 和 SFML 的初学者,所以我在理解时钟以及查看其他语言的蛇游戏以将其翻译成这个时遇到了一些麻烦。如果需要,我可以 post 完整的代码。
所以我知道它并不漂亮,但这是完整的代码:
#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;
}
如果我没看错,你的目标是:
Is there a way to make the controls more responsive
For instance it's very difficult to make a tight U-turn (where no additional space is used) consistently.
(1) 如下:(放入每帧调用的更新函数)
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)
将实施平滑移动已表明 here. Also, here is a little article 提供了一些关于 "event-checking" 与 "isKeyPressed()" 的有趣见解。
(2) 如果这不起作用,则可能是:
if(inputClock.getElapsedTime().asSeconds() >= 0.07)
如果 "inputClock" 延迟了您再次移动的能力,那么任何障碍(即使是 0.07 的小增量)都可能导致非预期的输出。
否则,请提供更多代码以便我自己测试,或者让我知道您从我的建议中得到了什么。
你的代码中有两个错误,我可以在你的原始示例代码中看到:
- 您在决定如何移动时没有注意事件是 KeyPressed 还是 KeyReleased 事件。
- 然后,您将使用计时器通过在按下移动键后忽略其他事件一段时间来解决此问题。
您只需对 KeyPressed 事件进行操作,然后完全删除计时逻辑。
查看 trails\answers 的其余部分,我认为这两个错误都没有立即修复...
仅删除计时逻辑(如评论中所述)会导致明显奇怪的行为,释放移动键可能会改变蛇的方向,具体取决于您的多键顺序 presses\releases。
检查 isKeyPressed(根据@Donald 的回答)将解决 press\release 问题,但您还需要决定一次按下多个键时要做什么。这可能会使您的代码过于复杂,因为事件将通过返回所按内容的时间顺序为您解决所有问题。
您当前的完整程序似乎已解决 KeyPressed 问题,但未删除计时器。这将阻止按键按照您的需要响应地工作。
所以我最终做的是从计时循环中删除控件,而是让它们控制第二个运动变量。然后将原始移动设置为等于计时循环内的第二个移动变量。这最终消除了错误并保持了良好的响应能力。