Gamemaker Studio 2. Система для создания простых модификаций
Привет!
Сегодня покажу и расскажу, как можно сделать простую систему моддинга для вашей игры. Она не позволит добавлять новые механики, но позволит добавлять извне любые объекты, будь то оружие, здания, новые расы и так далее - зависит от вашей фантазии.
Система крайне простая и реализуется за пять минут, но имеет подводные камни, о которых мы поговорим в конце.
Описание системы
Устройство у неё очень простое. Мы создаём текстовый файл, в котором будет храниться информация об объектах, которые нам нужны. Соответственно, при первом запуске, когда файла этого ещё нет - мы его создаём и заполняем, а все остальные запуски - только считываем.
В этот файл можно будет спокойно вписать новые объекты, закинув в эту же папку нужные нам спрайты.
В идеале - добавить некоторую защиту, чтобы нельзя было изменять уже существующие объекты, но здесь всё ограничивается вашей фантазией.
Реализация
Для удобства я сделал два объекта:
oWeaponsManager (со свойством persistent) и oWeapon.
Первый - управляющий. Он создаётся вместе с другими управляющими объектами при запуске и здесь происходит основная логика, о которой мы с вами будем сегодня говорить.
Второй объект - это объект-оружие, которому мы в последствии будем передавать всю нужную информацию, спрайты и прочее-прочее.
Ссылка на код:
https://pastebin.com/T6NaBMsb
Дубль с пастебина:
Функция-конструктор для объектов:
function scrWeaponsAdd(_sprite_index, _images, _name, _distance, _penetration, _damage) constructor{oWeaponsManager:
sprite_index = _sprite_index;
images = _images;
name = _name;
distance = _distance;
penetration = _penetration;
damage = _damage;
}
// Если файл уже существует - заполняем по новой.
if file_exists("weapons.txt") {
global.weapons = [];
var _buffer = buffer_load("weapons.txt");
var _string = buffer_read(_buffer, buffer_string);
buffer_delete(_buffer);
var _loadData = json_parse(_string);
// Загружаем оружие.
for (var i = 0; i < array_length(_loadData); ++i) {
// Если информация является строкой или такого спрайта не существует - создаём его и сохраняем
if is_string(_loadData[i].sprite_index) or !sprite_exists(_loadData[i].sprite_index) {
var name = _loadData[i].sprite_index;
if file_exists(name) {
var sprite = sprite_add(name, _loadData[i].images, false, false, 0, 0);
// Устанавливаем точку отсчёта в центре спрайта
sprite_set_offset(sprite, sprite_get_width(sprite) / 2, sprite_get_height(sprite) / 2);
_loadData[i].sprite_index = sprite;
}
}
array_push(global.weapons, _loadData[i]);
}
}
// Создаём с нуля, если не существует
else {
global.weapons = [
new scrWeaponsAdd(sPistol, 1, "Пистолет", 10, 1, 10),
]
var _string = json_stringify(global.weapons);
var _buffer = buffer_create(string_byte_length(_string) + 1, buffer_fixed, 1);
buffer_write(_buffer, buffer_string, _string);
buffer_save(_buffer, "weapons.txt");
buffer_delete(_buffer);
}
#endregion
#region Удаляем те объекты, что не смогли загрузиться
var i = 0;
while i != array_length(global.weapons) {
if is_string(global.weapons[i].sprite_index) {
array_delete(global.weapons, i, 1);
i -= 1;
}
i += 1;
}
#endregion
#region Создаём прошедшие проверку объекты. Дебаг.
for (var i = 0; i < array_length(global.weapons); ++i) {
var inst = instance_create_layer(32 + i * 32, 32, "Instances", oWeapon);
inst.sprite_index = global.weapons[i].sprite_index;
}
#endregion
Спрайты можно загружать как анимированные, так и одиночные. Всё зависит от параметра images, который должен быть равен количеству кадров. Принимает на вход: .png, .jpeg/jgp, .json, .gif.
Теперь немного интересностей. Как видно из кода, я добавляю только одно оружие, которое у меня в проекте уже имеет свой спрайт. Как загрузить второе? Всё просто.
Сочетанием клавиш "Windows + R" (или просто открыв окно "выполнить", либо прокликав пусть самостоятельно), вписывем туда: %localappdata%
Откроется папка Диск:\Пользователи\Пользователь\AppData\Local
Здесь ищем папку, название которой совпадает с названием нашего проекта. У меня это Pikabu.
Именно в эту папку у нас сохраняются файлы, с которыми мы работали. То есть, и сохранения, и файл weapons.txt, у нас находится здесь.
Чтобы добавить новое оружие - скинем его картинку в эту папку, затем немного подредактируем файл weapons.txt
Скопируем первую часть строки, что в фигурных скобках. Добавим запятую после закрывающей фигурной и вставим скопированный текст.
Что нам нужно заменить:
images - с 1 меняем на нужное число кадров. Как видно по скрину выше, у меня это число будет равняться трём, так как я хочу импортировать "анимированную" картинку. Важно знать, что на это число будет разделена картинка по ширине. Для анимации картинка должна быть выполнена в виде линии, где меняется только ширина. Хотите спрайт 32х32 с тремя кадрами анимации - делайте картинку 96х32 и размещайте в каждой новой "клетке" то, что нужно отрисовать.
sprite_index - пишем в кавычках полное название нашего файла. Полное название - это с учётом его формата. В моём случае - "sPistol3.png"
name, distance, penetration, damage - меняем по своему усмотрению, это уже чисто игровые параметры.
Сохраняем файл и заходим в игру, чтобы посмотреть результат.
Эпилептикам советую быть осторожнее.
Вот таким нехитрым способом можно реализовать добавление вообще любого объекта в игру.
Как по мне, это достаточно гибко и просто для рядового пользователя.
Теперь поговорим о минусах.
Первый из них - нагрузка. Для хранения каждого спрайта, загруженного "извне", движок создаёт свою текстурную карту. От этого больше нагрузка на память. Со спрайтами, которые импортированы в движок, нагрузка меньше.
Замечу, что нагрузка хоть и выше, но не существенно и эта проблема, если начнёт мешать, может быть решена как удалением ненужных спрайтов (ведь их можно спокойно подгружать и выгружать, они же хранятся у нас в папке проекта), так и иными методами.
Второй - определённые требования к структуре проекта. Как вы могли заметить, под оружие у меня всего один объект и в нём в дальнейшем будет расписана логика работы для всех доступных видов вооружения. Не всем это может быть удобно, но таков путь.
Если что-то пошло не по плану, посмотреть исходный код можно здесь:
https://disk.yandex.ru/d/bOSzQ5bj8IdN9w
P.S.
Комната была уменьшена в размерах с 1920х1080 до 128х128, чтобы показать оба объекта.
P.P.S.
Если не получается найти папку, то можете в коде oWeaponsManager, после создания файла weapons.txt, прописать следующий код:
show_debug_message(filename_path("weapons.txt"))
И запустить игру. В выводе выскочит сообщение с путём прямиком до нужной папки.
Если что-то не получилось - пишите, постараюсь помочь.

Лига Разработчиков Видеоигр
9.1K постов23.2K подписчиков
Правила сообщества
ОБЩИЕ ПРАВИЛА:
- Уважайте чужой труд и используйте конструктивную критику
- Не занимайтесь саморекламой, пишите качественные и интересные посты
- Никакой политики
СТОИТ ПУБЛИКОВАТЬ:
- Посты о Вашей игре с историей её разработки и описанием полученного опыта
- Обучающие материалы, туториалы
- Интервью с опытными разработчиками
- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе
НЕ СТОИТ ПУБЛИКОВАТЬ:
- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры
- Посты, единственная цель которых - набор команды для разработки игры
- Посты, не относящиеся к тематике сообщества
Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.
ЗАПРЕЩЕНО:
- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции
- Выдавать чужой труд за свой
Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.
О РАЗМЕЩЕНИИ ССЫЛОК:
Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:
- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества
- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз
- Cсылка размещается в формате: "Страница игры в Steam: URL"