Всем привет. Решил я немного наполнить журнал всякой писаниной по поводу программирования и всего такого. Но здесь меня поджидала сложность: с одной стороны текст хочется сделать интересным, а код прозрачным; с другой - у меня уже довольно много самописных модулей, от которых не хочется отказываться при написании демок или при разъяснении работы тех или иных хитростей программирования. Потому, чтобы всем было просто и уютно, я решил сначала начать с маленьких модулей, расписать их назначение и работу, а затем уже с чистой совестью подключать при необходимости в демонстрационных программах не боясь вопросов "а что это за модуль ты подключил?" Итак, начну с самого простого - ведения лога в играх. Что это? | ![]() |
По-простому: файл в который программа записывает информацию о своей работе. Зачем это нужно? Не знаю, бывал ли у вас случай (если нет - вам в какой-то степени повезло) слышать фразу: "твоя программа у меня не запустилась, вывелось сообщение что-то вроде Access violation" - очень и очень неприятно. В такие моменты чувствуешь свое бессилие - ведь по такой информации определить почему программа "упала" практически невозможно. Начинаешь задавать вопросы что-то вроде "а в какой момент? что при этом нажимал?" и т.д. Как вы все понимаете - юзер меньше всего знает, почему программа не работает и что вообще она делала в этот момент. Грамотным в такой момент будет ответ вроде: "пришли мне лог-файл, лежащий в папке трам-пам-пам".
Когда-то давно и я сделал для себя модуль, умеющий сохранять всякую информацию на диск.
Его можно найти по ссылке.
В этом модуле вы найдете один-единственный класс TLogManager:
TLogManager = class protected fFileName: String; fLogStrings: TStringList; fLastLogString: Integer; fLastSaveTime: Single; fReSaveTime: Single; Procedure SaveLogToStream(aFileStream: TStream); Procedure ReCreateFile; public Procedure SaveLogToFile; Procedure AddStringToLog(aString: String); Procedure DropStringToLog(aString: String); Procedure AddTimeToLog; Procedure DoProgress(const DeltaTime: Single); Constructor Create(const aFileName: String; const aReCreateFile: Boolean); Destructor Destroy; override; end;
и три функции:
Procedure Drop_StringToLog(aString: String); Procedure Add_StringToLog(aString: String); Procedure Set_LogManager(aLogManager: TLogManager);
Как это все работает? Очень просто!
Первым дело необходимо создать основной объект, который будет заниматься логом:
fLog := TLogManager.Create('log.txt', True);
Думаю, здесь все ясно. Мы создаем объект класса TLogManager, указываем файл log.txt в качестве файла для записи и выставляем флаг пересоздания файла (а не дописывания в конец) в значение "правда".
Теперь осталось установить этот объект в качестве основного объекта логов:
Set_LogManager(fLog)
Все! Настройка закончена.
Также важно позаботиться о регулярном вызове метода fLog.DoProgress(DeltaTime). Для чего? Штука в том, что доступ к файлам на жестком диске не всегда быстрое дело. Поэтому в данном случае используется следующая схема: сначала весь текст лога хранится в оперативной памяти, и только изредка (сейчас в коде жестко прописаны 30 секунд) вся накопившаяся информация записывается в файл лога.
Теперь из любого места программы можно вызвать:
- Drop_StringToLog(), если мы хотим тут же записать данные в лог-файл (выполняется перед критическими секциями, в которых возможен "вылет программы")
- Add_StringToLog(), если мы хотим отложить запись данных в лог.
Вот и всё. Просто, быстро и удобно. Не забудьте также уничтожить объект перед закрытием программы. Этот модуль я писал довольно давно и до сих пор им пользуюсь. Он более-менее безопасный: при отсутствие объекта для логирования информации, функции по добавлению информации просто не будут ничего выполнять.
Что же можно записывать в лог-файл? Да что угодно! ФПС вашей игры; уровни, в которые играл юзер в эту сессию; время прохождения каждого уровня; конфигурацию компьютера; отсутствие текстур/звуков/моделей игры; лицензионный ключ, используемый для игры и т.д!
Что же можно записывать в лог-файл? Да что угодно! ФПС вашей игры; уровни, в которые играл юзер в эту сессию; время прохождения каждого уровня; конфигурацию компьютера; отсутствие текстур/звуков/моделей игры; лицензионный ключ, используемый для игры и т.д!
Были у меня идеи и по созданию объекта в инициализационной секции, также витала идея насчет обработки сохранения информации по тикам внутреннего таймера - но все это по тем или иным причинам было откинуто. Возможно, когда-то наступит необходимость усовершенствования этого класса, но пока, вроде бы, все уютно. А вам как кажется?
Вообще, сегодня я планировал описать работу со звуковым движком Squall, но не сумел, так как увидел, что мой модуль для этой системы пронизан вызовами из uLog. Поэтому решил для начала осветить сторону, связанную с логированием ошибок и молчаливым продолжением работы программы.
Рад любым идеям, предложениям и пожеланиям.