Skip to content

改造: 回復アイテムを強化してみる

ここでは『Galaxy Wars』の回復アイテムの処理を題材に、JavaScript の「条件分岐」といくつかの演算子を紹介します。

体力を回復する処理

前ページ の最後で紹介した、Player.MAX_HP が登場する三つ目の箇所は、以下のようなコードでした。

js
class Player {
    /**
     * 衝突イベントハンドラ
     */
    onCollision(e) {
        if (e.type === EntityType_1.EntityType.ITEM) {
            ...
            switch (e.itemType) {
                ... 
                case ItemType_1.ItemType.RECOVER:
                    if (this.hp < Player.MAX_HP) 
                        this.hp++;
                    break;
                default:
            }
            ...
        }
        ...
    }
    ...
}

PlayeronCollision という名前から考えると、これは「自機が何かに衝突した時の処理」だと想像できます。

TIP

on〜 という名前は、しばしば「〜した時 (の処理)」という意味で利用されます。 ここでは collision (衝突、激突) がついているので、何かがぶつかった時の処理だろうと判断しています。 アクション要素のあるゲームには大抵、何かしらの衝突判定と衝突時の処理があります。

Player.MAX_HP が現れるのは次の文です。

js
if (this.hp < Player.MAX_HP)
    this.hp++;

条件分岐: if 文

JavaScript において、以下のような文を if 文 と呼びます。

js
if (条件)
  処理;

これは「この行を実行した時、条件 が満たされているなら 処理 を実行する。満たされていなければ 処理 は実行せずに次に進む」という意味になります。 具体的なコードに当てはめてみると、

js
if (this.hp < Player.MAX_HP)
    this.hp++;

という今回のコードでは、

  • 条件: this.hp < Player.MAX_HP
  • 処理: this.hp++

ということになります。

JavaScript において A < B は、数学と同じように「AB より小さい」という意味です。 また JavaScript で A++ は、 A = A + 1 と同じ意味、つまり「A を 1 増やす」という意味です。

TIP

  • 繰り返しになりますが、= の意味が数学とは異なることに気をつけてください。 自然数や実数で A = A + 1 という等式は成り立ちません。 JavaScript では「現在の A の値に 1 を足したものを、変数 A に代入する」という意味になるので、「A を 1 増やす」ことになります。
  • 厳密に言えば A++A = A + 1 は完全に同じではありません (式の一部として書いた時の挙動が異なります) が、ここでは気にする必要はありません。
  • ++ の逆で、「A を 1 減らす」 A-- という記述もできます。

そのためこのコードは「現在の自機の体力 (this.hp) が最大体力 (Player.MAX_HP) より小さいならば、自機の体力を 1 増やす」という処理になります。 つまりこれは体力を回復する処理です。

体力が最大の時に回復しようとしても何も起きるべきではないので、「体力が最大より小さい時」という条件がついています。

TIP: 演算子

<=, ++ などの、演算を表す記号をまとめて 演算子 (operator) と呼びます。

TIP: if 文とブロック

if 文の「条件が満たされた時の処理」は 1 文だけとは限りません。処理が複数の文にまたがる場合は、全体を {, } で囲むことができます。

js
if (条件) {
  処理1;
  処理2;
  ...
}

このように書くと、条件 が成り立っていれば 処理1, 処理2, ... と順に実行され、そうでなければ {} 内は実行されません。

このように {, } で囲んでひとまとめにした処理を ブロック と呼びます。 文が一つだけであっても {, } で囲むことはできるので、慣れない間は常に {, } を書く のもおすすめです。

またここで 処理1;処理2; は文ですが、それを含む if (条件) { ... } (if 文) もまた文と呼ばれることに注意してください。 日本語の文章と異なり、JavaScript では文が多重構造になることがあります。

体力を回復する時

さて、ではこの処理が実行されるのはいつでしょうか。

冒頭で見た通り、この処理は class Player, onCollision() というブロックの一部なので、名前から恐らく「自機 (Player) が何かに衝突した時の処理」でした。さらに周辺のコードを見てみましょう。

js
  switch (e.itemType) {
      ... 
      case ItemType_1.ItemType.RECOVER: 
          if (this.hp < Player.MAX_HP) 
              this.hp++;
          break;

体力回復処理の直前に case ItemType_1.ItemType.RECOVER という記述があります。 「ItemType (アイテムタイプ) の RECOVER (回復する)」という名前から、これは「回復アイテムを取った時の処理」だと推測できます。

Galaxy Wars の回復アイテム (黄色い R のオブジェクト)

実際これを取得すると「RECOVER」と表示されることを見ても、間違いないでしょう。

回復アイテムを取得した直後の画面

回復アイテムを強化してみる

ここでは回復アイテムの性能を変えてみましょう。 回復量を 1 から 2 に増やしてみます。

this.hp を増やす量が 1 ではなくなるので、 ++ 演算子は使えません。 代わりに代入 = と加算 + の演算子で次のように書くことができます。

js
if (this.hp < Player.MAX_HP)
    this.hp++; 
    this.hp = this.hp + 2; 

このように書くと「現在の this.hp の値に 2 を足したものを、this.hp に代入する」処理になるので、体力 (this.hp) が 2 増えることになります。

TIP: += 演算子

A = A + B は、 A += B と書くこともできます。そのためこの文は this.hp += 2; としても構いません。

しかしこれだけでは不十分です。体力 (this.hp) が 29 の時に回復アイテムを取った状況を考えてみましょう。

js
if (this.hp < Player.MAX_HP)
    this.hp = this.hp + 2;

前ページの改造で最大体力 (Player.MAX_HP) は 30 になっているので、体力が 29 の時 this.hp < Player.MAX_HP は成り立ちます。 そのため「体力を 2 増やす処理」が実行され、最大値よりも大きい体力 31 になってしまいます。

INFO

この状態でゲームを動かしてみると、表示上最大値より大きい体力にはなりません (最大値分までしか表示する処理がないため) 。 しかし内部的には 31 になっていて、ダメージを受けても一度だけ体力が減らない状態になっていることがわかります。

これを防ぐには、条件式を見直す必要があります。次のように変更しましょう。

js
if (this.hp < Player.MAX_HP) 
if (this.hp <= Player.MAX_HP - 2) 
    this.hp = this.hp + 2;

ここで < の代わりに <= を使っていることに注意してください。 < が「より小さい」の演算子である一方、<= は「以下」の演算子です。

TIP

もちろん < は変えずに if (this.hp < Player.MAX_HP - 1) と書くこともできます。 ここでは、登場する数を 2 に統一でき、関連性がわかりやすくなるため<= を使っています。

しかし、実はこれでもまだ十分ではありません。 体力が 29 (Player.MAX_HP - 1) の時、今度は条件を満たさなくなるためです。 回復アイテムを取っても回復しないことになってしまいました。

これを修正する方法はいくつかありますが、ここでは if...else 文を使って対処します。

条件分岐: if...else 文

if...else 文は次のような形の文です。

js
if (条件)
  処理1;
else
  処理2;

if 文の終わりに else という記述が続く形になっています。 JavaScript においてこれは「条件 が成り立っていれば 処理1 を、そうでなければ 処理2 を実行する」という意味になります。

処理1, 処理2 の部分は文でありさえすればよいので、 処理2 の部分にさらに別の if 文を書くこともできます。 すなわちこうです。

js
if (条件1)
  処理1;
else
  if (条件2)
    処理2;

このようにすると「 条件1 が成り立っていれば 処理1 を実行する。そうでなく、条件2 が成り立っていれば 処理2 を実行する」という意味になります。

実際には改行を単純化して、次のように書かれることが多いです。

js
if (条件1)
  処理1;
else if (条件2)
  処理2;

これを使って、先ほどの体力回復処理を次のように書き換えましょう。

js
if (this.hp <= Player.MAX_HP - 2) 
    this.hp = this.hp + 2;
else if (this.hp <= Player.MAX_HP - 1) 
    this.hp = this.hp + 1; 

これによって、「体力が (最大値 - 2) 以下ならば 2 回復」「そうでなく、体力が (最大値 - 1) 以下ならば 1 回復」という処理になりました。

二つ目の条件がチェックされるのは、一つ目の条件が成り立たなかった時だけであることに気をつけてください。

TIP: if...else 文のブロック

if 文と同じように if...else 文に含まれる文の部分もブロックを書くことができます。

js
if (条件1) {
  処理1;
} else if (条件2) {
  処理2;
}

こちらも、わかりやすいように慣れない間は常に {, } で囲むのがおすすめです。

akashic sandbox で動作を確認してみましょう。

sh
akashic sandbox

体力がちょうど 29 の状態で確認するのがちょっと難しいですが、正しく 2 回復できることが分かるかと思います。

余談: いろいろな比較演算子

このページでは数を比較する演算子として <<= を紹介しました。 これを含め JavaScript で数を比較できる演算子には、次のようなものがあります。

意味
A < BAB より小さい
A <= BAB 以下
A > BAB より大きい
A >= BAB 以上
A === BAB と同じ
A !== BAB と同じでない

TIP

その他に ==, != もありますが、一部の挙動が複雑なためひとまず使わないのがおすすめです。