鬥地主滑動選擇卡牌的實現
最近想自己做一個鬥地主遊戲(使用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即可。
效果圖如下:
參考: https://blog.csdn.net/themagickeyjianan/article/details/39208463#commentBox