如何使用C++和SFML创建自己的类
How to make my own classes with C++ and SFML
在学习了C++的基础知识后,我目前开始使用SFML。我已经了解了数组,引用和它之前的所有内容,但一直在努力掌握使用类的概念。
在SFML中,我创建了一个简单的精灵移动程序,但是,我想将此信息移动到一个类中(假设它将被称为"玩家"(。我搞砸了很多,但我无法让它工作。
我尝试在类中创建一个检查玩家输入的函数,但我无法访问我在 main 中创建的精灵。我想将与玩家相关的所有内容都移到 Player 类中,但需要一些建议。
正确的方法是什么?(请不要说回去学习课程,这是我想了解它们的地方!
主.cpp
#include <SFML/Graphics.hpp>
#include <string>
#include <iostream>
int main()
{
//character position
enum Direction{ Down, Left, Right, Up };
sf::Vector2i source(1, Down);
//window
sf::RenderWindow window(sf::VideoMode(1200, 700), "Testing");
window.setKeyRepeatEnabled(false);
//player character
sf::Texture pTexture;
sf::Sprite pSprite;
if(!pTexture.loadFromFile("image/playerSprite.png"))
std::cout << "Texture Error" << std::endl;
pSprite.setTexture(pTexture);
pSprite.setScale(1.5f, 1.5f);
//game loop
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) //move up
{
source.y = Up;
pSprite.move(0, -0.2);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) //move down
{
source.y = Down;
pSprite.move(0, 0.2);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) //move right
{
source.y = Right;
pSprite.move(0.2, 0);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) //move left
{
source.y = Left;
pSprite.move(-0.2, 0);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
pSprite.setTextureRect(sf::IntRect(source.x * 32, source.y * 32, 32, 32));
window.draw(pSprite);
window.display();
}
return 0;
}
免责声明:你不应该期待这样的答案,你真的应该阅读更多关于 OOP 的内容来理解重点,这与 SFML 无关,这只是基本的重构。
如何使用 OOP 思考
首先,在编写功能之前,您应该设计真正适合这种情况的 OOP 结构。将每个类视为整体的一部分,这就是您的程序。实际上,类只是具有有用方法的数据聚合,它仅以有意义的方式影响类中的数据(或通过方法参数提供的数据(。
请参阅C++的基础知识(更多 OOP 部分(以了解如何使其在 C++ 中工作。这些概念在其他编程语言中是相似的。
使用您提供的代码
您要求的是一个 Player 类,将播放器代码从程序主逻辑中取出是个好主意。你需要问问自己:"我的播放器代码需要什么才能工作?
玩家类
基本上,你的玩家只是一个精灵和一个位置。因此,您将这些数据作为私有成员封装到 Player 类中。这样可以防止其他代码弄乱玩家数据。若要使用玩家数据,需要在类中提供每个方法仅影响玩家的方法。
纹理和精灵
我故意将纹理保留在播放器之外。纹理是重的对象,这就是为什么 Sprite 对象只保留指向它的指针。精灵是轻量级的,可以轻松更改和复制。纹理对象和其他资产的管理是另一个主题,尽管这是我自己的资源管理器代码。
自选
我没有花时间对代码进行太多更改,但是您可以更改处理移动的方式,只制作一个具有参数Player::Direction
的"move"方法。
为了帮助你更多,并给你更多关于这个主题的指导方针,我使用了"前向声明",并将你的方向枚举移到了类中。这可能不是实现你想要的东西的最佳方式,但我只更改了你自己的代码以避免让你迷路。
《守则》
无论如何,这是我的尝试。
玩家.h
#ifndef PLAYER_H_
#define PLAYER_H_
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Sprite.hpp>
// Forward Declaration
namespace sf {
class Texture;
}
// provide your namespace to avoid collision/ambiguities
namespace test {
/*
*
*/
class Player: public sf::Drawable {
public:
enum Direction {
Down, Left, Right, Up
};
Player(const sf::Texture& playerTexture);
virtual ~Player();
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
void moveUp();
void moveDown();
void moveLeft();
void moveRight();
private:
sf::Sprite mSprite;
sf::Vector2i mSource;
};
} /* end namespace test */
#endif /* PLAYER_H_ */
播放器.cpp
#include "Player.h"
// you need this because of forward declaration
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
namespace test {
Player::Player(const sf::Texture& imagePath) :
mSprite(imagePath),
mSource(1, Player::Down) {
// do not need that line anymore, thanks to initialiser list
//pSprite.setTexture(pTexture);
mSprite.setScale(1.5f, 1.5f);
}
Player::~Player() {
// TODO Auto-generated destructor stub
}
void Player::draw(sf::RenderTarget& target, sf::RenderStates states) const {
target.draw(mSprite, states);
}
void Player::moveUp() {
mSource.y = Up;
mSprite.move(0, -0.2);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
mSprite.setTextureRect(sf::IntRect(mSource.x * 32, mSource.y * 32, 32, 32));
}
void Player::moveDown() {
mSource.y = Down;
mSprite.move(0, 0.2);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
}
void Player::moveLeft() {
mSource.y = Left;
mSprite.move(-0.2, 0);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
}
void Player::moveRight() {
mSource.y = Right;
mSprite.move(0.2, 0);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
}
} /* end namespace test */
主.cpp
#include <SFML/Graphics.hpp>
//#include <string> // not used for now
#include <iostream>
// don't forget to include your own header
#include "Player.h"
int main() {
// just to save typing the "std::"
using std::cout;
using std::endl;
using std::cerr;
//window
sf::RenderWindow window(sf::VideoMode(1200, 700), "Testing");
window.setKeyRepeatEnabled(false);
//player texture
sf::Texture pTexture;
if (!pTexture.loadFromFile("image/playerSprite.png")) {
cerr << "Texture Error" << endl;
}
test::Player thePlayer(pTexture);
//game loop
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
window.clear();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) //move up
{
thePlayer.moveUp();
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) //move down
{
thePlayer.moveDown();
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) //move right
{
thePlayer.moveRight();
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) //move left
{
thePlayer.moveLeft();
}
window.draw(thePlayer);
window.display();
}
return 0;
}
其他良好做法
访问器或 Getters/seters 是成员函数,它允许访问类私有成员。
在你的代码中,你可以做这样的事情:
class Player {
public:
Player(const sf::Texture& playerTexture);
virtual ~Player();
// to give access to a const reference of the sprite
// One could call it like: sf::Sprite mySprite = myPlayerObject.getSprite();
// notice also that the method itself is const, which assure you that
// myPlayerObject won't change by calling getSprite()
const sf::Sprite& getSprite() const{
return mSprite;
}
// setSprite is not a const method, so it will change the data
// inside myPlayerObject
void setSprite(const sf::Sprite& newSprite){
mSprite = newSprite;
}
private:
sf::Sprite mSprite;
sf::Vector2i mSource;
};
- 您应该在什么时候创建自己的异常类型
- 如何在C++中创建自己的循环版本?
- 如何在C++中创建自己的编译密钥
- 我正在尝试在视觉工作室上创建自己的库/源函数
- 创建自己的owner_ptr类;在传递堆栈或静态分配的地址时如何避免UB?
- 如何为C++映射创建自己的字符串比较对象
- (如何)在提升几何中创建自己的多边形类型并与之一起使用multi_polygon类型?
- 为什么要创建自己的自定义异常类
- 使用 const char * 与 char * 创建自己的字符串
- 创建自己的线性过滤器
- 如果不在派生类实现中执行此操作,"basic_streambuf"是否会创建自己的获取/放置区域?
- 在 C++ 中创建自己的错误处理机制
- 如何使用新字符串而不是分配器创建自己的字符串类型向量?
- 在C++中创建自己的运算符**?
- Windows:创建自己的杀死程序
- 创建自己的异常(2种方法)C
- 是否可以创建自己的QT注释
- 电子内部或外部方法创建自己的右键单击上下文菜单 - Windows
- 创建自己的链表时出错
- 在 C++ 中创建自己的数据范围