一只腊鸭做游戏

决战-Pow!第五集:游戏主逻辑

2018-02-14  本文已影响5人  回忆并快

最近几天,忙收拾回家的东西,又落下了几天,噶咩纳塞。
为了方便逻辑的开发,我通过一个单例GameManager来管理游戏主逻辑。

这个主要是游戏玩法的处理,创建对象,删除对象,碰撞检测,游戏更新均通过此类完成。

// 此管理类继承于事件分发器,让其拥有事件处理的能力。
class GameManager extends Laya.EventDispatcher {
    private static _instance: GameManager = null;
    constructor() {
        super();
    }
    // 派发事件
    public dispatchEvent(type:string, data?:any){
        this.event(type, data);
    }
    // 单例
    public static getInstance(): GameManager {
        return this._instance || (this._instance = new this());
    }

    // 游戏是否运行中
    private _isRunning: boolean;
    public get isRunning(): boolean {
        return this._isRunning;
    }

    // 游戏上下文 持有引用
    private _gameContext: GameContext;
    public get gameContext(): GameContext {
        return this._gameContext;
    }

    // 游戏模式(分单机和网络版本)
    public get gameMode(): GameMode {
        return this._gameContext.param.mode;
    }

    // 游戏玩家列表
    private _listPlayer: List<PowPlayer> = new List<PowPlayer>();

    // 玩家死亡回调
    public onPlayerDie: any = null;

    // 玩家数据与id的Dictionary,用于通过id查找游戏玩家的数据
    private _mapPlayerData: Dictionary<number, PlayerData> = new Dictionary<number, PlayerData>();
    // 主玩家id
    private _mainPlayerId:number = 0;
    // 对手玩家id
    private _opponentPlayerId:number = 0;
    // 注册主游戏玩家数据
    public regPlayerData(data: PlayerData) {
        this._mainPlayerId = data.id;
        if (this._mapPlayerData.containsKeyValue(data.id)) {
            this._mapPlayerData.set(data.id, data);
        }else{
            this._mapPlayerData.addKeyValue(data.id, data);
        }
    }
    // 注册对手玩家数据
    public regOpponentData(data: PlayerData){
        this._opponentPlayerId = data.id;
        if (this._mapPlayerData.containsKeyValue(data.id)) {
            this._mapPlayerData.set(data.id, data);
        }else{
            this._mapPlayerData.addKeyValue(data.id, data);
        }
    }

    // 获取主玩家的数据
    public getMainPlayerData(){
        return this._mapPlayerData.get(this._mainPlayerId);
    }
    // 获取对手玩家的数据
    public getOpponentPlayerData(){
        return this._mapPlayerData.get(this._opponentPlayerId);
    }

    // 拳头列表
    private _playerPows:List<PowEntity> = new List<PowEntity>();
    // 对手拳头列表
    private _opponentPows:List<PowEntity> = new List<PowEntity>();

    // 添加拳头
    addPlayerPow(pow:PowEntity){
        this._playerPows.add(pow);
    }

    // 移除拳头
    removePlayerPowAt(i:number){
        let pow:PowEntity = this._playerPows.get(i);
        this._playerPows.removeAt(i);
        pow.releaseWithAnim(()=>{
            EntityFactory.releaseEntity(pow);
        });
    }

    // 添加对手的拳头
    addOpponentPow(pow:PowEntity){
        this._opponentPows.add(pow);
    }
    
    // 移除对手的拳头
    removeOpponentPowAt(i:number){
        let pow:PowEntity = this._opponentPows.get(i);
        this._opponentPows.removeAt(i);
        pow.releaseWithAnim(()=>{
            EntityFactory.releaseEntity(pow);
        });
    }

    // 帧更新
    enterFrame(frameIndex: number) {
        if (!this._isRunning) {
            return;
        }
        // 帧驱动
        if (frameIndex < 0) {
            this._gameContext.currentFrameIndex++;
        } else {
            this._gameContext.currentFrameIndex = frameIndex;
        }

        // 每一帧前先移除所有标记释放的对象(回池子)
        EntityFactory.clearReleaseObjects();

        // 玩家逻辑更新
        for (var i = 0; i < this._listPlayer.count; i++) {
            this._listPlayer.get(i).enterFrame(frameIndex);
        }

        // 拳头逻辑更新
        for (var i = 0; i < this._playerPows.count; i++) {
            let pow:PowEntity = this._playerPows.get(i) as PowEntity;
            pow.enterFrame(frameIndex);
            // 拳头碰撞到边界
            if(pow.hitBound()){
                // 血条更新
                if(this.onPlayerInfoUpdate != null){
                    let opponentData:PlayerData = GameManager.getInstance().getOpponentPlayerData();
                    opponentData.hp -= 10;
                    this.onPlayerInfoUpdate(opponentData, false);
                }
                // 删除当前这个拳头,记住要把index-1,因为删除了当前这个,需要回到上一个index
                this.removePlayerPowAt(i);
                i--;
            }
            // 获取当前碰撞到的拳头index
            let hitItemIdx:number = pow.hit(this._opponentPows);
            let hitItem:PowEntity = this._opponentPows.get(hitItemIdx);
            // 如果有
            if(hitItem != null){
                // 胜利检测
                let res:number = this.compete(pow.type, hitItem.type);
                if(res == 0){ // 平手
                    this.removePlayerPowAt(i);
                    i--;
                    this.removeOpponentPowAt(hitItemIdx);
                }else if(res == 1){ //胜利
                    this.removeOpponentPowAt(hitItemIdx);
                }else{ // 失败
                    this.removePlayerPowAt(i);
                    i--;
                }
            }
        }

        // 对手拳头的逻辑处理,因为在主玩家拳头逻辑中处理了,这里只需要做边界碰撞处理
        for (var i = 0; i < this._opponentPows.count; i++) {
            let pow:PowEntity = this._opponentPows.get(i) as PowEntity;
            pow.enterFrame(frameIndex);
            if(pow.hitBound()){
                if(this.onPlayerInfoUpdate != null){
                    let mainPlayerData:PlayerData = GameManager.getInstance().getMainPlayerData();
                    mainPlayerData.hp -= 10;
                    this.onPlayerInfoUpdate(mainPlayerData, true)
                }
                this.removeOpponentPowAt(i);
                i--;
            }
        }
        // 检测当前是否有已经生命值为0的玩家
        let listDiePlayerId: List<number> = new List<number>();
        for (var i = 0; i < this._listPlayer.count; i++) {
            let player:PowPlayer = this._listPlayer.get(i);
            let playerData:PlayerData = player.data;
            if (playerData.hp <= 0) {
                listDiePlayerId.add(playerData.id);
                this.releasePlayerAt(i);
                i--;
                continue;
            }
        }
        // 死亡回调
        if (this.onPlayerDie != null) {
            for (var i = 0; i < listDiePlayerId.count; i++) {
                this.onPlayerDie(listDiePlayerId.get(i))
            }
        }
    }

    public onPlayerInfoUpdate:any = null;

    // 0 平手 1 胜 -1 败 (剪刀石头布的判定)
    public compete(myType:GameVKey, otherType:GameVKey){
        if (myType == otherType) return 0;
        return (myType - otherType + 3) % 3 == 1 ? 1 : -1;
    }

    // 游戏创建
    createGame(param: GameData) {
        if (this._isRunning) {
            Logger.log("Game is Running.");
            return;
        }
        Logger.log("Game is create success.")
        // 建立游戏上下文(主要用于管理当前游戏的元信息)
        this._gameContext = new GameContext();
        this._gameContext.param = param;
        this._gameContext.random = param.randomSeed;
        this._gameContext.currentFrameIndex = 0;

        // 初始化工厂
        EntityFactory.init();
        ViewFactory.init(Laya.stage);

        this._isRunning = true;
    }

    // 创建玩家
    createPlayer(playerId: number) {
        let data: PlayerData = this._mapPlayerData.get(playerId);
        if (data != null) {
            let player: PowPlayer = new PowPlayer();
            player.create(data);
            this._listPlayer.add(player);
        } else {
            Logger.log('PlayerData找不到~');
        }
    }

    // 释放游戏
    releaseGame() {
        if (!this._isRunning) {
            return;
        }
        this._isRunning = false;

        for (var i = 0; i < this._listPlayer.count; i++) {
            this._listPlayer.get(i).release();
        }
        this._listPlayer.clear();
        
        for (var i = 0; i < this._playerPows.count; i++) {
            this._playerPows.get(i).release();
        }
        this._playerPows.clear();

        for (var i = 0; i < this._opponentPows.count; i++) {
            this._opponentPows.get(i).release();
        }
        this._opponentPows.clear();

        ViewFactory.release();
        EntityFactory.release();

        this.onPlayerDie = null;
    }

    // 玩家逻辑输入
    inputVKey(vkey: number, args: any, playerId: number) {
        if (playerId == 0) {
            // 处理其他vkey,全局的vkey
            this.handleOtherVKey(vkey, args, playerId);
        } else {
            // Logger.log('playerId' + playerId);
            let player: PowPlayer = this.getPlayer(playerId);
            // Logger.log('player' + player);
            if (player != null) {
                player.inputVKey(vkey, args);
            } else {
                // 处理其他vkey
                this.handleOtherVKey(vkey, args, playerId);
            }
        }
    }

    handleOtherVKey(vkey: number, args: any, playerId: number) {
        // 全局的VKey处理
        // let hasHandled:boolean = false;
        // hasHandled = hasHandled || this.doCreatePlayer(vkey, args, playerId);
        // hasHandled = hasHandled || this.doReleasePlayer(vkey, args, playerId)
        // this.doCreatePlayer(vkey, args, playerId);;
    }

    // 执行创建玩家
    doCreatePlayer(vkey: number, args: any, playerId) {
        if (vkey == GameVKey.CREATE_PLAYER) {
            this.createPlayer(playerId);
            return true;
        }
        return false;
    }

    // 执行释放玩家
    doReleasePlayer(vkey: number, args: any, playerId) {
        if (vkey == FSPVKeyBase.GAME_EXIT) {
            this.releasePlayer(playerId);
            return true;
        }
        return false;
    }

    // 释放玩家
    releasePlayer(playerId: number) {
        let index: number = this.getPlayerIndex(playerId);
        this.releasePlayerAt(index);
    }

    // 释放指定位置的玩家
    releasePlayerAt(index: number) {
        if (index >= 0) {
            let player: PowPlayer = this._listPlayer.get(index);
            this._listPlayer.removeAt(index);
            player.release();
        }
    }

    // 获得玩家列表
    getPlayerList() {
        return this._listPlayer;
    }

    // 获取玩家
    getPlayer(playerId: number) {
        let index: number = this.getPlayerIndex(playerId);
        if (index >= 0) {
            return this._listPlayer.get(index);
        }
        return null;
    }

    // 获得玩家index
    getPlayerIndex(playerId: number) {
        // Logger.log(this._listPlayer);
        for (var index = 0; index < this._listPlayer.count; index++) {
            if (this._listPlayer.get(index).id == playerId) {
                return index;
            }
        }
        return -1;
    }

    // 初始化
    initialize() {
        Logger.log("initialize.")
    }
}

这里逻辑比较多,我直接贴代码,但上面有相应的注释,你应该看的明白~

创建一个PVEGame,人机对战游戏

class PVEGame{
    public onGameEnd:any = null;
    public onMainPlayerDie:any = null;
    private _mainPlayerId:number = 1;
    private _frameIndex:number = 0;
    private _pause = false;
    private _gameContext:GameContext;
    private _startTime:number = 0;
    constructor(){}
    // 游戏开始
    start(param:GameData){
        // 创建游戏
        GameManager.getInstance().createGame(param);
        GameManager.getInstance().onPlayerDie = this.onPlayerDie.bind(this);
        this._gameContext = GameManager.getInstance().gameContext;

        // 初始化输入
        GameInput.getInstance().initialize();
        GameInput.getInstance().onVKey = this.onVKey.bind(this);

        // 创建用户数据
        let ud:UserData = new UserData();
        ud.id = 999999;
        ud.coin = 1000;
        ud.name = "quinsmpang";
        ud.powid = 1;
        UserManager.getInstance().updateMainUserData(ud);
        // 需要注册player数据
        this.createPlayer();
        this.createOpponent();

        Logger.log("cur ms:" + Laya.timer.currTimer);
        this._startTime = Laya.timer.currTimer;
        // enterFame 帧循环
        Laya.timer.frameLoop(1, this, this.update);
    }
    createPlayer(){
        // 创建玩家
        let data:PlayerData = new PlayerData();
        data.id = this._mainPlayerId;
        data.userId = UserManager.getInstance().mainUserData.id;
        data.powData.speed = -10;
        data.powData.id = 3;
        data.powData.size = 60;
        data.powData.offsetY = -55;
        data.ai = 0;
        data.isMain = true;
        data.hp = data.maxHp = 100;
        GameManager.getInstance().regPlayerData(data);
        GameManager.getInstance().createPlayer(data.id);
    }
    createOpponent(){
        // 创建对手
        let data:PlayerData = new PlayerData();
        data.id = 2;
        data.powData.speed = 10;
        data.powData.id = 3;
        data.powData.size = 60;
        data.powData.offsetY = -55;
        data.ai = 1;
        data.isMain = false;
        data.hp = data.maxHp = 100;
        GameManager.getInstance().regOpponentData(data);
        GameManager.getInstance().createPlayer(data.id);
    }
    rebornPlayer(){
        // 重建玩家
        this.createPlayer();
    }
    stop(){
        // 停止
        Laya.timer.clear(this, this.update);
        GameInput.getInstance().finalize();
        GameManager.getInstance().releaseGame();
        this.onGameEnd = null;
        this.onMainPlayerDie = null;
        this._gameContext = null;
    }
    update(){
        // 游戏更新
        if(this._pause) return;
        this._frameIndex ++;
        GameManager.getInstance().enterFrame(this._frameIndex);
        this.checkTimeEnd();
    }
    
    public get isTimelimited() : boolean {
        // 是否限时
        return this._gameContext.param.mode == GameMode.PVE;
    }

    getRemainTime(){
        // 剩余时间
        if(this._gameContext.param.mode == GameMode.PVE){
            // return GameUtils.toInt((this._gameContext.param.limitedTime - this._gameContext.currentFrameIndex * 0.033333333));
            return this._gameContext.param.limitedTime - this.getElapsedTime();
        }
        return 0;
    }

    getElapsedTime(){
        // 流逝时间
        // return GameUtils.toInt(this._gameContext.currentFrameIndex * 0.033333333)
        // Logger.log("this ms:" + Laya.timer.currTimer);
        // Logger.log("s:" + GameUtils.toInt((Laya.timer.currTimer - this._startTime) * 0.001))
        return GameUtils.toInt((Laya.timer.currTimer - this._startTime) * 0.001);
    }

    getElapsedTimeMS(){
        // 流逝的时间ms
        // return GameUtils.toInt(this._gameContext.currentFrameIndex * 0.033333333)
        // Logger.log("this ms:" + Laya.timer.currTimer);
        // Logger.log("s:" + GameUtils.toInt((Laya.timer.currTimer - this._startTime) * 0.001))
        return Laya.timer.currTimer - this._startTime;
    }

    checkTimeEnd(){
        // 检查游戏是否结束
        if(this.isTimelimited){
            let remainTime = this.getRemainTime();
            // Logger.log("remainTime:" + remainTime);
            if(remainTime <= 0){
                this.terminate();
            }
        }
    }
    onVKey(vkey:number, args:any){
        // 用户输入
        GameManager.getInstance().inputVKey(vkey, args, this._mainPlayerId);
    }
    public onOpponentPlayerDie:any = null;
    // 玩家死亡处理
    onPlayerDie(playerId:number){
        if(this._mainPlayerId === playerId){
            if(this.onMainPlayerDie != null)
            {
                this.onMainPlayerDie();
            }else{
                Logger.log('OnPlayerDie() onMainPlayerDie == null!');
            }
        }else{
            if(this.onOpponentPlayerDie != null)
            {
                this.onOpponentPlayerDie();
            }else{
                Logger.log('OnPlayerDie() onOpponentPlayerDie == null!');
            }
        }
    }
    // 游戏暂停
    pause(){
        this._pause = true;
    }
    // 游戏继续
    resume(){
        this._pause = false;
    }
    // 游戏终止
    terminate(){
        this.pause();
        if(this.onGameEnd != null){
            this.onGameEnd();
        }
    }
}

最后就是通过一个插件来调用PVEGame的逻辑:

class PVEPlugin extends BasePlugin {
    constructor() {
        super();
    }
    private _game: PVEGame = null;
    open() {
        this.startGame(GameMode.PVE);
    }
    startGame(mode: GameMode) {
        let param: GameData = new GameData();
        param.mode = mode;

        this._game = new PVEGame();
        this._game.start(param);
        this._game.onGameEnd = this.stopGame.bind(this);
        // 打开战斗UI
        UICommon.showBackground();
        UIManager.getInstance().openPage(BattlePage, true);
    }
    stopGame() {
        if (this._game != null) {
            this._game.stop();
            this._game = null;
        }
        // 关闭战斗UI
        UIManager.getInstance().closePage(BattlePage);
    }
    getCurrentGame(): PVEGame {
        return this._game;
    }
    close(){
        this.stopGame();
    }
}

这个插件非常简单,控制UI和游戏逻辑。
到此,一个简单的单机游戏基本完成~里面涉及一些简单的游戏AI和游戏输入,下回补充讲解。

上一篇下一篇

猜你喜欢

热点阅读