Tower Defence VRメモ

三田祭に向けてゲームを作成するうえで気になったことをメモする。
ちなみにこの説明の中には今回のみのオリジナル仕様もあるのでそこには☆を付けておくことにする

#Photon

## Photonでゲームオブジェクトを通信させるには

### 通信するゲームオブジェクト全てに必要なこと 
まず、アタッチするゲームオブジェクトは必ず**「Resources」**フォルダにあるものを使用しなければならない。
![2017-09-05_23h19_21.png](https://qiita-image-store.s3.amazonaws.com/0/189852/3245b7c7-8fc4-8483-9a40-b463b9576925.png)

さらに、通信するゲームオブジェクトには**「Photon View」**コンポーネントをアタッチする必要がある。このコンポーネントの**「Observed Component」**のリストに通信するコンポーネントを登録する。
Transformを通信する際には**「Photon Transform View」**コンポーネントを利用する。
![2017-09-05_23h19_40.png](https://qiita-image-store.s3.amazonaws.com/0/189852/8948ece7-e989-bfa3-7bbe-24da16efe654.png)

### ゲーム開始時に作成するゲームオブジェクトに必要なこと☆
以下のように**「PhotonManager」**をシーン上に置き、**「Spawn Target」**にアタッチする。
![2017-09-05_23h19_06.png](https://qiita-image-store.s3.amazonaws.com/0/189852/91d11504-2eec-cf3c-4c97-20722aa2cf36.png)

ちなみにPhotonManagerの実装は以下の通り

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PhotonManager : MonoBehaviour
{
    [SerializeField] private GameObject[] _spawnTarget;

    private void Start()
    {
        //Photonサーバとの接続。引数で指定したバージョンごとに部屋を分断
        PhotonNetwork.ConnectUsingSettings("1.0");
    }

    //ロビーに入室したときに呼ばれる(ロビー=通信待機中)
    private void OnJoinedLobby()
    {
        //ランダムで部屋に入室
        PhotonNetwork.JoinRandomRoom();
    }

    //部屋に入り損ねたときに呼ばれる(空き部屋がないor部屋が作られていない)
    private void OnPhotonRandomJoinFailed()
    {
        //部屋の作成。引数で部屋名を付けることもできる
        PhotonNetwork.CreateRoom(null);
    }

    //部屋に入ったときに呼ばれる
    private void OnJoinedRoom()
    {
        print("joined a Room");

        foreach(var target in _spawnTarget)
        {
　　　　　　 //登録したゲームオブジェクトを生成する
            PhotonNetwork.Instantiate(
                target.name,
                target.transform.position,
                target.transform.rotation,
                0
            );
        }
    }
}
```

#### PhotonNetwork.Instantiate()メソッド
同期したいオブジェクトをInstantiateするときに使用。通常のInstantiateと異なり、第1引数には生成したいオブジェクトの名前をstring型指定する。第4引数にはPhotonViewのグループの番号がint型で入るが今回は関係ない。僕自身もよく理解していない。

# Unity Collaborate
http://qiita.com/warapuri/items/bfb2a7cdbbcebaa172cc

# 実装内容
## VR
- 攻撃するアルゴリズム
　　→左手を掲げるとチャージが始まって指を指した方向に飛んでいく
 
- 攻撃のエフェクト
　　→パーティクル、本体は球

- しもべたちを配置するアルゴリズム
　　→しもべをつかんで投げる

- 移動
　　→右指をさした方向に（ボタン押し込みながら）移動
　　　コントローラーの軸から読み取る

- HPのオーラ

- 日の傾き
　　→Dilectional Lightの角度を変更、PointLightをキャラクターに付けることで見づらさを解消

- 日の傾きから強さの変動
　　→日の傾きを管理するクラスから読み取る

## 非VR
- ユニットを選択するボタン

- 選択しているユニットを召喚
　　→ステージをクリックした位置に召喚（置ける場所に制限あり）
　　　配置と同時に資金を消費

- ユニットの実装
　　→ミニオン
　　　　一定間隔で自動的に出てきて、魔王に向かっていく
　　　設置型のユニット
　　　　トラップまたはカタパルト的な
　　　移動型ユニット
　　　　魔王軍に近づいて行って射程距離内に入ると自動攻撃

- 資金回復
　　→時間による自動回復（最大値からの割合で回復）
　　　廃品回収による回復

- 資金強化

- ユニット配置可能範囲
　　→魔王軍が通った部分は配置できなくなる

# Oculus
## 導入
https://developer.oculus.com/downloads/unity/
からダウンロード 

 - Oculus Utilities for Unity
 　　→コア部分。絶対必要

 - Oculus Avator SDK
 　　→手の表示

 - Oculus Platform
 　　→Oculus Avator SDKで使用されているため必要

# UNET
http://edom18.hateblo.jp/entry/2017/05/30/120811

## Spawn
Instantiate
↓
サーバ上でSpawn
↓
ネットワーク間で共通のオブジェクト認識用IDが生成したゲームオブジェクトのNetworkIdentityに付与される
↓
以降オブジェクトの識別はこのIDを通して行う
例）

```
// サーバ側
GameObject target = NetworkServer.FindLocalObject(netId);
クライアント側で処理を行う場合は以下のようにします。

// クライアント側
GameObject target = ClientScene.FindLocalObject(netId);
```

ただし、このSpawnを行うためにはあらかじめ、生成するPrefabをNetworkManagerに登録する必要がある。

### クライアントに権限を持たせる
オブジェクトを生成し、そのオブジェクトに特定のクライアントの権限を割り当てることができます。NetworkServer.SpawnWithClientAuthority で行う。
クライアント権限を持つオブジェクトの生成は、オブジェクトの NetworkIdentity に LocalPlayerAuthority を持つ必要があります。

```
[Command]
void CmdSpawn()
{
    var go = (GameObject)Instantiate(
       otherPrefab, 
       transform.position + new Vector3(0,1,0), 
       Quaternion.identity);
       
    NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
}
```

注意

```
	[SerializeField] private GameObject _gameObject;

	void Update () {
		if (Input.GetButtonDown("Fire1"))//この実装は不可能。サーバーがInstantiateしないとネットワーク全体には召喚されないっぽい
		{
			var go = Instantiate(_gameObject, Random.insideUnitSphere * 10, Random.rotation);
			CmdSpawn(go);
		}

		if (Input.GetKeyDown(KeyCode.A))
		{
			CmdSummon();
		}
	}

	[Command]
	private void CmdSpawn(GameObject go)
	{
		if (go) NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
	}

	[Command]
	private void CmdSummon()
	{
		var go = Instantiate(_gameObject, Random.insideUnitSphere * 10, Random.rotation);
		NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
	}
```

## プレイヤーに対してのオブジェクトの表示
### NetworkProximityChecker
プレイヤーに対するオブジェクト表示を制限する最も簡潔な方法として、NetworkProximityCheckerコンポーネントのアタッチがある。このコンポーネントはプレイヤーに接近したオブジェクトのみを表示する。”Vis Range”より遠いオブジェクトは表示されなくなる。各プレイヤーに対する表示非表示の変化は”Vis Update Interval”で設定された時間ごとに行われる。このコンポーネントはコライダーと併用する必要がある。