Best way to draw moveable strings


#1

Hello,

I’m currently learning to use xenko and I started fiddling around with some things. Now I came to a point where I wanted to draw the name and the healthbar above an moveable entity. With some trial and error I achieved it with this code.

var uiEntity = new Entity(playerShip.Name + “Info”);
UIComponent uiComponent = new UIComponent();
uiComponent.IsFullScreen = false;
uiComponent.IsBillboard = true;
uiComponent.SnapText = true;
uiComponent.VirtualResolution = new Vector3(Game.GraphicsDevice.BackBuffer.Width, Game.GraphicsDevice.BackBuffer.Height, 1000f);

        var stackPanel = new StackPanel()
        {
            Orientation = SiliconStudio.Xenko.UI.Orientation.Vertical,
            Margin = new SiliconStudio.Xenko.UI.Thickness(5f, 2f, 5f, 5f)
        };

        float height = 3f;
        string unitName = playerShip.Name;

        unitName = Helper.TruncateString(unitName, 15, "...");
        unitNameTextBlock = new TextBlock
        {
            Font = UnitNameFont,
            Margin = new SiliconStudio.Xenko.UI.Thickness(0f, -3f, 0f, 0f),
            Text = unitName,
            HorizontalAlignment = SiliconStudio.Xenko.UI.HorizontalAlignment.Center,
            Height = TEXT_HEIGHT
        };
        stackPanel.Children.Add(unitNameTextBlock);

        height += TEXT_HEIGHT;

        //lifeTextBlock = new TextBlock()
        //{
        //    Font = UnitNameFont,
        //    Text = string.Format("{0} / {1}", Life, LifeMax),
        //    TextSize = 10f,
        //    HorizontalAlignment = SiliconStudio.Xenko.UI.HorizontalAlignment.Center,
        //    VerticalAlignment = SiliconStudio.Xenko.UI.VerticalAlignment.Center,
        //    Margin = new SiliconStudio.Xenko.UI.Thickness(0f, -2f, 0f, 0f)
        //};
        lifeBar = new ContentDecorator()
        {
            BackgroundColor = Color.DarkRed,
            Content = lifeTextBlock,
            Height = BAR_HEIGHT,
            Width = Life / LifeMax * BAR_MAX_WIDTH,
        };
        stackPanel.Children.Add(lifeBar);

        height += BAR_HEIGHT;

        if (HasShield)
        {
            shieldBar = new ContentDecorator()
            {
                BackgroundColor = Color.DeepSkyBlue,
                Height = BAR_HEIGHT,
                Width = Life / LifeMax * BAR_MAX_WIDTH,
            };
            stackPanel.Children.Add(shieldBar);

            height += BAR_HEIGHT;
        }

        var contentElement = new ContentDecorator
        {
            BackgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.8f),
            Content = stackPanel,
            Height = height,
            Width = UI_MAX_WIDTH,
        };

        uiSize = new Vector2(contentElement.Width, contentElement.Height);

        uiComponent.RootElement = contentElement;
        uiEntity.Add<UIComponent>(UIComponent.Key, uiComponent);

        uiEntity.Transform.Position = calcUiPosition();

The problem with this approach is that the UI wont scale if the camera moves further away from the object. When I use IsFullscreen = true, then the UI is not affected from the camera, but I cant position it above a moveable Unit. In fact I wont be drawn at all if I set the Position of the entity.

My question is now: What’s the best approach here to get an UI which doesnt get smaller if the camera moves further away, but is able to be positioned freely on screen? Can I set some sort of absolute position? Do I need to use something completely different?

Thanks for your advice


#2

Hello Danny,

Thank you for your question.
It is very interesting for us to know how our users want to use xenko UI system, and important to see its limitations.

The intended way to use UI on characters is:

  • to add an child entity the character entity
  • add the Y offset of the UI compared to the character in the child entity transformation
  • add the UI component on the child entity with IsFullScreen=false and IsBillboard=true.
    By doing so all the transformations are calculated by the engine, and the UI position is automatically updated wherever the character moves.

Unfortunately this solution has initially been designed so that the size of the UI on the screen is automatically adjusted depending on the distance of the character to the camera, and is currently not usable for people who would like to keep the size of the UI on the screen constant. We will make sure to add this option in future releases though (probably 1.7.0 with the UI editor).

For the moment, I propose you the following workaround:

  • Set the IsFullScreen property of the UI component to True.
  • Set the Vertical/Horizontal alignment of your UI RootElement to Center
  • Project position of your character (+offset) on the screen using the camera ViewProjection matrix.
  • Convert the screen coordinates into UI virtual resolution coordinates. If your element is centered then (0,0,0) is the center of the screen, (VirtualRes.X/2, VirtualRes.Y/2, 0) the right/top corner of the screen, etc.
  • Set the transformation matrix of you UI entity to the obtained translation value. Note: if for some reasons you cannot modify the UI entity transformation (entity contains other components, etc), you can still use the LocalMatrix of UI RootElement to compensate and produce the correct translation matrix.

Hope it could help! Don’t hesitate if you have more questions.


#3

Thanks for your detailed answer, I’ll try your approaches and see what works best for me :smile:

While tinkering around with the UI I got across a bug I think. I tried to do some fade out by reducing the opacity of a stackpanel. By doing that, I have the problem that some characters of the textblocks inside the stackpanel fade out more slowly than the other characters. It doesnt matter what characters there are. I’ll provide a screenshot as soon as possible if needed.

Greetings