Skip to content

Commit

Permalink
Update the readme and add a couple of util files.
Browse files Browse the repository at this point in the history
  • Loading branch information
FairlySadPanda committed Jun 23, 2020
1 parent 7d2f126 commit 4a49418
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 11 deletions.
42 changes: 31 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,55 @@
# UdonStringEvents

A system by which complex events can be sent over the network in Udon for VRChat. Written in U#.

This includes a simple chat system.

An overview of the system can be found here: https://www.youtube.com/watch?v=77Z2nvkVsf4&feature=youtu.be
(Note: the Youtube video is a bit out of date)
(Note: the Youtube video is quite out of date)

# HOW TO USE THIS CODE

Assuming you are using UdonSharp:

1. Import the UdonStringEvents prefab into your world and modify the EventHandler.cs file with new event names.
2. Inside any UdonSharp file, pass in the imported EventReceiver object.
3. In any function where you want to send an event with a payload over the network, call the receiver's SendEvent(string eventName, string payload) method with a good event name.
4. In the EventHandler UdonSharp file, add a new case to the switch within Handle(). Match it to the name of the event you named in step 3.
5. In the case, do whatever you like. The private var EventHandler.newEvent contains a comma-separated string array formed as [eventName, payload, clock], where clock is an incrementing integer that is equal to the total number of events sent. If you send a CSV string as a payload, this will be reflected in the received event.

Take a look at UdonChat for an example of this in use.

# COMPONENTS

1) UdonStringEvents
1. UdonStringEvents

UdonStringEvent hides away event management behind syncing strings, meaning you can avoid having data races between updated synced vars and custom network events. Two different kinds of event receivers exist - the basic one is a shotgun which blasts an event with payload out to all world members, and the PlayerEvent version targets the specific owner of the receiver with the event.
UdonStringEvent hides away event management behind syncing strings, meaning you can avoid having data races between updated synced vars and custom network events.

2) UdonChat
2. UdonChat

An implementation of UdonStringEvent that makes a text chat interface for VRC. The latest prefab is always available on the Releases page.

3) UdonKeyboard
3. UdonKeyboard

A UK English Keyboard built in Udon. Easy to modify for other layouts. Feeds text into an InputField by default, but this is also easy to change.
A UK English Keyboard built in Udon. Easy to modify for other layouts. Feeds text into an InputField by default, but this is also easy to change. It is built into the UdonChat prefab.

# TECHNICAL NOTES

When implementing UdonStringEvents you'll probably want to unpick how UdonChat was made first, as it'll illuminate how to make the systems go. There's no example code for PlayerEventReceivers yet, unfortunately.
When implementing UdonStringEvents you'll probably want to unpick how UdonChat was made first, as it'll illuminate how to make the systems go brrrr.

EventReceivers pass all events to EventHandlers. An EventHandler is an abstract class that can be described as follows:

EventHandler {
string characterName;
string newEvent;
void HandleEvent();
string characterName;
string newEvent;
void HandleEvent();
}

An EventHandler is an UdonBehaviour, meaning this is a clean exit point into UdonGraph or other Udon implementations(!). As in, yes, you can use UdonStringEvents and its friends if you write in UdonGraph, as long as your EventHandler graph implements the above interface.
An EventHandler is an UdonBehaviour, meaning this is a clean exit point into UdonGraph or other Udon implementations. As in, yes, you can use UdonStringEvents and its friends if you write in UdonGraph, as long as your EventHandler graph implements the above interface.

There are three problems with this system:

1. Speed. This is the slowest possible way to send events in Udon.
2. Network load. This is a high-intensity system and will negatively effect your network performance if abused.
3. Event volume. If you send more than one event every half second or so via this system, there is a risk that the earlier of the two events is not received. Take
care to only use this system when you need a payload, and use SendCustomNetworkEvent otherwise.
62 changes: 62 additions & 0 deletions Utils/GenericButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;

///<Summary>A generic button that calls a UdonBehaviour's event when a collider hits it.</Summary>
public class GenericButton : UdonSharpBehaviour
{
public Collider triggerer;
public UdonBehaviour behaviour;
public string eventName;
private float activatedTimer = 1;
private bool activatedCountdown;
private float updateTimer = 0.25f;
private Image image;

void Start()
{
image = GetComponent<Image>();
}

void Update()
{
if (activatedCountdown)
{
activatedTimer -= Time.deltaTime;
if (activatedTimer < 0)
{
activatedCountdown = false;
activatedTimer = 1;
}

return;
}

if (updateTimer < 0)
{
updateTimer -= Time.deltaTime;
return;
}

if (Vector3.Distance(triggerer.transform.position, transform.position) < 0.05 && image != null) // Distance is quite expensive so let's avoid doing it too often.
{
image.color = Color.cyan;
}
else
{
image.color = Color.white;
}
}

private void OnTriggerEnter(Collider other)
{
if (other.name == triggerer.name && !activatedCountdown)
{
activatedCountdown = true;
behaviour.SendCustomEvent(eventName);
}
}
}
25 changes: 25 additions & 0 deletions Utils/GenericButtonTriggerer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

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

public class GenericButtonTriggerer : UdonSharpBehaviour
{
void Update()
{
var player = Networking.LocalPlayer;
if (player == null)
{
return;
}

var handLoc = player.GetBonePosition(HumanBodyBones.LeftIndexDistal);
if (handLoc == null)
{
handLoc = player.GetTrackingData(VRCPlayerApi.TrackingDataType.LeftHand).position;
}

transform.position = handLoc;
}
}

0 comments on commit 4a49418

Please sign in to comment.