用C++和SFML写游戏-Game类的创建(2)

这一节我们将会学习到游戏的基本结构,其中的内容包括了:

  • Game类的创建
  • 什么是帧数
  • Player类的创建
  • 事件管理器

Game类

在上一节中,我们用尽可能少的代码创建了一个基本游戏,它包括了:

  • 窗口的创建
  • 图形的绘制
  • 处理用户的输入
  • 将游戏元素绘制到屏幕上

上一节中的实例代码全部写在了 main 函数了,并没有使用到 C++ 的面向对象特性。为了提高我们代码的可复用性,从本节开始,我们将会一步步的使用 OOP 设计思想封装我们的游戏基本元素,搭建出一个游戏框架的雏形。首先是Game类的实现,如下所示:

class Game {
       public:
           Game(const Game&) = delete;
           Game& operator=(const Game&) = delete;
           Game();
           void run();
       private:
           void processEvents();
           void update();
           void render();
           sf::RenderWindow _window;
           sf::CircleShape  _player;
};
int main(int argc,char* argv[]) {
    Game game;
    game.run();
		return 0; 
}

Game类中的 =delete 是为了删除C++类默认的拷贝构造函数以及拷贝赋值运算符,有关这方面的介绍可以去了解一下,这里就不做赘述。

可以看到,我们的 main 函数里面不在包含任何循环,游戏的运行只需要调用 Game 类中的 run 方法。对于类中的其它方法 processEvents(),update(),render() ,下面一一介绍:

  • processEvents(): 这里处理用户的输入
  • update(): 更新游戏的状态,计算出下一步
  • render(): 绘制游戏的画面(渲染)

下面,先做简单的实现,为了简单,我们的游戏角色先用一个圆形表示:

1、构造方法的实现

Game::Game() : _window(sf::VideoMode(800, 600),"02_Game_Archi"),
_player(150) {
    _player.setFillColor(sf::Color::Blue);
    _player.setPosition(10, 20);
}

2、Game.run() 隐藏了 main 里面的循环体

void Game::run() {
    while (_window.isOpen()) {
        processEvents();
        update();
        render();
    }
}

3、processEvents() 用于处理用户的输入,这里它只是简单地通过轮询从上一帧到现在的事件。例如点击按钮或者按下键盘按键,我们这里就只检查用户按下窗口的关闭按钮以及 ESC 键,然后窗口就会关闭。

void Game::processEvents() {
    sf::Event event;
    while (_window.pollEvent(event)) {
        if ((event.type == sf::Event::Closed)
          || ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))) {
            _window.close();
        }
    }
}

4、update() 方法更新了我们的游戏逻辑。但是现在我们还没有具体的逻辑实现,后面将会加入。

void Game::update() {}

5、render() 负责将游戏画面渲染到屏幕上。首先默认用 sf::Color::Black 清除窗口,然后将我们的游戏对象渲染到窗口,最后在屏幕上显示出来。

void Game::render() {
    _window.clear();
    _window.draw(_player);
    _window.display();
}

运行效果跟前一节是一样的

02_Game_Archi

FPS

FPS(frames per second): 每秒的帧数,一帧就是通常指一个画面。

由于电脑硬件的不同,同一个游戏在不同电脑的运行速度很可能是不一样的。如果开发者没有注意到这个问题,可能会出现角色穿墙的情况,如 图 1 所示。

图 1

为了解决这个问题,通常有三种方案,一是动态时间步长,二是固定时间步长,三是两者一起使用。

1、动态时间步长

由于每台计算机的性能可能不一样,因此处理一帧所花费的时间也是不同的,但是现实世界中时间的流逝是一样的。 因此这种方法更新的主要原理就是计算出上帧到现在所花费的时间,然后将这个时间传入到 update() 函数。

最后的绘制效果如 图 2 所示,可以看到在运行快的计算机上面用户的帧数更高,而运行较慢的计算机上帧数较低,但是完成这个过程所花费的时间是一样的。

图2

这个过程的代码大概长这个样子:

void Game::run() {
    sf::Clock clock;
    while (_window.isOpen()) {
        processEvents();
        update(clock.restart());
        render();
    }
}

这时候,我们的 update 方法也需要做些改变:

void update(sf::Time deltaTime);

deltaTime 参数代表的是上次调用 update 到现在经过了多少时间


2、固定时间步长

自上一次游戏循环过去了一定量的真实时间。 需要为游戏的“当前时间”模拟推进相同长度的时间,以追上玩家的时间。 我们使用一系列的固定时间步长。 代码大致如下:

void Game::run(int frame_per_seconds) {
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;
    sf::Time TimePerFrame = sf::seconds(1.f/frame_per_seconds);
    while (_window.isOpen()) {
        processEvents();
        bool repaint = false;
        timeSinceLastUpdate += clock.restart();
        while (timeSinceLastUpdate > TimePerFrame) {
            timeSinceLastUpdate -= TimePerFrame;
            repaint = true;
            update(TimePerFrame);
        }
        if(repaint)
            render();
    }
}

在每帧开始的时候,timeSinceLastUpdate 表示的是 deltaTime,也就是上次两次 while 循环之间的时间差。 由于采用的是 固定时间步长(fix time step) 方法,如果机器得很慢,timeSinceLastUpdate 大于 fix time step,我们需要多做几次 update,这样才能保证游戏中的对象移动速度不会受机器性能的影响,它总是按照我们设置的速度前进。

3、最小时间步长

这个方法把前面两个方法结合。通过确保传入 update() 方法的时间参数不那么高使得游戏需要运行的足够快,也就是我们通过这个方法设置了最小的帧数,但是没有最大的。

就是将传入 update() 方法的时间参数不大于一个值,具体过程如图所示:

图 3

具体代码实现:

void Game::run(int minimum_frame_per_seconds)) {
    sf::Clock clock;
    sf::Time timeSinceLastUpdate;
    sf::Time TimePerFrame = sf::seconds(1.f/minimum_frame_per_seconds);
    while (_window.isOpen()) {
        processEvents();
        timeSinceLastUpdate = clock.restart();
        while (timeSinceLastUpdate > TimePerFrame) {
            timeSinceLastUpdate -= TimePerFrame;
            update(TimePerFrame);
        }
        update(timeSinceLastUpdate);
        render();
    }
}

在每一帧中,update() 方法都被调用,但是我们确保了传入参数不会太大。


下一节将会学习如何移动我们的角色?。

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
SFML is a cross-platform software development library written in C++ with bindings available for many programming languages. It provides a simple interface to the various components of your PC, to ease the development of games and multimedia applications. This book will help you become an expert of SFML by using all of its features to its full potential. It begins by going over some of the foundational code necessary in order to make our RPG project run. By the end of chapter 3, we will have successfully picked up and deployed a fast and efficient particle system that makes the game look much more ‘alive’. Throughout the next couple of chapters, you will be successfully editing the game maps with ease, all thanks to the custom tools we’re going to be building. From this point on, it’s all about making the game look good. After being introduced to the use of shaders and raw OpenGL, you will be guided through implementing dynamic scene lighting, the use of normal and specular maps, and dynamic soft shadows. However, no project is complete without being optimized first. The very last chapter will wrap up our project by making it lightning fast and efficient. What You Will Learn Dive deep into creating complex and visually stunning games using SFML, as well as advanced OpenGL rendering and shading techniques Build an advanced, dynamic lighting and shadowing system to add an extra graphical kick to your games and make them feel a lot more dynamic Craft your own custom tools for editing game media, such as maps, and speed up the process of content creation Optimize your code to make it blazing fast and robust for the users, even with visually demanding scenes Get a complete grip on the best practices and industry grade game development design patterns used for AAA projects Table of Contents Chapter 1. Under the Hood - Setting up the Backend Chapter 2. Its Game Time! - Designing the Project Chapter 3. Make It Rain! - Building a Particle System Chapter 4. Have Thy Gear Ready - Building Game Tools Chapter 5. Filling the Tool Belt - a few More Gadgets Chapter 6. Adding Some Finishing Touches - Using Shaders Chapter 7. One Step Forward, One Level Down - OpenGL Basics Chapter 8. Let There Be Light - An Introduction to Advanced Lighting Chapter 9. The Speed of Dark - Lighting and Shadows Chapter 10. A Chapter You Shouldnt Skip - Final Optimizations
好的,下面我会给你讲解如何使用SFML来实现n-puzzle游戏的可视化。首先,我们需要了解一下SFML是什么。 SFML是一个跨平台的图形库,它提供了简单易用的API,可以帮助我们快速创建2D游戏和图形应用程序。它支持多种编程语言,包括C++、Python、Java等。 接下来,我们可以开始实现n-puzzle游戏的可视化了。首先,我们需要创建一个窗口来显示游戏界面。以下是创建窗口的代码: ```cpp #include <SFML/Graphics.hpp> int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "N-Puzzle Game"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(sf::Color::White); // 绘制游戏界面 window.display(); } return 0; } ``` 这段代码创建了一个800x600大小的窗口,并且在每一帧中都会清空窗口并绘制游戏界面。接下来,我们需要实现游戏界面的绘制。 n-puzzle游戏的界面由一个N*N的网格组成,每个格子中都会有一个数字。我们可以使用SFML中的矩形来表示每个格子,并且使用文本来显示数字。以下是绘制游戏界面的代码: ```cpp const int N = 3; const int tileSize = 100; sf::RectangleShape tile(sf::Vector2f(tileSize, tileSize)); tile.setFillColor(sf::Color::White); tile.setOutlineColor(sf::Color::Black); tile.setOutlineThickness(2); sf::Font font; font.loadFromFile("arial.ttf"); sf::Text text("", font, tileSize / 2); text.setFillColor(sf::Color::Black); text.setStyle(sf::Text::Bold); text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int number = i * N + j + 1; if (number == N * N) continue; tile.setPosition(j * tileSize, i * tileSize); text.setString(std::to_string(number)); text.setPosition(j * tileSize + tileSize / 2, i * tileSize + tileSize / 2); window.draw(tile); window.draw(text); } } ``` 这段代码首先定义了每个格子的大小为100x100,并且使用白色填充和黑色边框。然后,它加载了一个字体文件,并且创建了一个文本对象来显示数字。最后,它使用两个嵌套循环来绘制整个游戏界面。 这样,我们就完成了n-puzzle游戏的可视化。完整代码如下: ```cpp #include <SFML/Graphics.hpp> const int N = 3; const int tileSize = 100; int main() { sf::RenderWindow window(sf::VideoMode(N * tileSize, N * tileSize), "N-Puzzle Game"); sf::RectangleShape tile(sf::Vector2f(tileSize, tileSize)); tile.setFillColor(sf::Color::White); tile.setOutlineColor(sf::Color::Black); tile.setOutlineThickness(2); sf::Font font; font.loadFromFile("arial.ttf"); sf::Text text("", font, tileSize / 2); text.setFillColor(sf::Color::Black); text.setStyle(sf::Text::Bold); text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(sf::Color::White); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int number = i * N + j + 1; if (number == N * N) continue; tile.setPosition(j * tileSize, i * tileSize); text.setString(std::to_string(number)); text.setPosition(j * tileSize + tileSize / 2, i * tileSize + tileSize / 2); window.draw(tile); window.draw(text); } } window.display(); } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值