加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-14 19:27:23 所属栏目:百科 来源:网络整理
导读:环境: win7 64位 Cocos2d-JS v3.1 Cocos Code IDE v1.0.0.Final 自带的跟随函数cc.follow,跟随的时候总是以目标为中心位置,而且在非移动范围边缘进行移动时,目标只要稍微进行移动,也会触发跟随动作。 本文 没有在web上进行测试、改善 本文以X轴为例,目

环境:

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,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://www.52php.cn/article/p-ackiqksi-qm.html



8.源码和资源

main.js:

cc.game.onStart = function(){
    cc.view.adjustViewPort(true);
    cc.view.setDesignResolutionSize(800,this);
};
cc.game.run();

app.js:

var HelloWorldLayer = cc.Layer.extend({
    isLeft:null,hero:null,cameraStatue:null,mapLength: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,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;
                }
            },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,this.mapLength);
    },setCamera : function(targetPositionX,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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读