百科问答小站 logo
百科问答小站 font logo



C++到底是如何从代码到游戏的? 第1页

  

user avatar   hanjie-zou 网友的相关建议: 
      
此回答被部分 b站 up主 盗了好多次...心累。
还是提前声明下吧:本文本项目不支持转载,谢谢~
(近期(2021.04.22)忙着学习,腾不开手来。朋友们如果看到盗用视频,可以顺手举报之,或发我链接也行~多谢啦)

原回答(2020.4):

恬不知耻地,拿一个我自己写着玩的项目来举例吧~

tprpix,一个用 “C++徒手撸的” 自娱自乐级游戏项目(流量警告!!!...

https://www.zhihu.com/video/1230124634190233600 https://www.zhihu.com/video/1230124683078471680 测试:地图自动生成模块 https://www.zhihu.com/video/1230124715835936768

这是一个我自己做着玩的 私人项目

项目仍在推进中,很多部分甚至尚未开工(还是脚手架状态)。不过我已经把它开源到了 GitHub ,欢迎大佬们去拍砖~

(有关这些项目的额外信息,可以参见这个答案:

关于 tprpix 的更多废话:

与你的问题相似,tprpix 的诞生,也是为了动手实践下:“用C++徒手搭建一个游戏,是个怎么样的体验”。在开启这个项目之前,我只用 C++ 实践过一两个 2000行级的迷你项目。(更早之前,则是在用 C 和一点点汇编,跟着教程搭一个 类unix 操作系统内核(玩具级))确切地说,tprpix 就是我的第一个 C++ 项目。在开工的头几个月里,我始终处于:边翻书学习新用法,边重写原有模块 的状态。

tprpix 中的绝大多数模块都是徒手撸出来的,包括但不限于:

  • 基于 OpenGL 的画面渲染层,和一个简易的帧动画播放器
  • 一个轻量级 移动碰撞检测模块
  • 一个基于 Perlin Noise 的地图生成器
  • 一个“蓝图”系统,读取 json 文件,用来定制不同生态的地景分配规则
  • 水域系统(已被改设定为:“源自异星的生物汤“...(中二...
  • 基于 SQLite3 数据库的 自动读档存档
  • 基于 glfw 的 游戏手柄支持(Xbox360-Style)
  • 跨平台,支持 Win10 / MacOSX / Linux(Ubuntu) 编译

如上述细节所提,我也使用到了第三方库,比例:

  • OpenGL 方面的库:GLMglfwglad
  • 小型数据库: SQLite3
  • json解析库:RapidJSON
  • debug库:fmtspdlog
  • enum库:magic_enum

目前的 tprpix 只为试玩者提供了非常有限的功能:你可以操纵一只鸡,遍历这个世界。不管朝任何方向走,地图都会自动生成/销毁。在这个世界中,运用一套规则(perlin noise+蓝图)来自动在游戏世界中分布景物和人造物(草,树,墙壁篝火等.... 类似于 MineCraft 的地图生成机制... )

更多功能还在编写中,更多生物也在制作中...


废话完毕进入正题:

“从零开始,搭建一个游戏项目,到底该怎么做?” 我觉得可以分为:

  1. 学习如何搭建 C++ 项目
  2. 选择图形库
  3. 主函数 和 主循环
  4. 游戏模块
  5. 在实践的过程中强化 “bypass” 思维
  6. "能跑的轮子就是好轮子"

以下是展开:

1 .学习如何搭建 C++ 项目

这其实是所有 C/C++ 玩家都要面临的问题。遗憾的是,绝大多数语法书都不会提及这块。这导致很多玩家在学习了一段时间语法后,仍然只能捏着一个 .h, 两个 .cpp文件(一个main.cpp, 一个 test.cpp)来构建项目,非常凄惨。

如果你的项目只想支持 win平台,那不妨直接学习如何在 VS中创建项目。如果你想要跨平台,那或许该学习下 CMake 的使用。往深了说就是:

学习使用 有组织的 目录,文件 (.h, .cpp, .hpp, .vs, .fs, .json, .png.... ) 再配合一个构建工具,来管理一个项目。

对于这件事,我的建议很粗暴:抄!去找一个别人写的迷你项目,照着对方的格式,搭建自己的项目。比方说 这个

这是一个大佬使用 C/OpenGL/Cmake 复刻 MineCraft 的项目。在某些方面,它做得挺屎,比如它会在单个 main.c 文件里堆上30来个函数,这些函数之间相互关联,难以解耦(也可能是大佬想把它快快做完,好赶去玩别的~)。但另一方面,它在目录规划上挺值得借鉴( 你甚至还可以学一学它的 CMakeLists.txt 文件的写法... )

抄到合适的项目格式后,可以先停顿一下,拿这个项目框架,去编写几个小工具。在实践中,加深对项目格式的理解。不出所料的话,你很可能会遭遇 编译链接 方面的问题,这其实也是一个暗坑:

链接(Linking)著名三不管地区~ 语法书们觉得这玩意儿不属于语法,所以不讲。环境库的书觉得这种东西你应该懂,所以也不讲。再加上它本来就有点神秘,如果之前的你都是在几个很小的 .h .cpp 文件里堆代码。相信我,当遭遇链接问题时,你会一脸懵逼。

这种时刻,最好的办法就是看书查资料,比如《深入理解计算机系统》(3th) 中的第七章。或者:

当然,如果你为了跨平台而投奔 cmake,那将是另一段磨难... (鉴于知乎 cmake大佬太多,而我在这方面又特菜,就不摆弄了...


最后的最后,如果你特别懒,并不想去琢磨别人的项目到底是怎么搭出来的,不妨去 GitHub 搜索关键词:“cpp empty project”(或类似的词)。其实 已经存在很多现成的模板。在这里,我也提供一份我自己的:

(夹带私货时间~)

这是一个 基于 cmake/C++17 的跨平台空项目。从上面那个游戏项目 tprpix 整理抽取而来。你可以在这个空项目基础上,搭建出你自己的跨平台程序。(具体食用方式已经写在项目中,在此略过...


2.选择图形库

游戏的一个核心模块就是 画面呈现。你可以直接使用功能便捷的图形界面框架,比如 QT。或者底层的图形库,比如 OpenGL, DirectX。不管选择哪一种,这些图形库的使用方式,都会影响你的 主函数,主循环的编写格式。

所以,请在项目开始的第一阶段,确定好自己使用的图形库。

在这里,我以 OpenGL 为例。你可以直接根据这个教程来入门图形学。顺带学习如何搭建一个 OpenGL 视觉交互程序:

图形库方面的代码有个特点,就是它们的编写格式往往是固定的。直白地说就是:你在项目前期劳烦下,把这些代码理解透,然后封装进你自己写的模块中、排布进你的游戏主循环中,然后就不大需要改动它们了(唯一需要开放出来,以便时刻添新的就是 shader 代码了)。也许,未来某天,当你领悟了更好的设计方法,你会回来重构。但整体上,你应该在游戏制作的前期,就把这些东西确定下来。

如果你希望你的游戏有惊人的画面表现,也许你该停下来,深入挖掘下图形学方面的知识。不过,做出一个游戏需要投入的的方面有很多,视觉只是它的一部分。

3.主函数 和 主循环

游戏的本质就是一个 时刻等待玩家输入的交互程序:

游戏程序启动,优先执行各个模块的初始化。然后跌入一个巨型的 while 循环中,以每秒60帧(或更多)的速度,反复执行 while 体内的代码。直到某一刻,玩家的某个输入触发exit事件。然后正式退出 while 循环。在执行一系列收尾工作后,彻底关闭游戏进程。

这么一看,一旦搞定了 主函数主循环。游戏框架似乎就出来了。我的建议是,项目前期请使用最简单粗暴的方式来实现,比如:

       int main( int argc, char* argv[] ){      // <---1--->     // init modules     // ...          // <---2--->     // Main Loop      while( !exit() ){         //...     }      // <---3--->     // end everything     // ...  }      

要么照着图形库教程传授你的格式去搭,也是完全够用了。或者说,这也许是更值得推荐的方法。如果你真的跟着那些图形库教程一路走下去,你会发现,它们已经教会你如何去搭建一个合格的 视觉交互程序。在这个交互程序的基础上,再往前多走两步,将它塑造成一款游戏,是件自然而然的事。

回到 主函数主循环。如果你想把这部分做得更考究的话,不妨阅读下这两篇文章:

4.游戏模块

一个游戏,到底该被划分成哪些模块呢?

此时不妨去看看 正经的游戏引擎是怎么设计的,比如 unity3D。你可以借鉴一下它的设计思路,结合自己的游戏内容,设计一组功能相似的模块。(可以是简化版,合并版,只要能满足你的游戏就行~)

好吧,这一段我讲得有点敷衍。但怎么讲呢:为自己的游戏,亲手实现一些功能性模块,其实是非常快乐的过程。如果说,在处理上文提及的图形渲染方面的代码时,你还有点畏手畏脚的话(毕竟大家都是第一次)。那么现在,当你开始写功能性代码时,你就可以像对待一个纯粹的程序那样,去组织,去实现它了。用上你在 C++ 和其他书里学到的所有知识:放开脑洞,埋头填坑,停下总结,然后再翻工重写...


5.在实践的过程中强化 “bypass” 思维

好吧我承认,这个概念是我自己编的。若有发现更专业的描述,还请告诉我...

当你正式开工,并且推进了一段时间后。你可能会失落地发现,有些模块的制作工期实在太长了。有些模块则相互依赖,在所有依赖模块都完成之前,你就是无法看见你的程序运行的样子。这种体验使人沮丧,在你撑到最后一刻之前,你的意志力已经被消费光了。我的建议就是:搭建微型的 旁路系统(bypass)

怎么讲呢,假设你正在写一个模块 A,而 A 还依赖于另一个模块 B。而这个 B,你其实一行代码都没写呢。传统的方法当然是把两个都写完整后再跑。但这个依赖关系可能很长,不只是 AB 这么简单。 此时的你不妨尝试:搭建一个空架子的 B,它暂时什么也不干。但它的一些接口函数,切切实实联通到了大流程中。(就像原初设计的那样) 然后就拿着这个看起来像是被故意短路了的程序,去测一测跑一跑了。(当然,并不是真的短路到没法运行,而是功能上的短路)

随时随地的反馈是很有必要的,它是你坚持下去的动力源

当然,以上只是一个很简陋的例子。放大了讲,万物皆可被 bypass。以各种灵活的方式。


6."能跑的轮子就是好轮子"

(注:没有对轮子哥的不敬,轮子哥是我偶像~)

这其实也是 bypass 思维的一个变种:一个简陋的,但能正常运行的模块,就是好模块。不要担心第一套方案设计得不够完善。随着项目的推进,经验的积累,你还可以回来重写嘛~(捂脸)


(废话先到这里,未来再来补充....)




  

相关话题

  C++ 允许「我们都是人,所以我可以把你私有的眼睛借来随便玩,再还给你」,这难道是一种设计上的妥协? 
  C++到底是如何从代码到游戏的? 
  qt5 msvc 版本如何检查内存泄漏? 
  C++的new操作符,底层使用的是malloc吗,在析构时,是如何确定自己需要释放内存的大小的? 
  学 C++ 是种怎样的体验? 
  C++可以直接返回多值? 
  C++ 的智能指针不就基本解决了野指针问题了吗?为什么还要吹捧rust的内存安全? 
  C++ 实现接口与实现分离后,文件变得更多了,到底有什么好处? 
  C++里 const int* 与 int const* 有什么区别? 
  学C#需要学好C++么? 

前一个讨论
如何看待第三方百度云Pandownload作者被捕?
下一个讨论
为什么中国校园欺凌电影只有女生被欺凌而不是男生?





© 2024-09-19 - tinynew.org. All Rights Reserved.
© 2024-09-19 - tinynew.org. 保留所有权利