Adding 3D items to a shop page

ui tutorials

6/6/2024

Mihail Todorov

One of the more interesting features of Gameface/Prysm are Live Views . It provides the ability to render dynamic textures from engine directly into the UI.

This allows for users to add things like 3D models, videos or views of the world inside their UI.

In this tutorial we’ll be showing you how add 3D items to a shop page and rotate them using the keyboard.

Prerequisites

For this tutorial we’ll be extending a sample that we have, called Parallax UI which utilizes React 18 with React Router 6. Apart from that we’ll be using our component scrollable container and the Interaction Manager library to help with the keyboard interactions.

Getting started

The first thing we’ll get started with is to set up the backend. In this case it will be an Unreal Engine 5.3 project in order to show the required flow by using a visual tool like the Unreal blueprints.

We’ll be using an existing project with Gameface integrated, but you can also do it for a blank project, which can be set up by following the documentation .

Adding our items

To start, we’ll create a Blueprint Class called Weapon which will contain all of our weapons. And inside we’ll place a SceneCaptureComponent2D that will be the camera that renders onto our UI, along with an Arrow component that will hold all of our weapons.

The idea is that we’ll place all of our weapons in the same place as static meshes and we’ll use some logic to switch between them. And to make rotating them easier, we’ll rotate the whole Arrow instead of each one individually.

In a larger project you may want to create a separate actor for each item as it will have more data than what we’ll be doing here, but for the purpose of this demonstration we’ll go with this approach.

For the weapons themselves we’ll be using the Infinity Blade: Weapons pack by Epic Games in the Unreal Engine Marketplace.

After we’ve added it to our project we can add some of the weapons that we like. In this case we’ve gone with the following 7.

Setting up the live view

In our case it’s pretty straightforward, we’ll follow the guide in our documentation . Since we’ll be showing the weapons inside the shop page in our UI, we’ll need to follow the second part of the tutorial in the documentation for Transparent Live Views .

Rotating the items

To rotate our Arrow component, we’ll be using events triggered from the UI. In our case, when we press the A and D button on the keyboard we’ll call the rotateCameraLeft and rotateCameraRight events respectively.

We’ll start by casting in the Weapon actor Event Graph to the CohtmlGameHUD in order to get the GamefaceHUD object.

Using it we’ll bind and event to Ready for Bindings so that when we access Gameface specific blueprints they will be available to us.

And then from the new event, we’ll do Register for Event for each of the events.

We’ll create an angle variable, so that whenever we change the rotation, we’ll rotate from the current position and not the initial. Then using this angle variable, we’ll add 10 degrees to it and set in the Arrow component world rotation. For the rotateCameraRight we’ll just subtract 10.

Hiding and Showing the items

First thing we need to do is to uncheck the visible setting for all of the items. This way we don’t have to hide everything on load and then show the correct item.

Then in the blueprints of the Weapon actor we can add a function, that will hide or show a weapon, we can call it toggleWeapon and we can pass the name of the weapon, if it’s shown or not. This function will loop through all of the weapons and hides or shows the correct weapon.

Creating the model with our weapons

To tie everything together we need to create a struct that will hold our weapons. Then we can use this struct to make a Data Model that we can use to bind our UI to the game data.

We’ll start by creating two structs. One is for the weapon itself

and the other is for the data model which will hold an array of the previous weapon struct

Once we have these set up, we need to go to our Weapon blueprint and add a variable called WeaponsArray which is just an array of the Weapon struct along with a single weapon that we’ll use to add new items to the array.

Then we can loop through all of arrow component’s children and for each one we will set the weapon struct we created before with random data and set the object name. After we’ve set the data we’ll add it to the WeaponsArray.

Finally we need to create a data model from this array, by going to the HUD Blueprint, getting the Weapon actor and then getting the WeaponsArray from it. Using this struct we can then create a data model called WeaponModel.

Setting the selected item model

The last thing we need to add is to create the SelectedItem model. This model will show which item is currently selected and all it’s stats.

To create it we’ll just get the first element of the WeaponModel, create and update a weapon struct and then create a new model from it.

Making sure to synchronize the models

We need to make sure to synchronize the models on each tick so that when we load our UI all of the data will be available to us in the bindings.

Making the frontend

In our Parallax UI project we’ll create a new page called Shop and set it up to look like the rest of the UI (we won’t be diving deeper into this).

What we’ll do is create two columns, one for the shop items holding a single shop item with default values and another column for the selected item again with default settings.

Creating the Shop items

To create the shop items, we’ll be using the WeaponModel that we’ve created earlier and loop through it’s weapons using data-bind-for on a wrapper <div> element.

Shop.jsx
1
<div data-bind-for="index,item:{{WeaponModel.weapons}}"></div>

Then we’ll add the rest of the data-bindings to change the default values. In our case this would be data-bind-background-image-url for the image, data-bind-value for the texts and data-bind-class-toggle to toggle a red text if the item costs more than what the Player has.

Shop.jsx
1
<div className="shop-item">
2
<div className="shop-item-image" data-bind-style-background-image-url="'./images/shop/' + {{item.name}} + '.png'"></div>
3
<div className="shop-item-gradient">
4
<div className="shop-item-info">
5
<div className="shop-item-title" data-bind-value="{{item.name}}"> Dehumanizer </div>
6
<div className="shop-item-price" data-bind-value="'¥ '+{{item.price}}" data-bind-class-toggle="shop-item-expensive:{{item.price}} > {{Player.currency}}">
7
¥ 300
8
</div>
9
</div>
10
</div>
11
</div>

For the images, we’ve just screenshotted the models in Unreal and added that to our UI.

To make sure that the selected item is shown or hidden we’ll use the focus and blur events in the .shop-item element. To make sure we can change focus we’ll also add the option to focus it by clicking on an item.

1
data-bind-focus="engine.trigger('setWeapon', {{item.name}}, true, {{index}} )"
2
data-bind-blur="engine.trigger('setWeapon', {{item.name}}, false, {{index}} )"
3
tabIndex="1"
4
onClick={() => this.focus()}

We are using data-bind-focus and data-bind-blur here to be able to use the model data in our events. And in those bindings we’ll be calling engine.trigger which we’ll fire an event that we can later handle in the backend.

Adding the selected item

To add the selected item we’ll just add the corresponding data bindings to the HTML.

Shop.jsx
1
<div className="shop-item-data">
2
<div className="shop-item-row">
3
<div className="shop-item-name">Name:</div>
4
<div className="shop-item-data-title" data-bind-value="{{SelectedItem}}.name">
5
Dehumanizer
6
</div>
7
</div>
8
<div className="shop-item-row">
9
<div className="shop-item-name">Rank:</div>
10
<div className="shop-item-data-title" data-bind-value="{{SelectedItem.rank}}" >
11
123
12
</div>
13
</div>
14
<div className="shop-item-row">
15
<div className="shop-item-name">Impact:</div>
16
<div className="shop-item-data-bar">
17
<div className="shop-item-data-bar-fill" data-bind-style-width="{{SelectedItem.impact}} + '%'"></div>
18
</div>
19
</div>
20
<div className="shop-item-row">
21
<div className="shop-item-name">Range:</div>
22
<div className="shop-item-data-bar">
23
<div className="shop-item-data-bar-fill" data-bind-style-width="{{SelectedItem.range}} + '%'"></div>
24
</div>
25
</div>
26
27
<div className="shop-item-row">
28
<div className="shop-item-name">Stability:</div>
29
<div className="shop-item-data-bar">
30
<div className="shop-item-data-bar-fill" data-bind-style-width="{{SelectedItem.stability}} + '%'"></div>
31
</div>
32
</div>
33
34
<div className="shop-item-row">
35
<div className="shop-item-name">Handling:</div>
36
<div className="shop-item-data-bar">
37
<div className="shop-item-data-bar-fill" data-bind-style-width="{{SelectedItem.handling}} + '%'"></div>
38
</div>
39
</div>
40
</div>

3D item preview

To add the live view to our UI we’ll just need to copy the reference from Unreal and add it as an <image> source.

Shop.jsx
1
<img src="/Script/Engine.TextureRenderTarget2D'/Game/LiveViewRT.LiveViewRT'" className="shop-item-preview"/>

Rotating the weapon

To rotate the weapon we’ll need to trigger the events that we’ve set up in our backend. This can be done utilizing our Interaction Manager library for binding them to some key presses.

We’ll start by adding the library to our page and importing the keyboard module:

1
import { keyboard } from "coherent-gameface-interaction-manager";

Then in our useEffect we’ll bind the A and D keys to rotate the item left and right respectively.

Shop.jsx
1
useEffect(() => {
2
keyboard.on({
3
keys: ["A"],
4
type: "hold",
5
callback: () => {
6
engine.trigger("rotateCameraLeft");
7
},
8
});
9
10
keyboard.on({
11
keys: ["A"],
12
type: "press",
13
callback: () => {
14
engine.trigger("rotateCameraLeft");
15
},
16
});
17
18
keyboard.on({
19
keys: ["D"],
20
type: "hold",
21
callback: () => {
22
engine.trigger("rotateCameraRight");
23
},
24
});
25
26
keyboard.on({
27
keys: ["D"],
28
type: "press",
29
callback: () => {
30
engine.trigger("rotateCameraRight");
31
},
32
});
33
34
window.requestAnimationFrame(() => {
35
document.querySelector(".shop-item").focus();
36
});
37
38
return () => {
39
keyboard.off(["A"]);
40
keyboard.off(["D"]);
41
};
42
}, []);

Here you can see that we are binding to both the key press and hold.

Wrapping it up

The last thing to do is to set the listener for the setWeapon event in the backend, so that we can hide and show the correct weapon.

Since receivng events with parameters in the blueprints is not currently supported out of the box, we’ll need to write some C++ to make the event listener available in the blueprint. You can see how to do it here

After that we’ll simply need to call the ToggleWeapon function in our Weapon actor and pass the necessary data.

The last thing we need to set up is to make sure the SelectedItem is being updated as well. So we’ll first check if the item has been selected and if so we’ll get the correct one from the array and set it.

After that we’ll need to update the whole data model with the new data.

Now if we open our UI we’ll see how it changes between weapons when we click on a new weapon.

On this page