MZでバトルシステム構築01
- のじ かのえ
- 3月26日
- 読了時間: 5分
更新日:5月6日
いよいよ本格的にMZに移行していきます。
地道に…………。
なにせVXAceで10年以上燻ぶっていたので、
Ace時代に自分でゴリラして構築したシステムの中身が、今のわたしに解読できないという怪奇現象が生じておりまして。
また、スクリプト言語もRGSS3(Ruby)からJavascriptに変わり、そもそも従来使用していたスクリプトとプラグインの仕様が全く持って違うため、もうほぼ初心者です。そして当然ながら、これまで作ったシステムと同じ挙動を再現するための代替プラグインがそろっているとも限らない。
そもそもMV→MZじゃなくてAce→MZですし……
1世代すっ飛ばしているので、はっきり言って完全移行は不可能です。
可能な限りの再現と、MZに移行したからこそやれるであろう新しい表現や、Aceで限界を感じて諦めた要素にも挑戦したいと思います。
というわけでスーパー初歩的な部分から、
バトル時の弱点開示システムを作ってみました。
Ace版

MZ版

ファイアⅢ(物理)になってますが。
オクトラやリベサガで採用されているあの弱点システムです。
Ace版はかなり無茶苦茶なことをしていて、複数スクリプトの機能を掛け合わせ、イベント変数と条件分岐でごり押ししてアイコン表示している(ので、時々処理が遅れる)ものだったのですが、MZ版はイベントスクリプトを用いてバトルイベントで構築しました。
中身的にはこんな処理です。(※挙動保証できないので絶対に使用しないでください)
(() => {
// エネミーの弱点情報を記録するオブジェクトをゲーム全体で保持
Game_System.prototype.initialize = function() {
this._discoveredWeaknesses = this._discoveredWeaknesses || {};
};
Game_System.prototype.getDiscoveredWeaknesses = function(enemyId) {
if (!this._discoveredWeaknesses) {
this._discoveredWeaknesses = {};
}
if (!this._discoveredWeaknesses[enemyId]) {
this._discoveredWeaknesses[enemyId] = {};
}
return this._discoveredWeaknesses[enemyId];
};
Game_System.prototype.setDiscoveredWeakness = function(enemyId, elementId) {
if (!this._discoveredWeaknesses[enemyId]) {
this._discoveredWeaknesses[enemyId] = {};
}
this._discoveredWeaknesses[enemyId][elementId] = true;
};
const _Sprite_Enemy_initMembers = Sprite_Enemy.prototype.initMembers;
Sprite_Enemy.prototype.initMembers = function() {
_Sprite_Enemy_initMembers.call(this);
this._weaknessIcons = [];
this._weaknessIconsCreated = false;
};
const _Sprite_Enemy_update = Sprite_Enemy.prototype.update;
Sprite_Enemy.prototype.update = function() {
_Sprite_Enemy_update.call(this);
this.updateWeaknessIcons();
};
Sprite_Enemy.prototype.updateWeaknessIcons = function() {
if (!this._enemy || this._weaknessIconsCreated) {
return; // すでにアイコン作成済みなら処理をスキップ
}
console.log("エネミー設定OK, アイコン作成開始", this._enemy.enemyId());
this.createWeaknessIcons();
this._weaknessIconsCreated = true;
};
Sprite_Enemy.prototype.createWeaknessIcons = function() {
if (!this._enemy || !this._enemy.enemy()) {
console.error("エネミーが正しく設定されていません", this._enemy);
return;
}
const iconSize = ImageManager.iconWidth;
let xOffset = -(iconSize * 1.5);
this._weaknessIcons = [];
const discoveredWeaknesses = $gameSystem.getDiscoveredWeaknesses(this._enemy.enemyId());
for (let i = 1; i < $dataSystem.elements.length; i++) {
const elementRate = this._enemy.elementRate(i);
if (elementRate >= 1.5) {
const iconIndex = discoveredWeaknesses[i] ? this.getWeaknessIconIndex(i) : 16;
const sprite = new Sprite(ImageManager.loadSystem("IconSet"));
const pw = ImageManager.iconWidth;
const ph = ImageManager.iconHeight;
const sx = (iconIndex % 16) * pw;
const sy = Math.floor(iconIndex / 16) * ph;
sprite.setFrame(sx, sy, pw, ph);
sprite.y = -30;
this._weaknessIcons.push({ elementId: i, sprite: sprite });
this.addChild(sprite);
}
}
// ここでアイコンの配置を更新
this.updateIconPositions();
};
const _Game_Action_apply = Game_Action.prototype.apply;
Game_Action.prototype.apply = function(target) {
_Game_Action_apply.call(this, target);
if (!target || !target.isEnemy()) return;
let elementId = this.item().damage.elementId || this.subject().attackElementId();
console.log("攻撃属性ID: ", elementId);
if (elementId > 0) {
const elementRate = target.elementRate(elementId);
console.log(`elementRate(${elementId}) = ${elementRate}`);
if (elementRate >= 1.5) {
$gameSystem.setDiscoveredWeakness(target.enemyId(), elementId);
const enemySprites = SceneManager._scene._spriteset._enemySprites;
for (const sprite of enemySprites) {
if (sprite._enemy && sprite._enemy.enemyId() === target.enemyId()) {
sprite.updateWeaknessIcon(elementId);
}
}
}
}
};
Sprite_Enemy.prototype.updateWeaknessIcon = function(elementId) {
const iconIndex = this.getWeaknessIconIndex(elementId);
let updated = false;
for (let i = 0; i < this._weaknessIcons.length; i++) {
if (this._weaknessIcons[i].elementId === elementId) {
const sprite = this._weaknessIcons[i].sprite;
const pw = ImageManager.iconWidth;
const ph = ImageManager.iconHeight;
const sx = (iconIndex % 16) * pw;
const sy = Math.floor(iconIndex / 16) * ph;
sprite.setFrame(sx, sy, pw, ph);
updated = true;
break;
}
}
if (!updated) {
console.warn(`対応するアイコンが見つかりませんでした: elementId=${elementId}`);
}
};
Sprite_Enemy.prototype.getWeaknessIconIndex = function(elementId) {
const elementToIconMap = {
1: 76, 2: 77, 3: 78, 4: 67,
5: 80, 6: 64, 7: 65, 8: 66,
9: 159, 10: 160,
};
return elementToIconMap[elementId] || 16;
};
Sprite_Enemy.prototype.updateIconPositions = function() {
for (let i = 0; i < this._weaknessIcons.length; i++) {
const icon = this._weaknessIcons[i].sprite;
icon.x = -(ImageManager.iconWidth * 1.5) - 30 + (i * (ImageManager.iconWidth + 2));
}
};
})();
この弱点開示システム、某プラグインが有名ですが、コアプラグインで不要なところまでいじられるのがネックで、だったら自分でやった方が早いだろうと。
ちなみに右側のエネミーのアイコン色がおかしいのは、おそらくエネミーのスプライトにかかっている色調補正がそのままアイコンにも反映されてしまっているからだと思われます……まあおそらくデフォルトの色調補正を使う予定はないので問題ないでしょう。
とりあえず現時点で
エネミーデータベースに登録した「属性有効度が150%以上に設定されている属性」を自動的に弱点として取り扱う
アイコンの表示順はデータベース順。つまり弱点開示状況によっては前後のアイコンから弱点予想ができる(オクトラ、リベサガ式)
エネミーIDごとに弱点開示状況などは共有、保存される(次回戦闘にも持ち越される)
までは形になりました。
2.に関しては、ネタ元のプレイヤーの中での暗黙の了解というか、「こうなってたらこうだよね」みたいなものが、半分ネタ的になっているようなところもあるので、ランダム配置でもいいのかな~とも考えたのですが、「弱点を開けるゲーム性」の中に「前後のアイコンから未開示の属性を予測するプレイ」も含まれるのかな~と考えてそのままに。
あと複雑怪奇なことするとそれはそれで面倒そう。
今後詰めていきたい点
弱点が光るようにする
弱点外でも一度叩いた履歴のある属性がわかるようにする(未定)
2.は未定…というかまだ構想段階です。
「弱点がなかなか開かない時に何の属性で叩いたか忘れる」んですよね…
一応、「属性有効度150%以上」を拾っているということはそれ以下の情報も拾えるわけで、弱点表示と同じ要領で表示をすること自体は可能です。
ただ、
「表示項目が増えた分視認性的にどうなのか」という問題と、
「前後予測でどうにかなるのでは?」という点、
「自作の属性が10属性でネタ元のオクトラ等に比べると属性数が少ないからどうにかなるのでは?」
というのもあってどうするかは未定です。
オクトラ本家は12属性、大陸の覇者は14属性、リベサガは13属性なんですよね……まあ10くらいなら……許されるかな……みたいな……
まだバトルシステムの初歩中の初歩段階なので、この先システムを詰めていく中で変更点は出そうな気がしますが、とりあえず1.がクリア出来たら次の段階に進もうかな~と思っています。
もうできている部分いじるだけなので…
あとはなんだか最近Vroidいじったり、Blenderを毎日3ミリずつ理解して2年を費やし自宅を立てようとしていたりしていませんが、それらに関してはそのうち……
Comments