如题,要完成这个功用,首要我们需求先了解下的CocosCreator中的接触事情传递的机制。

接触事情冒泡

接触事情支持节点树的事情冒泡,以下图为例:

CocosCreator: 实现斗地主滑动选择多张牌
在图中的场景里,假定 A 节点拥有一个子节点 B,B 拥有一个子节点 C。开发者对 A、B、C 都监听了接触事情(以下的举例都默许节点监听了接触事情)。

当鼠标或手指在 C 节点区域内按下时,事情将首要在 C 节点触发,C 节点监听器接收到事情。接着 C 节点会将事情向其父节点传递这个事情,B 节点的监听器将会接收到事情。同理 B 节点会将事情传递给 A 父节点。这便是最根本的事情冒泡进程。需求着重的是,在接触事情冒泡的进程中不会有接触检测,这意味着即便触点不在 A B 节点区域内,A B 节点也会经过接触事情冒泡的机制接收到这个事情。

接触事情的冒泡进程与普通事情的冒泡进程并没有区别。所以,调用event.stopPropagation()能够主动中止冒泡进程。

同级节点间的触点归属问题

假定上图中 B、C 为同级节点,C 节点部分掩盖在 B 节点之上。这时候假如 C 节点接收到接触事情后,就宣布了触点归属于 C 节点,这意味着同级节点的 B 就不会再接收到接触事情了,即便触点一起也在 B 节点内。同级节点间,触点归属于处于顶层的节点。

此时假如 C 节点还存在父节点,则还能够经过事情冒泡的机制传递接触事情给父节点。

这一点便是我们所需求注意的,因为牌组中的牌都处于同一层,因而不能直接监听每一张牌的接触事情,这样接触点归属一张牌之后,其他牌就无法收到事情传递,因而不能监听每张牌的接触事情,需求一个接触层,用来承受接触事情,经过判别接触点是否落在卡牌上。

原理

CocosCreator: 实现斗地主滑动选择多张牌

如上图,因为每张牌都要按照一个固定位移差摆放着,因而检查接触规模时,只要最终一张卡牌的接触规模是整张牌规模,其他牌的接触规模只要检查显现的部分。

CocosCreator: 实现斗地主滑动选择多张牌

不论接触点开始方位时在接触结尾的左边还是右边,只要判别卡牌的显现部分处于开始点和结尾之间便是被选中的状况

完成

1,创立一个卡牌视图类,用来控制卡牌被选中,挂起和复位作用,代码如下:

export default class CardView {
    ...
    /** 原始方位 */
    private _orginPos: cc.Vec3 = cc.v3();
    /** 是否被选中 */
    private _isSelect: boolean = false;
    public get isSelect (): boolean {
        return this._isSelect;
    }
    public set isSelect (isSelect: boolean) {
        this._isSelect = isSelect;
        this.selectStateImg.active = isSelect;
    }
    /** 是否挂起 */
    private _isUp: boolean = false;
    public isUp (): boolean {
        return this._isUp;
    }
    /** 牌之间的间隔 */
    private _interval: number = 0;
    public set interval (interval: number) {
        this._interval = interval;
    }
    public get interval (): number {
        return this._interval;
    }
    /** 被选中后,挂起的牌复位,原位的牌挂起 */
    public checkUp () {
        if (this._isUp) {
            this.unselectAction();
        } else {
            this.selectAction();
        }
    }
    /** 被选中后挂起动画 */
    public selectAction () {
        if (this._isUp) {
            return;
        }
        cc.tween(this.node)
            .to(0.1, { position: cc.v3(this.node.x, this._orginPos.y + 20, 0) }, { easing: 'sineIn'})
            .call(() => {
                this._isUp = true;
            })
            .start();
    }
    /** 复位动画 */
    public unselectAction () {
        if (!this._isUp) {
            return;
        }
        cc.tween(this.node)
            .to(0.1, { position: cc.v3(this.node.x, this._orginPos.y, 0) }, { easing: 'sineIn'})
            .call(() => {
                this._isUp = false;
            })
            .start();
    }
}

2, 创立接触层, 用来监听接触事情

xport default class CardPanelView extends EventComponent {
    @inject("playerPanel1", cc.Node)
    playerPanel1: cc.Node = null;
    /** 根视图 */
    private _gameView: UIView = null;
    public set gameView (view: UIView) {
        this._gameView = view;
    }
    /** 创立的牌组 */
    private _cardViews: cc.Node[] = [];
    /** 接触点开始方位 */
    private _startPos: cc.Vec2 = null;
    /** 接触到牌外之前的坐标 */
    private _moveEndPos: cc.Vec2 = null;
    /** 接触开始方位是否接触到牌 */
    private _isTouchedInCard: boolean = false;
    onLoad(): void {
        super.onLoad();
    }
    /** 监听接触事情 */
    addEvents(): void {
        this.onN(this.node, cc.Node.EventType.TOUCH_START, this._touchStart);
        this.onN(this.node, cc.Node.EventType.TOUCH_MOVE, this._touchMove);
        this.onN(this.node, cc.Node.EventType.TOUCH_END, this._touchEnd);
    }
    init() {
        this.initCards();
    }
    /** 初始化牌组 */
    initCards () {
        const cards = [0x1A, 0x1A, 0x1B, 0x1B, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x01, 0x02];
        createPrefab({
            url: DDZ_PREFAB_URL[0],
            view: this._gameView,
            complete: (node) => {
                for (let i = 0; i < cards.length; i++) {
                    const node1 = cc.instantiate(node);
                    this.node.addChild(node1);
                    node1.x = setCardPositionX(cards.length-1, i, node1.width, 60);
                    node1.y = this.playerPanel1.y;
                    const cardView1 = node1.addComponent(CardView); 
                    cardView1.gameView = this._gameView;
                    cardView1.interval = 60;
                    cardView1.value = cards[i];
                    this._cardViews.push(node1);
                }
            }
        });
    }
    private _touchStart (ev : cc.Event.EventTouch) {
        this._startPos = ev.getLocation();
        this._moveEndPos = this._startPos;
        this._isTouchedInCard = this._isTouchedCard(this._startPos);
    }
    private _touchMove (ev : cc.Event.EventTouch) {
        /** 开始接触点在牌组规模外,不进行接触检测 */
        if (!this._isTouchedInCard) return;
        /** 接触点移动到牌组规模外,就不继续进行新的碰撞检测 */
        const currentPos = ev.getLocation();
        const isCurTouchedCard = this._isTouchedCard(currentPos);
        if (!isCurTouchedCard) {
            return;
        }
        this._moveEndPos = currentPos;
        /** 碰撞检测,检测到牌显现部分在开始和结尾规模之间,则为选中状况,否则为选中 */
        this._cardViews.forEach((card, index, arr) => {
            const cardRect = card.getBoundingBoxToWorld();
            const cardView = card.getComponent(CardView);
            cardRect.width = index === arr.length - 1 ? cardRect.width : cardView.interval;
            if (this._isCardInSelectionRange(cardRect, this._startPos, this._moveEndPos)) {
                cardView.isSelect = true;
            } else {
                cardView.isSelect = false;
            }
        });
    }
    private _touchEnd (ev : cc.Event.EventTouch) {
        const currentPos = ev.getLocation();
        // 选中牌意外的区域,一切牌复位
        const _isEndTouchedInCard = this._isTouchedCard(currentPos);
        if (!this._isTouchedInCard && !_isEndTouchedInCard) {
            this._cardViews.forEach((card, index, arr) => {
                const cardView = card.getComponent(CardView);
                if (cardView.isSelect)
                    cardView.isSelect = false;
                if (cardView.isUp) {
                    cardView.unselectAction();
                }
            });
            return;
        }
        if (!this._isTouchedInCard) return;
        // 选中牌区域,挂起的牌复位,原始方位的牌挂起
        this._cardViews.forEach((card, index, arr) => {
            const cardRect = card.getBoundingBoxToWorld();
            const cardView = card.getComponent(CardView);
            cardRect.width = index === arr.length - 1 ? cardRect.width : cardView.interval;
            if (this._isCardInSelectionRange(cardRect, this._startPos, this._moveEndPos)) {
                cardView.checkUp();
            }
            if (cardView.isSelect)
                cardView.isSelect = false;
        });
    }
    /** 判别牌显现区域是否在两个点规模内 */
    private _isCardInSelectionRange(cardRect: cc.Rect, startPos: cc.Vec2, currentPos: cc.Vec2): boolean {
        const minX = Math.min(startPos.x, currentPos.x);
        const maxX = Math.max(startPos.x, currentPos.x);
        const minY = Math.min(startPos.y, currentPos.y);
        const maxY = Math.max(startPos.y, currentPos.y);
        if (cardRect.x + cardRect.width < minX || cardRect.x > maxX || cardRect.y + cardRect.height < minY || cardRect.y > maxY) {
            return false;
        }
        return true;
    }
    /** 判别是否在牌组内 */
    private _isTouchedCard (targetPos: cc.Vec2) {
        let isTouchedCard = false;
        this._cardViews.forEach((card, index, arr) => {
            const cardRect = card.getBoundingBoxToWorld();
            const cardView = card.getComponent(CardView);
            cardRect.width = index === arr.length - 1 ? cardRect.width : cardView.interval;
            if (this._isCardInSelectionRange(cardRect, targetPos, targetPos)) {
                isTouchedCard = true;
            }
        });
        return isTouchedCard;
    }
}

代码就不多解说了,看注释。 这样就根本能够完成多选卡牌的功用,作用如下:

CocosCreator: 实现斗地主滑动选择多张牌