VRC Worldで～したいときの方法

徐々に書き足します。

# worldを～したい
## worldを作成したい
Creator Companionをダウンロードして、指示に従うと、床だけあるworldが出来る。
基本的には下のページの「VCCによる環境構築(Unity)」を行えばOK(バージョン違いかちょっと画面違うところもあるけど)

https://note.com/rotemtable/n/nffb3b5596827

1. VCC起動
2. 右上の「Create New Project」をクリック
3. 左上の「Unity 2022 World Project」をクリック & Project名・保存場所を入力して、「Create Project」をクリック
4. "Manage Packages"と書かれた画面になるので、必要に応じて"Latest Version"列の"↑(数字)"を押して、パッケージを最新版にする。
5. 「Open Poject」を押すと、Unityが起動して、床だけのワールドが表示される。

## worldをアップロードしたい
1. 上のメニューバーの「VRChat SDK」>「Show Control Panel」とクリック
2. (場合によっては聞かれるので)VRChatアカウントのID・PWを入力。2段階認証コードも聞かれるので入力
3. "Builder"タブをクリック
4. ワールド名他必要な情報を入力し、Thumbnailも設定
5. 一番下の"Online Publishing"の"The information ～"横の□にチェックを入れる
6. 「Build and Upload」をクリックして待つ。

* "World Visibility"の「Publish to Community Labs」を押すと公開されるので注意

# オブジェクトを～したい
## オブジェクトを置きたい
"Hierarchy"ウィンドウ内で右クリック > "3D Object" > (置きたいオブジェクトの種類(e.g. Cube(直方体)/Sphere(球体)/Plane(板) etc...) をクリックする

## オブジェクトをグループ化して動かしたい
1. "Hierarchy"ウィンドウ内で右クリック > "Create Empty" >  をクリックする
2. 出来た"GameObject"にグループ化して動かしたいオブジェクトをD&Dする

## オブジェクトを反転させたい
"Inspector"ウィンドウの"Transform"の"Scale"のX,Y,Zの値を1→-1に書き換える

【参考】

https://x.com/akina_plus/status/1370384006403911682

## オブジェクトを整列したい/均等に並べたい
"Inspector"ウィンドウの"Transform"の"Position"に"L(a,b)" (a,bは開始、終了の値)と入れる

【参考】

https://codegenius.org/open/courses/57/sections/680

## オブジェクトをぴったりくっつけたい
移動したいオブジェクトを選択し、"V"キーを押しながら、頂点にマウスを持っていくと□が出るので、くっつけたい頂点にD&Dする

## オブジェクトを拾えるようにしたい
1. "Hierarchy"ウィンドウで当該オブジェクトを選択(Ctrlで複数選択も可能)
2. 右側の"Inspector"ウィンドウの「Add Component」をクリック
3. 入力欄に"VRC Pickup"と入れ、"Search"下部の"VRC Pickup"をクリック
4. 必要に応じて"VRC Pickup"内の下記設定を変更する。
    * Use Text  : 手を近づけたときに、出てくるテキストの設定
    * Proximity : どれくらい手を近づけると、拾えるようにするか
    * Pickupable: 拾えるようにするか否か。
    * Disallow Theft : 既に誰かが拾っているのを奪えるようにするか否か。
5. 上記を実施しても拾えない場合は"Colider"の追加を行う。
    1. 右側の"Inspector"ウィンドウの「Add Component」をクリック
    2. 入力欄に"Box Colider"と入れ、"Search"下部の"Box Colider"をクリック
    3. 必要に応じて、"Center"や"Size"を調整する。

## オブジェクトが落ちるようにしたい(重力を効かせたい)
1. (右側の"Inspector"ウィンドウ内に"Rigidbody"がなければ)"Rigidbody"の追加
    "オブジェクトを拾えるようにしたい"の"VRC Pickup"を"Rigidbody"と読み替えて作業
2. "Rigidbody"内の"Use Gravity"にチェックを入れる
3. 必要に応じて"VRC Pickup"内の下記設定を変更する。
    * Mass  : 物体の質量

【参考】

https://ekulabo.com/check-rigidbody

## オブジェクトの物理演算を無効/有効にしたい
1. (右側の"Inspector"ウィンドウ内に"Rigidbody"がなければ)"Rigidbody"の追加
    "オブジェクトを拾えるようにしたい"の"VRC Pickup"を"Rigidbody"と読み替えて作業
2. "Rigidbody"内の"Is Kinematic"を物理演算無効にしたいならチェック有 / 有効にしたいならチェック無にする
3. 必要に応じて"VRC Pickup"内の下記設定を変更する。
    * Drag        : オブジェクトにかかる抵抗
    * AngularDrag : オブジェクトの回転にかかる抵抗

* なので机とか床に固定しておきたいものはIs Kinematicのチェックを入れる。
* Is Kinematicチェック有 & PickUp有を入れてると、オブジェクトを離したときにその場に留まる
* "Use Gravity"にチェック有でも、"Is Kinematic"にチェック有だと、落ちなくなる
* オブジェクトを離した後、空中でほわーんと動かしたい場合に"Is Kinematic"のチェック無 & "Use Gravity"は無 & Drag・AngularDragを動かしたい時間の逆数で入力(0にすると動き続ける)

【参考】

https://ekulabo.com/rigidbody-angular-drag

https://ekulabo.com/rigidbody-is-kinematic

## オブジェクト同士がぶつかるようにしたい/すり抜けるようにしたい
"colider"を追加する。


https://qiita.com/iketomo/items/89417ecd6960d10cf760

## オブジェクトとアバターがぶつかるようにしたい

## オブジェクトに色を付けたい
1. マテリアルの作成
    1. メニューバーの "Assets" > "Create" > "Material" の順にクリック
    2. "New Material"が出来るので、必要に応じて名前を変更する。
    　 (あとから変えたくなったら、右クリックして"Rename")
    3. 右側の"Inspector"ウィンドウ内の"Albedo"の白色をクリックする
    4. "Color"ウィンドウが出てくるので、好きな色に調整

    他にもMaterialで[質感変えたりとか発光させたりとかいろいろできる](https://unityshader.hatenablog.com/entry/2017/02/25/130016
)っぽい。  
  

    
2. マテリアルの適用
    1. "Assets"内にある適用したい色のボール(Material)を、"Scene"上の適用したいObjectにドラッグアンドドロップ

【参考】

https://book.mynavi.jp/manatee/detail/id=59718

## オブジェクトに(半)透明にしたい

1. 当該オブジェクトに設定しているマテリアルを選択
2. 右側の"Inspector"ウィンドウ内で"Rendering Mode"を"Fade"にする
3. さらに"Albedo"の"A"の値を調整する

【参考】

https://mono-pro.net/archives/7572

## オブジェクトに画像を表示したい
1. マテリアルの作成
    1. 「オブジェクトに色を付けたい」の1の2までと同じ手順を行う
    2. 画像を下方の"Assets"内にドラッグアンドドロップする。
    3. 右側の"Inspector"ウィンドウ内の"Shader"を"Legacy Shader" > "Diffuse"とクリックして変更する(このShaderじゃなくてもいいっぽい?)
    4. 右側の"Inspector"ウィンドウ内の"None (Texture)"に2.で追加した画像をドラッグアンドドロップする
2. マテリアルの適用
    「オブジェクトに色を付けたい」の２を参照

## Planeの表裏で別の画像を表示したい
Plane：板状のオブジェクト。Shaderを改造する。
1. Shaderを改造する
    1. 下方の"Assets"内で右クリック、"Create" > "Shader" > "Unlit Shader" とクリック。
    2. "NewUnlitShader"が出来るので必要に応じて名前変更する。(今回は"reverseible.shader"とする。)
    (あとから変えたくなったら、右クリックして"Rename")
    3. 初回or設定しないと、「何のソフトで開く?」と聞いてくるので、適切なソフトを選んで「常に使う」をクリック。
    (VS Codeとか、メモ帳とか適当なテキストエディタでOK)
    4. 参考にしたサイトの「続いて作成したreversible.shaderに次のプログラムを入力してください。」以下のコードに書き換え
        ```
        Shader "Unlit/reversible"
        {
            Properties
            {
                _MainTex ("Texture", 2D)    = "white" {}
                _MainTex2("Texture2", 2D) = "white"{}
            }
            (略)
            }
        }
        ```
    5. Ctrl + Sとかで上書き保存して、テキストエディタを閉じる。  
    
2. 改造したShaderを適用する
    1. "Scene"上もしくは"Hierarchy"ウィンドウで、「オブジェクトに色を付けたい」or「オブジェクトに画像を表示したい」で設定したオブジェクトを選択
    1. "Inspector"ウィンドウで"～～(Material)"と書いてある下の"Shader"で、"Unlit" > "reverseible.shader"と選択。
    1. 右側の"Inspector"ウィンドウ内の"None (Texture)"2か所に表/裏にしたい画像をドラッグアンドドロップする

【参考】

https://nn-hokuson.hatenablog.com/entry/2018/02/20/203511%E3%83%9E%E3%83%86%E3%83%AA%E3%82%A2%E3%83%AB%E3%81%A8%E3%82%B7%E3%82%A7%E3%83%BC%E3%83%80%E3%83%BC%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%A3%8F%E9%9D%A2%E3%82%82%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B/post/1865

# UIを～したい
## UIを作成したい
1. "Hierarchy"ウィンドウで右クリック > "UI" > "Canvas" とクリック。"Hierarchy"ウィンドウで"Canvas"が生成される。
2.  "Inspector"ウィンドウの上の方にある"Layer"を"UI"から"Default"に変更
3.  "Inspector"ウィンドウの真ん中ら辺にある"Canvas"内の"Render Mode"を"Screen Space - Overlay"から"World Space"に変更
4. デカすぎてやりずらいので大きさと位置、スケールを修正する。
 "Inspector"ウィンドウの上の方にある"Rect Transform"の"Pos X"を0,"Pos Y"を2,(多分、Canvasの中心位置設定?)、"Width","Height"(多分、Canvasの大きさ設定?)をともに200、"Scale"のX,Y値を0.01くらいにする。
5. UI要素を追加する。"Hierarchy"ウィンドウで"Canvas"を右クリック > "UI" > 追加したいUI要素(e.g. "Button - TextMeshPro" / "Toggle" etc...)
6. ボタンなど操作するものについては"add component"で"VRC UI Shape"を付ける。

【参考】

https://zenn.dev/beeeegle/articles/4e7a67cb1624d0

## UIのボタンで～したい
"～したい"の部分をメソッドで書いておいて、ボタンのOnClickで呼ぶ、
1. 「UIを作成したい」を参考にUIを作成する。"VRC UI Shape"を付けるのを忘れないこと。
2. 「Udon Sharpを使いたい」などを参考にUdonSharpを記述する。
今回はボタンを押すと、数値が増加し、テキストに反映されるようにしてみる。
意味の解説は「UdonSharpで同期したい」を参照。
    ```
    using UdonSharp;
    using UnityEngine;
    using UnityEngine.UI;
    using VRC.SDKBase;
    using TMPro; // TextMeshProを使う場合はこれが必要
    
    [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
    public class UI : UdonSharpBehaviour
    {
        private const string DEFAULT_STATE_NAME = "Init";
        [SerializeField] public TextMeshProUGUI _boolLabel;
        [SerializeField] public TextMeshProUGUI _intLabel; 
    
        [UdonSynced, FieldChangeCallback(nameof(BoolData))] 
        private bool _boolData;
    
        [UdonSynced, FieldChangeCallback(nameof(IntData))] 
        private int _intData;
    
        public bool BoolData
        {
            get => _boolData;
            set
            {
                _boolData = value;
    
                // 実際の処理
                _boolLabel.text = $"bool: {_boolData}";
            }
        }
    
        public int IntData
        {
            get => _intData;
            set
            {
                _intData = value;
    
                // 実際の処理
                _intLabel.text = $"int: {_intData}";
            }
        }
    
        public void ToggleBool()
        {
            // 同期変数変更するためのオーナー譲渡
            SetOwner();
    
            // 値変更
            BoolData = !BoolData;
            RequestSerialization();
        }
    
        public void AddInt()
        {
            // 同期変数変更するためのオーナー譲渡
            SetOwner();
    
            // 加算処理
            IntData++;
            RequestSerialization();
        }
    
        public void DecInt()
        {
            // 同期変数変更するためのオーナー譲渡
            SetOwner();
    
            // 加算処理
            IntData--;
            RequestSerialization();
        }
    
        private void SetOwner()
        {
            Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
        }
    }   
     ```
3. 「Udon Sharpを使いたい」の8～10を参考に作成したUdonSharpをオブジェクトにアタッチする。
4.  `[SerializeField] public TextMeshProUGUI _~~;`と書いている分、オブジェクトを割り当てる。
UdonSharpをアタッチしたオブジェクトのInspectorタブの[#](UdonSharpのファイル名)(Script)内のUtilities内に、書いた分行が出来ているので、どれにどのオブジェクトを割り当てるかをドラッグアンドドロップなどで指定する。
5.  対象のボタンを選択し、"Inspector"ウィンドウの上の方にある"Button"の"On Click"のところで、"Runtime only","UdonBehaviour.SendCustomEvent(string)",(呼びたいメソッドをアタッチしたObject名),(呼びたいメソッド名)と設定する。
メソッド名のところは、「MethodName()」みたいではなく「MethodName」みたいにすること!
複数呼びたいときは"+"ボタンで欄を追加して、同様のことを実施する。

【参考】

https://hatuxes.hatenablog.jp/entry/2021/12/17/023026#%E3%81%9D%E3%81%AE--%E7%B4%A0%E7%9B%B4%E3%81%AB%E5%90%8C%E6%9C%9F%E5%A4%89%E6%95%B0%E3%82%92%E7%94%A8%E3%81%84%E3%82%8B

# ワールド内の一部分を撮って投影したい

1. "RenderTexture" アセットの作成
    1. メニューバーの "Assets" > "Create" > "Render Texture" の順にクリック
    2. 必要に応じてSizeで縦横比の設定などを行う。
2. カメラの設置
    1. "Hierarchy"ウィンドウで右クリック > "Camera" とクリックする。
    2. "Inspector"ウィンドウの"Camera"の真ん中よりちょっと下の"Target Texture"に、作成したRenderTextureを選ぶ。(◎をクリックして選択するなり、下方の"Assets"からD&Dする) 
    3. 映したいものに応じて、"Inspector"ウィンドウの"Camera"内3つ目の"Culling Mask"で"UI","PlayerLocal","UiMenu","InternalUI"などのチェックを外す
    4. 必要に応じて写す位置や方向などを調整する。
        * 位置や方向の調整："Scene"上の矢印を使ったり、"Inspector"ウィンドウの"Transform"の値を変更
        * 画角："Inspector"ウィンドウの"Camera"の"Field of View"。大きいほど広角
        * 写す範囲："Inspector"ウィンドウの"Camera"の"Clipping Planes"。"Near"~"Far"の範囲が撮れる。
3. 投影面の設置 
    1.  "Hierarchy"ウィンドウ内で右クリック > "3D Object" > "Quad" とクリックする。
    2. "Inspector"ウィンドウの"Transform"内の"scale"で投影したい映像の縦横比に合わせて置く 
    3. 作成された"Quad"に作成したRenderTextureをD&Dする。
    4. "Inspector"ウィンドウで"～～(Material)"と書いてある下の"Shader"で、"Unlit" > "Texture"と選択。

【参考】

https://vrcprog.hatenablog.jp/entry/recipie-camera-render-texture

https://x.com/xlwnya/status/1470188020800581636?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1470188020800581636%7Ctwgr%5E623669eb090a4b0c79849a81e400d61252d24dd6%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fembed.zenn.studio%2Ftweetzenn-embedded__ccb05096559f7

# Udon Sharpで～したい
## Udon Sharpを使いたい
1. オブジェクトを選択して、右側の「Inspector」内の「Add Component」ボタンをクリックします。
2. 入力欄が出てくるので「udon」と打って出てきた、「Udon Behavior」をクリックする。「Inspector」内に「Udon Behavior (Script)」というのが追加される。
3. 下の「Project」タブの「Assets」内で右クリックし、Create > U# Script とクリックします。
あらかじめ 右クリック > Create > Folder で、Scriptをまとめて入れて置くフォルダを作成しておくとよい。
4. 「Save UdonSharp File」ウィンドウが開くので、ファイル名欄にスクリプトの名前を入れて、「保存」ボタンを押します。
5. 3.で右クリックした場所に、4.で付けたファイル名でファイルが二つ出来ている。
大きく"#"が書かれている方をダブルクリックして、プログラムのエディタを立ち上げる。
6. コードを書く。下記は基本的な例。
    ```C#
    using UdonSharp;
    using UnityEngine;
    using VRC.SDKBase;
    using VRC.Udon;
    // ↑いろいろ使うためのおまじない。
    // Udon#では文頭に//をつけるとコメント行になります。
    
    public class UdonSharpCode : UdonSharpBehaviour
    //「public class 名前 : UdonSharpBehaviour」で基底クラス? を宣言? これもおまじない。
    // 名前は4.で付けたファイル名と同じにする
    {
    
        private void Update()
        // この下の{　}内に毎フレームごとに呼び出したい処理を書く
        {
        
        }
    }
    ```
7. Ctrl + S などをして保存してエディタを閉じる。
8. 1.で選択していた、オブジェクトを再度選択する。
9. 2.で追加された「Udon Behavior (Script)」の「Program Source」右隣にある「None（Abstract ～」と書かれている所の◎マークをクリックします
10. 「Select AbstractUdonProgramS～」というウィンドウが出てくるので、入力欄に4.で付けたファイル名の一部を入れると、当該ファイルが出てくるのでクリックする。

## Udon Sharpのコードの基本的な書き方を知りたい。
* // を付けたあとはコメント扱いになる
* 命令の区切りに;を入れる必要がある
* if文とかの範囲の区切りは { }

【参考】

https://qiita.com/JunShimura/items/3c2e23bb77cc9085bfda

https://hatuxes.hatenablog.jp/entry/2021/05/24/171942#%E5%90%8C%E6%9C%9F%E5%A4%89%E6%95%B0%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9

https://qiita.com/codez_vrc/items/8347ac06d74fe0de8023

コードの主な流れは下記。
### using ～ で使いたいものを取り込む
たぶんpythonのimportとかCのinclude的なやつ。
```c#
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
```
* (Legacyの)UI使うときは、`using UnityEngine.UI;`も入れる。
* TextMeshProを使うときは、`using TMPro;`も入れる。

### クラスを宣言する
```c#
public class ClassName : UdonSharpBehaviour
{
```
* ClassNameの場所は.csのファイル名と一致させる必要がある

### 変数を宣言する
```c#
    private bool isHoge = false;
    private float fuga;
    public GameObject piyo;
    [SerializeField] public TextMeshProUGUI _intLabel;
    
    [UdonSynced, FieldChangeCallback(nameof(IntData))] 
    private int _intData;
```
* ("属性") "アクセス修飾子" "変数の型" "変数名" (=初期値) という風に行う。
    * 属性：
後からjoinした人にも同様な値を見せたい変数には`[UdonSynced]`とつけたりする。
    * アクセス修飾子：
変数がアクセスできる範囲を定める。
他のクラスからもアクセス可能にするなら`public`、不可にするなら`private`にする。
    * 変数の型：
その変数にどういうものが入るのかを書く。
True/Falseなら`bool`、整数なら`int`、小数含む数なら`float`、何かオブジェクトなら`GameObject`、TextMeshProなUIなら`TextMeshProUGUI`とする。

### メソッドを宣言して、やりたいことを書く。
メソッド：Pythonでいう関数的なやつ。
```c#
    private void Update()
    {
    }

    private override void Interact()
    {
    }
```
* メソッドの宣言は "アクセス修飾子" (override) void "関数名"() と行う。
* クラス外から実行したい関数はアクセス修飾子をpublicにすること。
(e.g. UIのボタンを押したときに呼ばれたい関数)
* メソッド名には特別なものがある。毎フレーム呼ばれる`Update`、インタラクト時に呼ばれる`Interact`、スクリプトをアタッチしたオブジェクトが、初めてActiveになった際に実行される`Start`など。

### クラスを閉じる。
```c#
}
```
## Udon Sharpでオブジェクトを操作したい
1. 「Udon Sharpを使いたい」に従って、コードを作成し、オブジェクトにアタッチする。
2. 「Udon Sharpを使いたい」の5.の手順と同様に大きく"#"が書かれている方をダブルクリックして、プログラムのエディタを立ち上げる。
3. コードを書き換える。一例としてキューブの回転の止める/回すをインタラクトで切り替えるプログラムを記す。
```c#
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class Spinning_Cube : UdonSharpBehaviour
{
    private bool isSpinning = false;
    // isSpinningはtrue or false な変数(bool)だよ。初期値はfalseだよ。

    private void Update()
    // この下の{　}内に毎フレームごとに行いたい処理を書く
    {
       if(isSpinning)
       // isSpinningがTrueのときは下の{ }の中身を実行
        {
            transform.Rotate(Vector3.up, 90f * Time.deltaTime);
            // transform. : 何かモノを動かすよ
            // Rotate(a,b): 回転させるよ。aで回転軸,bでどれくらいを指定するよ
            // Vector3.up : Step3の画像2枚目で示した緑色の矢印 (= 床面に垂直方向)
            // 90f * Time.deltaTime : 単位時間あたりに90度
        }
    }

    private override void Interact()
    // この下の{ }内にインタラクトされたときに行いたい処理を書く
    {
        isSpinning = !isSpinning;
        // isSppiningの値を反転させるよ
    }
}
```
アタッチした以外のオブジェクトを操作したい場合は下記のように書く。
```c#
//あとで埋める
```

## Udon Sharpで同期したい。
下記のようなコードを書く。
```c#
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using TMPro; // TextMeshProを使う場合はこれが必要

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class UI : UdonSharpBehaviour
{
    [SerializeField] public TextMeshProUGUI _boolLabel; // 同期した状態を表示するTextMeshProのオブジェクト
    [SerializeField] public TextMeshProUGUI _intLabel;  // 同期した値を表示するTextMeshProのオブジェクト

    [UdonSynced, FieldChangeCallback(nameof(BoolData))]
    private bool _boolData;
 // よくわからんがBool値を同期するときはこう書く。上行の"BoolData"がクラス名、下行の"_boolData"がローカルで使う変数名っぽい?
 
    [UdonSynced, FieldChangeCallback(nameof(IntData))] 
    private int _intData;
// 整数を同期するときはこう

    public bool BoolData
    {
        get => _boolData;
        set
        {
            _boolData = value;

            // 値のセットがあったときに入れたい処理を↓に書く
            _boolLabel.text = $"bool: {_boolData}"; // 今回はテキストの中身を書き換える
        }
    }

    public int IntData
    {
        get => _intData;
        set
        {
            _intData = value;

            // 実際の処理
            _intLabel.text = $"int: {_intData}";
        }
    }

    public void ToggleBool() // ボタンクリックなどではここを呼ぶ
    {
        // 同期変数変更するためのオーナー譲渡
        SetOwner();

        // 値変更
        BoolData = !BoolData;
        RequestSerialization(); // オーナーからみんなに値を配る
    }

    public void AddInt()
    {
        // 同期変数変更するためのオーナー譲渡
        SetOwner();

        // 加算処理
        IntData++;
        RequestSerialization();
    }

    public void DecInt()
    {
        // 同期変数変更するためのオーナー譲渡
        SetOwner();

        // 加算処理
        IntData--;
        RequestSerialization();
    }

    private void SetOwner()
    {
        Networking.SetOwner(Networking.LocalPlayer, this.gameObject); // 自分にオーナー権限を移す
    }
}   
```

【参考】

https://hatuxes.hatenablog.jp/entry/2021/12/17/023026#%E3%81%9D%E3%81%AE--%E7%B4%A0%E7%9B%B4%E3%81%AB%E5%90%8C%E6%9C%9F%E5%A4%89%E6%95%B0%E3%82%92%E7%94%A8%E3%81%84%E3%82%8B

## Udon Sharpで他のUdon Sharpの値を読みたい/書きたい
UdonBehaviourとgetProgramValue/setProgramValueする
あとで書く