Добавление скриптинга и динамических аддонов в Unity (часть 1)
Запись от Storm23 размещена 30.05.2019 в 13:55
Показов 5696
Комментарии 0
|
Постановка задачи Требуется разделить игру на движок и игровой контент (сюжет, геймплей, диалоги, задания для игрока, и так далее). Контент будет содержать сложную логику, поэтому контент нужно представить не просто в виде текстовых файлов или БД. Для описания контента нужно использовать скриптовый язык программирования. При этом нужно разделить разработку движка и сюжета на несколько независимых проектов. А еще лучше сделать систему аддонов и плагинов, с помощью которой дополнительные сюжетные линии могут разрабатывать сторонние разработчики и подгружаться в игру прямо в рантайме. Опишу здесь как написать такую систему для Unity. В результате будет система скриптинга и аддонов, с возможностью отдельной компиляции скриптов и динамической подгрузкой их в игру, в рантайме. Сразу определимся с терминологией: здесь и далее под скриптами я буду подразумевать не те скрипты, которые MonoBehaviour из Unity, а именно скриптинг - то есть скрипты которые описывают контент игры. Скриптинг на C# В целом скриптинг работает так. В основном движке разрабатывается абстрактный класс (назовем его ScriptingBase) который содержит набор методов для взаимодействия скриптов с основным движком игры. ScriptingBase вынесем в отдельный namespace. Например это может выглядеть так:
Далее в движке пишем простой контроллер, который может показывать текст игроку. И еще пишем MonoBehaviour класс PluginsController который будет заниматься загрузкой и выполнением скриптов. Полный код на этом этапе: ScriptingBase
MyStory
MessageController
PluginsController
PluginsController создает объект MyStory и периодически вызывает его метод Update. Сейчас этот класс создает объект MyStory непосредственно, но мы потом изменим это поведение, потому что он не должен заранее знать о скриптах, которые он будет вызывать. Компилируем проект, проверяем работоспособность: Проект целиком на данном этапе: ScriptingExample 1.zip Работает! Разделение на проекты, выделение API Если посмотреть на код из предыдущей части, то видно, что классы из неймспейса Scrpits используют только System и ничего не знают ни о контролерах движка ни о UnityEngine. Это значит, что они независимы от движка игры. Это хорошо, потому что это позволит вынести скрипт MyStory в отдельную dll, которая будет независима от Unity и от игрового движка. С другой стороны, контроллеры знают о неймспейсе Scrpits и даже о классе скрипта MyStory. Это плохо, эту зависимость нужно разорвать. Для этого, изменим класс PluginsController так, что бы он не создавал MyStory непосредственно, а искал классы унаследованные от ScriptingBase, и автоматически создавал их: PluginsController
Запускаем, работает. Отлично! Теперь у нас движок знает только о классе ScriptingBase, но ничего не знает о скрипте MyStory. Таким образом, связь между скриптом и движком разорвана и они не знают друг о друге. Но проблема все же остается. Дело в том, что и движок и скрипт знают о классе ScriptingBase. Это неизбежно, потому что этот класс является связующим звеном между скриптом и остальной игрой. Если бы мы разрабатывали отдельное приложение, то можно было бы просто вынести класс ScriptingBase в отдельный проект, и тогда его могли бы использовать в виде dll и движок и скрипт. Но проблема в том, что Unity не позволяет делать отдельные проекты в виде dll. В Unity все пользовательские классы компилируется в единое целое, и выделить часть в отдельный проект - нельзя. Будем решать эту проблему так: создадим отдельный проект типа Class Library в VisualStudio. И назовем его API. Его папку расположим где-то рядом с папкой Assets. В этот проект добавим класс ScriptingBase как ссылку (As Link) и скомпилируем проект. Проект успешно компилируется (потому что ScriptingBase независим от Unity и движка, поэтому dll-ки Unity не нужны). В результате, мы получаем API.dll в которой находится откомпилированный класс ScriptingBase. Библиотека API.dll содержит базовый класс для скриптов, но не содержит классы самого движка. Это дает возможность свободно распространять эту dll. И она может быть использована сторонними разработчиками для разработки плагинов к игре, не имея самого исходного кода игры. Создание аддона Для аддона создадим еще один проект типа Class Library в VisualStudio, тоже рядом с папкой Assets. Назовем проект MyAddon. Перенесем класс скрипта MyStory из юнитовского проекта в новый проект. Файл MyStory.cs удалим из папки Asstes/Scripts/Scripting. К созданному проекту присоединим библиотеку API.dll через References. В свойствах reference для API.dll выставьте Copy Local в false. Это важно. Получим следующий проект: Поскольку класс MyStory зависит только от ScriptingBase, то ему не нужны никакие другие библиотеки, кроме API.dll. Компилируем проект. Он успешно компилируется. На выходе получаем файл MyAddon.dll. Убедитесь, что рядом с этой длл нет файла API.dll. Загрузка плагина Теперь сделаем так, что бы PluginsController мог найти наш аддон и загрузить его. Для этого будем искать файлы *Addon.dll вокруг себя. И пытаться загружать плагин: PluginsController
Если мы в данный момент запустим проект Unity, то нас ждет облом. Выпадает исключение: ReflectionTypeLoadException: Exception of type 'System.Reflection.ReflectionTypeLoadExc eption' was thrown. Отладка показывает, что файл аддона находится, но в момент загрузки ассембли - выпадает ошибка. Причина этого в том, что для работы аддона нужен файл API.dll, но его нет рядом с плагином, поэтому наш аддон не может загрузится. Для того, что бы это обойти, используем событие AppDomain.AssemblyResolve:
Запускаем проект на выполнение - он запускается без ошибок, и на экране выскакивает надпись из аддона, Ура! Плагин успешно загрузился. Полный код проекта на этом этапе: | |||||||||||||||||||||||||||||||||||||||||||||
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии


