這次是寫了兩個小遊戲,並從裡面學到一點 member function 的用法,還有字串跟血魔量的顯示。
今天學到了第一個是上一個遊戲的進階版
另外我也自己查了字串要怎麼在 window 上顯示
#include<iostream>
#include<SFML\Graphics.hpp>
#include<SFML\Window.hpp>
#include<SFML\System.hpp>
#include<sstream> // for string
using namespace sf;
using namespace std;
// 上一個遊戲的進化版
int main()
{
RenderWindow window(VideoMode(640, 480), "Simple game");
window.setFramerateLimit(60);
//記分板
// 計分用
int score = 0;
Font arial;
arial.loadFromFile("Arial.ttf");
ostringstream ssScore;
ssScore << "Score: " << score;
Text Score;
Score.setFont(arial);
Score.setCharacterSize(30);
Score.setPosition(0.f, window.getSize().y / 2);
Score.setString(ssScore.str());
//血量
int health = 3;
ostringstream ssHealth;
ssHealth << "Health: " << health;
Text Health;
Health.setFont(arial);
Health.setCharacterSize(30);
Health.setPosition(0.f, (window.getSize().y / 2 + 35));
Health.setString(ssHealth.str());
Text gameOver;
gameOver.setFont(arial);
gameOver.setFillColor(Color::Red);
gameOver.setCharacterSize(60);
gameOver.setPosition(window.getSize().x / 4, window.getSize().y / 2);
gameOver.setString("Game Over!");
//敵人 (黑球)
CircleShape hoop;
int dir = 0;
hoop.setRadius(50.f);
hoop.setFillColor(Color::White);
hoop.setOutlineThickness(2);
hoop.setOutlineColor(Color::Blue);
hoop.setPosition(Vector2f(0, 10.f));
//操作的腳色 (紅球)
CircleShape ball;
bool isShot = false; // 用來操作發射的子彈 / 跟滑鼠做互動
ball.setRadius(20.f);
ball.setFillColor(Color::Red);
ball.setPosition(Vector2f(0, window.getSize().y - ball.getRadius() * 3));
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
if (event.KeyPressed && event.key.code == Keyboard::Escape)
window.close();
}
//Update hoop
if (hoop.getPosition().x <= 0)
dir = 1;
else if (hoop.getPosition().x + 2 * hoop.getRadius() >= window.getSize().x)
dir = 0;
if (health != 0)
{
if (dir == 0)
{
hoop.move(-5.f, 0);
}
else
{
hoop.move(5.f, 0);
}
}
else // 當血量歸零時就不動
{
hoop.setPosition(hoop.getPosition().x, hoop.getPosition().y);
}
//Update ball
// for shooting
if (Keyboard::isKeyPressed(Keyboard::Space) && !isShot)
isShot = true;
if (!isShot)
ball.setPosition(Mouse::getPosition(window).x, ball.getPosition().y);
else
ball.move(0, -5.f);
//for collision ball
if (health > 0)
{
if (ball.getPosition().y < 0) // 撞到牆生命就 - 1
{
//Reset the ball
isShot = false;
ball.setPosition(Vector2f(0, window.getSize().y - ball.getRadius() * 3));
//扣血
health--;
ssHealth.str("");
ssHealth << "Health: " << health;
Health.setString(ssHealth.str());
}
else if (ball.getGlobalBounds().intersects(hoop.getGlobalBounds())) // 撞到黑球 point + 1
{
//Reset the ball
isShot = false;
ball.setPosition(Vector2f(0, window.getSize().y - ball.getRadius() * 3));
score++;
// 增加分數
ssScore.str("");
ssScore << "Score: " << score;
Score.setString(ssScore.str());
}
}
else // 遊戲終止
{
isShot = false;
ball.setPosition(Vector2f(0, window.getSize().y - ball.getRadius() * 3));
ball.setPosition(ball.getPosition().x, ball.getPosition().y); //讓紅球停在原地
ssHealth.str("");
ssScore.str("");
}
//Drawing
window.clear(Color::Black);
window.draw(hoop);
window.draw(ball);
window.draw(Score);
window.draw(Health);
if (health == 0)
window.draw(gameOver);
window.display();
}
}
首先,這一段是我為了計分(螢幕顯示左下角),而引進的字串
每個段的解釋都在他的註解上
int score = 0;
Font arial; // 宣告一個 object 叫 arial
arial.loadFromFile("Arial.ttf"); // 從同的資料夾中取 arial 的字檔
ostringstream ssScore; //他是一個 output stream class -> 也就是專門在印出東西的
ssScore << "Score: " << score; // 可見 << 也被 operator overloading
Text Score; // 這個就是文字了
Score.setFont(arial); // 想要甚麼文字
Score.setCharacterSize(30); // 文字大小
Score.setPosition(0.f, window.getSize().y / 2); //位置
Score.setString(ssScore.str()); //直接取用 output stream 的字串就可以顯示
// 最後記得要 draw 出來就好了!!
基本上就是照著邏輯寫就可以寫出來了,但是要注意的是那個 .tff 檔要放在跟專案同個資料夾裡面才會被讀到,其實基本上要讀檔案都是要放在同個資料夾裡,除非你有設定絕對路徑。
這個遊戲是躲貓貓(真的是字面上的意思)!
簡單說就是要讓我們操作的角色(狗狗)躲開螢幕右邊隨機出現的貓咪。
#include<iostream>
#include<SFML\Graphics.hpp>
#include<SFML\Window.hpp>
#include<SFML\System.hpp>
#include<sstream>
using namespace sf;
using namespace std;
/*
【這次會用 texture 還有 Sprite 做一些事情】
*/
int main()
{
//srand(time(NULL));
RenderWindow window(VideoMode(640, 480), "Cat Do(d)ge");
window.setFramerateLimit(60);
/*===========================================================//
【Cats object】
//===========================================================*/
Texture catTex;
Sprite cat;
if (!catTex.loadFromFile("textures/cat.png"))
throw "couldnt load cat.png!";
cat.setTexture(catTex);
cat.setScale(Vector2f(0.3f, 0.3f));
int catSpawnTimer = 15;
std::vector<Sprite>cats;
cats.push_back(Sprite(cat));
/*===========================================================//
【Dogge object】
//===========================================================*/
Texture doggeTex;
Sprite dogge;
if (!doggeTex.loadFromFile("textures/dogge.png"))
throw "couldnt load dogge.png!";
dogge.setTexture(doggeTex);
dogge.setScale(Vector2f(0.15f, 0.15f));
/*===========================================================//
【Dogge hp】
//===========================================================*/
int hp = 10;
RectangleShape hpBar;
hpBar.setFillColor(Color::Red);
hpBar.setSize(Vector2f((float)hp * 20.f, 20.f));
hpBar.setPosition(Vector2f(200.f, 10.f));
// 【game loop】
while (window.isOpen() && hp > 0) // hp < 0 的時候就 shut down
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Escape)
window.close();
}
// 【Update】
// 【Update】 Dogge (Player)
dogge.setPosition(dogge.getPosition().x, Mouse::getPosition(window).y);
if (dogge.getPosition().y > window.getSize().y - dogge.getGlobalBounds().height)
dogge.setPosition(dogge.getPosition().x, window.getSize().y - dogge.getGlobalBounds().height);
if (dogge.getPosition().y < 0)
dogge.setPosition(dogge.getPosition().x, 0);
// 【Update】Cats (Enemies)
// 【Update】Cats movement & spawning
for (size_t i = 0; i < cats.size(); i++)
{
cats[i].move(-7.f, 0.f);
// 當 cat 跑出螢幕外面的時候,讓它消失
if (cats[i].getPosition().x < 0 - cat.getGlobalBounds().width)
cats.erase(cats.begin() + i);
}
if (catSpawnTimer < 40)
catSpawnTimer++;
if (catSpawnTimer >= 40)
{
cat.setPosition(window.getSize().x, rand()%int(window.getSize().y - cat.getGlobalBounds().height)); // getGlobalBound => 抓到他的邊界
cats.push_back(Sprite(cat));
catSpawnTimer = 0;
}
// 【Update】Collision
for (size_t i = 0; i < cats.size(); i++)
{
if (dogge.getGlobalBounds().intersects(cats[i].getGlobalBounds()))
{
hp--;
cats.erase(cats.begin() + i);
}
}
// 【Update】 UI
hpBar.setSize(Vector2f((float)hp * 20.f, 20.f));
// 【Draw】
window.clear(Color::Black);
// 【Draw】dogge
window.draw(dogge);
//【Draw】Cat
for (size_t i = 0; i < cats.size(); i++)
{
window.draw(cats[i]);
}
// 【Draw】UI
window.draw(hpBar);
window.display();
}
}
最後會長這樣
在這個遊戲裡面有幾個小重點
圖片要找背景是透明的,也就是你會看到圖片後面是有小方格的(但是有些會是透明),像是這個
而且這種檔案只有 .png 所以在找圖的時候要注意一下(有時候後面的方格會騙你)
幾個新增功能:
getGlobalBound // => 抓到他的邊界
rand = random
push_back
碰撞寫法:
if (dogge.getGlobalBounds().intersects(cats[i].getGlobalBounds())) // intersect = 碰到誰
{
hp--;
cats.erase(cats.begin() + i);
}
原本讓我有點苦惱的血量還有魔量的顯示問題,好像就在這次的示範裡面解決了,簡單說就是要設
int hp = 10;
int mp = 430;
//建立 hp mp 所要用的方塊
RectangleShape hpBar;
hpBar.setFillColor(Color::Red);
hpBar.setSize(Vector2f((float)hp * 20.f, 20.f));
hpBar.setPosition(Vector2f(200.f, 10.f));
RectangleShape mpBar;
mpBar.setFillColor(Color::Blue);
mpBar.setSize(Vector2f((float)mp * 20.f, 20.f));
mpBar.setPosition(Vector2f(250.f, 10.f));
//最後記得要在 Update 的地方寫 他新的更新,不然就會永遠是一開始初始化的值
//例如在 Collision 要 hp--; 或是 使用技能的時候 mp -= 30;
hpBar.setSize(Vector2f((float)hp * 20.f, 20.f));
mpBar.setSize(Vector2f((float)hp * 20.f, 20.f));
一樣也是 這個 YouTuber 的影片!
字體的嵌入:
向量: