Сохранение состояния игры, когда состояние игры включает в себя функции - PullRequest
0 голосов
/ 28 марта 2019

Я работаю над простой веб-игрой, и мне нужно включить сохранение состояния игры, чтобы пользователи могли выбирать, где они остановились после закрытия браузера или обновления страницы.Игра полностью работает в браузере и использует базовый HTML / CSS / Javascript.

В настоящее время я использую локальное хранилище, поскольку оно обеспечивает простое решение проблемы сохранения состояния игры между сеансами:

function saveGame() {
  window.localStorage.setItem('gameData', JSON.stringify(game));
}

function loadGame() {
  game = JSON.parse(window.localStorage.getItem('gameData'));
}

Однако мое игровое состояние включает в себя массив функций и массив объектов, содержащих функции.Когда я загружаю предыдущее игровое состояние, эти функции и объекты не загружаются должным образом, потому что функции не могут быть преобразованы в JSON (по крайней мере, не так просто. Я попытался toString () для моих функций и eval () их, ихотя они, похоже, правильно хранятся, я не смог их восстановить. В любом случае это похоже на плохое решение).

Есть ли лучший способ сохранить и восстановить состояние игры, включающее функции?Это проблема модели данных?Кроме того, у меня есть случаи, когда HTML изменяется программно.Может ли HTML быть сохранен и затем повторно загружен, так что измененный HTML остается после перезагрузки?

Редактировать: Добавление игрового объекта для справки:

function Game() {
  this.player = new Player();
  this.triggerFnSet = new Set();
  this.tasks = [];
  this.activeTask = undefined;
  this.resources = { fame: {},
                     money: {},
                     beats: { instrument: "laptop",
                              clicksPer: 30,
                              xpPer: 5 },
                     samples: { instrument: "laptop",
                               resourcesPer: 25,
                               requiredResource: "beats",
                               xpPer: 50 },
                     notes: { instrument: "keyboard",
                              clicksPer: 50,
                              xpPer: 5 },
                     measures: { instrument: "keyboard",
                                 resourcesPer: 25,
                                 requiredResource: "notes",
                                 xpPer: 50 }
                    };
  this.specialResources = { songs: { instruments: ["laptop", "keyboard"],
                                     resourcesPer: 50,
                                     validResources: ["samples", "measures"],
                                     xpPer: 500 }};
  this.instruments = { laptop: { level: 1,
                                 currentTempo: "slow",
                                 tempoSpeeds: { slowest: 25,
                                                slow: 15,
                                                fast: 10,
                                                fastest: 5 },
                                 dropActive: false },
                       keyboard: { currentNote: undefined,
                                   currentSong: undefined }
                     };
};

Проблемными полями являются задачи иtriggerFnSet, где задача выглядит следующим образом:

function Task(name, tooltip, checkFn, failFn, startFn, tickFn, finishFn, timeToComplete) {
    this.name = name;
    this.tooltip = tooltip;
    this.checkFn = checkFn;
    this.failFn = failFn;
    this.startFn = startFn;
    this.tickFn = tickFn;
    this.finishFn = finishFn;
    this.timeToComplete = timeToComplete;
}

И triggerFn выглядит так, как показано ниже, и выполняется каждый тик внутри игры и удаляется из списка после возврата true:

function firstBeatTrigger() {
  if (game.player.stats.beats.lifetime >= 1) {
    document.getElementById('beats').style.display = "block";
    appendToOutputContainer("You've created your first beat. A building block to something greater.");
    game.triggerFnSet.add(tenthBeatTrigger);
    return true;
  }
}

Ответы [ 4 ]

2 голосов
/ 28 марта 2019

В этом случае вам, возможно, придется сопоставить десериализованный объект полной модели со всеми функциями. Возможно, JSON объединится с вашей любимой библиотекой. Кроме того, я не вижу проблем с сохранением HTML как значений в вашем объекте JSON. Но, действительно, отделите ваши функции от вашей модели. Все функции должны быть отдельным классом. Назовите это gameEngine или что-то.

1 голос
/ 29 марта 2019

Хотя, как уже отмечалось, ваше игровое состояние не должно зависеть от функций, возможно, стоит отметить, что массив функций можно решить, ссылаясь на клавишу, а не на функцию, и убедившись, что вы всегда имеетеобъект, содержащий возможные доступные функции. Итак: a={}, a.m=function () {}

и затем вызовите ваш массив следующим образом a[array[1]]() вместо array[1]()

. Этот подход не будет легко работать с объектами (если только они нефункции хоста) без необходимости переписывать код, который лучше потратить на более чистое решение, которое разделяет логику и данные.

0 голосов
/ 29 марта 2019

Прежде всего, большинство комментаторов здесь правы в том, что это в основном проблема модели данных.Я не собираюсь реорганизовывать модель, поэтому я рассматриваю следующий обходной путь для правильного ответа:

Сериализация функций в виде строковых ссылок:

function saveGame() {
  var triggers = [];

  game.triggerFnSet.forEach(function (trigger){
    triggers.push(trigger.name);
  });

  // Serialize regular game data
  window.localStorage.setItem('gameData', JSON.stringify(game));

  // Serialize triggerFns
  window.localStorage.setItem('triggers', JSON.stringify(triggers));
}

Затем десериализация выполняется путем поиска функцийчерез объект окна, преобразовывая их обратно в функции:

function loadGame() {
  var triggers = JSON.parse(window.localStorage.getItem('triggers'));
  var gameData = JSON.parse(window.localStorage.getItem('gameData'));

  if (gameData !== null) {
    game = gameData;
    game.triggerFnSet = new Set();

    triggers.forEach(function (trigger){
      game.triggerFnSet.add(window[trigger]);
    });
  }
}

Это работает, потому что мои функции не являются анонимными и существуют в глобальном контексте, который легко доступен для окна!

0 голосов
/ 28 марта 2019
function t(l){
   return l+1;
}

eval("("+t.toString()+")")(1)

кажется, что это работает , но я не рекомендую это

, кроме того, eval("function t(l){ return l+1;}") // без скобок

регистрирует функцию вобъект окна, позволяющий вам затем вызвать его

t(2);

(даже если он не вернет его)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...