1

私は自分が書いた Facebook ゲームの新機能に取り組んでいます。このゲームでは、プレーヤーはヨーロッパの都市間を移動し、商品を配達して利益を得ることができます。私が追加しようとしているこの機能は、経路探索 AI をゲームに追加します。プレイヤーが移動先の都市を選択できるようにします。その後、ゲームは自動的にプレイヤーの列車を開始都市から目的地の都市まで軌道に沿って移動させます。私は AJAX と setTimeout() を使用してバックエンドからデータを取得し、都市を結ぶ線路に沿って列車が移動できるようにしています。私がやろうとしていることをよりよく理解するために (できれば) コードを参照してください。

問題は、setTimeout() が頻繁に呼び出されることです。ENROUTE と ARRIVED の 2 つの値を含む statusFinalDest というグローバル変数を設定しました。列車が ENROUTE である間、バックエンドが ARRIVED の statusFinalDest を返すまで、JS 列車移動関数は setTimeout を使用して自身を呼び出します。この時点で、列車移動タイムアウト ループは (おそらく) 終了します。ただし、バックエンドが処理するターンごとに myEventMoveTrainManual() を 1 回呼び出す代わりに、statusFinalDest の変更された状態を認識していないかのように、非常に頻繁に呼び出されます。この過剰な動作を終わらせるために、より多くの制限構造をコードに入れようとしましたが、それらは機能していないようです。

関連する FBJS (Facebook JS) コードは次のとおりです。

function myEventMoveTrainManual(evt) {
      if(mutexMoveTrainManual == 'CONTINUE') {
        //mutexMoveTrainManual = 'LOCKED';
        var ajax = new Ajax();
        var param = {};
        if(evt) {
          var cityId = evt.target.getParentNode().getId();
          var param = { "city_id": cityId };
        }
        ajax.responseType = Ajax.JSON;
        ajax.ondone = function(data) {
          statusFinalDest = data.status_final_dest;
          if(data.code != 'ERROR_FINAL_DEST') {

            // Draw train at new location
            trackAjax = new Ajax();
            trackAjax.responseType = Ajax.JSON;
            trackAjax.ondone = function(trackData) {
              var trains = [];
              trains[0] = trackData.train;
              removeTrain(trains);
              drawTrack(trackData.y1, trackData.x1, trackData.y2, trackData.x2, '#FF0', trains);

              if(data.code == 'UNLOAD_CARGO') {
                unloadCargo();
              } else if (data.code == 'MOVE_TRAIN_AUTO' || data.code == 'TURN_END') {
                moveTrainAuto();
              } else {
                /* handle error */
              }
              mutexMoveTrainManual = 'CONTINUE';
            }
            trackAjax.post(baseURL + '/turn/get-track-data');
          }
        }
        ajax.post(baseURL + '/turn/move-train-set-destination', param);
      }

      // If we still haven't ARRIVED at our final destination, we are ENROUTE so continue
      // moving the train until final destination is reached
      // statusFinalDest is a global var
      if(statusFinalDest == 'ENROUTE') {
        setTimeout(myEventMoveTrainManual, 1000);
      }
}

バックエンドの PHP コードは次のとおりです。

  public function moveTrainSetDestinationAction() {
    require_once 'Train.php';
    $trainModel = new Train();

    $userNamespace = new Zend_Session_Namespace('User');
    $gameNamespace = new Zend_Session_Namespace('Game');

    $this->_helper->layout()->disableLayout();
    $this->_helper->viewRenderer->setNoRender();

    $trainRow = $trainModel->getTrain($userNamespace->gamePlayerId);
    $statusFinalDest = $trainRow['status_final_dest'];
    if($statusFinalDest == 'ARRIVED') {
      $originCityId = $trainRow['dest_city_id'];
      $destCityId = $this->getRequest()->getPost('city_id');
      if(empty($destCityId)) {
        // If we arrived at final dest but user supplied no city then this method got called
        // incorrectly so return an error
        echo Zend_Json::encode(array('code' => 'ERROR_FINAL_DEST', 'status_final_dest' => $statusFinalDest));
        exit;
      }

      $gameNamespace->itinerary = $this->_helper->getTrainItinerary($originCityId, $destCityId);
      array_shift($gameNamespace->itinerary); //shift-off the current train city location
      $trainModel->setStatusFinalDest('ENROUTE', $userNamespace->gamePlayerId);
      $statusFinalDest = 'ENROUTE';
    }
    $cityId = $trainRow['dest_city_id'];
    if($trainRow['status'] == 'ARRIVED') {
      if(count($gameNamespace->itinerary) > 0) {
        $cityId = array_shift($gameNamespace->itinerary);
      }
    }
    $trainRow = $this->_helper->moveTrain($cityId);
    if(count($trainRow) > 0) {
      if($trainRow['status'] == 'ARRIVED') {
        // If there are no further cities on the itinerary, we have arrived at our final destination
        if(count($gameNamespace->itinerary) == 0) {
          $trainModel->setStatusFinalDest('ARRIVED', $userNamespace->gamePlayerId);
          $statusFinalDest = 'ARRIVED';
        }
        echo Zend_Json::encode(array('code' => 'UNLOAD_CARGO', 'status_final_dest' => $statusFinalDest));
        exit;
        // Pass id for last city user selected so we can return user to previous map scroll postion
      } else if($trainRow['track_units_remaining'] > 0) {
        echo Zend_Json::encode(array('code' => 'MOVE_TRAIN_AUTO', 'status_final_dest' => $statusFinalDest));
        exit;
      } else { /* Turn has ended */
        echo Zend_Json::encode(array('code' => 'TURN_END', 'status_final_dest' => $statusFinalDest));
        exit;
      }
    }
    echo Zend_Json::encode(array('code' => 'MOVE_TRAIN_AUTO_ERROR'));
  }
4

1 に答える 1

2

関数 myEventMoveTrainManual がイベント ハンドラーから呼び出されている場合、イベントが発生するたびに新しいタイマーが実行されることになります。

役立つはずの簡単なハックの 1 つは、新しいタイマーを開始する前にタイマーを強制終了することです。

var timerId;

//...
clearTimeout(timerId);
timerId = setTimeout(myEventMoveTrainManual, 1000);
//...

しかし、あなたが本当にやりたいことは、タイマー ループが既に実行されているかどうかを確認する別の関数を呼び出すことだと思います。そうでない場合は、myEventMoveTrainManual を呼び出します。

于 2010-07-26T02:20:46.167 に答える