描述
开 本: 16开纸 张: 胶版纸包 装: 平装是否套装: 否国际标准书号ISBN: 9787302391111
全书分为六篇: 基础篇、进阶篇、数据与网络篇、优化篇、跨平台移植篇和实战篇,共23章。
基础篇包括第1~8章,分别是Cocos2dx Lua介绍、环境搭建、标签、菜单、精灵、场景、层、动作、特效、动画和用户事件。
进阶篇包括第9~12章,分别是游戏音乐与音效、粒子系统、瓦片地图和物理引擎。
数据与网络篇包括第13~15章,分别是数据持久化、基于HTTP的网络通信、Node.js与WebSocket网络通信。
优化篇为第16章,主要介绍性能优化。
跨平台移植篇包括第17章和第18章,分别是移植到Android平台和移植到iOS平台。
实战篇包括第19~23章,分别是使用Git管理程序代码、项目实战——迷失航线手机游戏、为迷失航线游戏添加广告、发布放到Google play应用商店和发布放到苹果App Store。
第1章 准备开始
第2章 Lua语言基础
第3章 Hello Cocos2dx Lua
第4章 标签和菜单
第5章 精灵
第6章 场景与层
第7章 动作、特效和动画
第8章 用户事件
第二篇 进阶篇
第9章 游戏背景音乐与音效
第10章 粒子系统
第11章 瓦片地图
第12章 物理引擎
第三篇 数据与网络篇
第13章 数据持久化
第14章 基于HTTP的网络通信
第15章 Node.js与WebSocket网络通信
第四篇 优化篇
第16章 性能优化
第五篇 跨平台移植篇
第17章 移植到Android平台
第18章 移植到iOS平台
第六篇 实战篇
第19章 使用Git管理程序代码版本
第20章 Cocos2dx Lua敏捷开发项目实战——迷失航线手机游戏
第21章 为迷失航线游戏添加广告
第22章 把迷失航线游戏发布放到Google play应用商店
第23章 把迷失航线游戏发布放到苹果的App Store
前言
手机游戏市场越来越火爆,Cocos2d团队推出了Cocos2dx游戏引擎,它的优势在于在一个平台下开发,多平台发布。很多开发团体都转型使用Cocos2dx开发游戏了。基于这样的一个背景,我们智捷课堂与清华大学出版社策划了5本有关Cocos2dx游戏引擎图书: 《Cocos2dx实战: C 卷》; 《Cocos2dx实战: JS卷——Cocos2dJS开发》; 《Cocos2dx实战: Lua卷》; 《Cocos2dx实战: 工具卷》; 《Cocos2dx实战: CocoStudio卷》。本书是Cocos2dx游戏引擎Lua卷,就是使用Cocos2dx的Lua语言API。本书的编写历经了5个月的时间,从Cocos2dx3.0alpha0到Cocos2dx3.2终版本经历了多个版本的变化,而且Cocos2dx 3多个版本之间有很多的变化,每次都重新修改案例、修改书中内容。经过几个月努力,我们终于在2014年10月完成初稿,几个月来智捷iOS课堂团队夜以继日,几乎推掉一切社交活动,推掉很多企业邀请我去讲课的机会,每天工作12小时以上,不敢有任何的松懈,不敢有任何的模棱两可,只做一件事情——编写此书。每一个文字、每一张图片、每一个实例都是我们的精心之作。关于本丛书具体进展请读者关注智捷课堂官方网站http://www.51work6.com。
关于本书网站为了更好地为广大读者提供服务,我们专门为本书建立的一个网站http://www.cocoagame.net,大家可以查看相关出版进度,并对书中内容发表评论,提出宝贵意见。
关于源代码本书配套提供了一百多个完整的案例项目源代码,读者可以到本书网站http://www.cocoagame.net下载。
勘误与支持我们在网站http://www.cocoagame.net中建立了一个勘误专区,及时地把书中的问题、失误和纠正反馈给广大读者,您发现了有什么问题,可以在网上留言,也可以发送电子邮件到: [email protected],我们会在时间回复您。也可以在新浪微博中与我们联系: @tony_关东升。
本书主要由关东升执笔撰写。此外智捷课堂团队的贾云龙、赵大羽、李玉超、赵志军、关珊和李政刚也参与了部分内容的编写工作。感谢赵大羽老师手绘了书中全部草图,并从专业的角度修改书中图片,力求更加真实完美地奉献给广大读者。感谢我的家人容忍我的忙碌,以及对我的关心和照顾,使我能抽出这么多时间,投入全部精力专心地编写此书。
由于时间仓促,书中难免存在不妥之处,请读者原谅,并提出宝贵意见。
关东升
2015年2月于北京
卞安-红孩儿:目前市面上Cocos2d-x教材很多,但缺乏体系化介绍工具化开发工作流程的指导书,赵老师的这本书详尽的介绍了基于Cocos2d-x进行游戏开发所的工具软件,对于游戏开发者提高工作效率将有极大的帮助和实用指导。
Cocos引擎中文官网/CocoaChina社区:基于Cocos引擎开发的产品已经占据移动游戏市场名的位置,Cocos仍继续吸引更多的开发者投入其中。著名移动开发专家关东升老师创作的这套“Cocos2d-x实战”系列,全面系统地论述了Cocos2d-x开发的理论与实践。书中构建了全面的Cocos2d-x学习体系:基础知识→核心技术→实战特训,是初学者极佳的学习路线!堪称Cocos2d-x开发入门指导的百科全书!
CSDN社区:关东升老师具有多年的Cocos2d-x教学经验,他的教学视频、技术书籍与技术博客都深受欢迎。“Cocos2d-x实战”系列图书是关东升老师倾力创作的技术经典,书中融汇了多年的教学实践与应用开发经验,凝练了大量贴合开发者实际工作需求的要点。如果您是一位渴望通过学习Cocos2d-x进入游戏开发领域的程序员,本书应当成为您的领航之作。
畅游2035?9ria社区:“Cocos2d-x实战”是关东升老师用创业者的心态撰写的技术图书,并全面超越了同类图书!经过10个月的磨练,历经Cocos2d-x 众多版本的变化,关老师及其团队不断修改案例、程序,终于完美定稿。借助本书,开发者可以快速进行项目实战,轻松完成“做中学”的体验。
51CTO总裁熊平:时至今日,移动互联网改变了人们的生活方式,同时也改变了IT产业的格局,手机游戏开发无疑成了移动开发领域火热的技术方向。“Cocos2d-x实战”系统论述了Cocos2d-x全方位的游戏开发知识与技能。无论您是就业者还是创业者,您都会通过本书获益匪浅。
在开始详细介绍Cocos2dx Lua引擎的API之前,有必要先了解一下手机游戏引擎有哪些和了解Cocos2dx Lua的前世今生。然后从一个HelloLua入手,介绍Cocos2dx Lua的基本开发流程、Cocos2dx Lua生命周期以及Cocos2dx Lua核心知识体系。
3.1移动平台游戏引擎介绍
游戏引擎是指一些已编写好的游戏程序模块。游戏引擎包含渲染引擎(即“渲染器”,包括二维图像引擎和三维图像引擎)、物理引擎、碰撞检测系统、音效、脚本引擎、电脑动画、人工智能、网络引擎以及场景管理等子系统。在目前移动平台游戏引擎中主要可以分为2D和3D引擎。2D引擎主要有Cocos2diphone、Cocos2dx、Cocos2dJS、Corona SDK、Construct 2、WiEngine和Cyclone 2D,3D引擎主要有Unity 3D、Unreal Development Kit、ShiVa 3D和Marmalade。此外还有一些针对于HTML 5的游戏引擎,如Cocos2dhtml5、XCanvas和Sphinx等。这些游戏引擎各有千秋,但是目前得到市场普遍认可的2D引擎是Cocos2diphone、Cocos2dx和Cocos2dJS,3D引擎是Unity 3D。
3.2Cocos2d游戏引擎Cocos2diphone、Cocos2dx和Cocos2dJS是目前流行的2D游戏引擎。它们属于同一家族,具有相同的API。3.2.1Cocos2d游戏引擎家谱在介绍Cocos2dx Lua之前有必要先介绍一下Cocos2d的家谱,如图31所示是Cocos2d的家谱。
图31Cocos2d的家谱
Cocos2d早是由阿根廷的Ricardo和他的朋友使用Python开发的,后移植到iPhone平台,使用的语言是ObjectiveC。随着在iPhone平台取得了成功,Cocos2d引擎变得更加多元化,将对其中各个引擎介绍如下。 ShinyCocos: 使用Ruby对Cocos2diphone进行封装,使用Ruby api开发。 CocosNet: 是在MonoTouch平台上使用的Cocos2d引擎,采用.NET实现。 Cocos2dandroid: 是为Android平台使用的Cocos2d引擎,采用Java实现。 Cocos2dandroid1: 是为Android平台使用的Cocos2d引擎,采用Java实现,由国内人员开发的。 Cocos2djavascript: 是采用Javascript脚本语言实现的Cocos2d引擎。 Cocos2dx: 是采用C 实现的Cocos2d引擎,它是由Cocos2dx团队开发的分支项目。 Cocos2dJS: 是采用JavaScriptAPI的Cocos2d引擎,一方面它可以绑定在Cocos2dx上开发基于本地技术的游戏; 另一方面依托浏览器运行,开发基于Web的网页游戏。它也是由Cocos2dx团队开发的分支项目。此外,历史上Cocos2d还出现过很多分支,随着技术的发展这些逐渐消亡了,其中有生命力的当属Cocos2dx和Cocos2dJS引擎了。
3.2.2Cocos2dx引擎Cocos2dx设计目标如图32所示。横向能够支持各种操作系统,桌面系统包括Windows、Linux和Mac OS X,移动平台包括iOS、Android、Windows Phone、Bada、BlackBerry和MeeGo等。纵向方面向下能够支持OpenGL ES1.1、OpenGL ES1.5和OpenGL ES2.0,以及DirectX11等技术,向上支持JavaScript和Lua脚本绑定。
图32Cocos2dx设计目标
简单说Cocos2dx设计目标是为了实现跨平台,而不再为同一款游戏在不同平台发布而进行编译了。而且Cocos2dx为程序员考虑的更多,很多程序员可能对于C 不熟悉,针对这种情况可以使用JavaScript和LuaLua是一个小巧的脚本语言,是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组(Roberto Ierusalimschy、Waldemar Celes和Luiz Henrique de Figueiredo)于1993年开发。——引自于百度百科http://baike.baidu.com/view/416116.htm?fr=wordsearch开发游戏。
3.2.3JavaScript和Lua绑定Cocos2dx设计得非常巧妙,可以绑定JavaScript和Lua脚本语言,使得不熟悉C 的人员也能使用Cocos2dx引擎开发游戏,Cocos2dx不使用C 语言的API,而是使用JavaScript和Lua语言API。Cocos2dx绑定JavaScript和Lua脚本原理如图33所示。
图33Cocos2dx绑定JavaScript和Lua脚本
从图33可知,通过Cocos2dx和Cocos2dJS引擎,程序员可以开发网页游戏和本地游戏。图中A线路和B线路都是给掌握JavaScript脚本的程序员准备的,通过A线路,使用Cocos2dJS引擎开发基于HTML5的网页游戏。同样的JavaScript代码,也可以通过B线路,使用js binding(js绑定)技术透过C API访问Cocos2dx引擎,开发本地游戏。使得同样的JavaScript代码就可以实现在不同平台下运行。
图33中的C线路是给熟悉C 的程序员准备的。通过C API访问Cocos2dx引擎,开发本地游戏。
图33中的D线路是本书重点介绍的内容,是为熟悉Lua脚本的程序员准备的。使用Lua binding(Lua绑定)技术透过C API访问Cocos2dx引擎,开发本地游戏,为统一概念,本书中Cocos2dx Lua绑定技术称为Cocos2dx Lua。
通过Cocos2dx和Cocos2dJS引擎Cocos2dx团队构建了自己的技术生态圈,通过这些引擎使得游戏开发越来越简单。3.3搭建Cocos2dx Lua开发环境
使用Cocos2dx Lua开发游戏,主要的程序代码是Lua语言,因此,凡是能够开发Lua语言工具都适用于Cocos2dx Lua游戏开发。本书推荐Cocos Code IDE工具。
3.3.1搭建Cocos Code IDE开发环境Cocos Code IDE是Cocos2dx团队开发的,用于开发Cocos2dJS和Cocos2dx Lua绑定的游戏工具,它是基于EclipseEclipse是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。——引自于百度百科http://baike.baidu.com/subview/23576/9374802.htm平台的开发工具,Eclipse基于Java的要想运行Cocos Code IDE工具,需要安装JDK或JRE,JDK是Java开发工具包,JRE是Java运行环境。1. JDKJDK的安装和设置见2.1.1节。
2. Cocos Code IDE的下载和安装Cocos Code IDE的下载地址是http://www.cocos2dx.org/download,在浏览器中的页面如图34所示。选择合适的文件下载,目前包括了Mac OS X版本和Windows版本,注意Windows有32位和64位之分,还有安装(Setup)版本和压缩(Zip)版本之分。
图34下载Cocos Code IDE
下载cocoscodeidewin641.0.0rc1.zip解压版本,解压后找到Cocos Code IDE.exe文件,运行可以启动Cocos Code IDE工具,在启动过程中需要选择Workspace目录,如图35所示,Workspace目录是工程的管理目录,选择好之后单击OK按钮,如果该目录不存在则重新创建。
图35选择Workspace
Cocos Code IDE的具体使用方法下面章节再介绍。
3.3.2下载和使用Cocos2dx Lua官方案例首先到Cocos2dx官方网站下载Cocos2dx开发包。Cocos2dx3.2下载解压后的目录结构如图36所示。
图36Cocos2dx 3.2开发包内容
如果要运行官方的案例可以进入到build目录,build目录中的内容如图37所示,这里包含了各个平台编
图37build目录内容
译和运行案例的工程等文件,其中cocos2d_tests.xcodeproj文件是Cocos2dx案例的Xcode工程文件,cocos2dwin32.vc2012.sln文件是Cocos2dx案例Win32平台下Visual Studio 2012解决方案文件,另外的cocos2dwp8.vc2012.sln文件是Cocos2dx案例Windows Phone 8平台下Visual Studio 2012解决方案文件。
如果在Window下学习和开发,一般运行cocos2dwin32.vc2012.sln解决方案就可以了。如果启动cocos2dwin32.vc2012.sln解决方案,进入如图38所示的Visual Studio 2012界面,其中的luatests工程是Cocos2dx官方提供的Cocos2dx Lua案例工程,需要选中luatests工程,右击选择“设置启动项目”,然后运行上方工具栏中的运行调试按钮,运行luatests工程。
图38Cocos2dx Lua案例
首次运行编译Cocos2dx时间会长一些,运行起来之后会看到一个Windows的窗口(如图39(a)所示),单击其中的一个菜单项可以运行相应的示例(如图39(b)所示)。
图39运行案例
如果想查看luatests源代码,需要到\tests\luatests目录下,使用文本编辑工具打开Lua代码文件查看。
3.4个Cocos2dx Lua游戏
编写的个Cocos2dx Lua程序命名为HelloLua,从该工程开始学习其他的内容。3.4.1创建工程创建Cocos2dx Lua工程可以通过Cocos2dx提供的命令工具Cocos实现,但这种方式不能与Cocos Code IDE集成开发工具很好地集成,不便于程序编写和调试。由于Cocos Code IDE工具是Cocos2dx开发的专门为Cocos2dJS和Cocos2dx Lua开发设计的,因此使用Cocos Code IDE工具创建Cocos2dx Lua工程很方便。首先需要在Cocos Code IDE工具中先配置Lua框架,打开Cocos Code IDE工具,选择菜单Window→Preferences,弹出对话框如图310所示,选择Cocos→Lua在右边的Lua Frameworks,选择。
图310配置Lua框架
Lua框架配置不需要每次都进行,只需要在开始时配置,但创建工程的时候,Cocos Code IDE工具会从这个Lua框架目录中创建工程文件。接下来就可以创建Lua工程了,选择菜单File→New→Project,如图311所示,弹出项目类型选择对话框。
图311项目类型选择对话框
选中CocosLuaProject,然后单击Next按钮,弹出如图312所示的对话框。在Project Name项目中输入工程名称,Create Project in Workspace是在Workspace目录中创建工程,需要选中该项目,Create From Existing Resource项目选中可以在已经存在的工程创建,现在不需要选中该项目。单击Next按钮进入如图313所示的配置运行环境对话框,在该对话框中可以配置项目运行时信息。Orientation是配置模拟器的朝向,其中landscape是横屏显示,portriat是竖屏显。Desktop Runtime Settings中的Title是设置模拟器的标题,Desktop Windows initialize Size是设置模拟器的大小。Add Native Codes是设置添加本地代码到工程。后单击Finish按钮完成创建操作,创建好工程之后如图314所示。
图312新建项目对话框
图313配置运行环境对话框
图314创建工程成功界面
3.4.2Cocos Code IDE中运行创建好工程后可以测试一下,在左边的工程导航面板中选中HelloLua工程,右击菜单选择Run As→Cocosluainding运行刚刚创建的工程,运行结果如图315所示。
图315运行工程界面
编写的程序代码在src目录下,在本例中,Lua文件负责处理如图316所示的场景界面逻辑。如果想调试程序,可以设置断点,如图317所示。单击行号之前的位置。
图316设置断点
右击选择Debug As→CocosLuabinding菜单。如图317所示,程序运行到第31行挂起,并进入调试视图,在调试视图中可以查看程序运行的堆栈、变量、断点、计算表达式和单步执行程序等操作。
图317运行到断点挂起
3.4.3工程文件结构创建的HelloLua工程已经能够运行起来了,下面介绍一下HelloLua工程中的文件结构,使用Cocos Code IDE打开HelloLua工程,左侧的导航面板如图318所示。
图318HelloLua工程中的文件结构
在如图318所示的导航面板中,res文件夹存放资源文件,src文件夹是主要的程序代码main.lua和GameScene.lua,其中main.lua是程序入口文件,Cocos2dx会在底层绑定该文件,并且启动和运行它。在GameScene.lua中实现了游戏场景。
3.4.4代码解释HelloLua工程中主要有两文件,下面详细解释它们内部的代码。1. main.lua文件main.lua是程序入口文件,代码如下:
require “Cocos2d”①
— cclog
local cclog = function(…) ②
print(string.format(…)) ③
end
— for CCLuaEngine traceback
function __G__TRACKBACK__(msg) ④
cclog(“—————————————-“)
cclog(“LUA ERROR: ” .. tostring(msg) .. “\n”)
cclog(debug.traceback())
cclog(“—————————————-“)
return msg
end
local function main() ⑤
collectgarbage(“collect”) ⑥
— avoid memory leak
collectgarbage(“setpause”, 100) ⑦
collectgarbage(“setstepmul”, 5000) ⑧
cc.FileUtils:getInstance():addSearchPath(“src”)
cc.FileUtils:getInstance():addSearchPath(“res”)
cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(480, 320, 0)
–create scene
local scene = require(“GameScene”) ⑨
local gameScene = scene.create() ⑩
gameScene:playBgMusic()
if cc.Director:getInstance():getRunningScene() then
cc.Director:getInstance():replaceScene(gameScene)
else
cc.Director:getInstance():runWithScene(gameScene)
end
end
local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
error(msg)
end
上述第①行代码require是在加载Cocos2d模块,并且可以避免重复加载。第②行代码是声明cclog函数,该函数的作用是输出日志信息。第③行代码print(string.format(...))是输出函数。第④行代码是声明__G__TRACKBACK__(msg)函数,在程序出错的时候由第行的xpcall调用,并输出堆栈信息。日志输出的堆栈信息如下:
[LUA-print] stack traceback:
[string “.\src/main.lua”]:13: in function ‘__index’
[string “.\GameScene.lua”]:52: in function [string “.\GameScene.lua”]:49
[LUA-print] —————————————-
第⑤行代码是main()函数,它是由第行的xpcall函数调用。第⑥行代码collectgarbage(“collect”)中的collectgarbage是垃圾收集器的通用接口函数,用于操作垃圾收集器,其他的定义如下:
collectgrabage(opt[,arg])
其中,opt参数是操作方法标志,标志包括如下。 collect: 执行一次全垃圾收集周期,见第⑥行代码。 stop: 停止垃圾收集器。 restart: 重启垃圾收集器。 count: 返回当前Lua中使用的内存数量,单位KB。 step: 单步执行一个垃圾收集,步长中的size属性是由参数arg指定,如果完成一次收集周期,将返回true。 setpause: 设置arg/100的值作为垃圾收集暂停时长,见第⑦行代码。 setstepmul: 设置arg/100的值,作为步长的增幅,即新步长=旧步长*(arg/100),见第⑧行代码。
上述第⑨行代码local scene=require(“GameScene”) 加载GameScene模块,返回值是table类型全局变量。第⑩行代码local gameScene=scene.create()通过静态create()函数创建GameScene场景。第行代码cc.Director:getInstance():getRunningScene()判断是否有一个场景正在运行,如果有场景运行则通过cc.Director:getInstance():replaceScene(gameScene)语句使用gameScene场景替换当前场景,否则通过cc.Director:getInstance():runWithScene(gameScene)语句运行gameScene场景,无论是replaceScene还是runWithScene函数游戏都会是进入到gameScene场景。第行代码local status, msg=xpcall(main, __G__TRACKBACK__)中的xpcall函数由Lua提供,用于调用其他函数,并且可以捕获到错误,xpcall函数的定义如下:
xpcall (f, err)
其中f参数是要调用的函数,err是捕获到错误的时候调用的函数。返回值status是错误状态,msg是错误消息。事实上第行代码local status, msg=xpcall(main, __G__TRACKBACK__)才是程序的入口。由它调用main()函数,如图319所示的调用堆栈中,能够看出它们的调用顺序。
图319调用堆栈
2. GameScene.lua文件GameScene.lua负责创建游戏主场景,如图315所示的场景就是在GameScene.lua中实现的,GameScene.lua主要代码如下:
require “Cocos2d”
require “Cocos2dConstants”
–声明GameScene类
local GameScene = class(“GameScene”,function() ①
return cc.Scene:create()
end)
–静态创建函数
function GameScene.create() ②
local scene = GameScene.new() ③
scene:addChild(scene:createLayerFarm()) ④
scene:addChild(scene:createLayerMenu()) ⑤
return scene
end
–构造函数
function GameScene:ctor() ⑥
self.visibleSize = cc.Director:getInstance():getVisibleSize()
self.origin = cc.Director:getInstance():getVisibleOrigin()
self.schedulerID = nil
end
–播放背景音乐
function GameScene:playBgMusic()
local bgMusicPath = cc.FileUtils:getInstance():fullPathForFilename(“background.mp3”)
cc.SimpleAudioEngine:getInstance():playMusic(bgMusicPath, true)
local effectPath = cc.FileUtils:getInstance():fullPathForFilename(“effect1.wav”)
cc.SimpleAudioEngine:getInstance():preloadEffect(effectPath)
end
–创建Dog精灵
function GameScene:creatDog() ⑦
…
local spriteDog = cc.Sprite:createWithSpriteFrame(frame0)
…
return spriteDog
end
— create farm 创建农场层
function GameScene:createLayerFarm() ⑧
local layerFarm = cc.Layer:create() ⑨
…
return layerFarm
end
— create menu 创建菜单层
function GameScene:createLayerMenu() ⑩
local layerMenu = cc.Layer:create()
…
return layerMenu
end
return GameScene
在GameScene.lua中创建GameScene场景,并在场景中添加了农场层和菜单层。第①行代码是声明GameScene场景类,class(“GameScene”,function(){…})函数是由Cocos2dx Lua引擎提供的,可以通过Lua创建对象。class函数定义如下:
class(classname, super)
其中参数classname是函数名,它是字符串类型,super是调用父类构造函数。第②行代码声明GameScene.create()静态函数,在main.lua中通过scene.create()语句调用。第③行代码local scene=GameScene.new()是创建GameScene创建对象,new()函数会调用第⑥行的GameScene:ctor()函数,ctor()是构造函数,用来初始化GameScene场景对象。第④行代码是调用GameScene场景对象的createLayerFarm()函数创建农场层(见第⑧行代码)。第⑤行代码是调用GameScene场景对象的createLayerMenu()函数创建菜单层(见代码第⑩行)。第⑦行代码函数是创建Dog精灵,使用cc.Sprite:createWithSpriteFrame(frame0)语句创建精灵对象,将在后面详细介绍。在创建农场层函数createLayerFarm()中的⑨行代码local layerFarm=cc.Layer:create()是创建层对象,将在后面详细介绍。第行代码返回GameScene变量,它是table类型,在main()函数中调用local scene=require(“GameScene”)语句时候返回。
3.5重构HelloLua在3.4节介绍了通过Cocos Code IDE工具创建一个Cocos2dx Lua工程,但是该工程还是有点复杂,对于初学者不容易读懂其中的代码。在本节中重新介绍一个HelloLua工程,在后面的章节中将以此为模板编写程序代码。该实例运行结果如图320所示,场景中有一个HelloWorld标签和一个图片。
图320HelloLua实例
修改main.lua主要代码如下:
require “Cocos2d”
— cclog
cclog = function(…)①
print(string.format(…))
end
— for CCLuaEngine traceback
function __G__TRACKBACK__(msg)
cclog(“—————————————-“)
cclog(“LUA ERROR: ” .. tostring(msg) .. “\n”)
cclog(debug.traceback())
cclog(“—————————————-“)
return msg
end
local function main()
collectgarbage(“collect”)
— avoid memory leak
collectgarbage(“setpause”, 100)
collectgarbage(“setstepmul”, 5000)
cc.FileUtils:getInstance():addSearchPath(“src”)
cc.FileUtils:getInstance():addSearchPath(“res”)
cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(960, 640, 0)
–create scene
local scene = require(“GameScene”)
local gameScene = scene.create()
–gameScene:playBgMusic()②
if cc.Director:getInstance():getRunningScene() then
cc.Director:getInstance():replaceScene(gameScene)
else
cc.Director:getInstance():runWithScene(gameScene)
end
end
local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
error(msg)
end
上述第①行代码cclog函数原来是local修饰,这里删除了local,说明该函数是全局函数,可以在其他模块中使用cclog函数。第②行代码中暂时注释gameScene:playBgMusic()代码。修改GameScene.lua主要代码如下:
require “Cocos2d”
require “Cocos2dConstants”
size = cc.Director:getInstance():getWinSize()
local GameScene = class(“GameScene”,function()
return cc.Scene:create()
end)
function GameScene.create()
local scene = GameScene.new()
scene:addChild(scene:createLayer())①
return scene
end
function GameScene:ctor()
end
— create layer
function GameScene:createLayer()②
cclog(“GameScene init”)
local layer = cc.Layer:create()
local label = cc.LabelTTF:create(“Hello World”, “Arial”, 46) ③
label:setPosition(cc.p(size.width/2,
size.height – label:getContentSize().height)) ④
layer:addChild(label)⑤
local bg = cc.Sprite:create(“HelloWorld.png”) ⑥
bg:setPosition(cc.p(size.width/2, size.height/2)) ⑦
layer:addChild(bg) ⑧
return layer
end
return GameScene
我们只保留了一个创建层函数createLayer(),在第①行代码创建层,第②行代码是声明创建成函数,第③行代码创建LabelTTF标签对象,第④行代码是设置标签对象的位置,第⑤行代码将标签对象添加到当前层中,第⑥行代码是创建精灵对象,第⑦行代码是设置精灵的位置,第⑧行代码是将精灵对象添加到当前层中。
3.6Cocos2dx Lua核心概念Cocos2dx Lua中有很多概念,这些概念很多来源于动画、动漫和电影等行业,例如导演、场景和层等概念,当然也有些有传统的游戏的概念。Cocos2dx Lua中核心概念如下: 导演。 场景。 层。 节点。 精灵。 菜单。 动作。 效果。 粒子运动。 地图。 物理引擎。
本节介绍导演、场景、层、精灵、菜单以及对应的类,由于节点概念很重要,将会在下面一节详细介绍。而其他的概念在后面的章节中介绍。3.6.1导演导演类Director用于管理场景对象,采用单例设计模式,在整个工程中只有一个实例对象。由于是单例模式,能够保存一致的配置信息,便于管理场景对象。获得导演类Director实例语句如下:
local director=cc.Director:getInstance()
图321Director类图运行结果
其中cc是Cocos2dx Lua中类的命名空间,Director是导演类,getInstance()函数获得调用实例。导演对象职责如下: 访问和改变场景。 访问配置信息。 暂停、继续和停止游戏。 转换坐标。Director类图如图321所示。
从如图321所示的类图中还可以看到它有一个子类是DisplayLinkDirector。
3.6.2场景场景类Scene是构成游戏的界面,类似于电影中的场景。场景大致可以分为以下几类: 展示类场景。播放视频或简单地在图像上输出文字,用来实现游戏的开场介绍、胜利和失败提示、帮助介绍。 选项类场景。主菜单、设置游戏参数等。 游戏场景。游戏的主要内容。场景类Scene的类图如图322所示,从类图可见Scene继承了Node类,Node类是一个重要的类,很多类都从Node类派生而来,其中有Scene、Layer等。
图322Scene类图
3.6.3层层是写游戏的重点,大约99%以上的时间是在层上实现游戏内容。层的管理类似于Photoshop中的图层,它也是一层一层叠在一起。如图323所示是一个简单的主菜单界面,它是由三个层叠加实现的。
图323层叠加
为了让不同的层可以组合产生统一的效果,这些层基本上都是透明或者半透明的。层的叠加是有顺序的,如图323(b)所示从上到下依次是菜单层→精灵层→背景层。Cocos2dx Lua是按照这个次序来叠加界面的。这个次序同样用于事件响应机制,即菜单层先接收到系统事件,然后是精灵层,后是背景层。在事件的传递过程中,如果有一个层处理了该事件,则排在后面的层将不再接收该事件了。每一层又可以包括很多各式各样的内容要素,如文本、链接、精灵、地图等内容。层类Layer的类图如图324所示。
图324Layer类图
3.6.4精灵精灵类Sprite是游戏中非常重要的概念,它包括了敌人、控制对象、静态物体和背景等。通常情况它会进行运动,运动方式包括移动、旋转、放大、缩小和动画等。Sprite类图如图325所示,从图中可见Sprite是Node子类,Sprite包含很多类型,例如物理引擎精灵类PhysicsSprite也都属于精灵。
图325Sprite类图
3.6.5菜单菜单在游戏中是非常重要的概念,它提供操作的集合,在Cocos2dx Lua中菜单类是Menu,Menu类图如图326所示,从类图可见Menu类派生于Layer。
在菜单中又包含了菜单项MenuItem,MenuItem类图如图327所示,从图327中可见菜单项MenuItem有很多种形式的子类,如MenuItemLabel、MenuItemSprite和MenuItemToggle,它表现出不同的效果。每个菜单项都有3个基本状态: 正常、选种和禁止。
图326Menu类图
图327MenuItem类图
3.7Node与Node层级架构Cocos2dx Lua采用层级(树状)结构管理场景、层、精灵、菜单、文本、地图和粒子系统等节点(Node)对象。一个场景包含了多个层,一个层又包含多个精灵、菜单、文本、地图和粒子系统等对象。层级结构中的节点可以是场景、层、精灵、菜单、文本、地图和粒子系统等任何对象。节点的层级结构如图328所示。
图328节点的层级结构
这些节点有一个共同的父类Node,Node类图如图329所示。Node类是Cocos2dx Lua为重要的根类,它是场景、层、精灵、菜单、文本、地图和粒子系统等类的根类。
图329Node类图
3.7.1Node中重要的操作Node作为根类,它有很多重要的函数,下面分别介绍: 创建节点。local childNode=cc.Node:create()。 增加新的子节点。node:addChild (childNode, 0, 123),第2个参数是Z轴绘制顺序,第3个参数是标签。 查找子节点。local node=node:getChildByTag(123),通过标签查找子节点。 node:removeChildByTag(123, true)通过标签删除子节点,并停止该节点上的一切动作。 node:removeChild(childNode, true)删除childNode节点,并停止该子节点上的一切动作。 node:removeAllChildrenWithCleanup(true)删除node节点的所有子节点,并停止这些子节点上的一切动作。 node:removeFromParentAndCleanup(true)从父节点删除node节点,并停止该节点上的一切动作。
3.7.2Node中重要的属性此外,Node还有两个非常重要的属性: position和anchorPoint。position(位置)属性是Node对象的实际位置。position属性往往还要配合使用anchorPoint属性,为了将一个Node对象(标准矩形图形)精准地放置在屏幕某一个位置上,需要设置该矩形的锚点,anchorPoint是相对于position的比例,默认是(0.5,0.5)。如图330所示是anchorPoint为(0.5,0.5)情况,这是默认情况。
如图331所示是anchorPoint为(0.0,0.0)情况。
如图332所示是anchorPoint为(1.0,1.0)情况。
如图333所示是anchorPoint为(0.66, 0.5)情况。
图330anchorPoint为(0.5,0.5)
图331anchorPoint为(0.0,0.0)
图332anchorPoint为(1.0,1.0)
图333anchorPoint为(0.66, 0.5)
为了进一步了解anchorPoint使用,修改HelloLua实例,修改GameScene.lua的GameScene:createLayer()函数如下,其中加粗字体显示的是添加的代码。
function GameScene:createLayer()
cclog(“GameScene init”)
local layer = cc.Layer:create()
local label = cc.LabelTTF:create(“Hello World”, “Arial”, 46)
label:setPosition(cc.p(size.width/2,
size.height – label:getContentSize().height))
label:setAnchorPoint(cc.p(1.0, 1.0))
layer:addChild(label)
local bg = cc.Sprite:create(“HelloWorld.png”)
bg:setPosition(cc.p(size.width/2, size.height/2))
layer:addChild(bg)
return layer
end
运行结果如图334所示,Hello World标签设置的anchorPoint为(1.0,1.0)。
图334Hello World标签的anchorPoint为(1.0,1.0)
3.7.3游戏循环与调度每一个游戏程序都有一个循环在不断运行,它是由导演对象来管理和维护的。如果需要场景中的精灵运动起来,可以在游戏循环中使用定时器(Scheduler)对精灵等对象的运行进行调度。因为Node类封装了Scheduler类,所以也可以直接使用Node中定时器相关函数。Node中定时器相关函数主要如下: scheduleUpdateWithPriorityLua(nHandler, priority)。每个Node对象只要调用该函数,那么这个Node对象就会定时地每帧回调用一次nHandler函数。priority是优先级,priority值越小越先执行。 unscheduleUpdate ()。停止scheduleUpdateWithPriorityLua的调度。
为了进一步了解游戏循环与调度的使用,修改HelloLua实例。修改后的GameScene.lua文件代码如下:
require “Cocos2d”
require “Cocos2dConstants”
size = cc.Director:getInstance():getWinSize()
local label①
local GameScene = class(“GameScene”,function()
return cc.Scene:create()
end)
function GameScene.create()
local scene = GameScene.new()
scene:addChild(scene:createLayer())
return scene
end
function GameScene:ctor()
end
— create layer
function GameScene:createLayer()
cclog(“GameScene init”)
local layer = cc.Layer:create()
label = cc.LabelTTF:create(“Hello World”, “Arial”, 46)
label:setPosition(cc.p(size.width/2,
size.height-label:getContentSize().height))
label:setTag(123)
label:setAnchorPoint(cc.p(1.0, 1.0))
layer:addChild(label)
local bg = cc.Sprite:create(“HelloWorld.png”)
bg:setPosition(cc.p(size.width/2, size.height/2))
layer:addChild(bg)
local function update(delta) ②
local x,y = label:getPosition()
label:setPosition(cc.p(x 2, y-2))
end
–开始游戏调度
layer:scheduleUpdateWithPriorityLua(update, 0) ③
function onNodeEvent(tag) ④
if tag == “exit” then ⑤
–开始游戏调度
layer:unscheduleUpdate() ⑥
end
end
layer:registerScriptHandler(onNodeEvent) ⑦
return layer
end
return GameScene
上述第①行代码定义了模块级标签对象label。第②行代码定义的update(delta)函数是调度函数。第③行代码layer:scheduleUpdateWithPriorityLua(update, 0)是开启游戏调度,按照帧率进行调度,优先级0是默认值。第④行代码是层处理事件回调函数,第⑤行代码是判断是否为退出层事件,如果是退出层事件则调用第⑥行代码停止调度。第⑦行代码layer:registerScriptHandler(onNodeEvent)是注册层事件监听器。
3.8Cocos2dx Lua坐标系在图形图像和游戏应用开发中,坐标系是非常重要的,在Android和iOS等平台应用开发时使用的二维坐标系的原点在左上角。而在Cocos2dx Lua坐标系中,它的原点在左下角,而且Cocos2dx Lua坐标系又分为世界坐标和模型坐标。3.8.1UI坐标UI坐标就是Android和iOS等应用开发时使用的二维坐标系。它的原点在左上角(见图335)。
图335UI坐标
UI坐标原点在左上角,x轴向右为正,y轴向下为正。在Android和iOS等平台使用的视图、控件等都是遵守这个坐标系。然而在Cocos2dx Lua默认不是采用UI坐标的情况下,有的时候也会用到UI坐标,例如在触摸事件发生的时候会获得一个触摸对象(Touch),触摸对象(Touch)提供了很多获得位置信息的函数,如下面代码所示:
cc.p touchLocation=touch:getLocationInView()
使用getLocationInView()函数获得触摸点坐标事实上就是UI坐标,它的坐标原点在左上角。而不是Cocos2dx Lua默认坐标,可以采用下面的语句进行转换:
cc.p touchLocation2=cc.Director:getInstance():convertToGL(touchLocation)
通过上面的语句就可以将触摸点位置从UI坐标转换为OpenGL坐标,OpenGL坐标就是Cocos2dx Lua默认坐标。3.8.2OpenGL坐标上面提到了OpenGL坐标是三维坐标。由于Cocos2dx Lua底层采用OpenGL渲染,因此默认坐标就是OpenGL坐标,只不过只采用两维(x和y轴)。如果不考虑z轴,OpenGL坐标的原点在左下角(见图326)。
图336OpenGL坐标
提示: 三维坐标根据z轴的指向不同分为左手坐标和右手坐标。右手坐标是z轴指向屏幕外,如图337(a)所示。左手坐标是z轴指向屏幕里,如图337(b)所示。OpenGL坐标是右手坐标,而微软平台的Direct3DDirect3D(D3D)是微软公司在Microsoft Windows操作系统上所开发的一套3D绘图编程接口,是DirectX的一部分,目前广为各家显卡所支持。与OpenGL同为计算机绘图软件和计算机游戏常使用的两套绘图编程接口之一。——引自于维基百科http://zh.wikipedia.org/wiki/Direct3D是左手坐标。
图337三维坐标
3.8.3世界坐标和模型坐标由于OpenGL坐标分为世界坐标和模型坐标,所以Cocos2dx Lua的坐标也有世界坐标和模型坐标之分。你是否有过这样的问路经历: 张三会告诉你向南走一公里,再向东走500米。而李四会告诉你向右走一公里,再向左走500米。这里两种说法或许都可以找到你要寻找的地点。张三采用的坐标是世界坐标,把地球作为参照物,表述位置使用地理的东、南、西和北。而李四采用的坐标是模型坐标,让你自己作为参照物,表述位置使用你的左边、你的前边、你的右边和你的后边。如图338所示,从图中可以看到A的坐标是(5,5),B的坐标是(6,4),事实上这些坐标值就是世界坐标。如果采用A的模型坐标来描述B的位置,则B的坐标是(1,-1)。
图338世界坐标和模型坐标
有时候需要将世界坐标与模型坐标互相转换。可以通过Node对象的如下函数实现: convertToNodeSpace(worldPoint): 将世界坐标转换为模型坐标。 convertToNodeSpaceAR(worldPoint): 将世界坐标转换为模型坐标,AR表示相对于锚点。 convertTouchToNodeSpace(touch): 将世界坐标中触摸点转换为模型坐标。 convertTouchToNodeSpaceAR(touch): 将世界坐标中触摸点转换为模型坐标,AR表示相对于锚点。 convertToWorldSpace(nodePoint): 将模型坐标转换为世界坐标。 convertToWorldSpaceAR(nodePoint): 将模型坐标转换为世界坐标,AR表示相对于锚点。
下面通过两个示例了解一下世界坐标与模型坐标互相转换。1. 世界坐标转换为模型坐标如图339所示是世界坐标转换为模型坐标的实例运行结果。
图339世界坐标转换为模型坐标
在游戏场景中有两个Node对象,其中Node1的坐标是(400, 500),大小是300×100像素。Node2的坐标是(200, 300),大小也是300×100像素。这里的坐标事实上就是世界坐标,它的坐标原点是屏幕的左下角。编写代码如下:
function GameScene:createLayer()
cclog(“GameScene init”)
local layer = cc.Layer:create()
–创建背景
local bg = cc.Sprite:create(“bg.png”)①
bg:setPosition(cc.p(size.width/2, size.height/2))
layer:addChild(bg) ②
local closeItem = cc.MenuItemImage:create(
“CloseNormal.png”,
“CloseSelected.png”)
closeItem:setPosition(cc.p(size.width – closeItem:getContentSize().width/2,
closeItem:getContentSize().height/2))
local menu = cc.Menu:create(closeItem)
menu:setPosition(cc.p(0, 0))
layer:addChild(menu)
–创建Node1
local node1 = cc.Sprite:create(“node1.png”) ③
node1:setPosition(cc.p(400,500))
node1:setAnchorPoint(cc.p(1.0, 1.0))
layer:addChild(node1, 0) ④
–创建Node2
local node2 = cc.Sprite:create(“node2.png”) ⑤
node2:setPosition(cc.p(200,300))
node2:setAnchorPoint(cc.p(0.5, 0.5))
layer:addChild(node2, 0) ⑥
local posX,posY = node2:getPosition()
local point1 = node1:convertToNodeSpace(cc.p(posX,posY)) ⑦
local point3 = node1:convertToNodeSpaceAR(cc.p(posX,posY)) ⑧
cclog(“Node2 NodeSpace = (%f,%f)”,point1.x, point1.y)
cclog(“Node2 NodeSpaceAR = (%f,%f)”,point3.x,point3.y)
return layer
end
第①~②行代码是创建背景精灵对象,这个背景是一个白色900×640像素的图片。第③~④行代码是创建Node1对象,并设置了位置和锚点属性。第⑤~⑥行代码是创建Node2对象,并设置了位置和锚点属性。第⑦行代码将Node2的世界坐标转换为相对于Node1的模型坐标。而第⑧行代码是类似的,它是相对于锚点的位置。运行结果如下:
Node2 NodeSpace=(100.000000,-100.000000)
Node2 NodeSpaceAR=(-200.000000,-200.000000)
结合图339解释一下,Node2的世界坐标转换为相对于Node1的模型坐标,就是将Node1的左下角作为坐标原点(图339中的A点),不难计算出A点的世界坐标是(100, 400),那么convertToNodeSpace函数就是A点坐标减去C点坐标,结果是(-100,100)。而convertToNodeSpaceAR函数要考虑锚点,因此坐标原点是B点,B点坐标减去C点坐标,结果是(-200,-200)。2. 模型坐标转换为世界坐标
如图340所示是模型坐标转换为世界坐标的实例运行结果。
图340模型坐标转换为世界坐标
在游戏场景中有两个Node对象,其中Node1的坐标是(400, 500),大小是300×100像素。Node2是放置在Node1中的,它对于Node1的模型坐标是(0, 0),大小是150×50像素。编写代码如下:
function GameScene:createLayer()
cclog(“GameScene init”)
local layer = cc.Layer:create()
–创建背景
local bg = cc.Sprite:create(“bg.png”)
bg:setPosition(cc.p(size.width/2, size.height/2))
layer:addChild(bg)
local closeItem = cc.MenuItemImage:create(
“CloseNormal.png”,
“CloseSelected.png”)
closeItem:setPosition(cc.p(size.width – closeItem:getContentSize().width/2,
closeItem:getContentSize().height/2))
local menu = cc.Menu:create(closeItem)
menu:setPosition(cc.p(0, 0))
layer:addChild(menu)
–创建Node1
local node1 = cc.Sprite:create(“node1.png”)
node1:setPosition(cc.p(400,500))
layer:addChild(node1, 0)
–创建Node2
local node2 = cc.Sprite:create(“node2.png”)
node2:setPosition(cc.p(0.0, 0.0)) ①
node2:setAnchorPoint(cc.p(0.0, 0.0))
node1:addChild(node2, 0) ②
local posX,posY = node2:getPosition()
local point1 = node1:convertToWorldSpace(cc.p(posX,posY)) ③
local point3 = node1:convertToWorldSpaceAR(cc.p(posX,posY)) ④
cclog(“Node2 WorldSpace = (%f,%f)”,point1.x, point1.y)
cclog(“Node2 WorldSpaceAR = (%f,%f)”,point3.x,point3.y)
return layer
end
上述主要关注第②行代码,它是将Node2放到Node1中,这是与之前代码的区别。这样第①行代码设置的坐标就变成了相对于Node1的模型坐标了。第③行代码将Node2的模型坐标转换为世界坐标。而第④行代码是类似的,它是相对于锚点的位置。运行结果如下:
Node2 WorldSpace=(250.000000,450.000000)
Node2 WorldSpaceAR=(400.000000,500.000000)
如图326所示的位置,可以用世界坐标描述。第①~②行代码修改如下:
node2-setPosition(Vec2(250, 450));
node2-setAnchorPoint(Vec2(0.0, 0.0));
this-addChild(node2, 0);
本章小结通过对本章的学习,读者可以了解Cocos2dx Lua开发环境的搭建,以及熟悉Cocos2dx Lua核心概念,这些概念包括导演、场景、层、精灵和菜单等节点对象。此外,还重点学习了Node和Node层级架构。后还介绍了Cocos2dx Lua的坐标系。
评论
还没有评论。