Столкнулся с тем, что встроенная в RPG Maker система погоды - полный отстой. Опишу то, как мне пришлось допиливать её до приличного состояния.
Я постарался исправить все эти недостатки. Результат - куда более зимнее, уютное настроение.
Более того - по производительности такой объем снежинок не сильно уступает тому, что было раньше. И раз уж речь зашла о производительности, то теперь пришла пора поговорить о коде.
Рассказ о том, как мне довелось приводить всё в порядок...
Я изначально для себя решил, что не буду создавать свои собственные эффекты, а лишь модифицирую имеющиеся, поэтому первым делом полез разбираться в устройстве класса 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 нуждается в допиливании. К счастью, для этого есть все необходимые инструменты и в реализации задумок ограничивает лишь собственная фантазия.