游戏进程管理
首先需要修改一下prompt_info函数,让界面能显示开关按键的状态以及游戏的分数
分数显示关键在与将Int型的变量数值以字符的形式显示在信息面板上
添加头文件
下面来讲解一下游戏进程管理。在游戏进程结束的时候让游戏循环能够重新开始。创建布尔变量gameQuit, 初始化为false
在main函数中用do-while循环来建立一个大循环,在内部建立一个while循环用于获取窗口的消息,并进行游戏重启和退出的判断
在Input函数中也加入gameQuit,使得游戏能够退出
游戏结束的提示信息编写在新创建的gameOver()函数中进行封装
在Draw函数中添加
在最前方定义变量的地方声明
到此的完整代码(一开始切换成平滑模式时蛇吃果子的音效没有了,问题在Logicstep里面,现在好了)
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <sstream>
#define WINDOW_WIDTU 80 //窗口的宽度
#define WINDOW_HEIGHT 25 //窗口的高度
#define STAGE_WIDTH 20 //舞台宽度
#define STAGE_HEIGHT 20 //舞台宽度
#define GRIDSIZE 25 //纹理尺寸
#define SCALE 0.5
#define MAXLENGTH 100 //蛇身最大长度
#define INFO_WIDTH 400
#define STEP 0.1
using namespace sf;
bool gameOver,gameQuit;
const int width = STAGE_HEIGHT;
const int height = STAGE_WIDTH;
int x, y, fruitX, fruitY, score;
int tailX[MAXLENGTH], tailY[MAXLENGTH];
int nTail;
enum eDirectin { STOP = 0, LEFT, RIGHT, UP, DOWN };
eDirectin dir, dir_ing;
int headRotation;
int delay;
int GameMode;
float stepX, stepY;
SoundBuffer sbEat, sbDie;
Sound soundEat, soundDie;
Music bkMusic;
int soundVolume;
bool MusicOn;
void gameOver_info(int _x, int _y);
Font font;
Text text;
//这里我们还没有用到类的封装,所以暂时把window作为全局变量
//对新建窗口的封装,第一个参数是一个VideoMode,表示窗口大小,第二个表示窗口标题,实际上最多可以使用4个参数,最后两个是可选的 -Style 和Cont...
//标题中有宽字符的,字符串前要加L,不然会显示乱码
sf::RenderWindow window(sf::VideoMode(width* GRIDSIZE + INFO_WIDTH, height* GRIDSIZE + GRIDSIZE), L"Keep going");
Texture tBackgroud, tSnakeHead, tSnakeBody, tFruit; //创建3个纹理对象
Sprite spBackgroud, spSnakeHead, spSnakeBody, spFruit; //创建3个精灵对象
void Initial()
{
window.setFramerateLimit(30); //用来控制窗口的更新频率,每秒设置目标帧数
if (!font.loadFromFile("data/Fonts/simsun.ttc")) //选择字体,SFML不能直接访问系统的字体,特殊的字体,需要自己加载
{
std::cout << "字体没有找到" << std::endl;
}
text.setFont(font);
tBackgroud.loadFromFile("data/images/BK.png");
tSnakeHead.loadFromFile("data/images/sh01.png");
tSnakeBody.loadFromFile("data/images/sb0102.png");
tFruit.loadFromFile("data/images/sb0202.png");
if (!sbEat.loadFromFile("data/Audios/Eat01.ogg"))//加载音频
{
std::cout << "Eat01.ogg没有找到" << std::endl;
}
if (!sbDie.loadFromFile("data/Audios/Die01.ogg"))//加载音频
{
std::cout << "Die01.ogg没有找到" << std::endl;
}
if (!bkMusic.openFromFile("data/Audios/BGM01.ogg"))//加载音频
{
std::cout << "BGM01.ogg没有找到" << std::endl;
}
spBackgroud.setTexture(tBackgroud); //设置精灵对象的纹理
spSnakeHead.setTexture(tSnakeHead);
spSnakeBody.setTexture(tSnakeBody);
spFruit.setTexture(tFruit);
spBackgroud.setOrigin(GRIDSIZE / SCALE / 2, GRIDSIZE / SCALE / 2);
spSnakeHead.setOrigin(GRIDSIZE / SCALE / 2, GRIDSIZE / SCALE / 2);
spSnakeBody.setOrigin(GRIDSIZE / SCALE / 2, GRIDSIZE / SCALE / 2);
spFruit.setOrigin(GRIDSIZE / SCALE / 2, GRIDSIZE / SCALE / 2);
spBackgroud.setScale(SCALE, SCALE);
spSnakeHead.setScale(SCALE, SCALE);
spSnakeBody.setScale(SCALE, SCALE);
spFruit.setScale(SCALE, SCALE);
soundEat.setBuffer(sbEat);//音效读入缓冲
soundDie.setBuffer(sbDie);//音效读入缓冲
bkMusic.play();//背景音播放
bkMusic.setLoop(true);//背景音循环
soundVolume = 50;
MusicOn = true;
gameOver = false;
gameQuit = false;
GameMode = 1;
stepX = 0.0;
stepY = 0.0;
headRotation = 0;
delay = 0;
dir = STOP;
dir_ing = STOP;
x = width / 2;
y = height / 2;
fruitX = rand() % width;
fruitY = rand() % height;
score = 0;
nTail = 1;
for (int i = 0; i < MAXLENGTH; i++)
{
tailX[i] = 0;
tailY[i] = 0;
}
}
void Input()
{
sf::Event event; //event types 包括Window、Keyboard、Mouse、Joystick,4类消息
//通过 bool Window :: pollEvent (sf :: Event&event) 从窗口顺序询问(polled)事件。
//如果有一个事件等待处理,该函数将返回true,并且事件变量将填充(filled)事件数据。
//如果不是,则还函数返回false。同样重要的是要注意,一次可能有多个事件;因此我们必须确保捕获每个可能的事件。
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close(); //窗口可以移动、调整大小和最小化。但是如果要关闭,需要自己去调用close()函数
gameOver = true;
gameQuit = true;
}
if (event.type == sf::Event::EventType::KeyReleased && event.key.code == sf::Keyboard::X)
window.close();
if (event.type == sf::Event::EventType::KeyReleased && event.key.code == sf::Keyboard::Space)
{
if (GameMode == 1)
{
GameMode = 2;
}
else
GameMode = 1;
}
if (event.type == sf::Event::EventType::KeyReleased && event.key.code == sf::Keyboard::Add)
{
soundVolume += 5;
bkMusic.setVolume(soundVolume);
}
if (event.type == sf::Event::EventType::KeyReleased && event.key.code == sf::Keyboard::Subtract)
{
soundVolume -= 5;
bkMusic.setVolume(soundVolume);
}
if (event.type == sf::Event::EventType::KeyReleased && (event.key.code == sf::Keyboard::Multiply|| event.key.code == sf::Keyboard::Enter))
{
if (MusicOn == true)
{
bkMusic.stop();
MusicOn = false;
}
else
{
bkMusic.play();
MusicOn = true;
}
}
}
if (Keyboard::isKeyPressed(Keyboard::Left))//按键判定
if (dir != RIGHT)
dir = LEFT;
if (Keyboard::isKeyPressed(Keyboard::Right))
if (dir != LEFT)
dir = RIGHT;
if (Keyboard::isKeyPressed(Keyboard::Up))
if (dir != DOWN)
dir = UP;
if (Keyboard::isKeyPressed(Keyboard::Down))
if (dir != UP)
dir = DOWN;
}
void Prompt_info(int _x, int _y)
{
int initialX = 20, initialY = 0;
int CharacterSize = 24;
text.setCharacterSize(CharacterSize);
text.setFillColor(Color(255, 255, 255, 255));
text.setStyle(Text::Bold); // |Text::Underlined
text.setPosition(_x + initialX, _y + initialY);
text.setString(L"■游戏说明:"); window.draw(text);
initialY += CharacterSize; //空一行,可以根据需要调整
text.setPosition(_x + initialX, _y + initialY);
text.setString(L"A.蛇身自撞,游戏结束"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L"B.蛇可撞墙"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L"■操作说明:"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □向左移动:←A"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □向右移动:→D"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □向下移动:↓S"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □向上移动:↑W"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □开始游戏,任意方向键"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □背景音开关: *或回车"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □背景音音量:+/-键"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □动画模式切换:空格键"); window.draw(text);
initialY += CharacterSize*1.5;
text.setPosition(_x + initialX, _y + initialY);
if (GameMode==1)
{
text.setFillColor(Color(0,0,255,255)); //蓝色字体
text.setString(L" 步进移动"); window.draw(text);
}
else
{
text.setFillColor(Color(255, 0, 0, 255));//红色字体
text.setString(L" 连续移动"); window.draw(text);
}
window.draw(text);
text.setFillColor(Color(255, 255, 255, 255));//白色字体
initialY += CharacterSize*1.5;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" □退出游戏: x键退出"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L"☆ 当前得分:"); window.draw(text);
text.setFillColor(Color(255, 0, 0, 255));
initialY += CharacterSize;
text.setPosition(_x + initialX + CharacterSize*7, _y + initialY);
CharacterSize = 48;
text.setCharacterSize(CharacterSize);
std::stringstream ss;
ss << score;
text.setString(ss.str()); window.draw(text);
CharacterSize = 24;
text.setCharacterSize(CharacterSize);
text.setFillColor(Color(255, 255, 255, 255));
initialY += CharacterSize;
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L"☆ 当前难度:"); window.draw(text);
text.setString(L" 3"); window.draw(text);
text.setString(L" 级"); window.draw(text);
}
void Logic() //上一章的代码,没改
{
int prevX = tailX[0];
int prevY = tailY[0];
int prev2X, prev2Y;
tailX[0] = x;
tailY[0] = y;//把新增的蛇身坐标存放在0位
switch (dir)
{
//case STOP:
//break; 为什么这两行不要
case LEFT:
x--;
headRotation = -90;
break;
case RIGHT:
x++;
headRotation = 90;
break;
case UP: //向上居然是y--
y--;
headRotation = 0;
break;
case DOWN:
y++;
headRotation = 90;
break;
default:
break;
}
//if (x > width || x<0 || y>height || y < 0) //蛇头遇到墙壁则停止
//{
// gameOver = true;
//}
if (x == fruitX && y == fruitY) //蛇头遇到果子
{
score += 10;
soundEat.play();//播放吃的音效
fruitX = rand() % width;
fruitY = rand() % height;
nTail++;
}
for (int i = 1; i < nTail; i++)
{
prev2X = tailX[i];
prev2Y = tailY[i];
tailX[i] = prevX;
tailY[i] = prevY;
prevX = prev2X;
prevY = prev2Y;
}
for (int i = 1; i < nTail; i++)
{
if (tailX[i] == x && tailY[i] == y)
{
soundDie.play(); //播放死亡的音效
gameOver = true;
}
}
if (x >= width) x = 0; else if (x < 0)x = width - 1;
if (y >= height) y = 0; else if (y < 0)y = height - 1;
}
void Draw()
{
window.clear(Color::Color(255, 0, 255, 255)); //清屏
Prompt_info(width * GRIDSIZE + GRIDSIZE, GRIDSIZE);
int detaX = GRIDSIZE / SCALE / 2;
int detaY = GRIDSIZE / SCALE / 2;
//绘制背景
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
spBackgroud.setPosition(i * GRIDSIZE + detaX, j * GRIDSIZE + detaY); //指定纹理的位置
window.draw(spBackgroud);
}
}
//绘制蛇
spSnakeHead.setPosition(tailX[0] * GRIDSIZE + detaX, tailY[0] * GRIDSIZE + detaY);
spSnakeHead.setRotation(headRotation);
window.draw(spSnakeHead);
for (int i = 1; i < nTail; i++)
{
spSnakeBody.setPosition(tailX[i] * GRIDSIZE + detaX, tailY[i] * GRIDSIZE + detaY);
window.draw(spSnakeBody);
}
//绘制水果
spFruit.setPosition(fruitX * GRIDSIZE + detaX, fruitY * GRIDSIZE + detaY);
window.draw(spFruit);
window.display(); //把显示缓冲区的内容,显示在屏幕上。SFML采用的是双缓冲机制
}
void LogicStep() //步径细化
{
int prevX = tailX[0];
int prevY = tailY[0];
int prev2X, prev2Y;
bool updateFlag = false;
switch (dir_ing)
{
//case STOP:
//break; 为什么这两行不要
case LEFT:
stepX -= STEP;
if ((stepX < -0.9999) || (stepX > 0.9999))
{
x--;
stepX = 0;
stepY = 0;
dir_ing = dir;
headRotation = -90;
updateFlag = true;
}
break;
case RIGHT:
stepX += STEP;
if ((stepX < -0.9999) || (stepX > 0.9999))
{
x++;
stepX = 0;
stepY = 0;
dir_ing = dir;
headRotation = 90;
updateFlag = true;
}
break;
case UP: //向上居然是y--
stepY -= STEP;
if ((stepY < -0.9999) || (stepY > 0.9999))
{
y--;
stepX = 0;
stepY = 0;
dir_ing = dir;
headRotation = 0;
updateFlag = true;
}
break;
case DOWN:
stepY += STEP;
if ((stepY < -0.9999) || (stepY > 0.9999))
{
y++;
stepX = 0;
stepY = 0;
dir_ing = dir;
headRotation = 90;
updateFlag = true;
}
break;
default:
dir_ing = dir;
break;
}
if (x >= width) x = 0; else if (x < 0)x = width - 1;
if (y >= height) y = 0; else if (y < 0)y = height - 1;
tailX[0] = x;
tailY[0] = y;
if (updateFlag == true)
{
if (x == fruitX && y == fruitY) //蛇头遇到果子
{
score += 10;
soundEat.play();
fruitX = rand() % width;
fruitY = rand() % height;
nTail++;
}
for (int i = 1; i < nTail; i++)
{
prev2X = tailX[i];
prev2Y = tailY[i];
tailX[i] = prevX;
tailY[i] = prevY;
prevX = prev2X;
prevY = prev2Y;
}
for (int i = 1; i < nTail; i++)
{
if (tailX[i] == x && tailY[i] == y)
{
soundDie.play(); //播放死亡的音效
gameOver = true;
}
}
}
}
void DrawStep()
{
window.clear(Color::Color(255, 0, 255, 255)); //清屏
Prompt_info(width * GRIDSIZE + GRIDSIZE, GRIDSIZE);
int detaX = GRIDSIZE / SCALE / 2;
int detaY = GRIDSIZE / SCALE / 2;
//绘制背景
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
spBackgroud.setPosition(i * GRIDSIZE + detaX, j * GRIDSIZE + detaY); //指定纹理的位置
window.draw(spBackgroud);
}
}
//绘制蛇
float stepLength;
stepLength = stepX + stepY;
if (stepLength<0)
{
stepLength = -stepLength;
}
spSnakeHead.setPosition((tailX[0]+stepX) * GRIDSIZE + detaX, (tailY[0]+stepY) * GRIDSIZE + detaY);
spSnakeHead.setRotation(headRotation);
window.draw(spSnakeHead);
for (int i = 1; i < nTail; i++)
{
if (tailY[i]==tailY[i-1]&& tailX[i] != tailX[i - 1])//水平跟随
spSnakeBody.setPosition((tailX[i]+(tailX[i-1]-tailX[i])*stepLength) * GRIDSIZE + detaX, tailY[i] * GRIDSIZE + detaY);
if (tailY[i] != tailY[i - 1] && tailX[i] == tailX[i - 1])//竖直跟随
spSnakeBody.setPosition(tailX[i] * GRIDSIZE + detaX, (tailY[i] + (tailY[i - 1] - tailY[i]) * stepLength) * GRIDSIZE + detaY);
//if (tailY[i] != tailY[i - 1] && tailX[i] != tailX[i - 1])//拐角跟随
// spSnakeBody.setPosition((tailX[i] + (tailX[i - 1] - tailX[i]) * stepLength)* GRIDSIZE + detaX, (tailY[i] + (tailY[i - 1] - tailY[i]) * stepLength)* GRIDSIZE + detaY);
window.draw(spSnakeBody);
}
//绘制水果
spFruit.setPosition(fruitX * GRIDSIZE + detaX, fruitY * GRIDSIZE + detaY);
window.draw(spFruit);
if (gameOver)
gameOver_info(width / 8 * GRIDSIZE, height / 4 * GRIDSIZE);
window.display(); //把显示缓冲区的内容,显示在屏幕上。SFML采用的是双缓冲机制
}
void gameOver_info(int _x, int _y)
{
int initialX = 20, initialY = 0;
int CharacterSize = 48;
text.setCharacterSize(CharacterSize);
text.setFillColor(Color(255, 0, 0, 255));
text.setStyle(Text::Bold);
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" 游戏结束!!"); window.draw(text);
initialY += CharacterSize;
text.setPosition(_x + initialX, _y + initialY);
text.setString(L" Y重新开始/N退出"); window.draw(text);
}
int main()
{
do
{
Initial();
while (window.isOpen() && gameOver == false)
{
Input();
switch (GameMode)
{
case 1:
delay++;
if (delay % 10 == 0)
{
delay = 0;
Logic();
}
Draw();
break;
case 2:
LogicStep();
DrawStep();
break;
}
}
while (gameOver)
{
Event e;
while (window.pollEvent(e))
{
if (e.type == Event::Closed)
{
window.close();
gameOver = false;
gameQuit = true;
}
if (e.type == Event::EventType::KeyReleased && e.key.code == Keyboard::Y)
gameOver = false;
if (e.type == Event::EventType::KeyReleased && e.key.code == Keyboard::N)
{
gameOver = false;
gameQuit = true;
}
}
}
} while (!gameOver);
system("pause");
return 0;
}