ECS en la interfaz de usuario en el cliente de World of Tanks Blitz

Desarrollo de la interfaz de usuario utilizando la arquitectura ECS (Entity-Component-System) en lugar de la herencia tradicional

Este artículo es una continuación del discurso de Evgeny Zakharov en la conferencia de verano C ++ Rusia, donde describió el desarrollo de una interfaz de usuario utilizando la arquitectura ECS (Entity-Component-System) en lugar de la herencia tradicional y parte del dispositivo UI en World of Tanks Blitz. 

En su informe, Eugene profundiza en los principios de creación de marcos para la interfaz de usuario que se utilizan en el mundo actual, y también explica cómo puede hacer amigos de ECS y UI, y qué ventajas y desventajas puede obtener como resultado.

En este artículo, utilizando un pequeño ejemplo de UI en World of Tanks Blitz, Eugene muestra cuál es la gran ventaja de la arquitectura ECS en la UI.

Antes de estudiar el artículo, le recomendamos que vea el video del informe.

Implementación de la pantalla de progreso radial

, , .. , 360 , - 100%. World of Tanks Blitz , ,    DLC  .

, :

  •  texture component;

  •  texture component.

, . , : RadialProgressComponent.

, .

-, :

. : , ( ) "" . 

texture- .

:

, texture- , .

:

, : RadialProgressComponent. ( ) 0.5:

:

, , , ( – RadialProgressSystem).

, ClipPolygonComponent – , - . RadialProgress- .

void RadialProgressSystem::RegisterControl(Control &control)
{
    auto *pieClippable = control.GetComponent<RadialProgressComponent>();
    if (pieClippable && control.GetComponent<TextureComponent>())
    {
        pieClippable->MarkAsDirty();
        this->registeredControls.insert(&control);
    }
}

, ,   TextureComponent  RadialProgressComponent. , , – Process

void RadialProgressSystem::UnregisterControl(Control &control)
{
    this->registeredControls.erase(&control);
}

, , .

 Progress  , . ""  dirty  RadialProgressComponent. "" ,  RadialProgressComponent   dirty  true,  false.

void RadialProgressSystem::Process(float elapsedTime)
{
    for (Control *control : this->registeredControls)
    {
        auto *pieClippable = control->GetComponent<UIRadialProgressComponent>();
        if (!pieClippable->IsDirty())
        {
            continue;
        }

        auto *polygon = control->GetComponent<ClipPolygonComponent>();
        if (!polygon)
        {
            ReportError(control, "You need UIClipPolygonComponent for UIRadialProgressComponent");
            continue;
        }

        auto *textureComponent = control->GetComponent<TextureComponent>();
        if (textureComponent != nullptr && textureComponent->GetSprite() != nullptr)
        {
            Polygon2 &polygonData = polygon->GetPolygon();
            polygonData.Clear();

            const Vector2 imageSize = textureComponent->GetSprite()->GetSize();
            const Vector2 pivot = CalcPivot(pieClippable);
            const Vector2 center = imageSize * pivot;

            const float progress = pieClippable->GetProgress();

            float startAngle = pieClippable->GetNormalizedStartAngle();
            float endAngle = pieClippable->GetNormalizedEndAngle();

            const float currentAngle = Interpolation::Linear(startAngle, endAngle, 0, progress, 1);

            const float width = pivot.x > 0 ? center.x : imageSize.x;
            const float height = pivot.y > 0 ? center.y : imageSize.y;
            const float initAngle = std::atan(width / height);

            polygonData.AddPoint(center);
            polygonData.AddPoint(CalcPointOnRectangle(startAngle, center, imageSize));

            int direction = startAngle < endAngle ? 1 : -1;
            float startOffset = direction > 0 ? 0 : PI_2 + pieClippable->GetAngleBias();

            float squareAngle = startOffset + direction * initAngle;

            const float directedStartAngle = direction * startAngle;
            const float directedEndAngle = direction * endAngle;
            const float directedCurrentAngle = direction * currentAngle;
            float directedSqureAngle = direction * squareAngle;
            const float doubledInitAngle = initAngle * 2.f;

            Vector<Vector2> squares {
                Vector2(imageSize.x, 0),
                Vector2(imageSize.x, imageSize.y),
                Vector2(0.f, imageSize.y),
                Vector2(0.f, 0.f)
            };

            int i = 0;
            while (directedSqureAngle < directedEndAngle)
            {
                if (directedSqureAngle < directedCurrentAngle && directedSqureAngle > directedStartAngle)
                {
                    int squareIndex = direction > 0 ? i % 4 : 3 - i % 4;
                    polygonData.AddPoint(squares[squareIndex]);
                }
                i++;
                int switcher = i % 2;
                squareAngle += direction * (PI * switcher - Sign(switcher - 0.5f) * doubledInitAngle);
                directedSqureAngle = direction * squareAngle;
            }

            polygonData.AddPoint(CalcPointOnRectangle(currentAngle, center, imageSize));

            pieClippable->ResetDirty();
        }
    }
}

,  RadialProgress-, , – , . :

,  UI  World of Tanks Blitz – . .  ECS  UI – , , ( ) .




All Articles