
Framework: GameConfig
Прочитал статью flashgameblogs.ru/blog/actionscript/751.html и решил показать свой конфиг.
Цель данного класса — вынести основные игровые переменные во внешний файл для удобного балансирования игры геймдизайнером и последующая интеграция финального конфига в игру.
Вот так выглядит файл конфига:
Вложенность может быть любой.
Использование класса в игре. Создаём объект класса и подписываемся на событие GameConfig.EVENT_LOADED, чтобы знать, когда уже можно создавать игру.
В период разработки игры используется код загрузки конфига из внешнего файла:
Когда игра уже готова, мы передаём в конфиг непосредственно текст конфига:
В функции onStartGame уже можно обращаться к данным из конфига.
P.S. Идея с динамическим классом (из вышеупомянутой статьи) мне нравится, так что скорее всего переделаю свой конфиг так же, чтобы убрать лишнее звено «data» и обращаться напрямую к конфигу.
Цель данного класса — вынести основные игровые переменные во внешний файл для удобного балансирования игры геймдизайнером и последующая интеграция финального конфига в игру.
Вот так выглядит файл конфига:
<config>
<player>
<spells type="array">
<item>
<type type="string">fireball</type>
<power type="int">80</power>
<delay type="number">1.2</delay>
</item>
<item>
<type type="string">airwave</type>
<power type="int">50</power>
<delay type="number">0.7</delay>
</item>
</spells>
</player>
<enemies type="array">
<item>
<name type="string">Sharick</name>
<type type="string">Dog</type>
<agressive type="number">0.6</agressive>
<health type="int">100</health>
</item>
<item>
<name type="string">Murka</name>
<type type="string">Cat</type>
<agressive type="number">0.4</agressive>
<health type="int">60</health>
</item>
</enemies>
</config>
Вложенность может быть любой.
GameConfig
elmortem.game.config.GameConfigpackage elmortem.game.config {
import elmortem.loaders.DataLoader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
/**
* ...
* @author elmortem
*/
public class GameConfig extends EventDispatcher {
static public const EVENT_ERROR:String = "GameConfig.Error";
static public const EVENT_LOADED:String = "GameConfig.Loaded";
public var data:Object; // здесь будут лежать все данные из конфига
public var loaded:Boolean; // готов ли конфиг к работе
public function GameConfig() {
data = { };
loaded = false;
}
// загрузка данных из файла
public function loadFromUrl(url:String):void {
DataLoader.loadData(url, null, onLoad, null, onError);
}
private function onLoad(e:Event):void {
loadFromString(e.target.data);
}
private function onError(e:IOErrorEvent):void {
trace(e);
dispatchEvent(new Event(EVENT_ERROR));
}
// загрузка данных из строки
public function loadFromString(str:String):void {
var xml:XML;
try {
xml = new XML(str);
} catch (err:Error) {
dispatchEvent(new Event(EVENT_ERROR));
trace(err.message);
trace(str);
return;
}
// парсим данные
data = parse(xml);
loaded = true;
dispatchEvent(new Event(EVENT_LOADED));
}
// Самая главная функция!
// разбор данных и генерация значений конфига
private function parse(xml:XML):Object {
//trace(xml);
var i:int;
var c:XMLList;
var type:String = String(xml.@type);
// Данные могут быть нескольких типов:
// Object, Array, int, Number и String
if(type == "" || type == "object" || type == "obj" || type == "o") {
c = xml.child("*");
var obj:Object = { };
for (i = 0; i < c.length(); i++) {
obj[c[i].localName()] = parse(c[i]);
}
return obj;
} else if(type == "array" || type == "arr" || type == "a") {
c = xml.child("*");
var arr:Array = [];
// имя ноды (item) для массива не играет роли, но в конфиге можно использовать логически понятное имя
for (i = 0; i < c.length(); i++) {
arr.push(parse(c[i]));
}
return arr;
} else if (type == "string" || type == "str" || type == "s") {
// тут индейская хитрость, чтобы получить строку в виде xml, если это требуется
c = xml.child("*");
if(c == null) {
return String(xml);
} else {
var s:String = "";
for (i = 0; i < c.length(); i++) {
s += String(c[i]);
}
return s;
}
} else if (type == "int" || type == "i") {
return int(xml);
} else if (type == "number" || type == "num" || type == "n") {
return Number(xml);
} else if (type == "boolean" || type == "bool") {
// гибкое задание булевых значений (:
return toBool(String(xml));
}
return null;
}
static public function toBool(d:*):Boolean {
if (d is String && d == "true") return true;
if (d is int && d == 1) return true;
return false;
}
}
}
Использование класса в игре. Создаём объект класса и подписываемся на событие GameConfig.EVENT_LOADED, чтобы знать, когда уже можно создавать игру.
config = new GameConfig();
config.addEventListener(GameConfig.EVENT_LOADED, onStartGame);
В период разработки игры используется код загрузки конфига из внешнего файла:
config.loadFromUrl("config.xml");
Когда игра уже готова, мы передаём в конфиг непосредственно текст конфига:
config.loadFromString("<config>...</config>");
В функции onStartGame уже можно обращаться к данным из конфига.
private function onStartGame(e:Event):void {
trace(config.data.player.spells[0].power);
}
P.S. Идея с динамическим классом (из вышеупомянутой статьи) мне нравится, так что скорее всего переделаю свой конфиг так же, чтобы убрать лишнее звено «data» и обращаться напрямую к конфигу.
- +6
- elmortem
Комментарии (14)
Ну кроме невозможности вынести эту настройку в файл в любом случае?
Вообще же, я предполагаю, что наиболее удобным будет вариант, когда таким образом берутся не только настройки, а вообще всё: графика, музыка и т.д. При работе с внешним художником (и не только) будет очень удобно дать ему возможность сразу пробовать нарисованное в игре.