Communicating with the engine
Prysm interprets and executes JavaScript code that can be used for scripting the UI. This mechanism allows you to call operations from the embedded JavaScript to an HTML page, which in turn can call C# functionalities, such as loading a new scene in your Unity3D project, changing a setting in project settings, retrieving data information from the server associated with your project, and so on. This feature enables seamless communication between the game engine and the frontend, ensuring your engine and UI stay in sync. You can easily communicate from the game engine to the frontend and from the frontend to the game engine, making it simple to maintain synchronized operations and data flow throughout your project.
How to use
All communication between JavaScript and the game goes through the engine
module in the cohtml.js
JS script. This file will be automatically added when you add at least one CohtmlView
to your Unity3D project for the first time. You can find it at Assets/StreamingAssets/Cohtml/UIResources/Hello/js/cohtml.js
. To prepare CohtmlView to work with event bindings, follow these steps:
- Create an empty HTML page in your Unity3D project at
Assets/StreamingAssets/Cohtml/UIResources/
. - Add a reference to
cohtml.js
at the end of thebody
tag of the HTML page. - Create a new
<script>
tag or reference a new script that will contain your binding logic. - In the script, you need to add the function engine.whenReady . This function will be triggered when the View is ready to receive binding events.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script class="scripts" src="[relative_path_to_the_file]/cohtml.js"></script>
<script>
engine.whenReady.then(() => {
// The View's JS is ready for binding
});
</script>
</body>
</html>
- In the Unity3D editor, add a
CohtmlView
component to any scene. - Set the newly created page in the
URL
field of theCohtmlView
. - Create a
C# MonoBehaviour
script and attach it as a component to the game object where theCohtmlView
is located. Add a reference to the component: - Subscribe to
CohtmlView.Listener.ReadyForBindings
delegate. This method will be called when the Cohtml View is ready to accept JavaScript bindings.
public class EventBinding : MonoBehaviour
{
public CohtmlView cohtmlView;
private void Start()
{
cohtmlView = GetComponent<CohtmlView>();
cohtmlView.Listener.ReadyForBindings += () =>
{
// The View C# is ready for binding. You should register for your binding events here
};
}
}
Binding
There are two ways to invoke a game code from JavaScript and vice versa:
- Events: Used to subscribe to the same event from one or more places. It does not return a result.
- Calls: Used to call only one registered handler and can return a result from it. Can be called with zero, one, or more arguments. For this purpose, in JS, you must use a promise to handle the result from the C# handler.
Cohtml
package, and then importing EventsBindings
from the samples. The example code shown here can be found in action in that sample project.Events
Event can be fired with or without parameters, can have multiple handlers, and does not return a result.
Example usage
JS triggers the C#
Let’s create a handler in C# and trigger it from JS. The following diagram visually how an event would be created and sent from the UI to the game.
This example will create an event that notifies C# that JavaScript is ready to listen for triggered events. To achieve that please follow these steps:
- In the
CohtmlView.Listener.ReadyForBindings
delegate callback register the event handler using theCohtmlView.NativeView.RegisterForEvent
method:- The first parameter is the name of the event, which you will trigger from the JS code. For the example will name it
TriggerC#Event_OnViewJSIsReady
- The second parameter is the handler delegate, which will be the method executed after the event is triggered. The type of the method callback will be an
Action
without parameters.
- The first parameter is the name of the event, which you will trigger from the JS code. For the example will name it
The event handler will look like this:
cohtmlView.NativeView.RegisterForEvent("TriggerC#Event_OnViewJSIsReady", (Action)OnViewJSIsReady);
The method callback will look like this:
private void OnViewJSIsReady()
{
// The View's JS is now ready for subsequent triggers from both directions.
}
- In the JS script, call the
engine.trigger
function with the name of the event created in C# e.g.TriggerC#Event_OnViewJSIsReady
:
engine.trigger('TriggerC#Event_OnViewJSIsReady');
C# triggers the JS
Let’s trigger an event in C# and handle it in the JS. The following diagram illustrates how the events sent from the game to the UI work.
The following steps are needed to achieve this:
- In the
engine.whenReady
promise function, add an event handler withengine.on
, specifying:- The first argument is the event name. For example, name it
TriggerJS_OnTriggerParameterlessHandler
- The second argument is the JS function to be invoked when the handler is called.
- The first argument is the event name. For example, name it
The code will look like this
function callback() {
// Execute the event handler callback
}
engine.on('TriggerJS_OnTriggerParameterlessHandler', callback);
- In your C# code, call the event using the
cohtmlView.NativeView.TriggerEvent
method:
cohtmlView.NativeView.TriggerEvent("TriggerJS_OnTriggerParameterlessHandler");
Calls
Use Calls when you want to return a value from the event handler. You can use JavaScript Promise to handle the result. The result can be only one value. Promises are only available when calling C# events from your JavaScript code.
Let’s look at an example where the JS requests a player name and the C# returns it to the JavaScript via a promise. Follow these steps:
- In the C# script, add the delegate to
ReadyForBindings
using theCohtmlView.NativeView.BindCall
method.- The first parameter is the name under which you will call from JS.
- The second is the delegate you will use to return the value. The delegate type must be
Func
with a return value string.
The BindCall method signature will look like this:
cohtmlView.NativeView.BindCall("CallC#_OnReturnPlayerName", (Func<string>)CallCSharp_OnReturnPlayerName);
The callback method should look like this:
private string CallCSharp_OnReturnPlayerName()
{
return "Name returned from C#";
}
- In the JS, add a function to be used as a callback via promise, which will accept the returned result.
- Call the C# event by attaching the method call as a promise with the
then()
function.
Your code should look like this:
function callback(name) {
// Handle the name
}
engine.call('CallC#_OnReturnPlayerName')
.then(callback);
Parameters
To add parameters to the triggers and the event handlers, specify them after the callback method or function. The parameters can range from one to eight per event. Supported data types can be found in the Parameter Types section.
In the JS to C# direction will look like this:
cohtmlView.NativeView.RegisterForEvent("EventName", (Action<type_arg_1, type_arg_2>)EventHandleMethod);
private void EventHandleMethod(arg_1, arg_2)
{
// Handle the arguments
}
engine.trigger('EventName', arg_1, arg_2);
In the C# to JS direction will look like this:
function eventHandleFunction(argument_1, argument_2) {
// Handle the arguments
}
engine.on('TriggerJS_EventName', eventHandleFunction);
cohtmlView.NativeView.TriggerEvent("TriggerJS_EventName", argument_1, argument_2);
Supported Parameter Types
Prysm supports most data types you might need in your project, whether in C# or JS direction. A detailed list is below:
C# side:
- Value types:
Boolean
,int
,uint
,long
,ulong
,sbyte
,byte
,float
,double
,DateTime
. - Reference types:
string
,Custom types
. - Collections:
Array
,List
,Dictionary
,IReadOnlyList
(Export to JS only).
- Value types:
JS side:
- Primitive types:
number
,string
,boolean
. - Collections:
array
,associative array
.
- Primitive types:
Custom Types
You can exchange custom types written by you. To do this, you need to describe the object that exists in C# as a JS object and add two Cohtml attributes to the type and its members:
- To expose your custom C# type, add the
cohtml.Net.CoherentType
attribute above the class. - To expose a field, property, or method, use the
cohtml.Net.CoherentProperty
attribute above it. You can change the name of the member in JS by adding a parameter to the attribute declaration and writing the desired name as a string. If you do not specify a name, the name given in C# will be used by default, Case Sensitive.
As an example, create a class named Item
with members Name
(field, string), Damage
(field, int), and Durability
(property, float). Follow these steps:
- In C#, create a new class named
Item
and add the necessary fields. Add thecohtml.Net.CoherentType
attribute above the class. - Add the
cohtml.Net.CoherentProperty
attribute above each field you want to expose in JS.- For
Damage
field, change the name todamage
. - For
Durability
property, change the name todurability
.
- For
[cohtml.Net.CoherentType]
public class Item
{
public int Id;
[cohtml.Net.CoherentProperty]
public string Name;
[cohtml.Net.CoherentProperty("damage")]
public int Damage;
[cohtml.Net.CoherentProperty("durability")]
public float Durability { get; set; }
}
- To create an instance of the
Item
class in JavaScript, you need to create a JS object. It is mandatory to have a property__Type
with the value being the full path to the class name in C#. In this case isItem
. - Add to the JS object properties for each member you need for your JS logic. In this case, these are
Name
,Damage
, andDurability
. Add data corresponding to the type of the respective member.
The JS object declaration should look like this:
var item = {
__Type: 'Item', // Match the Full Name of the C# type.
Name: 'Book Of Wisdom',
damage: 280, // the member name is lowercase because of CoherentProperty("damage")
durability: 10.15 // the member name is lowercase because of CoherentProperty("durability")
};
JS triggers C#
- Add a new event handler in the
ReadyForBindings
callback, usingRegisterForEvent
with a delegate usingAction<Item>
, which will process the received object.
The C# event handler should look like this:
cohtmlView.NativeView.RegisterForEvent("TriggerC#Event_OnSendCustomObject", (Action<Item>)CallCSharp_OnSendCustomObject);
The C# callback method should be like this:
private void CallCSharp_OnSendCustomObject(Item item)
{
// Receive a JS custom object mapped to the "Item" type. Name: {"Book Of Wisdom"}; Damage: {280}; Durability: {10.15};;
}
The JS trigger should look like this:
engine.trigger('TriggerC#Event_OnSendCustomObject', item);
C# triggers JS
- In C#, within the
ReadyForBindings
callback, trigger the event with the specified event handler name e.g.TriggerJS_OnLoadCustomObject
, and the Item instance as a parameter:
Item item = new Item { Id = 0, Name = "Axe of Might", Damage = 123, Durability = 4.75f };
cohtmlView.NativeView.TriggerEvent("TriggerJS_OnLoadCustomObject", item);
- Add an event handler using
engine.on
with a callback function to receive the Item object.
function callback(item) {
// Received a custom object mapped to the "Item" type. Name: {"Axe of Might"}; Damage: {123}; Durability: {4.75};";
}
engine.on('TriggerJS_OnLoadCustomObject', callback);
Unregister from events
To remove an already registered event handler from JavaScript, you need to call the engine.off
function with:
- The first argument is the name of the event handler.
- The second optional argument is to provide a specific function to be removed from this event. If you do not provide one, call
engine.off
will remove all registered functions for this event.
For example, if you have registered an event named TriggerJS_OnLoadCustomObject
with the callback
function, to unregister it you need to call the following line:
engine.off('TriggerJS_OnLoadCustomObject', callback);
To remove a C# Event or call, you need to keep a reference to the returned BoundEventHandle
. When you want to remove an Event, you should pass it as an argument to the CohtmlView.NativeView.UnregisterFromEvent
method. For example, you have the following event:
BoundEventHandle handle = cohtmlView.NativeView.RegisterForEvent("EventName", (Action)EventHandleMethod);
The removal should be done like this:
cohtmlView.NativeView.UnregisterFromEvent(handle);
To unregister a Call, you need to invoke the CohtmlView.NativeView.UnbindCall
method. For example, if you have a Call:
BoundEventHandle handle = cohtmlView.NativeView.BindCall("CallName", (Func<string>)CallHandleMethod);
The removal should be done like this:
cohtmlView.NativeView.UnbindCall(handle);
Binding Release event
The CohtmView.Listener.BindingsReleased
method in C# is invoked when all registered event handlers, call handlers, and models associated with the JavaScript bindings have been automatically unregistered. This provides an opportunity to release any resources that were used by the JavaScript bindings for the Cohtml View, such as destroying registered models and performing other cleanup tasks.
cohtmlView.Listener.BindingsReleased += () =>
{
// unload your resources
};