玩家玩大富翁4找不到光驱到一块空地地要按照上面的数值收钱吗?具体游戏规则是什么?

6060人阅读
软件工程(139)
理论篇(10)
采用JSON脚本组织通讯
字符串编码(数字类型),发送时再把字符串转成相应的数值类型发送(省发送长度)
as3中delete 其实就相当于把 obj = null
用json时报一大堆错, 把C/C++的代码生成的运行时库调成的类型是供用双方一致即可。
回调, 好组织面板, 但是不好扩展。
消息, 好扩展, 但是不好组织面板。
界面的东西,不适宜用消息, 而宜回调, 因为界面是可见的, 可见的东西一般是固定的,需强管理弱扩展。
框架的东西,不宜用回调,而宜用消息, 因为框架是抽象的,灵活多变的,需弱管理强扩展。
每个实体,除了程序运行给他生成的ID外, 还应有一个脚本ID属性(这个ID可以是脚本配的,也可以是玩家注册时数据库配的)
今天见到一同事写的页游代码, 它在create功能里实现了单例模块, 在get功能获得对象。 初看下,感觉怪怪的。 因为C++里,一般是get里面实现单例的, 而不是在create里。 后来一想, 还是C++准确些:因为当用户get时,他是不关心你这东西是那来的, 所以里面你实现单例是充许的。 但是当用户create时,他是会关心要创建问题的,有时他甚至会有创建多个的需要的。 这样把create给封成单例了, 就等于把创建多个的需求的封死了, 那还不如不要提供create这个功能呢。
施法者提出一个施法公式,施法公式以公式单元的方式存在于服务器列表。公式表的每次刷新,都会引起客户端的效果更新。
提炼出施法公式,有利于数值策划在更抽象的层次上独立的思考游戏世界的各种公式和数值组合,从而建立更合理和有趣的游戏体系。
每个施法者只是施法公式的发布中介。
服务器是不管客户端显示的。一切有关显示的问题客户端自已去解决。
施法公式的传参问题,可以考虑通过直接传参加框架API间接取值的方式相结合。
DB_ID, SPR_ID, U_ID,
color = frame.data.getPixel(localX , localY);
if (color != 0x)
通过牺牲一个色彩值,达到点与面的像素级的检测碰撞。
上帝说, 要有一个点击类,于是便有了一个点击类。提炼出“点击类”,可以让点击事件可控, 从而减少开销。
点击类功能点:&
1,使某实体可触碰。
2,提供点击类型。
3,鼠标的触发类型。
装饰类其实类似是C++类的友元,有时侯你想给类增加一些功能, 但这些功能点是附带的,会随需求增长变化的,如果集成或固化到类里,又感觉成本太高而且不灵活(附带功能的变动会引起整个类的变动)。举个例子, 任务NPC在有任务时头顶相应图标。如果直接服务器任务类发信号过来,则加重任务模块和UI的关联性了,这样的绑定弱化了任务模块的灵活性。 更好的做法则是类似客户端在处理服务任务数据时,增加一个友元(或者装饰的东西),处理NPC的头顶图标。
突然觉得如果击杀BOSS的头上可以有个光标就更好了。就像接受任务时,NPC头上会顶上卷轴。
如果打副本时,击杀BOSS的头上有“集火”,“击杀”的标志, 就更明确了。甚至如果可以, 当角色成为帮主,或者啥啥啥的, 可以送套限时的皮肤,例如角色的装备变成黄金圣衣那样, 就更叼了。
服务器其实做的就是把相关的部件数据刷新到客户端的所有相关部件里。&
某个时间段的单个部件SC行为(注意:是&&行为集):
&= SC行为(S_part_k -& Cx_part_k)
& & &SC行为(S_part_k -& Cx_part_k_1)
& && SC行为(S_part_k -& Cx_part_k_2)
& && SC行为(S_part_k -& Cx_part_k_3)
& && SC行为(S_part_k -& Cx_part_k_N)
某个时间段的单个部件CS行为(注意:是||行为集):
&= CS行为(Cx_part_k -& S_part_k)
& & &CS行为(Cx_part_k_1 -& S_part_k)
& || CS行为(Cx_part_k_2 -& S_part_k)
& || CS行为(Cx_part_k_3 -& S_part_k)
& || CS行为(Cx_part_k_4 -& S_part_k)
客户端的场景实体刷新算法是只刷新当前可见的实体,针对这样的情况,可以搞一个只针对当前需求单例的计时器, 每次调用只刷新该计时器的计时量即可。 这样玩家狂奔时, 到了最后终点时,才进行刷新。 不会每跑一步刷一次, 从而提高了效率。
调用或回调一般用于内部模块, 因为生命周期是可控的,完全信息博弈。 消息一般用于多个模块, 因为彼此是不完全信息博弈, 用消息省去很多生命周期不同的处理。
页游的客户端有时会因为多个刷新事件呀卡在一起导致骤发性卡, 可以以定时器为基础实现一个时间槽队列类, 每个函数的调用指令会分配到一个以时间槽为单位的队列。
-- ------------------------------------
-- 时序放大器(放大函数调用的时间差)
-- ------------------------------------
G_timer_num = 0
function Lua_Run_Fun_Time_Order_Amplifier( _fun, _time_dt )
&local function run_fun()
& if G_timer_num & 0 then
& &G_timer_num = G_timer_num - 1
&G_timer_num = G_timer_num + 1
&_time_dt = _time_dt or 30
&Lua_SimplenessTimer(run_fun, G_timer_num*_time_dt, 0)
凡是要进行服务器操作的, 定义为打断行为。
偶然发现QQ的设置窗口设计得挺好, 左边是标签栏, 右边是拖拉条。这样的话, 想局部浏览或者全局浏览都兼职到了, 挺方便的。
地图的流程估计是这样的, 每走一次, 就写入人物坐标数据, 然后广播罢了。 &(寻路算法的API可以放在这里提供(或者放在怪物AI里实现我认为也行)。)
优化1, 全局广播开销太大, 只需广播附近的。所以服务器引入了九宫格的概念。
优化2, 每次都搜出九宫格还是有开销,所以引了判断是不是有交换九宫格的行为。
移动算法:
方案1: 决定移动一段距离, 客户每走一个单位,就和服务器同步信息一下(进行可否到达和下一步的指令)。
方案2: 决定移动一段距离, 服务器直接开一个定时器每个单位写一个内部数据,客户端进行界面显示,服务器和客户端不进行交互,两者分头进行应用需求。 除非服务器进行中断情况或客户端进行中断情况才中止止。
好处,减少了交互量,就算被做弊了,也顶多就是界面显示问题,服务器数据正常的。&
LOL应该就是采用了方案2,因为玩LOL会发现掉线了,人还能一直走。
像有一些耦合操作,例如地图保存人物的信息, 人物又有保存着地图的信息, 那么可以通过第三方的友元函数API实现统一的写操作。
三十二个项目,一天重写一个.
由于配置文件一般属于资源文件,每次构造相应对象时都要从配置文件里读取参数将会相当耗时。一个好的方法预先构造出各种类型的对象保存为“火种”,然后当需求真正需要用到时,将“火种”克隆一份输出。这样就减去与外部资源打交道的开销。
频繁发送数据包问题的处理,可以在应用层提供批指令操作, 也可以在底层实现“粘包处理”。
有时侯,你会发现服务器的代码有些奇怪,它不是用delete去释放,而是调用模块自身的release(){}去释放。 其实这并不奇怪,这样做的原因是dll和com技术本身有一些局限,如果外界detete,可能会造成内存漏露,所以才采用由模块自已去释放的原因。 但是话说回来, 更好的做法,如果你决定某模块非要release,那最好把它的析构函数给私有了。
责任人其实并不是真的在说责任相关的人, 而是在说真正在做的人。 一旦有问题,真正在做的那人才麻烦了, 其它人不过在旁压阵罢了。 所以让没有做事的责任人在非原则性问题上对设计可以强制性的横插一刀是不合理的。 事实上, 真有问题, 都是压在做事人身上, 责任人帮不上任何忙的。
有时需要遍历里提供回调操作(这会更灵活), 但是回调操作又可能打砸遍历次序以致程序崩溃。
解决方法1:对遍历队列进行复制以避开删除后的野指针问题。这样在做删除时, 只删除数据源的项,
遍历队列的项因为是备份的,所以没被删到,仍可正常进行,操作时要判断数据源的项是否存在。
解决方法2:回调对象定义一个死亡状态,外部没有删除功能,只有致死的权限,这样就确认了遍历次序
不会因删除行为而异常,然后在遍历过程中发现死亡状态的再进行删除。
方法1要时间(拷贝的开销),方法2要空间(死亡的状态)。&
服务器计时器的实现应该是采用方法2, 因为算时间的数值刚好可以用来折现成空间的开销。
据说, 现在的效果多是拼“底图+特效”来实现最终效果的。
我觉得这样会有问题的。 &因为特效的数据源(那些锚点信息什么的)是来自配置表XML,而底图是没有这些的。 也就是说底图的数据源要在程序上写死。 这样到时一换皮,又要改配置表又要改程序,就不好了。
还有另一个更深层的问题, 设计上的: 其实需求是只需一个特效即可的,但是因为容量原因, 不得不把特效拆分,以便压缩空间占有量。 这其实是一个压缩环节, 既然有一个压缩环节, 就应有一个反压缩环节, 再到程序使用。
即 美术(源头) -& 美术(压缩) -& 程序( 反压缩 ) -& 程序(应用层使用)
而不是 美术(源头) -& 美术(压缩) &-& 程序(应用层使用)
去掉反压缩环节,会让程序应用层又要管应用逻辑,又要管特效的拼凑方法等细节,分工不清,在需求改变时, 这段程序就会废掉的( &原先那种,需求改变时, 只是反压穷缩的环节变下罢了。)
在VS2010的调试时, 通过观察堆栈信息, 就可以看到函数的来龙去脉。
场景服start时,开启自身管理的监听模块,中心服的监听模块,前置机的监听模块, 开启完毕后。就开始加载场景服自身的模块(各个模块如有监听需求, 向相关监听模块(自身通讯线,中心服,前置机)注册监听事件即可)。
要是菜鸟,不知不觉, 做了就做了。 尼玛,别人都看出这设计就是一坨屎了,那烂得都引蚊了, 还让人家吃屎,恶不恶心?
定时器会自已封装一个设制和取消的接口, 以便应用层在做定时逻辑时,可以纯应用逻辑,脱离与具体的应用平台的关联。
虽然目前的定位有其历史认识, 但是把LUA来做配置文件的诱惑还是很大的,因为有时侯会需要一些的确有了会非常好的功能: 例如,一个数据域对另一个数据域的引用,或者继承; 甚至还需要一些简单的运算,例如想把某个图片缩放到当前分辨率的几分之几这样;又例如想依据客户端的配置情况动态计算出当前特效每帧的持续时间; 又例如要配置大规模重复的数据。。。 如果用ini或者xml,这些需求就得改动到程序了, 而LUA可以只局限于在配置文件里改。
所以, 如果LUA能实现永久保存的话。。。嘿嘿。。。
虽然目前的定位有其历史认识, 但是把LUA来做配置文件的诱惑还是很大的,因为有时侯会需要一些的确
有了会非常好的功能: 例如,一个数据域对另一个数据域的引用,或者继承; 甚至还需要一些简单的运
算,例如想把某个图片缩放到当前分辨率的几分之几这样;又例如想依据客户端的配置情况动态计算出当
前特效每帧的持续时间; 又例如要配置大规模重复的数据。。。 如果用ini或者xml,这些需求就得改动
到程序了, 而LUA可以只局限于在配置文件里改。
所以, 如果LUA能实现永久保存的话。。。嘿嘿。。。
人物有自身的属性值, 而特效有时也有一些需要临时保存的属性值。 把特效的属性值和人物属性值掺和
在一起很奇怪的, 特效的属性值显然应该是透明的, 是特效内部的事。
TweenLite是个好类, 能实现flash里的&补间动画效果&。
静动态数据的合成常用方法:
void getData(int _ID, int _dynamic_par1, int _dynamic_par2)
&print( DB_STR[ID], _dynamic_par1, _dynamic_par2 )
游戏可以在扩展功能的过程增加各种补偿,通过补偿变相打折和表达人文关怀。
任务项的更新如果每个任务配一个版本号, 工作量相当麻烦,而且而且也不必要(因为直接更改玩家的任务项是不合适的), 所以任务的更新机制是:
1, 已接取任务的玩家任务内容保持不变。
2, 新接取任务的玩家任务内容为新的。
相当于新卖的东西会不一样, 已经卖出的就不管了。
如果真的就是修改马上直接修改玩家的任务项怎么办呢?
一个更合适的解决方法依然不是每个任务配一个版本号。而是应该把玩家接的任务ID做废,同时新增一个有效的任务ID。当玩家上线时, 发现相关任务ID是做废的,依据相应的补丁逻辑把任务删掉并新接一条指定的有效ID即可。
FB开发环境真心有太多的疑难杂症,犄角旮旯了。。。 调试时改时间会引起以后的修改无效, 有时又会莫名其妙开发端口被占用了而无效。
有时侯行政管理和外交供需分成两个模块会更清晰一些, 但是这两者又有内在关联。如何处理这问题的关系呢, 一个不错的方法是对其中一模块提供一个“设定耳朵”的接受管理功能, 从而实现划分成两个模块,同时又处理了内在逻辑。
关于状态那块(状态其实就是效果集的控制器), 客户端实现状态控制器也未尝不可考虑, 就像寻路一样, 开个头后, 服务器和客户端就两头跑了, 减少了中间的通讯。但是--这个方案实现成本太高了。因为效果也不过就那么几个, 但是效果的组合是挺多的,每个组合可能就要实现一个控制器, 那这样客户端不是举不胜举了。。。 再加上单例,叠堆,增量等需求,开发量大很多, 而节省的通讯量不一定可观。因为复杂状态的不多见(就是状态里有流程控制的状态), 所以少掉的通讯也不见得可观。
服务器的状态其实是指控制逻辑, 施加效果, 前提条件, 后续行为的容器。它其实就相当于的一个处理插件。
map& pair&int, int&, int& test_
test_map[ make_pair(1,2) ] = 3;
cout&& test_map[ std::make_pair(12,33) ] &&
std::map&std::pair&int, int&, int&
res.insert( &std::make_pair( std::make_pair(12,33), 33) );
客户端的状态其实是很一个结构很简单的东西, 没有太复杂的处理的(有逻辑处理的东西服务器都搞了),所以客户端的状态元素: 状态容器, 效果插件, 表现插件, 条件插件。
当读取配置文件时,把相关插件绑定到状态容器里监听相关调用即可。 其实也就三个, 开始, 更新,结束。 & &(用插件的方式, 是为了更灵活的组合,所以不是像上面那个属性变量一样写死到代码里)
另外,打算用Lua搞, 因为效果插件, 表现插件, 条件插件的扩展量可能不少,这些如果用AS3搞,那么还要去处理配置文件怎么把参数映射到插件的处理。 而用LUA,直接让策划配插件元素即可(相当于直接配简单代码)。 &万一LUA速度不行,再在AS3里做映射的工作好了。 而且用AS3,编译时间真是太让人想砸了它了。这小事还是先斩后奏好了,不然跟原负责人恐怕要讨论半天。
用调用必须去写代码,但是用消息触发,可以把一些简单组合的消息绑定配置交给策划去配。
关于各种特效和表现的实现方案。
1, 会有创建各种风格和特效小图标的工具类。
2,应用逻辑依据需求选用需要创建工具创建小图标。
3,应用逻辑依据需求将小图标和容器绑定或解决绑定。
4, 应用逻辑会将解除绑定的小图标删除。
备注: 这里真心不需要“容器或框架的好心,把图标的显示效果也给统一实现了”。因为如果实现了,那就灵活性和性能的后路堵死了, 事实上工具就是工具,没必要带上“应用逻辑的形容词”,例如增益容器,减益容器。 &这个增减是怎么用的事,让应用逻辑去做好了。同时也不必担心开发速度的问题, 如果真有统一实现的需求也是由一个优化工具去自动生成,而不是框架来搞。
编程中,经常面临谁先谁后,而又要都到达时才成立的生命周期不同的问题。 一个不错的解决方案是,对于各方面的到达消息都进行监听, 然后全为真时进行触发。
例如加状态图标时, 应用逻辑监听容器初始化消息和自身初始化消息(自身的可以省略为直接调用。当然, 如果更严谨一些, 也可以有一个第三方模块专门做处。或者用一个更好方法:初始化和开启
分开,就像声明和定义分开一样)。 两者都回调取句柄刷新处理,这样谁先谁后都会触发,而只要两者为真时,就进行刷图。
-- ------------------------------------
-- 描述信息
-- ------------------------------------
function DES_NameTips( _name, _tips)
&local function do_DES_NameTips()
& local public = {}
& function public.add( _t ) Lua_Trace( &增加描述信息& ) end
& function public.remove( _t ) Lua_Trace( &减少描述信息& )end
& function public.init(_t)
& &--监听打开状态窗口消息,回调add
& &--监听开启状态消息,回调add
& &--监听关闭状态消息,回调remove
& function public.uninit(_t)
& &--去除监听打开状态窗口消息
& &--去除监听开启状态消息
& &--去除监听关闭状态消息
& return public
&return do_DES_NameTips
初始化和开启最好是能分开, 因为有时会面临一个声明和定义的互相关联的问题,初始和开启分开能很好的解决这种问题。结束和反初始同理。
这回的任务配得相当有长进,至少有些趣味性了,而且我猜不出剧情了。场景也相当有长进, 不像剑舞的新手村一样, 好空旷的,一眼望尽东西。现在的场景好绕,而且山水相间,花香物语,沿途还布满小动物, 一个小地图跑得我有种大地图的错觉了。
有些公司的项目把名字当标志用了。 个人感觉好二, 因为名字是界面显示的, 如果把标志和名字挂钩, 就相当于把标志和界面显示挂钩,而界面显示的需求是多变的(例如说因为想设计故事背景或者配合相关电影什么的要更改人名,地名等, 这在策划配置时经常会出现的),这就和标志符的稳定性是冲突的。 所以更好的做法是标志和抽象ID挂钩。
关于任务更新的。 首先排除的是针对每个任务都进行一个版本跟踪,工作量太大,性能下降。二,凭任务内容ID更新到新的任务内容ID,这个也不适宜, 因为新的内容ID也可能是将来的被更新,配置时还要管“更新的更新”等递归问题的配置。哇X,太累了。 哥的绝杀是: 更新不是更改内容,而是删掉旧数据,然后用GM功能直接接取到目前最新配置的任务。 也就是说, 凭旧任务“脚本内容ID”换新任务,简单易用,暴力扎实。
用装饰类的好处是使用时很方便(不用支考虑传参等细节),但维护时会麻烦,因为他们要共同约定一些东西,从而造成两个模块间的耦合性。 所以适用前提最好是约定的是一些必有的属性。 例如world里边框的花纹可以用装饰,因为窗口必有长宽这些属性。否则还是弄成一个个小插件比较好。
一些通讯优化可以通过对象释放自动解除, 不必一条条通讯解除。
用全局事件机的好处是通讯能力更强,双方只要约定一个号码,就是能进行通电话,但坏处是删除是比较麻烦, 需指定删除。 而用局部事件机则通讯虽较为弱化(因为还有事件机与事件机之间的交流问题),但在增删时更方便一些, 直接把相关的局部事件机摧毁即可释放资源。
原先的副本机制是灵活了,也好扩展; 但是因为缺乏规范,在维护方面相当麻烦,除了作者,其它人接手时需理清消息流通情况才能真正放心。所以进行改进。
win7的net send指令没了, 改为在CMD下运行:msg/server:192.168.0.118 * &你的IP占用别人的了&
虽然设计以面向需求为指导是不错, 但过渡面向需求也会带来维护的成本,因为会造成过渡的黑箱操作。事实上, 需求有时并不需要特别高的质量,甚至会需要一点点技术上的透明性,例如说需求有时会想了解程序此刻在做什么。
有时会面临配参数的问题, 有些参数可能指向静态(例如地图坐标),也可能指向动态的(例如地图的UID)。 我的建议是全都用动态参。但是动态参可能要等程序运行时才能确定,怎么办? 这个问题的解决可以交由另一层处理去解决, 例如GetShareMapUID(Map_IID), 例如GetPersonalMapUID(Map_IID), 例如GetTeamMapUID(Map_IID)。
房间系统部件是由其相关地图部件,人员部件,游戏规则部件等构成的。其中地图部件和人员部件是用于提供基础数据,监听系统事件并汇总转发出相关的游戏事件。
原先的副本内部插件是一种内存结构为链,逻辑结构为消息的组织方式构成的。这在创建时是好弄, 但是在删除时很纠结,不通用。 因为会面临删除时, 是依据逻辑结构进行删除,还是依据内存结构进行删除的问题。 
副本的流程可以在程序上仅实现功能点, 然后把功能点的选择,参数, 还能功能的组合次序(通过信号量),完全提供给策划配。
也就是说,除了以前的提供功能和参数配置外, 还可以提供功能的时序关系给策划配。
by design 设计如此
duplicate 重复的bug
external 外部因素
fixed 已解决
not repro 无法重现
postponed 待解
won't fix 不管
用消息虽好,但是有一个弱点是需要预先约定某些信号。一个不错的替代方案是用回调函数做“触发种子”,然后再建立信号。
一小朋友问一富翁说 叔叔你为什么这么有钱。富翁摸摸小朋友的头说:小时候我爸给了我一个苹果,我卖掉了它有了两个苹果,后来我又赚到了四个苹果。小朋友若有所思的说 哦…叔叔,我好像懂了。
富翁说, “你懂你妹阿 后来我爸死了,我继承了他的财产…
这笑话告诉我们:不要痴迷于从阅读成功人士的传记,从中寻找经验,这些书大部分经过了精致的包装,很多重要的事实不会告诉你:&
1、盖茨的的书不会告诉你他母亲是IBM董事,是她给儿子促成了第一单大生意
2、巴菲特的书只会告诉你他8岁就知道去参观纽交所,但不会告诉你他国会议员的父亲带他去的,是高盛的董事接待的。
3、王石的自传不会告诉你,它的前老丈人是当年的广东省副书记
4、华为的任正非不会告诉你其岳父曾任四川省副省长
5、马化腾不会告诉你他的父亲是盐田港上市公司董事,腾讯的第一笔投资来自李泽楷,李与盐田港母公司啥关系无需多言
6、任志强不会告诉你他的父亲是曾经的商业部副部长
7、潘石歧不会告诉你他的发迹是和女富豪张欣结婚后开始的&
个人专用数据可采用“下发+刷新”的方式, 共享数据还是用“查询”比较好。不然共享的一有更新,则所有人都要下发,效率下降。
FB工程导入,然后设置项目属性http://127.0.0.1\Client\bin-debug\ArpgClient.html
任务的奖励如果和等级挂勾,可能会涉及要把接受时的等级保存起来
如果系统内部元素不需要对外公开,那么可以用插件的方式;否则,可以用注册系统组件的方式提供出来。 这两者都可实现灵活性。 注意, 需要注册,不要直接用一个动态类把所有东西都搞上去,那样会让代码很乱的。
传统的观点是“管理阶层主导”,即权力由上而下贯穿。 我觉得这对IT团队这种以创新为主,以受过高等教育人群为主的员工来说,是不适宜的。 事实上,权力来自五个基础: 合法权(组织认定),报酬权(回报值),强制权(开除等惩罚),专家权(在某领域具有高超而独到的见解),典范权(人格魅力)。 &在天下乌鸦一般黑的IT大环境下, 前三种权力基础的影响因子其实并不高。专家权或还有一定影响力,但那也只对那些真正的程序员有效果。 影响因子最大的还是典范力。 当面临令行不通的时侯, 其实做管理的人自问一句:你有自信和责任感吗?
你有道德和操守吗? 你有牺牲和奉献吗? 如果这些都自问做得到位,其实底下的人自会心甘情愿发挥出主动性。 &人与人相处, 是做出多少分的人,心里都自有一把尺。
游戏团队的管理应该是“员工参与”。在大师你身体状况如此,临危授命的情况下。 我的建议是,不宜去强化团队里每个员工的服从意识。 因为游戏的服务是员工在做,游戏产品是员工在生产,游戏的BUG也是员工在解, 一个人包打天下的时代已经不在了。 公司只有关注员工,员工才会关注产品, 产品才会更好的带来回报,而强化管理者的权威却对此一无所得。 强化的结果可能是,一旦你离开,阳奉阴违的事就出现了, 再然后可能因为不懂管理技巧而矛盾激化了。 &从“员工参与”这种实际公式出发, 我觉得遇到执行不力的问题, 不妨换个角度,不是从更高一层的权力出发去强化制度权,而是关起门来,对你的权力交接者说一句:“我在想我是不是升错人”了。
& 或者反有效果。
在怪死那里截流发送信息会造成很多需求上灵活的矛盾的。一种适宜的解决方式是在接收处前,插入一个“消息特定参数触发器”,由该触发器再产生需求相应的事件。
当提出“消息特定参数触发器”这个观点时,我突然想一个经常面临的问题。 有时需求只是需要监听某个特征的消息,但因为“依赖倒转原则”的限制, 发送方不得不发送一个宏观的消息,然后接收方再在具体实现里去挑出符合自已的消息。 &发送方和接收方没有一个合理的转换空间层。&
例如登陆地图消息, 供应方系统会发出一个“登陆事件,参数是相关地图和相关人”。 而需求方功能模块需要的是监听“某人登陆某张地图”。供需之间存在严重的代沟。
“消息特定参数触发器”似乎可以解决这个问题。 有“特定人物参数触发器”,“特定地图参数触发器”, 然后这两个“消息特定参数触发器”一组合,就可以在消息上预先挑捡出特定的消息,从而减轻实现层的压力并提高整个流程的复用性。 而且“消息特定参数触发器”还可以复用“参数检验”的过程,从而提高服务器性能。
通过“消息特定参数触发器”可以解决组合消息的问题。需要监听根消息就监听根消息, 需要子消息就监听相关的“消息特定参数触发器”的组合。
我开始在猜想,这个细节上的提高是否可能改变整个服务器的代码框架了。
普通情况, 可以在监听事件里引入函数, 发现有特定要求的,则定义一个全局唯一的“消息特定参数”触发器。
最好不要用插件式实现触发器,这样便让大量复用检索条件的功能失效。所以还是用关联式实现触发器。
那是因为代码太老了,有一些以前的设计方案。现在是到了可以更完善的时间了。
我的原理是: 针对消息,会有一些“特定消息拾取转发器”,然后这个转发器构成一个消息树。
使用如下:
现在的方式:
G_Game_Server.action_map.addListener( & EVENT, public )
G_Game_Server.action_map.removeListener( &EVENT, public )
扩展后的方式
G_Game_Server.action_map.addListener( & MAKE_EVENT(EVENT, { KEY1=VAL1,KEY2=VAL2 }), public )
G_Game_Server.action_map.removeListener( &UNMAKE_EVENT(EVENT, { KEY1=VAL1,KEY2=VAL2 }), public )
然后每个&KEY=VAL&采用引用计数法进行创建和删除“特定消息拾取转发器”。 因为这个&KEY=VAL&仅仅是“特定参数触发器”,它不对数据做任何加工处理的,所以需求可以共享同一个“特定参数触发器”。
MAKE_EVENT(EVENT, { {KEY, VAL} } 和 MAKE_EVENT(EVENT, { {KEY, VAL} } 实际是构造一颗特定参数消息树。
&特定消息拾取转发器& 其实就是把参数的验证过程从处理层提炼出来,放到消息流的模块里。 因为验证功能是不会对数据做任何加工处理的,所以放到消息流时可以用共享的方法供各方引用,从而提高了性能和减少空间消耗。 又因为检证功能提炼复用了,所以处理层的实现压力减少,代码简洁化。 并且,当提炼出种元素后,还在可在消息层实现消息树的动态组合检验以更大程度的复用代码。 正可谓“一石三鸟”。
有时侯UID需要具有时间和空间都要唯一点的需求。有很多种方法解决,我的方案是通过通过可动态扩展UID所占空间实现其理论上的无限范围。
1, 发现一个需求的矛盾:系统杀死怪物和玩家杀死怪的问题一直没有很好的解决。一开始是直接屏蔽掉系统杀死怪的信号的,以便游戏逻辑可以正常的进行。但后面一些需求发现这种方式很不好,例如说:按时杀死未死亡的怪,因为系统杀怪的信号被屏蔽了,所以虽然杀了怪,但是怪物列表的数据没有获得同步更新。
2, 我突然想一个经常面临的问题。 有时需求只是需要监听某个特征的消息,但因为“依赖倒转原则”的限制,接收方是没有权决定发送方的内容的。 所以发送方不得不发送一个宏观的消息,然后接收方再在具体实现里去挑出符合自已的消息。 &发送方和接收方没有一个合理的转换空间层。
例如登陆地图消息, 供应方系统会发出一个“登陆事件,参数是相关地图和相关人”。 而需求方功能模块需要的是监听“某人登陆某张地图”。供需之间存在严重的代沟。
于是我提出了“特参转发器”这个观点。
例如有“特定人物参数的转发器”,“特定地图参数的转发器”,然后这两个“特参转发器”一组合,就可以在消息上预先挑捡出特定的消息发给接收方。通过“特参转发器”可以解决组合消息的问题。需要监听根消息就监听根消息, 需要子消息就监听相关的“特参转发器”的组合。
1, 性能提高。 “特参转发器”并不对数据做任何加工处理的,所以该触发器可以共享,并可用计数法进行资源管理。 而又因为只有根和事件源是经常一样的,所以原来的优化估计也就拿这两个东西分两层去实现共享机制,从而减少创建量。   但新的方式没有两层之分,它是随意组合关键字的。 所以触发器能共享使用的元素更多。从而更趋近于需求上的共享数而不局限于(事件源)。从而提高性能减少开销。
2, 灵活性。 新的方式没有特定的层数构造,是由用户决定的,所以更灵活。
3, 更简单。 这里没有设定好的多层结构, 全是一层结构,只是用相关触发器转换特定参数罢了。
3,分配房间时需要UID,有时侯UID需要具有时间和空间都要唯一点的需求。有很多种方法解决,但今天我突然想到通过可动态扩展UID所占空间实现其理论上的无限范围。即是说UID会是一个不限字长的字符串,当前面的所有字符可能都尝试过后,会自动增加一个位计数。经验证,效果挺好的,更接近理论空间。
在怪物的创建,控制,删除方面,一直是个老大难的问题。 一方面希望这个模块的实现是插件式的,一方面又面临着杂乱的需求功能(例如外部的控制可能有:系统击杀,玩家击杀,关闭房间引起,玩家怪物表的记录刷新,最近怪死地点的记录)。 在以前这些问题是通过不断测试,修修补补而达到完善的。 这种完善的后果就是功能可以达到, 但维护性很差。
这回重写下定决心解决了这个问题。 在怪物模块里借用了访问者模式的方案和技巧:即内部关系上,访问者会通过被访问者的回调参数的访问回自已。 在这种方式下, 个体和群体之间的内在关系得以对外界透明。 从而使整个代码的维护变得简单安全起来。 对外界来说,仅仅留下个体的操作,或者群体的操作,而不用关心两者的同步。 从新效果来看,代码简单干净很多。
实现了“特参转发器”,将进行了监控和测试。监控和测试结果显示, “特参转发器”的创建和删除是安全的, 同时功能是有效和灵活的。
习近平指出,用一贤人则群贤毕至,见贤思齐就蔚然成风。选什么人就是风向标,就有什么样的干部作风,乃至就有什么样的党风。
由于在同步服务器和客户端的参数编码和解析花的时间比较浪费。
于是我尝试改进这个过程,即试图在应用层把send和recv的语句这两个总是对应的过程封装打包起来,包括其概念消灭掉。而改用
&g_client.disptchevt(...)
&g_client.run_Lua_fun(...)&
&g_client.run_AS3_fun(...)
&g_server.disptchevt(...)
&g_server.run_Lua_fun(...)&
&g_server.run_C_fun(...)&
&这样的方式。
&也就是说,会有一个通用的消息层处理。 两边直接面向应用处理, 而不再考虑send和recv这些细节,更不再考虑这些发送和接收后怎么映射到函数的细节。
在封装send和recv的打包过程接口里, 不应起名runXXX. 而应起名为startXXX。 因为用run表明要执行完的, 用start则没有这种意思。
实践证明“特参转发器”相当好用。 现在的不足是, 原系统之前是把一些参绑在消息里
的。 如果提到BUF参里, 就更方便了。
&特参转发器&带来了一个很方便的用途, 就是检索时,再也不必一定是要有序的了, 例
如说,一定得先检索第几波的信号,再找相应的怪ID, 而是想听那个就选那个。 因为触发
消息的层次再也不绑定某个运行期的东西了。
或许一个更好的名称是“特征触发器”
解决FB还是运行老代码的方法:
方法1, 把Internet临时文件和历史记录设置设为检查所存网页的较新版本为每次访问网页时。
方法2, 把查看文件的目录里的东西全删掉。
对手的武器太过华丽了,你像孔雀一样华丽地开屏一下,用户就来,关屏了他们又走。而我们是一点点地帮你倒杯水,帮你沏杯茶,帮你点根烟,没什么牛逼,但是你慢慢会发现这样留下用户的比例非常高。
我们有一句话,任何领域未经优化前,都有百分之三十的效益上升空间,这是我们做产品的原则。比如说,做首页大图,如果我们认真地去想怎么做,百分之三十的提升空间一定有。
人人都能看到的潮流,对于大部分人都是陷阱,因为这时候只有一个人能活。
由于目前暂定分工是客户只负责表现的功能, 服务器和客户端
的应用逻辑都是我在调。 这导致我要负责不少细节处理,其中在同步服务器和客户端
的参数编码和解析花的时间比较浪费。
于是我尝试改进这个过程,即试图在应用层把send和recv的语句这两个总是对应的过程封装打包起来,包括其概念消灭掉。而改用
&g_client.disptchevt(...)
&g_client.run_Lua_fun(...)&
&g_client.run_AS3_fun(...)
&g_server.disptchevt(...)
&g_server.run_Lua_fun(...)&
&g_server.run_C_fun(...)&
&这样的方式。
也就是说,会有一个通用的消息层处理。 两边直接面向应用处理, 而不再考虑send和recv这些细节,更不再考虑这些发送和接收后怎么映射到函数的细节。
在封装send和recv的打包过程接口里, 不应起名runXXX. 而应起名为startXXX。 因为用run表明要执行完的, 用start则没有这种意思。
有些需求如果拆成两套设计会更安全和灵活些。 例如副本离线奖励, 如果按这个字面需求做,就真的弄一个副本相关的离线奖励,这意味着可能要三个步骤。 首先,要通过标志1,通过一系列额外技巧,找到对应模块里的固定奖励集; 然后通过标志2,找到那些是随机生成的奖励集; 再然后要把这两者打包起来发出去。 这里有三个步骤,光测试就估计要半天是花在验证上了。
但是如果拆分开来, 副本奖励归副本奖励,离线奖励归离线奖励,则程序简单健壮很多。因为实现步骤缩减为:当发现离线时,直接查离线奖励表给固定奖励即可。 至于和副本的相关性,只要把相关的奖项值配成一样的即可。
不足:配置值要多配几次。
优点:各个模块独立,没有相关性,维护和扩展更方便。
很多人都跟我们一样,这就是婚姻。如果两个人老有激情,就累死了。两个人这么熟悉了,不小心摸到对方跟摸到自己似的,还能有激情?所以,我们的婚姻没有问题,是人们对婚姻的认识有问题,不成熟。现在你遇见这个人,他给了你新鲜感,你就觉得他好,我们俩不好。错!如果你跟他结婚过上几年,就会发现我俩的今天就是你们的明天,而且还不如。因为我们俩从一开始就没隔着心,有了儿子,一起买了房子置了家,你和我,挣来的每一分钱都给了咱们儿子、咱们的家。你和他呢?从一开始就是两家人,他有闺女你有儿子,谁都想多吃多占,永远不均。有激情时,糊里糊涂地过,激情没了,对方就成了外人,到那时候,你就后悔了!所以,你要是觉得跟他一起更好,你就去吧。”
老公一席话,她犹如星湖灌顶。不知为什么,当时她一把抱住他,紧紧地抱着,眼泪哗一下流出来。从那以后,妻子再也没说过离婚。婚姻其实本来就应该是平淡的,是一种合作关系,这种合作里面更多的是责任和义务,别奢求有太多的浪漫,奢求太多了,人会变得挑剔,一挑剔起来,婚姻就不能长久。成熟的面对婚姻。解决问题!
&太多的人经不起平淡的生活,脱离了生活的轨道,忽略了本有的责任。我想这应该是大多数人生活中都会遇到的问题吧,让我们学着珍惜眼前人吧。
&看 一辈子的爱人,不是一场轰轰烈烈的爱情,也不是什么承诺和誓言。而是当所有人都离弃你的时候,只有他在默默陪伴着你。当所有人都在赞赏你的时候,只有他牵着你的手,嘴角上扬,仿佛骄傲的说,我早知道。不要因爱人的沉默和不解风情而郁闷,因为时间会告诉你:越是平凡的陪伴 就越长久。&
奖励其实应有三个属性,即类型,ID, 数量。
感觉做得不错的地方:
解决方案:
在处理奖励时,会面临前提条件各种可能。例如加钱加经验的,是某属性值判断即可,而加物品的则要等全部加上并判断空间是否足够才能做出决定(因为用回滚的方式效果更差)。面对这种问题,最后选择采用访问者模式解决, 即在实现“缓冲背包”里的奖励行为里,遍历所有奖励项的各自否决判断,并把背包对象传递给其, 最后再判断背包自身的否决判断,所有都通过后,再一次性把奖励加上。
通过上述方案,在扩展新型奖励时变得简洁健壮很多。 例如增加宠物奖如下:
代码实现:
function TIG_PET( _ID, _num )
local public = TIG_BASE()
function public.vote( _bag )
return false
function public.action( _bag )
for i=1, _num do
API_GivePet(_bag.getRole(), _ID)
return public
奖励配置表
FIRST_PAY = { TIG_PET(1,10000) }
扩展如上的新功能完全不用改动框架层的代码。在外部新增一个新奖励功能类型,然后就可以直接配进配置表了。
个人觉得, 这个设计方案在后期实现一些VIP的特殊奖励时,将会很有用。
要有一个专门的通用的通讯模块,不是指socket的,而是指有一个专门封装各种打包数据的模块,或者说数据字典。 对,应该有一个共享的第三方:数据字典模块。
for key, value in pairs(tbtest) do &
for key, value in ipairs(tbtest) do &
for i=1, #(tbtest) do &
for i=1, table.maxn(tbtest) do &
意外发现灵活易扩张的奖励流程相当有用,因为奖励经常是在一些关键环节时给矛的,而这和需求的关键点恰好重叠。 所以当奖励流程灵活化好,像扩张一些达到关键点开启某模块,完成任务飞到某地点之类的需求,都可以通过奖励实现了。
通过查询来实现各模块的灵活组合还是可行的(这样不用定义一个静态类), 如果有性能问题,可以把查询到的对象记录起来并封锁其删除功能,以便后用。 这样成本仅仅在于第一次查询时的开销。  
不同模块之间的大量互动,宜采用消息机。 因为这样定义一个消息后, 互动双方就可分头独立实现功能,且是弱耦合的。 若是采用函数调用, 则不能独立进行某个模块的开发(因为调用空函数编译器是通不过的),且会导致模块之间强耦合的问题
两个原因造成我在指令功能的实现上采用消息机的方案而不是函数调用的方案:1,我不希望新增一个功能点要先两个模块的互动调通后才可进行功能点的实现; 2,我对指令功能的需求点不是特别明确,也就是说,我认为他的扩展可能很多,用函数的话挂载在那个实体都感觉不适合(除非是当作系统API来定义)。
客观的说, XX在工作上还是很认真负责的, 这点是没有问题。 只是在他那个位置上, 人际关系没有做好,这个是大问题滴。 &好坏我都是客观的分析的,我不是那种会因主观问题去否决客观事情的人滴。
而且坑,哥跳多啦~~~ &不在乎这么一两个。
像上周六那个, 我还能怎么说呢。 我根本就不知道那个walk里面在做啥的, 从它的设计意图上看(它封装了好多操作在里面,例如对话, 寻路, 打怪), 所以它就是想做一个中间者, 一个智能的封装。 &按它的设计意图,我应该是不用管他的细节的。&
再者我根本就不清楚里面做什么的。 &但是…… 呃, 一上来就是我这边做错了…… 然后哥就被拖进坑里, 被轮欧…… &X。 & YY说得还沟通得来,能说明为什么那个设计意图不行, 另一个则是完全在旁吹水, 都没进入到框架层说问题,就是依据几个细节反复的折腾问题, 其实那些细节问题真要解,我不信没有办法解(只是那个说多了就是在硬扯了)…… &不过对于有些讨论不能跳出细节,在框架层谈问题, 我觉得议论得有些没有多大意义了,都不是同个层次水平的……
这就是我那天事后对那次讨论的意义的真实想法, 我觉得有些人想问题, 还不够抽象, 聊起来不是一个层次的。&
但这不是说我的水平高, 各有各的长处啦。
区分组件, 插件, 部件, 还是很有好处的, 特别是在生命周期不同的环境时。 例如一开服就有的服务, 多用组件实现。 副本开启时才要确认的功能, 多用插件。 与人相关的长期绑定的东西,为部件。
加班加点拼着命去赶上面要求的进度, 结果在事情过看,回头看来,这在上面看来不是功劳,而是苦劳, 而且是没有意义的苦劳,在发展空间上也只有一票否决的考量,没有优先考虑的考量。 没意思。 或许不是做得最好的, 但确实是当时能做的人里面最适合做也做得最好的的, 人们总是健忘的,要你赶工时很客气, 赶完后就无所谓了。
面对上司的判断,认为你没错,你缺乏认识问题的能力;认为你错了,你没有解决问题的能力--接受错误的最好方式就是对错误避而不谈。最后一条,不准和老板谈公正。不认, 觉悟不行;认了,能力不行。 避而不谈最好。利益之争如果面对面解决,它就变得无法解决;如果不面对面解决,它就不会被真正解决。一个最终原则是,利益之争从来就不会被解决。学会不谈判的技巧。必须学会摆谱。如果你很靠谱但不摆谱,大部分人都认为你不靠谱。如果你不靠谱但经常摆谱,所有人都认为你很靠谱。
全局变量也有全局变量的好处, 那就是不用涉及太多的参数传递处理。 但任其横插也不是办法, 一个改进的方法是, 对于那种写少读多的情况, 可以有一个全局管理器统一的管理全局变量。
圣火微章(简单和好玩法) + 太阁立志伟(界面和剧情好)。
离线 + 在线 结合的游戏机制。
四技能固定,但是可以通过转职切换。
这个功能在调试时还是很需要的, 从运行到登陆大概要2分钟, 一个小时光执行这样的步骤也就只能跑30次……, 这还不算一些额外的开销(例如用GM接指令什么的),实际真正有效的调试,一个小时也就跑了个12、13次, & 开发成本就是这样耗掉的……。所以动态的LUA调试还是很重要的。
XMLSpy2011是个好东西。
问:你写数据的时候用 WaitForSingleObject(wOverLaped.hEvent,INFINITE);
如果一直没有写成个那不是卡死了吗
答:就是要这样的效果。 因为问题要一步步来解决,当时如果那里写不进,后绪的需求都没法实现,致于异常的问题可以在后续需求中处理,因为这并不是不可控的问题。 就如下围棋时, 先在关键点抢攻布局, 搭好框架,只要事态在可控范围内,不必急于把有限的资源投入到巩固阶段。 否则效率就低了。 效率低就会落后, 落后就要挨打。
数据处理流程:1, 处理程序静态配置数据; 2, 处理需求静态配置数据; 3, 处理数据库动态数据;4, 处理程序运行的动态数据;5,保存数据库动态数据。
有时侯你不希望把虚函数留给终端结点于处理, 而是希望不管提供出去后外部怎么处理, 最后总是决定权会回到你手里, 这时你就可以用“模块继承”的方式把第三方处理再构造到你的内部函数里以实现虚函数的最终决定。(配后的类型统一问题可以通过基类指针解决)。
副本有一个严重的设计问题,就是当初弄的时侯, 没把插件做成一个生成对象独占资源的。 弄得现在老要处理公共函数和独立函数的问题。
其实我想在游戏里做一个酒吧女郎, 玩家通过送花,送钻戒什么的, 可以获得女郎的好感度, 当好感度达到一定程度时, 女郎会给出一些提示信息:例如到某地方探索可以获得什么武器, 或者给出一条开启某隐藏副本的路线来。
突然想到一个解决取参和处理的函数方法了,那就是配置表去配置“取参函数 + 处理函数”的组合函数。
在自动生成配置表时, 用批克隆接口有时胜于写批生成处理接口, 1, 简单化, 因为批生成会有各种参数的处理, 而克隆可能只需处理那个克隆后需要修改的东西。2,耦合化,当生成参数接口修改时, 指接口也要跟着修改, 而指克隆则不需要。
由于动态链接库生成的对象全都受限于动态链接库的资源生命周期。 所以一种最安全规范的做法是, 不是每次都去查找地址以处理函数, 而是直接内定动态链接库只对外提供唯一的工厂对象,且库资源和其生命周期等同, 然后函数由工厂对象去扩展。 &因为生命期一样,数目一样且唯一。这样就把对dll资源处理问题转化为统一的对工厂对象的处理问题。
对于很耗时的操作,倒是可以通过“功能CD”来实现不让玩家频繁操作。
特别小心: 当函数里有代表对象的ID存在时,要特别小心。因为一旦涉及ID操作,就有可能是干掉对象的外包操作, 从而导致原先代表对象的指针是一个野指针存在。
冯小刚:常遇热心人苦口婆心劝我治疗脸上的白癜风且免费献出祖传秘方,在此一并叩谢。这病在下就惠存了。不是不识好歹,皆因诸事顺遂,仅此小小报应、添堵远比身患重疾要了小命强。这是平衡。也让厌恶我的人有的放矢出口恶气。再者即便治愈,我也变不成吕布、黄晓明,顶多就一不用打底色的杜月笙。
/englishlistening/
我们有一个看上去根本不像经验的经验是:用上所有渠道,包括内部人推荐。某季度或半年推荐入职最多的同事,即使本职工作不算突出,也可谓功臣。人手不足、虚席以待的问题很突出,那就应该花一半以上的时间去搜寻或者研究如何招聘。
  其次,切忌以快为纲,融合是大问题。因为经验表明,观念很难改变,不是不能沟通,而是对某个问题的认识基于不同的事实和逻辑。因此,人员招募不能能力合格就“上车”,要把认同和价值观当作非常关键的标准,职位越高越是如此。如果业务发展形势要求需要短平快的招聘,那就要确保管理一定要跟得上。
人多了,就要拆分为小团队,需要明确各个小团队的具体使命和责任。CTO要给各个团队讲清楚未来6个月、1年甚至2年的使命和目标,未来成长之路是什么,团队负责人自己怎么发展,团队成员怎么发展,小团队怎么建设,绩效怎么考核,怎么奖励……总之,要确保扩张而不脱节。
关于服务器和客户端的技能交互是这样的:服务器负责在客户端的相关队列里安插关键帧, 然后客户端负责把关键帧队列还原成动画。 &例如技能,有些技能在释放瞬间就计算好了并安插到客户端帧队列里, 只是客户端表现上还在播放漫长的关键帧队列动画罢了。
服务器:逻辑关键帧制造机。
客户端:逻辑关键帧播放器。
利用lua的充许同名顶替功能,可以实现lua的热更新。 通过GM功能随时随地加载一个lua文件,然后同名顶替实现了任意数据,任意功能函数的更新。
氛围比过程重要,他深信培养音乐家,需要在襁褓时听曲子;培养运动员,需要在学步时开始蹦跳。
在遍历时不采用标记后续法删除,而采用替换当前对象为空对象,可以速度更快。
可以通过置空+双缓冲的方式,把消息遍历过程中的非空结点拷到备用缓冲上去, 然后把备用缓冲的地址给拷到前台缓冲的地址上,从而避免了遍历后非空结点的遍历删除过程。
从长期的时间来看, 副本比功能难搞。 因为功能一般是作为基础操作, 不管是从设计角度还是从需求角色,它都不会长时间的扩展和变化, 所以很容易定型。 &而副本做为一种玩法,则经常天天换,天天调整,天天再另搞一个玩法出来。 所以麻烦很多。
在一些没有debug和release版本区分的语言, 可以通过SVN的上传文件标志来实现对debug和release版本的区分。
塔攻搞成拉据战还是没有快感和太慢, 不适合手游。 还是要保持抢攻快跑的节奏才好玩。
可以通过lua的顶替功能实现lua程序的热更新(即不重启服务器的情况下更新服务器功能)。当然,这只是暂时的更新,关服后还是会还原的。 所以完整的更新步骤是整个lua文件夹都覆盖掉,包括其中的热更新包。这样不管是临时的,还是关服后的, 都实现了更新。
VS2010的自动化测试还是挺强大的。 它的操作原理是,提供对支持测试类型,进行其所有相关的行为录制,并可随时中断添加对当前现象的判断逻辑。 每个录制步骤和其对应的判断逻辑就是一个测试用例。 &使用自动化测试时,要注意初始条件要一致,否则会导致测试中断。
它的生成测试用例步骤:
1, 录制目标对象的测试操作,并通过中断录制及时生成相关代码。(测试工具会自动把目标对象有关的行为转为“代码关键帧”)
2, 对当时的结果添加断言代码进行判断。
VS2010的自动化测试的录制过程一般会全部以代码的方式保存在XXXMap文件上,测试项目文件的代码只是调用XXXMap文件的方法罢了。
VS2010的自动化测试的原理是:先用一行行操作代码把所有记录起来。 然后把用户截取的“操作代码段”保存成一个测试步骤。再针对某个测试步骤调用完后进行相关的用户断言代码的判断。
其实概率按千分比百分比之类的, 是不合适的。 这会让经济模式固化而不适应市场需求发展。 合理的概率,应该由经济模块提供概率API,同时由经济模块来定价所有东西。例如充钱越多的人,幸运值越高。
其实有不少游戏逻辑的东西可以放到客户端做。 例如自动开始和自动结束。可以由客户端发起,服务器没必要开个倒计时去算时间然后把玩家T出副本。 因为当副本打完后,如果外挂要搞得玩家在副本,那就让他搞喽, 没什么大问题的。&
发现一个监控LUA内存漏露的好方法: 由于LUA的内存回收方式有一些自身的优化, 常导致不准确的信息。 所以直接监控内存量是不可靠的。 但是好在LUA的全局变量是有引用的, 所以通过穷举和追踪LUA全局变量对象的数量, 可以达到监控LUA的创建和解放是否正常的效果。
把客户端的入口绑限在唯一入口处是有好处的。外挂是可以把一个游戏,包括其运营平台都搞崩。唯一入口有利于检验,在有些情况下,这甚至是唯一的救命稻草。
客户端发向服务器的数据越少越好, 除了性能问题外, 最大的问题在于可以减少服务器对客户参数的依赖,以防外挂。这是原则性问题。
可以把查找泄漏的部分写入到关机逻辑里面,每次关机的时候自动查找泄漏,然后出具报告。
关服其实可以做一个汇总数据的功能
随着公司游戏上线, 挺有意思的。假装玩家和黑手交流, 看着他们的视频攻击自已的模块。 结果固是有些被攻破了, 但挺长见识的, 特别是穷举参数法, 一旦应用逻辑不逻辑,或视野太过切近应用, 很容易被穷举突破。
Lua的内存漏露是有可能多数是空表问题引起来。 解决方法是建立一个空表回收机制。
Lua的loadstring函数是一个极可怕的函数,甚至可用于注入式攻击, 使用时需要特别注意。
工具的复杂之处在对于系统接口的管理,逻辑的复杂之处在于对各种资源和需求的管理。 各有各的难处。
各类“特定消息码”在消息机里找到监听器的概率总和 其实就相当于 哈夫曼带权路径总合WPL。&故可以用哈夫曼树实现。 &而特定消息码的找到监听器的概率,就相当于消息码的出现概率。所以其概率设计公式 = Pmap^x &* &Pvip^y &* Prole^z * Puid^k... &( x ={0,1}, y =&{0,1}, z ={0,1}, k ={0,1} )
Puid = 5%--各类UID成为消息码在消息堆里找到监听器的概率
Pvip = 10%--VIP消息码在消息堆里找到监听器的概率
Ptm = 10%--时间消息码在消息堆里找到监听器的概率
Psvr = 15%--服务器消息码在消息堆里找到监听器的概率
Prole = 18%--玩家对象消息码在消息堆里找到监听器的概率
Pmap = 22%--地图消息码在消息堆里找到监听器的概率
Proom = 40%--房间消息码在消息堆里找到监听器的概率
则一个特定消息码的找到监听器的概率为:
Mk = ( Proom^a ) * ( Pmap^b ) * ( Pvip^c ) * ( Prole^d ) * ( Puid^e ) * (&
Psvr^f ) * ( Ptm^g )
--a = [0,1], b = [0,1], c = [0,1], d = [0,1], e = [0,1], f = [0,1], g = [0,1]
设MK为消息搜索树的里叶子结点,搜索路径结点数为Pathk
则所有特定消息码在消息堆里找到监听器的成本
WPL = ( MK1*Path1 ) + ( MK2*Path2 ) + ( MK3*Path3 ) + ... ( MKn*Pathn )
构建其最低本目前所知的技术为哈夫曼树。
以上文数据为例(不考虑复合消息码的情况下)
顺序依次分类
WPL1 = 5*1 + 10*2 + 10*3 + 15*4 + 18*5 + 22*6 + 40*7 = 617
采用哈夫曼树后
WPL2 = 40*1 + 22*2 + 18*3 + 15*4 + 10*5 + 10*6 + 5*7 = 283
性能提高值
TIME = 617/283 = 2.18
别小看那些文绉绉的文字性描述定义。 你现在觉得它不重要,只是因为你还局限于技术细节, 受制于语法或经验。 当你项目经验增多,无忌于细节时, 如何选择最优的解决方案就成了你的首要矛盾了, 这时这些文绉绉的心法口决,就会成为照亮你解决方向的一盏明灯。
鬼鸟消息机(是的,暂定这个名字),我希望做到以下三步:
1, 静态鬼鸟消息机。 能在设定范围内高效响应各类需求,挖尽所有可用的战场信息并加以利用。
2, 动态鬼鸟消息机。 能不断统计历史数据,调整自身结构,以最佳的姿态适应当场游戏需求压力并做出最佳的爆发反击。
3, 预判型鬼鸟消息机。 能通过当前战场信息导数或各种提示,以最快的反应调整估值函数并预先调整自身结构,处理接下来的消息。
我的鬼鸟消息机, 一骑绝尘去吧!
我的战绩: 第一次在服务器运用上热更新技术。
鬼鸟消息机的消息码将不再会是单一消息码, 而是标签码+参数类型码的组合。标签码用于快速索引,可能会用动态生成的方式。参数类型码用于指定消息的参数类型。
鬼鸟消息机的核心技术是标签码生成算法和消息堆结构算法。
-- 查找算法 &O( log2N ) &VS &O( log2(N-M) )
-- 如果不分层的话, 就会因基数N过于庞大,导至性能相差 lnN / ln(N-M) 倍, 即 log(N-M)(N)倍。
-- 函数图像可得,在N规模过小的情况, N-M的差值可以引起几倍的偏差。
鬼鸟消息机的实现,需要派发结构是层次派发和层次监听。
实现这种结构可以通过分析标签码生成中转消息监听器。 生成算法可依概率进行优化。
客户端的消息绑定到人身上后性能提升, 并不是因为绑定的问题, 而是因为没绑定消息的问题
。 前者其实也有做人物的ID比较, 但是在一个比较N遍。 后者把这个比较通过消息提炼出来了
虽然从我内心里看, 有时会觉得上面强制禁止我在副本里采用事务派发的方式是一种短视和急功近利的行为。 但是细看大富翁的代码, 倒是确实发现在一些地方上框架做得比我好。
假设现在完成了一个模块,后面的需求需要不断扩展这个模块的一些关键环节的功能(例如怪活怪死)。
大富翁和我的设计都考虑到了这点的灵活性问题了,但我是通过在模块的执行的关键帧上进行关键帧消息派发, 从而实现对模块的关键帧扩展的。 &而大富翁的需求和我的一样, 不过它使用了一种“回调对象中心”的设计。通过回调中心去封装扩展关键帧的回调函数渠道,从而实现了性能的提高。毕竟这种关键帧的功能扩展并不是时时有用的,所以无效的关键帧消息常常导致O(log2N)的消息派发检索量。 而采用“回调对象中心”则可达到O (1)的检索。
这里还不是直接使用回调函数, 而是回调函数中心。 这样当真的需要扩展回调函数时, 接口层还不用动。 美中不足的就是,这样的回调从代码全局上看, 没有事务派发来得汇总一些……
回调其实就是一种简化了的访问者模式。回调函数其实就是访问者,其参数就是类的内部元素。通过回调使在不改变类的前提下定义了作用于类内部中各元素的新操作。
对于模式(方法论)而言, 抽象才是其最精确的语言。
框架是一种可见的具体的结构,不是方法论,不是模式。&
初始化和反初始化还是很需要的。 虽然面对直接开启的需求时, 初始化没什么关系, 但是当面自临互相引用的情况时, 初始和反初始化就是一种前向声明, 可以解决需求。
用sizeof时要特别注意,当参数是字符数组时,会丢失类型(因为传参时会退化成指针).
游戏进度控制可以通过“封装进度信息结点 + 访问者模式”实现。
副本只管理副本的资源, 对玩家的外部控制,例如退出,由外部模块去控制。 别一移到副本,就把对玩家的管理权也移过来了。 没有那个必要, 也不能那样做。 副本只管副本,不转移对玩家控制的
不用递归或者调用层次强制在一两层是有道理的。 虽不够灵活,但可以避免互相调用的问题。例如进度控制器的调用, 例如回收器的回收不成的处理。
有些命名必须规范。例如成对出现的, 必须用init和uninit做前缀。 这样别人一用就知道这东西一定是要成对的。这是血的教训出来的经验。
初始化和反初始化还是很需要的。 虽然面对直接开启的需求时, 初始化没什么关系, 但是当面自临互相引用的情况时, 初始和反初始化就是一种前向声明, 可以解决需求。
用sizeof时要特别注意,当参数是字符数组时,会丢失类型(因为传参时会退化成指针).
游戏进度控制可以通过“封装进度信息结点 + 访问者模式”实现。
副本只管理副本的资源, 对玩家的外部控制,例如退出,由外部模块去控制。 别一移到副本,就把对玩家的管理权也移过来了。 没有那个必要, 也不能那样做。 副本只管副本,不转移对玩家控制的
不用递归或者调用层次强制在一两层是有道理的。 虽不够灵活,但可以避免互相调用的问题。例如进度控制器的调用, 例如回收器的回收不成的处理。
有些命名必须规范。例如成对出现的, 必须用init和uninit做前缀。 这样别人一用就知道这东西一定是要成对的。这是血的教训出来的经验。
内存的生死管理,数据库的创建删除,金钱的管理,只能由框架层去做。枝节应只充许做功能性的操作(非有创建的需求也应是在框架层的回调函数上操作)。 这样才可以避免各种内存问题, 外挂刷钱问题。而框架做这些管理时, 必须一定要挂载在某个实体对象上, 这样假如有万一,也有一份因对象消失而出现的删除操作。
一回开发可以以互动为主要目标,其次才是养成。 & 互动的切入点可以是:PK,技能互动(例如互相的挑逗,偷窃, 助人)
好的游戏就应该通过细致的设计,让尽量多的人找到奋斗的目标,找到成就感与归属感。
有的玩家热衷权势,
有的玩家热衷PK,
有的玩家热衷BOSS,
有的玩家热衷升级,
有的玩家热衷刷本,
有的玩家热衷刷怪,
有的玩家热衷经商,
有的玩家热衷时装,
有的玩家热衷打造装备,
有的玩家热衷培养宠物,
还有的玩家只喜欢拉风的坐骑,
第一天跟我说副本移到C++层做, 第二天跟我说先做任务的帮派需求,第三天跟我说先撸天龙八部副本,第四天说过0点刷新那个卡,先解决,第四天晚上说外网有个BUG先解, 第五天说一周过去了你怎么还没把副本移植上去?
装饰模式举例,例如接口提供了一个指针队列, 可以对这个队列进行打包扩展。 但是这样可能使复杂度提高,或使上下文的环境接口连接不上。 这时就可另外提供一个容器进行相关绑定。&
服务器应限制递归或互相派发, 如果真的非要有这样而且独殊的需求, 可以在递归层里用定时器跳开回绕。
传送应该有一个全局的定时传送器。这样可以避免手快的问题,可以避免进地图触发各种响应而又地图传送的回绕问题。
有些自觉万无一失的问题,多考虑一下这种情况在递归的环境下是否正常。 例如全局的初始和清空在递归时被先序遍历了两次清空了。
对于非提供给应用程序使用,而是内部封装了数据和生成的接口, 宜采用Build来命名,而非Create. 以示区分。
回调函数宜只能尾回调。
可采用状态模式, 对于一些粒度多变的情况的解决方案: 当状态集进行切换时, 发现状态项有变化的才进行构造或析构。
char Error[1024];
_snprintf_s( Error, sizeof(Error) - 1, &房间类型 %d 的未实现!&, &room_type );
这个最安全
好的设计不是“铁板型饱合火力覆盖”,而是“流弹型饱合火力覆盖”。
当传指针为this时, 用delete会出错, 用this-&release就不会……
终于找到原因了。 注意基类如果不是虚析构,那怕实际生成的对象是虚析构的, 但是删除基类指针也不会走虚析构的流程的。
是不同的。
delete this的删除方式是随着delete this的使用语境而决定虚函数的目标。
而delete p的删除方式是随着p的定义而决定虚函数的目标(假如p是一个基类而且没有虚析构则内存泄露了)
一个是“按使用语境”分,另一个是“按对象类型”分。
采用“状态模式+“状态集”的规则管理器模式”后, 副本变得相当简洁可控和安全。 另外, 删除副本时,可采用一个全局单例的清道夫模式来提交申请由它定时删除,从而避免删除时的回绕问题。
偶然翻到很久前的一部书《英语长难句结构分析》
偶然发现当副本地图和野外地图搞成同一张时,再走此副本流程,那个游戏代入感还不错。
Object OBJ_ID =
注意:虚析构和虚函数不一样, 虚析构是不会顶替的, 所以把释放放在虚析构里调用, 在继承类多时, 可能导致反复调用某个释放的行为。
析构原是指释放本类的资源,这本没有问题。 但是当类是管理资源类时, 析构就是释放另一块的资源了, 而管理类的虚析构就可以导致多次释放的问题。
除非是私有资源, 否则不要在析构函数里写操作。 不然会导致在继承体系里的无法避免的混乱和矛盾。
最好析构就是只管资源, 不管管理。 因为管理的东西可以是使用者去用, 如果析构里再潜规则用, 就容易出现矛盾。
每个副本的实现平均不过170行。 所以理解和使用的门槛其实不大的。
//空闲阶段
m_room_game_rule.RefState( PROGRESS_TYPE::FREE,
&m_room_life_time );
m_room_game_rule.RefState( PROGRESS_TYPE::FREE,
&m_room_watch_time );
m_room_game_rule.RefState( PROGRESS_TYPE::FREE,
&m_room_calculate );
//传送阶段
m_room_game_rule.RefState( PROGRESS_TYPE::MOVE_PLAYER,&m_room_life_time );
m_room_game_rule.RefState( PROGRESS_TYPE::MOVE_PLAYER,&m_room_watch_time );
//刷怪阶段
m_room_game_rule.RefState( PROGRESS_TYPE::PLAY_MONSTERS,&m_room_info_wnd );
以上指定了副本里各种状态的活动对象,m_room_game_rule则是这个副本的状态管理器,它会自动实现每个状态的过渡问题的。过渡里其实包括:需要活动的对象的开启,不需要活动的对象的关启,无用对象的管理。
原则上说:状态管理器是不充许直接接触到具体的某一句代码:例如开闭某个定时器的语句之类的。
状态管理器只负责: 
状态对象,状态对象,状态对象……
供应商只负责: 
提供状态对象(各种实现相关对象的语句),提供对象(各种实现相关对象的语句)
通过上述设计:副本的实现不用每次都从头写到尾,而且修改时还要考虑各种处理问题和异常问题。能住精力聚集到副本的游戏逻辑上,而不用分心于接口的使用逻辑。
在后面的开发过程中, 事实上接手的人多只需考虑一个初始化函数:怎么分清副本的各个阶段,并标上各个阶段的对象即可。 其它的几乎不用改动。 如果真有新需求, 也不过是供应商提供一个新的对象罢了。
向void*保存类型时,一定要指明类型。 当多继承时,会有丢失数据的危险的。
其实任务的更新需求让步下就好了, 不用为了保真,搞得要保存N多更新任务的问题,例如任务的版本,或者有一个任务刷新表。把需求让步下即可。 把完成任务时的奖励抽出来, 如果发现老任务找不到, 直接获得奖励接下条即可。玩家做着任务却因为厂商的问题平白无故丢失进度要重做, 代价他们来付其实不合理。
要做的仅仅是数据库的BUF段到缓存区的解析,解析算法为:牺牲四个BYTE用于保存解析参数。 第一个是保存有否解析过。第二个是保存只有一个BYTE的区域起点,第三个是保存有两个BYTE的区域起点,第四个是保存有四个BYTE的区域起点。然后每次读的时侯,就依据区域区别,按不同的空间读出每个key的数值。 保存时,则按大小压缩数据保存,并保存数据压缩后每个区域的大小。
在面临任务项的创建需求时, 不应设计为是直接用户点击创建的, 还是加载数据库数据创建的,这样会导致多个创建入口,从而使程序复杂。 而应划分为只有一种创建方式(用户点击创建), 只是加载数据库数据后,可以让任务直接跳到某个状态。 至于是那个状态, 数据库怎么和任务建造器交互, 则可以采用备忘录模式实现。
提供工具在某种程度上其实反而简单, 反倒是应用层很困难, 因为实现游戏逻辑和防外挂都是应用层搞的,而不是工具去处理的。
做服务器最好每个消息都让客户端发1万次,以便查性能和BUG, 不然一遇洪水攻击就挂了。
任务其实挺好锻炼设计能力的,因为它是处理中心, 其它人只是提供接口(提供工具), 而任务要把这些工具用起来做各种实现,还要防外挂,还要能支持不破坏的情况下的修改和发展。  但是也很烦这个, 做这个处理中心这么麻烦的事,都没报太多需的问题,倒是搞工具的,总是搞出一些次品, 然后每次一有事,就让处理中心的去找问题。 搞得好像处理中心的代码写得很烂似的……
一个大型复杂的故事副本设计应是如下这样:公共逻辑+个体逻辑。 公共逻辑管理所有公共资源的管理和公共资源的AI,例如针对所有人都有的怪,针对所有人都有的天气,针对所有人都有的地图。 个体逻辑管理个体资源的AI,例如个体的寻路,个体的任务发展。
以一个大型的交互押镖为例:公共逻辑负责创建镖车个体和所有相关情节个体,然后负责检控每个阶段的转化条件。然后各个个体都有自已的整条任务线逻辑去各种解决押镖的细节问题。
用回调是适用于生命周期强耦合且单一的关系, 而用消息是适用于生命周期弱耦合且群体的关系。 用回调逻辑更清晰,可查控。 用消息更独立,但查控性较弱。
有些需求的参数可能是动态需要的,解决这个问题的方法是,不在init里面直接开启。 而是init只负责加载数据。另有一个start专用于开启。
数据库怎么和任务建造器交互,从而产生任务呢? 一个较好的方法是采用备忘录模式 + 生成任务回调器。从数据库里加载入一个个指定的备忘录对象, 然后把备忘录对象和任务容器通过回调函数传送给使用方,由使用方去生成。 这样在加载过程里就显得很简单明了了。 只有一个for循环和回调函数。 而不用处理具体的生成任务代码,从而减轻复杂度。
例如以下片段:
start_key = start_key + 1
--帮会任务项数据保存并载入数据
for i=1, len do
 local task_memento = ConsortiaTaskMemento()
 local buf_size = task_memento.Read(_role_uid, start_key)
 BuildConsortiaTaskByMemento( mt_task, _role_uid, task_memento )
 start_key = start_key + buf_size
不要把加载和生成放在一起实现,因为加载失败有很多种可能,这会导致生成对象的实现与具体的环境结合得过深。而事实上生成对象的代码可能是很纯碎简单的,是加载代码复杂罢了。
所以宜:加载是加载, 生成是生成, 两者分开来,再通过回调函数动态组合一起。如果有一些强关联的成分,再把这些强关联成分提取出来做为插件,例如加载数据转入到生成对象里,可以通过备忘录插件来实现这过程的媒介。
COM模式没有中介者,成本简单,但仅适用于单向的,双向的话会导致指针互相引用,从而让模块强耦合,关系复杂,实现简单。而MSG模式有一个中介者,适用于双向的,交互双方都可只管理各自的生命周期,但是也因为有了一个中介者,所以性能成本更高,模块弱耦合,关系简单,实现复杂。
所以在房间时,因为各房间组件不会有互相关联的问题存在,所以可以用com模式。 但是在任务时,因为各任务插件经常有要互相关联的问题(例如需要各个插件都一致通过才能所有都通过), 所以任务插件是以msg模式实现的。
COM里如果不另定义Release纯虚操作,就需要定义虚析构函数(析构函数不能是纯虚的),而一旦不是非纯虚函数,就必须把实现内容发布到客户端的代码,这就导致了迭代版本时的问题。所以才会看到COM的东西都不用析构进行解析,而要另定一个。&
关键原因就是析构不能是纯虚的,而厂商又不希望把实现内容暴露到客户端以致产生日后可能版本矛盾的问题。
LOL常用意识:以场景为公式,代入各方角色参,预估结果。
服务器可以有一个有序项执行器。各方都压栈有序项。 然后执行器在合适的时间自动执行。注意清空问题。
算法虽然重要, 但可以想象在项目实施过程所遇到的各种各样问题中,算法问题绝大多数时候不会是主要瓶颈,没有到那种需要每个人都是算法高手的情况。实际上,绝大多数项目 真正难点并不是一两个算法瓶颈,甚至也不是单点的技术瓶颈,而是系统性的组织、协调、设计、开发问题,有大量的看起来不是那么有技术含量的脏活累活,也有 许多问题是由于信息不足,并不是技术能力强就能克服这些困难。一个团队最好优势互补,有人算法强,有人业务分析能力强,有人擅长后端服务,有人擅长前端界 面,有人聪明,有人踏实,这是最好的。如果按照“算法好”的单一标准选材,必定会把许多优秀的人才拒之门外。
础知识不重要,和古人说的“不积洼步无以至千里”是不是矛盾呢?不矛盾!“洼步”与“千里”是一种可累加关系,但再多的“基础知识”都累加不 成“综合能力”。学习软件开发要像持续集成一样,一开始就是一个完整的系统,虽然规模不大,问题很多,但它麻雀虽小五脏俱全,从小系统到大系统,从简单系 统到复杂系统逐步演化。
所以,基础好本身不足以说明太多的问题,必须进一步考察综合能力。对于基础面试表现不好的面试者,如果时间允许也要进一步考察,有的面试者其实是有能力的,只是没有进行充分的准备。最理想的状态当然是基础和综合能力俱佳,若不能兼顾,应当综合能力优先。
性格
  现在,我来谈我认为最重要的因素:性格。这可能是许多初为面试官的朋友所难以想象的,怎么会是性格最重要呢?说 实话,当我意识到这一点时,我自己也很惊讶!说白了,还是 P(工作好|性格好)的概率最高啊。我的实际经验是,如果一个人的性格好,他能把工作做好的可能性是最高的,性格好远比基础好、算法好要靠谱。
  一个人如果技术上有缺陷,经验上有不足,但性格好,在团队中是很容易由其他人来补位的,他自己也很容易逐渐补起来;相反,如果一个人的性格不好,所 有的技术优势经验优势都发挥不出来,甚至还会起到负作用,而且性格缺点很难改变。我一直谈到实际工作所需要的是综合性的能力,这种综合能力的发挥中性格是 至关重要的。项目中不止会遇到技术问题,要涉及沟通、协调,不同的人不同的部门既有合作又有磨擦,如何处理这些事情都需要一个良好的性格。可以说,在开发 团队里让你与众不同的不是你从哪个学校毕业,也不是你过去的经验,而是你的性格。
  当然,性格是一个复杂的东西,它包含了很多的方面,并非所有方面都是程序员面试所需要关注的。我的经验是可以重点考察这些方面:
  1) 态度积极还是消极。有的面试者在谈吐中就会自然给你一种积极上进的感觉,或者你可以在他的经历中发现他积极的因素,这些都不是太难看出来的。相反,有的面 试者你能明显感觉到他的消极情绪。积极性在工作中是十分重要的,积极的人能给团队带来朝气,也更易于合作。基本上,如果确定面试者属于态度积极的,他通过 我这一关的可能性就会大大增加;相反,如果确定属于态度消极的,即使技术能力不错我也会十分谨慎。
  2) IQ。我的经验是,总体来看,聪明的人在工作中的表现更为优秀。在面试中要考察一个人是否聪明并不一定要像Google和MS那样找些专门测试IQ的智力 题,其实,你只需要看他讨论问题是不是很有逻辑性,思考和说话是不是反应敏捷就可以做出大致的判断。另外,眼睛是人心灵的窗户,一个人聪明与否,眼睛是会 说话的。不过,聪明也不完全是优点,比如,当公司或项目遇到困难时,往往是聪明人先跑掉了,坚守的往往是IQ一般的人。
  3) 语言表达能力。语言表达能力也是程序员十分重要的一项素质,它关系到项目中的沟通是否顺畅。面试官可以看看面试者能否用简明的语言介绍清楚曾经做过的项目,能否抓住要点,能否考虑到听者的相关背景。一般来讲,语言表达能力强的人综合能力都不会太差。
  4) 是否具有用户意识。有人说程序员是做研发的,哪来什么用户?只有销售、市场人员才会和用户打交道。其实,这是完完全全的错误认识。你写一个模块,甚至一个 API,只要有别人用,他就是你的用户。有的程序员设计一个模块或是一个软件总是习惯于从使用者的角度来考虑,尽量地方便使用者,这就是一种良好的用户意 识。具有良好的用户意识的人更能考虑别人的感受和整体的需要,而不是单纯地从自己和局部来思考问题。当面试者谈及过去的项目经验时,面试官可以常常站在用 户的角度对其进行提问,从这个过程中观察其是否具有良好的用户意识。
  5) 如何应对质疑和压力。面试官应该对面试者的回答以及以往项目进行合理的质疑,看看他如何应对。曾经有一位面试者谈到做游戏登录服务器的经历,我就问:“如 果登录服务器挂了,怎么办呢”?他说原先虽然没有考虑这个问题,但是可以怎么怎么改进。其实,大家都理解项目中有各种不完美,这里面原因很多,只要面对质 疑和压力能从容应对努力往好的方向思考解决就可以了,不需要掩饰缺陷,更不应该有情绪。我遇到过有的面试者,一旦你对其项目提出质疑,他马上产生反抗情 绪,或不高兴,或不承认有问题,这很容易一下子看出来他在工作中容不得质疑和批评,这种人要想合作就很困难。
  6) 个性特点。许多面试者喜欢在简历上写“精通C++/Linux“,这些字眼看得人麻木,如果有人写”喜欢C++/Linux“,我就会有一种眼前一亮的感 觉。“精通”是没有感情色彩的叙述,而“喜欢”包含了面试者的个性,我更愿意看到面试者的个性。我相信对某样东西真正的热情远比你当前对它的掌握程度更为 重要。其实,N年的经历告诉我们,同一个班的同学,同一个项目组的同事,虽然每天所学的知识,所接触的工作都是相同的,但其实每个人的成绩和表现差异是十 分明显的。那么,到底本质的差异是什么呢?其实,就是每个人的个性。是个性使得有的人业余时间去打球,有的人业余时间去看书,有的人喜欢Linux,有的
人喜欢Mac。一个人在团队中扮演的角色也和他的个性有很大的关系。面试官应该引导面试者展现自己的个性,并判断其是否有益于团队。
  最后总结起来,我的经验是: 1) 面试官的目标是找到”工作好“的人,一定要围绕这个目标来进行面试,如果把面试当成了算法或操作系统期末考试这就走入了误区;2) 面试过程是通过学历、性格、基础、经验、算法等可以测试的因素去综合判断面试者“工作好”的概率;3) 在各种因素中,性格 & 经验 & 基础 & 算法。性格是最重要的,如果性格不好,所有技术能力都会大打折扣,而且技术缺陷容易弥补,性格缺陷很难改变;经验体现了一个人的综合能力,你可以从面试者 过去的经历中判断他能从事哪种工作,不能从事哪种工作;基础和算法则主要起到辅助参考的作用,基础好的程序员一般适应性比较强,学新技术更快,但是切忌单
纯从基础来判断一个人的能力。
古代的骑士,在成为骑士之前,都是先当其它骑士的助手的, 为他人牵马喂草,洗衣做饭, 在这个过程中即锻炼了心理素质,也体验了一个骑士的方方面面。 最后才能顺利转正。
1, 搭建一个桥梁最少需要多少积木?
2, 怎么搭建桥梁才能使桥梁的结构最稳定?
3, 如何让桥梁美观对称?
4, 这个桥梁如何搭建才便于扩展,例如说,我现在想在桥梁弄个护拦,但是护拦得有勾啊,不然空放上去, 风一吹, 护拦就从桥上掉下了。这个勾要怎么安排, 才能又有,又不影响美观。
5, &又在举例, 不是每一次搭积木都要重头开始搭的, 有一些好的积木方案是可以保留起来以备复用的。 例如说房顶, 例如说窗口, 如果做好这些方案的归纳和记录, 以便快速在下一个需求里, 搭出相应的模型,这又是一个广大的问题。
_snprintf_s( m_name, sizeof(m_name) - 1, &%s&, _name ); 和 _snprintf_s( m_name, sizeof(m_name) - 1, _name ); 是不同的。 后者会把name当格式公式串来用,而这个_name可能是客户端决定的,导致格式公式串暴露,服务器随时有可能因为格式公式串的语法问题而崩溃。
技能其实不是技能, 它只是一个触发消息的抽象标志符。 真正的技能内容是由一系统状态组实现的。 技能配置表配的其实仅仅只是消息的状态流通路径和状态路径结点的参数罢了。
一个技能其实就是一个生命域不一样的副本。只不过副本是地图为绑定源, 而技能是技能生物为绑定源。
build模式最大的好处是把资源和管理分开(就像BOSS和CEO是不同的岗位一样),例如说一个象棋游戏的开局界面就可以用build模式创建。 build模式针对对象过程的时间段问题,而factory模式针对对象时刻点的问题,仅管两者相似,但倾重不同。
其实移动也可以是一种技能,没必要特殊处理。
腐败腐败,物必先腐而后败。 声色犬马固然不是失败的直接原因,但却是根本原因, 沉迷之后, 各种机会主义妄想主义就会盛行,人处其中而无法自知,最后挂倒。
其实服客可以建立起一种“批指令机”。 批指令机能实现“指令的批发和批解”。
服务器和客户端的问答是片断式的, 但有时侯需求的问答是阶段式的。 所以就有一个如何把片断归纳到某个阶段里的问题,通常会采用一个标志值,并配以以下四种方案:
1, 服务器牺牲空间以记录标志,并通过监听阶段开头和下线来逻辑推断出阶段的开始和结束(安全,但服务器的时空压力都增加)。
2, 服务器把标志做为流水号下发下去,通过客户端的转发来推断阶段的开始和结束(不安全,但稍有时间压力无空间压力)。
3, 监听客户端的阶段结束问题(不安全,重时间压力轻安间压力)。
4, 客户端直接答好通过发信息上来领奖(不安全,服务器最少压力)。
或许可以考虑一种玩法, 可以通过某种方式打断别人的成长过程。 例如说截穴道可以阻止玩家当天之能升级武功。 或者就像韦小宝那小说一样,有一种武功可以让对手每天0点到2点之间武功下降三成,持续一周。
先合03,再由03更新到策划那里去进行测试,倒也有好处:虽然可能有弄崩03的风险, 但是呢, 要是成了就成了, 不会用本地测试成了, 但是上传到03有文件没更新到的风险。
也就是说,策划和程序再也不会私下进行测试了, 而是以03为中介者进行数据同步和测试。 这样虽然修改和难度增加,但是确保了一旦OK就真的是OK。
工作上,有些作风是不对的。有些人叼起来都不按套路走呢, 各种夸张句,颠倒黑白句呢。到了那份上,其实意义已经不是在工作上,纯碎就是想找人来发泄一下, 和道理、真相已经没有关系了的。
QueryInterface不一定非要转this指针出去, 也可以当转发器,把各类需要指口转出去。
领导的意见我肯定听、肯定执行,但是你们要非要我说你们说得对,这个不行,你们不能否定我一个艺术家的基本判断。
华罗庚当年对“速算天才”事件的评论:“我总觉得多讲科学化比多讲神秘化好些,科学化的东西学得会,神秘化的东西学不会,故意神秘化就更不好了。有时传播神秘化的东西比传播科学更容易些。在科学落后的地方,一些简单的问题就能迷惑人。在科学进步的地方,一些较复杂的问题也能迷惑人。看看沙昆塔拉能在一个科学发达的国家引起轰动,就知道我们该多么警惕了,该多么珍视在实践中考验过的科学成果了,该多么慎重地对待一些未到实践中去过而夸夸其谈的科学能人了。
服务器元素
& 数据{ &地图数据, 角色数据, 交互数据 &}&
& 脚本{ &技能表 &}
玩家怎么控制是另一回事, 玩家和角色是没有关系的。 &游戏设定时, 不应把玩家和角色绑定在一起。 在其设计里, 游戏应只可见游戏中的角色数据,地图数据,交互数据。 游戏只控制这三种数据的变化。 至于从玩家过渡到角色, 是另一个问题。
游戏体系设计入门问题:
1, 有什么数据。
2, 有什么操作。
数据: 地图数据, 角色数据, 交互数据。
游戏应只可见游戏中的角色数据,地图数据,交互数据, 游戏只控制这三种数据的变化。
交互数据其实可以有N种。数据: 地图数据, 角色数据, 交互数据。
游戏体系其实可以从操作系统体系中获得参考。
写应用程序不同于写框架体系, 应用程度简单一些, 原因在于越应用化,其需求越明确狭窄。而越框架体系,需求越不明确,但是要求却一点都没改。
对了, 批处理可以通过添加和处理消息并列的头尾监听结点实现。这另需在 vote,action, response前后再添加两个处理环节标志。
做为系统软件层, 其内部各个模块都应是独立的, 应用层软件才需要考虑各个系统软件模块协调的问题。
有时侯, 不想对角色分太多类(涉及到管理的问题), 但是角色之间又确实有一些不同之处。 解决方法是看这些不同之处是不是设计的本质不同, 如果确实不同, 可以用虚函数CetClass来区别,&如果只是皮不同, 则可采用一个轻量级的变量来显示不同。 更对一步的做法是除了提供GetClass之外,还仿照COM设计提供一个QueryInterface功能。&
有时不得不采用生命标志来避开用定时器实现跳栈的功能。其中生命标志可以不是数据对象的一部份,它可以是一个数据对象获取接口的一个套子,和数据对象动态组合而更灵活。 因为设计数据对象时,它的第一层设计可能只为功能, 第二步优化才有生命周期的问题。
生命周期套子的数据可以不是通过继承合成到数据对象里, 而是两个不同的模块,生命周期套子是一个对象管理器。 这样设计有一个好处, 就是用户通过管理器接口取得了数据对象,那么它在当前函数的整个使用过程中就必然是活的,而如果取不到就必然是死的。 & 这样就防止了每用一次都要担心是不是存在递归,嵌套的问题导致其已经死了,而要不时判断一下其是否活着。
用定时器来跳出递归是个惨痛的回忆,因为后面发展会出现定时器执行时生命周期(例如执行时遇到目标已经下线)是否充许, 与其它操作协调时次序(例如删除操作遇到添加操作)是否充许等限制问题,还有响应问题,性能开销等。 还不如用一个生命标志去规避。
等等, 为什么要为一个不必要的需求去设置一个生命标志呢, 事实上,之所以搞生命标志, 仅仅因为对象删除自身时,容易在某些场合导致管理体系统的遍历出问题罢了。 这个问题的解决方案,操作系统已经给出了很完美的解答了,删除时把相关地址值标志为空,而不是一删除就进行碎片管理即可。 碎片管理可以是另一个应用模块去做的事。
根本没必要大动干戈劳民伤财的搞一个生命标志模块出来。
删除对象其实在那都可以写的(包括对象的自身功能里面),它只不过是计算机里的一段写数据指令罢了。 需要注意的仅仅是删后要确保不再误用这段数据。
生命模式可以是这样,他用三个函数:Start, Stop, IsRun. Start负责生成对象,并把对象投进管理队列, Stop负责把队列里的相关对象置换为null值, IsRun负责提供对象是否存活动信息。
任务项应该采用com方式,让任务项自已去负责其Release的操作。 故而,如果遇到任务非得当项更新, 也应由载入后其初始化结束后,再自已Release。 
我希望任务链表只是一个工具,而不是一个任务管理者。 前者可以复用, 而后者会导致每多一个任务模块,虽只是管理不同,但是任务链表还是要重写,因为链表已和管理者组合成任务管理者。
地图的动态变化倒是可以用NPC来实现, 例如火焰的点燃, 传送门,

我要回帖

更多关于 大富翁4看不到鼠标 的文章

 

随机推荐