Categories: Unity

Unity:プレイヤーの体力をネットワーク化する~マルチプレイヤーネットワーキングのチュートリアルに挑戦

Unityの公式チュートリアルに掲載されているマルチプレイヤーネットワーキングを勉強を進めている様子をお送りします。前回の記事はこちらからどうぞ

プレイヤーの体力をネットワーク化する

公式サイトのチュートリアルページ プレイヤーの体力をネットワーク化する

今回は体力値の変更をサーバーで行い、その後クライアントで同期するように改造します。

スクリプトHealthの修正

体力値の変更をサーバー上のみで行うようにスクリプトを修正します。

  1. Health スクリプトを開く
  2. 名前空間 UnityEngine.Networking を追加
  3. スクリプトを、NetworkBehaviour から派生するように変更
  4. 変数 currentHealth を作成して [SyncVar]属性を付ける
  5. ダメージがサーバー上のみで適用されるように、関数 TakeDamage に isServer のチェックを追加
  6. スクリプトを保存

修正したスクリプトのコードにコメントを加えたものがこちら。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// UIコンポーネント関数を使用するため名前空間の追加
using UnityEngine.UI;
// ネットワークコンポーネント関数を使用するため名前空間の追加
using UnityEngine.Networking;

public class Health : NetworkBehaviour
{
    // 体力値を格納する変数(定数値 100)
    public const int maxHealth = 100;
    // 現在の体力値を格納する変数(ネットワーク同期、初期値は maxHealth)
    [SyncVar]
    public int currentHealth = maxHealth;
    // RectTranform コンポーネントを格納する変数
    public RectTransform healthBar;

    // ダメージ処理(サーバー上のみ)
    public void TakeDamage(int amount)
    {
        // サーバーでない場合
        if (!isServer)
        {
            // 以降の処理を中断
            return;
        }
        // 現在の体力値から 引数 amount の値を引く
        currentHealth -= amount;
        // 現在の体力値が 0 以下の場合
        if (currentHealth <= 0)
        {
            // 現在の体力値に 0 を代入
            currentHealth = 0;
            // コンソールに"Dead!"を表示する
            Debug.Log("Dead!");
        }
        // RectTranform コンポーネントのサイズを体力値に合わせて変更
        //(X値に currentHealth を代入、Y値は RectTranform コンポーネントのY値を代入)
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}

動作確認

  1. シーンをスタンドアロンのアプリケーションとして Build and Run を実行
  2. ゲーム内 UI から[Host]ボタンをクリックしてゲームをホストとして開始
  3. プレイヤーゲームオブジェクトを動かす
  4. Unity に戻ってPlay モードを開始
  5. ゲーム内 UI から[LAN Client]ボタンをクリックしてクライアントとしてホストに接続
  6. スタンドアロン プレイヤーを終了
  7. Unity に戻ってPlay モードを終了

このステップを終えてゲームを実行。ビルドしたゲームを2つ実行してウィンドウを並べました。画面左のホスト(サーバー&クライアント)のみ体力ゲージが処理されています。

スクリプト Health の修正

体力値ゲージの RectTransform を他のクライアントでも同期するようにスクリプトを修正します。

  1. Health スクリプトを開く
  2. Healthbar を変化させるコードを、新規関数 OnChangeHealth 内に移動
  3. currentHealth の SyncVar 属性内に、新規関数 OnChangeHealth へのフックを設定
  4. スクリプトを保存

修正したスクリプトのコードにコメントを加えたものがこちら。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// UIコンポーネント関数を使用するため名前空間の追加
using UnityEngine.UI;
// ネットワークコンポーネント関数を使用するため名前空間の追加
using UnityEngine.Networking;

public class Health : NetworkBehaviour
{
    // 体力値を格納する変数(定数値 100)
    public const int maxHealth = 100;
    // 現在の体力値を格納する変数(ネットワーク同期、初期値は maxHealth)
    // 変数値が変更されたら関数 OnChangeHealth を実行
    // OnChangeHealth の引数 health に currentHealth の値を渡す
    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;
    // RectTranform コンポーネントを格納する変数
    public RectTransform healthBar;

    // ダメージ処理(サーバー上のみ)
    public void TakeDamage(int amount)
    {
        // サーバーでない場合
        if (!isServer)
        {
            // 以降の処理を中断
            return;
        }
        // 現在の体力値から 引数 amount の値を引く
        currentHealth -= amount;
        // 現在の体力値が 0 以下の場合
        if (currentHealth <= 0)
        {
            // 現在の体力値に 0 を代入
            currentHealth = 0;
            // コンソールに"Dead!"を表示する
            Debug.Log("Dead!");
        }
    }

    // 体力値ゲージの変更処理
    // (引数 health には 変数 currentHealth の値が渡される )
    void OnChangeHealth(int health)
    {
        // RectTranform コンポーネントのサイズを体力値に合わせて変更
        //(X値に health を代入、Y値は RectTranform コンポーネントのY値を代入)
        healthBar.sizeDelta = new Vector2(health, healthBar.sizeDelta.y);
    }
}

チュートリアルの解説部分で書かれている 関数 void OnChangeHealthの引数名が、関数部分だけ書かれている部分では下記の通り記述されています。

void OnChangeHealth (int currentHealth)
{
healthBar.sizeDelta = new Vector2(health, currentHealth.sizeDelta.y);
}

一方、スクリプト Health の全コードが書かれている部分では void OnChangeHealth (int health) と記述されていたので、しばらく???となりましたが、意味を考えると Health の全コードが書かれている
void OnChangeHealth (int health) の方が正しいと判断しました。

動作確認

  1. シーンをスタンドアロンのアプリケーションとして Build and Run を実行
  2. ゲーム内 UI から[Host]ボタンをクリックしてゲームをホストとして開始
  3. プレイヤーゲームオブジェクトを動かす
  4. Unity に戻ってPlay モードを開始
  5. ゲーム内 UI から[LAN Client]ボタンをクリックしてクライアントとしてホストに接続
  6. スタンドアロン プレイヤーを終了
  7. Unity に戻ってPlay モードを終了

このステップを終えてゲームを実行。ビルドしたゲームを2つ実行してウィンドウを並べました。

画面左のホスト(サーバー&クライアント)側、画面右のクライアント側のどちらも外力ゲージが変化して同期されるようになりました。

今回のポイントはこの部分ですね。

ステート同期(State Synchronization)の為のもうひとつのツール、SyncVar フック の登場です。SyncVar フックは、関数を SyncVar と結び付けます。これらの関数は、SyncVar の値が変わるとサーバーおよび全てのクライアント上で呼び出されます。

ダメージ処理はサーバー側だけで行い、その変数の変化に合わせてクライアント側で体力ゲージの処理を行うといういうのはうまくできているな~と思いました。

corevale