Skip to content

改造: 体力を増やしてみる

ソースコードを改変する改造で、もっとも単純なのは定数の変更でしょう。 ここではプログラムの「変数」と呼ばれる機能を紹介し、自機の初期体力を増やす改造をします。

変数と定数

ゲーム中の処理で変化する値は、ソースコード上では 変数 として表現されます。 たとえば Galaxy Wars には次のような変数があります。

  • 自機の現在位置
  • 現在の体力やスコア
  • ゲームの残り時間
  • 今は爆発アニメーションの何フレーム目を表示しているか

プログラムは、時間経過やプレイヤーの操作に応じて処理を行い、変数の値を変化させます。 変数の値に応じて変化する画面を (一秒間に数十回ずつ) 描画しつづけるのが、ゲームの典型的な形です。

一部の値は、ゲーム中にまったく変化しません。これを 定数 と呼びます。たとえば次のような値は定数です。

  • 自機の最大体力
  • 敵の移動スピード
  • 敵を倒して得られるスコア
  • 弾の威力
  • タイトル画面の表示時間

ソースコードから定数を見つけて書き換えれば、自機の性能や敵の挙動をある程度自由に変更することができます。

TIP

ここではわかりやすさのため「数値」を扱う変数だけを挙げています。実際には「文字列」や、 ゲーム画面に表示される「画像データ」なども変数に代入して扱うことができます。 たとえば RPG でプレイヤーが指定する「主人公の名前」は、文字列の変数として表現されます。

自機の体力を変更してみる

ここでは定数を書き換える改造の例として、自機の体力を変更してみましょう。

自機の体力は画面左上に表示されています。ゲージの表示を数えると、どうやら初期値は 10 であることがわかります。

Galaxy Wars の体力表示

Galaxy Wars のスクリプト構成

改造にあたっては「自機の体力」の定数がどこで定義されているか探さなければなりません。

Akashic Engine のゲームのプログラムは script/ フォルダに格納されています。 Galaxy Wars のフォルダ構造を確認すると、次のようになっています。

GalaxyWars/
├── game.json
├── image/
│   ├── (中略)
│   └── title.png
├── script/
│   ├── AlphaEnemy.js
│   ├── Background.js
│   ├── BetaEnemy.js
│   ├── Bullet.js
│   ├── Enemy.js
│   ├── EnemyManager.js
│   ├── EntityType.js
│   ├── GameCore.js
│   ├── GameOverLogo.js
│   ├── GammaEnemy.js
│   ├── Global.js
│   ├── Item.js
│   ├── ItemGaugeTray.js
│   ├── ItemType.js
│   ├── Math.js
│   ├── Particle.js
│   ├── Player.js
│   ├── PlayerStatus.js
│   ├── ScreenEffector.js
│   ├── Shield.js
│   ├── bootScene.js
│   ├── emmitDamageEffect.js
│   ├── gameScene.js
│   ├── main.js
│   └── titleScene.js
└── text/
    ├── glyph_area.json
    └── version.txt

.js ファイルが 25 個あり、すべて読むと大変ですが、ファイル名からある程度内容を推測できそうです。 たとえば Bullet.js は恐らく弾丸に関する処理、Shield.js はシールド、 GameOverLogo.js はゲームオーバー画面関連の処理が書かれていることでしょう。

定数の書き換え

「自機の体力」は自機関連の値なので Player.jsPlayerStatus.js に定義されている、と予想できそうです。 実際に Player.js を確認すると、ファイルの末尾付近に次のような行が見つかります。

js
Player.MAX_HP = 10; // プレイヤー最大HP

これは JavaScsript で「 PlayerMAX_HP という変数に、 10 を代入する」という意味の文です。

TIP

等号 = の意味が数学とは少し異なることに注意してください。 数学において数式 A = B は「AB は等しい」という意味です。 一方 JavaScript の A = BA という変数に B の値を代入する」 という意味になります。 「等しい」というよりも「等しくする」ものだと考えてください。

MAX_HP という変数名、10 という値、そして プレイヤー最大HP というコメント (後述)、どれを見てもこれが自機の体力でしょう。 値を 30 に変えてみましょう。

js
Player.MAX_HP = 10; // プレイヤー最大HP
Player.MAX_HP = 30; // プレイヤー最大HP

TIP

// プレイヤー最大HP の部分は「コメント」です。 JavaScript では // とそのあと行末までの文字はコメントになります。 コメントはプログラムの実行上無視されるもので、何でも書くことができます。 実際には、プログラムを読む際にわかりやすいよう補足説明などを書くために使われます。

ターミナルで akashic sandbox を実行し、動作がどう変化するか確認しましょう。

sh
akashic sandbox

Web ブラウザを確認すると、無事体力が増えたことがわかります。

体力が変化した様子

周辺コード解説

MAX_HP が登場する他の箇所も眺めてみましょう。

Player.js の中で、 Player.MAX_HP という記述が出てくる箇所は三つあります。 一つは先ほどの定義箇所で、値として 10 を設定 (代入) しています。

js
Player.MAX_HP = 10; // プレイヤー最大HP

自機の状態をリセットする処理

二つ目は、class Player { ... } という塊の中の reset() { ... } という塊の中です。

js
class Player {
    ...
    /**
     * Player初期化
     */
    reset() {
        this.type = EntityType_1.EntityType.PLAYER;
        const imgAsset = g.game.scene().asset.getImage("/image/player.png");
        this.pos = { 
            x: (g.game.width - imgAsset.width) / 2,
            y: g.game.height - imgAsset.height * 2 
        };  
        this.hp = Player.MAX_HP; 
        this.score = 0;
        this.bulletCntr = 0;
        this.bulletInterval = 10; 
        ...
    }
    ...
}

ここでは class などの詳細は省きますが、この reset() の塊 (6 行目から 18 行目) は「Player (自機) の状態を初期化 (リセット) する処理」になっています。 (行番号はここでの表示上のもので、実際のファイルの行番号とは異なります)

TIP

このことは「 Playerreset 」という名前から推測できるほか、 reset() のすぐ上のコメントからも読み取れます。 「// から行末まで」と同様、「/* から */ の間」も JavaScript の「コメント」です。 // と違い、こちらは複数行に渡ってコメントを書くことができます。

その中で Player.MAX_HP が登場するのはこの部分です。

js
this.hp = Player.MAX_HP;

Player.MAX_HP の定義箇所と同じように、これも 変数 = 値; という形になっていることに気づきます。 これは「 thishp という変数に Player.MAX_HP の値を代入する」という文です。 名前と文脈から見るに、自機の体力をリセットする (最大値に設定する) 処理でしょう。

TIP

HP は「ヒットポイント」の略で、体力を表す言葉としてよく使われます。

TIP: this について

Player.MAX_HP は「自機の」最大体力、this.hp は「自機の」体力です。 ここで Playerthis という二つの語が、どちらも「自機」を表していることに気づかれるかもしれません。 この違いの詳細はここでは説明しきれないのですが、this は「ゲーム開発者が定義しなくても使える」「書かれている場所によって意味が変わる」特殊な値です。

ここでは名前と文脈から「おそらく自機のことだろう」と推測できれば十分です。

実験: 初期位置を変えてみる

自機のリセット処理 (reset()) の全体は複数の文でできています。 JavaScript の「文」とは、セミコロン ; で終わるひとかたまりの記述です。

TIP

セミコロン ; は日本語の句点 のようなものだと考えるとわかりやすいかもしれません。

ただし JavaScript にはセミコロンなしで終わる文もあります (ブロックを伴う if 文など)。 また場合によってはセミコロンを省略することもできます。ここではわかりやすさのため、セミコロンを省略しません。

再掲すると、reset() の全体像はこのようなものでした。

js
    reset() {
        this.type = EntityType_1.EntityType.PLAYER;
        const imgAsset = g.game.scene().asset.getImage("/image/player.png");
        this.pos = { 
            x: (g.game.width - imgAsset.width) / 2,
            y: g.game.height - imgAsset.height * 2 
        };  
        this.hp = Player.MAX_HP; 
        this.score = 0;
        this.bulletCntr = 0;
        this.bulletInterval = 10; 
        ...
    }

複数の文は、原則上から順に実行されます。つまりこの「自機のリセット処理」は、まず以下の文を実行し、

js
this.type = EntityType_1.EntityType.PLAYER;

次に以下の文を実行し、さらにその次の文を……と逐次実行していくものになっています。

js
const imgAsset = g.game.scene().asset.getImage("/image/player.png");

ここだけ見てもよく分からない記述が大半ですが、たとえば以下の文は

js
this.score = 0;

名前から恐らくスコア (得点) を 0 にリセットしているだろう、ということが読み取れます。また以下のような複数行にわたる文もあります。

js
this.pos = { 
    x: (g.game.width - imgAsset.width) / 2,
    y: g.game.height - imgAsset.height * 2 
};

pos は position (位置) の略、 x, y は画面の X, Y 座標に関係がありそうだと推測すると、この文は自機の位置をリセットしているものだと予想できます。

ためしに x のあとを 0 にしてみましょう。

js
this.pos = { 
    x: (g.game.width - imgAsset.width) / 2, 
    x: 0, 
    y: g.game.height - imgAsset.height * 2 
};

ターミナルで akashic sandbox を実行し、影響を確認します。

sh
akashic sandbox

Web ブラウザを確認すると、無事 (?) 自機の初期位置が画面左端になりました。

初期位置が横方向にずれた様子

改変した箇所は確かに自機の初期位置であることや、X 座標の 0 はどうやら画面の左端であるらしいことがわかります。 これはあくまで実験してみただけなので、 x の部分は元に戻しておきましょう。

IMPORTANT

このようにコード改造は、名前から処理の内容を推測していく ことと、ためしに書き換えてみる ことが重要です。 ゲームが途中で止まるようになったり、画面が真っ白のままゲームが始まらなくなってしまうこともありますが、その場合はまた改造前のコードから始めましょう。 恐れず試行錯誤を重ねるのがおすすめです。

回復アイテムの処理

Player.MAX_HP が現れる三つ目の箇所は、回復アイテムの処理です。

js
    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;
                ...
            }
            ...
        }
        ...
    }

次のページでは、このコードを簡単に解説しつつ、回復アイテムを強化してみます。