最近几天,忙收拾回家的东西,又落下了几天,噶咩纳塞。
为了方便逻辑的开发,我通过一个单例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和游戏输入,下回补充讲解。
网友评论