文本解析:

将txt存入DataFile,DataFile包含DataNode,每个DataNode是txt中的一行,每个DataNode包含若干token(std::vector<string>),token之间用空格隔开,多个单词可用引号括起来组成一个token,每个txt在读取时在末尾加一个换行符(\n),DataNode之间有层次关系,根据txt中的缩进确定(就是数空格)。

游戏的文本主要在void GameData::LoadFile(const string &path, bool debugMode)里面解析和载入,每一次读入一个txt到DataFile,他的上级是void GameData::BeginLoad(const char * const *argv),内含两级循环调用加载,将游戏主目录和插件目录的data文件夹内的txt依次读入。对txt的解析在DataFile类的构造函数中,他会调用void DataFile::Load(const char *it, const char *end)来实际进行解析,分为两步,先建立DataNode,然后对当前DataNode分token,用一个字符指针来访问每一个字符,数空格并将每一行开头的空格数记入std::vector<int>,注释(‘#’开头)和空白行(‘\n’开头)直接跳过该行(末尾为‘\n’)。添加DataNode需要明确层次关系,首先应该检查上一个DataNode的空格数与当前的哪个大。然后获取上一级的children list地址,再将上一级作为参数传入children的构造函数用来构造当前node,他会初始化上一级为当前对象的parent并预留4个token的空间,再将新对象加入上一级的children list,这样层次关系就建立了,然后再将该child加入std:: vector<DataNode *>,记录对应的空格数,用来决定下一个DataNode的层次关系。实际上root并不含实际值也就是token,只有children list,所有token都在不同层次的children里面,DataFile含root下一级的children list,该级DataNode又有children list,如此就可以继续往下了,用迭代器访问只能访问DataNode的下一级。第一步至此完成,第二步按分隔符来就行了。

将data载入系统:

DataNode的token都是std::string,实际使用时如果需要的是数字,调用double DataNode::Value(int index) const将指定token转为double,支持正负号、小数点和科学计数法。载入信息如下:

  1. color,包含rgba四个部分,需转成float,范围为0~1,用来区分不同政府和显示地图、任务以及飞船状态;
  2. conversation,游戏中访问某些星球或者空间站会触发任务,任务一般会跳出对话框,有的可以选择多个分支,当然也有接受和不接受,也可以是必须接受,而且可以触发接受以后马上起飞(有个任务就这样,飞起来就被三架raven围攻,只好退回去前面的存档去攒钱买船再来)。conversation有自己的node来管理显示和跳转,也就是说nodes[0]是conversation的第一个文本,指向下一个node的序号开始为1,conversation包含nodes(std::vector<Node>),Node类包含std::vector<std::pair<std::string, int>>来储存文本和下一个node的序号(因为有可能跳转,所以可能会不再指向创建时的node),相邻同级文本可以合并到同一个node,顺序是后面的合并到前面,判断依据是遇到choice将合并flag设为false,表示这个node不能合并后面的node,关键字不占用node。重要的关键字:
    1. scene用来显示图像;
    2. label用来标记node以便跳转,label不重复,每次添加label(std::map<std::string, int>)会检查是否有未关联的跳转,如果有会试图用新添加的label来关联,同时记录label在nodes中的位置用来指向下一个文本node;
    3. choice用来显示分支选项,choice的下级node为文本,有的choice的下下级node会有goto label,那么就需要建立跳转关系,如果跳转label已经添加,那么直接将label的位置设为跳转语句前一个文本的下一个node序号,如果没添加,那么就将其label和文本位置存进std::multimap<std::string, std::pair<int, int>>,还有的是decline等关键字,则将其对应的数字设为跳转语句前一个文本的下一个node序号(<0);
    4. name用来表示空的choice;
    5. branch用来表示条件分支,条件为branch的下一级,可为多重条件(and、or),存入std::vector<Expression>,expression可为一元或二元操作,一元操作会映射为二元操作,branch关键字后面必须跟条件测试为true的跳转label,此label会被建立关联,如果为false则继续执行下一个node,如果为其他关键字,同choice处理;
    6. apply用来改变condition,比如完成某些任务可以添加一些属性,可以用来触发下一个任务;
  3. effect,用来定义显示效果,比如引擎的尾焰、武器和Jump Drive等。关键字后跟效果名,下一级为sprite、sound、lifetime、velocity、random velocity、random angle、random spin和random frame rate,sprite下一级为sprite的动画参数,包括frame rate(用60归一化)、frame time(倒数就是frame rate)、delay、start frame、random start frame、no repeat和rewind,具体参数作用待查;
  4. event,用来定义特殊事件,需要定义对应的condition用来标记他是不是被触发了。其下一级用来设置事件发生对游戏状态的影响;
  5. fleet,用来定义舰队,关键字后面跟舰队名,同名舰队可以多次载入,载入的ship如果有variant,会将variant替换掉原来的ship。下一级用来设置fleet都有些什么ship,还有其他属性,variant后面跟了个权重用来估算战斗力,ship名后面跟的是数目;
  6. galaxy,用来定义星系,关键字后面跟星系名。相当于给某个区域取名,下一级为位置和显示的sprite,其实就是在指定位置显示sprite;
  7. government,用来定义所属政府,比如Republic、Free World和Hai等,关键字后面跟政府名,参数比较多,主要包括了各种好感度及玩家行为对其影响,有的外星人会说鸟语,有的敌对星球可以打或者贿赂,这些都在这里定义,government有个ID(数字)方便设置好感度;
  8. interface,用来定义显示界面,关键字后面跟界面名和对齐位置,下一级参数可设置显示的sprite、按钮、文字和各种自定义图案等。point关键字可用来画一些自定义图案,主要用来定位和设置尺寸。visible关键字可用来设置下一个node是否可见,后跟if条件。其他关键字可用来设置显示各种element(ImageElement、TextElement和BarElement,均继承自Element),各自的constructor调用void Interface::Element::Load(const DataNode &node, const Point &globalAlignment)来载入参数,整个Interface就是由element和point组成;
  9. minable,用来定义可开采的物体,继承自Body类,游戏中背景会有很多飞来飞去的陨石,有的里面有payload,但是概率不高,每个陨石1000血(有个用陨石撞星球去改变地貌的任务挺犀利,虽然需要大量脑补。。。);
  10. mission,用来定义任务,这货的加载就是一大坨if。日常任务(非剧情)有送货(有的有截止时间,有的会让送非法货物,如果被navy扫描到会罚款 )、送人、护送(一般有截止时间)和打海盗(这个比较赚),会随机生成,可以定义任务出现的地点,有的mission需要用LocationFilter来临时改变星球的某些属性,ConditionSet被用来记录任务的状态,另外会用到std::map<Trigger, MissionAction>来存储任务触发响应,有的任务还包含NPC,不同NPC会有不同的行为模式;
  11. outfit,用来定义ship上的装备(包括武器、货物等除机身以外全部可加载的东西),包括种类、图像、效果、音频和价格等属性,基本就是存到map或vector里,比较麻烦的是加载武器,武器有图像、音频、效果、各种杀伤力和机动性参数等,所有又是一堆if;
  12. outfitter,用来定义装备的买卖信息,基本就是添加删除读取outfitter里的outfit,所以需要传加载的outfit给他,实际是有一个Sale类来管理;
  13. person,用来定义非任务型NPC,游戏中会有飞船跟你说话,地点随机,人物唯一,基本就是加了一些限制的随机;
  14. phrase,用来定义短语,游戏有很多随机产生的名字啊对话啊什么的,就靠这个,需要载入词典;
  15. planet,用来定义星球,参数也多,if搞起。一个system可以有多个planet或者station,planet指可以登陆的星球,有装饰性星球不能登陆,planet不一定有shipyard或outfitter,但是都可以买卖货物,比较有其他功能的是attributes参数,有些任务会用到,还有tribute,带这个参数的可以被征服,然后会给你上贡;
  16. ship,用来定义飞船,参数多,支持飞船变种(同一个model参数不同),基本也是if,武器系统比较复杂且一个ship可装备多个武器,所以另有一个Armament类来管理,这样便于多个武器之间协同工作;
  17. shipyard,用来定义ship的买卖,和outfitter类似,只是传的是ship;
  18. start,用来定义初始化信息,包括时间、地点和玩家账户;
  19. system,用来定义星系信息,包含若干planet,所以要传planet给他。habitable后面跟的是可居住星球的范围,belt后面跟星球的环形带半径,link后面跟通过可通过Hyperdrive直接到达的星系,fleet后面跟舰队名和出现的周期,会用随机概率处理这个周期,随机的相关内容待查,trade定义的是system内商品的基价,实际价格会根据商品的供求量变化,object用来定义比较大物体(planet、star、station等),继承自body,会绕轨道运动,每天更新位置,object后跟星球名表示可以登陆(也就是planet),这个时候会返回对应的Planet对象来提供更多信息,同时记录planet所在的system(如果已经记录过则不添加),object的加载需要明确其类型(star、station、moon),object 有层次关系,注意这里的period不是出现概率而是用来运行周期,会换算成速度(度/天),distance为与parent的距离,offset为角度差(用来区分同速星体),object的运行角度用step表示(0~2^16-1),层次关系由加载object的顺序决定,载入完成后设置非planet的message,这里发现distance还有一个作用,一个星系的顶层object也可以设distance参数,这个distance用来与habitable一起决定object的message,远了就hot,近了就cold,中间就直接不可居住;
  20. trade,用来定义商品信息,每一类商品价格有范围,每一类有具体的商品表,实际交易不可见,有的任务可以用来显示,还有的是garbage,卖不了钱;
  21. tip,用来定义提示信息,下一级为具体提示,与tip后的string合并显示。
  22. 设置星系相邻关系:所有星系都载入到Set<System>里,通过遍历所有星系并比较距离是否大于NEIGHBOR_DISTANCE来建立相邻关系,这里分两类,一类是直接通过Hyperlink连接的星系,他们即便距离大于NEIGHBOR_DISTANCE也相邻,一类是距离小于NEIGHBOR_DISTANCE的任何星系,包括由Hyperlink连接的星系。遍历时只记录了当前星系的相邻星系,所以会重复检查;
  23. 设置飞船:前面从txt中已经分别读取了outfit和ship的信息,现在就是要把outfit装到ship上,获取ship的初始化属性(在加载txt时记录),如果是变种则将未定义属性从原型中复制过来,如果有新增加hardpoint,则添加相应的hardpoint,飞船的类别是装备类的一个属性,设置gun和turret的可装备数(hardpoint),武器的hardpoint和outfit为一一对应,一个ship可有任意个hardpoint,hardpoint可不挂载outfit,有的outfit只会影响attributes,所以只是数值变化,武器会体现在显示效果上,每一个outfit的增减都要计数,ship有最大加载量,同时武器和引擎也有自己的上线,hardpoint的加减是由Armament类来管理,武器由Hardpoint类管理,先计数,后安装,安装时会设置武器的发射方向,和武器的最大发射半径(不是射程,是距离ship中心的最大距离),飞船有主次关系,玩家的旗舰是parent,其他的是escort,只有parent和escort在同一系统时escort才能修复,当ship的hull降到一定值以下或crew没有了(特殊情况除外)就会失控,当ship登陆spaceport时,hull、shield、crew和fuel都会充满,所有负面效果都置零;
  24. 设置NPC飞船,同上。

载入存档:

  读取存档和之前的文本分析一样,由PlayerInfo类管理,读取后需要调用void PlayerInfo::ApplyChanges()将内容载入到游戏状态中。changes标签下为对system的改变,在改变system之间的link或neighbor状态时,需要将两个system的link或neighbor状态添加或删除,因为同一状态存储在两个system中,如果两个system距离大于限制,在添加或删除link的同时要添加或删除邻近关系。载入游戏中的日期,根据日期来设置星体的位置,日期为int date=day+(month<<5)+(year<<9),所以读区时要相应位移并用“&”取指定位数,有闰年。hull为负数表示ship挂了,特殊NPC挂了要记录,有上贡的planet要加入记录(std::set<const Planet *>)。

载入shader:

  根据不同的着色方式分为多种shader,上层为一个通用的Shader类,用来进行对象管理、编译等通用操作,实际使用的shader的定义和操作在对应的类中。shader对象的创建主要包括编译和联结两步,编译时将OpenGL版本和shader代码存入vector<GLchar>,并在末尾加“\0”(NUL),调用OpenGL函数将其整体编译,编译完成后创建program并将编译的shader与其联结。shader类型有:

  1. FillShader,用来画矩形;
  2. FogShader,用来产生战争迷雾;
  3. LineShader,用来画线;
  4. OutlineShader,用来画sprite的轮廓;
  5. PointShader,用来画“点”;
  6. RingShader,用来画圆或者环;
  7. SpriteShader,用来画sprite模型。

Endless Sky源码学习笔记-3的更多相关文章

  1. Endless Sky源码学习笔记-1

    难得遇到一个比较有趣的开源游戏,又是比较偏爱的太空背景,所以打算学习下源码. Endless Sky的作者是Michael Zahniser,是一个2D太空游戏.整个程序比较简洁明了,数据没有打包,游 ...

  2. Endless Sky源码学习笔记-5

    游戏启动后的UI划分为三个区域,左侧滚动显示credits等信息以及偏好设置和退出按钮,中间显示载入动画,右侧显示玩家信息以及载入存档按钮,调用void MenuPanel::Draw()实现.首先画 ...

  3. Endless Sky源码学习笔记-4

    事件处理: 事件包括:debug模式切换.切换到登陆窗口.退出.窗口大小变化.全屏切换和游戏中的鼠标键盘输入.处理方式分为两类,前几个为简单的if处理,最后一个涉及到游戏中的控制和交互,且事件由每一个 ...

  4. Endless Sky源码学习笔记-2

    数据载入框架: void GameData::BeginLoad(const char * const *argv)为数据载入的最上层method,其主要框架为: void Files::Init(c ...

  5. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  6. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  7. AXI_LITE源码学习笔记

    AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...

  8. Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...

  9. Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构

    Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构 之前我们简要的看过了DataNode的main函数以及整个类的大至,现在结合前面我们研究的线程和RPC,则可以进一步 ...

随机推荐

  1. android的四层体系结构,基于mvc三层结构浅析

    从多方面理解Android体系结构 1.以分层的方式来看Android 安卓体系结构分为四层. 首先看一下官方关于Android体系结构的图: 1).Linux Kernel:负责硬件的驱动程序.网络 ...

  2. 04-c#入门(类型转换)

    “无论是什么类型,所有的数据都是一系列的位,即一系列0和1.变量的含义是通过解释这些数据的方式来传达的.”——这句原话是书上翻译的,不过后一句话总感觉理解起来不是很通俗,自己觉得这样理解可能会合适些: ...

  3. STL之set

    set都快不会用了...整理下... 应该注意的是set中的值是不能相同的...和map一样... 原文链接:http://blog.csdn.net/wangran51/article/detail ...

  4. C语音常用库和函数

    #include <assert.h> //设定插入点 #include <ctype.h> //字符处理 #include <errno.h> //定义错误码 # ...

  5. 学习indy组件之一idhttp的使用方法

    登录 注册 百度首页 新闻 网页 贴吧 知道 音乐 图片 视频 地图 百科 文库 经验 搜索答案我要提问 首页 分类 公社 知道行家 问医生 高质量问答 经验 个人中心手机知道开放平台   关于del ...

  6. iOS 在tableView上添加button导致按钮没有点击效果和不能滑动的 zhuang

    转载请注明出处. 今天在调试代码的时候,在tableviewcell上添加button,发现button快速点击的话,是看不出点击效果的,查找资料发现, ios7上UITableViewCell子层容 ...

  7. android下拉框

    XML: <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:androi ...

  8. BCM94352HMB蓝牙BCM20702A0在Ubuntu 14.04下的驱动方法

    作者:秋忆 出处:http://www.cnblogs.com/qiuyi21/p/bcm20702a0_ubuntu.html 先确认蓝牙的产商编号(idVendor)和产品编号(idProduct ...

  9. Android 调用系统联系人界面的添加联系人,添加已有联系人,编辑和修改。

    一.添加联系人 Intent addIntent = new Intent(Intent.ACTION_INSERT,Uri.withAppendedPath(Uri.parse("cont ...

  10. 《UNIX/Linux网络日志分析与流量监控》新书发布

    本书从UNIX/Linux系统的原始日志(Raw Log)采集与分析讲起,逐步深入到日志审计与计算机取证环节.书中提供了多个案例,每个案例都以一种生动的记事手法讲述了网络遭到入侵之后,管理人员开展系统 ...