Generator函數在流程控制中的應用

扯蛋

作了兩年的Nodejs全棧開發,不知道爲何跑來作遊戲呢(大概是廈門nodejs很差找工做吧)。用的是網易的pomelo的遊戲框架。現接手了一個棋牌遊戲:二十一點,不懂的規則的能夠自行百度。
圖片描述javascript

二十一點遊戲流程圖

圖片描述

現狀

接手了平臺其餘相關遊戲的代碼,流程控制相互交錯,不易理解、難以維護。(多是剛作遊戲的緣由,若是大家有什麼更簡單的流程控制方法,歡迎分享)。我下意識的就想到了Generator函數的特性,感受用着這裏很是方便(之前一直以爲這是個異步流程控制中過分性質的方法,而且須要配合co才能自動執行,因此基本沒實際用過,koa不算...)java

Js Generator函數實現流程控制的優勢

一、易於理解、便於開發、容易維護(看到Generator函數猶如看到了流程圖)
二、開發思路清晰(每一個階段(函數)只須要關注本身的業務邏輯,完成直接下一步,而不用管下一步要作什麼操做)
三、不存在會忘記清除定時器的問題node

簡單說下Generator的執行流程

一、Generator函數執行後會生成一個Iterator。(注意不要加new)(簡單說就是個有next方法的對象,執行一次返回一個值)
二、每次next的調用,執行yield後面的語句並返回該語句執行的結果
三、每次只執行一個yield,後面的語句不會再執行、只有在執行下次next函數時才執行。(能夠利用這點作定時器的清理工做,並且能夠說基本不會忘記)
四、yield* 能夠將後面的變量(可迭代的變量,字符串、數組等)中的值一個一個的返回。執行一次返回其中的一個值數組

Js Generator函數完美實現流程圖代碼(部分)

class EsydProcess {

    constructor(room) {
        /**
         * ...其餘變量
         */
        this.flow = this.flowGenerator();
    }

    *['flowGenerator']() {

        yield this.betStage(); // 下注
        this.betStageTimer && clearTimeout(this.betStageTimer);// 下注階段完成後直接清除定時器。徹底不用擔憂定時器沒有被清理的狀況

        yield this.assignStage(); // 分牌

        if (this.esydCard.getCardPoint(this.bankerCards[0]) === 1) {

            yield this.ensureStage(); // 保險
            this.ensureStageTimer && clearTimeout(this.ensureStageTimer);

            if (this.esydCard.getCardType(this.bankerCards) !== CardTypes.BLACK_JACK) {
                yield* this.operateStage(); // 操做
            }

        } else {
            yield* this.operateStage(); // 操做
        }

        yield this.settleStage(); // 結算
    }

    *['operateStage']() {
        // 通知進入玩家操做階段
        this.noticeChangeStage(esydConsts.gameStage.OPERATE_STAGE);
        let players = this.players;
        for (let uid in players) {
            let player = players[uid];

            // 操做第一副牌
            yield this.changeOperatingPlayer(player);
            player.getCurCardInfo().isStop = true;
            this.operateTimer && clearTimeout(this.operateTimer);

            if (player.isSperated) {
                // 若是有一副牌,操做第二副牌
                player.curCardsIndex = 1;
                yield this.changeOperatingPlayer(player);
                player.getCurCardInfo().isStop = true;
                this.operateTimer && clearTimeout(this.operateTimer);
            }
        }
        yield this.bankerOperate(); // 莊家操做
    }

    // 轉到下個階段
    nextStage() {
        process.nextTick(() => {
            this.flow.next();
        });
    }
    
    // 開始遊戲
    start(seats) {
        /**
         * ...
         *
         */
        // 第一次next,直接進入下注階段。整個流程走完遊戲結束
        this.nextStage();
    }
    
    betStage(){
        // 進入下注階段
        this.noticeChangeStage(esydConsts.gameStage.BET_STAGE);
        /**
         * 其餘操做
         */
        this.betStageTimer = setTimeout(() => {
            this.betStageTimer = null;
            // 超時,直接進入下一步。沒有下注的玩家使用默認底注
            this.nextStage();
        }, esydConsts.stageTime.BET_STAGE);
    }

    assignStage(){
        /**
         * ...
         * 分牌操做,完成直接下一步
         */
        this.nextStage();
    }

    ensureStage(){
        /**
         * ...
         * 各類操做,如通知客戶端、開始超時定時器等。操做完後直接下一步就Ok了。只須要專一當前函數的功能,完成直接下一步
         */
        this.nextStage();
    }

    // 監聽用戶下注操做
    userBetOperateListener(uid){
    
        /**
         * ...
         * 檢查是否在下注階段,不是不能下注、記錄每一個玩家下注等
         */
         
        // 記錄已下注的玩家
        this.betedPlayers[uid] = true;
        if (Object.keys(this.betedPlayers).length === this.seatCount) {
            // 若是全部玩家都押注完畢,
            this.nextStage();
        }
    }
    
    /**
     *  其餘函數...
     */

}
相關文章
相關標籤/搜索