“史诗”级宇宙科幻大型多人在线网络游戏:《黑暗森林》

之后的更新主要分为三个内容,都是比较棘手的问题。正好一个类型一个:
核心算法方面:我想设计的结盟系统要求一个玩家不知道对方是否于自己结盟的状态下与对方结盟。但是结盟后对方能享受一些“好处”,比如科技、信息等方面。但是如果这样,一个文明就能通过自己是否享受这些“好处”来判断对方是否与自己结盟。这从一定程度上消除了猜疑链。
网络方面:我目前的设计是用户发来指令时,先存储在一个数组中,每一个TICK集中执行。后来发现,这种执行方式类似于nio(非阻塞式io),所以与其费时费力自己编写nio类与方法,为什么不直接使用java的nio呢?下一步准备io大手术。
界面方面:加速学习LWJGL,早日做出GUI

楼主 Andy18650  发布于 2019-06-03 21:39:00 +0800 CST  

百年修得同船渡,千年修得神同步。

恭喜黑暗森林omega(比alpha还要早,倒回去了)获得和教程一样的显示。。。

楼主 Andy18650  发布于 2019-06-04 15:13:00 +0800 CST  
震惊!java的泛型居然就是个摆设。。。

我往一个理论上只接受Son的HashMap中扔了一架飞机(Plane)它居然——
接了。
接了。。。
接了。。。。。
连一个异常都没有抛,就这么——
接了。
接了。。。
接了。。。。。
这样,核心算法又可以大改了。

楼主 Andy18650  发布于 2019-06-04 21:37:00 +0800 CST  
一个悲惨的消息:楼主在最近的更新工作时将双遍历改为单遍历,结果Bug终于控制不住爆发了。。。
事情是这样的:楼主编写这个程序的时候,主要目的是技术验证而非正式上线,所以很多编程风格方面没有注意,Bug也就是局部控制一下。就像医生头痛医头,脚痛医脚地控制病情发展,于是落下了很多问题,这些问题的核心,就是“双遍历”问题。简单说来,星球和舰队是同一个类的不同子类,目的就在于统一管理。但是很多时候,这个统一管理并没有落到实处。在存储上,游戏使用两个可变数组来分别存储两种文明单位,搜索时分别搜索。这就是“双遍历问题”。本身这没有太大计算上的浪费,但是每次维护搜索算法要维护四次(星球搜索舰队,舰队搜索舰队,星球搜索星球,舰队搜索星球)增加了人工误差的概率。而且用户处理发现事件时也需要两个方法、存储自己的文明单位需要两个数组。。。。。。
所以,楼主决定大改核心算法,但是。。。这直接导致之前产生的Bug大爆发,最终导致楼主放弃DF的维护与进一步开发。。。
然而,这个坑不会无限制鸽下去,楼主决定!重写!有了上一次的经验和一部分成熟代码,加上学LWJGL教程学来的正经游戏编程风格和编程理念(之前我的参数全部不会private,导致封装混乱。。。),下一次的程序应该会在不久的将来与吧友们见面。

楼主 Andy18650  发布于 2019-06-05 20:05:00 +0800 CST  
说实话,上次的程序失败的一个主要原因在于我想看到自己写了多少代码自我吹嘘一下行数,所以把服务器代码全部写在一个Java文件内,结果写了1400行以后自己找自己的代码得半天。。。

楼主 Andy18650  发布于 2019-06-05 20:08:00 +0800 CST  
趁端午节放假,楼主成功完成代码移植工作。

↑:游戏主类,空空如也。。。这表示新版的黑暗森林不会很快与吧友们见面,楼主要写完所有核心算法并且保证这个算法没有逻辑错误、代码风格优美、执行流畅、健壮性强以后才会开始编写用户界面,由于不是技术论证,这次直接上图形界面。

文明单位,可见TRP三大变量,名称和所有人等星球和舰队共有的属性。还有共有的构造方法,以及共有的Growth和Search两个方法(growth现在改成了grow,动词更能表现这个方法的“主动性”)。

↑:星球类,可见自身的布尔变量isalive,如果为false则星球休眠,还有实现的growth(grow)方法,但是科技支援方面被提取成为一个单独的方法而且注释起来,因为其中涉及“双遍历”问题:它只需要遍历所有星球(舰队不接受科技支援),但是PLANETS以及不存在了。

↑:舰队类:可见表示是否主动攻击的attackenemy变量,以及表示速度的矢量,还有完整的构造方法和变轨方法。

↑:信号类:比较简单,因为其搜索星球和舰队的tickSearch方法也涉及“双遍历”问题,所以没有施工。

↑:Utility类,其中只有方法,没有参数,而且方法全是静态的,说白了,这就是个工具箱,保存了上一版中Client类和主类中的很多工具方法(比如数组连接,各种类型转数组)。之前这些方法遵循“哪儿要用放哪儿”然后发现,这里用完了,那里要“借过去”用,所以干脆封装起来。

↑:变量类,其中只有变量和各种常数,也都是静态的,之前这些东西放在主类中。
话说回来,这个类可以和Utility合并。


↑:最后,矢量类,这个Vector和一个远古类重名了。。。但这才是真正的Vector啊!那个可变数组为什么要叫Vector啊啊啊啊啊?
啊啊啊啊啊?
啊啊啊啊啊?
啊啊啊啊唉!
这个类的更改主要在把计算方法由“非主动”改为“主动”,即原来用静态方法输入两个矢量进行计算,现在用对象调用,对象自己就是一个操作数。加法更新自身以外(因为用于更新舰队的坐标),减法是返回一个新的对象,这样方便连续计算(比如a.minus(b).absolute)还可见返回绝对值的absolute和返回距离(差的绝对值)的getdistance。
而且,在这些类中,多可见一些toBytes方法,这是因为当年我信不过Java的序列化系统,偏要手动序列化,现在系统地学习了序列化以后,这些方法都可以删去了。

楼主 Andy18650  发布于 2019-06-07 23:43:00 +0800 CST  
话说这样基本等于开源了。。。吧友们也可以积极建言献策,有合理的建议楼主会采纳并加入程序。

楼主 Andy18650  发布于 2019-06-07 23:45:00 +0800 CST  
更新通知:
[核心算法优化]: 新的服务端算法“单遍历”初步成功,代码量直线下降!

楼主 Andy18650  发布于 2019-06-10 09:40:00 +0800 CST  
[核心算法优化]: 增加Supportable接口,接口内没有方法,实现该接口的类皆可以接受科技支援,为未来程序扩展留下空间。

楼主 Andy18650  发布于 2019-06-10 15:15:00 +0800 CST  
[核心算法优化]: 把Variables并入Utility。

楼主 Andy18650  发布于 2019-06-10 15:23:00 +0800 CST  
[核心算法优化]: 把Search(搜索)算法的最关键、最核心的部分放入被搜索的单位的类中。解决了“双遍历问题”的核心部分。
“双遍历问题”的核心在于,星球的坐标是不会改变的,所以大可不必在其历史[ArrayList]中记录坐标数据。但是舰队不同。所以这导致两者的搜索方法很不相同。搜索分为两个阶段:第一,判断被搜索者是否能被“看见”,这与两者的科技人口无关,只是判断光锥是否覆盖。第二个才是比较科技人口的乘积与FINDING CONSTANT。
但是,在判断光锥是否覆盖的问题上,搜索星球时只需计算搜索者与星球之间的距离,再除以光速得到时间差,然后用当前时间减去时间差得到自己“看到”的“过去的那个星球”的时间,再请求调用该时刻的历史即可。对于舰队,不同时刻的距离不同,则需要遍历对方的历史,看其中是否有一个时刻,使得当时的位置与自己现在的位置的距离除以光速正好等于当前时间与那个时刻之间的时间差才能判定“看见”。

楼主 Andy18650  发布于 2019-06-11 11:41:00 +0800 CST  
刚才讲到的问题,导致文明单位的tickSearch方法要分别遍历星球和舰队,并用不同的方式求出自己能否“看见对方”,而后期的“能否发现”就都是一样的。What's worse,我原来把tick_growth和tick_search(即现在的tickGrow和tickSearch)两个方法都在Civilization_unit中设为abstract,然后在子类中实现。因为楼主认为星球和舰队无论在成长方式和搜索方式上都是不同的。后来的实践发现,虽然星球和舰队的成长方式天差地别(星球发展科技、降低资源,坐标不变,舰队资源、科技都不变但刷新坐标),它们搜索是方法都是相似的。所以楼主将这个方法集成到父类[CivilizationUnit]中,然而还是要分别遍历星球和舰队。这样还有一个问题:如果要添加新种类的文明单位的话,就必须修改最高父类CivilizationUnit,造成程序强耦合,不利于未来的扩展。后来楼主想:既然星球和舰队的“被搜索”方式不同的话,那么是否可以变“搜索方法”为“被搜索方法”呢?在这一思想的指导下,楼主写出了划时代的getShadow方法,一劳永逸地解决了造成之前程序重编的“双遍历”问题。同时大大增强了程序的可扩展性。

楼主 Andy18650  发布于 2019-06-11 15:28:00 +0800 CST  
[核心算法优化]: 增加config文件读取,自动设置各种参数,如果没有则取默认值。
PS:这个文件还可以夹藏“私货”,只要不以参数名开头即可。

楼主 Andy18650  发布于 2019-06-11 20:29:00 +0800 CST  
50楼订阅人数大涨啊。。。但是新一期程序离完成还有不少路要走。所以发布一期服务器端源代码大家过过干瘾。

楼主 Andy18650  发布于 2019-06-11 20:37:00 +0800 CST  
订阅人数突破20人大关!撒花!

楼主 Andy18650  发布于 2019-06-11 21:23:00 +0800 CST  
现在50楼专用发邮箱,其他内容都删除了,方便统计订阅人数

楼主 Andy18650  发布于 2019-06-11 21:24:00 +0800 CST  
[核心算法优化]: 新服务端engage(交战)算法完成使用和getShadow一样的思路,放在“被交战”的对象里。代码量减少YES!
到现在,核心算法已经基本完成,除了——结盟系统。

楼主 Andy18650  发布于 2019-06-12 10:50:00 +0800 CST  
[用户界面编写]: 成功做出简单动画(虽然傻的可爱。。。)

楼主 Andy18650  发布于 2019-06-12 14:46:00 +0800 CST  
结盟系统近期不打算编了。因为结盟到什么“程度”一直是一个问题:是仅仅双方的舰队互不干扰,还是舰队进入盟友星球不会占领?还是共享信息?共享哪些?自己星球状态?自己知道的敌人状态?甚至,是否可以从盟友星球起航舰队?这是否受光速限制?反过来,如果这些盟友特权都可以手动设置(目前想到的唯一解决办法),不开放任何特权,只是标记的“裸盟”可不可以?

楼主 Andy18650  发布于 2019-06-12 14:51:00 +0800 CST  
一个不可解决的Bug:文明内部超距作用。因为玩家要掌控自己的文明,所以文明内部是超距的,星球和舰队之间的信息传递不受光速限制。这个问题不是不可解决,设置一个明确的“指挥中心”即可。但是设想一下,一个远方的星球或舰队已经被消灭很久了,指挥中心才收到敌人进攻的消息。这看似比较合理,也增加了游戏的策略性,但是这给文明的扩张设置了绝对的限制(不能太大,否则根本无法管理边境),而且,难道远方星球或舰队的指挥是吃干饭的?不会做出反应?如果做出,这个反应由谁来操作?由AI?不行。这违反了这个游戏的基本思想——玩家代替AI操控文明。由玩家本人?也不行。那样就超距了。由其他玩家?怎么保证这个玩家执行指令?(其实《战舰世界》的航母单位也有这个问题,但是它用一个还行的AI搪塞过去了)那么玩家必须组队。但是,如果玩家组队,这个队伍必须十分庞大——一个文明到后来可能发展到几十个星球,上百支舰队,但是大部分时候,大部分队员又没有作用,怎么达成“给吧友们消遣”的目的?而且,玩家组队,必然依赖于现实中的关系,否则即使一个队伍也可能拒绝执行命令。那么一个队伍既然在一起玩,即使不在一起也可以用打电话或QQ、微信等方式联系,这样一来,岂不是又可以“超距”?好在《三体》早就给楼主这样的程序员找好了台阶下:可以设定一个文明的单位之间建立了基于智子的通讯网,可以超距通讯。

楼主 Andy18650  发布于 2019-06-12 15:08:00 +0800 CST  

楼主:Andy18650

字数:13774

发表时间:2019-05-27 19:39:00 +0800 CST

更新时间:2019-07-08 18:34:45 +0800 CST

评论数:253条评论

帖子来源:百度贴吧  访问原帖

 

热门帖子

随机列表

大家在看