Часть первая ‐ Начало

13 Марта, 2020 г.

RPG Maker. Сколько шедевров подарило миру это чудовище. И как фанат всего этого добра, я не мог не купить себе этот конструктор и не попробовать разобраться в его устройстве.

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

Кто знает, может быть из этого что-то и выйдет...
Хотя конечно же нет. Когда я доводил свои проекты до конца?


Часть вторая ‐ Работа со слоями

2 Апреля, 2020 г.

Постепенно осваиваюсь в редакторе и... Аргх. Как же странно в RPG Maker MV устроены слои.

Казалось бы, что может быть проще слоёв? Подобно тем, что можно встретить в любом нормальном редакторе? ЗАЧЕМ ВСЁ ТАК УСЛОЖНЯТЬ?!

Вкратце - в этом движке нет слоёв как таковых, однако тайлы здесь разделены на группы "А", "B", "C", "D", "R" (кто объяснит мне, почему "R"?!)

Группа тайлов "A", в свою очередь, делится на подгруппы "Animation", "Ground", "Buildings", "Walls" и "Normal".

Каждая из таких групп отличается своей уникальной компоновкой и порядком отрисовки.

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

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

Пытался использовать Tiled и плагин для импорта карт из него (https://forums.rpgmakerweb.com/threads/50752/), но надо признать, что это ещё более неудобная система мапмейкинга.

Видимо всё же придётся мириться с неуклюжестью нативного редактора и учиться работать с ним.

В целом трудновато нащупать отправную точку и сделать первый шаг. Боюсь, сначала мне нужно сочинить рассказ, дабы использовать его в качестве референса к сюжету. Может быть заодно удастся развить в себе писательский навык для дальнейшей проработки диалогов.


Часть третья ‐ Кошки

8 Апреля, 2020 г.

Кошки?.. Кошки! РПГ ПРО КОШОК!!!

early prototype screenshot

Часть четвёртая ‐ Плагины

12 Апреля, 2020 г.

Сделал для себя неожиданное открытие - RPG Maker MV работает на NW.js.
То есть игры на нём буквально запускаются и работают в браузре. Кхм.
Удивительно, что они при этом умудрились сохранить присущую им эстетику.

Что ж, зато писать собственные плагины оказалось довольно просто, хоть и смущает отсутствие вменяемой документации. Но это даже в какой-то мере круто - да здравствует творческий хаос, полный отчаяния и изощрённых костылей!

UPD:
Всё же нашёл неофициальные доки. С ними стало чуточку легче.


Кошкокошка

24 Мая, 2020 г.

koshkokoshka
...

Жила-была кошечка.

И было у неё много хороших и добрых друзей.

Но потом её друзья выросли и стали грустными.

Кошечка пыталась как-то повлиять на них и сделать их вновь весёлыми.

Но все над ней смеялись, потому что она не разделяла их грусть.

Но между тем ей было очень больно переживать то, что с ними стало.

И тогда она затаила обиду.

Ей больше было не с кем делиться радостью и переживаниями.

Она чувствовала себя очень виноватой за то, что произошло.

Но в сердце своём она надеется, что они сохранят о ней хорошие воспоминания и будут по-настоящему рады, что она была частью их жизни.


Часть пятая - Погода

26 Мая, 2020 г.

Столкнулся с тем, что встроенная в RPG Maker система погоды - полный отстой. Опишу то, как мне пришлось допиливать её до приличного состояния.

Оригинальный эффект снегопада.
Даже в первом сайленте снег был менее картонный.

Во-первых - почему снежинки такие огромные? Это не снежинки, а снежки. Они могли бы убить, свалившись на голову, если б их не было так мало.

Во-вторых - ПОЧЕМУ ИХ ТАК МАЛО?! Нет, серьёзно, неужели 90 снежинок на экране (при максимальном значении параметра "power") - это предел? Это всего лишь чёртовы спрайты. RPG Maker MV, в отличии от предшественников, работает на GPU - с учётом батчинга ими можно в три слоя весь экран засыпать.

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

Я постарался исправить все эти недостатки. Результат - куда более зимнее, уютное настроение.

Ссылка на скачивание плагина: MEW_BetterSnow.js

Более того - по производительности такой объем снежинок не сильно уступает тому, что было раньше. И раз уж речь зашла о производительности, то теперь пришла пора поговорить о коде.

Рассказ о том, как мне довелось приводить всё в порядок...

Я изначально для себя решил, что не буду создавать свои собственные эффекты, а лишь модифицирую имеющиеся, поэтому первым делом полез разбираться в устройстве класса Weather

Как оказалось, код в нём содержит некоторые не очень оптимальные решения.

Вот листинг оригинального кода:

Weather.prototype.update = function() {
    this._updateDimmer();
    this._updateAllSprites(); // Всё начинается отсюда
};

// 1.
Weather.prototype._updateAllSprites = function() {
    // Сначала высчитывается количество спрайтов на экране
    var maxSprites = Math.floor(this.power * 10);
    while (this._sprites.length < maxSprites) {
        this._addSprite();
    }
    while (this._sprites.length > maxSprites) {
        this._removeSprite();
    }

    // Затем для каждого из них вызывается метод _updateSprite
    this._sprites.forEach(function(sprite) {
        this._updateSprite(sprite);
        sprite.x = sprite.ax - this.origin.x;
        sprite.y = sprite.ay - this.origin.y;
    }, this);
};

// 2.
Weather.prototype._updateSprite = function(sprite) {
    // Здесь, в зависимости от типа погоды, вызывается
    // соответствующий метод обновления спрайта
    switch (this.type) {
        case 'rain':
            this._updateRainSprite(sprite);
            break;
        case 'snow':
            this._updateSnowSprite(sprite);
            break;
    }
    if (sprite.opacity < 40) {
        this._rebornSprite(sprite);
    }
};

// 3. Интересующий меня метод - _updateSnowSprite
Weather.prototype._updateSnowSprite = function(sprite) {
    sprite.bitmap = this._snowBitmap;
    sprite.rotation = Math.PI / 16;
    sprite.ax -= 3 * Math.sin(sprite.rotation);
    sprite.ay += 3 * Math.cos(sprite.rotation);
    sprite.opacity -= 3;
};

Сперва мне захотелось изменить паттерн падения снежинок и новая функция _updateSnowSprite обрела следующий вид:

Weather.prototype._updateSnowSprite = function(sprite) {
    sprite.bitmap = this._snowBitmap;
    sprite.ax -= Math.sin(performance.now() / 1000);
    sprite.ay += 1;
    sprite.opacity -= 3;
};

Если присмотреться к оригинальному коду, то выяснится, что параметр спрайта .rotation на самом деле используется лишь для того, чтобы хранить угол падения снежинок. Сами же снежинки круглые, поэтому при рендеринге не имеет смысл их поворачивать (скорее всего такое странное поведение у них осталось от копипасты кода дождя, где капли имеют угол наклона). Если убрать их поворот, то это позволит избежать лишних расчётов и слегка облегчить работу видеокарте.

Однако тут же я добавил вызов метода performance.now() - это было необходимо для плавного колебания снежинок из стороны в сторону, и теперь вызов high-resolution таймера во время обновления каждого спрайта сильно отразился на быстродействии. Тогда я перенёс его в родительский метод _update:

let now = 0;

Weather.prototype.update = function() {
    this.constructor.prototype.update.call(this, arguments);
    now = performance.now();
};

Weather.prototype._updateSnowSprite = function(sprite) {
    // ...
    sprite.x -= Math.sin(now / 1000);
};

И... Снежинки начали падать с одинаковой траекторией.

Но я тут же нашёл этому простое решение - просто прибавить номер спрайта к значению времени:

sprite.x -= Math.sin((now / 1000) + sprite.spriteId);

И теперь это смотрелось просто чудестно... Но ещё не совсем. Размер снежинок я уменьшил банальной перерисовкой текстуры:

Weather.prototype._createBitmaps = function() {
    _createBitmaps.call(this, arguments);
    this._snowBitmap = new Bitmap(4, 4);
    this._snowBitmap.drawCircle(2, 2, 2, '#ffffff');
};

А так же десятикратно увеличил их количество простым твиком, добавив один нолик к числу, на которое умножалось .power

Weather.prototype._updateAllSprites = function() {
    var maxSprites = this.power * 100; // Раньше тут было 10

    // ...
}

Подводя итог: RPG Maker MV нуждается в допиливании. К счастью, для этого есть все необходимые инструменты и в реализации задумок ограничивает лишь собственная фантазия.