RPGツクールと数学のブログ

RPGツクールと数学についてのブログです。

アクションRPGの基礎(RPGツクールMV)(3/4)

< 第2回 

敵への攻撃の続きを作ります。今回はダメージを与え、倒す処理を作ります。

攻撃の流れを再掲します。

① 剣で攻撃できる位置にイベントがいるかどうか調べる
② そのイベントが攻撃の通るイベントかどうか調べる
③ 攻撃の通るイベントならその敵を攻撃受けるモードに設定
④ 攻撃を受けるモードならダメージを与える
⑤ ダメージが一定量に達したらやられる

前回作ったのは①~②です。

③は攻撃を受ける状態になったら、その各イベントが勝手に反応して、勝手にぎゃーとか言ってくれれば、いちいち司令塔みたいなイベントを作る必要がなくなります。そこで、反応する対象のイベントのセルフスイッチをONにし、そのセルフスイッチがONならその敵が並列処理でダメージを受けるという作りにします。ダメージを受ける処理が終わったらセルフスイッチをOFFにします。

④では敵のダメージを追加していきます。セルフスイッチはあってもセルフ変数はないので、やむを得ないのでスクリプトグローバル変数を作っておいてそれに保持させることにします。

そして⑤でダメージの値が一定量ならやられる演出をし、更にセルフスイッチをONにしてキャラクターを消滅させます。ただ、消えた敵はイベントID取得時に邪魔になるので座標(0, 0)にワープさせます。

まずは対象者になったイベントのセルフスイッチAをONにしましょう。AがONだと敵はダメージを受ける反応をすることにします。セルフスイッチBは敵が倒された状態にあることにします。

「対象者取得」を次のように書き換えます。ほぼ全部スクリプトになっちゃってますね。

◆指定位置の情報取得:対象者ID,イベントID,({攻撃x},{攻撃y})
スクリプト:var targetID = $gameVariables.value(4);
:     :if(targetID != 0 && ~$dataMap.events[targetID].note.indexOf("[対象者]")){
:     :     var mapID = this._mapId;
:     :     if(!$gameSelfSwitches.value([mapID, targetID, "B"])){
:     :         $gameSelfSwitches.setValue([mapID, targetID, "A"], true);
:     :     }
:     :}

これまでの2つの条件分岐は、両方ともスクリプトに統一しました。

スクリプトの中身だけ再掲(コピペ用)↓

var targetID = $gameVariables.value(4);
if(targetID != 0 && ~$dataMap.events[targetID].note.indexOf("[対象者]")){
    var mapID = this._mapId;
    if(!$gameSelfSwitches.value([mapID, targetID, "B"])){
        $gameSelfSwitches.setValue([mapID, targetID, "A"], true);
    }
}

ここが一番スクリプトっぽいスクリプトの記述をしている場面ですね。JavaScriptは、一行の終わりにセミコロン「;」を付けることになっています。そして、原則として半角英数字を用いてコードを記述する必要があります。上では例外として、文字列の検索を行う対象として「対象者」だけ全角になってます。

var 変数名で、ローカル変数(そのスクリプト内だけで使える変数)の定義ができます。変数4番「対象者ID」の値を変数targetIDに代入しておきます。

if(条件文)は、条件分岐:スクリプトとまったく同じです。今回の条件文は前回の2つの条件分岐を両方混ぜたものになっています。

targetID != 0
=targetIDが0以外(対象者ID≠0)

~$dataMap.events[targetID].note.indexOf("[対象者]")
=targetID番イベントのメモの文字列に[対象者]が含まれる

二つの条件が両方とも満たされている、という意味で、&&で結んでいます。&&は「かつ」、||は「または」の意味になります。「または」とは、両方のうち少なくとも一方が条件を満たせばよいというもので、両方が満たしていてもOKです。

var mapIDで新しく変数mapIDを定義し、this._mapIdを代入します。this._mapIdは、現在のマップ番号を表す値です。

$gameSelfSwitches.value([mapID, targetID, "B"])は、セルフスイッチを取得するものです。マップ番号mapIDの、イベント番号targetIDの、セルフスイッチBを取得します。先頭に!を付けると反転するので、セルフスイッチBがOFFなら~という意味になります。セルフスイッチBは対象の敵がもうやられているという意味なので、「やられてないなら」ということを意味します。

最後に$gameSelfSwitches.setValue([mapID, targetID, "A"], true)は、セルフスイッチAをONにするという命令です。trueがON、falseがOFFです。

要するにこのスクリプトでは、取得した対象者IDを利用して、
イベントが存在し(targetID != 0)、
対象者であり(~$dataMap.events[targetID].note.indexOf("[対象者]" ))、
まだやられてないなら(!$gameSelfSwitches.value([mapID, targetID, "B"]))、
ダメージを与える($gameSelfSwitches.setValue([mapID, targetID, "A"], true))
という意味になります。

f:id:fermiumbay13:20190801191217p:plain

こうもりをコピペ前(1匹)に戻しておいて、2ページ目を作りましょう。出現条件をセルフスイッチAがONにし、トリガーは並列処理、実行内容はダメージの演出を入れてセルフスイッチAをOFFにする、というものです。こうしておけば、このイベントだけで勝手にダメージを受ける処理を行ってくれます。

{{{
◆アニメーションの表示:このイベント,ダメージ
◆移動ルートの設定:このイベント(飛ばす,ウェイト)
:        :◇移動速度:5
:        :◇プレイヤーの逆を向く
:        :◇一歩前進
:        :◇一歩前進
:        :◇移動速度:3
スクリプト:this.character(0).unlock();
◆セルフスイッチの操作:A = OFF

}}}

アニメーションの「ダメージ」は、完成版の方からもってきてもらってOKです。ただ赤くフラッシュするだけです。

最後の方にスクリプトを入れていますが、これは敵が固まってしまう現象防止のためのおまじないです。これがないと、敵から攻撃を受けた直後にプレイヤーの攻撃が当たったとき、敵が動かなくなってしまうことがあるようです。

this.character(0).unlock();

「敵から攻撃を受けて1ページ目が実行される処理」と、「攻撃して敵の2ページ目が実行される処理」が重なってしまうタイミングで、通常は働くはずの「キャラクターの動作を制限する処理の解除」が行われなくなることがあるので、上記のスクリプトで常に解除されるようむりやりしています。*1

f:id:fermiumbay13:20190801191846p:plain

これで敵を攻撃するとダメージを受ける演出がされるようになりました。コピペして量産できます。きちんと同時にダメージを受けるのが確認できますね。

次は実際にダメージを与えて、一定量を超えると倒せるようにしましょう。

f:id:fermiumbay13:20190801191906p:plain

すべてのイベントのダメージ量を記録するため、初期設定のイベントでenemyDamage変数をスクリプトで用意しておきます。こうすることで、どこからでもenemyDamage変数を呼び出すことができます。

スクリプト内容↓

enemyDamage = new Array(999);
for(var i = 0; i < 999; i++){
    enemyDamage[i] = 0;
}

イベントは一つのマップに999個置けるので、enemyDamage変数を999個作ります。enemyDamage = new Array(999);で999個の配列変数を作ります。例えばイベント10番の敵に与えたダメージ量は、enemyDamage[9]で参照できます。1だけずれているので注意してください。

また、さっきは変数を定義するときに先頭にvarを付けていましたが、ここでは付けていません。こうすることにより、enemyDamageは他からでも自由に使うことが出来るようになります(グローバル変数といいます)。

for(var i = 0; i < 999; i++){
    enemyDamage[i] = 0;
}
の部分は999回の繰り返しです。999個あるenemyDamageすべてに0を代入することで、敵の初期ダメージ量を0にしています。HPを管理するようにしてもいいのですが、HPにすると初期化の際にすべての敵にHPを入れないといけないので、ダメージ量とした方が管理するのがラクです。

そして、実際に敵がダメージを受けたときにダメージ量を加算、一定値に達するとやられる処理を入れて倒します。

f:id:fermiumbay13:20190801192100p:plain

敵の2ページ目に追記しましょう。↓◇から始まる行を追加してみます。

◆アニメーションの表示:このイベント,ダメージ
◆移動ルートの設定:このイベント(飛ばす,ウェイト)
:        :◇移動速度:5
:        :◇プレイヤーの逆を向く
:        :◇一歩前進
:        :◇一歩前進
:        :◇移動速度:3
スクリプト:enemyDamage[this.character(0)._eventId - 1] += 1;
◇条件分岐:スクリプト:enemyDamage[this.character(0)._eventId - 1] >= 3
 ◇SEの演奏:Explosion1(90,100,0)
 ◇移動ルートの設定:このイベント(ウェイト)
 :        :◇すり抜けON
 :        :◇不透明度:230
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:204
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:179
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:153
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:128
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:102
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:77
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:51
 :        :◇ウェイト:5フレーム
 :        :◇不透明度:26
 :        :◇ウェイト:5フレーム
 ◇イベントの位置設定:このイベント,(0,0)
 ◇セルフスイッチの操作:B = ON
 ◇
:分岐終了
スクリプト:this.character(0).unlock();
◆セルフスイッチの操作:A = OFF

スクリプトが2行ありますが、これは敵のダメージ量を表すものです。

enemyDamage[this.character(0)._eventId - 1] += 1;

これは、現在のイベントのダメージ量を1足すという意味です。大きな攻撃の場合は、ダメージ量を2以上の値にしてやるとそれっぽいですね。

this.character(0)._eventId
=このイベントのイベントID

enemyDamage[this.character(0)._eventId - 1]
=このイベントのenemyDamage(1ずらすのを忘れずに)

a += 1;
変数aに1を加算

次の条件分岐もスクリプトで記述していますが、enemyDamageの部分は同じです。「このイベントのダメージ量」を表します。それが3以上だったら条件分岐の中に入れます。3ダメージで倒れるということです。この「3」こうもりのHPになるので、ここを変更すれば敵のHPが変わります。

a >= b … a≧bのとき
a <= b … a≦bのとき
a > b … a>bのとき
a < b … a<bのとき
a == b … a=bのとき( = は2個あることに注意)
a != b … a≠bのとき

次のコマンドは爆発してフェードアウトする演出です。この通りにする必要はないです。演出の後に忘れちゃいけないのはこの2行です。

◇イベントの位置設定:このイベント,(0,0)
◇セルフスイッチの操作:B = ON

これからイベントを消すので、セルフスイッチBをONにして消えたことにしたいのですが、位置をそのままにしておくとまずいことになります。確かに敵は消えているのですが、その消えた位置に別の敵が来た際に剣を振ると、イベントID取得時にその消えた方の敵が反応してしまう可能性があるのです。

それを防ぐため、消滅前にイベントの位置を例えば(0, 0)に移動させておきます。こうすれば座標(0, 0)以外では反応しない現象が起こらないので、(0, 0)に行けないようなマップにしておけば無問題となります。

f:id:fermiumbay13:20190801192353p:plain

敵に3ページ目を新しく作り、出現条件をセルフスイッチBにしておきます。
これは敵が倒された状態なので、画像を空っぽにしておきましょう。

f:id:fermiumbay13:20190801192413p:plain

これで敵を倒せるようになりました。

次回で基礎は完結です。敵に触ったらダメージを受ける処理と、マップ初期化の処理を作って完成させましょう。

第4回

*1:このあたりは苦肉の策です……