やぎりのブログ

やぎりのブログです。ゲーム制作、プログラミング、エフェクトのことについて書いていきます。

UdonSharpコード走り書きメモ

概要

UdonSharpで自分が書いたコード集です。走り書きです。

Merlin-san/UdonSharp

UdonSharpを用いることで、ノード無し、C#コードでUdonを書けます。詳しくは以下。 github.com

オブジェクトのアクティブをオンオフ(ローカル)

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// ローカルで対象オブジェクトのアクティブ/非アクティブを切り替える
/// </summary>
public class ToggleBtnLocal : UdonSharpBehaviour
{
    [SerializeField] private GameObject target;
    public override void Interact()
    {
        target.SetActive(!target.activeSelf);
    }
}

オブジェクトを一つづつアクティブ/非アクティブ化(ローカル)

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// ローカルで複数のオブジェクトを順番にアクティブ/非アクティブにする
/// </summary>
public class SwitchObjsLocal : UdonSharpBehaviour
{
    [SerializeField] private GameObject[] objs;

    private int activationIndex = 0;

    private void SetActiveObjs()
    {
        for (int i = 0; i < objs.Length; i++)
        {
            objs[i].SetActive(i == activationIndex);
        }
    }

    private void Start()
    {
        SetActiveObjs();
    }

    public override void Interact()
    {
        activationIndex++;

        if (activationIndex >= objs.Length)
        {
            activationIndex = 0;
        }

        SetActiveObjs();
    }
}

UIスライダーの値をUIテキストに反映(ローカル)

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

public class SliderValToText : UdonSharpBehaviour
{
    [SerializeField] private Slider slider;
    [SerializeField] private Text targetText;

    public override void Interact()
    {
        targetText.text = slider.value.ToString();
    }
}

オブジェクトの位置をマテリアルのパラメータに反映

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// オブジェクトの座標を元に、マテリアルのパラメータを設定するサンプル
/// オブジェクト座標は同期されるので、値も同期される。
/// 同じマテリアル全てで共通のパラメータにしたい場合は、SharedMaterialを用いる。(処理もそちらのほうが軽い)
/// </summary>
public class LocalPosToMaterialParam : UdonSharpBehaviour
{
    [SerializeField] private MeshRenderer meshRenderer;
    [SerializeField] private string paramName = "_Color";
    [SerializeField] private float scale = 4.0f;

    private void Update()
    {
        Vector3 p = transform.localPosition * scale;
        meshRenderer.material.SetColor(paramName, new Color(
            Mathf.Clamp(p.x, 0.0f, 1.0f),
            Mathf.Clamp(p.y, 0.0f, 1.0f),
            Mathf.Clamp(p.z, 0.0f, 1.0f)
            ));
    }
}

Playerの動きのパラメーターを設定

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// プレイヤー移動のパラメータをセットする
/// </summary>
public class PlayerMovingParameterSetter : UdonSharpBehaviour
{
    [SerializeField] private float runSpeed = 2.0f;
    [SerializeField] private float walkSpeed = 4.0f;
    [SerializeField] private float strafeSpeed = 2.0f;
    [SerializeField] private float jumpImpulse = 3.0f;
    [SerializeField] private float gravityStrength = 1.0f;

    private void Start()
    {
        VRCPlayerApi localPlayer = Networking.LocalPlayer;

        if(localPlayer == null)
        {
            return;
        }

        localPlayer.SetWalkSpeed(runSpeed);
        localPlayer.SetRunSpeed(walkSpeed);
        localPlayer.SetStrafeSpeed(strafeSpeed);
        localPlayer.SetJumpImpulse(jumpImpulse);
        localPlayer.SetGravityStrength(gravityStrength);
    }
}

TeleportTo 特定の位置にプレイヤー座標を設定

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// 特定の位置にプレイヤー座標を設定する
/// 角度も設定できるが、Y軸以外は回転できない?(詳しくは未検証)
/// </summary>

public class TeleportToTest : UdonSharpBehaviour
{
    [SerializeField] private Transform target;
    public override void Interact()
    {
        var player = Networking.LocalPlayer;
        player.TeleportTo(target.position, target.rotation);
    }
}

SetVelocity プレイヤーの速度を設定

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

// プレイヤーの速度を設定するテスト
// SetVelocityを用いる。

public class VelocitySetterOnInteract : UdonSharpBehaviour
{
    [SerializeField] private Vector3 vel = Vector3.up * 10.0f;
    public override void Interact()
    {
        var player = Networking.LocalPlayer;
        player.SetVelocity(vel);
    }
}

同SetVelocity ピックアップした状態で押すとピックアップオブジェクトの前方に速度を設定

※オブジェクトにVRCPickupをアタッチ、VRC Pickupのパラメーターを設定する必要あり

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class VelocitySetterForward : UdonSharpBehaviour
{
    [SerializeField] private float speed = 10.0f;
    private Transform _transform;

    private void Start()
    {
        _transform = GetComponent<Transform>();
    }

    public override void OnPickupUseDown()
    {
        var player = Networking.LocalPlayer;
        player.SetVelocity(speed * _transform.forward);
    }
}

Immobilize プレイヤーの座標更新を行うかどうか設定

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

// Immobilize関数を用いて、プレイヤーが動かないようにする

public class ImmobilizeTest : UdonSharpBehaviour
{
    private bool isImmobilize = false;

    public override void Interact()
    {
        var player = Networking.LocalPlayer;
        isImmobilize = !isImmobilize;
        player.Immobilize(isImmobilize);
    }
}

(今は古いやり方)インスタンスにいる各プレイヤーにアクセスするテスト

※今は古いやり方です。現在はPlayer一覧を取得できます。

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// インスタンスにいる各プレイヤーにアクセスするテスト
/// プレイヤー名を表示しているが、名前以外にも色々アクセス可能。SetVelocityや、TeleportToなどなど
/// </summary>
public class AllPlayersTest : UdonSharpBehaviour
{
    [SerializeField] private Text textUI;

    private int[] ids = null;

    private void InitializeIdsIfNull()
    {
        if(ids == null)
        {
            ids = new int[80];
            for(int i = 0; i < ids.Length; i++)
            {
                // プレイヤーIDに-1が入らないと仮定し、-1を空白とする。
                ids[i] = -1;
            }
        }
    }
    private void UpdateText()
    {
        textUI.text = "---Players Name Shower---\r\n";

        // 各プレイヤーに対して処理を行う
        // テストとして、プレイヤーID, プレイヤー名を表示
        for (int i = 0; i < ids.Length; i++)
        {
            if (ids[i] != -1)
            {
                var player = VRCPlayerApi.GetPlayerById(ids[i]);
                textUI.text += string.Format("id:{0}, name:{1} \r\n", player.playerId.ToString(), player.displayName);
            }
        }
    }

    public override void OnPlayerJoined(VRCPlayerApi player)
    {
        InitializeIdsIfNull();

        for (int i = 0; i < ids.Length; i++)
        {
            if(ids[i] == -1)
            {
                ids[i] = player.playerId;
                break;
            }
        }

        UpdateText();
    }

    public override void OnPlayerLeft(VRCPlayerApi player)
    {
        InitializeIdsIfNull();

        for (int i = 0; i < ids.Length; i++)
        {
            if (ids[i] == player.playerId)
            {
                ids[i] = -1;
                break;
            }
        }

        UpdateText();
    }
}

SetPlayerTag

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// タグ(おそらくUnityのタグではない)を設定するテスト
/// 各プレイヤーごとにstringをキー、stringをバリューとした辞書を設定できる
/// 多分同期はされていない(要検証)
/// </summary>

public class TagTest1 : UdonSharpBehaviour
{
    [SerializeField] private Text textUI;
    public override void Interact()
    {
        var localPlayer = Networking.LocalPlayer;
        localPlayer.SetPlayerTag("a0", Random.Range(0, 10).ToString());

        textUI.text = "---TagTest---\r\n";

        // FIXME: 正しいやり方に修正する
        // 各プレイヤーごとに処理
        for (int i = 0; i < 99; i++)
        {
            var player = VRCPlayerApi.GetPlayerById(i);
            if (player == null)
            {
                continue;
            }
            textUI.text += string.Format("name:{0}, (tag:a0).value:{1} \r\n", player.displayName, player.GetPlayerTag("a0"));
        }
    }
}

VRCPlayerApiから取得できるシステム系の値を表示するテスト

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// VRCPlayerApiから取得できるシステム系の値を表示するテスト
/// 他にも座標など取れそうだった
/// </summary>
public class LocalPlayerParam : UdonSharpBehaviour
{
    [SerializeField] private Text textUI;

    public override void Interact()
    {
        var player = Networking.LocalPlayer;
        textUI.text = "---Networking.LocalPlayer Params---\r\n";
        textUI.text += string.Format("displayName: {0} \r\n", player.displayName);
        textUI.text += string.Format("isLocal: {0} \r\n", player.isLocal.ToString());
        textUI.text += string.Format("isMaster: {0} \r\n", player.isMaster.ToString());
        textUI.text += string.Format("playerId: {0} \r\n", player.playerId.ToString());
        textUI.text += string.Format("IsUserInVR: {0} \r\n", player.IsUserInVR().ToString());
        textUI.text += string.Format("IsPlayerGrounded: {0} \r\n", player.IsPlayerGrounded().ToString());
    }
}

プレイヤーのトラッキング情報を取得するテスト

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// プレイヤーのトラッキングデータを取得するテスト
/// 頭、右手、左手それぞれの位置の座標、角度を取得できる
/// </summary>

public class TrackingDataTest : UdonSharpBehaviour
{
    [SerializeField] private Text textUI;

    public override void Interact()
    {
        var player = Networking.LocalPlayer;

        textUI.text = "---TrackingDataTest---\r\n";
        {
            var headData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
            textUI.text += string.Format("Head-Pos: {0}\r\n", headData.position.ToString());
            textUI.text += string.Format("Head-Rot: {0}\r\n", headData.rotation.ToString());
            textUI.text += "\r\n";
        }
        {
            var rightHandData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.RightHand);
            textUI.text += string.Format("RightHand-Pos: {0}\r\n", rightHandData.position.ToString());
            textUI.text += string.Format("RightHand-Rot: {0}\r\n", rightHandData.rotation.ToString());
            textUI.text += "\r\n";
        }
        {
            var leftHandData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.LeftHand);
            textUI.text += string.Format("LeftHand-Pos: {0}\r\n", leftHandData.position.ToString());
            textUI.text += string.Format("LeftHand-Rot: {0}\r\n", leftHandData.rotation.ToString());
            textUI.text += "\r\n";
        }
    }
}

時刻表示

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

public class TimeTest : UdonSharpBehaviour
{
    [SerializeField] private Text textUI;

    private void Update()
    {
        textUI.text = string.Format("{0:hh:mm:ss}", System.DateTime.Now);
    }
}

現実の時間に応じて姿勢(transform.rotation)を更新

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// 現実の時間に応じて姿勢(transform.rotation)を更新する
/// </summary>
public class RotationOnRealTime : UdonSharpBehaviour
{
    // 一日に何周するか
    [SerializeField] private float frequency = 1.0f;

    private Transform _transform;

    private void Start()
    {
        _transform = GetComponent<Transform>();
    }

    private void Update()
    {
        var now = System.DateTime.Now;
        // NOTE: Ticks使ってもいいかも
        float rate = now.Hour / 24.0f + now.Minute / (24.0f * 60.0f) + now.Second / (24.0f * 60.0f * 60.0f) + now.Millisecond / (24.0f * 60.0f * 60.0f * 1000.0f);

        _transform.rotation = Quaternion.Euler(
            360.0f * rate * frequency,
            0.0f,
            0.0f);
    }
}

VRCInstantiate

※正常に動くのか要検証

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class VRCInstantiateTest : UdonSharpBehaviour
{
    [SerializeField] private GameObject prefab;

    private Transform cashedTransform;

    private void Start()
    {
        cashedTransform = GetComponent<Transform>();
    }

    public override void OnPickupUseDown()
    {
        var go = VRCInstantiate(prefab);
        go.GetComponent<Transform>().position = cashedTransform.position;
    }
}

RayCast

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/// <summary>
/// RayCastのテスト
/// レイがヒットした場所にオブジェクトを動かす
/// レイがどのレイヤーのオブジェクトに当たるか、LayerMaskを設定する必要あり
/// </summary>
public class RayCastTest : UdonSharpBehaviour
{
    [SerializeField] private Transform hitShower;
    [SerializeField] private LayerMask layerMask;

    private Transform cashedTransform;

    private void Start()
    {
        cashedTransform = GetComponent<Transform>();
    }

    private void Update()
    {
        RaycastHit hit;
        if (Physics.Raycast(cashedTransform.position, cashedTransform.forward, out hit, Mathf.Infinity, layerMask))
        {
            hitShower.position = hit.point;
        }
    }
}

参考になる記事等

github.com

ビルドなしで、Unity上でVRChatクライアントをエミュレートできます。
再生ボタンを押すとほぼ即実行されます。 効率が上がるので非常におすすめです。

hatuxes.hatenablog.jp

hatuxes.hatenablog.jp

github.com UdonSharp公式Wiki

kurotorimkdocs.gitlab.io

phi16.hatenablog.com

github.com

qiita.com