iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
Software Development

三十天內用C++寫出一個小遊戲系列 第 28

Day 28 - 設籍有關涉及射擊的射擊遊戲

Intro

這次是寫了兩個小遊戲,並從裡面學到一點 member function 的用法,還有字串跟血魔量的顯示。

Getting Started

tiny instance1

今天學到了第一個是上一個遊戲的進階版

另外我也自己查了字串要怎麼在 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 檔要放在跟專案同個資料夾裡面才會被讀到,其實基本上要讀檔案都是要放在同個資料夾裡,除非你有設定絕對路徑。

tiny instance2

這個遊戲是躲貓貓(真的是字面上的意思)!

簡單說就是要讓我們操作的角色(狗狗)躲開螢幕右邊隨機出現的貓咪。

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

	}

}

最後會長這樣

在這個遊戲裡面有幾個小重點

  • 圖片要找背景是透明的,也就是你會看到圖片後面是有小方格的(但是有些會是透明),像是這個

    dogge.png

    而且這種檔案只有 .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)); 

Ref


上一篇
Day 27 - 看起來很無聊又很好玩的遊戲開發
下一篇
Day 29 - 這個遊戲製作人瘋了吧
系列文
三十天內用C++寫出一個小遊戲30

尚未有邦友留言

立即登入留言