SFML(1) | 自由落体小球

小游戏和GUI编程(1) | 基于 SFML 的自由落体小球

1. 目的

通过一些简单的例子(2D小游戏的基础代码片段), 来学习 SFML 的使用。

2. SFML 适合做图形显示的理由

使用 SFML 做图形显示的库。 相比于其他用的过库:

  • EasyX: 不开源, 不能跨平台使用, API 风格陈旧, 不是 C++ API
  • OpenCV 的 highgui 模块: highgui 不是 OpenCV 的最强项, 功能有限
  • SDL2: 完全用 C 写的, 不利于让我保持 C++ 语法的熟悉度
  • Qt: 有 GPL License 导致的潜在法律问题, 弃用
  • Dear imgui: 比较 geek, 默认的字体风格我受不了, “代码即文档” 也难度较大
  • SFML: 开源, 跨平台, 现代的 C++, License 友好, 文档直白, 功能齐全

3. 使用 SFML - 构建阶段

目前是 2024 年 2 月 9 日, 使用最新版 SFML 2.6.1。 我是 mac-mini 环境, 安装了 CMake 3.28, C++编译器是苹果自带的 AppleClang 15.0.0。

首先安装 SFML:

brew intall sfml

然后在 CMakeLists.txt 里, 为可执行程序链接 sfml 的库。 SFML 的 cmake 里,要求指明每一个 component:

cmake_minimum_required(VERSION 3.20)
project(free-falling-ball)
set(CMAKE_CXX_STANDARD 11)

add_executable(free-falling-ball
    free-falling-ball.cpp
)

find_package(SFML COMPONENTS graphics window system)
target_link_libraries(free-falling-ball PRIVATE sfml-graphics sfml-window sfml-system)

简单起见, free-falling-ball.cpp 里先写一个 hello world, 用于完成构建:

#include <stdio.h>
int main()
{
   
    printf("hello SFML\n");
    return 0;
}

执行编译和运行:

cmake -S . -B build
cmake --build build

4. 使用 SFML - C++ 代码

官方给出了创建和管理窗口的文档: Opening and managing a SFML window

4.0 代码布局

在达到最终效果前, 每一步实现一个基础功能, 放在一个 demox_xxx() 的函数中, 在 main 函数里调用它, 后续的每一小节就不列出 main() 了:

int main()
{
   
    demo1_show_window();
    return 0;
}

4.1 创建窗口

创建窗口的最简代码如下, 运行的话是一闪而过,但确实是创建了窗口的:

#include <SFML/Window.hpp>
int demo1_show_window()
{
   
    sf::Window window(sf::VideoMode(800, 600), "My Window");
    return 0;
}

sf::Window 类

SFML 库中的窗口, 是定义在 sf::Window 类中, 通过包含 SFML/Window.hpp 来引入。 实际上是在 SFML/Window/Window.hpp 中给出类的声明:

class SFML_WINDOW_API Window : public WindowBase, GlResource
{
   
public:
   ...
}

SFML/Window.hpp 里则是类似于 OpenCV 的 opencv2/opencv.hpp, 只包含了各个模块的头文件:

#include <SFML/System.hpp>
#include <SFML/Window/Clipboard.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/Window/ContextSettings.hpp>
#include <SFML/Window/Cursor.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Window/Joystick.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Mouse.hpp>
#include <SFML/Window/Sensor.hpp>
#include <SFML/Window/Touch.hpp>
#include <SFML/Window/VideoMode.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/Window/WindowHandle.hpp>
#include <SFML/Window/WindowStyle.hpp>

sf::VideoMode类

sf::VideoMode 类定义在 SFML/Window/VideoMode.hpp 文件中, 构造函数如下, bpp参数默认值是 32, 前两个参数指定了窗口的宽度和高度:

namespace sf
{
   
class SFML_WINDOW_API VideoMode
{
   
public:
VideoMode(unsigned int modeWidth, unsigned int modeHeight, unsigned int modeBitsPerPixel = 32);
...
}

重构后的代码

让变量尽可能有意义, 避免硬编码:

int demo1_show_window_refactored()
{
   
    constexpr int win_width = 600;
    constexpr int win_height = 600;
    const std::string title = "Free falling ball";
    sf::Window window(sf::VideoMode(win_width, win_height), title);

    return 0;
}

4.2 循环显示窗口, 并处理关闭事件

如下代码创建的窗口, 能够持续显示, 并且带有最小化、最大化、关闭按钮, 能用鼠标点击关闭按钮后关闭窗口:

int demo2_show_window_with_loop()
{
   
    constexpr int win_width = 600;
    constexpr int win_height = 600;
    const std::string title = "Free falling ball";
    sf::Window window(sf::VideoMode(win_width, win_height), title);

    // run the program as long as the window is open
    while (window.isOpen())
    {
   
        // check all the window's evetnts that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event))
        {
   
            if (event.type == sf::Event::Closed)
            {
   
                window.close();
            }
        }
    }
    return 0;
}

首先, 增加了一个外层循环 while(window.isOpen()), 用来确保只要窗口没有被关闭, 就持续的刷新显示。 绝大多数的 SFML 窗口程序都有这个循环, 也叫做 “main loop” 或 “game loop”.

内存循环, 是处理所有的窗口事件, 意思是说如果有多个事件, 比如同时做了鼠标和键盘的操作, 都会被处理。
window.pollEvent() 函数返回 bool 类型, 如果现在还有没被处理过的事件, 它返回 true, 如果所有事件都处理完了, 它返回 false。
这里我们只处理了 sf::Event::Closed 事件, SFML/Window/Event.hpp 里定义了 EventType 枚举类型: Closed 的注释写的很清晰, 是窗口关闭的请求。

    
    /// \brief Enumeration of the different types of events
    ///
    
    enum EventType
    {
   
        Closed,                 //!< The window requested to be closed (no data)
        Resized,                //!< The window was resized (data in event.size)
        LostFocus,              //!< The window lost the focus (no data)
        GainedFocus,            //!< The window gained the focus (no data)
        TextEntered,            //!< A character was entered (data in event.text)
        KeyPressed
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值