cocos creator Touch事件应用(触控选择多个子节点)

最近参与了cocos creator的研究,开发小游戏,结果被一个事件坑得不行不行的。现在终于解决了,分享给大家。

原理

1.触控事件是针对节点的

2.触控事件的冒泡,是直接关系冒泡,父子可以,孙子不行,就是不能隔代冒泡

3.父节点不响应触控事件,肯定是被孩子节点遮挡了,只要孩子节点也监听一下事件,父节点就可以响应了

4.触控位置是绝对坐标,相对于整个canvas,节点位置相对于父节点,相对位置可以与绝对坐标相互转化

5.节点是否被触控到,touch start事件可以肯定被触摸到,但是一个节点触摸到必须等待其结束,另一个节点才能响应touch事件

6.判断是否框选中,根据坐标计算相互交叉即是选中。就是说我从触控起点->触控终点 构成的矩形区域,与节点的矩形存在重叠,就是被框选。本例中,采用比较粗略的算法实现,根据横坐标的范围是否包含子节点的横坐标判断是否选中。

7.计算某个数值是否在某一范围内,首先计算出范围的最大值、最小值,然后作比较即可。

核心代码

cc.Class({
    extends: cc.Component,

    properties: {
        // foo: {
        //    default: null,      // The default value will be used only when the component attaching
        //                           to a node for the first time
        //    url: cc.Texture2D,  // optional, default is typeof default
        //    serializable: true, // optional, default is true
        //    visible: true,      // optional, default is true
        //    displayName: ‘Foo‘, // optional
        //    readonly: false,    // optional, default is false
        // },
        // ...
            poker:{
                default:null,
                type:cc.Node
            },
            cardMask:{
                default:null,
                type: cc.Prefab
            }
    },

    // use this for initialization
    onLoad: function () {

            //牌
            this.cards = this.poker.children;

            //牌初始位置
            this.cardInitY = this.cards[0].y;

            //触摸选择到的牌
            this.touchedCards = [];

            //选中的牌
            this.selectedCards = [];

            console.info(this.cards);
        },

        start: function () {
            // this.cards = this.poker.children;
            // console.info(this.cards);

            this.addTouchEvent();
        },

        /**
         * 添加事件
         */
        addTouchEvent:function(){

            //父节点监听touch事件(直接子节点必须注册同样的事件方能触发)
            this.poker.on(cc.Node.EventType.TOUCH_START, function (event) {
                console.log(‘poker TOUCH_START‘);

                //牌
                var card = event.target;

                //起始触摸位置(和第一张card一样,相对于poker的位置)
                this.touchStartLocation = this.cards[0].convertTouchToNodeSpace(event);
                console.log(‘touch start Location:‘+ JSON.stringify(this.touchStartLocation));

                //计算牌位置
                var index = 0;
                for(var i=0;i<this.cards.length;i++){
                    var c = this.cards[i];
                  if(c.name == card.name){
                        index = i;
                        break;
                    }
                }

                //暂存第一次触摸到的牌
                var touchedCard = {
                    index:index,
                    card:card
                };
                this.firstTouchedCard = touchedCard;
                //暂存
                this.pushTouchedCards(touchedCard.index,touchedCard.card);

            }, this);

            //父节点监听touch事件(直接子节点必须注册同样的事件方能触发)
            this.poker.on(cc.Node.EventType.TOUCH_MOVE, function (event) {
                console.log(‘poker TOUCH_MOVE‘);
                //先清除原先触摸到的牌
                this.clearTouchedCards();
                //保存第一张牌
                this.pushTouchedCards(this.firstTouchedCard.index,this.firstTouchedCard.card);

                //触摸点转换为card节点坐标
                var nodeLocation = this.cards[0].convertTouchToNodeSpace(event);
                console.log(‘touch nodeLocation:‘+ JSON.stringify(nodeLocation));
                var x = nodeLocation.x;
                var y = nodeLocation.y; 

                //找到当前选中的牌
                var currentCard = null;
                for(var i=0;i< this.cards.length;i++){
                    var card = this.cards[i];
                    var cardX = card.x;
                    var cardY = card.y;
                    console.log(‘card x=‘+cardX+‘,y=‘+cardY);

                    //某张牌范围包括了鼠标位置,选中此牌与触摸开头的所有牌
                    var cardWidth = i==5 ? card.width:19;
                    var cardHeight = card.height;
                    if(cardX<=x && x <= cardX+cardWidth && cardY<=y && y<= cardY+cardHeight){
                        currentCard = card;

                        //暂存触摸到的牌
                        this.pushTouchedCards(i,card);

                        break;
                    }
                }

                //添加开头与此牌直接的所有牌
                var startTouchLocation = this.touchStartLocation;
                for(var i=0;i< this.cards.length;i++){
                    var card = this.cards[i];
                    var cardX = card.x;
                    //框选的范围包括了的牌
                    var min,max;
                    if(startTouchLocation.x < nodeLocation.x){
                        min = startTouchLocation.x;
                        max = nodeLocation.x;
                    }else{
                        min = nodeLocation.x;
                        max = startTouchLocation.x;
                    }
                    console.log(‘min=‘+min+‘, max=‘+max);

                    if(min <= cardX && cardX <= max){
                        //暂存触摸到的牌
                        this.pushTouchedCards(i,card);
                    }
                }

            }, this);

        //父节点监听touch事件(直接子节点必须注册同样的事件方能触发)
        this.poker.on(cc.Node.EventType.TOUCH_END, function (event) {
            console.log(‘poker TOUCH_END‘);
            this.doSelectCard();
        }, this);

        //父节点监听touch事件(直接子节点必须注册同样的事件方能触发)
        this.poker.on(cc.Node.EventType.TOUCH_CANCEL, function (event) {
            console.log(‘poker TOUCH_CANCEL‘);
            this.doSelectCard();
        }, this);

        //给所有的牌注册事件,会自动冒泡到poker节点
        for(var i=0;i< this.cards.length;i++){
            var cards = this.cards;
            //闭包传递i值
            (function(i){
                var card = cards[i];
                card.on(cc.Node.EventType.TOUCH_START, function (event) {
                    console.log(‘card TOUCH_START‘);
                }, card);

                card.on(cc.Node.EventType.TOUCH_MOVE, function (event) {
                    console.log(‘card TOUCH_MOVE‘);
                }, card);

                card.on(cc.Node.EventType.TOUCH_END, function (event) {
                    console.log(‘card TOUCH_END‘);
                }, card);

                card.on(cc.Node.EventType.TOUCH_CANCEL, function (event) {
                    console.log(‘card TOUCH_CANCEL‘);
                }, card);

            })(i)

        }

    },

    /**
     * 暂存触摸到的牌
     */
    pushTouchedCards:function(index,card){
        //构造牌对象
        var cardObj = {
            index:index,
            name:card.name,
            isSelected:card.y==this.cardInitY?false:true //高度不一样,表示选中
        };

        //防止重复添加
        var existCard = this.touchedCards.find(function(obj){
            if(obj.name == card.name){
                return obj;
            }else{
                return null;
            }
        });
        if(!existCard){
            //添加暂存
            this.touchedCards.push(cardObj);

            //包含提示
            this.addCardMask(card);
        }
    },

    /**
     * 清除原先暂存的触摸到的牌
     */
    clearTouchedCards:function(){
        for(var i=0;i<this.touchedCards.length;i++){
            var cardIndex = this.touchedCards[i].index;
            var card = this.cards[cardIndex];
            card.removeChild(card.children[0]);
        }
        this.touchedCards = [];
    },

    /**
     * 选择牌
     */
    doSelectCard:function(){
        this.selectedCards = [];

        console.log(this.touchedCards);

        //改变牌状态
        for(var i = 0; i< this.touchedCards.length;i++){
            var cardObj = this.touchedCards[i];
            var card = this.cards[cardObj.index];
            if(cardObj.isSelected){ //如果是选中改为不选中
                card.y = card.y - 30;
            }else{ //不选中改为选中状态
                card.y = card.y + 30;
            }
        }

        //重置
        this.clearTouchedCards();

        //显示选中的牌
        this.showSelectedCards();
    },

    /**
     * 包含牌遮罩
     */
    addCardMask:function(card){
        var cardMask = cc.instantiate(this.cardMask);
        cardMask.setPosition(cc.p(0, 0));
        card.addChild(cardMask);
     },

    /**
    * 显示选中的牌
    */
    showSelectedCards:function(){
        this.selectedCards = [];
        for(var i=0;i< this.cards.length;i++){
            var card = this.cards[i];
            var isSelected = card.y==this.cardInitY?false:true;
            if(isSelected){
                this.selectedCards.push(card.name);
            }
        }
        //输出
        console.info("selected cards is: "+ JSON.stringify(this.selectedCards));
    },

    // called every frame, uncomment this function to activate update callback
    // update: function (dt) {

    // },
});

效果

时间: 2024-10-20 14:40:21

cocos creator Touch事件应用(触控选择多个子节点)的相关文章

WPF触屏Touch事件在嵌套控件中的响应问题

原文:WPF触屏Touch事件在嵌套控件中的响应问题 前几天遇到个touch事件的坑,记录下来以增强理解. 具体是 想把一个listview嵌套到另一个listview,这时候如果list view(子listview)的内容过多超过容器高度,它是不会出现滚动条压缩内容区域的,反而会将滚动区域转移到外面的list view(父listview),这个无可争议,但这个问题开始没留意,为待会的坑埋下伏笔. 因为 然后就是设置鼠标滚轮. 首先我使用了MouseWheel事件,奇怪的是它明明是个路由事件

JS的Touch事件们,触屏时的js事件

丫的,终于找到了JS在平板电脑上的事件!!! iphone.ipod Touch.ipad触屏时的js事件 1.Touch事件简介 pc上的web页面鼠标会产生onmousedown.onmouseup.onmouseout.onmouseover.onmousemove的事件,但是在移动终端如iphone.ipod Touch.ipad上的web页面触屏时会产生ontouchstart.ontouchmove.ontouchend.ontouchcancel事件,分别对应了触屏开始.拖拽及完成

Touch事件及触屏滑动距离计算

移动端涉及图片轮播或者一些交互性的游戏时都会用到,毕竟移动端交互大多都靠手指. 移动端有四个关于触摸的事件,分别是touchstart.touchmove.touchend.touchcancel(比较少用), 它们的触发顺序是touchstart-->touchmove-->touchend-->click,所以touch事件触发完成后会接着触发click事件,需要注意一下 ,阻止一下事件冒泡就可以了. touch事件可以产生一个event对象,这个event对象除基本的一些属性外还附

Windows phone 8 学习笔记(1) 触控输入(转)

Windows phone 8 的应用 与一般的Pc应用在输入方式上最大的不同就是:Windows phone 8主要依靠触控操作.因此在输入方式上引入一套全新的触控操作方式,我们需要重新定义相关的事件和方法.触控覆盖了Windows phone 8绝大部分用户的输入,如何处理输入呢,微软从SL和XNA两个方面提供了多种选择,并支持多点触控,下面我们看看具体的实现方式. 一.触控输入的处理方式 Silverlight 1)操作事件    用于触控操作是一个过程性的,因此通过三个事件Manipul

cocos2dx 3.1从零学习(三)——Touch事件(回调,反向传值)

第三讲 Touch 前面两篇我们学习的内容,足够我们做一款简单的小游戏.也可以说,我们已经入门了,可以蹒跚的走路了. 本篇将讲解cocos2dx中很重要的touch回调机制.你肯定记得第一章做定时器时间的时候用过CC_CALLBACK_1宏定义,它让我们回调一个只有一个形参的函数来执行定时操作. 回调函数的实现(Lambda表达式) 学习本篇前请仔细学习一下C++11的特性,std::function和lambda表达式.C++11还引入了很多boost库的优秀代码,使我们在使用的时候不必再加b

触控与手势

随着移动设备的广泛应用,对触屏的支持势在必行. H5中新增了Touch API来支持触控,包括: TouchEvent表示触控事件 Touch表示一个触控点 TouchList表示一组触控点 TouchEvent是触控事件对象,比普通事件对象多了touches.targetTouches. changedTouches属性 触控事件类型有touchstart.touchend.touchmove.touchcancel Touch对象包含identifier.screenX.clientX.pa

Android Touch事件原理加实例分析

Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. Android Touch事件原理描述 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP. 当屏幕中包含一个ViewGr

新编辑器Cocos Creator发布:对不起我来晚了!

1月19日,由Cocos创始人王哲亲手撰写的一篇Cocos Creator新品发布稿件在朋友圈被行业人士疯狂转载,短短数小时阅读量突破五位数.Cocos Creator被誉为"注定将揭开Cocos开发世界中全新的一页",究竟它有什么魅力?下面让我们来细细阅读这篇文章. 以下为全文: 上周我在厦门的「哎哟,2016」酒会上,宣布了今年会发布一款新的编辑器.是的,今天我来了: Cocos Creator在经历了一年多的低调研发之后,今天终于发布了第一个正式对外的公测版,大家可以从 coco

【事件】封装touch触摸事件 --- DOM2级、单点触控

//封装touch触摸事件 --- DOM2级.单点触控 if (!Object.prototype.addTouchEvents) { Object.defineProperty(Object.prototype, 'addTouchEvents', { value: function(Obj) { var noop = function() {}; var defaults = { start: noop, //开始 move: noop, //移动 end: noop, //结束 left