Adding a UI into the world
11/8/2024
Martin Bozhilov
Integrating an interactive UI directly into the game world creates a more immersive experience, allowing players to engage with the interface as a natural part of the environment.
Overview
In this tutorial, we’ll guide you through adding an existing UI to the game world and seamlessly blending it with the environment using Gameface’s in-world UI
feature
in Unreal Engine. We’ll enhance our Tetris UI
sample by adding effects and animations, helping to integrate it naturally into the scene for a more authentic, immersive look.
Resources
The Assets for this tutorial have been taken from Sketchfab and fab .
List of assets:
Getting started - Unreal Engine
We’ll begin by setting up the project in Unreal Engine. Use the getting started guide from our documentation to install the Gameface plugin and integrate it into an existing project or use the sample project created by the installer.
Once that’s complete, create a new blank map in Unreal Engine to start configuring your setup.
Setting up the scene
We’ll start by importing our assets into a newly created level and setting up a simple scene of a dark room with a console and a TV for demonstration purposes.
Adding in-world UI
Next, we’ll add the in-world UI. To do this, go to the Gameface plugin’s dropdown menu and select the Add in-world UI
option.
This will add a CohtmlPlane
actor to your world, containing a plane mesh with the cohtmlMaterial
applied to display the UI.
The cohtmlMaterial
is used as the material for the plane mesh, rendering the UI onto it. To learn more about Gameface’s in-world UI
feature, you can reference
our documentation .
To display your UI, go to the plane’s details panel. In the properties of the Cohtml Gameface Component, locate the URL field and specify the path to the HTML page you want to display in your world.
Important: Ensure your HTML page is included in your game’s uiresources
folder otherwise, the page will not render.
And thats it! Now, if you hit play, your HTML page will be displayed on the plane.
Adjusting the plane to fit the screen
To make the plane blend naturally with the TV and achieve the classic curved appearance of old CRT screens, we’ll use Unreal Engine’s Modeling Tool to slightly bend the plane.
Alternatively, you can model a curved plane in a 3D software like Blender
, import it into Unreal, and replace the plane in the CohtmlPlane
actor.
Bending the plane
Click here for more information on how we did our plane modeling.
First, resize and overlay your plane on top of the TV screen to better gauge how much to bend the plane.
Next up, open the modeling tool and select Deform, and set the resolution to 10 on all axes.
Now, start bending the plane. The simplest method is to select the outer rows and columns one by one and adjust their position on the Y-axis, as shown below:
Here, we lowered the Y-axis values of the outer points of the mesh by 2 to bend it backward
Apply the same method to the remaining sections until you achieve the desired curvature.
A useful tip is to toggle the visibility of the TV object to check how the plane aligns with it and identify areas needing further adjustments. In the image below, the plane is almost finished, but the center needs to be bent slightly more forward.
Here’s the finished result:
Making the TV emit light
To give the TV a more realistic glow, we’ll create a simple actor with a Point Light component as its child. Place the light slightly in front of the plane and adjust its position until it looks natural.
Feel free to experiment with the light settings and adjust the color to match your UI. In our example, we used the color #00F1ACFF
.
To add dynamic behavior, we’ll attach some logic to the light, which will later sync with a CSS
effect to make the TV appear to turn on and off procedurally.
Setting up light logic
-
Set the initial intensity of the Point Light to 0.
-
Open the blueprint editor for the light actor and create a custom event called
SetLightIntensity
. This event will accept one argument—a float value to set the light’s intensity.
The logic for this event is straightforward. We’ll increase the light’s intensity in two stages, with brief delays in between
And then simply connect the logic to Event BeginPlay
to trigger it when the level starts.
Begin Object Class=/Script/BlueprintGraph.K2Node_Event Name="K2Node_Event_0" ExportPath="/Script/BlueprintGraph.K2Node_Event'/Game/Blueprints/TV/TV_Light.TV_Light:EventGraph.K2Node_Event_0'"
EventReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.Actor'",MemberName="ReceiveBeginPlay")
bOverrideFunction=True
NodePosX=-784
NodePosY=-48
bCommentBubblePinned=True
NodeGuid=000A41B246E80E97C6B11B87AE3CEFCF
CustomProperties Pin (PinId=536EB98347ECB48A7D43A8A72CF61788,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.Actor'",MemberName="ReceiveBeginPlay"),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=B76D201E454AE6CBBB92938C134E4B4D,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_9 36FD224041875DB6FE45F0BB8870B57A,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_9" ExportPath="/Script/BlueprintGraph.K2Node_CallFunction'/Game/Blueprints/TV/TV_Light.TV_Light:EventGraph.K2Node_CallFunction_9'"
FunctionReference=(MemberName="SetLightIntensity",MemberGuid=98D35F0F4C35058BCC7AD1AA6AF2A746,bSelfContext=True)
NodePosX=-480
NodePosY=-64
NodeGuid=B68BBE4E46CD1DD78074B5B7B586442A
CustomProperties Pin (PinId=36FD224041875DB6FE45F0BB8870B57A,PinName="execute",PinToolTip="
Exec",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Event_0 B76D201E454AE6CBBB92938C134E4B4D,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=7EB1BC1E4FB1642C2D86DD82B28A4996,PinName="then",PinToolTip="
Exec",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=E3F336BE4002EC675B27C68E29AABD44,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target
Self Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="self",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=39F40A204715395D264AA7AB5B8C49B7,PinName="NewIntensity",PinToolTip="New Intensity
Float (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="5000.000000",AutogeneratedDefaultValue="0.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Setting up TV turn off logic
To make the TV turn off, we’ll reuse the SetLightIntensity
event we created earlier by calling it from the UI with an argument of 0 for the light intensity.
Open the blueprint for the CohtmlPlane
actor. In the Event BeginPlay
node, register the Cohtml Component
for a JavaScript event.
For more details on how to trigger events from the UI, refer to
our documentation ,
Then you need to specify the custom event which will trigger from the UI. In this case, we’ll call it QuitGame
. The event will simply get the light actor and call the
SetLightIntensity
event but with intensity of 0, effectively turning off the light.
Begin Object Class=/Script/BlueprintGraph.K2Node_Event Name="K2Node_Event_1" ExportPath="/Script/BlueprintGraph.K2Node_Event'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_Event_1'"
EventReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.Actor'",MemberName="ReceiveBeginPlay")
bOverrideFunction=True
NodePosX=-1056
NodePosY=-80
bCommentBubblePinned=True
NodeGuid=E59C47A0429E38BCFBACF89ACBDE3EEA
CustomProperties Pin (PinId=B2E33CBE4D10CB733205828FB906C5FD,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.Actor'",MemberName="ReceiveBeginPlay"),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=A56E1DD94763614CBD458B8DD7C0F32F,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Bind_Event_to_Ready_for_Bindings D453DB184A8026BC0D3EE99AC0265352,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CustomEvent Name="K2Node_CustomEvent_0" ExportPath="/Script/BlueprintGraph.K2Node_CustomEvent'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_CustomEvent_0'"
CustomFunctionName="QuitGame"
NodePosX=-1056
NodePosY=352
NodeGuid=4E71F6234CEA7CBDE5B13C99F68833D9
CustomProperties Pin (PinId=F1B2BEFB430AF01CF1A0CF9011D60138,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/Engine.BlueprintGeneratedClass'/CohtmlPlugin/BendedPlane.BendedPlane_C'",MemberName="QuitGame",MemberGuid=4E71F6234CEA7CBDE5B13C99F68833D9),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_9 65B282664CFA39A663CD5886A6089615,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=93AFC91C462D455C1AB01C9FCEDC24D6,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_25 9F5A8F074D71D017704379813EF3CF97,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_AssignDelegate Name="K2Node_Bind_Event_to_Ready_for_Bindings" ExportPath="/Script/BlueprintGraph.K2Node_AssignDelegate'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_Bind_Event_to_Ready_for_Bindings'"
DelegateReference=(MemberParent="/Script/CoreUObject.Class'/Script/CohtmlPlugin.CohtmlBaseComponent'",MemberName="ReadyForBindings")
NodePosX=-816
NodePosY=-80
ErrorType=1
NodeGuid=C20AD51445BC4FC421D2969D22A448E4
CustomProperties Pin (PinId=D453DB184A8026BC0D3EE99AC0265352,PinName="execute",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Event_1 A56E1DD94763614CBD458B8DD7C0F32F,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=DFC74ED14D01F43A2881C8BA92DB093E,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=98B5F5C945A226E535BED6BED0CFED92,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "BaseMCDelegateSelfPinName", "Target"),PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/CohtmlPlugin.CohtmlBaseComponent'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableGet_15 7D23ED154F9905A8358E1FB922BF9CE3,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=CB93B7924C7F3EAEEE40F79D7951ADA0,PinName="Delegate",PinFriendlyName=NSLOCTEXT("K2Node", "PinFriendlyDelegatetName", "Event"),PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/CoreUObject.Package'/Script/CohtmlPlugin'",MemberName="CohtmlReadyForBindings__DelegateSignature"),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=True,PinType.bIsConst=True,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CustomEvent_9 F19721084C7E49312885FE955C3F7793,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CustomEvent Name="K2Node_CustomEvent_9" ExportPath="/Script/BlueprintGraph.K2Node_CustomEvent'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_CustomEvent_9'"
CustomFunctionName="ReadyForBindings"
NodePosX=-1056
NodePosY=176
ErrorType=1
NodeGuid=8DBD185D4821EFC6F5E3BAACAACE486C
CustomProperties Pin (PinId=F19721084C7E49312885FE955C3F7793,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/Engine.BlueprintGeneratedClass'/CohtmlPlugin/BendedPlane.BendedPlane_C'",MemberName="ReadyForBindings",MemberGuid=8DBD185D4821EFC6F5E3BAACAACE486C),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Bind_Event_to_Ready_for_Bindings CB93B7924C7F3EAEEE40F79D7951ADA0,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=FF7167DE49D356D44FA2CBB5B4F4A699,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_9 DCBD6DF84366654606117481E4E28698,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_9" ExportPath="/Script/BlueprintGraph.K2Node_CallFunction'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_CallFunction_9'"
FunctionReference=(MemberParent="/Script/CoreUObject.Class'/Script/CohtmlPlugin.CohtmlBaseComponent'",MemberName="RegisterForEvent")
NodePosX=-816
NodePosY=96
ErrorType=1
NodeGuid=9E1DBA2F4E9E0E36E9FB7E93FEFD8110
CustomProperties Pin (PinId=DCBD6DF84366654606117481E4E28698,PinName="execute",PinToolTip="
Exec",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CustomEvent_9 FF7167DE49D356D44FA2CBB5B4F4A699,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=97105E2E42D83C6308B11499863BA99E,PinName="then",PinToolTip="
Exec",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=AF6F6B2B418836217B8E058FE2ABD1C6,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target
Gameface Base Component Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/CohtmlPlugin.CohtmlBaseComponent'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableGet_15 7D23ED154F9905A8358E1FB922BF9CE3,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=3CD9ADA04996DEB16E5DCD8C8BC5629C,PinName="JSEventName",PinFriendlyName="JavaScript Event Name",PinToolTip="Java Script Event Name
String",PinType.PinCategory="string",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="QuitGame",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=65B282664CFA39A663CD5886A6089615,PinName="Delegate",PinFriendlyName="Event",PinToolTip="Event
Delegate",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent="/Script/CoreUObject.Package'/Script/CohtmlPlugin'",MemberName="CohtmlJSEventBPCallback__DelegateSignature"),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CustomEvent_0 F1B2BEFB430AF01CF1A0CF9011D60138,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_VariableGet Name="K2Node_VariableGet_15" ExportPath="/Script/BlueprintGraph.K2Node_VariableGet'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_VariableGet_15'"
VariableReference=(MemberName="Cohtml",bSelfContext=True)
NodePosX=-1056
NodePosY=80
NodeGuid=CD9B6358427C9BD1FB3AC8A5A9999C5B
CustomProperties Pin (PinId=7D23ED154F9905A8358E1FB922BF9CE3,PinName="Cohtml",Direction="EGPD_Output",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/CohtmlPlugin.CohtmlComponent'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Bind_Event_to_Ready_for_Bindings 98B5F5C945A226E535BED6BED0CFED92,K2Node_CallFunction_9 AF6F6B2B418836217B8E058FE2ABD1C6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=E1CC498A4DA03042B42BB68D2D029C65,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/Engine.BlueprintGeneratedClass'/CohtmlPlugin/BendedPlane.BendedPlane_C'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_25" ExportPath="/Script/BlueprintGraph.K2Node_CallFunction'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_CallFunction_25'"
FunctionReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.GameplayStatics'",MemberName="GetActorOfClass")
NodePosX=-848
NodePosY=352
NodeGuid=5C9BDB774E0531D8CAB5C892A10993FC
CustomProperties Pin (PinId=9F5A8F074D71D017704379813EF3CF97,PinName="execute",PinToolTip="
Exec",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CustomEvent_0 93AFC91C462D455C1AB01C9FCEDC24D6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=698225A24D5360C4FFC496A8D5472799,PinName="then",PinToolTip="
Exec",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_3 40850407448A6846323F75B04B8B130E,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=BFB74ACA4466B1FCEEF87F9E26D67A26,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target
Gameplay Statics Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/Engine.GameplayStatics'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Script/Engine.Default__GameplayStatics",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=A826AF594A5AD715DACC21B01C82F810,PinName="WorldContextObject",PinToolTip="World Context Object
Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/CoreUObject.Object'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=True,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=6195DD1F4099A6493C7DEDB5B576FE13,PinName="ActorClass",PinToolTip="Actor Class
Actor Class Reference
Class of Actor to find. Must be specified or result will be empty.",PinType.PinCategory="class",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/Engine.Actor'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=True,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Game/Blueprints/TV/TV_Light.TV_Light_C",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=762C769448EA7D6B9759778255003B2F,PinName="ReturnValue",PinToolTip="Return Value
TV Light Object Reference
Actor of the specified class.",Direction="EGPD_Output",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/TV/TV_Light.TV_Light_C'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_3 3FBE9E6640EBA1EEFB0088A0D0BB671C,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_3" ExportPath="/Script/BlueprintGraph.K2Node_CallFunction'/CohtmlPlugin/BendedPlane.BendedPlane:EventGraph.K2Node_CallFunction_3'"
FunctionReference=(MemberParent="/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/TV/TV_Light.TV_Light_C'",MemberName="SetLightIntensity",MemberGuid=98D35F0F4C35058BCC7AD1AA6AF2A746)
NodePosX=-480
NodePosY=352
NodeGuid=2938596E44CF6CC6CEC0509FC9253134
CustomProperties Pin (PinId=40850407448A6846323F75B04B8B130E,PinName="execute",PinToolTip="
Exec",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_25 698225A24D5360C4FFC496A8D5472799,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=59F951A14EF934E9E82853B670E0640E,PinName="then",PinToolTip="
Exec",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=3FBE9E6640EBA1EEFB0088A0D0BB671C,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target
TV Light Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/TV/TV_Light.TV_Light_C'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_25 762C769448EA7D6B9759778255003B2F,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=0618AE0B48AAE99B7A47ED8A4F4FF0FC,PinName="NewIntensity",PinToolTip="New Intensity
Float (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.0",AutogeneratedDefaultValue="0.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
And with that, we’ve completed the Unreal Engine setup! Next, we’ll move on to setting up the frontend to bring the UI to life.
Getting started - Frontend
For the UI of this tutorial, we will be enhacing one of our existing samples - the Tetris UI, which you can find in the ${Gameface package}/Samples/uiresources/TetrisInventoryUI
directory.
Or if you have installed our Unreal Coherent Sample
project you can find it in the Content/uiresources/TetrisInventoryUI
directory.
Because it is an existing project, everything is already setup for us, now let’s just modify it.
Making TV on/off animations
To create the effect of the TV turning on and off, we’ll modify the HTML by adding a wrapper element around the content and applying animations to it.
These animations will control the width
, height
, and opacity
of the container holding the content, simulating the TV’s behavior.
Start by wrapping the entire content inside the <body>
tag with a <div>
element that has the class content-container
Next, slightly modify the body
styles and define the styles for the content-container
and its animations:
After applying the styles, the UI will transition as if it opens from the center of the screen.
To “turn off” the TV, we’ll later add the class screen-off
to the content-container
via JavaScript to trigger the opposite animation.
Slightly adjusting the UI
In the preview, you may notice the bottom-right corner of the UI is slightly cut off due to the plane’s curvature. To fix this, adjust the button’s CSS properties as follows:
Adding the old tv screen noise effect
To add the effect of a game being played on an old CRT TV, let’s add an effect throug css and overlay it on top of the content. We are going to achieve this effect by
adding a gradient background to the overlay
element and animate it’s background position slightly to simulate the lines flickering.
Start by adding a <div>
with the class overlay
inside the content-container
:
Now, define the styles for the overlay
element and add an animation for the static noise effect:
Once the styles are applied, the overlay will create a subtle, animated noise effect reminiscent of an old CRT TV.
Implementing a pause menu to turn off the TV
To simulate the player interacting with the game on the TV, we’ll add a simple pause menu. When the player opens the menu and clicks Quit, the TV will turn off.
Begin by adding the HTML for the pause menu
And the styles
Next, add a visual indicator on the bottom left corner of the screen for the player to know which button to press to open the pause menu:
Now let’s make the menu interactable so we can trigger the QuitGame
event we defined in Unreal Engine earlier.
First, get references to the pause menu and content container:
Update the existing focusin
event to avoid breaking previous logic when interacting with the pause menu:
Modify the handleEnterPress
function to handle pause menu interactions:
- The Play button will close the menu.
- The Quit button will trigger the QuitGame event in Unreal Engine and execute the TV’s “turn off” animation.
Create a function to open the pause menu, initialize spatial navigation , and focus the Play button:
Now we need to attach both functions and since the Tetris UI
supports playstation controller input with the
Interaction manager
we can easily extend our logic to be supported by a gamepad,
Players can now open the pause menu by pressing P
on a keyboard or the Start button on a PlayStation controller. The menu allows them to resume the game or quit, triggering the TV “turn off” effect.
With this the UI is completed!