当前位置:嗨网首页>书籍在线阅读

23-游戏状态机

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.9.1 游戏状态机

状态机是一个编程的结构,它允许游戏在任何时间都只处于单一的应用状态之中。这里将为游戏创建一个状态机,命名为application state。它包含7个状态,由以下7个常量来代表:

  • GAME_STATE_TITLE;
  • GAME_STATE_NEW_GAME;
  • GAME_STATE_NEW_LEVEL;
  • GAME_STATE_PLAYER_START;
  • GAME_STATE_PLAY_LEVEL;
  • GAME_STATE_PLAYER_DIE;
  • GAME_STATE_GAME_OVER。

为每个状态创建一个函数对象。每个函数包含了该状态所要运行的游戏逻辑,并且在适当的时候进入新的状态。在创建每一个新游戏时都可以使用相同的结构,只需要将每个状态所引用的函数内容换掉即可。

下面看看如何实现这个架构的基本版本。使用一个名为currentGameStateFunction的变量引用函数,以及一个名为currentGameState的整数变量保存当前应用状态的常量值。

var currentGameState = 0;
var currentGameStateFunction = null;

创建一个名为switchAppState()的函数,当切换到新状态时,调用这个函数。

function switchGameState(newState){
  currentGameState = newState;
  switch (currentGameState){
   case GAME_STATE_TITLE:
     currentGameStateFunction = gameStateTitle;
     break;
   case GAME_STATE_PLAY_LEVEL:
     currentGameStateFunctionappStatePlayeLevel;
     break;
   case GAME_STATE_GAME_OVER:
     currentGameStateFunction = gameStateGameOver;
     break;
  }
}

在gameLoop()函数中,通过调用setTimeOut()函数,触发runGame()函数的重复调用,从而启动应用程序。在setTimeout()函数中设置重复调用runGame()函数。runGame()函数在每个帧时隙中调用currentGameStateFunction所引用的函数。这样可以根据应用状态的变化,很轻松地切换runGame()所调用的函数。

gameLoop();
function gameLoop() {
   runGame();
   window.setTimeout(gameLoop, intervalTime);
}
function runGame(){
  currentGameStateFunction();
}

下面看一下完整的代码。为每个不同的应用状态函数创建一个壳函数。在应用启动前,先调用switchGameState()函数,传入一个常量值,将想要运行的函数赋值给currentGameStateFunction变量。

//*** 应用程序启动
  switchGameState(GAME_STATE_TITLE);

在例8-9中,使用GAME_STATE_TITLE状态绘制一个简单的标题画面,并在每一帧中重新绘制。

例8-9 标题画面状态

<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false); 
function eventWindowLoaded(){
  canvasApp();
}
  function canvasApp(){
  var theCanvas = document.getElementById('canvas');
  if (!theCanvas || !theCanvas.getContext){
   return;
  }
  var context = theCanvas.getContext('2d');
  if (!context){
   return;
  }
  //应用状态
  const GAME_STATE_TITLE=0;
  const GAME_STATE_NEW_LEVEL=1;
  const GAME_STATE_GAME_OVER=2;
  var currentGameState=0;
  var currentGameStateFunction=null;
  function switchGameState(newState){
   currentGameState=newState;
   switch (currentGameState){
     case GAME_STATE_TITLE:
       currentGameStateFunction=gameStateTitle;
       break;
     case GAME_STATE_PLAY_LEVEL:
       currentGameStateFunctionappStatePlayeLevel;
       break;
     case GAME_STATE_GAME_OVER:
       currentGameStateFunction=gameStateGameOver;
       break;
   }
  }
  function gameStateTitle(){
   ConsoleLog.log("appStateTitle");
   // 绘制背景和文字
   context.fillStyle = '#000000';
    context.fillRect(0, 0, 200, 200);
   context.fillStyle = '#ffffff';
   context.font = '20px sans-serif';
   context.textBaseline = 'top';
   context.fillText ("Title Screen", 50, 90);
  }
  function gameStatePlayLevel(){
   ConsoleLog.log("appStateGamePlay");
  }
  function gameStateGameOver(){
   ConsoleLog.log("appStateGameOver");
  }
  function runGame(){
   currentGameStateFunction();
  }
  //*** 应用程序启动
  switchGameState(GAME_STATE_TITLE);
  //**** 应用循环
  var FRAME_RATE=40;
  var intervalTime=1000/FRAME_RATE;
  gameLoop();
  function gameLoop() {
    runGame();
    window.setTimeout(gameLoop, intervalTime);
  }
}
//***** 对象原型 *****
//*** consoleLog工具对象
//创建构造函数
function ConsoleLog(){
}
//创建函数并将函数添加到类中
console_log=function(message){
  if(typeof(console)!== 'undefined' && console != null){
   console.log(message);
  }
}
//通过赋值向类中添加一个类方法/静态方法
ConsoleLog.log=console_log;
// consolelog日志对象结束
</script>

提示

例8-9中添加了一个在之前章节没有出现过的Consolelog对象。持续使用这个工具在浏览器的JavaScript日志窗口中输出有用的调试信息。因为如果没有打开控制台选项,有的浏览器可能会崩溃。添加Consolelog对象可以解决这个问题。不过,绝大多数支持Canvas的浏览器都不会发生崩溃。

本书将继续带领读者学习状态机,并在8.10节中为游戏创建一个状态机。