【知識０から】VRChatを別タブで操作できるアプリ自作してみた！【OSC / フォームアプリケーション】

# はじめに

こんにちは。
ジョジョ好きなXRクリエイターの [もふるね](https://twitter.com/mofurune_k) です。

VRChatで使えるOSCの便利なツールって色々あるじゃないですか。
あれ見てたら思ったんです。
してェ...　自作してェ～～～


## 結果

<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">VRChatのキャラ操作できる自作アプリ作ってる。<br>前後左右・ジャンプボタン搭載！ <a href="https://t.co/EcfeVR4nM6">pic.twitter.com/EcfeVR4nM6</a></p>&mdash; もふるね𓇶 (@mofurune_k) <a href="https://twitter.com/mofurune_k/status/1743262701286211959?ref_src=twsrc%5Etfw">January 5, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<font color="Red">ブラボー！ おぉ...ブラボー!！</font>


## 前置き

これを作った私がどれくらいの知識から始めたかと言うと...

OSCって何だ？
そもそもアプリってどうすりゃ作れるん？
Visual Studio、まともに使ったこと無いが？
ライブラリの導入方法なんて知らんが？

まぁ、こんな感じで知識０からのスタートです。

OSCが理解るまではこの記事「[【OSC何も分からん人向け】OSCでVRChatを操作してみよう！](https://qiita.com/mofurune/items/eab78dee10ace5735a9a)」にまとめました。
ので、今回の記事はアプリを作るところから始めます。

## 参考資料

grinpeacemanさんの記事
[【C#開発環境構築】Visual Studio 2019インストールからHello Worldまで](https://qiita.com/grinpeaceman/items/b5a6082f94c9e4891613)
SIOSTechLabさんの記事
[【Visual Studio】C#でWindowsフォームアプリを作成 ～ ボタンクリックでHello Worldを表示してみよう ～](https://tech-lab.sios.jp/archives/29810)
n_kimuraさんの記事
[C#でOSCをする](https://qiita.com/n_kimura/items/32af3d03537744ab26ef)
Visual Basic 中学校
[C# 入門講座 第７回 ビルド](https://www.umayadia.com/CSNyumon/BEGIN7.htm)

# アプリを作る流れ

まずは作りたいアプリの設計を考えましょう。
ウィンドウがあって、そこにボタンが置いてあって、押したらVRChatに信号が送られ、キャラの操作ができる。
そういう物を作ります。

調べてみると、ウィンドウがあるアプリのことを「フォームアプリケーション」と呼ぶようで、Visual Studioで作成できるようです。
PythonやC++、C#で作れるようなので、Unityで用いられるC#で作ることに決めました。

OSCを扱うには[Rug.Osc](https://bitbucket.org/rugcode/rug.osc/wiki/Home)というライブラリを用いると良いようです。


# 環境構築


## .Net SDKインストール 

初めに、[.Net SDK](https://dotnet.microsoft.com/ja-jp/download)をインストールします。
.Netとは、どのOSでも同じようにソフトウェア開発できる便利な環境らしいです。

<img width="500" alt="MyFormApp01.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/6b08796b-fa3d-7f68-9ba8-97e1af24c714.jpeg">

コマンドプロンプトで``dotnet --version``と打って、バージョンが表示されたら成功です。

<img width="500" alt="MyFormApp02.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/c8ae9de5-0504-5075-8c30-69e9b8d1c823.jpeg">


## Visual Studioインストール

[microsoftのページ](https://visualstudio.microsoft.com/ja/downloads/)からインストールします。

<img width="500" alt="MyFormApp03.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/cbad13a3-3e78-1fbd-72bc-1cae715244bd.jpeg">

インストーラーを起動し、インストールするコンポーネントを選択する画面で、「.NET デスクトップ開発」にチェックを付けてインストールします。

すでにインストール済みでVisual Studioを開いている場合は、上部にある[ツール]>[ツールと機能を取得] を選ぶと、Visual Studio インストーラーが開きます。
「.NET デスクトップ開発」にチェックを付け、インストールします。

<img width="500" alt="MyFormApp04.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/9bfee916-0307-2440-6e95-e2ea8dc0361c.jpeg">


## プロジェクト作成

Visual Studioを起動し、「新しいプロジェクトの作成」をクリックします。
「Windowsフォームアプリケーション(.NET Framework)」をクリックし、「次へ」をクリックします。
プロジェクト名を入力し、「作成」をクリックします。

<img width="500" alt="MyFormApp05.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/da2dcc28-e477-2125-1bfb-5b76d581a065.jpeg">


## Rug.Oscライブラリを導入

Visual Studioで[ツール]>[NuGetパッケージマネージャー]>[ソリューションのNuGetパッケージの管理]をクリックします。
検索バーにRug.Oscを入力し、出てきたRug.Oscをクリックし、右の欄で入れたいプロジェクトにチェックを付け、インストールします。

<img width="600" alt="MyFormApp06.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/9f7ea118-a645-0925-df08-5c4531e6e037.jpeg">

「適用」をクリックします。

<img width="400" alt="MyFormApp07.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/e0e98eb6-b357-9f42-b919-31a6dd872421.jpeg">

インストールが完了すると、プロジェクトのフォルダのpackagesの中にRug.Oscフォルダができます。


# フォームアプリケーション作成


## ボタンの作成

Visual Studioで「Form1.cs[デザイン]」をクリックします。
なにやらアプリのウィンドウ的なのが表示されます。
上部にある[表示]>[ツールボックス(X)]をクリックします。
ツールボックスの検索欄で「button」と入力します。
出てきた「Button」をウィンドウの所にドラッグ＆ドロップします。
ボタンは、ウィンドウ上で位置や大きさを変えることができます。

<img width="500" alt="MyFormApp08.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/95009ccd-174e-5bfb-b498-db9b683bcefc.jpeg">

作ったボタンを右クリックして「プロパティ」をクリックします。
ボタンの見た目を変えたい場合、プロパティの表示欄を設定しましょう。
BackColorで背景色、Fontで文字の大きさとフォント、ForeColorで文字の色、Textで文字を変更できます。


## ラベルの作成

ボタンを押したときにウィンドウ上に「押したよ！」と表示させたいのでラベルを設置しましょう。
ツールボックスの検索欄で「label」と入力します。
出てきた「label」をウィンドウの所にドラッグ＆ドロップします。
ラベルは、ウィンドウ上で位置を、プロパティの表示欄を設定することで文字を変更できます。

<img width="500" alt="MyFormApp09.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/bf6b56cb-7d89-b3cc-2b45-7688addb34d9.jpeg">

# プログラミング

ウィンドウ上のボタンをダブルクリックします。
すると、Form1.csタブが開き、コードが書かれた場所に辿り着きます。
今回は、ボタンに「VRChatでジャンプするOSC信号を送る機能」を付けます。


## IPアドレスと送信ポートの設定

コードの1行目付近を見てみましょう。
usingは、必要なライブラリを読み込む処理のことです。
ここにRug.Oscを読み込む処理を追加しましょう。

```cs:Form1.cs
using Rug.Osc;
```

次に、コードの18行目付近を見てみましょう。
「public partial class Form1: Form{}」の括弧の中に、追加したい変数や関数を書きます。
「public Form1(){}」は最初に発動します。

ここでは、OscSenderを定義し、送信先のポートとIPアドレスを設定します。
VRChatに情報を送るポート番号は9000です。
IPアドレスは人によって違うので、コマンドプロンプトで``ipconfig``と打って調べましょう。

<img width="500" alt="oscvrc03.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/a17af02c-4cbe-dcca-6242-4823dd94a3f0.jpeg">

```cs:Form1.cs
private OscSender oscSender;

public Form1()
{
    InitializeComponent();

    // 送信先のIPアドレスを設定　例：192.168.123.456
    IPAddress address = IPAddress.Parse("192.168.123.456");
    // 送信先のポートを設定
    int sendPort = 9000;
    // OSCのセンダーをポート[sendport]で作る
    oscSender = new OscSender(address, sendPort);
    // 接続
    oscSender.Connect();
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    // アプリケーションが終了する前にOSCセンダーを閉じる
    oscSender.Close();
}
```

（追記2：2024/01/09）～～～～～～～～～～～～～～～～～～～～～～～～～
自分のIPアドレスを調べずに設定する方法が見つかりました。
``127.0.0.1``は自分自身を指すIPアドレスです。

```
IPAddress address = IPAddress.Parse("127.0.0.1");
```

そのため、このようにコードを変更しても正しく動作します。
～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～


## VRChatでジャンプするOSC信号

関数button1_Clickはボタンをクリックした時に発動します。
[VRChatドキュメントのOSCのページ](https://docs.vrchat.com/docs/osc-as-input-controller)を見ると、OSC信号で``/input/Jump 1``を送るとジャンプをするようです。
ただし、再度ジャンプするには、一度``/input/Jump 0``を送ってジャンプの情報をリセットする必要があるようです。

```cs:Form1.cs
private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "入力：Jump";
    Console.WriteLine("入力：Jump");

    // OSC送信
    oscSender.Send(new OscMessage("/input/Jump", 1));
    System.Threading.Thread.Sleep(10);
    oscSender.Send(new OscMessage("/input/Jump", 0));
}
```

ここで、画面上部の「緑の三角マーク（開始）」を押してエラーがでないかつ、ボタンを押すとコンソールに「入力：Jump」が出力されてれば成功です。

ちなみに、Form1.cs[デザイン]のウィンドウ上でボタンをダブルクリックすることで「クリックしたときに発動する関数button1_Click」が作られたのですが、別の方法でも作れます。
Visual Studioの左側にあるソリューションエクスプローラーで、Form1.cs > Form1.Designer.csをダブルクリックしてForm1.Designer.csを開きます。
コードの39行目付近を見てみましょう。
button1の詳細を見ることができます。
ここにある``this.button1.Click += new System.EventHandler(this.button1_Click);``が、クリックされたときにイベントを発生させる処理です。
もし「クリックしたときに発動する関数button1_Click」が不要になったなら、この処理を消しましょう。


## VRChatで動作確認

VRChatを起動し、ExplessionメニューからOptions > OSC > Enabledをオンにします。
これでVRChatはOSCを送受信できるようになります。
（３の画像のOSC Debugを押すと、VRChatに送られたOSC情報が見えるようになるので、確認に使うと良いですよ）

<img width="600" alt="oscvrc09.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/50c96ed7-3222-9f9e-2cb5-e20c847689dc.jpeg">

ここで先程作ったアプリを実行するために「緑の三角マーク（開始）」を押します。

<img width="600" alt="MyFormApp10.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/809d10d1-3b78-f59c-3a91-625e8a26d6da.jpeg">

...エラーが出ました。
VRChatを起動してない状態ではエラーが出ず、起動すると出るようです。
ポート9000はVRChatに占領されていて使えないという事でしょうか？
~~（よくわからないので、解決策を知っている方教えてください m(_ _)m ）~~ → 解決策は追記1にて

<details><summary>対処法（情報として残してますが、先に追記1の解決策を読んだほうが良いです）</summary>

ただ、最良の策ではないかもしれませんが、対処法はあります。
[OSC Router](https://github.com/ETCLabs/OSCRouter/blob/master/README.md)を使います。
これは、複数のクライアントでoscを読みたいときに、間にOSC Routerを挟んで他のポートにルーティングさせるのに用いられます。
[OSC Routerのダウンロードページ](https://github.com/ETCLabs/OSCRouter/releases)から OSCRouter_Windows.zip をダウンロードして解凍して起動します。

<img width="600" alt="MyFormApp11.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/50176df8-76fe-8255-5d9f-d5d9c16e120e.jpeg">

左側のLabelは好きな文字を、Portはなんでも良いですが今回は9002にします。
右側のOutgoing IPは自分のIPアドレスを、Portは9000にします。
最後にApplyを押します。

<img width="600" alt="MyFormApp12.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/483e6514-43f1-e5fd-28b1-2f6c123d4305.jpeg">

次にVisual StudioでForm1.csの変数sendPortを9002に変更します。

```cs:Form1.cs
int sendPort = 9002;
```

これで「緑の三角マーク（開始）」を押してみると、エラーがでなくなりました。
自作アプリのボタンを押してみると、VRChatでキャラがジャンプします。
成功です！

</details>

（追記1：2024/01/09）～～～～～～～～～～～～～～～～～～～～～～～～～
「VRChatの起動中にポート9000に情報を送れない問題」の解決策が見つかりました。
``oscSender = new OscSender(address, sendPort);``
としていたところを
``oscSender = new OscSender(address, 0, sendPort);``
に変更すると、``int sendPort = 9000;``のままでもOSC Routerを介さずにVRChatにOSC信号を送ることができます！
これで「緑の三角マーク（開始）」を押し、自作アプリのボタンを押してみて、VRChatでキャラがジャンプしたなら成功です！
～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～～

Visual Studioでプログラムを実行をすると、自動的にビルドが行われています。
出来上がったアプリはbinフォルダに保存されます。
binフォルダ下部にはDebugフォルダとReleaseフォルダがあり、今回はDebugビルドで実行したので、Debugフォルダ内にアプリが出来ています。
.dllファイルが作成したプログラムそのものです。
それを起動するのが.exeファイルです。
dllとexeの２つのファイルがあれば、作成したアプリを起動することができます。
（ちなみに、objフォルダーとbinフォルダーは必要になるたびに自動的に中身が生成されたり書き換えられるフォルダーなので、一時的な存在です。）
なので、dllとexeファイルは別の場所に退避させておくと安心です。


## 応用：VRChatで前後左右に動かす信号

仕組みはジャンプのときと同じですが、クリックした瞬間に1の信号を、離した瞬間に0の信号を送るように変えています。

Form1.cs[デザイン]にボタンを４つ追加し、名前をそれぞれbuttonLeft, buttonRight, buttonUp, buttonDownとします。

Form1.Designer.csにクリックした瞬間と離した瞬間にイベントを発生させる処理を追加します。
Form1.csにVRChatで前後左右に動かすOSC信号を送る処理を追加します。

<details><summary>コード</summary>

以下のコードを4つのボタン分作ります。

```cs:Form1.Designer.cs
this.buttonLeft.MouseDown += new System.Windows.Forms.MouseEventHandler(this.buttonLeft_MouseDown);
this.buttonLeft.MouseUp += new System.Windows.Forms.MouseEventHandler(this.buttonLeft_MouseUp);
```

以下のコードを追加します。

```cs:Form1.cs
private void buttonLeft_MouseDown(object sender, EventArgs e)
{
    label1.Text = "入力：←";
    Console.WriteLine("入力：←");

    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveLeft", 1));
}

private void buttonLeft_MouseUp(object sender, EventArgs e)
{
    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveLeft", 0));
}

private void buttonRight_MouseDown(object sender, EventArgs e)
{
    label1.Text = "入力：→";
    Console.WriteLine("入力：→");

    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveRight", 1));
}

private void buttonRight_MouseUp(object sender, EventArgs e)
{
    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveRight", 0));
}

private void buttonUp_MouseDown(object sender, EventArgs e)
{
    label1.Text = "入力：↑";
    Console.WriteLine("入力：↑");

    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveForward", 1));
}

private void buttonUp_MouseUp(object sender, EventArgs e)
{
    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveForward", 0));
}

private void buttonDown_MouseDown(object sender, EventArgs e)
{
    label1.Text = "入力：↓";
    Console.WriteLine("入力：↓");

    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveBackward", 1));
}

private void buttonDown_MouseUp(object sender, EventArgs e)
{
    // OSC送信
    oscSender.Send(new OscMessage("/input/MoveBackward", 0));
}
```
</details>


# 振り返り

神は１～６日で世界を創造し、７日目に休息をとったらしい。
私は２日で自作アプリを開発できたので、あと５日間は休めちゃうね。

## OSC記事まとめ

OSC解説記事
[【OSC何も分からん人向け】OSCでVRChatを操作してみよう！](https://qiita.com/mofurune/items/eab78dee10ace5735a9a)

OSCアプリを自作した記事1
[【知識０から】VRChatを別タブで操作できるアプリ自作してみた！【OSC / フォームアプリケーション】](https://qiita.com/mofurune/items/451c88c080692fec97c7)

OSCアプリを自作した記事2
[OSCでVRChatのキャラを回転操作するアプリを自作してみた！【OSC / フォームアプリケーション】](https://qiita.com/mofurune/items/f3879545d6972bd28f11)

OSCアプリを自作した記事3
[CPU使用率をOSC通信でVRChatに送って、アバターの色を変化させてみた](https://qiita.com/mofurune/items/a401f7f16454551c8591)

OSCアプリを自作した記事4
[GPU使用率の取得方法【C#】](https://qiita.com/mofurune/items/38daa7bb61babbe46740)