斗地主滑动选牌&&出牌(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

原文地址:https://www.cnblogs.com/gao88/p/11632667.html

时间: 2024-08-29 08:28:14

斗地主滑动选牌&&出牌(Cocos Creator)的相关文章

cocos2dx《单机斗地主》源码解剖之八 电脑玩家出牌与跟牌(结束)

上一篇文章对玩家手中的牌进行分析归类,下面就该实现电脑玩家出牌与跟牌的策略了.首先我们来看看出牌的策略,代码如下: void GameScene::update(float delta){ switch (m_iState) { case 0: SendPk(); break; case 1: schedule(schedule_selector(GameScene::Call),1); break; case 2: scheduleOnce(schedule_selector(GameScen

开发h5斗地主大厅算法——第十二章の主动出牌(1)

本章开始,我们介绍主动出牌的算法,和被动出牌类似,我们第一步把主要架子搭起来. 首先清空出牌序列 [cpp] view plain copy clsHandCardData.ClearPutCardList(); 主动出牌的策略按照优先级大体可以分为三类: [一]能直接一手牌出去,优先出. [二]两手牌出去且有绝对大牌,先出绝对大牌. [三]出一手牌使得接下来自己手牌价值最大化. [cpp] view plain copy //剪枝:如果能出去最后一手牌直接出 CardGroupData Sur

Unity3D手机斗地主游戏开发实战(03)_地主牌显示和出牌逻辑(不定期更新中~~~)

Hi,之前有同学说要我把源码发出来,那我就把半成品源码的链接放在每篇文件的最后,有兴趣的话可以查阅参考,有问题可以跟我私信,也可以关注我的个人公众号,互相交流嘛.当然,代码也是在不断的持续改进中~ 上期我们实现了叫地主功能,不过遗留了一个小功能:叫地主完成以后,要显示地主的3张牌,这期首先弥补这块的功能: 接着我们要进入开发出牌逻辑的开发阶段,好了,废话不多说,继续我们斗地主开发之旅~ 地主牌的显示 我们在玩家界面的顶部中间位置,放置一个新的GameObject,命名为BidCards,用来记录

斗地主AI出牌

斗地主游戏的初期版本目前为止大概已经完成的一半了... 还剩下最麻烦的部分(AI)没写,写这篇博文主要是想理一下基本的思路,然后把这一部分也搞完. 先上一个目前的进度截图纪念一下好了 经过较长时间的冥思苦想,包括网上找资料...(网上关于斗地主AI的资料很少.) 终于想到了一个大概可行的办法~先写出来试试吧.. 斗地主的AI部分主要分为两块: 一个是主动出牌.(自己是第一个出牌的,或者上一把打出的牌没有人要,又轮到自己了) ---这时候就需要从当前的手牌中选取一道合适的牌打出. 怎样选取呢?这正

要讲究相互之间的规则,同创娱乐不会随意出牌

其实按照每一个牌面的组合和相互之间的影响,在玩法上特别需要注意的是彼此之间的陷阱,首先要确定的是应该如何发挥出来各自的标准和相互之间固定的条件,因为随着这些影响以及在提升的方式上,每一种范围以及在利用的表现方式上都是能够获得的,所以同创娱乐会将这些牌面的组合情况能够给我们一些比较具有优势的组合.往往就会比较积极的截杀这种好牌,而且随着这些优势以及在相互之间固定的条件,每一种标准以及在固定的搭配上,这些方式确实能够获得稳定的效果,因为他们要掌握这个牌面组合的主动性. 出牌要有更好的策略,不会受到干

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

最近参与了cocos creator的研究,开发小游戏,结果被一个事件坑得不行不行的.现在终于解决了,分享给大家. 原理 1.触控事件是针对节点的 2.触控事件的冒泡,是直接关系冒泡,父子可以,孙子不行,就是不能隔代冒泡 3.父节点不响应触控事件,肯定是被孩子节点遮挡了,只要孩子节点也监听一下事件,父节点就可以响应了 4.触控位置是绝对坐标,相对于整个canvas,节点位置相对于父节点,相对位置可以与绝对坐标相互转化 5.节点是否被触控到,touch start事件可以肯定被触摸到,但是一个节点

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

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

【COCOS CREATOR 系列教程之四】基于0.7.1先简单制作一个PAGEVIEW

本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/cocos-creator/1999.html 由于当前版本还没有发布1.0,因此还有不少组件没有发布,那么Himi也看到Cocos Creator群里有几个童鞋问起过PageView的问题,那么Himi正好借此练手,基于当前版本制作一个PageView. 本文分为两部分进行讲解: 1. 制作PageView     2. 如何使用 一. 制作

反复横跳的瞄准线!从向量计算说起!基于射线检测的实现!Cocos Creator!

最近有小伙伴问我瞄准线遇到各种形状该怎么处理?如何实现反复横跳的瞄准线?最近刚好在<Cocos Creator游戏开发实战>中看到物理系统有一个射线检测,于是,基于这个射线检测,写了一个反复横跳的瞄准线效果.一起往下看吧!文章底部获取完整项目! 国际惯例,先上最终效果! 在讲解之前我们需要一些向量的知识,简单的介绍一些吧! 向量的加法,OA + AB = OB 向量的点乘,表示一个向量在另一个向量上的投影,是个标量,有正负之分.向量夹角小于 90度 为正数,等于 90度 为 零,大于 90度