Хранение данных в игре

Аватара пользователя
Snake_B
MOSC Team
Сообщения: 285
Регистрация: 25 янв 2011

Хранение данных в игре

Сообщение Snake_B »

Vasaka писал(а): 06 ноя 2011, 16:16Есть идея по архитектуре.
Предлагаю сделать параметр "не видим" для игрока такого-то, вместо "видим".
Что это даст?
Вначале игры, когда у нас мало объектов которые нужно держать в памяти и как-то обсчитывать, и это не сильно будет нагружать движок.
А когда игрок обнаружил звезду или планету, флагов становится всё меньше и потом, когда все знают об этом объекте (звезде), можно вообще убрать у неё соответствующее поле.

Но это если я правильно понял как всё работает. Всё-таки программист я ещё очень "не очень". :)


Васяка... переменная видим / не видим в любом случае должна быть... поэтому такой перестановкой ты ни чего не добьешься... ну только если сделать как массив переменной длины и удалять от туда данные... но я не уверен, что так будет лучше...
Изображение
AndreyKl
Сообщения: 51
Регистрация: 22 фев 2011

Хранение данных в игре

Сообщение AndreyKl »

В принципе это актуально, если работа через БД - тогда для хода игрока идет выборка только тех обьектов, которые по флагу игроку видны и не будет стоять проблема с багами типа - "отдал приказ прыгнуть в невидимую струну" или атаковать "невидимого врага". но тогда параметр видим/не видим - некая маска с числом бит равным числу игроков при перемножении | на бит игрока дающая значение отличное от 0.

Но вот поля добавлять/убирать не реально. Я не спорю - есть вариант var a=new {name="планета"}; и придумывай классы какие хочешь с произвольным числом полей и перекомпоновывай как хочешь, но производительность от неименованных классов...
Аватара пользователя
Vasaka
MOSC Team
Сообщения: 3195
Регистрация: 24 янв 2011

Хранение данных в игре

Сообщение Vasaka »

Тут я ничего не могу сказать. Не имею достаточной квалификации.

Для меня понятно лишь, что игра не должна думать по нескольку минут при передаче хода на больших картах на поздних стадиях игры, как это было в Цивилизации 4.

Как это лучше сделать, я не знаю. Денис (krupennikov), говорил что лучше делать с БД, но он сейчас занят и активного участия в проекте не принимает.

Очень не помешало бы, если бы тот кто знает как это всё организовать, поделился своими знаниями.
AndreyKl
Сообщения: 51
Регистрация: 22 фев 2011

Хранение данных в игре

Сообщение AndreyKl »

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

Если говорить про БД, то .NET имеет богатый инструментарий.
Собственно пакет для работы через запросы поддерживает практически любую БД, какая понравится. Но лично я рекомндую работать через ADO Entity.
Так как всеравно пишем с NET фреймворком, то в нем без дополнительных пакетов присутствует только две БД совместимые с Entity: MS SQL Server и SQL Compact (при желании можно доустановить несколько провайдеров).
первый нам врядли подойдет ибо платный и т п. второй - фактически файловая БД ранее использовалась только в карманных устройствах, но была модифицирована и устонавливается вместе с фреймворком (вроде минимальное требование - 3.5), ограничения размера для нас врядли существенные (что-то около 4гб, а то и вовсе убрали).

Через OleDB и подобные приходится вручную создавать всю БД, строки подключения и подобное:
классический пример работы

Код: Выделить всё



using (OleDbConnection dbcon = new OleDbConnection(connectionString))
{
OleDbCommand command = new OleDbCommand(queryString, dbcon);
dbcon.Open();
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader[0].ToString());
}
reader.Close();
}

В данном случае connectionString указывает к чему мы собственно подключаемся, а queryString - что-то вроде "Select Name from table1"

С адо ентиты, мы создаем БД через визуальный редактор, доступ через классы
аналог Entity

Код: Выделить всё



using (var dbcon = new databaseModel())
{
foreach (var elem in dbcon.table1)
{
Console.WriteLine(elem.Name);
}
}
Я сильно ужал код, чтобы было ясно на сколько он может быть компактен, правильный вариант предпологает сперва копирование всех элементов таблицы в ОЗУ а затем уже вывод (так намногоэффективней в плане уменьшения числа запросов, но в таком коротком коде компилятор сделает это сам), при желании можно указывать строку подключения (в ней будет меняться только адрес файла)

Вобщем первый вариант потребует знаний SQL языка запросов депарсинга записей таблицы, второй вариант при правильном использовании не менее эффективен, позволяет избавиться от ряда сложностей в частности знания БД как таковых, ручного создания БД (даже есть команда CreateDatabase и все последующее автоматически создающие нарисованную БД), проблем с непонятно откуда идущими ошибками. для работы с выборками рекомендую LINQ, всеравно без него далеко в C# 3.5+ не уйти.
Аватара пользователя
Vasaka
MOSC Team
Сообщения: 3195
Регистрация: 24 янв 2011

Хранение данных в игре

Сообщение Vasaka »

AndreyKl писал(а): 21 ноя 2011, 19:38тема конечно офтоп...

Тема очень даже не оффтоп.

Она проясняет самую тёмную область для меня на данный момент.
Я поговорю с Денисом, хочу узнать его мнение на этот счёт. Всё-таки тут уже есть конкретные предложения и он сможет дать конкретный ответ.
После этого я сразу отпишусь в этой теме.

P.S. А то раньше наше с Денисом общение на этот счёт, напоминало мне разговор слепого с глухим. :pardon:
Он мне что-то пытался объяснить, а я его абсолютно не понимал.


UPD: Ошибка в подсветке синтаксиса была в большой букве С. Оказывается надо писать маленькую.
AndreyKl
Сообщения: 51
Регистрация: 22 фев 2011

Хранение данных в игре

Сообщение AndreyKl »

Просто на заметку: описанный мной вариант в итоге потребует Net Framework 4.0, разработка скорее всего может вестись не только в Visual Studio, но я не уверен, чо такие вещи как Entity поддерживаются в mono. кроме того использование 4.0/3.5 фреймворков наверняка убьет крос плтформенность с линуксом если таковая планировалась. вобщем это тема для детального продумывания и обсуждения. В Mono скорее всего по умолчанию поддержка джавашных БД.
krupennikov
Сообщения: 53
Регистрация: 25 янв 2011

Хранение данных в игре

Сообщение krupennikov »

Привет всем! Начну по-порядку.

theCrow писал(а): 03 ноя 2011, 22:48Реализация инфраструктуры игровых объектов

Весь мир генерируется на основе массивов объектов с переменными, принадлежащим соответствующему классу.

Например, у нас есть класс StarshipClass, определяющий всё, что касается объектов кораблей. Также есть массив под названием StarshipObject, каждый элемент которого презентует отдельный корабль.

Класс StarshipClass определяется следующим образом:
StarshipClass.Player - владелец корабля.......


Не совсем понятно, для чего нужны массивы одновременно с БД в данном случае?
Постоянно массивы загружать и выгружать в/из БД. То есть получится, где то забыл добавить строку с обработкой нового элемента и ОППА - ошибка! Опять таки нагрузка на процессор и оперативную память. Все это не то, по крайней мере с моей колокольни.

Мое предложение следующее.
Использовать только по одному классу на каждую сущность (корабль, звезда, планета, ну и т.д.). А так же использовать виртуальную БД, очень мне понравившуюся в шарпе, - DataTable. Эта база данных находится в ОЗУ (памяти). В любой момент можно легко сохранить, загрузить, обратиться к любой ячейке данных.
Как все это будет происходить. Пишутся классы на каждую сущность со всеми полями и их типами. Каждый класс располагается отдельным файлом-библиотекой (*.dll). Все эти фалы в отдельной папке. Затем пишется код для работы с виртуальной БД следующим образом. Проверяется наличие сохраненных файлов БД-таблиц (DataTable). Название файлов будет соответствовать названию класса. Если какой-то БД-таблицы (далее БД) не существует, генерируется БД согласно недостающего класса сущности. То есть тупо будет проверяться количество файлов и поочередная их обработка. На каждую сущность своя БД. Название столбцов с точностью соответствует названию полей сущности (класса). Какими данными будет заполняться каждая таблица? За это будут отвечать файлы-библиотеки (*.dll) также расположенные в отдельной папке. То есть, расположение звезд, технические данные кораблей, расс и т.д., виды сущностей можно изменить простым изменением пути расположения всех папок, файлов (простыми словами МОД игры).
Если файл БД найден будет происходить проверка названия столбцов согласно класса.
Если файл БД найден, а класса такого нет, файл БД удаляется. Чтобы не было лишнего мусора в процессе написания игры.
Этим способом программист не тратит время на постоянное изменение, добавление, удаление нужных полей. Достаточно будет просто добавить/изменить поле в классе или создать новый класс, остальное все само изменится. Первый стобец (нулевой) будет содержать порядковый номер строки (в шарпе есть автонумерация), так сказать индивидуальный нумерованный адрес объекта сущности. Также игра будет легко поддаваться обновлению и исправлению багов, и даже в большинстве случаев, не требующая начинать всю игру заново, как это происходит в Civilization после каждого обновления (меня это очень напрягает).
Назовем все мною написанное примерно так: "Движок БД".
Т.к. все БД будут находиться в памяти, скорость обработки говорит сама за себя. Если кто то будет переживать за нехватку памяти из за БД, хотя я сомневаюсь в ее нехватке на такую игру, можно загружать и выгружать поочередно все БД. Но так как Виндовс при нехватке памяти, начинается использовать жесткий диск, опять таки не вижу в этом необходимости.
Все координаты, флаги и т.д. сущностей будут напрямую меняться в БД. И ни каких массивов! Так как БД в памяти это есть своего рода массив.
Если кого то беспокоит вопрос: "Как сделать автогенерацию всех БД и как это возможно?". То отвечу сразу: "Возможно! Я сделал, все работает, все супер!. Сделал это в своем проекте.".

Виртуальные БД можно сохранять двумя типами файлов:
1. Тип XML - (плюсы - возможность редактировать прямо в блокноте, минусы - большой размер файла, больше времени на загрузку в память и сохранение в файл)
2. Тип BIN(двоичный) - (плюсы - маленький размер файлов, мизерное время на загрузку в память и сохранение в файл; минусы - невозможность редактирования, требуется написание своего редактора для любителей создания МОДов)

Все БД для запуска игры предлагаю хранить в типе XML. Сохранку игры предлагаю хранить в типе BIN. Для этого просто необходимо добавить все таблицы в DataSet(набор таблиц) и сохранить одним файлом. Для сохранки, возможно, будут требоваться не все таблицы, поэтому можно будет добавлять только необходимые (например, зачем в сохранке таблица, с расположением файлов звуковых эффектов; прикрепленные к типу кораблей расположения путей к файлам графики и др.)...

Потратив время на создание движка БД, вы очень сильно сократите время на создание игры.

Добавлено через 2 минуты 18 секунд
Если у кого то есть переживание о кроссплатформенности игры, то лучше опираться на большинство. А 90% пользователей сидят на Винде

Добавлено через 2 минуты 49 секунд
Для работы с DataSet не требуется знание языка запросов типа SQL, Oracle и др. Также не требуется знание LINQ, но знание LINQ немного упрощает написание кода и сокращает количество строк.
AndreyKl
Сообщения: 51
Регистрация: 22 фев 2011

Хранение данных в игре

Сообщение AndreyKl »

krupennikov писал(а): 22 ноя 2011, 21:47Не совсем понятно, для чего нужны массивы одновременно с БД в данном случае?

Реальная БД позволит грузить в ОЗУ к примеру не всех игроков и дизайны сразу, а только тех, которые сейчас выполняют ход/используются. Так же это решает проблему с читами (влезанием в файл), так как все держат пароли, контрольные суммы и минимальное шифрование. Если все уже будет ОЗУ или абсолютно всё нужно в ОЗУ, то тогда БД в процессе игры преимуществ не имеет и только вредит.

krupennikov писал(а): 22 ноя 2011, 21:47Использовать только по одному классу на каждую сущность
Ну это вроде уже говорилось. Просто мое предложение: некий класс SpaceObject с HP, Mass и т п, от него наследуются класс MobileObject (классы наследники SpaceShip, Drones, Missiles, Vehicles, Comets), StationaryObject (системы, и туманности) и класс OrbitingObject (stars, planets, Colony, asteroid belts, satelites) TransferObjects (класс для всяких струн и им подобных). Класс Owner, от которого будут наследоваться все обьекты, кторые могут иметь владельца (Colony, SpaceShip...)

krupennikov писал(а): 22 ноя 2011, 21:47А так же использовать виртуальную БД, очень мне понравившуюся в шарпе, - DataTable. Эта база данных находится в ОЗУ (памяти). В любой момент можно легко сохранить, загрузить, обратиться к любой ячейке данных.

Извините, но в DataTable при использовании LinQ смысла совсем не вижу, этот класс тянется еще с первого фреймворка и потерял свои функции если отсуствует сервер (думаю и с сервером тоже). Можно пример, в чем вы видете его удобство?
На мой взгляд достаточно написать некий класс SpaceShip, сделать массив/список/словарь с использованием этого класса, далее любая выборка делается очень просто.

Код: Выделить всё

//обьявление, для примера

var _shiplist=new List<SpaceShip>();
//обьявление - вариант два, если захотим дополнительного быстродействия и возню с динамикой
var _shiplist=new SpaceShip[MaxShips];

//пропускаем некое заполнение

//собственно выборка, разумеется может находится даже в других классах получивших обьект тем или иным образом. Хотя лучше всего обьявить синглтон класс для работы с обьектами типа звез/кораблей...
var playerShips=from elm in _shiplist where elm.Player=PlayerId select elm;

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

Если говорить об элементах БД, то Linq помещает первую выборку в память и все последующие выборки по сделанной уже выполняются в ОЗУ без запроса к БД. Но параноики в случаях, когда нужно сохранить исключительно в ОЗУ делают что-то вроде

Код: Выделить всё

var dbcon=new GameSaveModel(); //название класса инициализации соединения к БД

var starships=(from elm in dbcon.StarShipsSet
where elm.InNormalSpace==true
select new Starship(){Name=elm.Name, Speed=elm.Speed /*и так далее*/}).ToList();
преобразование в ToList - так как потребуется редактирование, хотя можно сперва сделать выборку, а затем пробежаться по элементам foreach добавляя элемент в нужный массив/массивы.
Если не быть параноиком и просто использовать, то в БД программа полезет только когда будет выполнен dbcon.Refresh() (мог немного напутать с командой) или dbcon.SaveChanges().
krupennikov
Сообщения: 53
Регистрация: 25 янв 2011

Хранение данных в игре

Сообщение krupennikov »

AndreyKl писал(а): 23 ноя 2011, 19:55Реальная БД позволит грузить в ОЗУ к примеру не всех игроков и дизайны сразу, а только тех, которые сейчас выполняют ход/используются.

Еще раз повторюсь DataTable будет работать быстрее из за редкого обращения к жесткому диску. Также возможно грузить/выгружать в/из ОЗУ отдельные DataTable.

AndreyKl писал(а): 23 ноя 2011, 19:55Так же это решает проблему с читами (влезанием в файл), так как все держат пароли, контрольные суммы и минимальное шифрование.

Если речь идет об однопользовательской игре, то вопрос такой: где легче изменить данные - в ОЗУ или на жестком диске? В любом случае, при большом желании можно сделать и то и другое, но не вижу смысла для игрока, для него самого пропадет интерес к игре. Если речь идет о многопользовательской игре, то для этого в любом случае будет писаться отдельный код и использоваться реальная БД с сервера. Как по мне, так удобнее использовать вместо List именно DataTable, и делать выборку данных с реальной БД также удобнее с помощью DataTable (DataSet).

Добавлено через 8 минут 6 секунд
AndreyKl писал(а): 23 ноя 2011, 19:55Извините, но в DataTable при использовании LinQ смысла совсем не вижу, этот класс тянется еще с первого фреймворка и потерял свои функции если отсуствует сервер (думаю и с сервером тоже).

Опять таки повторюсь, все что можно сделать с использованием LINQ, можно сделать и без LINQ. LINQ - это только удобство для программиста, более короткий код. Не все отлично работают с LINQ. Я пробовал с ним работать, мне понравилось. Имхо, Но не думаю что ты один писать весь код будешь, ребята не то что LINQ, а более простые вещи пока написать не могут. Если кому удобно, то и с DataTable (DataSet) LINQ прекрасно работает, и запросы используются точно такие же как и с реальной БД. И не совсем понятно, какие функции DataTable потерял, на сколько мне известно он только и делает что расширяет свой функционал

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

Добавлено через 34 секунды
А решать уже вам, так именно вам писать игру

Добавлено через 7 минут 42 секунды
Не спорю, что использовать массивы типа List тоже удобно. Что касается примера использования с DataTable, то код я выложу позже, при первой возможности свободного времени
AndreyKl
Сообщения: 51
Регистрация: 22 фев 2011

Хранение данных в игре

Сообщение AndreyKl »

krupennikov писал(а): 23 ноя 2011, 20:51Еще раз повторюсь DataTable будет работать быстрее из за редкого обращения к жесткому диску. Также возможно грузить/выгружать в/из ОЗУ отдельные DataTable.
Просто для справки:
1. сериализовать в XML можно практически любой другой класс,
2. Для работы с данными майкрософт заменила DataTable на ObjectSet(наследник Data.Objects.ObjectQuery, специально оптимизированный под запросы Linq to SQL), первый остается ради некоторых элементов интерфейса используемых в WinForms.
для уведомления окон об изменениях с версии 3.0 применяются только INotifyCollectionChanged, INotifyPropertyChanged, так что с биндингом дататейбл на контролы из 3.0+ могут быть проблемы.
С другой стороны DataTable остается единственным классом работающим с базами данных не через ADO.NET Entity (4.0), и реализован в Mono на линуксе.
3. обращений к диску будет столько же, сколько и с любой БД/массивом - считал один раз и работай. каждая запись читается всего один раз при открытии если не удалять лишний раз значения, не важно XML или DB, все остальное время работа всеравно с ОЗУ. Я часто перебрасываю ObjectSet в List и закрываю подключение до необходимости сохранить всю работу.
4. В MSDN не нашел никаких изменений этого компонента с версии 1.1 помимо LinQ, но им обрабатывают вообще всё.


krupennikov писал(а): 23 ноя 2011, 20:51Если речь идет об однопользовательской игре, то вопрос такой: где легче изменить данные - в ОЗУ или на жестком диске?
врядли ктолибо будет реализовывать запись в БД в процессе игры. Просто пример:
делаем выборку из домашней системы и кидаем в неё игрока, а подгрузку всех остальных систем ставим отделным потоком по одной (приоритет с тех, где колоний игрока больше), если игрок попытается перейти в систему, которая еще не подгрузилась - загружаем её с приоритетом. когда всё загрузится - закрываем подключение к БД. Открываем в будующем только для глобальной записи всего сейва.
Разумеется подобное можно реализовать при помощи Xml и Xpath, но в таком случае опять удобнее обычный List, так как мы сами будем управлять сериализацией класса и соответственно знать, где и что искать (было бы не плохо сравнить быстродействие таких вариантов - пока не приходило в голову сравнивать скорость выборок из БД и XML при тысячах записей).

krupennikov писал(а): 23 ноя 2011, 20:51Если речь идет о многопользовательской игре, то для этого в любом случае будет писаться отдельный код и использоваться реальная БД с сервера.
Справедливо только для риалтайм игр, а там БД по быстродействию не всегда применима, хотя если смотреть на тот же lineage/Wow...
krupennikov писал(а): 23 ноя 2011, 20:51и делать выборку данных с реальной БД также удобнее с помощью DataTable (DataSet).
вот тут уж поверьте наслово, дататейбл по сравнению с ентити нервно курит в сторонке - через ентити по всем элементам базы можно пробегаться не прибегая к помощи linq, даже выборки делать, визуально никакого отличия от List нет помимо немного переименованных Add, наличия команд save/refresh и ивент обновления(кстати про рефрешь узнал только когда обнаружил, что программа оперирует с элементами удаленными из БД 2-3 часа назад).

krupennikov писал(а): 23 ноя 2011, 20:51Добавлено через 7 минут 42 секундыНе спорю, что использовать массивы типа List тоже удобно. Что касается примера использования с DataTable, то код я выложу позже, при первой возможности свободного времени

в уроках наверное темку по БД надо, это всеравно их часть (ADO), а я там же для сравнению выложу аналог на Entity по твоему примеру. На счет незнания Linq - я его выучил минут за 30, если забыть про всякие Join и некотрые регулярные выражения, не думаю, что у кого либо возникнет с ним проблема, хотя мне уже был известен SQL.
Закрыто