前言
上一篇文章我们讲了一点无聊的东西,今天我们来讲easy的东西,关于cocos的常用类,为了配合这一点,我们讲一个实际项目:飞机大战
此项目已经在github上开源https://github.com/YoungForLong/WXDFJ
场景
场景是一个游戏的基本结构,所有的内容都附着于场景
local StartScene=class("StartScene",function()
return display.newScene("StartScene")
end)
function StartScene:ctor()
-- init list
self.count=1
-- init funcs
self:init()
self:setStyle()
end
创建一个空的层,我们在构造函数ctor里面添加了初始化的函数init(添加缓存)和setStyle(设置UI样式)
function StartScene:init()
if self then
-- 加入缓存
-- img
display.addSpriteFrames("shoot.plist","shoot.png")
display.addSpriteFrames("shoot_background.plist","shoot_background.png")
-- audio
audio.preloadMusic("sound/bgmusic.mp3")
audio.preloadMusic("sound/hero_down.mp3")
audio.preloadMusic("sound/enermy1_down.wav")
audio.preloadMusic("sound/enermy2_down.wav")
audio.preloadMusic("sound/enermy3_down.wav")
audio.preloadMusic("sound/shoot.wav")
-- data
if cc.UserDefault:getInstance() then
cc.UserDefault:getInstance():setStringForKey("initTag", "initialized")
cc.UserDefault:getInstance():flush()
print("RunTime : Data Initialized")
end
end
end
我们先判断self是否为空,为空的话那就是之前出问题了。
然后加载所有的缓存工作,包括图片,声音和数据储存
图片缓存是从一个plist表单里面添加一个合成图片,plist是一个json文件,里面主要储存了图片在合成图的位置和图片的名字,使用plist,能大大减少opengl的渲染开销,因为渲染一张大图效率远远大于很多张小图,有兴趣的童鞋可以去看看关于opengl渲染批次,节点的资料
声音的预加载很简单,格式固定
UserDefault是继承于cocos-2dx的类,它是一个xml文件读写的封装,xml节点的名字保存为key,值保存为vlaue,后面我们做数据存储的时候会讲到,这里不细谈
function StartScene:setStyle()
-- bg
local bgSp=cc.DrawNode:create()
-- 画一个四边形,display.cx是屏幕的半宽,display里面有很多常值用来调用
bgSp:drawPolygon({cc.p(0,0),
cc.p(2*display.cx,0),
cc.p(2*display.cx,2*display.cy),
cc.p(0,2*display.cy)})
--把这个四方形添加到这个场景上,默认的z坐标为0
self:addChild(bgSp)
-- btn
local startBtn=cc.Sprite:createWithSpriteFrameName("game_resume_nor.png")
startBtn:setPosition(display.cx,display.cy)-- 设置坐标,该坐标基于他的父节点,是一个相对坐标
startBtn:setScale(2) -- 设置缩放倍数,默认为1
self:addChild(startBtn,10)
startBtn:setTouchEnabled(true) -- 添加触摸属性
startBtn:setTouchMode(cc.TOUCH_MODE_ONE_BY_ONE) -- 设置触摸方式,单点还是多点
startBtn:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event) -- 添加执行函数
if event.name=="began" then
-- 切换图片,表示手指按下去了
startBtn:setSpriteFrame("game_resume_pressed.png")
return true
elseif event.name=="ended" then
startBtn:setSpriteFrame("game_resume_nor.png")
-- 场景转换
display.replaceScene(require("app.scenes.MainScene").new())
end
end)
end
此处我们的背景图片使用了绘图方法,用drawPolygon方法可以画一个多边形,参数是一个table,里面存的是一系列点,绘图方法会根据点的顺序来绘图。
关于addChild,cocos引擎使用了Node,一种链式结构,addChild会向当前Node添加子链,然后在游戏主循环和更新(如果你不了解主循环的概念,可以参考我之前写的文章http://blog.csdn.net/qq_22984991/article/details/51069571)中,会遍历最高的场景Node的所有子链的draw函数,draw函数里面有render方法,会执行opengl的渲染,这样,就把整条场景链渲染出来了。
触摸
我们来注重讲一下设置触摸的方法,我们利用setTouchEnable方法为Node节点添加触摸属性后,游戏的更新逻辑中会把该节点的碰撞检测放入更新检查列表中。具体的说,就是添加了触摸点(这是由硬件系统来提供的)是否在Node的包装方格中的判断
图片的包装方格为图片的大小和位置组成的矩形,可以用getBoundingBox来获取
发生触摸的执行函数我们大概讲一下,我们先死记硬背它的实现方法,格式固定如下:
startBtn:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event) -- 添加执行函数
if event.name=="began" then
-- 手指按下时触发的函数
return true-- 必须要这句
elseif event.name=="ended" then
-- 手指离开时执行的函数
end
end)
至于这个函数为什么叫addNodeEventListener,这涉及到事件分发机制,我们后面遇到了再详细地讲
实现效果
关于代码风格
如果你是成熟的开发者,可以忽略我下面说的话。
有的人可能说,你实现这么一个简单的场景,就写了这么多代码。
我希望大家在写代码的时候能将逻辑封装好,因为游戏的复杂性,逻辑有区别时,尽量封装成不同的函数,这样才能应付更复杂的项目(我实在不喜欢菜鸟们不写类,不写函数,面向过程的思维逻辑)
其他的类
其他的类我们这里没遇到,暂时不表(太多了根本说不完),有兴趣的可以去查看API文档,cocos关于UI的层面还是很成熟的,基本的效果都能实现。