• Register

Wave Engine was born on February 21, 2013 with the exciting mission of developing an engine to help the mobile game developers’ community.

Post tutorial Report RSS UI controls sample

All games have some kind of interface to interact with users. Those interfaces are used typically to change some game configuration, allow some in-game interactions, etc. Those elements are positioned not in the 3D world but in the screen surface. They are 2D basic elements with less complexity than 3D typical objects we can find in games. With this sample we introduce Wave Engine UI Controls and we will see how we can create easy and quickly a sample interface.

Posted by on - Basic Client Side Coding

Objectives

All games have some kind of interface to interact with users. Those interfaces are used typically to change some game configuration, allow some in-game interactions, etc.

Those elements are positioned not in the 3D world but in the screen surface. They are 2D basic elements with less complexity than 3D typical objects we can find in games.
With this sample we introduce Wave Engine UI Controls and we will see how we can create easy and quickly a sample interface like this:
ScreenFinalUI

Create Project

Create a new WaveEngine Game Project from the Wave Engine template and name it UISample:

In MyScene.cs file add at these Color variables we will use in the sample:

private Color backgroundColor = new Color(163 / 255f, 178 / 255f, 97 / 255f);
private Color darkColor = new Color(120 / 255f, 39 / 255f, 72 / 255f);
private Color lightColor = new Color(157 / 255f, 73 / 255f, 133 / 255f);

Modify CreateScene() method with this code that adds a FixedCamera and sets the backgroundColor:

//Create a 3D camera
var camera3D = new FixedCamera("Camera3D", new Vector3(2f, 0f, 2.8f), new Vector3(.5f, 0, 0)) 
{
   BackgroundColor = backgroundColor 
};
EntityManager.Add(camera3D);

Note: Do not forget to add usings to WaveEngine.Components.Cameras and WaveEngine.Common.Math

We will add a basic cube to the scene to see how we can manipulate it with Wave Engine UI Controls. First add the texture using the Assets Exporter Tool on the Resouces.weproj of the project.

Add these usings we will need:

using WaveEngine.Framework.Graphics;
using WaveEngine.Components.Graphics3D;
using WaveEngine.Materials;

As we are going to need access to some properties of our cube, create these class variables before the CreateScene() method:

private Transform3D cubeTransform;
private BasicMaterial cubeMaterial;

  • cubeTransform will give us access to cube LocalWorld, position, rotation and scale.
  • cubeMaterial will give us access to cube texture, alpha, color and other texture properties.

Add the function that will create and add our cube to the scene:

private void CreateCube3D()
{
    Entity cube = new Entity("Cube")
         .AddComponent(new Transform3D())
         .AddComponent(Model.CreateCube())
         .AddComponent(new MaterialsMap(new BasicMaterial("Content/crate.jpg")))
         .AddComponent(new ModelRenderer());


    this.cubeTransform = cube.FindComponent<Transform3D>();
    this.cubeMaterial = (BasicMaterial)cube.FindComponent<MaterialsMap>()
                                           .DefaultMaterial;

    EntityManager.Add(cube);
}

With these lines we are initializing cubeTransform and cubeMaterial. One important thing is that we are not positioning the cube in the screen center, we are setting the position just a little left moved. That is because we are going to use the right screen zone to display some UI Controls.

Add the line that will call this method at the end of CreateScene() method:

this.CreateCube3D();

So if you build and run, you will see something similar to:
ScreenCubeUI

Adding UI Controls

We are going to start our UI interface by adding a simple slider to the left, just to allow us modify cube rotation in X axis. First, add the usings to Wave Engine UI Controls:

using WaveEngine.Components.UI;
using WaveEngine.Framework.UI;

Rotation Sliders

Define a new method called CreateSliders() we will use to add the code for the sliders entities:

private void CreateSliders()
{

}

And add the correspondent call to it at after CreateCube3D() sentence.

As we are going to need access to some properties of our cube, create these class variables before the CreateScene() method:

private Slider sliderRotX;
private Slider sliderRotY;

We will add a TextBlock and a Slider to the CreateUI method in this way:

TextBlock t_rotX = new TextBlock()
{
    Text = "Rot X",
    VerticalAlignment = VerticalAlignment.Bottom,
    Margin = new Thickness(15, 0, 0, 60),
};

EntityManager.Add(t_rotX);

this.sliderRotX = new Slider()
{
    Margin = new Thickness(50, 0, 0, 0),
    Orientation = Orientation.Vertical,
    VerticalAlignment = VerticalAlignment.Center,
    Height = 360,
    Width = 40,
    Minimum = 0,
    Maximum = 360,
    Foreground = darkColor,
    Background = lightColor,
};

EntityManager.Add(this.sliderRotX);

The TextBlock add some text with a little description of slider’s functionality

Regardless to Slider component, by setting Margin property we are setting the slider position respect to left upper screen corner origin (0,0). By default, a slider orientation is Horizontal but for this one we need to set it to vertical. VerticalAlignment.Center makes the slider to be centered vertically. We could set HorizontalAlignment to Left, but that’s default value, so the slider will be positioned in the left center screen position. As we are going to rotate the cube in X axis, we set the Minimum and Maximum properties to 0 and 360 respectively.

If we build and run we will see this:
ScreenSliderRotX1UI
As we want to modify cube rotation in X Axis when we move the slider we need to subscribe to RealTimeValueChanged event handler, so after adding the EntityManager.Add(this.sliderRotX) add this code:

this.sliderRotX.RealTimeValueChanged += this.sliderRot_RealTimeValueChanged;

And then define the method behaind the CreateSliders() method:

private void sliderRot_RealTimeValueChanged(object sender, ChangedEventArgs e)
{
    var xRadians = MathHelper.ToRadians(this.sliderRotX.Value);
    var yRadians = 0f;

    this.cubeTransform.LocalRotation = new Vector3(xRadians, yRadians, 0);
}

If we build and run you will be able to modify the cube rotation in X axis:
ScreenSliderRotXUI
Now we will add other slider to allow cube rotation in Y axis in the same way. After the RealTimeValueChanged event handler subscription, add this code to create another TextBlock and a slider in a very similar way as we did with the X slider:

TextBlock t_rotY = new TextBlock()
{
    Text = "Rot Y",
    VerticalAlignment = VerticalAlignment.Bottom,
    Margin = new Thickness(40, 0, 0, 40),
};

EntityManager.Add(t_rotY);

this.sliderRotY = new Slider()
{
    Margin = new Thickness(100, 20, 0, 32),
    VerticalAlignment = VerticalAlignment.Bottom,
    Width = 360,
    Height = 40,
    Minimum = 0,
    Maximum = 360,
    Foreground = darkColor,
    Background = lightColor,
};

EntityManager.Add(this.sliderRotY);

this.sliderRotY.RealTimeValueChanged += this.sliderRot_RealTimeValueChanged;

And update the method sliderRot_RealTimeValueChanged to include the rotation in the Y axis:

var yRadians = MathHelper.ToRadians(this.sliderRotY.Value);

If we build and run we will see:
ScreenSliderRotYUI

Grid

The grid in Wave Engine UI Controls is a layout panel that let us arranges controls in a tabular structure of rows and columns. It facilitates how to position controls in the screen and we will use it to add the right controls we saw at the beginning of the sample.

We will need to access the screen’s height and width to set the grid size; those properties are located in WaveEngine.Framework.Services namespace so add the properly using.

Put this code at the end of CreateScene():

private void CreateGrid()
{
    Grid grid = new Grid()
    {
        HorizontalAlignment = HorizontalAlignment.Right,
        Height = WaveServices.Platform.ScreenHeight,
    };

    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Proportional) });
    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(4, GridUnitType.Proportional) });
    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Proportional) });
    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Proportional) });
    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Proportional) });
    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Proportional) });
    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(3, GridUnitType.Proportional) });
    grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(200, GridUnitType.Pixel) });

    EntityManager.Add(grid);

}

We define a grid with seven rows, each one with the same height, and one column.

For the first row in the grid we will create a TextBlock that will be added to the first row in the grid:

TextBlock t_colors = new TextBlock()
    {
        Text = "Colors",
        VerticalAlignment = VerticalAlignment.Bottom,
        Margin = new Thickness(10),
    };

t_colors.SetValue(GridControl.RowProperty, 0);
t_colors.SetValue(GridControl.ColumnProperty, 0);

grid.Add(t_colors);

If we build and run:
ScreenTextColorUI

StackPanel and RadioButtons

Now we will add a StackPanel with four RadioButtons inside that will allow us to change the cube color. The necessary code to create the stack panel and the radio buttons is:

StackPanel stackPanel = new StackPanel()
{
    Margin = new Thickness(30, 0, 0, 0),
};

stackPanel.SetValue(GridControl.RowProperty, 1);
stackPanel.SetValue(GridControl.ColumnProperty, 0);

grid.Add(stackPanel);

RadioButton radio1 = new RadioButton()
{
    Text = "Red",
    GroupName = "colors",
    Foreground = Color.Red,
};

radio1.Checked += (s, o) =>
{
    this.cubeMaterial.DiffuseColor = Color.Red;
};

stackPanel.Add(radio1);

RadioButton radio2 = new RadioButton()
{
    Text = "Green",
    GroupName = "colors",
    Foreground = Color.Green,
};

radio2.Checked += (s, o) =>
{
    this.cubeMaterial.DiffuseColor = Color.Green;
};

stackPanel.Add(radio2);

RadioButton radio3 = new RadioButton()
{
    Text = "Blue",
    GroupName = "colors",
    Foreground = Color.Blue,
};

radio3.Checked += (s, o) =>
{
    this.cubeMaterial.DiffuseColor = Color.Blue;
};

stackPanel.Add(radio3);

RadioButton radio4 = new RadioButton()
{
    Text = "White",
    GroupName = "colors",
    Foreground = Color.White,
    IsChecked = true,
};

radio4.Checked += (s, o) =>
{
    this.cubeMaterial.DiffuseColor = Color.White;
};

stackPanel.Add(radio4); 

Although this code is a quite large, it is not very complex. We have defined a StackPanel with a 30 pixels margin and added to the grid. By default, if we do not indicate anything when we add a control to a grid, it is added to the first row and column.

Note that all RadioButtons are initialized with the same GroupName property; this indicates that they are in the same group and only one of them can be checked at a time. Note that rdbWhite button IsChecked property is set to true, meaning that rdbWhite will be checked by default. And see how we are handling the Checked event to be able to change the cube color.

Last thing to note is that the four RadioButtons are only added to the StackPanel, not the grid, because StackPanel is already added to the grid.

So if we build and run:
ScreenRadioButtonsUI

ToggleSwitch

We will see a ToggleSwitch in action by adding and deleting the cube material depending if it is toggled on or off. Add this code at the end of CreateGrid() method:

TextBlock t_texture = new TextBlock()
    {
        Text = "Textures",
        VerticalAlignment = VerticalAlignment.Bottom,
        Margin = new Thickness(10),
    };

t_texture.SetValue(GridControl.RowProperty, 2);
t_texture.SetValue(GridControl.ColumnProperty, 0);

grid.Add(t_texture);

ToggleSwitch toggleTexture = new ToggleSwitch()
{
    Margin = new Thickness(30, 0, 0, 0),
    Foreground = darkColor,
    Background = lightColor,
    IsOn = true,
};

toggleTexture.Toggled += (s, o) =>
{
    this.cubeMaterial.TextureEnabled = toggleTexture.IsOn;
};

toggleTexture.SetValue(GridControl.RowProperty, 3);
toggleTexture.SetValue(GridControl.ColumnProperty, 0);

grid.Add(toggleTexture); 

We added a TextBlock and a ToggleSwitch directly to the grid, but, with the line toggleTexture.SetValue we are indicating that the control will be added to the second row in the grid.

Note that we will be able to enable/disable the cube texture:
ScreenTextureUI

Scale Slider

It would be interesting to scale the cube with other Slider with a TextBlock. So add this code at the end of CreateGrid() method:

TextBlock t_scale = new TextBlock()
    {
        Text = "Scale",
        VerticalAlignment = VerticalAlignment.Bottom,
        Margin = new Thickness(10),
    };

t_scale.SetValue(GridControl.RowProperty, 4);
t_scale.SetValue(GridControl.ColumnProperty, 0);

grid.Add(t_scale);

Slider sliderScale = new Slider()
{
    Width = 150,
    VerticalAlignment = VerticalAlignment.Bottom,
    Margin = new Thickness(30, 0, 0, 0),
    Foreground = darkColor,
    Background = lightColor,
    Value = 50,
};

sliderScale.RealTimeValueChanged += (s, o) =>
{
    this.cubeTransform.Scale = Vector3.One / 2 + (Vector3.One * (o.NewValue / 100f));
};

sliderScale.SetValue(GridControl.RowProperty, 5);
sliderScale.SetValue(GridControl.ColumnProperty, 0);

grid.Add(sliderScale); 

Note that this time we are modifying the cubeTransform.Scale property:
ScreenScaleUI

Reset Button

Another feature that could be interesting is a button that reset all values changed in the interface, so add this code at the end of CreateGrid():

Button b_reset = new Button()
    {
        Text = "Reset",
        Margin = new Thickness(10, 0, 0, 20),
        VerticalAlignment = VerticalAlignment.Bottom,
        Foreground = Color.White,
        BackgroundColor = lightColor,
    };

b_reset.Click += (s, o) =>
{
    radio4.IsChecked = true;
    toggleTexture.IsOn = true;
    sliderScale.Value = 50;
    this.sliderRotX.Value = 0;
    this.sliderRotY.Value = 0;
};

b_reset.SetValue(GridControl.RowProperty, 6);
b_reset.SetValue(GridControl.ColumnProperty, 0);

grid.Add(b_reset); 

Now if you build, run, play with the interface and click on reset you will get:
ScreenResetUI

Debug ToggleSwitch

Last interesting feature we can add is seeing some debug information by writing this code at the beginning of CreateScene():

csharp code:
RenderManager.DebugLines = true;

Just to avoid build/stop/modify/build cycle, we will add a ToggleSwitch in the upper left screen corner that let us change it in real time. So add another function in this way:

private void CreateDebugMode()
{
    ToggleSwitch debugMode = new ToggleSwitch()
    {
        OnText = "Debug On",
        OffText = "Debug Off",
        Margin = new Thickness(5),
        IsOn = true,
        Width = 200,
        Foreground = darkColor,
        Background = lightColor,
    };

    debugMode.Toggled += (s, o) =>
    {
        RenderManager.DebugLines = debugMode.IsOn;
    };

    EntityManager.Add(debugMode.Entity);
}

At the end of CreateScene() method add a call to it.We can see the debug mode by toggling on:
ScreenDebugOnUI
Have a look to the complete sample here.

Post comment Comments
tuansavi
tuansavi

this.cubeTransform = cube.FindComponent();
this FindComponent() function is changed in 1.3.2 version and the error occur.

Reply Good karma Bad karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: