Cocos2d-x之Lua核心编程(第2版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-27 11:38:41

点击下载

作者:刘克男

出版社:清华大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Cocos2d-x之Lua核心编程(第2版)

Cocos2d-x之Lua核心编程(第2版)试读:

前言

本书分8章,知识由浅入深、步步推进,建议按照顺序阅读。

第1章介绍Cocos2d-Lua背景,Cocos2d各版本之间的关系。

第2章旨在引导读者快速入门,已有Lua编程基础的读者,可跳过这一部分。

第3章介绍Cocos2d-Lua基础,本章是开发游戏必须掌握的知识结构。

第4章介绍Fruit Fest消除游戏的第一部分,实现了游戏的核心玩法。本章应用Cocos2d-Lua基础知识,展示了游戏开发的主要流程。

第5章介绍Cocos2d-Lua进阶,涵盖UI控件、瓦片地图、内存管理以及数据存储等内容。

第6章介绍Fruit Fest游戏的第二部分,为游戏添加了音乐与粒子特效。

第7章介绍Cocos2d-Lua高级内容,虽然其中的网络通信和物理引擎不是每个游戏都会用到,但它们是引擎必不可少的一部分。自定义事件和状态机为游戏系统架构提供基础支撑,而Lua Binding要求熟练掌握C语言开发,它们都是为高级工程师准备的。

第8章介绍打包与发布。之所以把该部分放在最后,是因为Cocos2d-Lua提供的Player模拟器已足够应对游戏的前期开发,通过Player测试游戏将为开发者节省大量时间。适用版本

本书基于Quick-Cocos2dx-Community 3.6.4版本撰写,由于Quick框架对接口稳定性所做的贡献,本书与引擎功能模块相关的章节适用于Cocos2d-Lua v3.3 Final之后的所有版本。可以在http://cocos2d-lua.org/download/index.md下载到最新的Cocos2d-Lua引擎。注:本书不适用于Cocos2d-Lua 2.x分支版本。书中实例

本书以理论结合实践,各章均配备了测试用例。其中第4章和第6章以Fruit Fest展示了完整游戏的开发流程,每一节各介绍一个独立的功能并配备实例代码,让初学者充分体验到游戏开发的细节。

读者可以从本书的主页(http://cocos2d-lua.org/book/index.md)免费获取所有章节配套的最新实例代码。读者对象

本书作为Cocos2d-Lua的权威书籍,从Lua语言基础开始,全面覆盖Cocos2d-Lua基础、进阶和高级编程,并指导读者逐步完成一款消除游戏的开发。

对于Cocos2d新手而言,本书可以作为手机游戏开发的入门书籍。

对于有经验的手游开发者而言,本书依然是进行Cocos2d-Lua开发必不可少的参考手册,大量的实例代码可以帮助读者节省宝贵的时间。

对于院校师生而言,本书的编排完全按照学习顺序系统展开,是教材的不二之选。

由于笔者水平有限,书中内容难免会有不妥或者疏漏,欢迎读者批评指正。作者2017年5月第1章Cocos2d-Lua1.1 概述

Cocos2d-x是一个跨平台开源游戏引擎,用它可以构建基于图形交互的跨平台游戏和应用。Cocos2d作为Cocos2d-x以及其他分支的鼻祖,有其特别的历史故事。1.1.1 Cocos2d的起源

2005年在阿根廷的一个叫Los Cocos的镇上,一个叫Ricardo的开发者(后来Cocos2d-iPhone的作者)和一群朋友计划每一个星期使用一种编程语言完成一个小游戏。在开发这些小游戏的过程中,他们发现做一个游戏引擎可以节省不少开发时间。2008年他们借鉴之前做小游戏的经验,发现使用python作为开发语言需要的时间最少,所以用python开发了第一个版本引擎,并用Ricardo的家乡地名命名该引擎为Los Cocos。一个月后(2008年3月),团队发布了0.1版并将引擎更名为Cocos2d。

在Cocos2d发布不久后,Apple公司正式发布AppStore以及相关SDK。此后大量使用Objective-C开发的iOS应用和游戏流行起来。这一年,Ricardo使用Objective-C重写了Cocos2d引擎,并发布了Cocos2d-iPhone的第一个版本。1.1.2 Cocos2d-x的诞生

最开始,Cocos2d没有跨平台的版本,推动Cocos2d-x诞生的重要因素在于Android的普及和国内对跨平台(iOS和Android)游戏开发的强烈需求。Android的开放性使得国内产生一大批的Android智能手机,这时候急需一款简单易用的跨平台游戏引擎来解决游戏内容提供商的疾苦。Cocos2d-x的作者王哲抓住了这个机遇,他发邮件给Ricardo表达了想衍生一款跨平台Cocos2d引擎的想法,出乎意料地,这个想法得到了Ricardo的大力支持并提供了很多技术支援,于是Cocos2d-x在王哲及其团队的努力下诞生了。

Cocos2d-x 1.x和2.x与Cocos2d-iPhone的1.x和2.x在设计上没有区别,Cocos2d-x用C++把Cocos2d-iPhone重写了一遍,并加入了一些有用的跨平台库和接口,很多用法都很像Objective-C风格,一些初学Cocos2d-x的用户会觉得不适应。这时候的Cocos2d-x的发展还跟不上Cocos2d-iPhone,然而在2013年的苹果开发者大会上,这一切有了变化。苹果公布了自家的SpriteKit游戏框架,设计概念完全抄袭了Cocos2d-iPhone,这种赤裸裸的剽窃激怒了Ricardo。Ricardo当即在社区宣布他将停止Cocos2d-iPhone的开发。随后Ricardo从Zenga公司跳槽到触控,正式转入Cocos2d-x的开发,于是全新的Cocos2d-x 3.x出现。

伴随着Cocos2d的成长,很多分支版本随之崛起,包括Cocos2d-x。这些分支版本罗列如图1-1所示,不同的分支支持不同的开发语言和平台,如表1-1所示。图1-1 Cocos家族表1-1 不同分支对应开发语言和平台1.1.3 Cocos2d-Lua的发展

2012年,由于Cocos2d-x中使用的C++对开发人员要求较高,所以网龙科技利用tolua++,将Cocos2d-x的C++接口转为Lua接口(这层C++与Lua之间的胶水代码叫Lua Binding)。开发者可以使用Lua这种简单易懂的脚本语言来编写游戏,从而极大地提高开发效率。

2012年上半年,廖宇雷的公司开始使用Cocos2d-x+Lua来开发游戏。但是发现当时Cocos2d-x对Lua的支持还存在相当多不完善的地方。所以他重写了整个Lua Binding代码,解决了内存泄漏和只能使用全局函数做回调等问题。

在Cocos2d-x 2.0发布后,Lua Binding又进行了不少改进和完善,截止到Cocos2d-x 2.1.4,整个Lua Binding已经可以说是相当稳定了。所以《我是MT》(月流水3000万人民币)、《大掌门》(月流水4000万人民币)这些赚钱像印钱的游戏,纷纷采用Cocos2d-x+Lua的解决方案。

自从2012年后,Cocos2d-x团队开发把重心放在了JavaScript脚本方案上,于是廖宇雷继续改进Lua代码并以Quick-Cocos2d-x为代号开发了一个新的开源引擎。2013年底,Quick-Cocos2d-x以一款千万级游戏《唐门世界》证明了引擎的实力,最终Quick-Cocos2d-x被Cocos2d-x并入主线,成为新的引擎版本Cocos2d-Lua,而廖宇雷也成为这个版本的核心开发者。2015年Cocos2d-x团队再次偏移开发重心到以JavaScript为主的Cocos Creator工具上,Quick-Cocos2d-x的后续版本由社区志愿者进行维护,并建设http://cocos2d-lua.org/网站,作为Quick-Cocos2dx-Community的社区网站。

Cocos2d-Lua的发展始终贯彻了三个目标:(1)降低学习曲线;(2)提高易用性;(3)创建一个精简、但更容易扩展的架构。1.2 版本介绍1.2.1 Cocos2d-x版本介绍(C++)1.Cocos2d-x 1.x

这个版本沿袭了Cocos2d-iPhone 1.x版本的架构与风格接口。引擎很多模块使用了iOS中的功能模块,不同的是这些模块都是由C++模拟实现的。此外,创建Xcode工程需要安装Cocos2d-x的项目模板。底层图形引擎使用的是OpenGL ES 1.0。2.Cocos2d-x 2.x

随着iPhone对OpenGL ES 2.0的支持,Cocos2d-iPhone发布了2.x版本,以支持OpenGL ES 2.0,并且Cocos2d-iPhone开始支持Lua和JS脚本开发游戏。

Cocos2d-x紧随其后发布2.x版本,但是它已不再只是局限于简单的Cocos2d-iPhone的C++版本,因为它有着更大的市场目标——Android。为了创建跨平台的项目,在没有脚本创建项目之前,使用Xcode模版的方式,只能先生成一个iOS或Mac的项目,大多开发者首先在Mac上开发(因为调试方便),然后再建一个Android工程移植过去。为了解决这个难题,Cocos2d-x在2.1中引入了脚本创建项目,一次性创建多个跨平台的项目,用户按照文件规范在相应的目录放源代码或资源,后期可以很轻松地编译跨平台的游戏。

为了适应Android错综复杂的分辨率,Cocos2d-x在2.0.4版本开始设计自己的分辨率适配方案,有别于Cocos2d-iPhone 2.x仅为iPhone优化的设计,Cocos2d-x提出的设计分辨率概念能更好地适应各种分辨率,这个方案在Cocos2d-x 2.1.3版本得以成熟稳定。

Cocos2d-x在2.x中还直接集成了很多跨平台第三方库,如WebSocket和HttpRequest等,为Cocos2d-x的跨平台战略打下坚实的基础。Cocos2d-x 2.x的Lua和JS支持也由社区推动起来,为游戏内更新提供了有力支持。3.Cocos2d-x 3.x

Cocos2d-x 3.x是由Ricardo加入触控科技后主导开发的全新版本,引擎接口完全去掉了Objective-C的影子。大量使用C++11的新特性并着重提升性能,而且慢慢开始向3D引擎转型。最新的《捕鱼达人3》就使用了Cocos2d-x的3D技术。在图形渲染上做了改进,独立的渲染线程能更好地利用CPU,提升游戏帧率。高度整合的物理引擎,降低了上手难度。全新的GUI系统也让人充满了期待。1.2.2 Cocos2d-Lua版本介绍

Cocos2d-Lua 2.x底层引擎采用Cocos2d-x 2.x,提供了非常便于使用的Player模拟器,能够不更改C++代码在模拟器上运行Cocos2d-Lua游戏,而且通过Quick的Player可以很方便地创建和管理项目。在Cocos2d-x的基础上添加了大量的第三方库,如json和xml等,并加入了强大的MVC架构支持。

Cocos2d-Lua 3.x底层引擎采用了Cocos2d-x 3.x,并最大程度地兼容Cocos2d-Lua 2.x的游戏。Quick的Player更新到了3.x版本,支持了3D模型的绘制和操作,还能够支持VisualStudio、Xcode和Eclipse等进行调试。第2章Lua编程2.1 Lua在Windows下的运行环境搭建

在进入Cocos2d-Lua游戏开发之前,需要先掌握Lua这门脚本语言。首先搭建Lua在Windows中的开发调试环境,推荐安装Lua For Windows这套工具。

Lua For Windows是一整套Lua开发环境,它包括Lua解释器、Lua参考手册、Lua范例、Lua库和文档以及SciTE多用途编辑器。

可以看到Lua For Windows整合了在Windows上学习和开发Lua所需要的所有东西,这也是我们推荐它的原因。

Lua For Windows官方网站:http://code.google.com/p/luaforwindows/。这里直接下载已发布的exe安装包:http://luaforwindows.googlecode.com/files/LuaForWindows_v 5.1.4-46.exe注:此处选用5.1版本是为了对应Cocos2d-Lua中集成的Lua解析器版本。2.1.1 安装

双击LuaForWindows_v5.1.4-46.exe文件开始安装,注意在Select Additional Tasks界面,勾选如图2-1所示的选项,安装完毕,会在桌面上增加两个快捷图标Lua和SciTE,如图2-2所示。2.1.2 运行1.console模式

双击桌面上的Lua图标,出现如图2-3所示的Windows控制台窗口,输入代码print("hello world"),按Enter键执行代码。注:Lua中的io.write("Hello world")也能实现标准输出,和print的区别是它不提供换行符。图2-1 LuaForWindows安装图2-2 Lua快捷方式图2-3 console模式2.IDE模式

双击桌面上的SciTE图标,出现如图2-4所示的程序界面。(1)在文本区域输入print("hello world");(2)单击“磁盘”工具按钮保存代码到文件;(3)单击“运行”工具按钮运行代码;图2-4 IDE模式(4)IDE底部的日志窗口显示运行信息。2.2 Lua基础2.2.1 Lua介绍

Lua是一个小巧的脚本语言,作者是巴西人。Lua设计的初衷是为了能够嵌入到其他应用程序中,能够为应用提供灵活的扩展。Lua脚本和C/C++可以很容易地相互调用,这样Lua能够使用C/C++已有的库,并且能够在带来脚本的开发效率的同时,保证程序的运行效率。

Lua的所有代码都是由标准的ANSI C编写而成,代码简洁优美,只要在支持ANSI C的系统上都能够完美地运行。

Lua主要面向三类用户:(1)需要一门简单的脚本语言嵌入到应用程序中的开发者;(2)想要提高开发效率的C/C++语言开发者;(3)想要提高运行效率的脚本开发者。

Lua有如下特点:(1)易嵌入,可以很方便地与C/C++编写的游戏逻辑互相调用;(2)简单,不涉及任何复杂的编程概念,麻雀虽小五脏俱全;(3)轻量,库体积很小,只有几百千字节;(4)易学习,游戏策划人员也可以使用Lua;(5)高性能,对比C/C++原生语言,Lua解析带来的性能损失可忽略不计。

使用Lua开发的经典游戏有:(1)大话西游2(PC);(2)魔兽世界WOW(PC);(3)剑侠情缘3(PC);(4)我叫MT(iOS、Android);(5)刀塔传奇(iOS、Android)。2.2.2 Lua语法

最新的Lua版本是5.3版本,但使用最广泛的Lua版本是5.1版本。

Lua 5.1与5.3的设计理念不一样,语法上也有不同,Cocos2d-Lua集成的Lua解析器是5.1版本。本书涉及到的Lua均指Lua 5.1。1.命名规范

Lua中命名规范基本上和C语言一样。

Lua中的标识符可以由任意字母组合、数字和下画线构成,但不能以数字开头,并且应该避免以下画线开头,或后面是几个大写字母的命名方式。下面是几个正确的命名方式:i j var abcdefg ab_cd1234

不要使用Lua中的关键字。Lua中的关键字很少,只有21个,为变量命名时,不能使用相同的名称。关键字如下: and break do else elseif end false for function if in local nil not or repeat return then true until while

Lua区分大小写,大写和小写的两个单词是不同的。2.类型与值

Lua是一种动态类型的语言。在语言中没有类型定义的语法,每个值本身就包含了类型信息。对于一个变量来说,它的值的类型是可以随意变换的。

Lua中有8种基础类型:(1)nil——空;(2)boolean——布尔;(3)number——数字;(4)string——字符串;(5)function——函数;(6)table——表;(7)userdata——自定义数据类型;(8)thread——线程。

函数type用来检测一个变量的类型。print(type(var))-->输出为nilvar=20print(type(var))-->输出为numbervar="Hello world"print(type(var))-->输出为stringvar=printprint(type(var))-->输出为functionvar={}print(type(var))-->输出为table

1)nil

在Lua中nil表示的是一种类型,它只包含一个值nil。任何变量在未赋值之前都是nil。当给一个全局变量赋值nil时,表示删除这个变量。

2)boolean

boolean有两个值:true和false。在Lua中只有false和nil表示假,其他情况都为真,数字0也为真,例如下面的代码:var=nilif var then print("var is true")else print("var is false")end

①当第一句local var不给var赋值时,输出为var is false;

②当local var=false时,输出为var is false;

③当local var=true时,输出为var is true;

④当local var=0时,输出为var is true(区别于C/C++语言);

⑤当local var=""时,输出为var is true。

3)number

在Lua中number类型表示实数。不论是浮点数还是整型数都是number类型。有人可能会担心number浮点数的效率,其实现在的CPU大部分都有单独的协处理器计算浮点数,对于浮点数的运算效率完全不是问题。

Lua中的number既可以使用普通的写法,也可以使用科学计数法。如下:a=10b =50.2c =5e20

4)string

Lua中的字符串表示的是一个字符序列。里面可以包含任意字符,包括数字0(C/C++中的字符串终止字符)。并且Lua中字符串的长度是不限制的,既可以是空字符串,也可以是整本书,Lua对字符串的处理是很快的。一般定义一个字符串变量如下:str="I'm a string"

在Lua中,单引号和双引号都可以作为字符串的标识,建议统一使用一种。如果字符串有多种格式,则可以使用下面的方式来定义字符串:long_str=[[I'm a "long", long str]]

如果字符串中有多个连在一起的方括号时,可以在两个方括号之间添加多个=,例如:long_str=[===[[[I'm a "long", long str]]]===]

当需要连接两个字符串时,可以使用字符串连接操作符“..”(类似于C语言中的strcat),例如:print("Hello".."world")

当需要获取字符串的长度时,使用#能获取字符串的总长度,例如:a="Hello world"print(#a)

5)table

Lua中的table类似于C++中的map,但是相对于map的烦琐,table极大地简化了使用过程中的操作。例如要在C++中声明一个的map,定义如下:std∷mapmyMap;myMap["a"]="test";myMap.insert(std∷Map∷value_type("b","test2"));

而在Lua中只需要这样声明:t={a="test",b="test2"}3.表达式

1)算术运算符^

算术运算符有:+(加)、-(减)、*(乘)、/(除)、(指数)、%(取模)、-(负号)。a =3b =5print(a+b)--8print(a-b)---2print(a*b)--15print(a/b)--0.6print(a^b)--243print(a%b)--3print(-a) ---3注:Lua中的除法不区分整形和浮点,所有这里计算的结果是浮点数。

2)关系运算符

关系运算符有<(小于)、>(大于)、<=(小于等于)、>=(大于等于)、~=(不等于)、==(等于)。

对于table、userdata和function类型,Lua中是以它们的引用来做比较,即只有在它们指向同一个对象时才是相等的。

3)逻辑操作符

逻辑操作符有and(与)、or(或)和not(非)。(1)对于and来说,当第一个操作数为假时,返回第一个操作数,否则返回第二个操作数;(2)对于or来说,当第一个操作数为真时,返回第一个操作数,否则返回第二个操作数。

这样使用会带来一些语法上的便利。例如有时候需要判断一个值是不是为空,为空的话给它赋值,按正常的写法应该是这样的: if not x then x=v end

而Lua中则可以简化如下:x =x or v

4)字符串连接操作符

字符串连接操作符“..”用来连接两个字符串,当后一个为其他类型时会转为字符串,例如:print("Hello"..2)->Hello 2

数字2先转化为字符串再与"Hello"做字符串拼接。

5)操作符优先级

操作符优先级如下(由高到低):^not-(unary)* /+-..<><=>= ~= ==andor

除了指数操作符“^”和连接操作符“..”是右连接外,其他操作符是左连接。

6)table构造式

构造式是用来创建和初始化table的表达式,是Lua中特有的一种表达式。最简单的构造式如下:t ={}

构造式还可以初始化一个记录化风格的table,如下:a={x=10,y=20}

在构造一个Lua table的时候,如果不给它一个key,那么其他默认的key就是从1开始的,例如要声明一个有关星期的table:t={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}

这样一个table它的构造应该是这样的:t={}t[1]="Sunday"t[2]="Monday"⋮

这段代码看起来是不是很眼熟呢?它其实就是我们以前使用过的数组。如果想在一个table中既使用数组又使用map,那么也可以像下面一样构造一个table:t={ name ="美女", age =18, "美女背后的男人1", "美女背后的男人2"}

如果比较习惯C/C++语言中结构体的用法,也可以这样来给一个table赋值:t={}t.name="美女"t.age=18t[1]="美女背后的男人1"t[2]="美女背后的男人2"

也可以使用混合方式给table赋值,未设置key的项,key会自动从1开始编号:b={["x"]=10,["y"]=20,1,2,3,4}

删除一个table项,只需要给它赋值为nil就行了。例如:t[1]=nil4.语句

1)赋值语句

Lua中的赋值,直接使用赋值操作符,例如:a=20

当有多个参数赋值时可以同时给多个参数赋值,例如:a,b=10,20

这个功能在交换值和函数需要返回多个参数时很有用,例如在C语言中交互两个值需要这样做:temp=a;a =b;b =temp;

在Lua中不需要临时变量做中介,只需要a,b =b,a

同样地,如果函数需要返回多个值时,可以这样写: function someValue() local a,b=10,20 return a,bendprint(someValue())

2)局部变量与块

Lua中也有局部变量和全局变量之分。定义一个局部变量,只需要在变量名称前面加一个local关键字,不加local修饰的变量均是全局变量。例如:i=10 --全局变量local j=i --局部变量

局部变量的作用域只在声明它的块内,例如一个局部变量如果只在函数内声明的话,退出函数时它就不能被访问了。当一个局部变量在循环中声明,那么循环结束,局部变量的生命周期也结束了。(1)如果变量没有赋值的话,会自动被赋值为nil;(2)如果赋值操作符的左边的变量更多,那么多出来的变量会自动被赋值为nil;(3)如果赋值操作符的右边的值更多,那么多余的值会被抛弃;(4)如果需要在一段代码块中再插入一段代码块,可以使用do...end。例如: if true then do local a=20 print(a) endend

3)控制语句(1)if...then...else

if语句同其他语言中的if判断作用是一样的,只是写法略有区别,在C语言中写一个if判断如下: if(a>b){}else if(a>c){}else{}

在Lua中,应该这样写: if a>b thenelse if a>c thenelseend

Lua中是不支持switch语句的,条件判断只有if语句。(2)while

while语句同其他语言中的while语句一样,例如C语言中这样写while语句: while(a<5){ ++a;}

在Lua中,应该这样写: while a<5 do a=a+1end(3)repeat...until

Lua中的repeat语句类似于C语言中的do...while语句,都是先执行一次,然后再判断条件是否满足。例如在C语言中这样写do...while语句:i =0;do{ i =i+1; printf("%d,",i);}while(i<3);//输出为:1,2,3

在Lua中,应该这样写: local i=0repeat i =i+1 print(i..",")until i>3--输出为1,2,3,4

和其他语言不同的是,repeat...until中定义的局部变量作用域范围包括了until中的语句。(4)for

Lua当中的for语句分成两种:数字型for循环和泛型for循环。

①数字型for循环的语法类似于VBasic中的for语句写法: for var=from,to,step doend

表示从from到to,每次递增step。step参数如果不设置的话默认是1。注:for循环中的参数为表达式或者函数调用时,只会调用一次。

②泛型for循环通过一个迭代器(iterator)函数来遍历所有值,同C++中的vector之类的STL库遍历很类似,但是使用起来更简单方便。

Lua库为泛型for循环提供了几个迭代器函数:io.lines用于遍历每行;pairs用于迭代table元素;ipairs用于迭代数组元素;string.gmatch用于迭代字符串中单词。

泛型for循环的使用非常广泛,例如遍历一个table:days ={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}for k,v in pairs(days)do print(v)end注:for循环的step无法在循环体内修改,这点和C/C++不同。实际上每次for循环会新生成一个局部var变量。(5)break与return

break跳出当前块,return结束当前函数。break和return只能是块的最后一条语句,否则需要用do break end构造一个块。5.函数

1)函数定义

函数是封装和抽象块的主要机制。函数的主要功能就是在内部封装一些需要的功能模块,并且对外只开放函数名称和参数。Lua中函数的定义和其他语言基本一样。 function func()end

不管函数是否有参数,都必须有(),并且以function开头,以end结尾。end可以不另起一行,如下:func=function()end

以上两种定义方式效果是一样的。

在面向对象编程中经常会定义内部方法,在Lua中怎么定义呢?关于面向对象编程在后面的章节中会讲到,首先来看定义对象的方法:class={} --一个对象function class.func1()endfunction class:func2()end

首先定义了一个名为class的table,然后在table上定义了两个函数成员,func1()和func2()使用了不同的定义方法。

这两种定义形式从表面上看是“.”与“:”的区别。在Lua中使用“:”定义的函数会自动传入一个名为self的变量,self同C++中的this一样,表示当前对象的指针;而“.”定义的函数没有self。

func1()和func2()可以用如下方式等价起来: function class:func2()endfunction class.func1(self)end --传入一个self作为参数

2)函数参数与返回值

Lua中函数的参数可以有任意多个,当给函数传递参数时超过了函数定义的形参个数,那么多余的参数会被丢弃。如果传入的参数比形参少,那么缺少的形参的值默认为nil。

Lua中如果函数返回多个参数时,所赋值的表达式不是最后的一个元素,那么函数只返回一个值用来赋值给变量,如下: function foo() return 30,50endx,y =foo(),20-->x=30,y=20

return返回一个函数的返回值,需要注意的地方是return f()语句会把f()返回的所有返回值都返回,而return(f())会迫使它只能返回一个结果。

关于多重返回值还有一个特殊的函数unpack,它接受一个数组作为参数,并从下标1开始返回该数组所有元素。print(unpack{10,20,30}) -->10 20 30a,b=unpack{10,20,30} -->a=10,b=20,30被丢弃

unpack经常用于函数参数传递。a ={"hello","ll"}string.find(unpack(a))

3)可变参数

Lua中的函数也可以接受数量不定的实参。例如在print时可以传入多个实参,下面是一个定义参数可变的函数的例子。 function add(...) local s =0 for i,v in ipairs{...}do s=s+v end return sendprint(add(1,2,3,4,5)) -->15

参数表中的3个点…表示该函数可以接受不同数量的实参。当这个函数被调用时,它的所有参数都会被收集到一起。这部分收集起来的实参称为这个函数的变长参数。访问变长参数可以用{...}。

4)闭包函数

闭包函数是指将一个函数写在另一个函数之内,这个位于内部的函数可以访问外部函数中的局部变量。下面是一个简单例子: function newCounter() local i =0 return function() i=i+1 return i endendc1 =newCounter()print(c1()) -->1print(c1()) -->2

在这段代码中,匿名函数访问了一个非局部的变量i,i用来保持一个计数器。表面上看,由于创建变量i的函数(newCounter)已经返回,所以之后每次调用匿名函数时,i均已超出了它的作用域;但是因为匿名函数一直在使用变量i,所以Lua会正确地维护i的生命周期。

在匿名函数内部,i既不是全局变量,也不是局部变量,它被称为外部的局部变量或upvalue。

闭包大量地使用在各种编程语言中,特别是它在回调函数方面的便利性,让人们对它爱不释手。2.3 Lua进阶

在第一章介绍了Lua用table模拟面向对象,但并不完整,面向对象的三大特性并未解决。在完善整套模拟之前,首先理解一个新的概念metatable。2.3.1 Metatable(元表)

metatable是table预定义的一系列操作。例如把两个table相加,那么Lua会先去检查两个table是否有metatable,然后再检查metatable中是否有_add方法。如果有就会按照_add方法中的操作来执行,否则报错。

Lua中的每一个值都有或者可以有一个元表,但table和userdata可以拥有独立的元表,其他类型的值就只能共享其类型所属的元表,例如字符串使用的就是string的元表。需要注意的是,Lua在新建table的时候是不会创建metatable的,需要使用setmetatable来设置元表。setmetatable的参数可以是任意table,包括要赋值的table本身。可以通过下面的方式来查看一个变量是否有元表:print(getmetatable(a)) --nil表示没有元表

为了更形象地展示metatable的特性,接下来实现一个table,让它能实现+运算。该table的定义如下:Set={}function Set.new(t) local set={} for i,v in ipairs(t)do set[v]=true end return setendfunction Set.union(a,b) local res=Set.new{} for k inpairs(a)do res[k]=true end for k in pairs(b)do res[k]=true end return resendfunction Set.tostring(set) local s ="{" local sep="" for e in pairs(set)do s=s..sep..e sep ="," end return s.."}"endfunction Set.print(s) print(Set.tostring(s))end

上述代码定义一个table名为Set。Set包含四个函数,分别为new、union、tostring和print。

Set的定义使用了一个技巧:Set.new传入的数字,存储在set的key上,而不是传统的value上。

接下来定义metatable,为了避免命名空间污染,可以在set内部定义如下metatable。Set.mt={}

然后在Set.new()中添加如下代码:Set.mt._add=Set.unionsetmetatable(set,Set.mt)

这样就给Set添加了一个metatable,metatable的_add元方法指向Set的union函数。

接下来测试Set:s1=Set.new{10,20,30,50}s2=Set.new{30,1}s3=s1+s2Set.print(s3)-->{1,30,10,50,20}

从Set.print的结果可以看到,我们成功实现了自定义table的相加操作——集合并集。

Set展示了metatable中_add元方法的作用,table的主要预定义元方法如图2-5所示。图2-5 预定义元表2.3.2 重要元方法介绍1._index元方法

Lua中访问table的元素是先通过_index元方法来查找是否有这个函数,如果没有则返回nil。通过改变_index元方法,能够改变检索之后的结果。_index的值可以直接是table,也可以是函数。若是table,则会以该table来作为索引进行查询;若是函数,Lua则将table和缺少的域作为参数调用这个函数。

下面是一个用函数来检索的例子:Window ={}Window.mt ={}Window.prototype ={x=0,y=0,width=100,height=100}Window.mt._index =function(table,key) return Window.prototype[key]endfunction Window.new(t) setmetatable(t,Window.mt) return tend--测试w =Window.new{x=10,y=20}print(w.height)2._newindex元方法

_newindex和_index的使用基本一样,区别在于_newindex的作用是用于table的更新,_index用于table的查询操作。当对table中不存在的索引赋值时,就会调用_newindex元方法。组合使用_index和_newindex可以实现很多功能。下面是_newindex的例子:Window ={}Window.mt ={}Window.mt._newindex=function(table,key,value) print("update of element"..tostring(key)..tostring(value)) rawset(table,key,value) --测试下面的语句! --table[key]=valueendfunction Window.new(t) setmetatable(t,Window.mt) return tend--测试w =Window.new{x=10,y=20}w.a =10print(w.a)

_index和_newindex小结:_index在get表中未定义元素时候触发,对应有rawget(table,key)来避免调用_index。_newindex在set表中未定义元素时候触发,对应有rawset(table,key)来避免调用_newindex。2.3.3 封装

了解metatable之后,Lua类的封装就会变得容易。只要为table添加metatable,并设置_index元方法。如下例中People的new方法:People ={age=18}function People:new() local p ={} setmetatable(p,self) self._index=self return pendfunction People:growUp() self.age=self.age+1 print(self.age)end

People测试代码:p1 =People:new()p1:growUp() -->19p2 =People:new()p2:growUp() -->19

运行结果可以看出,两个对象拥有的age成员是完全独立的,而且所有的有关People的方法都可以对外不可见。这样完全实现了面向对象中类的封装。2.3.4 继承

继承是面向对象编程中必不可少的一部分。依然用上例中的People,展示Lua实现继承的方法。创建一个People实例Man,再在Man上重写People的同名方法,如下:Man=People:new()function Man:growUp() self.age=self.age+1 print("man's growUp:"..self.age)end

Man测试代码:man1=Man:new()man1:growUp() -->man's growUp:19

结果说明Man成功重写了growUp方法,并能使用People定义的age成员。实现了继承的基本用法。2.3.5 多态

Lua不支持函数多态,而指针的多态,由于Lua动态类型的特性,本身就能支持。

接前面的例子,做如下的测试:person =People:new()person:growUp()person =Man:new()person:growUp()---->>People's growUp:19---->>Man's growUp:19第3章Cocos2d-Lua基础3.1 Cocos2d-Lua开发环境配置

工欲善其事,必先利其器。在进入Cocos2d-Lua学习之前,需要搭建Cocos2d-Lua的开发环境。3.1.1 安装Cocos2d-Lua

首先,在http://cocos2d-lua.org/download/index.md中下载最新的Cocos2d-Lua版本。然后解压zip包到一个路径,路径以及文件夹的名称不能有中文或空格。注:编写本教材时最新的版本为Quick-Cocos2dx-Community 3.6.4。1.在Mac系统下安装引擎

进入解压后的引擎目录,在终端中执行引擎根目录下的setup_mac.sh脚本,执行脚本的命令前不需要加入sudo。当提示输入密码的时候,请输入当前用户的登录密码。正确的安装过程如图3-1所示。安装完成后,单击Launchpad中的player3启动引擎。图3-1 setup_mac.sh

部分Mac用户可能会在环境配置后,新建工程的mac_ios项目不能编译通过。这时候需要在终端手动输入下面的命令来配置Xcode环境变量。defaults write com.apple.dt.Xcode IDEApplicationwideBuildSettings-dict#路径替换为自己的Quick rootdefaults write com.apple.dt.Xcode IDEApplicationwideBuildSettings -dict-add QUICK_V3_ROOT"/User/u0u0/Quick_cocos2dx_Community"defaults write com.apple.dt.Xcode IDESourceTreeDisplayNames-dictdefaults write com.apple.dt.Xcode IDESourceTreeDisplayNames -dict-add QUICK_V3_ROOT QUICK_V3_ROOT2.在Windows系统下安装引擎

进入解压后的引擎目录,双击setup_win.bat开始执行安装脚本。正确的安装过程如图3-2所示。安装完成后,双击Windows系统桌面上的player3快捷方式启动引擎。图3-2 setup_win.bat

Player中可以进行创建项目、运行项目等操作。切换到“示例”标签可运行引擎自带的各种测试程序,如图3-3所示。3.1.2 安装Sublime与QuickXDev

脚本语言的优点是无须编译且log功能强大,但开发环境都略简陋。幸运的是,Cocos2d-Lua的开发人员基于强大的Sublime编辑器,开发了QuickXDev插件,为Cocos2d-Lua代码编写提供了不少便

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载