おもちゃラボ

Unityで遊びを作っていきます

【Unity】インべーダ作りで学ぶUnet超入門3

前回はUnetを使って、ネットワークで同期して動くプレイヤ(自機)を作成しました。今回はネットワークで同期して動くインベーダーを作りましょう。

【Unity】インベーダ作りで学ぶUnet超入門1 - おもちゃラボ
【Unity】インベーダ作りで学ぶUnet超入門2 - おもちゃラボ

インベーダを作る

Unetを使って敵キャラなどプレイヤ操作の必要がないオブジェクトを作る場合、基本的にはサーバー「だけ」で生成し、サーバー「だけ」で動かすようにします。

 このように、サーバーにすべておまかせすることで、クライアントの状態によらずに、どのクライアントでも同じ動きを保証できます。逆にクライアントで敵の座標などをいじってしまうと、クライアント間で同期がとれずに齟齬が生じてしまうので注意が必要です。

f:id:nn_hokuson:20190306211450p:plain:w400

では実際に敵(インベーダー)を作っていきましょう。インベーダーを作る流れは、まずインベーダーのPrefabを作り、次にそれを生成するマネージャクラスを作ります。この設計の流れはUnityの教科書と同じなので、そちらを参考にしてください。

Unityの教科書 Unity 2018完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

Unityの教科書 Unity 2018完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

まずはインベーダーのPrefabをつくるため、プロジェクトウィンドウからenemyをシーンにドラッグ&ドロップしてください。

f:id:nn_hokuson:20190306212217j:plain

ネットワークで位置を同期するために、インスペクタのAdd ComponentボタンをクリックしてNetwork→Network Transformコンポーネントをアタッチしてください。

f:id:nn_hokuson:20190306212413j:plain

次にインベーダーを左右に移動させるスクリプトを作成します。名前をEnemyControllerに変更して次のスクリプトを入力して下さい。

using UnityEngine;
using UnityEngine.Networking;

public class EnemyController : NetworkBehaviour {

    int moveDir = 1;
    int moveCount = 0;

    void Update ()
    {
        if (!isServer) return;
        if (Time.frameCount % 50 != 0) return;

        moveCount++;

        if (moveCount == 5)
        {
            moveCount = 0;
            moveDir = -moveDir;
        }
		transform.Translate(moveDir, 0, 0);
    }
}

ここでもMonoBehaviorの代わりにNetworkBehaviorを使用していることに注意してください。

Updateメソッドの中で左右への移動処理を書いています。大切なのは、Updateの先頭でisServerがtrueでなければreturnしている部分です。このように書くことで、以下の処理はサーバーのみで行われるようになります。

スクリプトを記述できたらInvaderオブジェクトにドラッグ&ドロップしてアタッチしておきましょう。

f:id:nn_hokuson:20190306212549j:plain

最後にヒエラルキーウインドウからプロジェクトウィンドウにInvaderをドラッグしてPrefabを作成します。Prefabの名前はenemyPrefabに変更してください。

f:id:nn_hokuson:20190306212725j:plain:w500

ヒエラルキーウインドウのenemyPrefabは不要なので削除しておいてください。

f:id:nn_hokuson:20190306212923j:plain:w250

インベーダを生成するクラスを作る

次にいま作成したインベーダーのPrefabからインベーダーを複数作るジェネレータクラスを作りましょう。ジェネレータも、インベーダーと同じくサーバーのみで動作するようにします。

まずはヒエラルキーウインドウでCreate→Create Emptyを選択して空のオブジェクトを作成し、名前をEnemyGeneratorに変更しておきます。

f:id:nn_hokuson:20190314091938p:plain:w300

次にジェネレータ用のスクリプトを作ります。先程と同様にプロジェクトウィンドウでC# Scriptを作り、名前をEnemyGeneratorに変更しましょう。変更できたら次のスクリプトを入力して下さい。

using UnityEngine;
using UnityEngine.Networking;

public class EnemyGenerator : NetworkBehaviour
{
    public GameObject enemyPrefab;

    void Start()
    {
        if (!isServer) return;

        for (int y = 0; y < 3; y++)
        {
            for (int x = 0; x < 5; x++)
            {
                GameObject go = Instantiate(enemyPrefab,
                                            new Vector3(-5f + x * 2, y*1.5f+1.0f, 0),
                                            Quaternion.identity);
                NetworkServer.Spawn(go);
            }
        }
    }
}

このスクリプトでもStartメソッドの最初でisServerかをチェックしています。そしてサーバーであればインベーダーを5体を3段ぶん生成しています。このように、サーバーだけで動作させたいスクリプトは必ずisServerでチェックするようにしましょう。

インベーダーを生成するには一旦おなじみのInstantiateメソッドを使ってインベーダーオブジェクトを作っています。ただ、Instantiateするだけだと、サーバーでしかインベーダーは生成されません。各クライアントにも同じオブジェクトを生成するにはNetworkServer.Spawnメソッドを使ってSpawnする必要があります。Spawnの引数にはクライアントでも生成したいゲームオブジェクトを渡します。

 スクリプトができたら、ヒエラルキーウインドウのEnemyGeneratorにドラッグしてアタッチします。Enemy Prefabの欄には、プロジェクトウィンドウの「enemyPrefab」をドラッグ&ドロップします。

f:id:nn_hokuson:20190314092732j:plain

ネットワークマネージャーにPrefabを登録する

これで完成!といいたいところですが、ネットワーク上で動的に生成するPrefabはすべてNetworkManagerに登録する必要があります。これ、超忘れがちなので注意してください。(忘れても最近はちゃんとエラーメッセージを出してくれるようです)

ヒエラルキーウインドウでNetworkManagerを選択してRegistered Spawnable Prefabsの欄の「+」をクリックします。欄が新たに作成されるので、そこにプロジェクトウィンドウからenemyPrefabをドラッグ&ドロップしてください。

f:id:nn_hokuson:20190314093056j:plain

これで実行すれば、次のように敵を生成できます。ビルドして動作確認しておきましょう。

f:id:nn_hokuson:20190314093943g:plain:w500

次回は自機から弾を発射してインベーダーをやっつけられるようにします。

【Unity】インベーダ作りで学ぶUnet超入門2

前回はUnetを使ったプログラムの作り方の概要を紹介し、プログラムを作るための準備をしました。今回はプレイヤの動きを作っていきましょう。

nn-hokuson.hatenablog.com

Network Managerのはたらき

まずはネットワーク関係の準備をすべて受け持ってくれるNetwork Managerについて説明します。Network Managerの主な役割には次のようなものがあります。

  • サーバーになるかクライアントになるかを選択
  • プレイヤを人数ぶん自動的に生成
  • ネットワーク同期するオブジェクトを生成

このようにNetwork Managerはネットワークに必要な作業全般を受け持ちます。

色々あってややこしそうですが、Network Mangerコンポーネントが基本的に全部やってくれるので、プログラマがやることは次の2つです。

  1. プレイヤのPrefabオブジェクトの登録
  2. ネットワーク上で動的に生成するPrefabの登録

プレイヤのPrefabを登録しておくと、人数分のプレイヤをゲーム開始時に自動生成してくれます。

f:id:nn_hokuson:20190303134321j:plain:w400

また、ネットワーク上で動的に生成したいPrefabもNetwork Managerに登録しておく必要があります(こちらは敵を作るときに説明します)。

Network Managerを作る

では早速NetworkManagerコンポーネントをアタッチするところから作っていきましょう。まずはヒエラルキービューでCreate→Create Emptyを選択し、空のオブジェクトを作成します。オブジェクト名はNetworkManagerにしておきましょう。

f:id:nn_hokuson:20190303134946p:plain:w270

作成したNetworkManagerオブジェクトにネットワーク用のコンポーネントをアタッチします。インスペクタからAdd Componentボタンをクリックし、Network → NetworkManagerコンポーネントとNetwork → NetworkManagerHUDオブジェクトをアタッチして下さい。NetworkManagerHUDはサーバーになるかクライアントになるかを、ゲーム実行時にGUIで選択できるようにするための、お助けコンポーネントです。

f:id:nn_hokuson:20190303140310j:plain

ここまでで、一度実行してみましょう。次のような画面が表示されて、ホスト(サーバー)かクライアントかを選択できるようになっています。

f:id:nn_hokuson:20190303135520p:plain:w400

ネットワーク対応のプレイヤを作る

次にプレイヤ(自機)を作っていきましょう。ネットワークゲームといっても、普通のゲームと作り方はそんなに変わりません。

プレイヤのスプライトをシーンウインドウにドラッグ&ドロップします。続いてプレイヤの位置をネットワークで同期するためNetworkTransformコンポーネントをアタッチします。Playerオブジェクトを選択した状態でインスペクタのAdd Componentボタンを押し、Network→Network Transformを選択してください。

f:id:nn_hokuson:20190303140505j:plain

前回の記事で説明したとおり、Unetではプレイヤはクライアントの入力を受けてサーバーを更新するという順番で動かします。そのため、PlayerオブジェクトのNetwork Identityコンポーネントの「Local Player Authority」にチェックを入れます。

f:id:nn_hokuson:20190303140546p:plain:w350

キー入力に応じてプレイヤを移動させるスクリプトを作成します。名前をPlayerControllerに変更しましょう。変更できたら次のスクリプトを入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour {

    void Start () {
         transform.position = new Vector3(0, -4, 0);        
    }

    void Update () {
        if (!isLocalPlayer) return;

        if (Input.GetKey(KeyCode.A)) transform.Translate(-0.1f, 0, 0);
        if (Input.GetKey(KeyCode.S)) transform.Translate( 0.1f, 0, 0);
    }
}

通常のスクリプトとほぼ同じですね。ただ、普通のクラスがMonoBehaviorクラスを継承しているのに対して、ネットワークオブジェクトの場合にはNetworkBehaviorクラスを継承します。

Updateメソッドの中で、キー入力を受け取る前にisLocalPlayerかどうかをチェックしています。Unetでは、自分のプレイヤを「ローカルプレイヤ」として他人のプレイヤとは区別します。isLocalPlayerをチェックすることで、自分のプレイヤだけを動かすことができるようになります

f:id:nn_hokuson:20190303140817p:plain:w500

作成したPlayerControllerは、Playerゲームオブジェクトにドラッグしてアタッチしておきましょう。

f:id:nn_hokuson:20190303141151j:plain

普通のUnityプロジェクトなら、これで実行すればPlyerを動かせますね。ただ、Unetを使ったプロジェクトではプレイヤはNetwork Managerに作成してもらう必要があります。

プレイヤをNetworkManagerで生成してもらう

では、作成したプレイヤをNetworkManagerに生成してもらう部分を作ります。まずはNetwork Managerに登録するためのPrefabを作ります。

ヒエラルキーウインドウのPlayerをプロジェクトウィンドウにドラッグして、名前をplayerPrefabに変更してください。作成したplayerPrefabをNetworkManagerのSpawn Info→Player Prefabにドラッグしてください。

f:id:nn_hokuson:20190303142732j:plain

Player PrefabにセットすることでNetworkManagerがゲーム開始時に人数分のプレイヤを自動生成してくれます。したがって、現在ヒエラルキーウインドウにあるPlayerは不要です。deleteで消しておいて下さい。

f:id:nn_hokuson:20190303142909j:plain:w200

2台の端末で仮想的に通信する

実際にゲームを実行してちゃんと動くか試してみましょう。2台で通信するのは手間がかかるので、通常はPC/Mac用の実行ファイルとして書き出したアプリと、Unityエディタ間の通信で動作確認します。

 書き出した実行ファイルを実行してHostを選択、エディタではClientを選択します。HostでA/Sキーを使ってプレイヤを動かすと、それにあわせてClientのプレイヤも移動します。また逆にClientでプレイヤを動かすと、Hostのプレイヤが左右に移動します。それぞれでプレイヤを動かしてみて、同期して動くか確かめてください。

f:id:nn_hokuson:20190303143347g:plain:w400

プレイヤの作り方まとめ

プレイヤの生成とネットワーク対応はこれで終了です。やったことをまとめておきましょう。

  1. プレイヤオブジェクトにNetworkIdentityコンポーネントとNetworkTransformコンポーネントをアタッチ
  2. クライアントで移動を制御するためLocalAuthorityにチェックを入れる
  3. isLocalPlayerで自分のプレイヤだけを移動させるスクリプトを作成
  4. NetworkManagerにプレイヤのPrefabを登録する

次回はネットワークで同期して動くインベーダーを作っていきます。
nn-hokuson.hatenablog.com

【Unity】インベーダ作りで学ぶUnet超入門1

Unityでネットワークゲームを作るにはUnetというライブラリを利用します。このUnetを使うことで簡単(?)にネットワークゲームを作ることができます。

簡単にハテナがついているのは、それほど簡単じゃないからです(笑)

Unetには様々なネットワークの専門用語が出てきて、どうやって作ったら良いのか混乱しがちです。ただ、基本的なパターンはある程度決まっているので、それを紹介してからインベーダーゲームづくりに取り掛かりましょう。

f:id:nn_hokuson:20190303132538p:plain:w600

今回の目次は次のようになります。

ネットワークゲームの基本パターン

Unetを使ってネットワークゲームを作る場合、基本になるのは次のようなサーバとクライアントがあるパターンです。
f:id:nn_hokuson:20180617131709p:plain:w380

2人以上の対戦ゲームでは、1台がサーバーとクライアントを兼ねた「ホスト」と呼ばれる役割になります。ときどきホストという言葉がでてくるので、覚えておきましょう。

オブジェクトの生成と移動

さて、いよいよネットワークゲームの作り方について考えていきます。いきなり、とても大切なことですが、Unetを使ったネットワークゲームを作る場合には、サーバーでオブジェクトを生成するのが基本になります。

サーバーでオブジェクトを生成すると、それが各クライアントでも自動的に生成されます。

f:id:nn_hokuson:20180617131801p:plain:w380

サーバーでオブジェクトを動かすと、各クライアントでも自動的に移動します。

f:id:nn_hokuson:20180617131852p:plain:w360

超簡単ですね(笑)

クライアント側でオブジェクトを動かす

上のように動きが決まった敵などはサーバー上で動かせばよいですが、プレイヤなどユーザーが操作するものをこの仕組で動かすのは基本的にNGです。

クライアントからの入力をうけてサーバー経由で動かそうとすると次のようになります。

f:id:nn_hokuson:20180617131941p:plain:w500

ただ、この仕組でプレイヤを動かすと、ボタン入力してからプレイヤが動くまでに、サーバーに行って来ての通信ラグ(②〜④)が発生してしまいます。これではゲームになりません。

そこで、プレイヤだけは特別扱いしてクライアント側で動かせる「Local Player Authority」というチェックボックスがあります。Unetを使ってプレイヤを動かす場合には、基本的にこの方法を使います。

f:id:nn_hokuson:20180617132138p:plain:w500

Unetの基本スタイル

さて、まとめると・・・

  1. 基本的にネットワークで同期したいオブジェクトはサーバーで生成する。また、移動するのもサーバー側で行う。
  2. ただしプレイヤだけは、通信のラグを考えてクライアント側で動かす

この2つをおさえておけば、今後Unetを使ってネットワークゲームを作る場合に役立つこと間違いなし!です。たぶん(笑)

今回作るUnet対応インベーダーゲーム

今回作るインベーダーゲームのイメージは次のような感じになります。

敵が左右に移動しているのば通常のインベーダーゲームと同じですが、今回作るゲームではプレイヤの機体が2台表示されています。

f:id:nn_hokuson:20190303132538p:plain:w400

各プレイヤが1台1台の機体を操って、インベーダーを破壊します。機体はネットワークで同期されているので、一人が機体を動かすと、相手の画面でも同じ動きをします。機体を動かすにはAキーとSキー、弾を打つにはスペースキーを押します。

だいたい、今回作るインベーダーゲームのイメージは掴めたでしょうか。ではプログラムを作っていきましょう。

プロジェクトの作成

では、今回はUnetでインベーダーゲームを作る下準備だけしておきましょう。ここではUnity2018.1.0f3を使って紹介しますが、Unity2017以前のものでも、ほぼ同じ方法で動作できると思います。

まずはUnityを起動し、プロジェクト名をInvader、Templateを2Dにしておきます。Unetは2Dでも3Dでも問題なく動作します。

f:id:nn_hokuson:20180618151600p:plain:w500

つづいて必要なアセットをプロジェクトビューにドラッグ&ドロップしてください。今回使用するアセットは↓からダウンロード可能です。

https://app.box.com/s/2v7zmz7hdttiyaxe4t9yi4af7xsi4z6m

このような状態になればOKです。

f:id:nn_hokuson:20190303133459j:plain

画面の背景を設定しておきます。プロジェクトウィンドウからbackgroundをシーンビューにドラッグしてください。再背面に表示するため、インスペクタから「Order in Layer」を「-1」に設定します。

f:id:nn_hokuson:20190303133842j:plain

今回はここまでにしておきましょう。次回はネットワーク同期したプレイヤを動かすところまでを作っていきたいと思います。