Cocos2d-JS 实现X轴自定义视窗跟随

来源:互联网 发布:js body append html 编辑:程序博客网 时间:2024/06/01 07:35

环境:

win7 64位

Cocos2d-JS v3.1

Cocos Code IDE v1.0.0.Final


自带的跟随函数cc.follow,跟随的时候总是以目标为中心位置,而且在非移动范围边缘进行移动时,目标只要稍微进行移动,也会触发跟随动作。


本文没有在web上进行测试、改善


本文以X轴为例,目标移动时超出某个范围才进行跟随动作,如图:



本文只是经验分享,Y轴的跟随本文没有实现,而且响应的事件为键盘上的左右方向。另外,程序写的比较随意,显得略搓.......大家别在意....

另外这里的视窗这个hellowWorldLayer,也就是这个helloWorld层,是一个层,不是opengl的视口,写的时候没注意让大家混乱了不好意思


(其实也就是层的移动跟随,本文的视窗不是指opengl的视口,让层进行移动来达到看起来是视窗跟随的效果)


最终效果如下(暂时没有黑线):

上传的时候不知道为何变成JPG格式了= =那就传到相册空间好了,这里放一个链接吧...:

http://my.csdn.net/my/album/detail/1795641


背景图片用的是Tiled制作的地图,本文最后会提供资源,瓦片地图的大小为:



制作该地图的过程就省略了,网上好多教程。



正文:

1.新建一个工程,本文的工程为横向,分辨率为960*640,不过显示的是main.js里800*450的默认分辨率。把app.js里用不上的删掉,剩下这些:

var HelloWorldLayer = cc.Layer.extend({    ctor:function () {        this._super();        var size = cc.winSize;        return true;    }});var HelloWorldScene = cc.Scene.extend({    onEnter:function () {        this._super();        var layer = new HelloWorldLayer();        this.addChild(layer);    }});

2.把.tmx地图文件和对应的.png文件放到res文件夹下。接着修改tmx文件引用的图片路径为相对路径,改路径这里介绍两种方法。


方法一:把引用图片路径改为引用的图片名字(加上格式)。


然后在main.js里面添加res路径到文件搜索,添加后整个文件代码为:

cc.game.onStart = function(){    cc.view.adjustViewPort(true);    cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);    cc.view.resizeWithBrowserSize(true);    var searchPaths = jsb.fileUtils.getSearchPaths();    var paths = [                 'res'                 ];    for (var i = 0; i < paths.length; i++) {        searchPaths.push(paths[i]);    }    jsb.fileUtils.setSearchPaths(searchPaths);    //load resources    cc.LoaderScene.preload(g_resources, function () {        cc.director.runScene(new HelloWorldScene());    }, this);};cc.game.run();

方法二:直接在路径写上res/  :

 <image source="res/tmw_desert_spacing.png"

3.加载地图,在app.js的HelloWorldLayer构造函数ctor里添加如下代码:

var tiled = new cc.TMXTiledMap("res/test1111.tmx");//直接加载路径下tmx文件        this.addChild(tiled);

4.添加目标,这里用helloworld图片,先在HelloWorldLayer里面添加一个变量:

hero:null,

然后在第三步后面继续添加:

this.hero = new cc.Sprite("res/HelloWorld.png");        this.hero.scale = 0.2;//因为原图片太大,于是缩小为原大小的20%        this.hero.setPosition(size.width * 0.5, size.height * 0.3);        this.addChild(this.hero, 1);

5.添加键盘事件和目标左右移动:

(关于更多的事件,请参考这个:http://www.cocos2d-x.org/docs/manual/framework/html5/v3/eventManager/en,或者其他demo、资料。)

HelloWorldLayer里添加以下变量,用作判断左移或者右移、目标缩放后的宽度:

isLeft:null,    isRight:null,    heroWidth:null,

再添一个目标移动速度:(好吧我表示直接初始化更加省事了)

heroSpeed:5,

在ctor里初始化:

 this.isLeft = false;        this.isRight = false;

注意在创建hero后才赋值,经过测试,在缩放后获取的宽度为不缩放时大小,所以要乘以0.2:

this.heroWidth = this.hero._getWidth() * 0.2;

在第四步后添加事件监听和处理:
cc.eventManager.addListener({            event: cc.EventListener.KEYBOARD,//事件为键盘按键            onKeyPressed:  function(keyCode, event){                var helloWorldLayer = event.getCurrentTarget();//获取当前操作的作用域,与最后一行的那个参数有关,this则为该HelloWorldLayer。                cc.log("Key " + keyCode.toString() + " was pressed!");//测试出按下的键的编号,额,,,没去找按键对应编号的表                if (keyCode == 23) {//左方向                    cc.log("left");                    helloWorldLayer.isLeft = true;                    helloWorldLayer.isRight = false;                }                if (keyCode == 24) {//右方向                    cc.log("right");                    helloWorldLayer.isRight = true;                    helloWorldLayer.isLeft = false;                }            },            onKeyReleased: function(keyCode, event){                //var label = event.getCurrentTarget();                var helloWorldLayer = event.getCurrentTarget();                if (keyCode == 23)                    helloWorldLayer.isLeft = false;                if (keyCode == 24)                    helloWorldLayer.isRight = false;                cc.log("Key " + keyCode.toString() + " was released!");            }        }, this);

这里特别说明一下:为了在按下一个方向时,按下另一个方向,目标会立刻转向运动,所以在键盘按下时除了要将需要前进的方向值为true,另一个方向同时置为false,实现的方法有好多种,大家不要在意本文的写法......


接着开启update,在update里面更新目标的位置。在后面继续添加:

this.scheduleUpdate();
然后再在HelloWorldLayer里重写update方法(注意在ctor的最后加逗号,):
update : function(dt) {        var pos = this.hero.getPositionX();        if (this.isLeft && !this.isRight) {            if ((pos - this.heroWidth *0.5) > 0  ) //超出左边移动边界就不让目标左移                this.hero.setPositionX(pos - this.heroSpeed);        }        if (this.isRight && !this.isLeft) {            if ( (this.mapLength ) > (pos + this.heroWidth)) //超出右边界就不让目标右移                this.hero.setPositionX(pos + this.heroSpeed);        }    },

如无意外目标可以左右移动了。

注意,按键事件和update在每一帧的处理顺序:先处理按键,再到update。


5.5这里说一下cc.Follow

用new创建的话,控制台会报这样的问题jsb_create_apis.js:437:ReferenceError: ret is not defined

改为.ctreate创建就好了

接着不想做自定义视窗跟随函数的同学,直接添加以下代码即可完事:

        var followAction = cc.Follow.create(this.hero, cc.rect(0, 0, 3800, size.height));        this.runAction(followAction);

若有黑线问题第七步有讲


6.添加自定义的视窗跟随函数(也就是层的移动跟随,本文的视窗不是指opengl的视口,让层进行移动来达到看起来是视窗跟随的效果):

先交代一下cc.Follow,这个动作创建的时候最好用.create方法,用new的话


先在HelloWorldLayer里添加一个变量,用来表示视窗移动的状态,本人比较懒,用一个整形表示,0为不动,1为左移,2为右移,理论上用枚举类型会比较符合规范= =...

 cameraStatue:null,

再加个变量保存地图的宽度

 mapLength:null,

接着在ctor初始化为0:

this.cameraStatue = 0;

在创建tiled(地图)后保存他们的宽度

this.mapLength = tiled.getContentSize().width;

然后在HelloWorldLayer里添加自定义的视窗跟随函数:
setCamera : function(targetPositionX, offsetLeft, offsetRight, windowWidthHalf,tiledW) {        var cameraX = this.getPositionX();//获取视窗的X坐标        var isOut = Math.abs(targetPositionX - (-cameraX));//得出视窗与目标X轴上的距离        var ofx = windowWidthHalf - 105;//实际上减去的值为80+25,下面介绍如何得出        if (targetPositionX >  (ofx) && (targetPositionX < (tiledW - windowWidthHalf +25))) {//视窗移动范围判定            if (isOut < offsetLeft) {//视窗是否移动判定                this.cameraStatue = 1;            }            if (isOut  >= offsetLeft && isOut <= offsetRight) {//视窗是否移动判定                this.cameraStatue = 0;            }            if (isOut > offsetRight) {                this.cameraStatue = 2;            }            if (this.cameraStatue == 1) {                this.setPositionX(cameraX + this.cameraSpeed);            }            if (this.cameraStatue == 2) {                this.setPositionX(cameraX - this.cameraSpeed);            }        }    }
接下来讲解一下这个函数(锚点默认):

参数targetPositionX为需要跟随目标的X坐标,

参数offsetLeft为左边移动边界,也就是目标和视窗X轴上的相对位置,

参数offsetRight为右边移动边界,也就是目标和视窗X轴上的相对位置,
参数windowWidthHalf为视窗宽度的一半,

参数tiledW为地图的宽度。


以下所有的分析都是以锚点为默认的情况下(Layer默认锚点是(0,0),里面的node默认(0.5,0.5)),而且main.js里显示的画布为:800*450,所以cc.winSize的宽度为800。(大家可以用cc.log打印cc.winSize.width看看)

相机起始X坐标:0,

目标起始X坐标400,(在屏幕宽度(800)一半(400),恩,应该是这样算的)


var isOut = Math.abs(targetPositionX - (-cameraX));
这句的作用是取得跟随的目标和视窗X轴上的差值,这里视窗,也就是cameraX值取反,是因为视窗往右移,X轴的坐标是越来越小的,也就是和目标的方向相反,X轴的原点一致,大家可以自己输出视窗的X轴坐标观察结果。这里把最后的值取绝对值,在本文中其实加不加绝对值也没问题,因为目标都是在X大于0的时候进行移动,取绝对值是为了应对在目标移动到X轴负坐标的时候,但是本文没有这种情况= =...

var ofx = windowWidthHalf - 105;

这句等号右边完整的表达式是:windowWidthHalf - 80 -25,减去80是为了补偿图片一半的宽度,因为锚点为目标中点,所以要补回左边边缘到中点的长度。然后这里的25为人工调试测出的结果(猜测跟分辨率从960*640的3:2变到800*450的16:9有关),不然会出现视窗移动后再移回来,回不到视窗原点。

(上传后不知为何变成JPG格式= =那就传到相册空间,放个链接吧)

http://my.csdn.net/my/album/detail/1795643


if (targetPositionX >  (ofx) && (targetPositionX < (tiledW -windowWidthHalf +25))) {

再次强调目标右移的X坐标为增量,而视窗右移为减量。大家可以自己输出看看结果

这句为视窗可以移动的范围,由于判断是根据目标的X轴,所以向右为正。视窗不能移到地图外面,所以左边界不是0,而是视窗宽度的一半,加上目标锚点和测试时出现的偏移影响,所以左边界为上一句表达式所示;右边界同理。


之后就是判断目标和视窗的相对距离有没有超出预设的左右值,由于视窗X轴向右为负,所以向左移动时加上移动速度,向右移动时减去移动速度。


最后在update里的最后调用这个函数:

this.setCamera(pos, 300, 500, cc.winSize.width * 0.5, this.mapLength);



7.去除移动时出现的黑线

上面的动态图最后会出现这样的黑线:



在本博客的另一篇文章讲解了如何修复黑线的问题:http://blog.csdn.net/et_sandy/article/details/41446099



8.源码和资源

main.js:

cc.game.onStart = function(){    cc.view.adjustViewPort(true);    cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);    cc.view.resizeWithBrowserSize(true);    var searchPaths = jsb.fileUtils.getSearchPaths();    var paths = [                 'res'                 ];    for (var i = 0; i < paths.length; i++) {        searchPaths.push(paths[i]);    }    jsb.fileUtils.setSearchPaths(searchPaths);    //load resources    cc.LoaderScene.preload(g_resources, function () {        cc.director.runScene(new HelloWorldScene());    }, this);};cc.game.run();

app.js:

var HelloWorldLayer = cc.Layer.extend({    isLeft:null,    isRight:null,    hero:null,    cameraStatue:null,    mapLength:null,    heroWidth:null,    heroSpeed:20,    cameraSpeed:20,    ctor:function () {        this._super();        var size = cc.winSize;        this.isLeft = false;        this.isRight = false;        this.cameraStatue = 0;                var tiled = new cc.TMXTiledMap("res/test1111.tmx");        this.addChild(tiled);        this.hero = new cc.Sprite("res/HelloWorld.png");        this.hero.scale = 0.2;        this.hero.setPosition(size.width * 0.5, size.height * 0.3);        this.addChild(this.hero, 1);        this.mapLength = tiled.getContentSize().width;        this.heroWidth = this.hero._getWidth() * 0.2;        cc.log(this.hero._getWidth()*0.2*0.5);                cc.eventManager.addListener({            event: cc.EventListener.KEYBOARD,            onKeyPressed:  function(keyCode, event){                var helloWorldLayer = event.getCurrentTarget();                var pos = helloWorldLayer.hero.getPositionX();                cc.log("Key " + keyCode.toString() + " was pressed!");                if (keyCode == 23) {                    cc.log("left");                    helloWorldLayer.isLeft = true;                    helloWorldLayer.isRight = false;                }                if (keyCode == 24) {                    cc.log("right");                    helloWorldLayer.isRight = true;                    helloWorldLayer.isLeft = false;                }            },            onKeyReleased: function(keyCode, event){                //var label = event.getCurrentTarget();                var helloWorldLayer = event.getCurrentTarget();                if (keyCode == 23)                    helloWorldLayer.isLeft = false;                if (keyCode == 24)                    helloWorldLayer.isRight = false;                cc.log("Key " + keyCode.toString() + " was released!");            }        }, this);                this.scheduleUpdate();        return true;    },    update : function(dt) {        var pos = this.hero.getPositionX();        if (this.isLeft && !this.isRight) {            if ((pos - this.heroWidth *0.5) > 0  )                 this.hero.setPositionX(pos - this.heroSpeed);        }        if (this.isRight && !this.isLeft) {            if ( (this.mapLength ) > (pos + this.heroWidth))                 this.hero.setPositionX(pos + this.heroSpeed);        }                this.setCamera(pos, 300, 500, cc.winSize.width * 0.5, this.mapLength);    },    setCamera : function(targetPositionX, offsetLeft, offsetRight, windowWidthHalf,tiledW) {        var cameraX = this.getPositionX();        var isOut = Math.abs(targetPositionX - (-cameraX));        var ofx = windowWidthHalf - 105;        if (targetPositionX >  (ofx) && (targetPositionX < (tiledW -windowWidthHalf +25))) {            if (isOut < offsetLeft) {                this.cameraStatue = 1;            }            if (isOut  >= offsetLeft && isOut <= offsetRight) {                this.cameraStatue = 0;            }            if (isOut > offsetRight) {                this.cameraStatue = 2;            }            if (this.cameraStatue == 1) {                this.setPositionX(cameraX + this.cameraSpeed);            }            if (this.cameraStatue == 2) {                this.setPositionX(cameraX - this.cameraSpeed);            }        }    }});var HelloWorldScene = cc.Scene.extend({    onEnter:function () {        this._super();        var layer = new HelloWorldLayer();        this.addChild(layer);    }});

用到的tmx和tmx对应的png文件:http://download.csdn.net/detail/et_sandy/8164981



0 0
原创粉丝点击