Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Хранение справочных данных (http://www.flasher.ru/forum/showthread.php?t=215657)

Appleman 18.10.2018 18:36

Tails, у меня к тебе вопрос по поводу используемой тобой реляционной модели. Вот смотри, я загоняю справочные данные в реляционные таблицы. Например, там могут быть профили каких-нибудь атак, как в примере, который ты приводил ранее в другой ветке. Так вот, этих самых атак может быть условно пяток видов по качеству (урон огнём, водой и т.п., в т.ч сочетания), при этом цифры по каждому виду имеют какой-то разброс. Таким образом, возможных уникальных комбинаций с ходу может за сотню зашкалить.

Каким образом ты создаёшь и систематизируешь идентификаторы подобных записей, чтобы потом ориентироваться в них и не сойти с ума?

Tails 18.10.2018 19:37

Например так: (Сущности и их свойства)
Тип атаки: id.
Атака: id, тип, damage, cooldown
Юнит: id, name, attack1, attack2

Код AS3:

// Заполняем таблички.
// Типы атак:
attackTypes.add(1); // Физическая.
attackTypes.add(2); // Огненная.
attackTypes.add(3); // Ледяная.
 
// Атаки:
attacks.add(1, 1, 100, 2); // Физическая атака, 100 урона, 2 сек кд.
attacks.add(2, 1, 500, 10); // Физическая атака, 500 урона, 10 сек кд.
attacks.add(3, 2, 2000, 100); // Огненная атака, 2000 урона, 100 сек кд.
 
// Юниты:
units.add(1, "Goblin", 1, 0); // Гоблин, может лупить физ уроном.
units.add(2, "Mage", 3, 0); // Маг, может лупить фаерболами.
units.add(3, "Paladin", 2, 3); // Паладин, может лупить сильным физ уроном и ещё параллельно кастовать фаерболы.
units.add(4, "Rabbit", 0, 0); // Безобидный кролик.

Обрати внимание, что из этих данных мы всё обо всех знаем. В коде нам не надо пилить никаких дополнительный условий сравнения типов, наследников и т.п.

Примерно такая структура в игре Warcraft 3. У них один юнит может иметь не более 2 атак. Ещё, id типа атаки можно захардкодить, так-как он не будет меняться от проекта к проекту. Это очень удобно:
Код AS3:

attackTypes.add(AttackType.PHYSICS); // Физическая.
attackTypes.add(AttackType.FIRE); // Огненная.
attackTypes.add(AttackType.ICE); // Ледяная.

Потом, в коде движка оперировать константами, а не волшебными числами, в формулах расчёта урона и т.п. Хороший пример, как некоторые данные зашиты в движок (ID Типа атаки). Движок просто берёт эту структуру и просчитывает её. Всё что ему нужно знать есть в табличках. Структуру данных ты можешь взять откуда угодно, захардкодить, загрузить с диска или из сети. Юниты другие, игра другая, а код тот-же.

Appleman 19.10.2018 10:07

Угу, более-менее ясно. Скажи, пожалуйста, какого типа переменные attackTypes, attacks и units и что делает метод add()? Здесь интерфейс используется?

Мне представлялось, что классы-парсеры - это "статики".

Добавлено через 4 часа 51 минуту
...да, и если взять твой блок кода с атаками, то там теоретические может быть та самая сотня записей. Как удержать в голове, что "1" - это физическая на 100, "3" - это магическая на 2000, а "103" - это холодом на 40? В этом собственно и был исходный вопрос.

Tails 19.10.2018 22:45

Цитата:

Какого типа переменные attackTypes, attacks и units
Списки с доступом данных по id или перечислением. Хранят соответствующие записи сущностей: AttackType, Attack и Unit соответственно.

Цитата:

Что делает метод add()
Метод списка, добавляет новую запись.

Цитата:

Здесь интерфейс используется?
Интерфейс, который Intarface? (Конструкция языка AS3) Нет, список - это просто обёртка над Object или Vector, с некоторыми публичными методами для удобной работы с данными. (Доступ записи по ID, как минимум)

Цитата:

Мне представлялось, что классы-парсеры - это "статики".
Это отдельный вопрос, я его тут никак не освещал. Парсер будет отдельным классом. Статики - плохо, лучше использовать обычный экземпляр класса, если нет конкретных причин для статики. Из соображений невозможности наследования статиков.

Цитата:

...да, и если взять твой блок кода с атаками, то там теоретические может быть та самая сотня записей. Как удержать в голове, что "1" - это физическая на 100, "3" - это магическая на 2000, а "103" - это холодом на 40?
Никак. Это структура данных, она предназначена не для запоминания людьми, а для удобного представления данных в памяти приложения, возможности расширения и т.д. Это данные для машины. Если работать с голыми данными становится сложно - пишется редактор, который будет отображать их для человека в удобном виде, с выпадающими списочками, подсветкой зависимостей и т.д. Если структура данных слишком сложная, то такая админка будет обязательно нужна.

Добавлено через 1 час 29 минут
пс. Обычно, каждый юнит имеет собственную, уникальную запись атаки. То есть, 2 юнита не могут ссылаться на одну и ту-же запись.

Если запись атаки уникальная для каждого юнита:
Отношение "1 к 1", можно редактировать атаку у каждого юнита отдельно.

Если запись атаки может быть общей для многих юнитов:
Отношение "1 к многим", редактируя одну запись, атака меняется у всех владельцев.

Это вопрос к проектированию бд, что конкретно требуется. Я привел пример "1 к многим" чисто для понимания связывания данных.

Appleman 20.10.2018 00:22

Прости мне моё занудство. Я на принципиальном уровне идею прекрасно понимаю (и имею опыт работы с реляционными СУБД как пользователь), но чего-то никак в голову не укладывается практическая реализация в создаваемой программе.

Цитата:

Сообщение от Tails (Сообщение 1206018)
Списки с доступом данных по id или перечислением. Хранят соответствующие записи сущностей: AttackType, Attack и Unit соответственно.

Не совсем понимаю, что ты подразумеваешь под списками. Или это и есть те самые классы, которые непосредственно взаимодействуют с json-таблицами?

Я себе как представлял твой пример. Класс атаки примерно такой:

Код AS3:

public class Attack
{
private var _id: uint;
private var _type: uint;
private var _damage: int;
private var _cooldown: uint;
 
public function Attack (id: uint)
{
_id = id;
var jsonData: Object = JsonAttackData.getData(_id);
_type = jsonData[type];
_damage = jsonData[damage];
_cooldown = jsonData[cooldown];
}

То есть в момент, когда нам требуется данная атака, программа создаёт экземпляр приведённого мной класса,
который получает в конструктор требуемый id, по нему ломится в класс-парсер, обслуживающий соответствующую json-таблицу, и получает от статического метода все остальные данные из таблицы.

Цитата:

Метод списка, добавляет новую запись.
Добавляет куда? В json-таблицу?

Цитата:

Интерфейс, который Intarface? (Конструкция языка AS3) Нет, список - это просто обёртка над Object или Vector, с некоторыми публичными методами для удобной работы с данными. (Доступ записи по ID, как минимум)
Ну да, который конструкция. Если у разных классов одинаковый публичный метод add(), то это похоже на использование интерфейса. Или это всё-таки не разные классы, а наследники?

Цитата:

Это отдельный вопрос, я его тут никак не освещал. Парсер будет отдельным классом. Статики - плохо, лучше использовать обычный экземпляр класса, если нет конкретных причин для статики. Из соображений невозможности наследования статиков.
Не понимаю, как этого можно добиться. Если у наших таблиц разное число полей и данные в них могут быть разного типа, то как ещё сделать можно? Один базовый класс и пачку наследников - по одному на каждый вид таблицы? Они же должны ещё "знать" и названия полей таблиц.

Tails 20.10.2018 02:07

Вложений: 1
Вот, я набросал небольшой пример. Есть реализация данных в отдельном JSON, парсинг.

Appleman 20.10.2018 16:13

Tails, огромное спасибо, тронут твоей заботой. Пример разобрал, теперь всё встало на места. Поясни ряд мелких моментов, плиз:

Я верно понял, что объявление переменных в классах AttackData, AttackTypeData и UnitData публичными - это такой коммунистичный вариант реализации дата-класса, чтобы не запариваться с приватными переменными и комплектом сеттеров-геттеров? Меня смущает, что раз данные всё-таки из таблицы переписываются в поля класса для дальнейшего использования, нет риска, что они в процессе будут изменены и тем самым поломаны? Почему всё-таки не используешь приватные? Чтобы парсер передавал значения прямо в конструктор создаваемого дата-класса, а забирать геттерами?

У тебя в момент запуска приложения все данные из json-таблиц читаются, парсятся и перегоняются в объекты-списки. Больше в рантайме исходные json-таблицы не используются, все классы Модели обращаются к классам-спискам, зная id нужных им вещей. Так?

Формат json-таблицы тоже весьма нестандартный у тебя. Так как это не классическая 2-мерная таблица "ключ: значение", а наборы "ключ: массив значений". Чем ты создашь их? Только самописный редактор или есть какие-то варианты заполнять комфортно вручную и потом экспортировать в подобный формат?
Я пока нашёл плагин для Гугл.докс, но он по-моему так не умеет :(

Твой вариант 100% отлично подходит для хранения справочных данных. А что ты делаешь с данными, которые меняются в рантайме? Тоже их по подобным спискам распихиваешь?

Добавлено через 2 минуты
P.S. Забыл... Почему обжекты хэшами называют?

Tails 20.10.2018 16:51

Цитата:

Я верно понял, что объявление переменных в классах AttackData, AttackTypeData и UnitData публичными - это такой коммунистичный вариант реализации дата-класса, чтобы не запариваться с приватными переменными и комплектом сеттеров-геттеров? Меня смущает, что раз данные всё-таки из таблицы переписываются в поля класса для дальнейшего использования, нет риска, что они в процессе будут изменены и тем самым поломаны? Почему всё-таки не используешь приватные? Чтобы парсер передавал значения прямо в конструктор создаваемого дата-класса, а забирать геттерами?
В конструктор передаю только id, иначе потом не очень удобно работать. В парсере, например. Лучше обращаться по имени свойства. Сами свойства открывать/закрывать для прямого редактирования геттер/сеттерами - дело автора. Я вот решил для простоты не усложнять.

Цитата:

У тебя в момент запуска приложения все данные из json-таблиц читаются, парсятся и перегоняются в объекты-списки. Больше в рантайме исходные json-таблицы не используются, все классы Модели обращаются к классам-спискам, зная id нужных им вещей. Так?
Да, всё верно. Зачем каждый раз парсить JSON? Это будет очень накладно, неудобно и можно легко ошибиться из-за отсутствия типизации.

Цитата:

Формат json-таблицы тоже весьма нестандартный у тебя. Так как это не классическая 2-мерная таблица "ключ: значение", а наборы "ключ: массив значений". Чем ты создашь их? Только самописный редактор или есть какие-то варианты заполнять комфортно вручную и потом экспортировать в подобный формат?
Ключ - имя таблицы, массив - её записи. Этот json написал вручную. Для небольших структур данных это не сложно. На больших проектах я использую админку или редактор.

Цитата:

Твой вариант 100% отлично подходит для хранения справочных данных. А что ты делаешь с данными, которые меняются в рантайме? Тоже их по подобным спискам распихиваешь?
Данные в рантайме будут находиться в объекте World из этого примера. Там тоже будут аналогичные списки, только, они ещё будут пулять события (Унаследованы от EventDispatcher) при добавлении/удалении из списка, изменения их свойств и т.д. События нужны для тех, кому интересно что происходит в игровом мире. (Вьюшке, например)
Например, если класс UnitData описывает юнит, то представление конкретного юнита в рантайме будет что-то вроде:
Unit:
id - ID Юнита. (Не путать с UnitData.id)
unitID - ID Описания юнита. (Ссылка на UnitData)
life - Текущее здоровье этого юнита.
...

Например, если на карте будет 10 гоблинов, у нас будет 10 записей Unit:
{ id:1, unitID:2, life:55 }
{ id:2, unitID:2, life:45 }
{ id:3, unitID:2, life:100 }
...

Цитата:

P.S. Забыл... Почему обжекты хэшами называют?
Я называю. Потому что Object реализует интерфейс хеш-таблицы. (Ассоциативный массив)
https://ru.wikipedia.org/wiki/%D0%A5...B8%D1%86%D0%B0

Appleman 20.10.2018 17:05

Цитата:

Сообщение от Tails (Сообщение 1206024)
Данные в рантайме будут находиться в объекте World из этого примера. Там тоже будут аналогичные списки, только, они ещё будут пулять события (Унаследованы от EventDispatcher) при добавлении/удалении из списка, изменения их свойств и т.д. События нужны для тех, кому интересно что происходит в игровом мире. (Вьюшке, например)

Например, если на карте будет 10 гоблинов, у нас будет 10 записей Unit:
{ id:1, unitID:2, life:55 }
{ id:2, unitID:2, life:45 }
{ id:3, unitID:2, life:100 }
...

Да, очень круто! То есть если в процессе игры гоблину прилетело от мага, то его life в списке будет отминусован, о чём список ещё и уведомит всех желающих.

Tails 20.10.2018 17:31

UnitData.life - Максимальное здоровье этого типа юнита.
Unit.life - Текущее здоровье конкретного юнита.
Это разные переменные. Можно написать UnitData.lifeMax, если это вводит в заблуждение.

UnitData - Описывает класс юнитов. Unit - Описывает конкретного юнита. Конкретным юнитом может быть кто угодно, Маг, Гоблин и т.д., смотря на кого он ссылается через unitID.

Цитата:

В классах юнитов у тебя только методы реализованы?
Какие методы? Обычно нет, обычно всё взаимодействие с миром происходит через апи объекта World. Например, для добавления юнита в мир, нужно вызывать не world.units.add() (Хотя этот метод есть у списка юнитов), а world.unitAdd(). Дело в том, что там бывает нужно не просто добавить юнит, а ещё выполнить некоторые другие действия, в том числе с другими списками. Кд обнулить, цену заплатить и т.д. Объект world содержит публичное апи для взаимодействия с игровым миром.
world.unitAdd()
world.unitKill()
world.buyUnit()

Ну, я тут уже не знаю. Тут уже зависит от проекта, что кому и как нужно.


Часовой пояс GMT +4, время: 19:55.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.