CPU使用率をOSC通信でVRChatに送って、アバターの色を変化させてみた

# はじめに

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

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

VRChatのアバターパラメーターをOSCでいじることができるみたい。
OSCアプリ作ったけど、VRゴーグル付けたまま操作はし辛いなぁ。
自分で操作せずとも値が変動するものがあれば・・・
あっ💡
CPU使用率！


# 結果

<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">CPU使用率をOSC通信でVRChatに送って、アバターの色を変化させてみた<br><br>CPUに負荷をかけることで緑色→赤色に変わる <a href="https://t.co/nSUpGQgsV9">pic.twitter.com/nSUpGQgsV9</a></p>&mdash; もふるね𓇶 (@mofurune_k) <a href="https://twitter.com/mofurune_k/status/1746544056275214567?ref_src=twsrc%5Etfw">January 14, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


# CPU使用率を取得する

どうやったら取得できるんだろう？
調べると以下の記事がヒット。
[.NET/CPU使用率を取得する](https://windev.just4fun.biz/?.NET/CPU%E4%BD%BF%E7%94%A8%E7%8E%87%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)
[C# で CPU 使用率を取得する](https://www.delftstack.com/ja/howto/csharp/csharp-get-cpu-usage/#google_vignette)

どうやら、「System.Diagnostics」をインポートするとCPU使用率とかメモリー使用量を扱えるようになるらしい。
まずはその処理を追加します。

```cs
using System.Diagnostics;
```

次に、CPU使用率の記録用のオブジェクトを作成します。

```cs
private PerformanceCounter cpuCounter;
```

そしてそのオブジェクトを初期化して

```cs
cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
```

以上を行うと、CPU使用率を``cpuCounter.NextValue()``で得ることができるようになります。


# VRChatアバターに色変化オブジェクトを追加する

大体は以下の記事通りに作ると良いです。
[【VRChat】アバターの色を段階的に変更する方法（Fx-Float） ](https://signyamo.blog/fx_motion-time/)

簡単にまとめると
- アバター直下に球体オブジェクトを追加
- 新しくマテリアルを作って球体に付与
- 新しくアニメーションを作ってマテリアルの色を0フレーム:緑,100フレーム:赤にする
- アバターパラメーターに「cpuValue」を追加、TypeはFloat
- FXレイヤーでパラメーターに「cpuValue」を追加
- FXレイヤーで新規レイヤーを追加
- 新規レイヤーにアニメーションをドラッグ＆ドロップ
- Write Defaultsのチェックを消し、Motion Timeにチェックを付けてcpuValueを設定


# VRChatのアバターパラメーターをOSCで送信する

以下のコードで送信できます。

```cs
oscSender.Send(new OscMessage("/avatar/parameters/[パラメータ名]", [送りたい数値]));
```

パラメータ名は、UnityでVRChatアバターを作る際に追加したパラメーターと同名にします。
送りたい数値は、Bool,Int,Float型で送れます。

<img width="400" alt="osccpu01.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3518924/73d10539-fe09-082e-762c-a85550ed5a0d.jpeg">


# プログラム

以上のことをコードにまとめます。
並列処理をするために``System.Threading``を読み込んでいます。


```cs:Form1.cs
//パフォーマンスカウンターを扱えるようにする
using System.Diagnostics;
//並列処理をできるようにする
using System.Threading;
```

CPU使用率は0から100の値が取得できます。
今回作ったVRChatのアバターパラメーターはFloatで0から1の値に対応しています。
そのため、CPU使用率を100で割った値をOSCで送信します。

アプリを終了しようとして閉じた際に、cpuPerformanceCountメソッドが処理途中で、UIスレッドの処理を実行しようとして失敗しエラーを吐いていました。
なので、以下のコードを追加してメソッドが終了するまで待機するようにしました。
``Task.WaitAll(Task.Run(() => cpuPerformanceCount()));``

```cs:Form1.cs
//CPU使用率の送信をするか否か
private bool cpuPerformanceSwitch = true;
//CPU使用率の記録用のオブジェクト作成
private PerformanceCounter cpuCounter;

public Form1()
{
    InitializeComponent();

    int sendPort = 9000;
    // OSCのセンダーをポート[sendPort]で作る
    oscSender = new OscSender(sendAddress, 0, sendPort);
    // 接続
    oscSender.Connect();

    // cpuPerformanceCountメソッドを開始
    Task.Run(() => cpuPerformanceCount());
}

//CPU使用率をOSC信号で送るメソッド
private void cpuPerformanceCount()
{
    //CPU使用率の記録用のオブジェクト初期化
    cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
    //0.5秒毎にCPU使用率をVRChatに送信する
    while (cpuPerformanceSwitch == true)
    {
        //CPU使用率を変数に代入
        float cpuValue_f = cpuCounter.NextValue();
        int cpuValue_i = (int)Math.Floor(cpuValue_f);
        float cpuValue_vrc = (float)cpuValue_i / 100f;

        //OSC送信
        oscSender.Send(new OscMessage("/avatar/parameters/cpuValue", cpuValue_vrc));

        // UIスレッドで処理を実行
        this.Invoke((MethodInvoker)delegate
        {
            label2.Text = $"CPU : {cpuValue_i}%";
        });
        Console.WriteLine($"CPU : {cpuValue_i}%, cpuValue : {cpuValue_vrc}");
        Thread.Sleep(500);
    }
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    //CPU使用率の送信を止める
    cpuPerformanceSwitch = false;
    //cpuPerformanceCountメソッドが終了するまで待機
    Task.WaitAll(Task.Run(() => cpuPerformanceCount()));

    //アプリケーションが終了する前にOSCセンダーを閉じる
    oscSender.Close();
}
```


# 色変化できないときの対処法

いろいろVRChatで試しているときに、OSC信号は送れてるのにアバターパラメーターが変動しない現象が起こりました。
調べてみるとこんな記事が
[VRChat : OSC についての知見とメモ](https://note.com/yui0471/n/n0ce6b48ef025)

どうやら、OSCを有効化したままアバターを着替えると、以下のファイルが生成されるようです。
``C:\Users\[PCのユーザ名]\AppData\LocalLow\VRChat\VRChat\OSC\[ユーザーID]\Avatars\[アバターID].json``
そしてこのファイルは今後一切更新されないにも関わらず、VRChatはこのファイルを参照して使用するようです。
私は、このファイルを削除することでこの厄介な現象に対処しました。


# 振り返り

VRChat中にPCが重い時に、周りの人が察してくれるようになるね！

## 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)