这次写了一个小游戏。里面包含了前几次练习的功能,但是有些东西是用 class 来写出来,感觉很新奇!
这次的游戏大概就是像宇宙飞船一样,右边会跑出移动中的敌人,我们在左边要射击他们。
因为我之前对 sprite 还有 texture 的概念不太了解,所以查了一下这一篇
所以可以简单的分类说
Sprite : 是一个物件,可以透过 sprite 来做各种操作!
Texture: 就是外面的包装纸,你想要怎样的图案,就贴在 sprite 上面!
下面是我试做了一个小小的实验,看看她到底是怎麽运作的。
#include<iostream>
#include<SFML\Graphics.hpp>
#include<SFML\Window.hpp>
#include<SFML\System.hpp>
#include<sstream>
#include<cstdlib>
#include<math.h>
#include<vector>
using namespace sf;
using namespace std;
int main()
{
RenderWindow window(VideoMode(800, 600), "Space war");
window.setFramerateLimit(200);
Texture doggeTex;
Sprite dogge;
if (!doggeTex.loadFromFile("textures/dogge.png"))
throw "Could not find file: dogge.png!";
dogge.setTexture(doggeTex);
dogge.setScale(Vector2f(0.15f, 0.15f));
// 【game loop】
while (window.isOpen()) // 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】
//===========================================================*/
if (Keyboard::isKeyPressed(Keyboard::W))
dogge.move(0.f, -5.f);
if (Keyboard::isKeyPressed(Keyboard::S))
dogge.move(0.f, 5.f);
if (Keyboard::isKeyPressed(Keyboard::D))
dogge.move(5.f, 0.f);
if (Keyboard::isKeyPressed(Keyboard::A))
dogge.move(-5.f, 0.f);
/*===========================================================//
【Draw】
//===========================================================*/
window.clear();
window.draw(dogge);
window.display();
}
}
了解了 Sprite 还有 texture 的概念我们就可以开始了!
首先我们要宣告三个 class ,包含 player、enemy、还有 bullets
class Bullet
{
public:
Sprite shape;
Bullet(Texture* texture, Vector2f pos)
{
this->shape.setTexture(*texture);
this->shape.setScale(Vector2f(0.1f, 0.1f));
this->shape.setPosition(pos);
}
~Bullet() {}
};
class Player
{
public:
Sprite shape;
Texture* texture;
int HP;
int HPMax;
vector<Bullet> bullets;
Player(Texture* texture)
{
this->HPMax = 10;
this->HP = this->HPMax;
this->texture = texture;
this->shape.setTexture(*texture);
this->shape.setScale(Vector2f(0.15f, 0.15f));
}
~Player() {}
};
class Enemy
{
public:
Sprite shape;
int HP;
int HPMax;
Enemy(Texture* texture, Vector2u windowSize)
{
this->HPMax = rand() % 3 + 1; // 随机血量 -> 1~4
this->HP = this->HPMax;
this->shape.setTexture(*texture);
this->shape.setScale(0.3f, 0.3f);
this->shape.setPosition(windowSize.x - this->shape.getGlobalBounds().width, rand() % (int)(windowSize.y - this->shape.getGlobalBounds().height));
}
~ Enemy() {}
};
这三个 class 中可以看到有他们的血量,还有关於 texture size 方面的设定,这些都可以在设定 class 的时候设定好。比较不好的地方是,现在他全部都是用 public (虽然只有我一个人再写),但有些他们独有的特性还是尽量要用 private 的方式写会比较好。
srand(time(NULL)); // 这一部分是为了 random 函数的,但详细不太清楚
RenderWindow window(VideoMode(800, 600), "Space war");
window.setFramerateLimit(200);
/*===========================================================//
Init Text
//===========================================================*/
Font font;
font.loadFromFile("font/Arial.ttf");
/*===========================================================//
Init Textures
//===========================================================*/
Texture playerTex;
playerTex.loadFromFile("textures/spaceship.png");
Texture enemyTex;
enemyTex.loadFromFile("textures/enemies.png");
Texture bulletTex;
bulletTex.loadFromFile("textures/MissileRight.png");
/*===========================================================//
UI init
//===========================================================*/
Text scoreText;
scoreText.setFont(font);
scoreText.setCharacterSize(20);
scoreText.setFillColor(Color::White);
scoreText.setPosition(10.f, 10.f);
Text gameOverText;
gameOverText.setFont(font);
gameOverText.setCharacterSize(30);
gameOverText.setFillColor(Color::Red);
gameOverText.setPosition(100.f, window.getSize().y / 2);
gameOverText.setString("Game Over!!!!");
/*===========================================================//
Player init
//===========================================================*/
int score = 0;
Player player(&playerTex);
int shootTimer = 20;
Text HpText;
HpText.setFont(font);
HpText.setCharacterSize(12);
HpText.setFillColor(Color::White);
/*===========================================================//
Enemy init
//===========================================================*/
int enemySpawnTimer = 0;
vector<Enemy> enemies;
enemies.push_back(Enemy(&enemyTex, window.getSize()));
Text eHpText;
eHpText.setFont(font);
eHpText.setCharacterSize(12);
eHpText.setFillColor(Color::White);
接下来这一段是在初始化,也就是设定文字,设定 player, enemy, 还有 bullet 的一些设定。
/*===========================================================//
【game loop】
//===========================================================*/
while (window.isOpen()) // 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();
}
if (player.HP > 0)
{
/*===========================================================//
【Update】 player
//===========================================================*/
// movement
int speedInc = 0;
if (Keyboard::isKeyPressed(Keyboard::W))
player.shape.move(0.f, -5.f - speedInc);
if (Keyboard::isKeyPressed(Keyboard::S))
player.shape.move(0.f, 5.f + speedInc);
if (Keyboard::isKeyPressed(Keyboard::D))
player.shape.move(5.f + speedInc, 0.f);
if (Keyboard::isKeyPressed(Keyboard::A))
player.shape.move(-5.f - speedInc, 0.f);
// collision with window
if (player.shape.getPosition().x <= 0) // left
player.shape.setPosition(0.f, player.shape.getPosition().y);
if (player.shape.getPosition().x >= window.getSize().x - player.shape.getGlobalBounds().width) // right
player.shape.setPosition(window.getSize().x - player.shape.getGlobalBounds().width, player.shape.getPosition().y);
if (player.shape.getPosition().y <= 0) // Top
player.shape.setPosition(player.shape.getPosition().x, 0.f);
if (player.shape.getPosition().y >= window.getSize().y - player.shape.getGlobalBounds().height) // bottom
player.shape.setPosition(player.shape.getPosition().x, window.getSize().y - player.shape.getGlobalBounds().height);
// Text set
HpText.setPosition(player.shape.getPosition().x, player.shape.getPosition().y - HpText.getGlobalBounds().height);
HpText.setString(to_string(player.HP) + "/" + to_string(player.HPMax));
/*===========================================================//
【Update】 controls
//===========================================================*/
if (shootTimer < 20)
shootTimer++;
if (Mouse::isButtonPressed(Mouse::Left) && shootTimer >= 20)
{
player.bullets.push_back(Bullet(&bulletTex, player.shape.getPosition()));
shootTimer = 0; // reset timer
}
/*===========================================================//
【Update】 Bullet
//===========================================================*/
for (size_t i = 0; i < player.bullets.size(); i++)
{
// Move
player.bullets[i].shape.move(20.f, 0.f);
// Out of window bounds
if (player.bullets[i].shape.getPosition().x > window.getSize().x)
{
player.bullets.erase(player.bullets.begin() + i);
break;
}
// enemy collision
for (size_t k = 0; k < enemies.size(); k++)
{
if (player.bullets[i].shape.getGlobalBounds().intersects(enemies[k].shape.getGlobalBounds()))
{
if (enemies[k].HP <= 1)
{
score += enemies[k].HPMax;
enemies.erase(enemies.begin() + k);
}
else
enemies[k].HP--;
player.bullets.erase(player.bullets.begin() + i);
break;
}
}
}
/*===========================================================//
【Update】 Enemy
//===========================================================*/
if (enemySpawnTimer < 30)
enemySpawnTimer++;
if (enemySpawnTimer >= 30)
{
enemies.push_back(Enemy(&enemyTex, window.getSize()));
enemySpawnTimer = 0;
}
for (size_t i = 0; i < enemies.size(); i++)
{
enemies[i].shape.move(-2.3f, 0.f);
if (enemies[i].shape.getPosition().x <= 0 - enemies[i].shape.getGlobalBounds().width)
{
enemies.erase(enemies.begin() + i);
break;
}
if (enemies[i].shape.getGlobalBounds().intersects(player.shape.getGlobalBounds()))
{
enemies.erase(enemies.begin() + i);
player.HP--;
break;
}
}
/*===========================================================//
【Update】 UI
//===========================================================*/
scoreText.setString("Score: " + to_string(score));
}
/*===========================================================//
【Draw】
//===========================================================*/
window.clear();
/*===========================================================//
【Draw】 player
//===========================================================*/
window.draw(player.shape);
/*===========================================================//
【Draw】 bullets
//===========================================================*/
for (size_t i = 0; i < player.bullets.size(); i++)
{
window.draw(player.bullets[i].shape);
}
/*===========================================================//
【Draw】 Enemies
//===========================================================*/
for (size_t i = 0; i < enemies.size(); i++)
{
eHpText.setString(to_string(enemies[i].HP) + "/" + to_string(enemies[i].HPMax));
eHpText.setPosition(enemies[i].shape.getPosition().x, enemies[i].shape.getPosition().y - eHpText.getGlobalBounds().height);
window.draw(eHpText);
window.draw(enemies[i].shape);
}
/*===========================================================//
【Draw】 UI
//===========================================================*/
window.draw(scoreText);
window.draw(HpText);
if (player.HP <= 0)
window.draw(gameOverText);
window.display();
}
再来这一段,就是游戏的 loop,
首先可以看到 player 的部分
/*===========================================================//
【Update】 player
//===========================================================*/
// movement
int speedInc = 0;
if (Keyboard::isKeyPressed(Keyboard::W))
player.shape.move(0.f, -5.f - speedInc);
if (Keyboard::isKeyPressed(Keyboard::S))
player.shape.move(0.f, 5.f + speedInc);
if (Keyboard::isKeyPressed(Keyboard::D))
player.shape.move(5.f + speedInc, 0.f);
if (Keyboard::isKeyPressed(Keyboard::A))
player.shape.move(-5.f - speedInc, 0.f);
// collision with window
if (player.shape.getPosition().x <= 0) // left
player.shape.setPosition(0.f, player.shape.getPosition().y);
if (player.shape.getPosition().x >= window.getSize().x - player.shape.getGlobalBounds().width) // right
player.shape.setPosition(window.getSize().x - player.shape.getGlobalBounds().width, player.shape.getPosition().y);
if (player.shape.getPosition().y <= 0) // Top
player.shape.setPosition(player.shape.getPosition().x, 0.f);
if (player.shape.getPosition().y >= window.getSize().y - player.shape.getGlobalBounds().height) // bottom
player.shape.setPosition(player.shape.getPosition().x, window.getSize().y - player.shape.getGlobalBounds().height);
// Text set
HpText.setPosition(player.shape.getPosition().x, player.shape.getPosition().y - HpText.getGlobalBounds().height);
HpText.setString(to_string(player.HP) + "/" + to_string(player.HPMax));
一样是包含了上下左右的移动,还有与 window 撞到的反应,最後是会显示再 player 头上的 血量。
下面这段则是 bullet 与敌人的碰撞还有移动。
/*===========================================================//
【Update】 Bullet
//===========================================================*/
for (size_t i = 0; i < player.bullets.size(); i++)
{
// Move
player.bullets[i].shape.move(20.f, 0.f);
// Out of window bounds
if (player.bullets[i].shape.getPosition().x > window.getSize().x)
{
player.bullets.erase(player.bullets.begin() + i);
break;
}
// enemy collision
for (size_t k = 0; k < enemies.size(); k++)
{
if (player.bullets[i].shape.getGlobalBounds().intersects(enemies[k].shape.getGlobalBounds()))
{
if (enemies[k].HP <= 1)
{
score += enemies[k].HPMax;
enemies.erase(enemies.begin() + k);
}
else
enemies[k].HP--;
player.bullets.erase(player.bullets.begin() + i);
break;
}
}
}
/*===========================================================//
【Update】 Enemy
//===========================================================*/
if (enemySpawnTimer < 30)
enemySpawnTimer++;
if (enemySpawnTimer >= 30)
{
enemies.push_back(Enemy(&enemyTex, window.getSize()));
enemySpawnTimer = 0;
}
for (size_t i = 0; i < enemies.size(); i++)
{
enemies[i].shape.move(-2.3f, 0.f);
if (enemies[i].shape.getPosition().x <= 0 - enemies[i].shape.getGlobalBounds().width)
{
enemies.erase(enemies.begin() + i);
break;
}
if (enemies[i].shape.getGlobalBounds().intersects(player.shape.getGlobalBounds()))
{
enemies.erase(enemies.begin() + i);
player.HP--;
break;
}
}
值得注意的是,我们在碰撞到的时候都记得要 break loop,否则会当掉。
剩下的就是 一些 draw 的功能了。
简单的来说,这次的小游戏包含了前几次的练习,再加上一些使用 class 的方式。
接下来就是我自己的小游戏了!!!!!
这个是这次的示范影片
<<: day26 老板我赶时间,给我最快完成的料理 select
>>: Day 28 : 撰写LineBot,利用短短三天认识自动化机器人(中)
Virtual Judge ZeroJudge Zerojudge 直接破图 题意 输入一数字 N...
Sum of a sequence our task is to make function, wh...
天有不测风云,任何单位就算已经完全做到了前面四大面向,你应该还是要在内部建立一套事件回应的机制,透过...
前言: 因为套件有可能会有版本问题的缘故,所以我照网路上的推荐用Anaconda,来管理我每个Pro...
欢乐的时光总是过得特别快,不知不觉连假就要结束了,不过威尔猪也太悲催,为了铁人赛,中秋节还要在电脑...