别把语言当信仰

上次在公众号里发了条消息,有好多人问知乎上的争论是什么?其实没什么,只是在一个讨论unity里嵌入lua的话题里,有人说如果官方能提供原生的热更新解决方案,所有嵌入Lua的这些unity框架都得死,言下之意就是我C#各方面都碾压你Lua,我用你只不过是因为你可以暂时帮我解决热更新问题。

 

关于这个话题,并不想讨论孰是孰非。凭心而论,每种语言其实有它的适用环境,比如要快速做一个小工具,我首选会采用Python,因为三方库非常齐全。但如果对实时性有很高要求可能我会改用C/C++去实现核心模块,但如果写游戏的业务逻辑我会采用Lua,因为语言本身简洁,约束少,更灵活。

 

当然每个人对所谓的语言适用环境的理解都不一样,比如曾经的我就觉得C++可以搞定一切,也正应验了那句话,拿惯了锤子,看一切都是钉子。所以最开始我因为项目原因接触Lua的时候也只不过是将它当作了无须编译的C++来编写,还特意为了能让Lua实现一些C++的特性去写了一堆勉强算作中间层的代码,那时候我认为这就是跨语言的编程思想,语言并不能对我构成障碍。

 

但后来随着自己开始独立承担整个项目,我开始面临项目语言的选择这样一个问题的时候,我不禁开始反思,C++真的是最合适的语言么?并且在潜移默化间习惯了Lua的表这种方便的数据结构后再回头看C++,突然觉得在实现各种游戏逻辑业务的时候,C++的STL实在是有点刻板和笨重。在处理有些异步事务时,一个协程几行代码就能讲清楚的逻辑换做C++来写,既啰嗦冗长还不便于阅读。还有回调逻辑,由于匿名函数和闭包的缺失(那时候C++11还没出台)用使C++写个回调真是又累限制又多(参数限制)。这时候我开始认识到,哦,原来每个语言都是有自己的魅力的。

 

再到后来,我又陆续接触了Python,JS,Scheme,Haskell,C#,当然有些语言并不是为了解决什么问题,而是纯粹为了感受下这些语言给我们带来的不一样的思维。(比如Scheme和Haskell,强烈推荐使用Lua的朋友一定要去体验下Scheme这门语言,因为Lua很多地方都有借鉴它)

 

最后来说信仰。信仰这个东西它会令你非常有归属感,但是它也会给你加上一个无形的枷锁,让你排斥这个信仰之外的东西,看看一些极端穆斯林们对非穆斯林的表现,算是比较极端了。然而即便是穆斯林内部,逊尼派和什叶派也是互掐的一塌糊涂,基督教也是,天主教东正教互相开除对方教皇。

 

写代码本来是件简简单单的事情,为什么也要搞出个XXX是世界上最好的语言呢?寻找语言的归属感,其实我觉得还有一个词更合适——思维懒惰。也许有人要问,那为什么要做这个公众号?就像说明里说的,只是希望借此让更多人了解并喜欢上这门语言,也能够发现同样喜欢这门语言的人,仅此而已。并不是要宣传鼓吹Lua是宇宙语言。

 

Lua元表应用汇总

真的是好久好久没有写过东西了。= 。=

元表是Lua里头一个很强大的特性,有了它,可以自定义扩展出很多本身Lua并不原生支持的东西。所以其实一直都想做一个关于元表应用的汇总,趁着过年这点闲暇,暂且汇总下目前所了解的一些,也权当抛砖引玉吧。

重载算术运算符


这个没什么好说的,也算是元表的最初级运用了,具体应用见Lua手册中的介绍。

Cache表


这个也属于元表的基本运用,没什么好说的,通过弱引用来做一个Cache缓存。

只读表


通过实现__index和__index元方法,来实现对一个表的只读访问(这里的实现只针对一层表结构)

实现面向对象


这个之前有专门写过,参见

一个完整的Lua版OOP示例

简单使用Lua实现类的继承

共享表数据


有时候我们可能需要这样一个表参数传递,表里大部分数据是固定的,但是部分数据是动态创建的,如果每次都全部重新创建一遍就比较浪费,于是可以将固定数据做成一张表,然后每次创建新表使其可以共享这个固定数据表里的数据,新表内部只包含那些动态的数据。


以上的都是比较常见的一些应用,下面的这些也许就比较“冷门”了,至少我第一次接触的时候是觉得很“黑科技”的。

带功能的函数


这个主要运用了__call函数,用一个表来扩展原有函数,从而来实现带记忆(对运行过的输入参数保存结果,下次同样的参数传递进来可直接返回结果,达到优化效率的目的,这里也应用了上述的Cache表)。且此表可直接对原有函数进行替换,并可附加清除记忆功能。

为函数添加标签


这里也是通过__call将函数转换为可执行表,然后在其元表中加入一个标签来为此函数添加标签,同时新生成的函数可以与原函数完美替换。

统计函数运行信息


这个跟上面的类似,还是利用__call将函数转换为可执行表,向其元表中添加统计记录,并在函数运行时统计信息,最后再通过GetStatInfo来查看此函数的运行信息,这样在我们需要对某些函数做性能统计时,就不用修改原函数或者全盘查找替换了。

Lua装饰器


其实上面几个应用在其他语言里(比如Python)都可被称为装饰器(Decorator),并且为其提供了一些语法糖,这里我们也利用Lua的元表为其做了一个类似的语法糖(使用 .. 运算符),并用这个语法糖将以上几个函数修改了下。

先看最终效果:

这里首先我们用统计函数来修饰原函数,用于统计原函数真正的运行次数,然后为这个函数加上记忆函数功能使其相同输入参数时仅须运行1次,最后加上标签foo_final, 为这个最终函数打上标记。

最终运行结果显示,一切符合预期。

最后附上各个装饰器的实现:

以及最后用于生成装饰器的函数实现:

其实有了这个装饰器,就可以使用Lua去做所谓的面向切面的编程(Aspect-Oriented Programming)了,能做的事情就远不止上面提到的那几种,只是上面几个我个人比较常用而已。
最后,元表的世界好奇妙,哈哈。

 

欢迎关注微信公众号:Lua爱好者

使用Lua制作动态数据表格

表格配置中有种很常用的表格就是等级数值表,类似下面这种:

table_1
一般都是数值策划用exce制作出来的,而且很大可能是用公式计算出来的。通常情况下这么做没什么问题,但是有这么一种需求,一个怪物的掉落经验会随着玩家等级变化而变化,那么要配置这样一个掉落表就会是个让人很繁琐的事情了。通常我们会有两种做法:(我之前呆过的项目经验) 继续阅读

谈谈如何设计超过你驾驭能力的游戏系统

点此阅读图文并茂版

其实标题是有一点误导性的,标题里说的驾驭严格来说是指你的大脑能够直接驾驭的意思。这里我将一个人能驾驭的东西分为两种,一种是直接驾驭,一种是间接驾驭,什么意思呢?举个例子。

一个人的家乡,他出生的地方,假设他应该是再熟悉不过了,那么我认为他的大脑是能够驾驭这个地方的地形的。在我的定义里,如果是能够直接驾驭的人,那么他就应该能够瞬间能掌握这个地方的所有细节。简单的测试就是他能够非常准确迅速的将这个地方的地图给画出来,在规划线路的时候,脑中有个很清晰的类似地图APP导航一样的线路出线。

但还有一种熟悉的方式就是虽然我不知道地图怎么画,但是把我扔到任何一个地方我都能知道附近的地形。同样,规划路线虽然没法做到上面那点。但凭着脑海中对这条路有几个分叉,每个分叉对应哪个地方,然后逐步的规划,再通过正推反推,最终形成一条路线(这只是我对大脑思路的一个逻辑模拟,实际上它可能也就是一瞬间就完成了)。这就是我想说的间接驾驭。

说完了定义性的东西,那么其实今天想记录的就是如何通过一些手段去设计出我们能够间接驾驭但是没法直接驾驭的复杂系统。在这里要说的是两种方法:分层设计和闭包设计。

这两个概念,有人可能会说这东西是学程序才要学的,这也许是一个误解,这些东西并不是专属于编程的,而是逻辑思维训练后的产物,(其中闭包是数学推理中的,所以才说数学是很锻炼逻辑思维的)所以它们会影响你的逻辑设计。

首先分层就是把你要做的事情按照层级来划分,通过一层层的递进从而最终实现目的。简单举个例子,以用基本电子元器件(二极管,电阻什么的)制造计算机来举例子吧。如果直接让一个人用这些东西来做计算机,估计会疯掉的吧。但是如果换个思路,首先用基本电子元器件做成一些更高级的元件,比如与门,非门。再用这些高级元件去构成更复杂一点儿的,比如加减器,计数器等,接下来在用这些做成能够简单四则运算的CPU,存储器,那么计算机的雏形就已经出来了,接下来要做的就是把这些元件的性能做得越来越强大(引入集成电路)。看似无法完成的任务就通过这么一步步最终居然就实现了,这就是分层设计的威力。简单来说就是从下往上,每一层的结果变成了上一层的基石,通过将整体的复杂逐步拆分到每一层分解它的复杂度从而让我们能够对每一层直接驾驭,最终实现间接驾驭。

万物相通,其实游戏设计一样,通过游戏规则的分层设计,也许就能够设计出很多原本我们无法驾驭的游戏玩法。

第二个要说是闭包(不是一些计算机语言中的闭包,而是离散数学里定义的),具体定义可以自己去查阅。通俗点理解大概就是有一个元素的集合,它包含了一定规则,而在这个集合里的任意的元素都可以依照这些规则进行演算,且无论如何演算它的结果仍然被包含在这个集合中。比如有理数的集合和四则运算规则组成的闭包,任意的有理数之间都可以进行四则运算,且运算出的结果仍然是个有理数。

因为最近对这个感悟比较深,就稍微多说点。应用到游戏里,比如我们设计一个进化游戏,游戏由病毒,高级病毒,超级病毒3种生物组成,战斗力分别为1,2,3,我们的目的就是要培养出具有足够高的战斗力的病毒军队。其中有一些规则:每人初始只有一个病毒,病毒可以进化,中途无法通过其他途径获得任何病毒。这里如果进化规则设计成病毒->高级病毒->超级病毒,那么这不是一个闭包,因为其中的进化规则导致超级病毒并不能应用进化规则。带来的后果也显而易见,每个玩家最终结果只能拥有一个超级病毒,游戏的天花板立马就显现了。如果要提高天花板怎么办,可能有以下三种办法:
1 加入更高级的生物,但是终归最终玩家还是能够走到终点。
2 引入新的规则,每个生物进化到最终版本可以提升战斗力数值。
3 修改原有的进化规则,补充超级病毒的进化规则,即超级病毒可以进化为4个病毒,这样就形成了一个闭包。(数值保持3到4)

这里2和3都可以彻底解决天花板问题,如果只是单纯比拼战斗综合确实也并没有太大区别。但如果这游戏还具有战斗系统,且是实时战斗,一个生物与多个生物能产生的结果就会不一样,那么3带来的复杂度的提升就超过了2(当然,进化规则可能要相应修改,比如超级病毒只能进化为3个甚至2个病毒,因为毕竟个数也是一种潜在战斗力)

同样是设计一个游戏系统,为了解决一些问题或是创造更多变的玩法,方法的不同也许会导致最终结果呈指数级不同。所以我也是推荐一些游戏策划不妨多了解了解理性的逻辑知识,不要让自己永远停留在感性的阶段,仅仅依靠直觉(天才本能,然而大部分人并不具有)和经验(其实就是去借鉴自己过去做过的游戏或者其他同类型游戏)去做设计。

如何进行有效的思考

最近看到知乎一篇文章(原文链接:http://www.zhihu.com/question/23614288/answer/25288450),我关注的倒不是答主给出的答案,而是他给出答案的整个思考过程,见过太多人的思考过程,我觉得有必要针对有效思考这个问题探究下。

看过一本书叫《思考,快与慢》,里头说思考可以分为两种,快思考与慢思考。快思考就是依赖情感、记忆和经验迅速作出判断,它见闻广博,使我们能够迅速对眼前的情况作出反应,如一个熟练掌握四则运算的人看到3X7这个算式的时候会本能的得出21这个思考结果。但它很容易上当,它固守“眼见即为事实”的原则,任由损失厌恶和乐观偏见之类的错觉引导我们作出错误的选择。另一种被称为慢思考,它通过调动注意力来分析和解决问题,并作出决定,它比较慢,不容易出错,比如考试的时候遇到了一种新题型需要解答时。
继续阅读

金山这几年--如何把游戏做失败(三)

本来前段时间约了几个朋友让他们分享些自己的心得,结果貌似各位都跟我一样也是重度拖延症患者……之前也有朋友跟我反映说我写的都是跟编程技术相关的,没什么兴趣,所以决定这篇写点其他的。

记得飞舟给我们的培训的第一堂课上就问了我们一个问题:你为什么要学习编程?每个人都有自己的初衷,有很多人是因为喜欢技术,而我则是为了可以自己做游戏。

能做出一款让大家喜欢玩并且玩得很开心的游戏是我的梦想。但是很可惜,至今为止我也没有参与过任何一款可以称之为成功的产品,但我却有幸目睹了一些失败产品的历程。成功不可以复制,但是失败可以避免,况且在成功学如此流行的今天,成功游戏的分析多入牛毛,所以这里还是分享些游戏失败的经验吧。

继续阅读

金山这几年--程序员的信仰(二)

“信仰是指对一个人(同样的对他的能力)、事物、神、宗教的教条或教导、没有经验证据的观点(例如拥有强烈的政治信仰)抱有信心和信任。” 维基百科如是说。

做为一名程序员,在金山的经历也使我拥有了一种编程信仰,我相信通过正确的方式方法可以写出优质没有bug的程序(是真的没有bug,而不是自己的声称)。只有你相信了它,你才有了这种可能去做到。

PS:至今还没有人从Tex的bug悬赏金中大幅获利给了我们这种教徒以极大的曙光。

继续阅读

金山这几年--program in project(一)

不知不觉在金山已经呆了近五年,这段时间里犯过很多错误,同时也学到了很多东西,既然已经离开,那也该为这段时间好好的做个回顾。今天就先来谈谈在编程技术这块吧。

首先声明,我谈到的技术相关的东西都是指在项目中的经验,那些纯粹的追求技术的人和在学术上追求的人,这些东西可能对你们来说毫无意义。 ^_^

继续阅读