Github传送门
效果图
// To-Do List 自带传送门
To-Do List
//以下所有代码均未封装
1.配置
SFML 2.4.2
2.显示静止飞机
1 2 3 4 5 6 7 8
| sf::RenderWindow mWindow(sf::VideoMode(1280, 960), "TouHou20.0-chs"); sf::Texture Reimu; if (!Reimu.loadFromFile("E:\\Media\\Sources\\Jpg&Png\\TH\\Ex\\player\\pl00\\pl00.png", sf::IntRect(0, 0, 30, 45))) { puts("Error: Load Reimu failed!"); } sf::Sprite pl01(Reimu); mWindow.draw(pl01);
|
Tips:实际代码中采用了动画效果,会根据当前帧数来选定 Sprite。
3.BGM
1 2 3 4 5 6
| if (!music.openFromFile("上海アリス幻樂団 - 不思議なお祓い棒.wav")) { puts("Error: Open 上海アリス幻樂団 - 不思議なお祓い棒.wav failed!"); }
music.play();
|
Tips:music
不会预先把文件读进缓冲区,所以适合大文件(1分钟以上?)的播放,在小音效的处理上应该使用预先把文件读进缓冲区的
sound,可以极大的节省CPU的开销(实测大概是20倍的差距)。
4.左右键控制
1 2 3 4 5 6 7 8 9 10 11 12
| if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) { }
|
Tips:这里在实际的代码中其实采取了一个监听和处理分开的策略,感觉这样结构更清晰一下,效率也更高(大概)。
5.限制区域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if (mIsMovingUp == true && player.hero.getPosition().y > 40) { player.hero.move(0.0, -player.speed); } if (mIsMovingDown == true && player.hero.getPosition().y < 850) { player.hero.move(0.0, player.speed); } if (mIsMovingLeft == true && player.hero.getPosition().x > 69) { player.hero.move(-player.speed, 0.0); } if (mIsMovingRight == true && player.hero.getPosition().x < 751) { player.hero.move(player.speed, 0.0); }
|
Tips:STL 里的 list 下的 remove_if
因为其内部实现的原因,是不支持函数重载的,所以后来写了一个针对 FO
类的越界判定函数。之后最好能把两个函数整合一下,都针对 FO
对象来判定会更灵活一些。
6&7&9.发射子弹&越界回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| if (mIsFire) { if (i % 2 == 1) { player.LSAmmo.setPosition(sf::Vector2f(player.hero.getPosition().x + 4, player.hero.getPosition().y + 100)); playerBullets.push_back(player.LSAmmo); layer.LSAmmo.setPosition(sf::Vector2f(player.hero.getPosition().x + 20, player.hero.getPosition().y + 100)); playerBullets.push_back(player.LSAmmo); player.HSAmmo.setPosition(sf::Vector2f(player.hero.getPosition().x + 4, player.hero.getPosition().y + 130)); playerBullets.push_back(player.HSAmmo); player.HSAmmo.setPosition(sf::Vector2f(player.hero.getPosition().x + 20, player.hero.getPosition().y + 130)); playerBullets.push_back(player.HSAmmo); } } playerBullets.remove_if(isOutOfBoard); for (list<sf::Sprite>::iterator it = playerBullets.begin(); it != playerBullets.end(); it++) { it->setPosition(it->getPosition().x, it->getPosition().y - 60);
mWindow.draw(*it); }
bool isOutOfBoard(sf::Sprite value) { if (value.getPosition().y <= 136) { return true; } return false; }
|
Tips:本来是给自机配备了两种火力模式以供选择的,后来觉得火力太薄了所以干脆一起搭载上了,大概对于敌机来说是很不友好的设定吧(笑)。
8.1&16.时间轴管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| void Game::Stage1() { static sf::Time elapsed1 = clock.restart(); elapsed1 = clock.getElapsedTime(); static int evts[20] = { 0 };
static int curTime = 1; if (curTime < elapsed1.asSeconds()) { printf("%.0f\n", elapsed1.asSeconds()); curTime++; }
switch ((int)elapsed1.asSeconds()) { case 1: //pre evts[1] = 1; break; case 12: //title evts[2] = 1; break; case 37: //wave evts[3] = 1; break; case 50: //middle evts[4] = 1; break; case 63: //spellCard1 evts[5] = 1; break; case 100: //boss evts[6] = 1; break; }
|
Tips:如果 Clock 达到了触发条件,就把某个事件开关置
1,新世界的大门就打开了(明明只是奇奇怪怪的弹幕游戏来着)。
8.2.时间轴事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| int Stage01Event01() { static int curFrame = 0; curFrame++; static list<FO> wave1, wave2; double gapTime = 0.4; int gapFrame = gapTime * 60; static int gap = 0, temp = 0; if (curFrame % gapFrame == 1 && curFrame < 17 * gapFrame) { //wave1 } if (curFrame == 270) { //wave2 } wave1.remove_if(isFOOutOfBoard); for (list<FO>::iterator it = wave1.begin(); it != wave1.end(); it++) { //Trajectory equation 01 } wave2.remove_if(isFOOutOfBoard); for (list<FO>::iterator it = wave2.begin(); it != wave2.end(); it++) { //Trajectory equation 02 } if (i1 > 15 * 60) { wave1.clear();//Final clear for accident wave2.clear(); return 1; } return 0; }
|
Tips:记得 return 1; 的时候顺便把门关上,不然可就糟了。
10.1.碰撞检测
1 2 3 4 5 6 7 8 9 10
| bool Game::checkCollision(sf::Sprite obj1, sf::Sprite obj2) { sf::FloatRect f1 = obj1.getGlobalBounds(); sf::FloatRect f2 = obj2.getGlobalBounds(); if (f1.intersects(f2)) { return true; } return false; }
|
10.2.碰撞处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void Game::enemyCollisionProcessing(list<FO>::iterator it) { for (list<sf::Sprite>::iterator itAmmo = playerBullets.begin(); itAmmo != playerBullets.end(); itAmmo++) { if (checkCollision(it->hero, *itAmmo)) { enemyUnderAttack(it, itAmmo);
if (it->HealthPoint <= 0) { enemyCrash(it); } } } }
|
Tips:自机用的碰撞处理也是差不多的,稍微改改就能用。
11&12.敌机爆炸过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void Game::enemyCrash(list<FO>::iterator it) { breakSound.play(); score += it->score; deathEff.setTexture(deathCircle); deathEff.setTextureRect(sf::IntRect(64, 0, 64, 64)); deathEff.setOrigin(32, 32); deathEff.setPosition(it->hero.getPosition().x + it->width * 0.25, it->hero.getPosition().y + it->height * 0.25); deathEff.setScale(0.1, 0.1); deathEffs.push_back(deathEff); deathEff.setScale(0.3, 0.06); deathEff.setRotation(rand() % 360); deathEffs.push_back(deathEff); it->hero.setPosition(-100, -100); }
|
13.计分和显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| void Game::boardDisplay() { mWindow.draw(front01);//Display main background mWindow.draw(front02); mWindow.draw(front03); mWindow.draw(front04);
switch (remnant) { case 3: lifeBoard.setTextureRect(sf::IntRect(0, 0, 272, 36)); break; case 2: lifeBoard.setTextureRect(sf::IntRect(0, 44, 272, 36)); break; case 1: lifeBoard.setTextureRect(sf::IntRect(0, 90, 272, 36)); break; default: ; }
lifeBoard.setScale(1.5, 1.5); lifeBoard.setPosition(830, 300); mWindow.draw(lifeBoard);
static string scoreStr; scoreStr = "Score: "; scoreStr += to_string(score); tempScore.setString(scoreStr); tempScore.setStyle(sf::Text::Italic); tempScore.setFont(font); tempScore.setCharacterSize(50); tempScore.setPosition(840, 50); mWindow.draw(tempScore); }
|
14.敌机炮弹处理
举了一个随机弹(尖弹)作为例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void Game::setSharpRandom(list<FO>::iterator it, double speed) { enemyBulletSound.play(); FO SharpRandom; SharpRandom.speed = speed; SharpRandom.theta = rand()%360; SharpRandom.width = 16; SharpRandom.height = 16; SharpRandom.hero.setTexture(allBullets1); SharpRandom.hero.setTextureRect(sf::IntRect(64, 64, 16, 16)); SharpRandom.hero.setOrigin(8, 8); SharpRandom.hero.setScale(1.5, 1.5); SharpRandom.hero.setPosition(it->hero.getPosition().x, it->hero.getPosition().y + it->height); SharpRandom.hero.setRotation(SharpRandom.theta / PI * 180.0 + 90); enemyBullets.push_back(SharpRandom); }
|
15.自机判定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| bool Game::checkPlayerCollision() { sf::Vector2f JP = julgePoint.getPosition(); JP.x -= 8; JP.y -= 8;
for (list<FO>::iterator it = enemyBullets.begin(); it != enemyBullets.end(); it++) { sf::FloatRect f = it->hero.getGlobalBounds();
f.width /= 2.0; f.height /= 2.0; if (f.contains(JP)) { return true; } } return false; }
|