PUN Classic (also called PUN1) is the original and first major version of PUN. It is now replaced by PUN2 which is refactored and enhanced. We highly recommend starting new projects with PUN2 and if possible migrating existing ones from PUN1 to PUN2 by following our "Migration Notes". PUN Classic will be maintained for the coming months. We will fix important bugs and support new Unity versions but new features will be added only to PUN2.

インスタンス化

どのようなゲームでも、プレイヤーごとに1つ以上のプレイヤーオブジェクトをインスタンス化する必要があります。 ネットワーク化されたゲームでオブジェクトを同期するには、特別なワークフローを使用します。

PhotonNetwork.Instantiate

PUN ではPhotonNetwork.Instantiateメソッドに開始位置と回転およびプレハブの名前を渡すことで、ネットワークオブジェクトの生成を自動的に管理することができます。 要件:プレハブは「Resources」フォルダの直下にあり (実行時に読み込むため)、PhotonViewコンポーネントを持つ必要があります。

ウェブプレイヤーに注意してください:デフォルトではResourcesフォルダのすべてのものが最初のシーンでストリーミングされます。 ウェブプレイヤーの設定で、「First streamed level」を用いることで、Resourcesフォルダのどのアセットを最初のレベルで使用するかを指定することができます。 最初のゲームシーンでこれを設定し、Resourcesフォルダのアセットを使わないようにすればプリローダーとメインメニューの速度は低下しません。

void SpawnMyPlayerEverywhere()
{
    PhotonNetwork.Instantiate("MyPrefabName", new Vector3(0, 0, 0), Quaternion.identity, 0);
    //The last argument is an optional group number, feel free to ignore it for now.
}

新しいゲームオブジェクトのインスタンス化に設定が必要な場合、OnPhotonInstantiate(PhotonMessageInfo info)をスクリプト内に実装できます。 このスクリプトは、インスタンス化をトリガーした情報(PhotonMessageInfo info)にコールされます。GameObjectをプレイヤーのタグオブジェクトとして設定することが可能です。 以下に例を示します:

void OnPhotonInstantiate(PhotonMessageInfo info)
{
    // e.g. store this gameobject as this player's charater in PhotonPlayer.TagObject
    info.sender.TagObject = this.GameObject;
}

Back To Top

ネットワークオブジェクトのライフタイム

PhotonNetwork.Instantiateで作成されるゲームオブジェクトは通常、同じルーム内にいる限り存在し続けます。 Unityのシーンを切り替えるときと同様に、ルームを替えてもオブジェクトが移動することはありません。

クライアントがルームを退出するとき、そのプレイヤーが所有または作成したオブジェクトはすべて破壊されます。 この処理がゲームロジックに合致しない場合、このステップは省略してください。その場合、PhotonNetwork.autoCleanUpPlayerObjectsをfalseに設定して下さい。

または、マスタークライアントは PhotonNetwork.InstantiateSceneObject()を使用して、ルームと同じライフタイムを持つGameObjectを作成することができます。 このオブジェクトはマスタークライアントではなく、ルームにひもづいています。 デフォルトではマスタークライアントがこれらのオブジェクトを管理しますが、photonView.TransferOwnership()を用いてその管理を渡すことができます。

オーナーシップの移行については、デモを参照してください。

Back To Top

ネットワーク化されたシーンオブジェクト

シーン内のオブジェクトにPhotonViewsを設定できます。 PhotonViewsはデフォルトではマスタークライアントによって管理されますが、ルームに関連するRPCを送信するための「ニュートラル」なオブジェクトを作ると便利かもしれません。

重要:ルームに入る前に、ネットワーク化されたオブジェクトを持つシーンを読み込む場合、まだ使用できないPhotonView値がいくつかあります。 たとえば、ルーム内にいないとAwake()でisMineを確認することはできません!

Back To Top

シーンの切り替え

シーンを読み込むとUnityは通常、階層内のすべてのGameObjectsを破壊します。 ネットワークオブジェクトも含まれるため、混乱を招く場合があります。

例:メニューシーンでは、ルームに参加して別のシーンを読み込みます。 ルームに早く到着してしまい、ルームの初期メッセージを取得するかもしれません。 PUNはネットワークオブジェクトのインスタンス化を始めますが、あなたのロジックは別のシーンを読み込むため、インスタンス化されたオブジェクトは失われてしまいます。

シーンの読み込みに関する問題を避けるにはPhotonNetwork.automaticallySyncSceneをtrueに設定し、PhotonNetwork.LoadLevel()を用いてシーンを切り替えることができます。

RPCのタイミングと読み込みレベルを参照してください。

Back To Top

PrefabPoolを使用

多くのオブジェクトを高い頻度で再利用する場合には、メモリ割り当てと開放の繰り返しを避けるためプレハブプールを使用して、ゲームパフォーマンスへの悪影響を防止できます。

PUNのプレハブプールを使用するには、 IPunPrefabPoolインターフェースを実装する必要があります。インターフェースにはオブジェクトをプールに追加する機能と、プールから削除する機能があります。これらの機能は GameObject Instantiate(文字列prefabId、Vector3位置、Quaternion回転);およびvoid Destroy(GameObject gameObject);です。プール自体にはすべての種類の(動的)データ構造が可能です。以下の例ではキューを使用していますが、リストやHashsetの使用も可能です。GameObjectにはパブリックリファレンスも追加されており、後にインスペクターにも設定できます。プールと名づけられたクラスは、以下のとおりです。

public class Pool : MonoBehaviour, IPunPrefabPool
{
    private Queue<GameObject> pool;

    public GameObject Prefab;

    public new GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation)
    {
    }

    public void Destroy(GameObject gameObject)
    {
    }
}

プール化システムを正常に動作させるため、UnityのAwake機能を追加しています。この機能によって、キューがインスタンス化され、PUNに通知が送信されます。

public void Awake()
{
    pool = new Queue<GameObject>();

    PhotonNetwork.PrefabPool = this;
}

PhotonNetwork.InstantiateまたはPhotonNetwork.Destroyを使用する場合は常に、これらのコールは以前に実装したプール、特にInstantiateDestroy機能を使用します。、現時点では、これらの機能はなにもおこなっていません。このため、これらの機能の挙動を調整します。

Instantiateが呼ばれると、発生しうる状況として2つが考えられます。再び使用できるプールに少なくとも1つのオブジェクトがある場合と、新たなオブジェクトをインスタンス化する場合です。これを把握したうえで、この機能を以下のようにアップデートします。

public new GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation)
{
    if (pool.Count > 0)
    {
        GameObject go = pool.Dequeue();
        go.transform.position = position;
        go.transform.rotation = rotation;
        go.SetActive(true);

        return go;
    }

    return Instantiate(Prefab, position, rotation);
}

この例ではキューを使用しているため、オブジェクトを取得してプールからそのオブジェクトを削除するには1つのDequeue()コールを使用します。リストを使用する場合には、同様の挙動を実装する必要があります。キューからオブジェクトを取得したら、変換データを設定して有効化します。プールが空の場合、新たなオブジェクトをInstantiateします。

Destroyが呼ばれると、オブジェクトを非有効化してプールに返す必要があります。この機能もアップデートが必要です。

public void Destroy(GameObject gameObject)
{
    gameObject.SetActive(false);

    pool.Enqueue(gameObject);
}

注:パフォーマンスの観点からいって、GameObjectの非有効化は重要です。オブジェクトが不要な場合には、この処理をおこなってスクリプトの実行やオブジェクトのレンダリング、衝突の確認などを回避します。

シーン内のオブジェクトにこのスクリプトを必ず添付してください。

Back To Top

手動でインスタンス化

ネットワーク上でのオブジェクトのインスタンス化でResourcesフォルダに依存したくない場合には、 このセクションの末尾の例にあるように、 手動でオブジェクトをインスタンス化します。

手動でインスタンス化する主な理由は、 ウェブプレイヤーのストリーミング時にダウンロードされるものを管理するためです。 ストリーミングとUnityのResourcesフォルダに関する詳細は、こちらを 参照してください。

インスタンス化するオブジェクトにRPCを送ることができます。 もちろん、どのオブジェクトをインスタンス化するかをリモートクライアントに伝える方法が必要です。 単にGameObjectへの参照を送信することはできません。そのため、名前やそれに類するものを提供する必要があります。

オブジェクトの型と同様に重要なのが、オブジェクトのネットワークIDです。 PhotonView.viewIDはネットワークメッセージを正しいゲームオブジェクトやスクリプトに伝達するキーとなります。 手動で生成する場合、PhotonNetwork.AllocateViewID()を使用してviewIDを割り当て、送信する必要があります。 ルームにいる全員が、新規オブジェクトに対して同じIDを持つ必要があります。

インスタンス化のためのRPCはバッファリングする必要があります。 後から接続するクライアントも、生成指示を受信しなければなりません。


void SpawnPlayerEverywhere()
{
    // You must be in a Room already

    // Manually allocate PhotonViewID
    int id1 = PhotonNetwork.AllocateViewID();

    PhotonView photonView = this.GetComponent<PhotonView>();
    photonView.RPC("SpawnOnNetwork", PhotonTargets.AllBuffered, transform.position, transform.rotation, id1);
}

public Transform playerPrefab; //set this in the inspector

[RPC]
void SpawnOnNetwork(Vector3 pos, Quaternion rot, int id1)
{
    Transform newPlayer = Instantiate(playerPrefab, pos, rot) as Transform;

    // Set player's PhotonView
    PhotonView[] nViews = newPlayer.GetComponentsInChildren<PhotonView>();
    nViews[0].viewID = id1;
}

アセットバンドルを使ってネットワークオブジェクトを読み込む場合、アセットバンドルの読み込みコードを追加して、サンプルの「playerPrefab」を自分のアセットバンドルのものと置き換えます。

To Document Top