elmortem → Framework: GameConfig

Прочитал статью flashgameblogs.ru/blog/actionscript/751.html и решил показать свой конфиг.

Цель данного класса — вынести основные игровые переменные во внешний файл для удобного балансирования игры геймдизайнером и последующая интеграция финального конфига в игру.

Вот так выглядит файл конфига:
<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.GameConfig
package 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
  • 19 октября 2011, 15:20
  • elmortem

Комментарии (14)

RSS свернуть / развернуть
+
0
Не помешал бы
avatar

Vel

  • 19 октября 2011, 15:23
+
+2
Используй кат. :)
avatar

MikRad

  • 19 октября 2011, 15:59
+
+1
Кат потерял :)
avatar

saikspaik

  • 19 октября 2011, 16:00
+
+2
Я бы еще что-то в таком духе в класс добавил, чтоб вручную не копировать настройки:
public var USE_EXTERNAL_CONFIG:Boolean = true;
....
if(USE_EXTERNAL_CONFIG)
{
[Embed(source="config.xml",mimeType="application/octet-stream")]
private var ConfigClass:Class;
var configByteArray:ByteArray = new ConfigClass();
var configString:String = configByteArray.readUTFBytes(configByteArray.length);
config.loadFromString(configString);
}
avatar

romamik

  • 19 октября 2011, 16:26
+
0
только смысл флага наоборот перепутал…
avatar

romamik

  • 19 октября 2011, 16:27
+
0
В сам класс это не нужно добавлять, при использовании вполне можно сделать так, отличное решение.
avatar

elmortem

  • 19 октября 2011, 16:29
+
0
На самом деле в идеале конечно добавить в сам класс, а флаг вынести в настройки :-))))
avatar

romamik

  • 19 октября 2011, 16:37
+
0
А если я хочу 2 файла настроек сделать?
avatar

elmortem

  • 19 октября 2011, 17:35
+
0
А что мешает?
Ну кроме невозможности вынести эту настройку в файл в любом случае?

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

romamik

  • 19 октября 2011, 17:53
+
+1
Можно и с графикой такое провернуть, но нам пока так извращаться не требовалось. Да и нет особой надобности именно в игре графику смотреть. К тому же вряд ли уважающий себя художник будет копаться в каких-то текстовых файлов настроек. (:
avatar

elmortem

  • 19 октября 2011, 18:37
+
0
Кстати, я вот очень хочу 2 файла настроек сделать. Можно порезать?
avatar

grouzdev

  • 19 октября 2011, 20:32
+
0
Можно. «Во второй части». (:
avatar

elmortem

  • 19 октября 2011, 21:12
+
+3
Меня прям подмывает написать огромный пост о командной работе
avatar

grouzdev

  • 19 октября 2011, 21:35
+
+1
Дык давай, я бы почитал с удовольствием.
avatar

elmortem

  • 19 октября 2011, 23:20

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.