别把语言当信仰

上次在公众号里发了条消息,有好多人问知乎上的争论是什么?其实没什么,只是在一个讨论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制作出来的,而且很大可能是用公式计算出来的。通常情况下这么做没什么问题,但是有这么一种需求,一个怪物的掉落经验会随着玩家等级变化而变化,那么要配置这样一个掉落表就会是个让人很繁琐的事情了。通常我们会有两种做法:(我之前呆过的项目经验) 继续阅读

简单使用Lua实现表格动态解析

在游戏开发里,游戏配置是必不可少的一项。表格式配置是一种非常常见的格式,简单易用,支持excel等表格工具编辑,深受广大策划同学的喜爱。这里介绍一种简单使用Lua来做表格配置的方式。

首先是表格的格式,为了简单起见没有采用直接解析excel格式的方式来做,而采用了csv格式,个人喜好,采用制表符作为分隔符。生成出来的格式大概如下
(这里推荐使用OpenOffice来进行编辑):

ID\tName\tDescription\n
1\tTom\t他是一个工人\n

继续阅读

唔,流水账~

从1月28日开工,到现在又过去了整整2个月零3天。从第一版设定,反反复复修改到现在方向基本已经差不多确定,今天终于做完了战斗和关卡的框架,出了第一个战斗demo,接下来的目标是把游戏的其余基本框架搭建完,然后开始做内容,然后调数值,抠细节,嗯,大概这样,加油。
继续阅读

farewell cocos2d

今天开始正式投入unity的阵营了,向cocos2d说再见。从3.0版本开始用到现在,感觉从3.2往后cocos2d就开始点错了天赋树,不在长处2D上发力,不在编辑器上下狠功夫,却非要花大力气去抢夺3D的用户,一声叹息。

PS:昨天用扑克牌做了一个游戏原型跟老婆玩了下,觉得可玩性还可以,不过很多地方还需要调整,第一次切实感受到游戏原型的力量,低成本,而且可以发现很多光靠脑子想想不出来的问题。

我的Sublime设置

写在这里,免得每次在一个新的地方又要重新网上找相关资料。

sublime text 3 package control install:

import urllib.request,os; pf = ‘Package Control.sublime-package’; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), ‘wb’).write(urllib.request.urlopen( ‘http://sublime.wbond.net/’ + pf.replace(‘ ‘,’%20’)).read())

继续阅读

腾讯很可能知道你每天都打开过哪些网页哦

近来无事在家写一些爬虫程序,但是抓包的时候却发现一个很诡异的包经常出现,就是下面这个。
qq1
点开一看,非常生气,原来是把我输入的网址给秘密的发送到 http://c.gj.qq.com/mailreport 这个URL去了,内容如下:
qq2
然后查看发送进程,居然是chrome!!!(见第一幅图)于是换了个猎豹浏览器试了下,并没有发送这个包,可以肯定是chrome除了问题!我的Chrome是官网下载的,应该是没问题的,于是很自然就想到肯定是偷偷给我的chrome装了什么插件,于是查看了下插件列表,只有这货是腾讯的:
qq3
于是尝试将它禁用,再试一次:
qq4
很好,这个包并没有再出现了。证据确凿,这个电脑管家真是细心周到,每天把我看过什么网页一五一十全部汇报上去了,感(qu)谢(ni)备(ma)至(de)!

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

点此阅读图文并茂版

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

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

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

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

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

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

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

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

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

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

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