@xiaojian233
2016-08-20T23:51:14.000000Z
字数 4385
阅读 1618
项目展示
嗯,其实看名字也能明白,这个游戏与《我的世界》 有不少的关系吧。
项目的风格几乎是照搬《我的世界》了,考虑的原因无非是,风格简单,对美术的要求没有那么高;实现难度低。
这个项目是基于开源的3D 引擎OGRE 编写的,是一款第三人称的ARPG 游戏。游戏的目标是打死怪物,收集糖果存活下去。
图【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】物理引擎实现的效果,图为火山场景,绿色的是怪物
主要是因为物理引擎的实现太多艰难了,才给我留下那么深刻的印象。
我们的地形也很有特色。
地形是根据地形高度图生成的。
比如下面是草地的地形图,生成的对应的游戏场景:
从而,使得,游戏只需要读入不同的地形高度图,就能够生成不同的地图了。
这个节省了我们很多建模的工作。通用性也更强一点。
GUI 的框架是选择了CEGUI 实现的。我们在游戏中还设立了一些属性点,通过CEGUI 和很好地表现出来:
游戏实现的一个功能是,能够通过设立在场景的传送点,传送到下一个场景。
设计了一共4种场景:草地、火山、沙漠、峡谷。
这个实现的场景大致如下:
图【7】火山的场景
图【8】沙漠的场景
最主要的问题是,游戏场景中资源的回收和分配要做好。每次场景的载入,都会销毁一批资源(然而并不是所有的资源都销毁,所以才比较头疼),并且分配一批新的资源。
我是考虑着,能够写一个简单的脚本系统, 用于设置游戏中的数据,比如地形、糖果设立点、人物复活点等等
使得数据和代码分离。
这是个不错的想法,于是就这么干了。
脚本的语法自己定义实现,自己写代码解释。
大致的效果如下吧:
# 拿到各种初始化位置
# 对应地形图像
# 名字
desert
{
# 图像的文件名
image_file_name = "desert.png"
# 复活位置
relive_position = (115,132)
# 初始化位置
# initialise_position = (127,161)
initialise_position = (127,161)
# 糖果位置
candy_positions
{
(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)
}
# 传送点
transfer_point volcano
{
(287,286)(288,286)(287,287) (288,287)
}
transfer_point grass
{
(37,7) (37,8) (38,7) (38,8)
}
}
volcano
{
# 图像的文件名
image_file_name = "volcano.png"
# 复活位置
relive_position = (137,138)
# 初始化位置
initialise_position = (140,7)
# 糖果位置
candy_positions
{
(22,13)(44,7)(81,7)(124,23)(5,33)(20,45)(14,62)(14,81)(15,101)(11,115)
(34,139)(78,143)(145,104)(140,78)(136,52)(48,62)(47,105)
}
# 传送点
transfer_point desert
{
(146,2) (147,2) (146,3) (147,3)
}
}
grass
{
# 图像的文件名
image_file_name = "grass.png"
# 复活位置
relive_position = (231, 73)
# 初始化位置
initialise_position = (164,136)
# initialise_position = (40,236)
# initialise_position = (265,277)
# 糖果位置
candy_positions
{
(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)
}
# 传送点
transfer_point desert
{
(43,236)(44,236)(43,237)(44,237)
}
#无剧情地图,,只有怪物的峡谷
transfer_point canyon
{
(270,277)(271,277)(270,278)(271,278)
}
# NPC 名字 id
NPC Odom 0
{
position = (173, 122)
}
NPC Goodman 1
{
position = (213, 234)
}
}
canyon
{
# 图像的文件名
image_file_name = "canyon.jpg"
# image_file_name = "ttttt.jpg"
# 复活位置
relive_position = (73,241)
# 初始化位置
initialise_position = (195,107)
# 糖果位置
candy_positions
{
(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)
}
# 传送点的意思
transfer_point grass
{
(236,261)(237,261)(236,262)(237,262)
}
}
volcano
{
# 图像的文件名
image_file_name = "volcano.png"
# 复活位置
relive_position = (137,138)
# 初始化位置
initialise_position = (140,7)
# 糖果位置
candy_positions
{
(22,13)(44,7)(81,7)(124,23)(5,33)(20,45)(14,62)(14,81)(15,101)(11,115)
(34,139)(78,143)(145,104)(140,78)(136,52)(48,62)(47,105)
}
# 传送点
transfer_point desert
{
(146,2) (147,2) (146,3) (147,3)
}
}
#文本的结束
end
END,感谢阅读
Email: li.xiaojian233@qq.com