最近想自己做一个斗地主游戏(使用cocoscreator + javascript),发现滑动选择卡牌还有一点点麻烦呢,这里把实现分享下。
1、首先封装卡牌 CardCtrl.js
卡牌的touched属性即为触摸框选标记,selected属性为触摸结束所选择卡牌的标记。其他的牌面花色什么的这里不做处理。
/** * Created by skyxu on 2018/11/1. * * 卡牌组件 */ cc.Class({ extends: cc.Component, properties: { spSelected: cc.Node, touched: { default: false, notify(){ this.spSelected.active = this.touched; } }, selected: { default: false, notify(){ if (this.selected){ this.node.y += 20; } else{ this.node.y -= 20; } } }, }, // LIFE-CYCLE CALLBACKS: onLoad () { }, start () { }, // update (dt) {}, });
2、接着实现滑动选择组件 DragSelect.js
思路就是触摸滑动时画一个矩形,把和矩形有交集的卡牌都设为touched,触摸结束时把属性touched为true卡牌的selected属性设为true即可。
需要注意的是,由于卡牌是有叠加的,选择时要注意。我这里卡牌是右侧的压左侧的,所以每次touchmvoe时,只选择框选范围内最右侧的一张即可。主要分为3步:
1. 判断矩形范围进行框选,在onTouchMove调用_checkSelectCard(beginPos, endPos), 这里两个参数都传入当前的touchmove获得的pos, 即画一个最小的矩形,选择范围内最右侧一张即可。
2.处理框选范围外的卡牌,在onTouchMove调用_checkSelectCardReverse(beginPos, endPos), 这里两个参数一个传最初触摸开始的点和当前触摸的点。 这里需要注意的是,如果从右侧向左侧框选然后又退回到右侧则要特别处理,一担向右退回到当前卡牌的右侧卡牌上时则要把当前卡牌的touched属性设置为false(虽然矩形范围还包括当前卡牌,但包括的部分为右侧卡牌压着的部分)
3. 触摸结束,touched属性为true的卡牌即为所选,并重置touched为false
/** * Created by skyxu on 2018/11/1. * * 滑动选择卡牌组件 * 把此组件拖放到卡牌根节点即可,卡牌根节点添加cc.Layout组件来自动布局 * */ cc.Class({ extends: cc.Component, // LIFE-CYCLE CALLBACKS: // onLoad () {}, start () { this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this); this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this); this.cardsArr = this.node.getChildren(); let card = this.cardsArr[0]; // 指左侧卡牌锚点到右侧相邻卡牌边缘的距离 this.CARD_DISTANCE = card.width*card.anchorX + this.node.getComponent(cc.Layout).spacingX; cc.log("CARD_DISTANCE: " + this.CARD_DISTANCE); }, onTouchStart(event){ let pos = event.getLocation(); let beginPos = this._beginPos = this.node.convertToNodeSpaceAR(pos); this._checkSelectCard(beginPos, beginPos, true); }, onTouchMove(event){ let pos = event.getLocation(); let movePos = this.node.convertToNodeSpaceAR(pos) // 这里确定是(movePos, movePos) 每次移动只选择右侧一张 this._checkSelectCard(movePos, movePos); // 这里要传入起点和结束点,获取总的框取范围 this._checkSelectCardReverse(this._beginPos, movePos); }, onTouchEnd(event){ this._onSelectCardEnd(); }, _onSelectCardEnd(){ for (let card of this.cardsArr){ let ctrl = card.getComponent("CardCtrl"); if (ctrl.touched){ ctrl.selected = !ctrl.selected; } ctrl.touched = false; } }, _checkSelectCard(beginPos, endPos, isBegin){ let len = this.cardsArr.length; if (isBegin){ for (let i=len-1; i>=0; i--){ let card = this.cardsArr[i]; if (cc.rectContainsPoint(card.getBoundingBox(), beginPos)){ card.getComponent('CardCtrl').touched = true; return; } } } else{ let w = Math.max(1, Math.abs(beginPos.x - endPos.x)); let h = Math.max(1, Math.abs(beginPos.y - endPos.y)); let x = Math.min(beginPos.x, endPos.x); let y = Math.min(beginPos.y, endPos.y); let touchRect = cc.rect(x, y, w, h); for (let i=len-1; i>=0; i--){ let card = this.cardsArr[i]; if (cc.rectIntersectsRect(card.getBoundingBox(), touchRect)){ card.getComponent('CardCtrl').touched = true; return; } } } }, _checkSelectCardReverse(beginPos, endPos){ let p1 = beginPos.x < endPos.x ? beginPos : endPos; let w = Math.abs(beginPos.x - endPos.x); let h = Math.abs(beginPos.y - endPos.y); let rect = cc.rect(p1.x, p1.y, w, h); let len = this.cardsArr.length; for (let i=len-1; i>=0; i--){ let card = this.cardsArr[i]; if (!cc.rectIntersectsRect(card.getBoundingBox(), rect)){ card.getComponent('CardCtrl').touched = false; } } // 从右向左框取然后又移动回右侧则取消左侧已经选择的卡牌 for (let i=0; i<len; i++){ let card = this.cardsArr[i]; if (p1.x - card.x >= this.CARD_DISTANCE){ card.getComponent('CardCtrl').touched = false; } } } // update (dt) {}, });
3、测试
在场景里添加一个卡牌根节点 cardPanel, 并为它添加cc.Layout组件(自动布局),然后把DragSelect.js组件拖放到cardPanel即可。
效果图如下:
Git链接: https://github.com/sky068/dragSelectCards
参考: https://blog.csdn.net/themagickeyjianan/article/details/39208463#commentBox