- 游戏开发
- 图形学
- 3D渲染
【免费下载链接】raylib
A simple and easy-to-use library to enjoy videogames programming
导读
本文以 raylib 官方仓库中的 projects/CMake/README.md 项目模板为核心,完整讲解如何用 CMake 搭建一个可同时面向桌面平台与Web 平台的 raylib 项目。你将学会使用cmake -B build完成桌面构建、借助 Emscripten SDK 通过emcmake交叉编译出.html网页版本,并理解模板中find_package+FetchContent自动拉取 raylib、Web 链接参数、macOS 框架链接等底层细节,最终让一个core_basic_window.c示例在你的目标平台上顺利跑起来。
一、模板定位与项目结构
该模板位于仓库的projects/CMake/目录,是一个最小可用的 raylib CMake 工程骨架,目录下只有三个文件:
- CMakeLists.txt:核心构建脚本,负责依赖解析、目标定义与平台差异化配置;
- core_basic_window.c:一个特意为 HTML5 平台做过适配的"基础窗口"示例(文件头注释明确说明"This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP");
- README.md:官方给出的构建说明。
它的设计意图非常清晰:同一份源码、同一个 CMake 工程,通过不同的 CMake 命令切换目标平台。这与 raylib 主工程(src/CMakeLists.txt)中通过PLATFORM变量(Desktop;Win32;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW;Memory)控制平台编译的做法一脉相承,只不过模板把平台选择收敛到了构建命令层面,让用户无需深入理解 raylib 内部的平台宏。
二、桌面平台构建:两行命令
README 给出的桌面构建命令非常简单:
cmake -B build cmake --build buildcmake -B build将构建目录指定为build,并在该目录中生成构建系统文件(Linux/macOS 上默认是 Makefile,Windows 上则是 Visual Studio 工程);cmake --build build直接在目标平台默认的生成器上完成编译链接,最终产出可执行文件。
如果你正在仓库的projects/CMake/目录下操作,也可以把它当作独立工程直接构建。构建完成后,运行生成的可执行文件即可看到 800×450 的窗口,中央显示 "Congrats! You created your first window!"。
三、Web 平台构建:借助 Emscripten 交叉编译
Web 构建与桌面最大的不同在于:它需要一个能把 C 代码编译为 WebAssembly 的工具链——Emscripten SDK。README 中给出的完整流程如下:
mkdir build cd build emcmake cmake .. -DPLATFORM=Web -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXECUTABLE_SUFFIX=".html" emmake make逐条拆解这条命令链:
mkdir build && cd build:新建并进入构建目录(模板中的-B build写法在 emcmake 场景下同样适用,这里沿用 README 的原始写法);emcmake cmake ..:用 Emscripten 的 CMake 包装器配置工程,确保编译器、链接器被替换为emcc/emar;-DPLATFORM=Web:告诉 raylib 以 Web 平台模式编译。该参数对应 raylib 主工程的 CMakeOptions.txt 中enum_option(PLATFORM "Desktop;Win32;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW;Memory" ...)的取值之一;-DCMAKE_BUILD_TYPE=Release:使用 Release 优化级别;-DCMAKE_EXECUTABLE_SUFFIX=".html":使最终产物带上.html后缀,方便直接用浏览器打开;emmake make:调用 Emscripten 包装后的 make 完成编译,产出.html+.wasm等文件。
四、模板 CMakeLists.txt 逐段解析
模板的 CMakeLists.txt 虽然只有 40 余行,却完整覆盖了依赖解析、目标定义与平台分支,是理解整个构建流程的关键。下面逐段拆解。
4.1 工程声明与 compile_commands
cmake_minimum_required(VERSION 3.11) # FetchContent is available in 3.11+ project(example) # Generate compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON)- 最低要求 CMake 3.11,注释点明了原因:FetchContent 自 3.11 起可用;
project(example)将可执行目标命名为example;CMAKE_EXPORT_COMPILE_COMMANDS ON会在构建目录生成compile_commands.json,方便 clangd、ccls 等语言服务器实现精确的代码补全与跳转。
4.2 依赖解析:find_package + FetchContent 双保险
set(RAYLIB_VERSION 6.0) find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) FetchContent_Declare( raylib DOWNLOAD_EXTRACT_TIMESTAMP OFF URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz ) FetchContent_GetProperties(raylib) if (NOT raylib_POPULATED) # Have we downloaded raylib yet? set(FETCHCONTENT_QUIET NO) FetchContent_MakeAvailable(raylib) endif() endif()这是模板中最核心的机制,采用"先找后拉"策略:
- 优先查找已安装的 raylib:
find_package(raylib 6.0 QUIET)会通过系统安装的raylib-config.cmake(仓库中对应文件为 cmake/raylib-config.cmake,内部include("${CMAKE_CURRENT_LIST_DIR}/raylib-targets.cmake"))定位已安装的库。若系统未安装,或版本不满足,raylib_FOUND为假; - 找不到则自动下载编译:进入
FetchContent分支,从官方 tag 下载v6.0源码压缩包,由FetchContent_MakeAvailable(raylib)将 raylib 作为子工程直接编入你的项目。DOWNLOAD_EXTRACT_TIMESTAMP OFF避免了解压时间戳差异导致的校验失败; - 幂等保护:
if (NOT raylib_POPULATED)确保同一个配置过程内不会重复拉取。
提示:
find_package(raylib 6.0 QUIET)也可改为REQUIRED(注释里明确写着"QUIET or REQUIRED"),此时若系统没有安装 raylib,CMake 会直接报错而非自动下载,适合对依赖来源有严格要求的团队。
4.3 目标定义与链接
add_executable(${PROJECT_NAME} core_basic_window.c) #set(raylib_VERBOSE 1) target_link_libraries(${PROJECT_NAME} raylib)- 以
core_basic_window.c为唯一源文件创建可执行目标; target_link_libraries(... raylib)链接 raylib 目标。无论 raylib 来自find_package还是FetchContent,目标名都是raylib,因此这一行对两种来源都成立;- 被注释掉的
set(raylib_VERBOSE 1)是 raylib 内部的调试开关,打开后可观察其编译细节。
4.4 Web 平台专属配置
if (${PLATFORM} STREQUAL "Web") set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html") # Tell Emscripten to build an example.html file. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s GL_ENABLE_GET_PROC_ADDRESS=1") endif()当PLATFORM=Web时:
SUFFIX ".html"让 Emscripten 以 HTML 外壳形式输出,即最终example.html;- 通过链接器标志告诉 Emscripten:使用内嵌 GLFW3(
-s USE_GLFW=3)、开启断言(ASSERTIONS=1)、生成 WASM(WASM=1)、启用ASYNCIFY(用于文件预加载等异步场景)以及允许运行时获取 GL 函数地址。
这些标志与 raylib 主工程 src/CMakeLists.txt 中if (${PLATFORM} MATCHES "Web")分支的做法(target_link_options(raylib PUBLIC "-sUSE_GLFW=3" -sEXPORTED_RUNTIME_METHODS=ccall))互相印证,说明 Web 构建在 raylib 生态中是一等公民。
4.5 macOS 框架链接
if (APPLE) target_link_libraries(${PROJECT_NAME} "-framework IOKit") target_link_libraries(${PROJECT_NAME} "-framework Cocoa") target_link_libraries(${PROJECT_NAME} "-framework OpenGL") target_link_libraries(${PROJECT_NAME} "-framework QuartzCore") endif()macOS 上 raylib 依赖的窗口/图形系统来自系统框架,因此必须显式链接 IOKit、Cocoa、OpenGL、QuartzCore 四个框架。这与 raylib 自身在静态库安装场景下向消费者传播这些框架依赖的处理(见 src/CMakeLists.txt 中APPLE AND PLATFORM STREQUAL "Desktop"分支的find_library逻辑)是一致的。
五、示例源码:一套代码,两种主循环
模板中的 core_basic_window.c 是一个刻意示范"跨平台写法"的示例。它的核心结构如下:
#include "raylib.h" #if defined(PLATFORM_WEB) #include <emscripten/emscripten.h> #endif int screenWidth = 800; int screenHeight = 450; void UpdateDrawFrame(void); int main() { InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); #if defined(PLATFORM_WEB) emscripten_set_main_loop(UpdateDrawFrame, 0, 1); #else SetTargetFPS(60); // Set our game to run at 60 frames-per-second while (!WindowShouldClose()) // Detect window close button or ESC key { UpdateDrawFrame(); } #endif CloseWindow(); return 0; } void UpdateDrawFrame(void) { BeginDrawing(); ClearBackground(RAYWHITE); DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); EndDrawing(); }要点在于帧循环的分裂:
- 桌面/原生平台:使用
while (!WindowShouldClose())经典循环,配合SetTargetFPS(60)锁定 60 FPS; - Web 平台:浏览器不允许阻塞主线程的死循环,必须把"每帧更新+绘制"逻辑注册为
emscripten_set_main_loop(UpdateDrawFrame, 0, 1)回调,由浏览器事件循环驱动; - 通用的
UpdateDrawFrame()函数把"更新"和"绘制"逻辑抽离,保证两种平台共享同一套渲染代码。
这也解释了为什么 README 在 Web 构建时特别要求-DPLATFORM=Web:只有该宏开启,示例中的PLATFORM_WEB分支才会被激活。
六、从模板到 raylib 主工程:平台与选项的对应关系
如果你要基于该模板扩展成自己的游戏工程,理解模板与 raylib 主工程构建选项的对应关系会很有帮助。
- PLATFORM 取值:模板中
PLATFORM=Web只是 CMakeOptions.txt 中枚举(Desktop;Win32;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW;Memory)的一种。想构建其他平台,只需用对应字符串替代,例如-DPLATFORM=Android; - 依赖 GLFW 的方式:raylib 主工程通过 cmake/GlfwImport.cmake 决定使用内嵌 GLFW(
external/glfw子目录)还是系统 GLFW。桌面平台默认编译内嵌 GLFW,并强制关闭其文档/测试/示例构建以加快编译;Web 平台的 GLFW 则由 Emscripten 的-s USE_GLFW=3链接标志提供,因此模板的 Web 分支无需额外处理 GLFW; - Web 示例的完整形态:如果想了解 Web 构建的完整参数组合,可参考 examples/CMakeLists.txt 的 Web 分支——它为每个示例追加
-sALLOW_MEMORY_GROWTH=1、导出requestFullscreen运行时方法、使用 src/shell.html 作为 HTML 外壳,并通过--preload-file将resources目录预加载进 WASM 文件系统。这些是模板之外更精细的 Web 优化项,可按需借鉴。
七、常见问题与注意事项
- 桌面构建找不到 raylib?两种解决方案:先安装 raylib 到系统(使
find_package命中),或保持模板默认,让FetchContent自动下载 v6.0 源码编译。后者需要网络可达 GitHub 下载源。 - Web 构建报
emcmake: command not found?说明 Emscripten SDK 未安装或未激活环境,请先按官方文档安装并source对应环境脚本。 - Web 产物运行方式:
emmake make产出的.html文件不能直接用file://双击打开(WASM 加载受浏览器安全策略限制),应在本地启动静态文件服务(如python3 -m http.server)后通过http://localhost访问。 - Web 与桌面主循环差异:移植自己的代码到 Web 时,务必像模板示例那样把帧逻辑收敛进回调函数,并保证全局状态可跨帧保持,因为
emscripten_set_main_loop不再拥有普通while循环的栈上下文。 - OpenGL 版本与框架:在 macOS 上如果遇到链接错误,检查模板中的四个
-framework参数是否完整;在 Linux 桌面端,raylib 主工程在GLFW_BUILD_X11开启时还会要求 X11 开发包(见 src/CMakeLists.txt 的find_package(X11 REQUIRED)),请确保系统已安装相关开发库。
八、总结
projects/CMake/模板用极简的工程结构,演示了 raylib 跨平台构建的标准姿势:桌面端两条命令即可完成配置与编译;Web 端借助 Emscripten 的emcmake/emmake与-DPLATFORM=Web实现源码级复用。其背后依赖解析的"find_package 优先、FetchContent 兜底"策略、平台分支的链接参数组织,以及示例中桌面/Web 双主循环的写法,都是你在自己项目中可以直接复用的成熟模式。无论是快速跑通第一个窗口,还是以此为起点搭建正式的游戏工程,这份模板都是一个可靠的地基。
- 游戏开发
- 图形学
- 3D渲染
【免费下载链接】raylib
A simple and easy-to-use library to enjoy videogames programming
相关推荐
RTranslator:1.2GB 模型本地推理,三种模式实现离线实时翻译
RTranslator:1.2GB 模型本地推理,三种模式实现离线实时翻译 飞机降落、关掉飞行模式,信号栏还是空的——这时打开 RTranslator,它依然能
游戏开发图形学3D渲染重构你的桌面:FancyZones窗口管理实战指南
重构你的桌面:FancyZones窗口管理实战指南 在多任务处理成为日常的今天,你的桌面是否还在被杂乱无章的窗口占据?FancyZones作为PowerToys
桌面应用开发工具7天精通PyQt6桌面开发:从零基础到实战项目的完整指南
7天精通PyQt6桌面开发:从零基础到实战项目的完整指南 PyQt6是一款强大的Python GUI框架,能够帮助开发者快速构建跨平台的桌面应用程序。本教程将带
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考