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


<<:  予焦啦!结论与展望(一):Hoddarla 专案的过去、现在与未来

>>:  Progressive Web App 针对应用操作介面优化操作体验 (27)

Day 1 - JavaScript Drum Kit

前言 JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用...

【Day 10】os模组

OS模组(Python内建) 说明 : os模组是一种与作业系统相关的模组,提供数十种与作业系统沟通...

[Day4]在Rock pi4 上安装Armbian

下载安装包 Armbian Buster 和 Focal之後的作业系统基於完全不同的作业系统,两者所...

[Day 26] 使用Heroku部署机器学习API

使用 Heroku 部署机器学习 API 今日学习目标 动手部署自己的机器学习 API 使用 Her...

属性图模型 Property Graph Diagram

Property Graph Diagram (属性图) 与前一篇文章一样是一种针对概念的建模图,他...