• 斗地主滑动选牌&&出牌(Cocos Creator)


    本文主要讲解以下几个方面:
    • card model
    • 滑动处理
    • 阴影
    • 选择
    • 出牌

    Card Model

      首先,牌有两个属性:数字、花型;
      ps:本文现在是,用数字和花型来组成一张牌,有空可以再用另一种形式走一遍,比如用54个数字(0-53)来表示一副牌。

    CardInfo.js
    
    //花型
    var CardSuit = cc.Enum ({
        none: 0,
        spade: 1,  //黑桃
        heart: 2,  //红心
        club: 3,   //梅花
        diamond: 4,//方块
    });
    
    //数字
    var CardNumber = cc.Enum ({
        none: 0,
        num_3: 3,
        num_4: 4,
        num_5: 5,
        num_6: 6,
        num_7: 7,
        num_8: 8,
        num_9: 9,
        num_10: 10,
        num_J: 11,
        num_Q: 12,
        num_K: 13,
        num_A: 14,
        num_2: 15,
        littleJoker: 16,
        bigJoker: 17,
    });
    var CardInfo = cc.Class ({
        extends: cc.Component,
    
        properties: {
            //数字
            number: {
                default: CardNumber.none,
                type: CardNumber
            },
            //花型
            suit: {
                default: CardSuit.none,
                type: CardSuit
            },
        },
    
        statics: {
            CardNumber: CardNumber,
            CardSuit: CardSuit
        },
    
        //主要用于打印时,能清晰看到现在是哪张牌
        desc() {
            var desc = "";
    
            if (this.number == CardNumber.littleJoker) {
                return "小王";
            }
            if (this.number == CardNumber.bigJoker) {
                return "大王";
            }
    
            switch(this.suit) {
                case CardSuit.heart:
                    desc = "红桃";
                    break;
                case CardSuit.spade:
                    desc = "黑桃";
                    break;
                case CardSuit.club:
                    desc = "梅花";
                    break;
                case CardSuit.diamond:
                    desc = "方块";
                    break;
            }
    
            switch(this.number) {
                case CardNumber.num_3:
                    desc += "3";
                    break;
                case CardNumber.num_4:
                    desc += "4";
                    break;
                case CardNumber.num_5:
                    desc += "5";
                    break;
                case CardNumber.num_6:
                    desc += "6";
                    break;
                case CardNumber.num_7:
                    desc += "7";
                    break;
                case CardNumber.num_8:
                    desc += "8";
                    break;
                case CardNumber.num_9:
                    desc += "9";
                    break;
                case CardNumber.num_10:
                    desc += "10";
                    break;
                case CardNumber.num_J:
                    desc += "J";
                    break;
                case CardNumber.num_Q:
                    desc += "Q";
                    break;
                case CardNumber.num_K:
                    desc += "K";
                    break;
                case CardNumber.num_A:
                    desc += "A";
                    break;
                case CardNumber.num_2:
                    desc += "2";
                    break;
            }
    
            return desc;
        },
    });
    

      ps:小王,大王的花型为 CardSuit.none

    显示自己手上的牌

      首先,在场景中,添加一个空节点,锚点设置为(0,0);
      宽度,我这里设置为屏幕宽度1334;(如果你能准确算出,所有牌最长的宽,也可以设置为具体值)
      高度,牌的高+牌选中状态向上的偏移量(我这里设置为:201+19)

    手牌node.png

      代码走起来

        main.js(绑定在当前场景上)
    
        var CardInfo = require("CardInfo");
        ...
        initHandCards : function(numbers){
             //随便初始化几张牌
             for (var number = 3; number <= 15; number++) {
                let cardInfo = new CardInfo();
                cardInfo.suit = number%2==0?CardInfo.CardSuit.diamond:CardInfo.CardSuit.spade;
                cardInfo.number = number;
                cardInfo.name = cardInfo.desc();
    
                //cardInfoArr 存储牌的信息对象的数组
                this.cardInfoArr.push(cardInfo);
    
                //根据牌的信息 初始化预制体
                var card = cc.instantiate(this.cardPrefab);
                card.getComponent("Card").mCardInfo = cardInfo;
         
                //将牌预制体 添加到父节点
                this.handCardArea.addChild(card, cardInfo.number);
                card.isChiose = false;
    
                //cardArr 存储card prefab的对象数组
                this.cardArr.push(card);
            }
    
            //计算posx,第一张牌显示的x坐标
            var posx = 1334/2 - (this.cardArr.length -1)/2 * 50;
            for (var i = 0; i < this.cardArr.length; i++){
                this.cardArr[i].setPosition(posx+ i*50, 100.5);
            }
        }
    

    运行结果如下(牌的间距为50):

    显示自己牌.png

    滑动选牌

    选牌的事件监听对象,是不是牌呢?

    如果只需要点击选择,我们可以直接在card prefab上添加点击事件监听
    如果做滑动选牌,就需要在card prefab的父节点上(handCardArea),监听touchDown,touchMove,touchUp等事件

    CardArea.js(绑定在handCardArea节点上)、
    
    var Main = require("Main");
    cc.Class({
        extends: cc.Component,
    
        properties: {
            _touchBegan: null,
            _touchMoved: null,
    
            //用于调用Main场景上的脚本的方法,同时可以传递数据
            game:{
                default : null,
                type: Main,
            }
        },
    
     onTouchEvent: function () {
            this.node.on(cc.Node.EventType.TOUCH_START, this.touchBegan, this);
            this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.touchCancel, this);
            this.node.on(cc.Node.EventType.TOUCH_END, this.touchEnd, this);
            this.node.on(cc.Node.EventType.TOUCH_MOVE, this.touchMoved, this);
        },
    
        offTouchEvent: function () {
            this.node.off(cc.Node.EventType.TOUCH_START, this.touchBegan, this);
            this.node.off(cc.Node.EventType.TOUCH_CANCEL, this.touchCancel, this);
            this.node.off(cc.Node.EventType.TOUCH_END, this.touchEnd, this);
            this.node.off(cc.Node.EventType.TOUCH_MOVE, this.touchMoved, this);
        },
    
        onLoad () {
            this.onTouchEvent();
        },
    
        onDestroy(){
            this.offTouchEvent();
        },
       
         /**
         * Touch begin
         * 当前触摸的点 是否在牌的区域
         * */
        _getCardForTouch: function (touch, cardArr) {
            cardArr.reverse();      //to 1
            for (var k in cardArr) {
                var box = cardArr[k].getBoundingBox();   //获取card覆盖坐标范围
                if (cc.rectContainsPoint(box, touch)) {      //判断触摸的点,是否在当前牌的范围内
                    cardArr[k].isChiose = true;
                    cardArr[k].getComponent("Card").setMaskShowing(true);  //显示阴影遮罩
                    cc.log("CCC touch select: "+k);
    
                    cardArr.reverse();
                    return cardArr[k];
                }
            }
            cardArr.reverse();
        },
    
        /**
         * Touch move
         *
         * */
        _checkSelectCardReserve(touchBegan, touchMoved) {
            //获取左边的点 为起始点
            var p1 = touchBegan.x < touchMoved.x ? touchBegan : touchMoved;
            //滑动的宽度
            var width = Math.abs(touchBegan.x - touchMoved.x);
            //滑动的高度 最小设置为5
            var height = Math.abs(touchBegan.y - touchMoved.y) > 5 ? Math.abs(touchBegan.y - touchMoved.y) : 5;
            //根据滑动 获取矩形框
            var rect = cc.rect(p1.x, p1.y, width, height);
    
            for (let i = 0; i < this.game.cardArr.length; i++) {
                //判断矩形是否相交
                if (!cc.rectIntersectsRect(this.game.cardArr[i].getBoundingBox(), rect)) {
                    //不相交 设置为反选状态
                    this.game.cardArr[i].isChiose = false;
                    this.game.cardArr[i].getComponent("Card").setMaskShowing(false);
                }
            }
    
            //如果是从右向左滑动
            if (p1 === touchMoved) {
                for (let i = this.game.cardArr.length - 1; i >= 0; i--) {
                    //从右往左滑时,滑到一定距离,又往右滑
                    //这是要判断反选
                    if (this.game.cardArr[i].x - p1.x < 24) {  //
                        this.game.cardArr[i].getComponent("Card").setMaskShowing(false);
                        this.game.cardArr[i].isChiose = false;
                    }
                }
            }
    
        },
    
        /**
         * 开始点击  TOUCH_START回调函数
         * */
        touchBegan: function (event) {
            cc.log("Touch begin");
            var touches = event.getTouches();
            var touchLoc = touches[0].getLocation();
            cc.log("touch begin location: "+touchLoc);
            this._touchBegan = this.node.convertToNodeSpace(touchLoc);
            this._getCardForTouch( this._touchBegan, this.game.cardArr);
        },
    
        /**
         * 移动  TOUCH_MOVE回调函数
         * */
        touchMoved: function (event) {
            cc.log("Touch move");
            var touches = event.getTouches();
            var touchLoc = touches[0].getLocation();
            this._touchMoved = this.node.convertToNodeSpace(touchLoc);
            this._getCardForTouch(this._touchMoved, this.game.cardArr);
            this._checkSelectCardReserve(this._touchBegan, this._touchMoved);
        },
    
        touchCancel: function () {
    
        },
    
        /**
         * 点击结束  TOUCH_END回调函数
         * */
        touchEnd: function (event) {
            cc.log("Touch end");
            var touches = event.getTouches();
            var touchLoc = touches[0].getLocation();
            for (var k in this.game.cardArr) {
                this.game.cardArr[k].getComponent("Card").setMaskShowing(false);
                if (this.game.cardArr[k].isChiose === true) {
                    this.game.cardArr[k].isChiose = false;
                    // to 2
                    if (this.game.cardArr[k].status === SITDOWN) {
                        this.game.cardArr[k].status = STANDUP;
                        this.game.cardArr[k].y += 19;
                    } else {
                        this.game.cardArr[k].status = SITDOWN;
                        this.game.cardArr[k].y -= 19;
                    }
                }
            }
        },
    

    1. 为什么要调用 cardArr.reverse()?
      首先,显示牌的时候,我们是从左往右显示的,也是从 0 到 cardArr.length-1 显示的。也就是说cardArr中,第0个元素是显示在最左边的。
      第二点,我们举个例子:

     上面这张图里,鼠标现在放在 “方块8” 上,由于牌重叠放置,现在 “方块6”,“黑桃7”,“方块8” 三张牌的坐标范围,都包含了鼠标当前的位置。
      现在鼠标在当前位置点击一下
      如果,我们从“黑桃3”开始判断,那结果就会出现 “方块6”,“黑桃7”,“方块8” 三张牌,
      很显然,这样的记过是不对的,我们想选择的只有 “方块8” 而已。
      所以,正确的操作应该是,从最右边的 “黑桃2” 开始判断,因此,我们需要调用一次 reverse()。
      第三点,需要注意的是,如果在 _getCardForTouch(cardArr) 中,使用的是原始cardArr数据,则需要在本地判断完成后,再次调用 reverse() 方法,将cardArr原始数据顺序还原,否则当你判断下一个点的时候,调用 reverse() 后,cardArr的数据就变成了正序,就达不到我们想要的效果了;
      当然,如果每次传递的是原始数据,在 _getCardForTouch(cardArr) 方法中,使用的是cardArr的引用,就不需要再次调用 reverse()方法了。

    2. touchEnd()中,为什么要将牌的状态反置?
      如果滑动覆盖的区域内,包含已经是STANDUP状态的牌,应该将其置为未选中状态。

    选牌效果

    选牌时.png
    选牌后.png

    出牌

    注意:
    要打出的牌,不能直接用handCardArea节点下的组件,否则会提示被绑定。

    代码先行

    discards (cards) {
            //出牌
            if (!cards || cards.lenngth == 0) return;
    
            //从父节点分离
            for (const key in cards) {
                if (cards.hasOwnProperty(key)) {
                    const card = cards[key];
                    card.node.removeFromParent(true);   // 1
    
                    for (var i = 0; i < this.handCards.length; i++)   {
                        var cardInfo = this.handCards[i];
                        if (cardInfo == card.cardInfo) {
                            this.handCards.splice(i, 1);     // 2
                            break;
                        }
                    }
                }
            }
    
            this.appendCardsToOutZone(cards);     // 3
            this.updateCards();                             // 4
    
            if(this.handCards.length == 0) {
                this.game.gameOver(this);
            }
        },
    /**
     * 将牌的预制体,添加到出牌区域
     */
    appendCardsToOutZone(cards) {
           this.outCardZone.node.removeAllChildren(true);   //  3.1
    
            var count = cards.length;
            var zeroPoint = count / 2;
    
            for (var i = 0; i < count; i++)   {
                var card = cards[i];
                var cardNode = card.node;
                this.outCardZone.node.addChild(cardNode, 100 - card.cardInfo.number);    // 3.2
            }
            this.outCardZone.node.sortAllChildren();
    
            // 设置position
            for (var i = 0; i < count; i++)   {
                var cardNode = this.outCardZone.node.getChildren()[i];;
    
                var x = (i - zeroPoint) * 30;
    
                var y = cardNode.getPositionY()+180;
                cardNode.setScale(0.7, 0.7);                   // 3.3
                cardNode.setPosition(x, y);                     // 3.4
            }
            
        },
    

    1. 将 选中的牌 从父节点中移除
      card.node.removeFromParent(true);
    2. 从handCards 数组中,删除 选中的牌
      this.handCards.splice(i, 1);
    3. this.appendCardsToOutZone(cards)
      将 “选中的牌” 添加到出牌区域
      3.1 清空出牌区域
        this.outCardZone.node.removeAllChildren(true);
      3.2 添加子节点
        this.outCardZone.node.addChild(cardNode, 100 - card.cardInfo.number);
      3.3 设置scale
        cardNode.setScale(0.7, 0.7);
      3.4 设置position
        cardNode.setPosition(x, y);
    4. this.updateCards();
      重新设置 手中的牌 的位置,这一点和显示手上牌的最后一步类似。

     来源: https://www.jianshu.com/p/29883621184c

  • 相关阅读:
    我的安全测试面试_自问自答,不亦乐乎
    Linux Shell 网络层监控脚本(监控包括:连接数、句柄数及根据监控反馈结果分析)
    netstat监控大量ESTABLISHED连接与Time_Wait连接问题
    详解 Spotlight on MySQL监控MySQL服务器
    详解 Spotlight on Unix 监控Linux服务器
    某听书网站系统漏洞,利用抓包拼接方式获取网站资源
    不懂得使用工具的测试不是好测试
    【好书摘要】性能优化中CPU、内存、磁盘IO、网络性能的依赖
    性能调优从哪里入手
    报文解析,从请求报文详细讲到响应码
  • 原文地址:https://www.cnblogs.com/gao88/p/11632667.html
Copyright © 2020-2023  润新知