《世嘉新人培训教材—游戏开发》2DGraphics1项目cmake构建
《世嘉新人培训教材—游戏开发》作为经典的游戏开发教程,提供了相关样例代码供我们进行开发使用。但是该样例是基于VS进行编写构建的,而本人日常喜欢CLion进行C/C++开发,于是准备使用cmake重新组织该书籍的样例项目:2DGraphics1中的NimotsuKunBox和drawPixels。当然,这个过程不仅是移植,也是对cmake组织项目一个深入的实践。
对现有样例项目的认识与构建
样例代码结构
在进行cmake迁移前,有必要对现有的VS体系的代码结构进行了解。
GameLib(样例根目录)
该目录下主要存放了:
各个样例会使用的工具静态库/头文件;
src:样例源码;
tools:工具二进制程序。
GameLib/src目录
该目录下主要存放:
各种数字+下划线开头的文件夹:书中使用到的各种样例工程;
GameLibs文件夹:生成GameLib根目录中的静态库/头文件的源码。
GameLib/src/GameLibs目录
该目录主要存放:
- GameLib根目录下各个被样例项目使用的静态库/头文件的源码;
- Modules:其他静态库项目的依赖静态库。
使用VS构建样例项目静态库
在GameLib下,本书的译者已经帮我们编写了一个基本的指南:
编译顺序
在系统环境变量中添加 GAME_LIB_DIR 值为源码工程的根目录
注意要重启visual studio①先编译类库的Modules
src\GameLibs\Modules\Modules.sln②再编译各个小功能的类库
比如 src\GameLibs\2DActionGame\GameLib.sln③最后编译游戏本身
比如 src\01_FirstGame\FirstGame.sln为什么要按照这样的顺序呢?请看下面这个例子
譬如对src\02_2DGraphics1\2DGraphics1.sln 来说,
首先用vs打开它,右键点击 drawPixels查看属性
在链接器 的附加库目录一栏可以看到 $(GAME_LIB_DIR)\2DGraphics1\lib;%(AdditionalLibraryDirectories)
这意味着它需要在2DGraphics1\lib中查找某些类库,
具体要用什么类库呢?可以点击 链接器 -> 输入 ,看到附加依赖项中有 GameLib_d.lib;%(AdditionalDependencies)如何才能生成这个 GameLib_d.lib呢?
打开 src\GameLibs\2DGraphics1\GameLib.sln 编译即可
但是,通过右键Framework属性, 查看库管理器 的附加依赖项可以看到 Modules_d.lib
这就要求必须先编译好 Modules工程
于是打开 src\GameLibs\Modules\Modules.sln 编译即可。
这里有两个关键点需要牢记:
- 需要配置环境变量
GAME_LIB_DIR
,原因在于后续即将编译的各个样例,都会使用$(GAME_LIB_DIR)
然后找到对应的类库; - 编译有一个顺序:先核心静态库:Modules;然后各个样例需要的GameLib;最后是样例。
编译核心的Modules
加载$(GAME_LIB_DIR)\src\GameLibs\Modules\Modules.sln
进行构建
使用vs编译后会生成$(GAME_LIB_DIR)\src\GameLibs\Modules\lib\Modules_d.lib
(_d
代表Debug的静态库)
编译各独立样例需要的GameLib
在本文中,我们的目标是构建2DGrphics1-NimotsuKunBox项目,所以我们加载2DGrphics1的GameLib:$(GAME_LIB_DIR)\src\GameLibs\2DGraphics1\GameLib.sln
进行构建,通过配置也能看到的确需要Modules_d.lib
:
该项目构建完成后,会生成:$(GAME_LIB_DIR)\2DGraphics1\lib\GameLib_d.lib
,并且会将GameLib_d.lib
静态库以及相关头文件都复制到$(GAME_LIB_DIR)\2DGraphics1\中
,:
$(GAME_LIB_DIR)\2DGraphics1\
├─include
│ └─GameLib
│ │ Framework.h
│ │
│ └─Base
│ DebugStream.h
│
└─lib
Framework_d.idb
Framework_d.pdb
GameLib_d.lib(lib库)
GameLib_d.pdb
Modules_d.pdb
目前为止,我们生成了如下的两静态库以及头文件:
$(GAME_LIB_DIR)\src\GameLibs\Modules\lib\Modules_d.lib
$(GAME_LIB_DIR)\2DGraphics1\lib\GameLib_d.lib
$(GAME_LIB_DIR)\2DGraphics1\include(头文件)
当然,因为我们的GameLib_d.lib
是使用Modules_d.lib
进行构建的,已经将Modules_d.lib
链接到了GameLib_d.lib
内部了,所以接下来我们的cmake项目不再需要Modules_d.lib
了。
演示2DGraphics1-NimotsuKunBox和drawPixels项目
使用VS打开$(GAME_LIB_DIR)\src\02_2DGraphics1\2DGraphics1.sln"
解决方案,该解决方案中有如下的5个样例:
NimotsuKunBox
NimotsuKunBoxWithTermination
NimotsuKunTextOnly
NimotuKunDot
drawPixels
将NimotsuKunBox项目作为启动项目,然后运行可以看到如下的界面:
将drawPixels作为启动项,运行可看到如下效果:
接下来,我们将使用cmake来移植这两个项目。
使用cmake搭建2DGraphics1项目
在经过前戏后,我们终于编译出了2DGraphics1所需要的GameLib_d.lib
静态库以及相关的头文件,并且,我们还构建了2DGraphics1样例解决方案中的NimotsuKunBox和drawPixels项目。接下来我们将创建一个cmake项目,移植该样例中的两个项目。
搭建初始项目
首先,我们建立一个文件夹2DGraphics1_cmake,在该文件夹中,我们再创建两个文件夹:NimotsuKunBox和:drawPixels,并且这两个文件夹中分别各自创建一个main.cpp文件和CMakeLists.txt,内容如下:
2DGraphics1_cmake/NimotsuKunBox:
// 2DGraphics1_cmake/NimotsuKunBox/main.cpp
#include <iostream>
int main() {
std::cout << "hello, NimotsuKunBox" << std::endl;
}
CMAKE_MINIMUM_REQUIRED(VERSION 3.22)
PROJECT(NimotsuKunBox)
SET(CMAKE_CXX_STANDARD 11)
ADD_EXECUTABLE(NimotsuKunBox main.cpp)
2DGraphics1_cmake/drawPixels:
// 2DGraphics1_cmake/drawPixels/main.cpp
#include <iostream>
int main() {
std::cout << "hello, drawPixels" << std::endl;
}
CMAKE_MINIMUM_REQUIRED(VERSION 3.22)
PROJECT(drawPixels)
SET(CMAKE_CXX_STANDARD 11)
ADD_EXECUTABLE(drawPixels main.cpp)
接着,我们在2DGraphics1_cmake中创建一个CMakeLists.txt来统一管理NimotsuKunBox和drawPixels这两个cmake项目,内容如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.22)
PROJECT(2DGraphics1_cmake)
SET(CMAKE_CXX_STANDARD 11)
# cmake子项目,子项目的名称就是子目录下的CMakeLists.txt中的PROJECT一致的名称
ADD_SUBDIRECTORY(NimotsuKunBox)
ADD_SUBDIRECTORY(drawPixels)
于是,当前整体目录结构如下:
2DGraphics1_cmake
├─ CMakeLists.txt
├─ NimotsuKunBox
│ ├─ CMakeLists.txt
│ └─ main.cpp
└─ drawPixels
├─ CMakeLists.txt
└─ main.cpp
cmake实践点:子项目管理
父级CMakeLists.txt可以通过ADD_SUBDIRECTORY来添加子CMake项目。这里有一篇特别详细的博文CMake基础 第13节 构建子项目 - 橘崽崽啊 - 博客园 (cnblogs.com)
头文件与静态库添加
在前面我们已经编译出了GameLib_d.lib
,并且把头文件已经复制到了指定目录。现在,我们在项目根目录中创建一个@lib
文件夹,专门放置静态库和头文件,目录状态如下:
2DGraphics1_cmake
├─ CMakeLists.txt
├─ NimotsuKunBox
│ └─ // ... ...
├─ drawPixels
│ └─ // ... ...
└─ @lib // => 存放要使用的静态库和头文件
├─ include
│ └─ GameLib
│ ├─ Base
│ │ └─ DebugStream.h
│ └─ Framework.h
└─ lib
├─ Framework_d.idb
├─ Framework_d.pdb
├─ GameLib_d.lib
├─ GameLib_d.pdb
└─ Modules_d.pdb
配置NimotsuKunBox项目
为了让NimotsuKunBox项目中的能够使用到根目录下的静态库和头文件,我们需要配置NimotsuKunBox/CMakeLists.txt,添加头文件和静态库:
SET(CMAKE_CXX_STANDARD 11)
+ MESSAGE("Current CMakeLists.txt dir:\n${CMAKE_CURRENT_SOURCE_DIR}")
+
+ # 配置头文件查找路径
+ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../@lib/include)
+ # 配置链接库文件查找路径
+ LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../@lib/lib)
+
ADD_EXECUTABLE(NimotsuKunBox main.cpp)
+
+ # 实际链接
+ TARGET_LINK_LIBRARIES(NimotsuKunBox GameLib_d.lib)
之后,我们将在VS中能够运行的NimotsuKunBox项目代码拷贝到当前的main.cpp中,由于篇幅的关系,就不贴出代码本身了,给一个整体的修改:
编译问题
当我们尝试运行该项目的时候,发现至少有以下几个问题:
问题1:在CLion+msvc编译器下,编码字符报错:warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失。
该问题原因在于CLion中的文件是默认使用的UTF-8编码,而msvc在不指定的情况默认以当前代码页(936)编码方式读取文件(代码页936(Codepage 936)是Microsoft的简体中文字符集标准,是东亚语文的四种双字节字符集(DBCS)之一。其最初版本和GB 2312一模一样,但在推出Windows 95时扩展成GBK)。
在CMake中想要给msvc指定文件编码方式,需要在CMakeLists.txt配置如下内容:
... ...
SET(CMAKE_CXX_STANDARD 11)
# 配置编译器以指定编码读取代码源文件
ADD_COMPILE_OPTIONS("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
ADD_COMPILE_OPTIONS("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
MESSAGE("Current CMakeLists.txt dir:\n${CMAKE_CURRENT_SOURCE_DIR}")
... ...
问题2:GameLib_d.lib(MemoryManager.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(main.cpp.obj 中)
这一类报错通常比较普遍,简单来讲就是:GameLib_d.lib这个库是一个静态库带Debug(MTd_StaticDebug),但是我们的项目链接步骤是以动态库的方式链接这些库文件。对于这个问题,有两种方式来解决,一种就是重新编译GameLib为一个dll(动态链接库);另一种则是修改当前项目的链接方式为静态库链接。当然,简便起见,我们修改项目的链接形式为静态库链接形式:
... ...
SET(CMAKE_CXX_STANDARD 11)
ADD_COMPILE_OPTIONS("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
ADD_COMPILE_OPTIONS("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
+# 设置策略CMP0091为NEW,新策略
+if (POLICY CMP0091)
+ CMAKE_POLICY(SET CMP0091 NEW)
+endif (POLICY CMP0091)
+
MESSAGE("Current CMakeLists.txt dir:\n${CMAKE_CURRENT_SOURCE_DIR}")
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../@lib/lib)
ADD_EXECUTABLE(NimotsuKunBox main.cpp)
+# 设置MT/MTd
+SET_PROPERTY(
+ TARGET NimotsuKunBox
+ PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+
# 实际链接
TARGET_LINK_LIBRARIES(NimotsuKunBox GameLib_d.lib)
关于这块配置的细节,可以参考这篇文章:CMake设置MSVC工程MT/MTd/MD/MDd_Copperxcx的博客-CSDN博客_cmake mt
问题3:error LNK2019: 无法解析的外部符号 _main,函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了该符号
稍有C/C++开发经验的开发者看到这个报错其实心里还是有底的,应该是没有提供main函数作为函数的入口。但是对于我们的项目,细心的读者发现似乎样例代码中确实是没有提供main入口函数的。那么,为什么vs项目能够正确运行起来呢?观察vs中的项目属性—连接器—系统,会发现子系统(SubSystem)的值是:/SUBSYSTEM:WINDOWS
在cmake项目中,我们可以按照如下的方式进行配置:
# 设置MT/MTd
SET_PROPERTY(
TARGET NimotsuKunBox
PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+ # 配置 "/SUBSYSTEM:WINDOWS"
+ TARGET_LINK_OPTIONS(NimotsuKunBox PRIVATE "/SUBSYSTEM:WINDOWS")
# 实际链接
TARGET_LINK_LIBRARIES(NimotsuKunBox GameLib_d.lib)
实际上,配置成了/SUBSYSTEM:WINDOWS
之后也是需要有一个入口函数的,这个入口函数其实是在Modules那个项目里面定义好了的,具体可以搜索Modules项目中的int APIENTRY _tWinMain
函数实现:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
上述问题处理完成后,我们通过项目编译
配置drawPixels项目
实际上,配置drawPixels项目的CMakeLists.txt和NimotsuKunBox的CMake配置结构上没有区别,只是需要把相关的项目名字等换位drawPixels即可。最终运行的效果和之前的vs下是一致的~
附录:项目地址
本cmake移植的项目地址在:w4ngzhen/2DGraphics1_cmake (github.com)
《世嘉新人培训教材—游戏开发》2DGraphics1项目cmake构建的更多相关文章
- Cocos2d-x手机游戏开发与项目实践具体解释_随书代码
Cocos2d-x手机游戏开发与项目实战具体解释_随书代码 作者:沈大海 因为原作者共享的资源为UTF-8字符编码.下载后解压在win下显示乱码或还出现文件不全问题,现完整整理,解决全部乱码问题,供 ...
- 学习手机游戏开发的两个方向 Cocos2d-x 和 Unity 3D/2D,哪个前景更好?
如题! 首先说一说学习手机游戏(移动游戏)这件事. 眼下移动互联网行业的在以井喷状态发展.全球几十亿人都持有智能终端设备(ios android),造就了非常多移动互联网创业机会: 一.移动社交 微信 ...
- 内置3D对象-Unity3D游戏开发培训
内置3D对象-Unity3D游戏开发培训 作者:Jesai 2018-02-12 19:21:58 五大面板: -Hierachy:当前场景中的物体 图 1-1 -Project:项目中的所有资源 图 ...
- Unity3D游戏开发培训
Unity3D游戏开发培训 作者:Jesai 时间:2017-01-08 修改:2017-01-09 12:36:15 1 项目的构成 图1-1 如图1-1所示,Unity3D的项目构 ...
- 项目游戏开发日记 No.0x000005
14软二杨近星(2014551622) 还有一周就要交项目了, 看着周围的人也都忙碌了起来, 看着大部分人的项目都已经初具容貌, 我们团队里面也搞得人心惶惶, 一来是, 时间不多了, 还有很多事情要做 ...
- 项目游戏开发日记 No.0x000002
14软二杨近星(2014551622) 项目开发的开始, 到现在已经很久了, 软件工程的课也上了很久了, 不过, 我们的游戏现在依然还没有影子, 只能说...还是啥也不会... 从一开始, 兴致勃勃地 ...
- [Unity3D]Unity3D游戏开发之飞机大战项目解说
大家好,我是秦元培,欢迎大家继续关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei. 首先感谢大家对我博客的关注,今天我想和大家分享的是一个飞机大战的项目.这是一个比較综合的 ...
- .Net Core ORM选择之路,哪个才适合你 通用查询类封装之Mongodb篇 Snowflake(雪花算法)的JavaScript实现 【开发记录】如何在B/S项目中使用中国天气的实时天气功能 【开发记录】微信小游戏开发入门——俄罗斯方块
.Net Core ORM选择之路,哪个才适合你 因为老板的一句话公司项目需要迁移到.Net Core ,但是以前同事用的ORM不支持.Net Core 开发过程也遇到了各种坑,插入条数多了也特别 ...
- 用户输入- Unity3D游戏开发培训
用户输入- Unity3D游戏开发培训 作者:Jesai 时间:2018-02-12 14:28:45 用户输入Input 鼠标按键: -方法:GetMouseButton(); -方法:GetM ...
- 投影方式- Unity3D游戏开发培训
投影方式- Unity3D游戏开发培训 作者:Jesai 2018-02-12 20:33:13 摘 要 透视投影是3D渲染的基本概念,也是3D程序设计的基础.掌握透视投影的原理对于深入理解其他 ...
随机推荐
- 尴尬:在zoom中找不到会议密码
临时要开一个zoom会议,点击"新会议"进入,然后邀请其他与会者. "会议" - "复制邀请链接", 得到一行这样的信息(会议号已模糊处理) ...
- 如何使用MBP制作Win启动盘
最近有一个需求,想给家人的一台笔记本安装一套win 10的操作系统,但是我手头上现在没有对应的启动U盘. 由于工作原因,很多年没用win了,工作电脑也都是MBP,根本没有之前使用win时熟悉的Ultr ...
- mybatis SQL in() 为什么要在 mapper.xml里 用 foreach
结论: 若存在 in () 语句,要使用 #{} 预编译入参的方式,需要在 mapper.xml里 使用 foreach ======================================= ...
- 聚石塔的K8s 容器服务使用注意事项,坑的总结
1. 首先聚石塔是不能使用 8080 端口的,会审核不通过. 2.然而,容器服务默认的却是8080,最彻底的解决方法就是修改成80,注意3个地方: 以上3个地方要严重留意,缺一不可,已经踩了2次坑了, ...
- OGG-将PostgreSQL通过OGG_BigData同步到Kafka后数据存在8小时时间差
问题描述: 将PostgreSQL通过OGG_BigData同步到Kafka后数据存在8小时时间差. 问题原因: kafka.properties中的参数goldengate.userexit.tim ...
- Kafka-启动时报错: ERROR Fatal error during KafkaServer startup. Prepare to shutdown
一.问题描述 在启动kafka时报错: ERROR Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server. ...
- 下载Apache软件基金的软件和项目(Hadoop相关组件)
一.下载Hadoop相关组件,可以到Apache软件基金的资源目录: Apache 分发目录地址:https://dlcdn.apache.org/ 二.下载软件 方法一:在页面中找到需要下载的软件目 ...
- Python 国内常用python模块下载地址
国内常用python模块下载地址 清华大学:https://pypi.tuna.tsinghua.edu.cn/simple 中国科技大学 https://pypi.mirrors.ustc.edu. ...
- Centos7使用memtester测试内存
memtester http://pyropus.ca/software/memtester/ 下载并编译, 不用安装 wget http://pyropus.ca/software/memteste ...
- 「源码分析」CopyOnWriteArrayList 中的隐藏的知识,你Get了吗?
点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 前言 ...