Unity Commands & Events Guide
This guide was written as a quick reference step by step guide for setting up a simple Unity command/event. For more in depth information on each step please check the documentation at https://www.genvidtech.com/for-developers/sdk-latest/
Stop Command (Unity)
We will be creating a command that is only available on the admin screen that allows you us to stop the clock from ticking
First select “Create Command Parameter” in the contextual menu under Create->Genvid
The parameter will have one property named “Id”. Set the Id of our command to be “Stop”
In the GenvidPlugin object in your scene under the property SessionManager->Session->Commands->Settings add an element. Drag our new command parameter into element 0
In the Commands Listener object in your scene under the property Commands->Listeners create a new element. Drag our new parameter into the “Command” property in Element 0.
Note there is an “OnCommandTriggered” we can bind a function to. We will make this next. Create a script component named “ClockCommand” on the Commands Listener.
Add a public GameObject property named “Clock” to the script.
Add a function named “OnStopCommand”. It returns void and takes in 3 properties. A string named “Id”, a string named “Command”, and an IntPtr named “CallbackPointer”. Make the function public
Note that the 3 properties are the types that “OnCommandTriggered” asks for
Add the following code to the command:
if (Id == "Stop") { Clock.GetComponent<Clock>().enabled = false; }
The component we are getting is our script on the Clock. If you named the script something that is not “Clock” that will be what you are getting instead.
Setting enabled on a script to false stops it from ticking
On your Command Listener there should now be a “Clock” property on the script you created. Drag your Clock in the level into that property.
On Commands Listener - drag a reference to itself to the object property under “OnCommandTriggered”.
In the function dropdown look for “OnStopCommand” - and set this function
GenvidPlugin with the new command
Stop Command (Web)
In your web/config folder open up the web.hcl and make sure the “admin” link templates all end in “/admin”. Then in your web/public folder create an “admin.html” file.
Add the following code to the new html file:
<!doctype html> <html> <head> <title>Genvid Overlay</title> <link rel="stylesheet" href="style.css"> <script src="genvid.umd.js"></script> <script src="genvid-math.umd.js"></script> <script src="overlay-admin.js"></script> </head> <body style="background:black"> <div id="video_player"></div> <div id="overlay"> <tr> <td><button class='commandButton' id='admin_stop_button'>STOP</button></td> </tr> </div> </body> </html>
We are creating a button with the text “STOP” on the admin link
We reference a new .js file named “overlay-admin.js” file. We will create that next
In the public folder create a file named “overlay-admin.js”. This will be the javascript file that will back the admin version of the steam
Add the following code to the new js file:
class AdminController { constructor(videoPlayerId) { this.videoPlayerId = videoPlayerId; } start() { fetch("/api/public/channels/join", { method: "POST", }) .then((data) => data.json()) .then((res) => this.onChannelJoin(res)) .catch((error) => genvid.error(`Can't get the stream info: ${error}`)); } onChannelJoin(joinRep) { this.client = genvid.createGenvidClient( joinRep.info, joinRep.uri, joinRep.token, this.videoPlayerId ); let StopButton = document.getElementById( "admin_stop_button" ); StopButton.addEventListener( "click", () => { this.stopClock(); }, false ); this.client.start(); } sendCommands(bodyCommands, successMessage, errorMessage) { fetch("/api/admin/commands/game", { method: "POST", body: JSON.stringify(bodyCommands), headers: { "Content-Type": "application/json", }, }) } stopClock(){ const commands = { id: "Stop", value: "Clock:Stop", }; const successMessage = "Command sent."; const errorMessage = "Stop Clock Error"; this.sendCommands(commands, successMessage, errorMessage); } } let admin = new AdminController("video_player"); admin.start();
Notice this file is a bit different then the last js file. This version gives us more control over the controller class. We will be updating the other js file in a similar fashion later
We bind a function that sends a command with the id “Stop” to a button click. Recall we named our command “Stop” in the project. This is how they connect
Go through the steps of setting up the local cluster, package the project (or play in editor), and start up the services
Click on the admin link. The username and password will both be “admin”
Click on the stop command. The clock should immediately stop ticking in the project and eventually stop on stream
Stream with Command
Color Change Event (Unity)
We will be creating a event allows viewers to change the clocks color
First select “Create Event Parameter” in the contextual menu under Create->Genvid
The parameter will have one property named “Id”. Set the Id of our event to be “colorChange”
In the GenvidPlugin object in your scene under the property SessionManager->Session->Events->Settings add an element. Drag our new event parameter into element 0
In the Events Listener object in your scene under the property Events->Listeners create a new element. Drag our new parameter into the “Event” property in Element 0.
Note there is an “OnEventTriggered” we can bind a function to. We will make this next. Create a script component named “ClockEvent” on the Commands Listener.
Add a public GameObject property named “Clock” to the script.
Add a function named “OnColorChangeEvent”. It returns void and takes in 4 properties. A string named “Id”, a EventResult array named “Results”, an int32 named “NumResults”, and an IntPtr named “CallbackPointer”. Make the function public
Note that the 4 properties are the types that “OnEventTriggered” asks for
Add the following code to the command:
int PinkEvents = 0; int BlackEvents = 0; foreach(EventResult Result in Results) { if (Result.key.fields[0] == "pink") { foreach (EventValue value in Result.values) { if (value.reduce == 2) { PinkEvents += (int) value.value; } } } else if (Result.key.fields[0] == "black") { foreach (EventValue value in Result.values) { if (value.reduce == 2) { BlackEvents += (int)value.value; } } } } bool BlackColor = BlackEvents > PinkEvents; Clock.GetComponent<Clock>().changeColor(BlackColor);
The component we are getting is our script on the Clock. If you named the script something that is not “Clock” that will be what you are getting instead.
We loop through the events and look for a key of “pink” or “black”. Then if the reduce operation is type “2” (or ‘count’) then we add those values to a counter we are tracking
We then call a function on our clocks script that does not exist yet but we will make soon (if the clock script was imported it will already exist)
The next few steps will already be done if the clock was imported
Open your clock script and create 5 public properties. 3 GameObjects: HourHandCube, MinuteHandCube, and SecondHandCube. 2 Materials: BlackMaterial and PinkMaterial.
Create a function called “changeColor” that returns void and takes in one parameter named “BlackColor” of type boolean. Make it public. Add the following code:
if (BlackColor) { HourHandCube.GetComponent<MeshRenderer>().material = BlackMaterial; MinuteHandCube.GetComponent<MeshRenderer>().material = BlackMaterial; SecondHandCube.GetComponent<MeshRenderer>().material = BlackMaterial; } else { HourHandCube.GetComponent<MeshRenderer>().material = PinkMaterial; MinuteHandCube.GetComponent<MeshRenderer>().material = PinkMaterial; SecondHandCube.GetComponent<MeshRenderer>().material = PinkMaterial; }
If we pass in true then we make all the hands of the clock black. Otherwise make them pink
On your Clock object there should now be 3 new properties for the hands of the clock. Drag the hands into each property.
Create a material and make it pink. Drag this material and the existing black material you are using for the clock hands into the 2 other properties of the clock script.
On your Event Listener there should now be a “Clock” property on the script you created. Drag your Clock in the level into that property.
On Event Listener - drag a reference to itself to the object property under “OnEventTriggered”.
In the function dropdown look for “OnColorChangeEvent” - and set this function
GenvidPlugin with the new event
Event Listener
Color Change Event (Web)
Go to your web/public folder
Open your index.html file. Add the following code after the existing streams, notifications, and annotations:
<div> <button class='eventButton' id='pink_button'>Pink</button> </div> <div> <button class='eventButton' id='black_button'>Black</button> </div>
We Declare two buttons. One with the text “Pink” the other with the text “Black”
Open your overlay.js file. Replace the contents with the following code:
var genvidClient; class WebController { constructor(videoPlayerId) { this.videoPlayerId = videoPlayerId; } start() { fetch("/api/public/channels/join", { method: "POST", }) .then(function (data) { return data.json() }) .then((res) => this.onChannelJoin(res)) .catch((error) => genvid.error(`Can't get the stream info: ${error}`)); } onChannelJoin(joinRep) { this.genvidClient = genvid.createGenvidClient( joinRep.info, joinRep.uri, joinRep.token, this.videoPlayerId ); this.genvidClient.onStreamsReceived(function (dataStreams) { for (let stream of [...dataStreams.streams, ...dataStreams.annotations]) { for (let frame of stream.frames) { try { frame.user = JSON.parse(frame.data); } catch (e) { console.log(e, frame.data); } } } }); this.genvidClient.onVideoPlayerReady(function () { console.log("Video Player is Ready"); }); this.genvidClient.onDraw(function (frame) { let gameDataFrame = frame.streams["ClockTime"]; if (gameDataFrame && gameDataFrame.user) { update(gameDataFrame.user); } if ("ClockAnnotation" in frame.annotations && frame.annotations["ClockAnnotation"].length){ for (let gameAnnotation of frame.annotations["ClockAnnotation"]) { if (gameAnnotation && gameAnnotation.user) { annotationUpdate(gameAnnotation.user); } } } }); this.genvidClient.onNotificationsReceived(function (notifications) { for (let notification of notifications.notifications) { try { notification.user = JSON.parse(notification.data); notificationUpdate(notification.user); } catch (e) { console.log(e, notification); } } }); let PinkButton = document.getElementById( "pink_button" ); PinkButton.addEventListener( "click", () => { this.onColorChange("pink"); }, false ); let BlackButton = document.getElementById( "black_button" ); BlackButton.addEventListener( "click", () => { this.onColorChange("black"); }, false ); this.genvidClient.start(); } onColorChange(color) { this.genvidClient.sendEventObject({ colorChange: color, }); } } function update(data) { document.getElementById("hour").innerHTML = "Hour: " + Math.round(data.hour); document.getElementById("minute").innerHTML = "Minute: " + Math.round(data.minute); document.getElementById("second").innerHTML = "Second: " + Math.round(data.second); } function notificationUpdate(data) { document.getElementById("notification").innerHTML = "Notification: " + Math.round(data.second); } function annotationUpdate(data) { document.getElementById("annotation").innerHTML = "Annotation: " + Math.round(data.second); } let web = new WebController("video_player"); web.start();
Don’t be intimidated by the changes. Most of it has remained the same - just restructured in a way that gives us more control.
Note there is some new code related to events
We get the pink button and black button and bind to a function named “onColorChanged” but pass a different string value
This is the string value we look for in MulticastConfirmEventColorChange_Implementation
onColorChange sends the string with the key “colorChange”. Recall we named our event in the C++ “colorChange” as well.
Go to your web/config folder and open events.json. Replace the contents with the following code:
{ "version": "1.7.0", "event": { "game": { "maps": [ { "id": "colorChange", "source": "userinput", "where": {"key": ["colorChange"], "name": "<color>", "type": "string"}, "key": ["colorChange", "<color>"], "value": 1 } ], "reductions": [ { "id": "colorChange", "where": {"key": ["colorChange", "<color>"]}, "key": ["<color>"], "value": ["$count"], "period": 250 } ] } } }
Update your “version” number if necessary
This sets up the map reduce of the “colorChange” event
See the documentation for more info on map reduce. The important part is multiple instances of “pink” and “black” are put together and counted
Copy the events.json file to your config folder that is one step above your project
This is where “game.hcl” and “sample.hcl” live
Both the web and client need to know about the event format
Go through the steps of setting up the local cluster, package the project (or play in editor), and start up the services
Click on the non-admin link
Click on the pink or black. The clock hands will shift color depending on what is pressed. If multiple sessions were connected and clicking at the same time, whatever had the most clicks per map-reduce would win out.
Stream with event buttons
Events Improvements (Unity)
The event is functional. But we can do better.
We will send notifications updating the current bid count of each event and instead only update when the clock strikes a 15 second interval
Open your ClockEvents script
Create two class variables of type int with named “PinkEvents” and “BlackEvents”. Make them both public
Replace your “OnColorChangeEvent” with the following code:
foreach(EventResult Result in Results) { if (Result.key.fields[0] == "pink") { foreach (EventValue value in Result.values) { if (value.reduce == 2) { PinkEvents += (int) value.value; } } } else if (Result.key.fields[0] == "black") { foreach (EventValue value in Result.values) { if (value.reduce == 2) { BlackEvents += (int)value.value; } } } } }
We no longer reset the pink and black vote count
We no longer call the function to update the color of the clock hands
PinkEvents and BlackEvents are now class properties
Create a new function called “ColorChange” that takes no inputs and returns void. Make it public. Copy the following code into it:
if (BlackEvents == PinkEvents) { return; } bool BlackColor = (BlackEvents > PinkEvents); Clock.GetComponent<Clock>().changeColor(BlackColor);
When called this function does nothing if the values are the same. Otherwise it updates to the greater of the two values
In your ClockStream script create a new public class variable called “ClockEvent” of type game object
In the “Update” function. Change the if statement that flips the notification and annotation booleans to the following:
if (second % 15 == 0 && second != previousSecond) { // Note here how we are sending the notification and the annotation at the same time. canSendAnnotation = true; canSendNotification = true; previousSecond = second; ClockEvents.GetComponent<ClockEvents>().ColorChange(); }
We now call the new function we just created on the Clock Events script to update the colors of the hands every 15 seconds
Now we will add a notification stream for the bid numbers - but first we need a structure that will contain our notification data. Create a serializable public structure named “BidUpdates” with two SerializeFields that are public of type int. One named “pink” and the other is “black”:’
[System.Serializable] public struct BidUpdates { [SerializeField] public int black; [SerializeField] public int pink; }
Create “SubmitClockColorBidNotification” that takes in a GenvidStreamParameters named “streamParams” and returns void. Add the following code to it:
if (GenvidPlugin.Instance.IsInitialized && GenvidPlugin.Instance.enabled) { string streamId = streamParams.Id; ClockEvents ClockScript = ClockEvents.GetComponent<ClockEvents>(); BidUpdates notification = new BidUpdates() { black = ClockScript.BlackEvents, pink = ClockScript.PinkEvents, }; GenvidPlugin.Instance.SessionManager.Session.SubmitNotificationJSON(streamId, notification); }
We get the pink and black events and stuff them in our new serializable structure before submitting it as a notification
Back in unity create a new genvid stream parameter and set the id to be “ColorBidNotification”
In your GenvidPlugin add a 4th Stream element and drag your new parameter on to this element
On the Streams Listener add a new element to the listeners list. Drag your new parameter to the Stream property. Drag a reference to itself on “OnStreamSubmit” object property. Then select our new “SubmitClockColorBidNotification” function. Drag our new parameters unto the parameters property
Still on the Streams Listener scroll to your ClockStreams script component. For the ClockEvents object drag your “EventsListener” gameobject in the scene to it.

Stream Listener with the additional stream
Events Improvements (Web)
We now have a notification being sent to us we want to intercept. However, first some general improvements.
First open the style.css file and add the following classes:
.btn { opacity: 0.7; border-radius: 50%; padding: 12px; margin: 12px; width:16px; } .black { background-color:rgb(0, 0, 0); border-color:rgb(251,72,196); } .pink { background-color:rgb(251,72,196); border-color:rgb(0, 0, 0); }
We will now have colored buttons that users can click on for the black and pink bids
Open index.html. Replace the contents with the following:
<!doctype html> <html> <head> <title>Genvid Overlay</title> <link rel="stylesheet" href="style.css"> <script src="genvid.umd.js"></script> <script src="genvid-math.umd.js"></script> <script src="overlay.js"></script> </head> <body style="background:black"> <div id="video_player"></div> <div id="overlay"> <div class="child" id="hour">Hour:</div> <div class="child" id="minute">Minute:</div> <div class="child" id="second">Second:</div> <div class="child" id="notification">Notification:</div> <div class="child" id="annotation">Annotation:</div> <div> <button class='btn pink' id='pink_button'></button> </div> <div class="child" id="PinkBid">Pink Bids:</div> <div> <button class='btn black' id='black_button'></button> </div> <div class="child" id="BlackBid">Black Bids:</div> </div> </html>
We now have a place to display the number of bids for each color next to the buttons
Additionally we added some default values to all the streams
We are now ready to receive the new notification. Open “overlay.js”
Replace the onNotificationsReceived override with the following:
this.genvidClient.onNotificationsReceived(function (notifications) { for (let notification of notifications.notifications) { try { notification.user = JSON.parse(notification.data); if (notification.id == "ClockNotification") { notificationUpdate(notification.user); } else if (notification.id == "ColorBidNotification") { colorBidUpdate(notification.user); } } catch (e) { console.log(e, notification); } } });
We now only call notificationUpdate if the id is “ClockNotification”
There is a new call to a function we will create below called “colorBidUpdate” if the notification has the name “ColorBidNotification”
Add the following function at the bottom of the overlay.js file:
function colorBidUpdate(data) { document.getElementById("PinkBid").innerHTML = "Pink Bids: " + Math.round(data.pink); document.getElementById("BlackBid").innerHTML = "Black Bids: " + Math.round(data.black); }
We use the notification to update our overlay with the current bid numbers
Run through the usual steps detailed in the web guide to deploy the client and local cluster. You can now vote for the colors and have their values updated in real time. Additionally the colors only change when a clock hand hit’s a 15 second mark. However the votes are tallied later due to the stream delay
