当前位置:   article > 正文

斗地主滑动选牌&&出牌(Cocos Creator)_cocos手牌显示

cocos手牌显示

转载自:https://www.jianshu.com/p/29883621184c

本文主要讲解以下几个方面:

  • card model
  • 滑动处理
  • 阴影
  • 选择
  • 出牌

Card Model

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

 

  1. CardInfo.js
  2. //花型
  3. var CardSuit = cc.Enum ({
  4. none: 0,
  5. spade: 1, //黑桃
  6. heart: 2, //红心
  7. club: 3, //梅花
  8. diamond: 4,//方块
  9. });
  10. //数字
  11. var CardNumber = cc.Enum ({
  12. none: 0,
  13. num_3: 3,
  14. num_4: 4,
  15. num_5: 5,
  16. num_6: 6,
  17. num_7: 7,
  18. num_8: 8,
  19. num_9: 9,
  20. num_10: 10,
  21. num_J: 11,
  22. num_Q: 12,
  23. num_K: 13,
  24. num_A: 14,
  25. num_2: 15,
  26. littleJoker: 16,
  27. bigJoker: 17,
  28. });
  29. var CardInfo = cc.Class ({
  30. extends: cc.Component,
  31. properties: {
  32. //数字
  33. number: {
  34. default: CardNumber.none,
  35. type: CardNumber
  36. },
  37. //花型
  38. suit: {
  39. default: CardSuit.none,
  40. type: CardSuit
  41. },
  42. },
  43. statics: {
  44. CardNumber: CardNumber,
  45. CardSuit: CardSuit
  46. },
  47. //主要用于打印时,能清晰看到现在是哪张牌
  48. desc() {
  49. var desc = "";
  50. if (this.number == CardNumber.littleJoker) {
  51. return "小王";
  52. }
  53. if (this.number == CardNumber.bigJoker) {
  54. return "大王";
  55. }
  56. switch(this.suit) {
  57. case CardSuit.heart:
  58. desc = "红桃";
  59. break;
  60. case CardSuit.spade:
  61. desc = "黑桃";
  62. break;
  63. case CardSuit.club:
  64. desc = "梅花";
  65. break;
  66. case CardSuit.diamond:
  67. desc = "方块";
  68. break;
  69. }
  70. switch(this.number) {
  71. case CardNumber.num_3:
  72. desc += "3";
  73. break;
  74. case CardNumber.num_4:
  75. desc += "4";
  76. break;
  77. case CardNumber.num_5:
  78. desc += "5";
  79. break;
  80. case CardNumber.num_6:
  81. desc += "6";
  82. break;
  83. case CardNumber.num_7:
  84. desc += "7";
  85. break;
  86. case CardNumber.num_8:
  87. desc += "8";
  88. break;
  89. case CardNumber.num_9:
  90. desc += "9";
  91. break;
  92. case CardNumber.num_10:
  93. desc += "10";
  94. break;
  95. case CardNumber.num_J:
  96. desc += "J";
  97. break;
  98. case CardNumber.num_Q:
  99. desc += "Q";
  100. break;
  101. case CardNumber.num_K:
  102. desc += "K";
  103. break;
  104. case CardNumber.num_A:
  105. desc += "A";
  106. break;
  107. case CardNumber.num_2:
  108. desc += "2";
  109. break;
  110. }
  111. return desc;
  112. },
  113. });

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

显示自己手上的牌

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

 

手牌node.png

 

  代码走起来

 

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

 

 

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

显示自己牌.png

滑动选牌

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

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

 

  1. CardArea.js(绑定在handCardArea节点上)、
  2. var Main = require("Main");
  3. cc.Class({
  4. extends: cc.Component,
  5. properties: {
  6. _touchBegan: null,
  7. _touchMoved: null,
  8. //用于调用Main场景上的脚本的方法,同时可以传递数据
  9. game:{
  10. default : null,
  11. type: Main,
  12. }
  13. },
  14. onTouchEvent: function () {
  15. this.node.on(cc.Node.EventType.TOUCH_START, this.touchBegan, this);
  16. this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.touchCancel, this);
  17. this.node.on(cc.Node.EventType.TOUCH_END, this.touchEnd, this);
  18. this.node.on(cc.Node.EventType.TOUCH_MOVE, this.touchMoved, this);
  19. },
  20. offTouchEvent: function () {
  21. this.node.off(cc.Node.EventType.TOUCH_START, this.touchBegan, this);
  22. this.node.off(cc.Node.EventType.TOUCH_CANCEL, this.touchCancel, this);
  23. this.node.off(cc.Node.EventType.TOUCH_END, this.touchEnd, this);
  24. this.node.off(cc.Node.EventType.TOUCH_MOVE, this.touchMoved, this);
  25. },
  26. onLoad () {
  27. this.onTouchEvent();
  28. },
  29. onDestroy(){
  30. this.offTouchEvent();
  31. },
  32. /**
  33. * Touch begin
  34. * 当前触摸的点 是否在牌的区域
  35. * */
  36. _getCardForTouch: function (touch, cardArr) {
  37. cardArr.reverse(); //to 1
  38. for (var k in cardArr) {
  39. var box = cardArr[k].getBoundingBox(); //获取card覆盖坐标范围
  40. if (cc.rectContainsPoint(box, touch)) { //判断触摸的点,是否在当前牌的范围内
  41. cardArr[k].isChiose = true;
  42. cardArr[k].getComponent("Card").setMaskShowing(true); //显示阴影遮罩
  43. cc.log("CCC touch select: "+k);
  44. cardArr.reverse();
  45. return cardArr[k];
  46. }
  47. }
  48. cardArr.reverse();
  49. },
  50. /**
  51. * Touch move
  52. *
  53. * */
  54. _checkSelectCardReserve(touchBegan, touchMoved) {
  55. //获取左边的点 为起始点
  56. var p1 = touchBegan.x < touchMoved.x ? touchBegan : touchMoved;
  57. //滑动的宽度
  58. var width = Math.abs(touchBegan.x - touchMoved.x);
  59. //滑动的高度 最小设置为5
  60. var height = Math.abs(touchBegan.y - touchMoved.y) > 5 ? Math.abs(touchBegan.y - touchMoved.y) : 5;
  61. //根据滑动 获取矩形框
  62. var rect = cc.rect(p1.x, p1.y, width, height);
  63. for (let i = 0; i < this.game.cardArr.length; i++) {
  64. //判断矩形是否相交
  65. if (!cc.rectIntersectsRect(this.game.cardArr[i].getBoundingBox(), rect)) {
  66. //不相交 设置为反选状态
  67. this.game.cardArr[i].isChiose = false;
  68. this.game.cardArr[i].getComponent("Card").setMaskShowing(false);
  69. }
  70. }
  71. //如果是从右向左滑动
  72. if (p1 === touchMoved) {
  73. for (let i = this.game.cardArr.length - 1; i >= 0; i--) {
  74. //从右往左滑时,滑到一定距离,又往右滑
  75. //这是要判断反选
  76. if (this.game.cardArr[i].x - p1.x < 24) { //
  77. this.game.cardArr[i].getComponent("Card").setMaskShowing(false);
  78. this.game.cardArr[i].isChiose = false;
  79. }
  80. }
  81. }
  82. },
  83. /**
  84. * 开始点击 TOUCH_START回调函数
  85. * */
  86. touchBegan: function (event) {
  87. cc.log("Touch begin");
  88. var touches = event.getTouches();
  89. var touchLoc = touches[0].getLocation();
  90. cc.log("touch begin location: "+touchLoc);
  91. this._touchBegan = this.node.convertToNodeSpace(touchLoc);
  92. this._getCardForTouch( this._touchBegan, this.game.cardArr);
  93. },
  94. /**
  95. * 移动 TOUCH_MOVE回调函数
  96. * */
  97. touchMoved: function (event) {
  98. cc.log("Touch move");
  99. var touches = event.getTouches();
  100. var touchLoc = touches[0].getLocation();
  101. this._touchMoved = this.node.convertToNodeSpace(touchLoc);
  102. this._getCardForTouch(this._touchMoved, this.game.cardArr);
  103. this._checkSelectCardReserve(this._touchBegan, this._touchMoved);
  104. },
  105. touchCancel: function () {
  106. },
  107. /**
  108. * 点击结束 TOUCH_END回调函数
  109. * */
  110. touchEnd: function (event) {
  111. cc.log("Touch end");
  112. var touches = event.getTouches();
  113. var touchLoc = touches[0].getLocation();
  114. for (var k in this.game.cardArr) {
  115. this.game.cardArr[k].getComponent("Card").setMaskShowing(false);
  116. if (this.game.cardArr[k].isChiose === true) {
  117. this.game.cardArr[k].isChiose = false;
  118. // to 2
  119. if (this.game.cardArr[k].status === SITDOWN) {
  120. this.game.cardArr[k].status = STANDUP;
  121. this.game.cardArr[k].y += 19;
  122. } else {
  123. this.game.cardArr[k].status = SITDOWN;
  124. this.game.cardArr[k].y -= 19;
  125. }
  126. }
  127. }
  128. },

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节点下的组件,否则会提示被绑定。

代码先行

 

  1. discards (cards) {
  2. //出牌
  3. if (!cards || cards.lenngth == 0) return;
  4. //从父节点分离
  5. for (const key in cards) {
  6. if (cards.hasOwnProperty(key)) {
  7. const card = cards[key];
  8. card.node.removeFromParent(true); // 1
  9. for (var i = 0; i < this.handCards.length; i++) {
  10. var cardInfo = this.handCards[i];
  11. if (cardInfo == card.cardInfo) {
  12. this.handCards.splice(i, 1); // 2
  13. break;
  14. }
  15. }
  16. }
  17. }
  18. this.appendCardsToOutZone(cards); // 3
  19. this.updateCards(); // 4
  20. if(this.handCards.length == 0) {
  21. this.game.gameOver(this);
  22. }
  23. },
  24. /**
  25. * 将牌的预制体,添加到出牌区域
  26. */
  27. appendCardsToOutZone(cards) {
  28. this.outCardZone.node.removeAllChildren(true); // 3.1
  29. var count = cards.length;
  30. var zeroPoint = count / 2;
  31. for (var i = 0; i < count; i++) {
  32. var card = cards[i];
  33. var cardNode = card.node;
  34. this.outCardZone.node.addChild(cardNode, 100 - card.cardInfo.number); // 3.2
  35. }
  36. this.outCardZone.node.sortAllChildren();
  37. // 设置position
  38. for (var i = 0; i < count; i++) {
  39. var cardNode = this.outCardZone.node.getChildren()[i];;
  40. var x = (i - zeroPoint) * 30;
  41. var y = cardNode.getPositionY()+180;
  42. cardNode.setScale(0.7, 0.7); // 3.3
  43. cardNode.setPosition(x, y); // 3.4
  44. }
  45. },

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();
  重新设置 手中的牌 的位置,这一点和显示手上牌的最后一步类似。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/145531
推荐阅读
相关标签
  

闽ICP备14008679号