▲前のページへ
次のページへ▼

キャラクタ同期(1) 導入指針と下準備


【目次】
導入指針
Unityちゃんアセットのインポート
地面を作る
プレハブを展開する
プレイヤープレハブにアニメーターコントローラを適用する
プレイヤープレハブにスクリプトを適用する
プレイヤープレハブを保存する
非プレイヤープレハブを保存する
複数体表示の下準備


導入指針

prefork_battle_matching_lite を使って、複数のキャラクタの姿勢同期を行ってみましょう。
イメージとしては以下のようになります。
  
1. キャラクタはSD-Unityちゃんを用い、三人称視点で自由に走れるようにする。
2. キャラクタの頭上にCharaId(プレイヤーのユニークID)を表示する。
3. キャラクタ同士の位置や向きを同期させる。
4. その他の部分は prefork_battle_matching 準拠で実装する。
上記のうち、1.と2.についてはモノビットエンジン特有の部分は全くなく、Unity特有のコーディングです。
厄介ではありますが、先にこちらの方を実装していきましょう。

次のページでは 3. について触れています。既に「チャットで会話するシステム」は出来上がっていますので、これを参考に
『文字列送受信』と同様に『キャラクタ姿勢の送受信』を組むことで実現します。


Unityちゃんアセットのインポート

■ まだ基礎編を実践していない方は
  先に基礎編を一通り行ない、現状のサンプルが正常に動作することを確認してください。

■ SD-Unityちゃんアセットのインポート
  SD-UnityちゃんアセットをUnityプロジェクトにインポートします。
  まずは以下のリンクからアセットをダウンロードしてください。

    SDユニティちゃん 3Dモデルデータ ダウンロード

  ダウンロード(Download ver1.0)をクリックし、SD_unitychan.unitypackage を任意の場所に保存してください。
  保存したファイルは Unity の Projectビューの Assets にドラッグ&ドロップします(下図参照)。
  

  ドロップするとアセットの Importing package のウィンドウが開きますので、Import ボタンを押下します。
   

  インポートが終了すると、以下のように、Assets 直下に UnityChan フォルダが作成されます。
  


地面を作る

■ Plane を作成する
  続けて、地面を作成してみましょう。
  Unity メニューから GameObject > 3D Object > Plane を選択します。
  Inspecter から Scale を 20倍にしておきましょう。
   

■ テクスチャを貼る
  作成した Plane にテクスチャを貼り付けましょう。
  テクスチャはSD-Unityちゃんアセットを利用します。

  Assets/UnityChan/Stage/Textures にある4枚のテクスチャのうち1枚を選択して
  Hierarchyビューにある Plane にドラッグ&ドロップしてください。
  


  選択したテクスチャに応じて地面の色が変化します。
  


プレハブを展開する

■ ベースとなるプレハブを展開する
  今回登場させる複数のキャラクタを表示するための、SD-Unity ちゃんのプレハブを新規作成します。

  まずはベースとなるプレハブを Hierarchy に展開します。
  ProjectビューからAssets/UnityChan/SD_unitychan/Prefabs 内にある SD_unitychan_generic.prefab を
  Hierarchy にドラッグ&ドロップしてください。
  


  すると画面中央にSD-Unityちゃんが表示されます。
  

  ただし、今の状態ではライトが反映されませんので、SD-Unityちゃん専用のライトをシーンに配置します。
  ProjectビューからAssets/UnityChan/Prefabs 内にある Directional light for UnityChan.prefab を
  Hierarchy にドラッグ&ドロップしてください。
  


  するとライトが追加されます。
  


■ 三人称視点のカメラを追加する
  SD-Unityちゃんを三人称視点で表示させるためのカメラを用意します。
  Unity メニュ-から GameObject > Camera を選択し、Hierarchy に新規カメラ Camera を作成します。
  更に作成した Camera を、同じ Hierarchy にある SD_unitychan_generic にドラッグ&ドロップします。
  

  以下のように、SD_unitychan_generic の子になるように設定してください。
  


  更に、Camera のインスペクタを変更します。
  下記のように、キャラクタの原点位置に対し、(0, 1, -2) の位置に来るように設定します。
  

  この時点でUnityの実行ボタンを押し、SD-Unityちゃんの後姿が以下のように表示されることを確認してください。
  確認し終わったら実行ボタンを解除してください。
  


プレイヤープレハブにアニメーターコントローラを適用する

■ アニメーターコントローラを新規作成する
  今回使用するSD-Unityちゃんの仕様に基づいた、アニメーターコントローラを新規作成します。
  Projectビューから Assets/Projects/Prefabs を選択した状態で、
  Unity メニューから Assets > Create > Animator Controller を選択して、新規作成してください。
  

■ アニメーターコントローラを編集する
  作成した New Animator Controller.controller をダブルクリックで開きます。
  すると以下の画面に変化します。
  

  薄黒グリッド枠の任意の場所を右クリックして、CreateState > Empty を選択します。
  すると以下のように New State が作成されます。
  

  作成した New State について、Inspector から Stand に名前を変更します。
  また Motion について、◎ボタンを押し Select Motion ウィンドウを表示させ
  Assets タグから Standing@loop を選択します。
  

  なお Standing@loop は2種類表示されますが、ウィンドウ下部に表示される Animation Clip を参照し
  SD_unitychan_motion_Generic.fbx に関連付けられているものを選択してください。
  

  同様の手順で、Run も作成しておきましょう。
  こちらもSelect Motion の選択時に Running@loop が2種類表示されますが、
  SD_unitychan_motion_Generic.fbx に関連付けられているものを選択してください。
  

  更に、作成した Stand の枠内を右クリックして、Make Transition を選択します。
  すると引き伸ばし可能な矢印(Transition)が出てきますので、Run の枠内を選択します。
  すると、以下のように Stand → Run への矢印が出来ます。
  

  同様の手順で、Run → Stand への矢印も引きます。
  

  これがアニメーションの遷移を示すわけですが、その条件として変数値を用いましょう。
  Parameters の右側の + ボタンを押し、Int を選択します。
  

  変数の名前が付けられますので、animIdとしておきましょう。
  

  Stand → Run の Transitions(矢印)を選択し、Inspector から遷移条件を設定します。
  Atomicのチェックボックスを外し、かつ Conditions の ExitTime を animId に差し替え、
  animId, Equals, 1 となるよう設定します。
  

  同様に Run → Stand への遷移条件についても以下のように設定してください。
  
  これで animId が 0 なら Stand, animId が 1 なら Run になるように設定できました。

■ アニメーターコントローラを適用する
  作成した New Animator Controller.controller を Hierarchy ビュー内の SD_unitchan_generic に適用します。
  Hierarchy ビューから SD_unitchan_generic を選択し、/Assets/Projects/Prefabs フォルダ内の
  New Animator Controller.controller を Inspector 無いの Animator > Controller にドラッグ&ドロップします。
  


  以下のように切り替わります。
  


プレイヤープレハブにスクリプトを適用する

■ スクリプトを新規に作成する
  今回使用するSD-Unityちゃんの仕様に基づいた、スクリプトを新規作成します。
  Projectビューから Assets/Projects/Scripts/sample を選択した状態で、
  Unity メニューから Assets > Create > C# Script を選択して、新規作成してください。
  また作成したスクリプト名は SD_Unitychan_PC にしておきましょう。

  この SD_Unitychan_PC をダブルクリックして開き、以下の中身に置き換えてください。
using UnityEngine;
using System;
using System.Collections;

public class SD_Unitychan_PC : MonoBehaviour
{
    private Animator animator;                  // アニメータコントローラ
    private int animId = 0;                     // 再生中のアニメーションID
    private UInt64 charaId = 0;                 // キャラクタID
    private Vector3 charaIdDisp;                // キャラクタIDの表示位置
    private static Camera mainCamera = null;    // メインカメラ

    // Use this for initialization
    void Start()
    {
        animator = gameObject.GetComponent<Animator>();
        animId = Animator.StringToHash("animId");
    }

    // Update is called once per frame
    void Update()
    {
        // キャラクタID表示位置の算出
        if (mainCamera != null)
        {
            charaIdDisp = mainCamera.WorldToScreenPoint(gameObject.transform.position + new Vector3(0, 1.25f, 0));
        }

        // キャラクタの移動&アニメーション切り替え
        if (Input.GetKey("up"))
        {
            gameObject.transform.position += gameObject.transform.forward * 0.1f;
            animator.SetInteger(animId, 1);
        }
        else
        {
            animator.SetInteger(animId, 0);
        }
        if (Input.GetKey("right"))
        {
            gameObject.transform.Rotate(0, 2.0f, 0);
        }
        if (Input.GetKey("left"))
        {
            gameObject.transform.Rotate(0, -2.0f, 0);
        }
    }

    void OnGUI()
    {
        // キャラクタIDの表示
        if (charaIdDisp != null)
        {
            GUI.Label(new Rect(charaIdDisp.x - 60, Screen.height - charaIdDisp.y - 20, 120, 40), "CharaId:0x" + mln.Utility.ToHex(charaId));
        }
    }

    public void SetCharaID(UInt64 charaId)
    {
        this.charaId = charaId;
    }

    public void SetMainCamera(Camera camera)
    {
        mainCamera = camera;
    }

    public static Camera GetMainCamera()
    {
        return  mainCamera;
    }
}

■ 既存のスクリプトをすべて破棄し、SD_Unitychan_PC のスクリプトを割り当てる。
  スクリプトを作成し終えたら、Hierarchy の SD_unitychan_generic を選択します。
  Inspecter に表示されている以下のスクリプトをすべて、右クリック > Remove Component で破棄します。
    ・ Idle Changer
    ・ Spring Manager
    ・ Random Wind
    ・ Auto Blinkfor SD
    ・ Face Update
  その上で、先ほど作成した SD_Unitychan_PC のスクリプトを SD_unitychan_generic に
  ドラッグ&ドロップして組み込み、以下のように設定します。
  

■ 実行ボタンを押し、動作することを確認する。
  ここでUnityの実行ボタンを押下し、カーソルキーによってキャラクタが移動・回転するのを確認しておきましょう。


プレイヤープレハブを保存する

■ プレイヤーのプレハブとして保存しておく
  ここまで作成した SD_unitychan_generic を一旦プレイヤープレハブとして保存しておきます。
  まず、SD_unitychan_generic を SD_unitychan_PC にリネームしておきましょう。
  

  その上で、Projectビューを Assets/Projects/Prefabs を開いた状態で、SD_unitychan_PC をドラッグ&ドロップします。
  


  SD_unitychan_PC.prefab は以下のように保存されます。
  


非プレイヤープレハブを保存する

■ Hierarchy登録データのリネーム
  操作しないプレイヤー(ネット回線先にいるユーザーの操作プレイヤー)用の「非プレイヤー」のプレハブを同じ手順で作成します。
  すべて最初からやり直しだと厳しいので、既存の hierarchy に登録されている SD_unitychan_PC を使いましょう。

  SD_unitychan_PCのプレハブは既に保存して退避済みですから、SD_unitychan_NPC とリネームしてしまいましょう。
  

■ 非プレイヤー用のスクリプトを作成する
  Projectビューから Assets/Projects/Scripts/sample を選択した状態で、
  Unity メニューから Assets > Create > C# Script を選択して、新規作成してください。
  また作成したスクリプト名は SD_Unitychan_NPC にしておきましょう。

  この SD_Unitychan_NPC をダブルクリックして開き、以下の中身に置き換えてください。
using UnityEngine;
using System;
using System.Collections;

public class SD_Unitychan_NPC : MonoBehaviour
{
    private Animator animator;          // アニメータコントローラ
    private int animId = 0;             // 再生中のアニメーションID
    private UInt64 charaId = 0;         // キャラクタID
    private Vector3 charaIdDisp;        // キャラクタIDの表示位置

    // Use this for initialization
    void Start()
    {
        animator = gameObject.GetComponent<Animator>();
        animId = Animator.StringToHash("animId");
    }

    // Update is called once per frame
    void Update()
    {
        // キャラクタID表示位置の算出
        if (SD_Unitychan_PC.GetMainCamera() != null)
        {
            charaIdDisp = SD_Unitychan_PC.GetMainCamera().WorldToScreenPoint(gameObject.transform.position + new Vector3(0, 1.25f, 0));
        }
    }

    void OnGUI()
    {
        // キャラクタIDの表示
        if (charaIdDisp != null)
        {
            GUI.Label(new Rect(charaIdDisp.x - 60, Screen.height - charaIdDisp.y - 20, 120, 40), "CharaId:0x" + mln.Utility.ToHex(charaId));
        }
    }

    public void SetCharaID(UInt64 charaId)
    {
        this.charaId = charaId;
    }

    public void SetAnimId(Int32 animId)
    {
        if(animator != null )
        {
            animator.SetInteger(this.animId, animId);
        }
    }
}

■ 既存のスクリプトをすべて破棄し、SD_Unitychan_NPC のスクリプトを割り当てる。
  スクリプトを作成し終えたら、Hierarchy の SD_unitychan_NPC を選択します。
  Inspecter に表示されている SD_unitychan_PC を右クリック > Remove Component で破棄します。
  その上で、先ほど作成した SD_Unitychan_NPC のスクリプトを SD_unitychan_NPC に
  ドラッグ&ドロップして組み込み、以下のように設定します。
  

■ カメラを削除する
  もう1つ、Hierarchy の SD_unitychan_NPC に追加しているCamera情報は
  プレイヤーキャラクタとしては三人称視点で必要ですが、他人のプレイヤーキャラクタとしては
  カメラ情報として不要になりますので、これを削除します。

  Hierarchy の SD_unitychan_NPC 以下のような状態にしてください。
  


■ 非プレイヤーのプレハブとして保存しておく
  ここまで作成した SD_unitychan_NPC を一旦非プレイヤープレハブとして保存しておきます。
  Projectビューを Assets/Projects/Prefabs を開いた状態で、SD_unitychan_NPC をドラッグ&ドロップします。
  

  SD_unitychan_NPC.prefab は SD_unitychan_PC.prefab と同階層に保存されます。
  

■ プレハブ生成用に作成したオブジェクトを削除する。
  後片付けとして、PC/NPC 両方の prefab の元となったhierarchyオブジェクトを削除し、最初の画面では地面以外なにも表示させないようにします。
  hierarchy 内の SD_unitychan_NPC を削除してください。
  


複数体表示の下準備

■ ここまでの内容を踏まえて、複数体表示のための組み込みを行なう。
  ここまでの内容を踏まえ、ゲーム中の複数体キャラクタ表示の下準備を行います。
  NPC/PCの両方のprefabを使い、Instantiateで任意のタイミングで出現できるようにします。

  では、Projectビューの Assets/Projects/Scripts/sample の中から ClientScene(ClientScene.cs)を開き、
  フィールド要素として以下の項目を追加してください。
    public GameObject unityChan_PC;     //< プレイヤープレハブ(プレハブ受け取り口)
    public GameObject unityChan_NPC;    //< 非プレイヤープレハブ(プレハブ受け取り口)
    public static GameObject g_unityChan_PC;    //< プレイヤープレハブ(グローバルオブジェクト)
    public static GameObject g_unityChan_NPC;   //< 非プレイヤープレハブ(グローバルオブジェクト)
    public static Dictionary<UInt64, GameObject> g_CharaList = new Dictionary<UInt64, GameObject>();    //< キャラクタリスト
    public static readonly object g_mutex = new object();    //< g_CharaList の排他制御用
  

  さらに Start メソッドで、以下のような手法で(強引ですが)プレハブインスタンスをグローバル変数として使用可能にします。
        // グローバル変数として利用可能に
        g_unityChan_PC = unityChan_PC;
        g_unityChan_NPC = unityChan_NPC;
  


■ unityChan_PC, unityChan_NPC にプレハブを割り当てる。
  上記で追加した GameObject 型の変数 unityChan_PC および unitychan_NPC は hierarchy の Inspector から
  実行前情報として値を設定することが出来ます。
  これを利用して、先ほどまで作成していた2つのプレハブオブジェクトを割り当てます。

  Unity の Hierarchyビューから ClientScene を選択します。
  

  Inspector の ClientScene(Script)の欄に public の変数一覧が出ますので、
  このうち、Unity Chan_PC および Unity Chan_NPC に対し、それぞれ
  Asset/Projects/Prefabs に保存していた SD_unitychan_PC および SD_unitychan_NPC を
  ドラッグ&ドロップで割り当てましょう。
  


※ これによって何が出来るか?
  この組み込みによって、任意のスクリプト内で「プレイヤーキャラ/非プレイヤーキャラ」を出現させたい場合
  そのキャラクタIDが把握できているのであれば
    lock( ClientScene.g_mutex )
    {
        // charaId はいずこかで与えられる。
        ClientScene.g_CharaList[charaId] = MonoBehaviour.Instantiate( ClientScene.g_unityChan_PC ) as GameObject;
    }
    lock( ClientScene.g_mutex )
    {
        // charaId はいずこかで与えられる。
        ClientScene.g_CharaList[charaId] = MonoBehaviour.Instantiate( ClientScene.g_unityChan_NPC ) as GameObject;
    }
  という形でプレハブから生成することができ、かつその情報を ClientScene.g_CharaList で管理することが出来るようになります。

▲前のページへ
次のページへ▼