⛏️

UdonSharpメモ ~(最初だけ)はじめての人に向けて書いてみる~

2021/08/17に公開

VRChat上で動作するUdon。
とはいえ根幹はUnityなので、Unityの知識がほぼ使える。
つまり詰まったらUnityの記事を参考にすれば処理の方法がある程度わかるんじゃないかと。

その中でUdonSharpというものが存在し、
UdonをC#の書き方で動かすことができるすごいやつを作ってくれている人がいます。
今回はそのUdonSharp、U#をちょっとずつ書いていきたい(願望)


まず何したらよい

2021/08/17時点でのUnityバージョンはUnity 2019.4.29f1ですが、
最新は VRChat Doc - Currently Supported Unity Version から確認して使用してください。

それぞれダウンロード、Unityプロジェクトにインポートします。

本記事の進め方

各セクションに1問の問題を用意しています。
その問題を解決するように進めていきます。

問0 Udonスクリプトを使う

作業用のフォルダをAssetsフォルダ内作成しましょう。
「USharpScript」とか「Script」とかでいいと思います。

そしてそのフォルダの中に、各問毎にフォルダを作っておくと、
振り返りやすくなると思います。

スクリプトの作り方

作成したいフォルダ上で右クリックし、[Create] > [U# Script]で作成できます。
名前は必ず英数字で決めてください。パソコンは日本語が苦手です。

実行できるところまで

Hierarchy(ヒエラルキー)上で右クリック、[Create Empty]で空のオブジェクト[Game Object]を生成します。

そのオブジェクトを選択し、Inspector(インスペクタ)で[Add Component] > [Udon Behaviour]を選んで追加します。

先程作ったスクリプトファイルのうち、拡張子(名前の最後の方)が[.asset]の方を、
[Program Source]にドロップします。

これでU#が動くようになりました。
各問毎にやっておいたほうが良いです。

問1 ワールド読み込み時Consoleに文字を出力する

どんな事にも始めに行う準備という物があります。
その準備のことを、「初期化」 なんて呼んだりします。

ワールドが読み込まれるということは、
ワールド内のすべてのオブジェクトが仕事をする準備をしなければなりません。
つまり、「すべてのオブジェクトが初期化をする」 んです。
その初期化のタイミングで出力処理を書けば、今回の問題は解けそうです。

初期化のタイミングの処理したい場合、Startメソッドに処理を書く事で実現できます。

メソッド...?(わかってる人は見なくてもok)

■メソッド とは
「関数」 とも呼ばれたりする、処理の塊を定義するものです。

基本文法
<アクセス修飾子[省略可]> <オプション修飾子[省略可]> <戻り値[必須]> <メソッド名>(){
   // 処理...
}
一例
// public: アクセス修飾子
// static: オプション修飾子
// string: 戻り値
// getName: メソッド名
public static string getName(){
   return "名前";
}

// Udon上での基本的な形
public void Toggle(){
   // 処理...
}

■メソッドの役割
「カレーを作る」というメソッドに「カレーのレシピ通りに作る処理」を書く事で、
「カレーを作る」を実行するだけでカレーを作ってくれます。
上記のことをC#の記法で書いてみると...

カレー カレーを作る(){
    var エコバッグ = お店.材料を買う(材料リスト);
    foreach(var 材料 in エコバッグ){
       if(材料.GetType().Equals(typeof(野菜))){
          ((野菜)材料).切る();
       } else {
          //処理...
       }
    }
}

なんかわからない記号出てますが気にせずに、
こういった書き方をするだけで、カレーを作る一連の流れを定義できます。
■メソッドのメリット
「カレーを作る」事を何回も行う場合、その都度「カレーを作る」処理を書く...
のはスマートではありません。

すまーと...
void カレーパーティ(){
   // 1回目のカレー作り...
   var エコバッグ = お店.材料を買う(材料リスト);
   foreach(var 材料 in エコバッグ){
      if(材料.GetType().Equals(typeof(野菜))){
         ((野菜)材料).切る();
      } else {
         //処理...
      }
   }
   // 2回目のカレー作り...
   var エコバッグ = お店.材料を買う(材料リスト);
   foreach(var 材料 in エコバッグ){
      if(材料.GetType().Equals(typeof(野菜))){
         ((野菜)材料).切る();
      } else {
         //処理...
      }
   }
   // 3回目のカレー作り...
   var エコバッグ = お店.材料を買う(材料リスト);
   foreach(var 材料 in エコバッグ){
      if(材料.GetType().Equals(typeof(野菜))){
         ((野菜)材料).切る();
      } else {
         //処理...
      }
   }
}

しかし、メソッドにまとめてしまえば、非常にコードをスッキリさせることができます。

スッキリ!
カレー カレーを作る(){
    var エコバッグ = お店.材料を買う(材料リスト);
    foreach(var 材料 in エコバッグ){
       if(材料.GetType().Equals(typeof(野菜))){
          ((野菜)材料).切る();
       } else {
          //処理...
       }
    }
}
void カレーパーティ(){
     カレーを作る();
     カレーを作る();
     カレーを作る();
}

と、いうのがメソッドのメリットです。
■Unityでは
Unityでは、必要に応じて固定の名前のメソッドを実行してきます。
初期化ではStartメソッド、毎フレームではUpdate,LateUpdateメソッドなど...
なので、タイミングでメソッドの名前が変わるということになります。

Startメソッドに出力処理を書けば良いので、まずはメソッドの形はこうなります。

void Start(){

}

...実はもう生成時に書いてあったりするんですよね。

そして!あとは出力処理があれば完成ですね!
出力処理はDebug.Log("Hello Udon World!")でできます!

あ、実行はエディタ上部の再生ボタン[▶]でできます。

正解

Consoleウィンドウ(無い場合はエディタ上部の[Window] > [General] > [Console])に
下のように書いた文字が表示されていれば成功です!

コード例
Seq1_StartToOutput.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class Seq1_StartToOutput : UdonSharpBehaviour
{
   void Start(){
      Debug.Log("Hello Udon World!"); // ""内は任意の文字でOK
   }
}

問2 Inspectorで決めうち

現状、スクリプトに値を書き込むことでしか任意のデータを入力できません。
つまり同じスクリプトを使った時にすべて同じデータで動く...

これは移動させたいオブジェクトが違ったりする時に、少し困ります。

[A]から[B]を動かしたいし、[C]から[D]を動かしたい...
動かす処理は一緒だから使いまわしたいけどオブジェクト違うし...

そういった時に、エディタ上で使うオブジェクトを決めることができれば非常に楽になります。
今回やることは 「Inspectorでオブジェクトや数値を決めて、スクリプトで使う」 事です。

Inspectorに表示するには、publicな変数を定義すれば表示されます。

変数...?(わかってる人は飛ばしてok)

とも言われたりする、データを入れておく入れ物です。
■変数の定義

基本文法
<アクセス修飾子[省略可]> <> <名前> = <>;

というのはデータの種類のことです。
stringであったら「文字列」、intであったら「整数」...
見たことない型があったらすぐ検索をかけましょう。

一例
// 整数(int)型「num」に10を代入
public int num = 10;

// 文字列(string)型「str」を定義(値が入っていない)
string str;

publicな変数...つまりどこでも使える変数を定義する事でInspectorでも扱えるようになります。

書き方は...

public string name;

これだけ。ゎぉ。
しかし、適当に書けばいいってわけではないです。
public class <名前> : UdonSharpBehaviour {なんて文字見えません?
その下に書かなければなりません。{の後です。

書いた後はこんな感じ
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class Seq2_InspectorSetter : UdonSharpBehaviour
{
   public string name;
   void Start(){
      
   }
}

この状態で一回保存し、Unityを開き、オブジェクトのInspectorを表示すると...

見慣れた枠が出てきました。
とりあえず、自分の名前入れてみましょう。

さぁ、後は出力するだけです。
出力処理はDebug.Log(<文字>)でしたね?

普通に書くとDebug.Log(name)になるんですが、ちょっとこれだとつまらない...
なので、文字列と文字列をくっつける、文字列結合をして出力しましょう!

二つをつなぐ橋... マヨネーズで言うところの卵に当たるものは+です。
"文字列" + 文字列変数とか文字列変数 + "文字列"で繋がります。

ではやってみましょう!

正解

Consoleウィンドウ(無い場合はエディタ上部の[Window] > [General] > [Console])に
下のように書いた文字が表示されていれば成功です!

コード例
Seq2_InspectorSetter.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class Seq2_InspectorSetter : UdonSharpBehaviour
{
   public string name;
   void Start(){
      Debug.Log("My name is " + name);
   }
}

ここから難しくなるヨ

ここまでやれたあなたなら自力でC#の書き方をなんとな~く理解できるはずです。
わからない箇所があったらただひたすらにネットを彷徨って探したり、
知り合いに聞いてみるなどしてみましょう。

ここからは基本部分の説明は減ります。(ツカレチャッタ)

■あわせて読みたい
UdonSharp Wiki


問3 プレイヤーが入った時に音を鳴らす

必要なもの

  • AudioSource

イベントの書き方

public override void <イベント名>(<引数:> <引数名>){
   // 処理...
}

イベント名はここから探す

ここで使うイベントはOnPlayerJoined。引数はVRCPlayerApi。

public override void OnPlayerJoined(VRCPlayerApi player){
   // 処理...
}

そしてもう一つのパーツ、音を鳴らす方法。
音源となるAudioSourceが必要になるため、public変数として用意する。

public AudioSource audio;

そして、設定したサウンドを再生するには、Playメソッドを使う。

正解

プレイヤーが入室した時に設定したサウンドが再生されれば成功。

コード例
Seq3_JoinSound.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class Seq3_JoinSound : UdonSharpBehaviour
{
   public AudioSource audio;
   public override void OnPlayerJoined(VRCPlayerApi player){
      audio.Play();
   }
}

Discussion