[关闭]
@xiaojian233 2016-08-20T23:51:14.000000Z 字数 4385 阅读 1629

Candy Craft

项目展示


嗯,其实看名字也能明白,这个游戏与《我的世界》 有不少的关系吧。
项目的风格几乎是照搬《我的世界》了,考虑的原因无非是,风格简单,对美术的要求没有那么高;实现难度低。

自述

这个项目是基于开源的3D 引擎OGRE 编写的,是一款第三人称的ARPG 游戏。游戏的目标是打死怪物,收集糖果存活下去。

//TODO1
图【1】 游戏截图

项目是在实验室产生的,因为这是我们的作业之一。
而游戏的灵感来源是我基于一篇知乎回答提出来的:为什么富人越来越富,穷人越来越穷?-- 崔绍瑄的回答

项目从立项到结题,40天左右,对我们来说比较匆促。
而OGRE 平台,也是从项目开始才去了解的,所以,面对一个陌生的平台技术框架,走了不少弯路,导致项目完成度不算特别高。

好了,说完这些,倒是讲一下游戏的背景。

故事背景:

鲁菲的妻子得了一种难以治愈的绝症,但是有一个神医告诉了他在一片神奇的大陆上存在一个叫one candy的珍贵物品可以治愈他心爱的妻子,于是深爱着妻子的鲁菲不惧艰险来到了这个神奇的大陆。
但是这个大陆远比他想的要复杂。大陆的构造很是奇特,因为大陆里面还有其他的大陆,而one candy只存在于最后的一个大陆,大陆之间通过传送点相连接。每个大陆都有着特定的地形,例如,平原,丘陵,沙漠,峡谷,火山等。每个大陆上都存在很多攻击性的生物,并且越靠近one candy,生物就越加强大,打败这些生物后,才会出现candy,鲁菲可以用这些candy提升自己。

主线任务:经过地图的几个区域 得到one candy。

一些想法

在这个项目中,我是主要的开发者了,贡献了超过百分之六十的代码。
但是,也许还是因为技术不够精湛,OGRE 的熟悉成本也比较高,导致游戏的完成度不是很高。

感觉,还是继续努力学习啊。

技术点

这部分仅作一个简单的介绍吧。
也不是有很多的内容,一个半月的时间,从底层开始慢慢做起,有点吃力。

总体架构

项目其实是采用快速迭代的方法方式,在初期只有一个大概的构思,做到后面,才慢慢细化了。
整个架构图就如下面所示:
//模块图
图【2】模块图

//类设计图
图【3】类详细设计图

而游戏的启动流程图如下:

//场景转换图
图【4】游戏流程

有些模块是小伙伴负责的,所以,设计、代码风格都会有一点点的差异。

在这里得到的教训是,要有编码规范。

物理引擎

OGRE 本质上,算是一个渲染引擎。
它的渲染性能其实非常好,一些商业的游戏就是基于它实现的,如《天龙八部》。
但是其他模块的功能就比较缺失了,如物理引擎。

但是,OGRE 作为一个老牌子的游戏引擎,其实前人做了很多工作来使得它和其他的模块一起工作。
如物理引擎的驱动模块有OGREBullet ,连接的就是OGRE 和Bullet 物理引擎。

在物理引擎的实现上,我踩过不少的坑。

主要是如下三种尝试方法:

OgreBullet
这是最保守的实现方式,因为前任就有去做这个工作了。
但是问题在于,它的版本太低了,最后一次的貌似是08 年的,兼容起来各种问题。
算是踩过的第一个坑。
这是实现失败后,我马上就弃坑,转而寻找其他的实现方式。

Bullet
我做的第二个尝试,就是抛弃OgreBullet直接编写Bullet 的驱动程序。
这个一切还算是比较顺利。

直到。。
集成的时候,因为一些不知名的bug ,使得项目莫名崩溃。

并且,因为设计上的不合,导致这个方案在项目中不能够被兼容。
所以,我又抛弃它了。

空间记录表
我最后做的挣扎,是自己去实现它。
其实最大的问题,是怎么去做碰撞检测吧。
OGRE 提供了场景查询的接口,可以查询在某个范围的可移动的节点的。

但是,我们的场景太大了,节点数过多,这个方法行不通,性能低下。无奈,我们只能将场景渲染成为静态的物体。

静态的物体,意味着,OGRE 只是把它作为画面渲染而已,不提供任何的查询可用。

所以,最后,我提出一个采用空间记录表的方式来实现它。

因为我们的地形就是一个个的正方体的方格,所以我将游戏场景是为一个矩阵。
然后用位去记录(x, y) 上的方格的位置信息。
1 表示有方格, 0 表示没有方格。
每个元素用32-b 的无符号数表示,于是可以支持最高32 层的表示。

但是,我们的游戏远远不会到大那个高度的。

而这样的表示,对内存也要求也很低,300*300*32 的地形,仅需要360kb 大小的表格就能够完整地表示出来了。

//物理怪物图
图【5】物理引擎实现的效果,图为火山场景,绿色的是怪物

主要是因为物理引擎的实现太多艰难了,才给我留下那么深刻的印象。

地形生成

我们的地形也很有特色。
地形是根据地形高度图生成的。
比如下面是草地的地形图,生成的对应的游戏场景:
//TODO

//TODO

从而,使得,游戏只需要读入不同的地形高度图,就能够生成不同的地图了。

这个节省了我们很多建模的工作。通用性也更强一点。

GUI

GUI 的框架是选择了CEGUI 实现的。我们在游戏中还设立了一些属性点,通过CEGUI 和很好地表现出来:
//CEGUI

多场景管理

游戏实现的一个功能是,能够通过设立在场景的传送点,传送到下一个场景。

设计了一共4种场景:草地、火山、沙漠、峡谷。
此处输入图片的描述

这个实现的场景大致如下:

//TODO
图【7】火山的场景

//各个场景图
图【8】沙漠的场景

最主要的问题是,游戏场景中资源的回收和分配要做好。每次场景的载入,都会销毁一批资源(然而并不是所有的资源都销毁,所以才比较头疼),并且分配一批新的资源。

脚本系统

我是考虑着,能够写一个简单的脚本系统, 用于设置游戏中的数据,比如地形、糖果设立点、人物复活点等等
使得数据和代码分离。

这是个不错的想法,于是就这么干了。
脚本的语法自己定义实现,自己写代码解释。

大致的效果如下吧:

  1. # 拿到各种初始化位置
  2. # 对应地形图像
  3. # 名字
  4. desert
  5. {
  6. # 图像的文件名
  7. image_file_name = "desert.png"
  8. # 复活位置
  9. relive_position = (115,132)
  10. # 初始化位置
  11. # initialise_position = (127,161)
  12. initialise_position = (127,161)
  13. # 糖果位置
  14. candy_positions
  15. {
  16. (4,25)(78,18)(155,28)(231,7)(34,57)(86,70)(234,43)(191,105)(219,76)(272,87)(252,116)(39,170)(152,179)(242,190)(287,176)(64,216)(140,228)(192,223)(276,224)(12,282)(112,292)(200,278)(250,281)
  17. }
  18. # 传送点
  19. transfer_point volcano
  20. {
  21. (287,286)(288,286)(287,287) (288,287)
  22. }
  23. transfer_point grass
  24. {
  25. (37,7) (37,8) (38,7) (38,8)
  26. }
  27. }
  28. volcano
  29. {
  30. # 图像的文件名
  31. image_file_name = "volcano.png"
  32. # 复活位置
  33. relive_position = (137,138)
  34. # 初始化位置
  35. initialise_position = (140,7)
  36. # 糖果位置
  37. candy_positions
  38. {
  39. (22,13)(44,7)(81,7)(124,23)(5,33)(20,45)(14,62)(14,81)(15,101)(11,115)
  40. (34,139)(78,143)(145,104)(140,78)(136,52)(48,62)(47,105)
  41. }
  42. # 传送点
  43. transfer_point desert
  44. {
  45. (146,2) (147,2) (146,3) (147,3)
  46. }
  47. }
  48. grass
  49. {
  50. # 图像的文件名
  51. image_file_name = "grass.png"
  52. # 复活位置
  53. relive_position = (231, 73)
  54. # 初始化位置
  55. initialise_position = (164,136)
  56. # initialise_position = (40,236)
  57. # initialise_position = (265,277)
  58. # 糖果位置
  59. candy_positions
  60. {
  61. (92,21)(167,20)(277,37)(53,61)(93,64)(129,62)(160,51)(201,57)(73,105)(247,107)(46,163)(126,153)(142,141)(206,124)(69,180)(169,162)(40,213)(188,193)(228,192)(259,184)(116,268)(262,204)(65,245)(177,234)(268,241)(117,269)(222,273)
  62. }
  63. # 传送点
  64. transfer_point desert
  65. {
  66. (43,236)(44,236)(43,237)(44,237)
  67. }
  68. #无剧情地图,,只有怪物的峡谷
  69. transfer_point canyon
  70. {
  71. (270,277)(271,277)(270,278)(271,278)
  72. }
  73. # NPC 名字 id
  74. NPC Odom 0
  75. {
  76. position = (173, 122)
  77. }
  78. NPC Goodman 1
  79. {
  80. position = (213, 234)
  81. }
  82. }
  83. canyon
  84. {
  85. # 图像的文件名
  86. image_file_name = "canyon.jpg"
  87. # image_file_name = "ttttt.jpg"
  88. # 复活位置
  89. relive_position = (73,241)
  90. # 初始化位置
  91. initialise_position = (195,107)
  92. # 糖果位置
  93. candy_positions
  94. {
  95. (109,47)(226,51)(82,75)(104,70)(150,70)(198,64)(223,83)(268,95)(248,118)(232,153)(202,187)(128,132)(91,160)(134,174)(65,196)(72,221)(51,261)(146,214)(130,246)(181,260)(205,263)(269,244)(265,278)
  96. }
  97. # 传送点的意思
  98. transfer_point grass
  99. {
  100. (236,261)(237,261)(236,262)(237,262)
  101. }
  102. }
  103. volcano
  104. {
  105. # 图像的文件名
  106. image_file_name = "volcano.png"
  107. # 复活位置
  108. relive_position = (137,138)
  109. # 初始化位置
  110. initialise_position = (140,7)
  111. # 糖果位置
  112. candy_positions
  113. {
  114. (22,13)(44,7)(81,7)(124,23)(5,33)(20,45)(14,62)(14,81)(15,101)(11,115)
  115. (34,139)(78,143)(145,104)(140,78)(136,52)(48,62)(47,105)
  116. }
  117. # 传送点
  118. transfer_point desert
  119. {
  120. (146,2) (147,2) (146,3) (147,3)
  121. }
  122. }
  123. #文本的结束
  124. end

END,感谢阅读


Email: li.xiaojian233@qq.com

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注