ECS in der Benutzeroberfläche im World of Tanks Blitz-Client

Entwicklung der Benutzeroberfläche unter Verwendung der ECS-Architektur (Entity-Component-System) anstelle der herkömmlichen Vererbung

Dieser Artikel ist eine Fortsetzung von Evgeny Zakharovs Rede auf der Sommerkonferenz C ++ Russia, wo er die Entwicklung einer Benutzeroberfläche unter Verwendung der ECS-Architektur (Entity-Component-System) anstelle der traditionellen Vererbung und eines Teils des UI-Geräts in World of Tanks Blitz beschrieb. 

In seinem Bericht geht Eugene ausführlich darauf ein, welche Prinzipien zum Erstellen von Frameworks für die Benutzeroberfläche heutzutage in der Welt verwendet werden, und erklärt, wie Sie ECS- und UI-Freunde finden können und welche Vor- und Nachteile Sie daraus ziehen können.

In diesem Artikel zeigt Eugene anhand eines kleinen UI-Beispiels in World of Tanks Blitz, was das große Plus der ECS-Architektur in der UI ist.

Bevor Sie den Artikel lesen, empfehlen wir Ihnen, sich das Video des Berichts anzusehen.

Implementierung der radialen Fortschrittsanzeige

, , .. , 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