Читать и писать игры сохранить данные


У меня есть класс, который считывает и записывает данные В и из .txt файл с помощью fstream.

Насколько я могу сказать, это именно то, что я хочу сделать:

  • если файл не существует, создайте его с исходных переменных по умолчанию (эти константы.)
  • если файл не существует, прочитайте его и задайте значения структуры данных в файл.
  • перезаписать файл с данными при вызове.
  • Я даже оставил себе немного сбросить функцию, что я могу использовать, чтобы позволить игроку, чтобы сбросить свои данные и начать от scatch (хотя удаление файла тоже будет работать)

Я не проверяю, что файл не был изменен, но я намерен иметь эти ценности проверены перед передачей их на свои объекты.

saveManager.ч:

#pragma once
#ifndef SAVEMANAGER
#define SAVEMANAGER
#include <fstream>
#include "saveData.h"

class saveManager
{
public:
    saveManager();

    void read(saveData& data);

    void reset();

    void write(saveData& data);
private:
    // default magic numbers for initializing data

    const int m_major = 1;
    const int m_minor = 1;
    const int m_highWorld = 1;
    const int m_highSubWorld = 1;
    const int m_loot = 0;
    const int m_batSpeed = 4;
    const int m_batSize = 64;
    const int m_maxBallSpeed = 18;
    const int m_ballSize = 10;
    const int m_ballStrength = 1;
    const int m_homing = 0;
    const int m_value = 1;
    const int m_magnet = 30;
    const int m_maxLives = 2;
    const int m_bossDmg = 1;
    const int m_phantom = 0;
    const int m_TBD = 0;

    std::fstream saveFile;
};
#endif // !SAVEMANAGER

saveManager.cpp:

#include "saveManager.h"

saveManager::saveManager()
{
    // do nothing
}

void saveManager::read(saveData& data)
{
    int major;
    int minor;
    saveFile.open("saveData.txt");
    if (saveFile.is_open())
    {
        saveFile >> major >> minor;
        if (major == data.major && minor >= data.minor)
        {
            saveFile >> data.highWorld >> data.highSubWorld >> data.loot >> data.batSpeed >> data.batSize >> data.maxBallSpeed >> data.ballSize >> data.ballStrength >> data.homing >> data.value >> data.magnet >> data.maxLives >> data.bossDmg >> data.phantom >> data.TBD;
        }
        // handle older versions here as needed
    }
    else
    {
        saveFile.open("saveData.txt", std::fstream::in | std::fstream::out | std::fstream::trunc);
        reset();
    }
    saveFile.close();
}

void saveManager::reset()
{
    if (!saveFile.is_open())
    {
        saveFile.open("saveData.txt");
        saveFile << m_major << " " << m_minor << " " << m_highWorld << " " << m_highSubWorld << " " << m_loot << " " << m_batSpeed << " " << m_batSize << " " << m_maxBallSpeed << " " << m_ballSize << " " << m_ballStrength << " " << m_homing << " " << m_value << " " << m_magnet << " " << m_maxLives << " " << m_bossDmg << " " << m_phantom << " " << m_TBD;
        saveFile.close();
    }
    else
    {
        saveFile << m_major << " " << m_minor << " " << m_highWorld << " " << m_highSubWorld << " " << m_loot << " " << m_batSpeed << " " << m_batSize << " " << m_maxBallSpeed << " " << m_ballSize << " " << m_ballStrength << " " << m_homing << " " << m_value << " " << m_magnet << " " << m_maxLives << " " << m_bossDmg << " " << m_phantom << " " << m_TBD;
        saveFile.close();
    }
}

void saveManager::write(saveData& data)
{
    if (!saveFile.is_open())
    {
        saveFile.open("saveData.txt");
        saveFile << data.major << " " << data.minor << " " << data.highWorld << " " << data.highSubWorld << " " << data.loot << " " << data.batSpeed << " " << data.batSize << " " << data.maxBallSpeed << " " << data.ballSize << " " << data.ballStrength << " " << data.homing << " " << data.value << " " << data.magnet << " " << data.maxLives << " " << data.bossDmg << " " << data.phantom << " " << data.TBD;
        saveFile.close();
    }
    else
    {
        saveFile << data.major << " " << data.minor << " " << data.highWorld << " " << data.highSubWorld << " " << data.loot << " " << data.batSpeed << " " << data.batSize << " " << data.maxBallSpeed << " " << data.ballSize << " " << data.ballStrength << " " << data.homing << " " << data.value << " " << data.magnet << " " << data.maxLives << " " << data.bossDmg << " " << data.phantom << " " << data.TBD;
        saveFile.close();
    }
}

Я чуть не забыл добавить данные.

saveData.txt:

1 1 1 1 0 4 64 18 10 1 0 1 30 2 1 0 0

Я любил бы любую обратную связь. Спасибо.



220
5
задан 7 марта 2018 в 05:03 Источник Поделиться
Комментарии
1 ответ

Дизайн

const int m_major = 1;
const int m_minor = 1;
const int m_highWorld = 1;
const int m_highSubWorld = 1;
const int m_loot = 0;
const int m_batSpeed = 4;
const int m_batSize = 64;
const int m_maxBallSpeed = 18;
const int m_ballSize = 10;
const int m_ballStrength = 1;
const int m_homing = 0;
const int m_value = 1;
const int m_magnet = 30;
const int m_maxLives = 2;
const int m_bossDmg = 1;
const int m_phantom = 0;
const int m_TBD = 0;

Эти цифры не имеют никакого, повторяю, никакого бизнеса, чтобы быть там, где они находятся сейчас.

Это вроде одного класса делает-все схемы, что код ниже будет падать на ваши ноги рано или поздно, а скорее рано. Давайте просто представим, ты продолжишь работу над игрой и добавить больше и больше функциональности. По состоянию на сейчас, у вас есть два отдельных значения по умолчанию для функций, т. е. пар, таких как m_batSpeed и m_batSize, m_major и m_minor и т. д. Теперь представьте, что вы добавить некоторую функциональность к вашей игре, которое добавляет около пяти новых субъектов. Это означает, что около 10 новых констант в среднем, просто в этом классе!

Я надеюсь, вы понимаете, что это не является устойчивым. Даже сейчас, нет никакого способа, чтобы сказать, что постоянное принадлежит фактического класса, кроме гадание по имени. Через год, вы еще знаете, какой класс m_TBD соответствует, или m_loot? Возможно, но это более чем вероятно, что вы забудете некоторые из них. Кроме того, если кто-нибудь когда-нибудь должен или хочет читать исходный код, они будут массово тупик, то раздражается.

Так что вы можете с этим поделать? Взять сериализатор-ориентированного подхода. Обычно, когда ты пишешь простые данные игры, вы будете иметь объекты, соответствующие текущему состоянию вы хотите сохранить. Решение я предлагаю подойти к классы этих объектов и вставить serialize метод, который записывает объект содержание в std::ostream. Чтобы реально сэкономить, вы просто позвоните сериализовать в порядке на все объекты, которые должны быть сохранены.

Второй шаг-это, конечно, добавлять похожие deserialize метод, который принимает std::istream и создает объект из считанных значений. Тогда у вас есть парсер файла в файл, определить размеры блоков, и называть эти методы десериализатор.

Огромный плюс этого решения в том, что это относительно легко реализовать, при этом позволяя инкапсулировать функциональность подальше, где она принадлежит, не в классе монстр, как вы в настоящее время. Недостатком является то, что разделение все-таки несколько неоптимальным, поскольку теперь у вас есть то, что в основном методы синтаксического анализатора в состояние игры классов. Это может быть смягчено путем использования шаблона проектирования фабрика, например. Кроме того, этот метод может стать грязным, если сложность savefile и игра в целом становится слишком большим, но я предполагаю, что это не тот случай для вашего проекта (и если это так, вы будете иметь, чтобы положить много думал в том, как справиться с этим ну, во всяком случае).

Другие советы и хитрости


  1. Использовать #pragma once или включают в себя охранников, а не обе. Они служат той же цели. Если вы действительно заботитесь о том, соответствующий стандартному, придерживаться включают в себя охранников. В большинстве остальных случаев, #pragma once тоже хорошо.

  2. Не выписать конструкторы по умолчанию, если они не делают ничего особенного, или вообще ничего. Компилятор достаточно хорошо, чтобы генерировать их автоматически для вас, когда ваш не определяют какие-либо другие конструкторы, которые вы в настоящее время нет.

  3. saveManager это большой класс, в плане размера. Одна из причин этого заключается в том, что все эти intС вас определение есть часть каждого объекта можно создавать и разносятся по всему везде, где ваш объект выходит, что совершенно ненужными, поскольку все константы. Вы, вероятно, хотите сделать эти static по крайней мере, или, поскольку они являются частными и недоступных в любом случае, даже удалить их из класса и ставить их исключительно на выполнение файла как static константы.

    Другая причина заключается в том, что вы носите с собой std::fstream вокруг, когда вы не нужно. На самом деле, вы относитесь к saveFile почти как локальная переменная: в каждый способ вы открываете основной файл заново, просто чтобы закрыть его, прежде чем вернуться. Хорошо, если вы сделаете это уже, то вам не нужно иметь его в качестве переменной-члена, потому что нет ничего, чтобы сохранить здесь. Просто создайте новый std::fstream каждый раз, который, в качестве дополнительного преимущества, и позволит вам избавиться от этих уродливых open() и close() звонки и полагаться на РАИИ вместо.


  4. Ваша самая длинная линия в настоящее время 422 символов. Это путь, путь слишком много. Нет безопасного и стабильного правило, что люди считают подходящей длины линии, но большинство программистов согласен что линий больше чем 100 и несколько персонажей слишком длинные (я лично являюсь учеником традиционной доктрины 80 столбцов). Чрезмерно длинные строки, как правило, очень неудобно работать (просто посмотрите на поля код в ваш вопрос!) может испортить ваш терминал, и, как правило, раздражало при выполнении таких действий, как, глядя на изменения кода или слияния коммитов.

  5. Обратите внимание на const правильность. void write(saveData& data) должно быть void write(saveData const& data) const (предполагая, что вы также реализовывать подсказку о std::fstream я дал в пункте 3). void read(saveData& data) должно быть void read(saveData& data) constили даже saveData read() const.

  6. Глядя на пункт 3 и пункт 5, Вы можете покончить с saveManager как класс вообще. Вам не нужно, чтобы сохранить любого государства, поэтому ваш класс практически не осталось. Положить read и write как автономные функции будет делать эту работу просто отлично. Если вы все же хотели бы сохранить эту "эти функции должны быть вместе"-отношения, вы могли бы поставить их в свое собственное пространство имен.

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

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

3
ответ дан 7 марта 2018 в 08:03 Источник Поделиться