자주 쓰는 U# 코드들이다
핸들이 손에 잡힌 상태인지
GetComponent<VRC_Pickup>().IsHeld
//주의: GetComponent<VRC_Pickup>().currentHand != VRC_Pickup.PickupHand.None는 오작동하므로 쓰면 안됨
왼손으로 잡혀있는지
GetComponent<VRC_Pickup>().currentHand == VRC_Pickup.PickupHand.Left
변수동기화
[UdonSynced]
int state = 0;
매뉴얼 동기화 예제
이 코드는 종각역 만들때 썼다
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class StageText : UdonSharpBehaviour
{
[UdonSynced]
[Multiline]
public string syncText;
public Text textUI;
public void UpdateText(string text)
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
syncText = text;
RequestSerialization();
}
void Update()
{
textUI.text = syncText;
}
}
텔레포트
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class Teleport : UdonSharpBehaviour
{
public Transform teleportTarget;
public override void Interact()
{
Networking.LocalPlayer.TeleportTo(teleportTarget.position, teleportTarget.rotation);
}
}
스크립트 LOD
기본 LOD도 훌륭하지만 이걸 쓰는 이유는 라이팅이나 스크립트 On Off도 용이하기 때문이다
몰론 그렇다고 이거만 쓰진 말고 기본 LOD와 병행해서 쓰자
https://ahzkwid.booth.pm/items/5892835
https://gist.github.com/ahzkwid/6647d3aec625afe96c54bb38b2671c72
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class ScriptLOD : UdonSharpBehaviour
{
public float radius = 20;
public GameObject[] hqObjects;
public GameObject[] lqObjects;
#if UNITY_EDITOR
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, radius);
}
#endif
void Update()
{
var active = Vector3.Distance(Networking.LocalPlayer.GetPosition(), transform.position) < radius;
foreach (var obj in hqObjects)
{
obj.SetActive(active);
}
foreach (var obj in lqObjects)
{
obj.SetActive(!active);
}
}
}
글로벌 토글 스위치
https://gist.github.com/ahzkwid/4ee36e9fb9678b6c07522f4fbd1b931d
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class GlobalSwitch : UdonSharpBehaviour
{
public GameObject[] targets;
[UdonSynced]
public bool targetsEnabled=false;
public override void Interact()
{
Networking.SetOwner(Networking.LocalPlayer,gameObject);
targetsEnabled = !targetsEnabled;
}
public void Update()
{
foreach (var target in targets)
{
target.SetActive(targetEnabled);
}
}
}
플레이어가 Trigger 콜라이더 내부인지 체크
콜라이더 레이어는 가급적 Default로 해두자
https://gist.github.com/ahzkwid/2c6a020d9c33af2456239baed64726fb
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerInsideCheck : UdonSharpBehaviour
{
public GameObject[] gameObjects;
private void Start()
{
foreach (var gameObject in gameObjects)
{
gameObject.SetActive(false);
}
}
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (player.isLocal)
{
foreach (var gameObject in gameObjects)
{
gameObject.SetActive(true);
}
}
}
public override void OnPlayerTriggerExit(VRCPlayerApi player)
{
if (player.isLocal)
{
foreach (var gameObject in gameObjects)
{
gameObject.SetActive(false);
}
}
}
}
유저출입기록
https://ahzkwid.booth.pm/items/6791634
https://gist.github.com/ahzkwid/4fab060b1d493188b10f545f626ab979
using System;
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
public class UserAccessLog : UdonSharpBehaviour
{
public Text log;
public Scrollbar scrollbar;
public Text userCount;
public Color joinColor=Color.green;
public Color leaveColor = Color.red;
int logCount = 0;
public int logCountMax = 20;
public void Start()
{
log.text = "";
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
if (Time.time<5)
{
if (player.isLocal==false)
{
//return;
}
}
Log(true, player);
UpdateUserCount();
}
public void UpdateUserCount()
{
userCount.text = VRCPlayerApi.GetPlayerCount().ToString();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
Log(false, player);
SendCustomEventDelayedSeconds(nameof(UpdateUserCount), 1f);
}
string ToHtmlStringRGB(Color c)
{
int r = (int)(c.r * 255f);
int g = (int)(c.g * 255f);
int b = (int)(c.b * 255f);
return $"#{r:X2}{g:X2}{b:X2}";
}
public void Log(bool isJoin,VRCPlayerApi player)
{
var color = joinColor;
var joinText = "Join";
if (isJoin==false)
{
color = leaveColor;
joinText = "Left";
}
var time = $"{DateTime.Now.ToString("HH:mm:ss")}";
//var webColor = $"#{ColorUtility.ToHtmlStringRGB(color)}";
var webColor = ToHtmlStringRGB(color);
var text = $"{time} <color={webColor}>{joinText}</color> {player.displayName}";
if (string.IsNullOrWhiteSpace(log.text)==false)
{
text = $"\n{text}";
}
if (logCount>= logCountMax)
{
int index = log.text.IndexOf('\n');
log.text = log.text.Substring(index + 1);
}
log.text += text;
scrollbar.value=0;
logCount++;
}
}
자동사이즈의자
https://ahzkwid.booth.pm/items/6795564
https://gist.github.com/ahzkwid/3b847810eff472ee2198a05b94abcadd
using System.Linq;
using UdonSharp;
using Unity.Mathematics;
using UnityEngine;
using VRC.SDKBase;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class AutoScaleChair : UdonSharpBehaviour
{
public GameObject VRCChair;
float defaultHeight = 1.5f;
Vector3 smallChairOffset = new Vector3(-0.27f, 0.2125f, 0);
float smallChairScale = 0.5f;
//[UdonSynced]
float scale = 1;
float avatarHeightPre = 1.5f;
bool isOwner = false;
// Custom Animation Character (local)
public bool bonePositionCheck=true;
public Transform kneeIntersection;
float sittingTime = 0f;
//HumanBodyBones[] heightBones = new HumanBodyBones[] { HumanBodyBones.Hips, HumanBodyBones.LeftUpperLeg, HumanBodyBones.RightUpperLeg };
//HumanBodyBones[] fowardBones = new HumanBodyBones[] { HumanBodyBones.LeftUpperLeg, HumanBodyBones.RightUpperLeg, HumanBodyBones.LeftLowerLeg, HumanBodyBones.RightLowerLeg };
VRCPlayerApi seatedPlayer = null;
float lastCheckTime = 0;
void Update()
{
/*
if (isOwner)
{
var height = Networking.LocalPlayer.GetAvatarEyeHeightAsMeters();
if (avatarHeightPre != height)
{
scale = GetScale(Networking.LocalPlayer);
avatarHeightPre = height;
//RequestSerialization();
}
}
*/
var time = Time.time;
if (time<lastCheckTime+0.1f)
{
return;
}
lastCheckTime = time;
UpdateScaleChair();
UpdateOffset();
}
void UpdateOffset()
{
if (bonePositionCheck==false)
{
transform.localPosition = Vector3.zero;
}
if (sittingTime + 3f > Time.time)
{
return;
}
if (seatedPlayer == null)
{
return;
}
var player = seatedPlayer;
/*
var positions = System.Array.ConvertAll(heightBones, bone =>
{
var globalPosition = player.GetBonePosition(bone);
var localPosition = Quaternion.Inverse(transform.rotation) * (globalPosition - transform.position);
return localPosition;
});
*/
var positions = new Vector3[3];
for (int i = 0; i < positions.Length; i++)
{
//var bone = heightBones[i];
var bone = HumanBodyBones.Hips;
switch (i)
{
case 1:
bone = HumanBodyBones.LeftUpperLeg;
break;
case 2:
bone = HumanBodyBones.RightUpperLeg;
break;
}
var globalPosition = player.GetBonePosition(bone);
var localPosition = Quaternion.Inverse(transform.rotation) * (globalPosition - transform.position);
positions[i] = localPosition;
}
var kneeIntersectionLocalPosition = Quaternion.Inverse(transform.rotation) * (kneeIntersection.position - transform.position);
//var yMin = positions.Min(position => position.y);
var yMin = float.MaxValue;
for (int i = 0; i < positions.Length; i++)
{
yMin = Mathf.Min(yMin, positions[i].y);
}
var yOffset = yMin - kneeIntersectionLocalPosition.y;
/*
if (Mathf.Abs(yOffset)<0.1f)
{
//snap
yOffset = 0;
}
*/
{
var localPosition = VRCChair.transform.localPosition;
//localPosition.y = Mathf.Lerp(localPosition.y, -yOffset + scale * 0.05f, 0.1f);
localPosition.y = Mathf.Lerp(localPosition.y, -yOffset + 0.1f, 0.1f);
VRCChair.transform.localPosition = localPosition;
}
}
float GetScale(VRCPlayerApi player)
{
//var player = Networking.LocalPlayer;
var height = player.GetAvatarEyeHeightAsMeters();
var scale = height / defaultHeight;
scale = Mathf.Clamp(scale, 0.2f, 5f);
return scale;
}
void UpdateScaleChair(float scale)
{
VRCChair.transform.localScale = Vector3.one * scale;
VRCChair.transform.localPosition = smallChairOffset / smallChairScale * (1f - scale);
}
void UpdateScaleChair()
{
if (seatedPlayer == null)
{
scale = 1;
}
else
{
scale = GetScale(seatedPlayer);
if (seatedPlayer.GetAvatarEyeHeightAsMeters() == 0)
{
VRCChair.GetComponent<VRCStation>().ExitStation(seatedPlayer);
seatedPlayer = null;
scale = 1;
}
}
UpdateScaleChair(scale);
}
public override void Interact()
{
Networking.LocalPlayer.UseAttachedStation();
/*
//Networking.SetOwner(Networking.LocalPlayer, gameObject);
scale = GetScale(Networking.LocalPlayer);
//RequestSerialization();
avatarHeightPre = Networking.LocalPlayer.GetAvatarEyeHeightAsMeters();
isOwner = true;
*/
sittingTime = Time.time;
}
/*
public void UpdateSeatedPlayer()
{
seatedPlayer = null;
}
*/
public override void OnPlayerLeft(VRCPlayerApi player)
{
if (seatedPlayer == player)
{
seatedPlayer = null;
//SendCustomEventDelayedSeconds(nameof(UpdateSeatedPlayer), 1f);
}
}
public override void OnStationEntered(VRCPlayerApi player)
{
seatedPlayer = player;
UpdateScaleChair();
UpdateOffset();
}
public override void OnStationExited(VRCPlayerApi player)
{
/*
if (isOwner)
{
scale = 1;
//RequestSerialization();
}
isOwner = false;
*/
seatedPlayer = null;
}
}
'게임 > VRChat' 카테고리의 다른 글
Booth 선물 방법 (0) | 2023.04.14 |
---|---|
VRChat 캐릭터 압착되는 문제 (0) | 2023.01.01 |
VRChat OSC (0) | 2022.02.19 |