
Утилитный класс для чтения настроек
В процессе разработки игр не раз убеждался что крайне полезно хранить все более-менее значимые настройки в одном месте, а именно — файле настроек. Этот подход имеет как минимум следующие преимущества —
Хочу поделиться с сообществом небольшим утилитным классом для быстрого и удобного чтения настроек из xml-файла. Он позволяет имея файл настроек следующего вида —
Легко читать его следующим образом —
Структура файла настроек может быть другой, для этого надо изменить выборку аттрибутов в методе getProperty. Т.е. класс по сути делает мэппинг своих фэйковых пропертей на одноименные аттрибуты файла настроек. Класс также можно легко модифицировать в релизной версии для чтения заэмбеженного xml-файла.
Собственно, сам класс —
Буду рад если кому-то этот класс окажется полезным. Спасибо за внимание.
- все параметры и настройки локализованы в одном месте, а не хаотично разбросаны по классам проекта
- возможность вносить изменения в настройки и смотреть изменения без перекомпиляции проекта
Хочу поделиться с сообществом небольшим утилитным классом для быстрого и удобного чтения настроек из xml-файла. Он позволяет имея файл настроек следующего вида —
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<app_settings>
<property id="SCREEN_WIDTH" value="640" />
<property id="SCREEN_HEIGHT" value="480" />
<property id="FPS" value="50" />
<property id="STAGE_SCALE_MODE" value="noScale" />
<property id="PLAY_BUTTON_X" value="100" />
...................................................
</app_settings>
</settings>
Легко читать его следующим образом —
var s:Settings = Settings.getInstance();
stage.frameRate = s.FPS;
stage.scaleMode = s.STAGE_SCALE_MODE;
.......................................
Структура файла настроек может быть другой, для этого надо изменить выборку аттрибутов в методе getProperty. Т.е. класс по сути делает мэппинг своих фэйковых пропертей на одноименные аттрибуты файла настроек. Класс также можно легко модифицировать в релизной версии для чтения заэмбеженного xml-файла.
Собственно, сам класс —
package
{
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.flash_proxy;
import flash.utils.Proxy;
public dynamic class Settings extends Proxy implements IEventDispatcher
{
public function Settings()
{
if (m_instance)
throw new Error("Use Settings.getInstance()");
m_event_dispatcher = new EventDispatcher();
var urlRequest:URLRequest = new URLRequest(m_url);
m_url_loader = new URLLoader();
m_url_loader.addEventListener("complete", onDataLoaded)
m_url_loader.addEventListener("ioerror", onDataFailed)
m_url_loader.load(urlRequest);
}
public static function getInstance():Settings
{
if (m_instance == null)
{
m_instance = new Settings();
}
return m_instance;
}
private function onDataLoaded(e:Event):void
{
m_data = XML(m_url_loader.data);
dispatchEvent(new Event("SETTINGS_LOADED"));
}
private function onDataFailed(e:ErrorEvent):void
{
trace(e);
}
override flash_proxy function getProperty(name:*):*
{
var str:String = name;
return m_data.app_settings.property.(@id == str).@value;
}
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
{
m_event_dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public function dispatchEvent(event:Event):Boolean
{
return m_event_dispatcher.dispatchEvent(event);
}
public function hasEventListener(type:String):Boolean
{
return m_event_dispatcher.hasEventListener(type);
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
{
m_event_dispatcher.removeEventListener(type, listener, useCapture);
}
public function willTrigger(type:String):Boolean
{
return m_event_dispatcher.willTrigger(type);
}
private static var m_instance:Settings;
private var m_url:String = "settings.xml";
private var m_url_loader:URLLoader;
private var m_data:XML;
private var m_event_dispatcher:EventDispatcher;
}
}
Буду рад если кому-то этот класс окажется полезным. Спасибо за внимание.
- +8
- MikRad
Комментарии (14)
Обычный ini формат в 100 раз лучше поддерживается вручную + элементарно парсится двумя сплитами:
Ну а в целом подход с вынесением настроек правильный, конечно. :)
На самом деле даже данные для баланса можно в игре прямо на ходу в консоли подтюнивать, а потом нажать педаль и сгенерить AS файл.
XML универсальнее — им можно описать и уровни, и гуи, да все что угодно можно описать) Нужно извращаться, чтобы реализовать вложенность тегов и атрибуты в INI. Хотя вот в анриловском движке извратились все-таки :)
Ну есть вариант задания разделов типа
и т.д.
просто вместо A=1 писать это бесчеловечно! :)
Что касается тенденций — загляни в конфиг софтины CruiseControl.NET — у нас на ней вертятся несколько проектов и параметров настроек там получается стопятьсот к кучей уровней вложенности и прочей чепухой. Юзать здесь ини было бы самоубийством.
XML хорош не только сам по себе для более-менее сложных данных, а больше скорее дополнительными плюшками — вроде трансформаций XSL и прочих.
Понятно, что ini формат не универсальное средство от всех болезней, просто то, что легче поддерживать и легче обрабатывать априори лучше.
А так можно вообще настройки в бинарник упаковать, на RAID 50 записать и использовать дополнительный софт для их изменения с встроенным контролем версий, желательно еще через веб. :) А вы XML, XML…