cocos2d-js的平面世界

cocos2d世界的经纬度–坐标系

首先,我们看一个典型的游戏画面,这个图有几个元素,首先最明显的应该是一个超人,然后右侧有一个’HUNGRY HERO’字样的图标,另外还有2个按钮(PLAY和ABOUT),最后就是底下的背景图,如图3-1所示。
在这个游戏画面中,超人和位置按钮是怎么表示呢,我们知道,地球上某一点的位置可以用经纬度来表示。同理,游戏的二维世界也可以通过这样的方式表示,具体来说,就是用x和y分别表示横向和纵向位置。有没有似曾相识的感觉,对了,这就是我们中学时代的坐标系,在cocos2d-js游戏中,画面左下角是(0,0),x从左往右递增,而Y是从上往下递增的,跟数学的二维坐标系一致。

场景组成了cocos2d世界

来到cocos2d-js世界,我们需要了解一个概念–场景。这跟我们日常生活中的场景很相似。例如,刚进入游戏的菜单界面是一个场景,游戏过程一个场景,游戏的商店也是一个场景。cocos2d-js框架把游戏拆分为很多场景,当玩家在不同界面切换的时候,框架实际上就是让游戏在不同场景中切换。
使用场景拆分游戏界面,能让游戏结构更清晰,代码更便于维护,而且有利于运行时的性能优化,从一个场景切换到另一个场景时候,cocos2d-js框架会把前者销毁,该场景包含的所有图片、文本饿的那个资源都会被清楚。
接下来我们看如何拆分场景、自定义一个场景。在新建的cocos2d-js工程中,可以找到HelloWorldScene.js,代码如下:

var helloWorldScene = cc.Scene.extend({
    onEnter:function(){
    this._super();
    var layer = new HelloWorldLayer();
    this.addChild(layer);
}
});

这段代码就定义了一个场景,其中重点是extend函数,他扩展了cc.Scene,得到HelloWorldScene.cc是一个JS对象,目的是实现跟C++的命名空间类似的作用,把各种cocos2d-js原生类型都封装在这个命名空间中。extend接受一个对象作为参数,在这里我们姑且理解extend函数就是扩张或者继承的意思。在这个继承中,我们重定义了onEnter函数,当游戏加载进入HelloWorldScene这个场景时,就会执行这个函数。在onEnter函数中,首先执行this._super()调用父类(cc.Scene)原有的onEnter.然后再添加一个名为HelloWorldLayer的子节点。

Cocos2d世界物体的祖宗–节点

既然cocos2d由一个一个的场景组成,那么场景中的物体又用什么来表示和实现呢?这一节我们要重点了解节点。因为节点是Cocos2d世界中最基础的东西,所有显示在屏幕中的东西实际上都有节点的影子。这就好比现实生活中,一切东西都可以成为物体一样。
节点封装了一些基本的操作和功能,例如缩放、坐标变化、缩放变化、透明度、可见性。而其他的显示类都继承节点,并按照各自需要扩展节点的功能,例如Layer和Sprite。
节点有一个很重要的属性–_children,正如这个名字的意思一样,表示节点的孩子。节点包含节点孩子,节点孩子又可以包含节点孩子,子子孙孙无穷尽也。这个关系就类似于一棵树。
这样的包含关系有非常重要的特性:父节点位置变化,它所包含的子节点也会跟着变化,以整体的形式移动:父节点拉伸变大,子节点也会按照一样的比例变大。除了位置和拉伸,还有一些属性会有这种整体效应,包括可见性(visible)、透明度(opaque)、旋转角度(rotation)、倾斜值(skewX和skewY)等。
#让2d世界层次化—层
节点可以包含子节点,实现一个整体的效果,而在实际开发中,我们用得更多就是层。层继承了节点,加入了更多功能,例如背景颜色。
##按层管理所有物体
层更符合我们对世界的认识,就好像百货大厦一样, 每一层销售的商品种类都不一样。相对应的,在一个典型游戏中,往往会包含一些层:背景层、任务层、道具层、系统信息层。背景层是固定的图片或地图,人物层中主角和各种可以不断移动,两层之间不互不干扰,各自管理层内的子节点。层继承节点的特点,可以在层中继续嵌套子层,例如人物层中又可以继续拆分为主角层、npc层。
使用层的方式很简单,首先新建一层:
var layer = new cc.Layer();
把层添加到舞台上:
scene.addChild(layer);
再把子节点添加到这个层上:
layer.addChild(child);
##把层扩展成各种功能的面板
Cocos2d-js提供了两个常用的Layer给我们使用:LayerColor和LayerGradient。顾名思义,LayerColor是一个纯色背景的层,利用这个类, 我们可以方便地实现纯色背景。在自定义Scene的onEnter函数中添加代码

var layerColor = new cc.LayerColor(cc.color(255,255,255),100,100);

这样就创建了一个纯白色宽和高都是100像素的背景,并添加到舞台上。其中cc.color是一个全局函数,接受0到255的R、G、B三通道的值,产生一个颜色值。如果不设置宽和高,层将默认为全屏大小。
LayerGradient是颜色渐变的层。例如以下代码可以实现一个由红色渐变为蓝色的背景:

var layerGradient = new cc.LayerGradient(cc.color(255,0,0),cc.color(0,0,255));
this.addChild(layerGradient);

除了Cocos2d-js提供固定的Layer外,我们可以自行扩展层,扩展方式跟自定义场景类似。例如我们自定义一个背景层,就可以在不同的场景中重复。在HelloWorld代码中就有HelloWorldLayer,里面的代码较为复杂,这里我们看一个简化版本,

var HelloWorldLayer = cc.Layer.extend({
ctor:function(){
this._super();
var bg = new cc.Sprite(res.HelloWorld_png);
var size = cc.director.getWinSize;
bg.x = size.width/2,
bg.y = size.height/2,
this.addChid(bg,1);
return ture;
}
});

在上述代码中,HelloWorldLayer扩展了cc.Layer,默认加载了一张图作为背景。addChild(bg,1)表示把背景添加到最底层,Layer会根据第二个参数把孩子节点做排序,数字越小越方在越低层次。
在任意一个场景中, 如果要添加同样的背景,只需要添加两句代码即可,这就是复用的好处。

var layer = new HelloWorldLayer();
this.addChild(layer);

二维直接的人物 –精灵(Sprite)

上面出现了cc.Sprite,它就是精灵。精灵也是由节点Node扩展而成的,原来的目的是用于表示游戏中的人物,但实际上,因为精灵封装了图片加载等常用功能,我们还可以用精灵来加载背景图、障碍物等非人物内容。
使用Sprite非常简单,只需要两句代码就可以完成工作。

var ball = new cc.Sprite('res/item_2.png');
  this.addChild(ball,0)

新建Sprite时,只要加载构造函数中传入图片的路劲即可。

另外,增加ball坐标大妈,让ball在屏幕中间显示:

var size = cc.director.getWinSize();
ball.x = size.width/2;
ball.y = size.height/2;

这里cc.director.getWinSize可以获得窗口的设计尺寸,也就是我们在main.js中设置的宽和高。
在游戏中,无论是主角还是NPC,还是各种飞机大炮,都可以使用精灵来加载。精灵替我们把图片处理的繁琐工作都处理好了,我们只需要设置精灵的位置和后续动作即可,不过,在HTML5游戏中,我们还需要更新resource.js,把所有图片的路径添加到数组中,这样Cocos2d-js在初始化时会预加载这些资源,最后我们才能正常新建精灵。
如果单纯加载一个图片还无法满足需求,例如飞机有血量、导弹数量等信息,我们可以扩张精灵,定义自己的类,例如代码清单中。

var Plane = cc.Sprite.extend({
 life:100,
 cotor:function(imageUrl){
 this._super(imageUrl);
 this.life = 100;
},
onHit:function(){
this.life -=20;
}
});

当飞机碰到障碍物时,我们调用Plane的onHit函数,控制飞机的生命减20.这些都是面向对象的编程方式。
另外,精灵和层一样,还可以继续嵌套其他的节点,精灵还可以包含精灵。这个嵌套机制可能在开始时难以理解,但是使用起来将非常方便。举个例子,例如游戏中飞机得到了某种加强的能力,画面上需要表现为加上一个尾翼之类的效果,我们可以选择用一张全新的图片来表现,也可以在原图片上叠加一个小的尾翼图片。第二个方案可以节省图片的大小,而且,在多个飞机都需要做一样的处理时候,这个方案将明显更加实用,这个方案的代码如下:

var plane = new cc.Sprite('res/plane.png');
this.addChild(plane);
var tail = new cc.Sprite('res/tail.png');
plane.addChild(tail);
tail.x = -50;
tail.y = 10;

把tail添加到plane上之后,需要设置tail的位置,而这个坐标并不是相对真个画面的左下角定位的,而是相对plane自身内部的左边系。

天外有天–当层和精灵嵌套时怎么设置坐标

我们在学习了cocos2d-js世界的坐标系,这个坐标系是整个游戏画面的坐标成为全局坐标,而当层和精灵不断嵌套时将产生一个新的坐标系,层添加在舞台上,这时候层的定位使用全局坐标,同时,层的内部也有一个局部坐标,而再把精灵添加到层上时,精灵将以层的局部坐标定位。

var bg = new cc.LayerColor(cc.color(100,100,100),200,200);
bg.x = 100;
bg.y = 100;
this.addChild(bg,1);

var ball = new cc.Sprite('res/item_2.png');
ball.x = 100;
ball.y = 300;

this.addChild(ball,2);
var ball2 = new cc.Sprite('res/item_3.png');
ball2.x= 100;
ball.y = 100;
bg.addChild(ball2,1);

在舞台上添加一个宽、高为200像素的灰色层,并把层移动到画面(100,100)位置,再添加一个球,放到画面(100,300)位置,最后再添加一个球,弹这个区在灰色的层上,坐标设置为(100,100)。
我们可以发现:灰色层以整个画面为参考,左下角的位置正好就是(100,100),而ball在层的左上角的位置,球的中心跟层的左上角正好重合,球心的位置相对整个画面来说,正好是(100,300),而层中间的球ball2又正好在层的中间位置,球心相对层的左下角来说位置是(100,100)。
由此我们可以总结出,每个节点都有自己内部的局部坐标系,这个局部坐标系的原点就是该节点的左下角,例如上边的灰色层,它的局部坐标系原点在全局的位置就是(100,100)。嵌套的子节点都以父节点的左下角为坐标原点,再设置自身的位置。
另外,在上述的例子中,大家可以明显的发现精灵和层不一样,同样设置坐标为(100,100),层的左下角移动到了,而精灵却是把中心点移动到(100,100)。我们这样理解:精灵加载图片做了特殊的处理,把图片忠心移动到了精灵的坐标点位置。不过,无论是层还是精灵,如果它在添加子节点,都以左下角作为子节点的局部坐标系原点,对于LayerColor来说,就是色块的左下角,而对于Sprite,则是图片的左下角。
这里顺便再介绍另一个重要的属性–锚点(anchor)。层和精灵的archorX和anchorY默认都是0.5,表示以中心作为锚点。锚点的作用是,当这个节点缩放旋转时,将以这个点作为参考点,锚点为0.5能让节点缩放和旋转时位置保持不变,

导演

我们在运行helloWorld时,就知道有cc.director.runScene,但只是见过一面,不知道这个导演为何方神圣?Cocos2d-js世界拆分为若干个场景,那么场景和场景之间的切换工作就交给导演了,顾名思义,这个跟实际拍电影是一样的,导演说,现在拍第一场,那么第一场的导演就纷纷各就各位:导演说cut,大家都停下来,导演说下一场,那么当前的演员就退出而下一场的演员就上台

场景的切换

首先,我们要记住的是cc.director.runScene,这个函数用于加载或切换场景。接着,由于我们已经有HelloWorldScene,我们再新建一个

SecondScene。如代码如下:
var SendScene = cc.Scene.extend({
onEnter : function(){
this._super();
var layer = new cc.LayerGradient(cc.color(255,0,0),cc.color(0,0,255));
this.addChild(layer);
}
});

这个场景只有一个渐变的层。
另外,再在HelloWorldScene中添加切换场景的定时器,为了方便理解和容易理解,这里的定时器使用了JS的setTimeout,控制3秒后切换为场景2,但为了原生版本兼容性。实际上我们不推荐使用这个函数,而是用Cocos2d-js提供的定时器,代码清单3-10所示,

 var HelloWorldScene = cc.Scene.extend({
onEnter:function(){
this._super();

var layer = new HelloWorldLayer();
this.addChild(layer);
setTimeout(function(){
  cc.director.runScene(new SecondScene());
})
}
})

大家运行后发现,从场景1到场景2的过程是瞬间的,没有任何过度效果,是不是觉得这样不够高大上?那我们看看Cocos2d-js给我们预备了什么高大上的套餐
首先介绍滑动切换(Slide);

cc.director.runScene(new cc.TransitionSlideInt(2,new SecondScene()));

跟原来稍有不同的是,传递给runScene的参数并不是直接的Scene,而是经过了cc.TransitionSlideInT的包装。TransitionSlideInt(2,new SecondScene())就表示让SecondScene在2秒内从上往下滑进舞台,旧场景也同时画出。
runScene会销毁旧场景上的所有内容,下次回到该场景时,所有内容都要从新建立,如果频繁切换场景,我们可以使用pushScene和popScene。pushScene跟runScene用法一样,但不销毁场景内容,而是把上一个场景存起来,popScene则把当前场景销毁,然后快速回到上一个场景中,这时候我们只需要重新唤起原来的内容,并不需要重新建立。这对方法特别适合一些游戏设置界面,例如游戏主界面切换到音频界面设置界面,由于玩家设置完音效后马上切换回到游戏界面,所以这里把游戏界面销毁再重建会浪费效率。

导演可以提供的信息

导演这个老大不是只懂得只会就可以当的,它必须还有求必应,懂得如何解决拍摄中的各种问题。在Cocos2d-js中,导演cc.director可以为我们提供很多信息或功能,这里笔者列出一些常用的功能:
窗口的设计尺寸:getWinSize;
窗口的实际尺寸:getVisibleSize;这个信息在适配手机屏幕的时候十分重要
获取全局的定时器:getScheduler;
暂停/恢复场景:pause/resume

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值