Cocos Creator_实现策略_多点触控之触控管理器监听单例_TypeScript

触控管理器(TypeScript实现) 

【主要功能】

1.实时访问每个触摸手指的状态(手指顺序,touch位置,按钮状态,Touch类)

2.使鼠标与触摸的API兼容访问

3.实时访问键盘按钮状态

4.增加基础数据结构 List Dictionary Statck Queue

 

开头说一下我也是最近才开始接触CocosCreator,所以有哪些错误望指出。

完成版我会放到GitHub.

CocosCreator在使用的过程中对事件的监听事件方式我用着很别扭所以才特意的早了个轮子,起始就是简单的对其内部API进行了加工而已,大佬就不用看了-_-。

  CocosCreator的触摸互交监听事件需要先绑定到指定的Node上,所以想由单例调用的话就必须绑定到一个合适的Node上。而且这个Node的触摸面积必须是最大的!

  那显而易见的就是Canvas了或者Camera,Camera我没试过你感兴趣的话可以试试。首先Canva符合我们的需要啊,真是刚需。

先看下最终效果20多秒的Gif:包括了TouchFinger的 手指顺序记录、点击、滑动、弹起、失效( | 与 1 不一样哦在图里, | 代表无触摸)

  接下来就开始制定实现策略,不过你必须先了解一下CocosCreator的内置触摸API,才能进行加工啊!!!

  对了顺便一提键盘的实现策略我会顺便一提的。

1.EventType

cc.Node.EventType    
包含着触控与鼠标的事件,至于为什么没有键盘的呢?因为触摸与鼠标的监听事件需要实体(Node)啊!
而键盘并不需要特定实体,Cocos早已在内部留有了一个系统事件单例,来方便全局使用,所以键盘就
不要绑定特定了实体了直接绑这个全局的就行(systemEvent)向深入理解的看官方的“节点系统事件”就好。
看看源码你就清楚了,在这暂不详述,存在即合理^_^。

【看源码理解一下吧】

cocos2d/core/event/system-event.js:198

2.枚举对象定义

枚举对象定义对应的事件名事件触发的时机
cc.Node.EventType.TOUCH_START自定义当手指触点落在目标节点区域内时
cc.Node.EventType.TOUCH_MOVE自定义当手指在屏幕上目标节点区域内移动时
cc.Node.EventType.TOUCH_END自定义当手指在目标节点区域内离开屏幕时
cc.Node.EventType.TOUCH_CANCEL自定义当手指在目标节点区域外离开屏幕时

3.Touch 类型

  Touch类封装了触摸手指的具体信息

  • getLocation 获取当前触点位置。
  • getLocationX 获取当前触点 X 轴位置。
  • getLocationY 获取当前触点 Y 轴位置。
  • getPreviousLocation 获取触点在上一次事件时的位置对象,对象包含 x 和 y 属性。
  • getStartLocation 获获取触点落下时的位置对象,对象包含 x 和 y 属性。
  • getDelta 获取触点距离上一次事件移动的距离对象,对象包含 x 和 y 属性。
  • getLocationInView 获取当前事件在游戏窗口内的坐标位置对象,对象包含 x 和 y 属性。
  • getPreviousLocationInView 获取触点在上一次事件时在游戏窗口中的位置对象,对象包含 x 和 y 属性。
  • getStartLocationInView 获取触点落下时在游戏窗口中的位置对象,对象包含 x 和 y 属性。
  • getID 触点的标识 ID,可以用来在多点触摸中跟踪触点。
  • setTouchInfo 设置触摸相关的信息。

4. cc.Event.EventTouch

  触摸事件绑定的回调参数类型,里面包含着触摸事件的当前触摸状态的所有信息

cocos2d/core/event/event.js:202

   

[Tip]
    Cocos的触摸事件的touch信息是以栈形式存储

【主体策略】

1.为主Canvas绑定枚举对象,的回调方法

        this.node.on(cc.Node.EventType.TOUCH_START, this.touchBtnDownMonitor);
        this.node.on(cc.Node.EventType.TOUCH_END, this.touchBtnUpMonitor);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.touchBtnDisMonitor);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this.touchMoveMonitor);

2.静态访问触摸手指的Touch信息,以及触摸的状态:按下、移动、弹起(失效)

       

const ButtonStatu= cc.Enum({ Up: 0 , Down: 1, Dis:2,Move:3})

       定义按键状态:ButtonStatu[],并一开始初始化为10(cocos指出8点触控,具体可自行设置)

3.对触摸手指数据进行数组存储(在这里我并不想用List、Dictionary等数据结构(需自行实现))

     

 touchs : cc.Touch[];    //存储Touch信息
 touchStatu : ButtonStatu[];    //存储状态信息

       根据枚举对象在绑定触摸事件后,访问的绑定枚举对象的回调方法时对数据记录

3.根据CocosCreator API 将触摸手机数据进行保留与更新.

    需要两个共用函数,

        a.手指加入   --touchFingetMonitor

private touchFingetMonitor(event :cc.Event.EventTouch){

        //手指数组为空则直接进行保存,否则将进行数据更新
        if(this.touchs){
            
            //初始化变量
            let temp : cc.Touch[] = [];        //最终的手指数组信息
            let havThis :Boolean= false;       //当前手指是否已经被保存过
            let havThisID :number= -1;         //当前手指ID,为-1则当前手指未被保留

            //检测当前手指是否已经被保存过,未保存则记录其ID
            for(let i=0;i<this.touchs.length;i++){
                temp[i] = this.touchs[i];

                if(!havThis && temp[i] == event.touch){
                    havThis = true;
                }
                
                //处理getTouches()中不区分,触摸手指的前后信息,即第几根手指触摸其该手指排序就是第次(例如有三手指按顺序触摸A,B,C,B一直按下弹起但其保存位置一直是第二位)
                if(havThisID == -1 && ((temp[i] == null && event.touch.getID() == i) ||temp[i].getID() == event.touch.getID())){
                    havThisID = i;
                }
            }
            if(!havThis){
                if(havThisID != -1){
                    temp[havThisID] = event.touch;
                }
                else{

                    if(event.touch.getID() < this.touchs.length){

                        for(let i=this.touchs.length;i>event.touch.getID();i--){
                            temp[i] = temp[i-1];
                        }
                        temp[event.touch.getID()] = event.touch;
                    }
                    else{
                        temp[this.touchs.length] = event.touch;
                    }
                }
            }
            
            this.touchs = temp;
        }
        else{
            this.touchs = [event.touch];
        }
    }

        b手指减去(CocosCreator默认用的是Stack),我觉得不怎么好使.   --touchFingetOutMonitor

private touchFingetOutMonitor(event :cc.Event.EventTouch){

        let touchID = event.touch.getID();

        //只有一根手指就直接清除数据,更新数据
        if(this.touchs.length == 1){
            
            this.touchs = [];
        }
        else{

            let temp : cc.Touch[] = [];
            let thisindex :number= -1;
            for(let i=0;i<this.touchs.length;i++){
                if(this.touchs[i].getID() == event.touch.getID()){
                    thisindex = i;
                    break;
                }
            }
            let i=0;
            for(i ;i<thisindex;i++){
                temp[i] = this.touchs[i];
            }
            for(i ;i<this.touchs.length-1 ;i++){
                temp[i] = this.touchs[i+1];
            }
            this.touchs = temp;
        }
    }

4.数据处理

    private touchBtnDownMonitor(event :cc.Event.EventTouch){
        
        this.touchFingetMonitor(event);
        this.touchStatu[event.touch.getID()] = BtnStatu.Down;
    }
    private touchMoveMonitor(event :cc.Event.EventTouch){

        this.touchFingetMonitor(event);
        this.touchStatu[event.touch.getID()] = BtnStatu.Move;
    }
    private touchBtnUpMonitor(event :cc.Event.EventTouch){

        this.touchStatu[event.touch.getID()] = BtnStatu.Up;
        this.touchFingetOutMonitor(event);
    }
    
    private touchBtnDisMonitor(event :cc.Event.EventTouch){

        this.touchStatu[event.touch.getID()] = BtnStatu.Dis;
        this.touchFingetOutMonitor(event);
    }

    这四个方法在响应时传入的EventTouch信息,会时刻的进行更新,所以需要时刻的将其中的数据进行记录。

而touchBtnDownMonitor 和 touchBtnUpMonitor 只需要处理 手指加入,touchBtnDisMonitor 和 touchMoveMonitor只需要处理 手指减去即可(所以只能实现三种状态:按下、弹起、移动),可自行设计。

5.测试处理

  顺便建个脚本就能用,这里我用到了数据结构比较方便


    finger : Dictionary<number,cc.Node>;

    if(InputMonitor.Instance().Touchs() != null){

            for(let i=0;i<InputMonitor.Instance().Touchs().length;i++){

                if(!this.finger.ContainKey(id)){
                    
                    let obj:cc.Node = cc.instantiate(this.fingerPrefab);
                    obj.parent = cc.director.getScene();
                    obj.position = InputMonitor.Instance().Touchs()[i].getLocation();
                    obj.getComponentInChildren(cc.Label).string = id.toString();
                    this.finger.Add(id,obj);
                }
                else{
                    this.finger.Item(id).position = InputMonitor.Instance().Touchs()[i].getLocation();
                }
            }
        }
        
        let del : List<number> = new List<number>();

        for(let j=0;j<this.finger.Count();j++){

            let id : number = this.finger.ItemKeyofIndex(j);
            if(!InputMonitor.Instance().ContainID(id)){
                del.Add(id);
            }
        }
        for(let j =0;j<del.Count();j++){

            this.finger.Item(del.Item(j)).destroy();
            this.finger.Remove(del.Item(j));
        }

第二次更新

这次支持了鼠标显示世界位置

提要:

  在cocosCreate中的要获取鼠标的世界位置,就必须是在使用两台摄像机的情况下(一台的话你还费什么劲^_^)。

    a.一台是UI的相机

    b.一台是世界相机

  当然这时的世界原点还是以Canvas的(0,0)点为准,鼠标的世界位置也是以此为原点计算。

  世界相机有时会跟随玩家而移动(UI相机当然不可能,不然你的UI元素还要不要了)

[Tip]
    新手可能会将UI相机与世界相机搞混。
    所以我在这阐述一下,首先Canvas只是画布并不会将UI图像显示出来,所以需要一个特定的相机来获取UI(Canvas中的元素)图像,cocosCreate在你的默认场景的Canvas中的MainCamera相机就是的,但他默认是世界元素与UI元素都会显示。
    世界元素就是不以Canvas为根节点,UI元素才是(这里只是我的个人区分),这样区分要实现就需要自己定义Group了。
    所以我们通常用一个或多个世界相机只显示世界元素,一个UI相机只显示UI元素,来实现UI永远显示在世界元素之上.(可能cocosCreate的3D成熟了自然就是了)

【警惕点】

1.屏幕的分辨率变化

2.世界位置的与触点位置的转换

【实现策略】

1.获取触点的屏幕位置

2.获取屏幕的实际尺寸

3.将触点位置通过屏幕实际尺寸转换为实际的屏幕位置(触点的屏幕位置只会根据canvas的大小来计算,子啊屏幕拉伸后回不准确,所以需要再次转换为实际的屏幕位置)

4.根据世界相机将实际的屏幕尺寸转换为世界位置

【实现方法】

1.定义参数


    @property(cc.Node)
    worldCamera : cc.Node= null;    //世界相机

    MousePosition() :cc.Vec2        //鼠标屏幕位置 --不是实际位置

    MouseToWorldPosition() :cc.Vec2 //鼠标的世界位置
    

2.获取屏幕的实际尺寸

cc.view.setResizeCallback(this.initScreenInfo);

//初始化屏幕参数    --启动时调用或者屏幕大小改变时调用
initScreenInfo(){

    InputMonitor.Instance().visibleSize = new cc.Vec2(cc.view.getVisibleSize().width/2 ,cc.view.getVisibleSize().height/2); 
}

2.将触点位置通过屏幕实际尺寸转换为世界位置

        if(this.worldCamera != null){
            
            this.mouseWorldPosition.x = this.worldCamera.position.x - this.visibleSize.x +                 this.mousePosition.x;
            this.mouseWorldPosition.y = this.worldCamera.position.y - this.visibleSize.y + this.mousePosition.y;
            return this.mouseWorldPosition;

        }else{
            return this.mousePosition;
        }

第三次更新

这次支持了实时访问键盘的响应事件

1.调用接口范例

    update(){
        if(InputMonitor.Instance().GetKeyBeginDown(cc.macro.KEY.a)){

            if(this.node.width > 0)
                this.node.width = -this.node.width;
            this.anim.play("run",0);
        }
        if(InputMonitor.Instance().GetKeyBeginDown(cc.macro.KEY.d)){

            if(this.node.width < 0)
                this.node.width = -this.node.width;
            this.anim.play("run",0);
        }
        if(InputMonitor.Instance().GetKeyBeginDown(cc.macro.KEY.w)){

            this.anim.play("jump",0);
        }
        if(InputMonitor.Instance().GetKeyBeginDown(cc.macro.KEY.s)){

            this.anim.play("dying",0);
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值