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

 

斗地主滑动选牌&&出牌(Cocos Creator)_第1张图片
手牌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):

斗地主滑动选牌&&出牌(Cocos Creator)_第2张图片
显示自己牌.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个元素是显示在最左边的。
  第二点,我们举个例子:

斗地主滑动选牌&&出牌(Cocos Creator)_第3张图片
 上面这张图里,鼠标现在放在 “方块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状态的牌,应该将其置为未选中状态。

选牌效果

斗地主滑动选牌&&出牌(Cocos Creator)_第4张图片
选牌时.png
斗地主滑动选牌&&出牌(Cocos Creator)_第5张图片
选牌后.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

你可能感兴趣的:(斗地主滑动选牌&&出牌(Cocos Creator))