Пользовательский актер для BitmapFont (libgdx) - PullRequest
0 голосов
/ 24 февраля 2020

Я потратил несколько разочаровывающих часов, пытаясь реализовать (как я думал, будет) простой FontActor класс.

Идея состоит в том, чтобы просто рисовать текст в указанной позиции c, используя BitmapFont. Так много, мне удалось сделать sh. Однако я изо всех сил пытаюсь вычислить ширину / высоту моего актера на основе отрисованного текста.

(Использование FitViewport для тестирования)

open class FontActor<T : BitmapFont>(val font: T, var text: CharSequence = "") : GameActor() {
    val layout = Pools.obtain(GlyphLayout::class.java)!!

    companion object {
        val identity4 = Matrix4().idt()
        val distanceFieldShader: ShaderProgram = DistanceFieldFont.createDistanceFieldShader()
    }

    override fun draw(batch: Batch?, parentAlpha: Float) {
        if (batch == null) return
        batch.end()

        // grab ui camera and backup current projection
        val uiCamera = Game.context.inject<OrthographicCamera>()
        val prevTransform = batch.transformMatrix
        val prevProjection = batch.projectionMatrix
        batch.transformMatrix = identity4
        batch.projectionMatrix = uiCamera.combined
        if (font is DistanceFieldFont) batch.shader = distanceFieldShader

        // the actor has pos = x,y in local coords, but we need UI coords
        // start by getting group -> stage coords (world)
        val coords = Vector3(localToStageCoordinates(Vector2(0f, 0f)), 0f)

        // world coordinate destination -> screen coords
        stage.viewport.project(coords)

        // screen coords -> font camera world coords
        uiCamera.unproject(coords,
                stage.viewport.screenX.toFloat(),
                stage.viewport.screenY.toFloat(),
                stage.viewport.screenWidth.toFloat(),
                stage.viewport.screenHeight.toFloat())

        // adjust position by cap height so that bottom left of text aligns with x, y
        coords.y = uiCamera.viewportHeight - coords.y + font.capHeight

        /// TODO: use BitmapFontCache to prevent this call on every frame and allow for offline bounds calculation
        batch.begin()
        layout.setText(font, text)
        font.draw(batch, layout, coords.x, coords.y)
        batch.end()

        // viewport screen coordinates -> world coordinates
        setSize((layout.width / stage.viewport.screenWidth) * stage.width,
                (layout.height / stage.viewport.screenHeight) * stage.height)

        // restore camera
        if (font is DistanceFieldFont) batch.shader = null
        batch.projectionMatrix = prevProjection
        batch.transformMatrix = prevTransform
        batch.begin()
    }
}

И в реализации моего родительского Screen класса Я масштабирую свои шрифты при каждом изменении размеров окна, чтобы они не стали «смутыми» или растянутыми:

override fun resize(width: Int, height: Int) {
    stage.viewport.update(width, height)
    context.inject<OrthographicCamera>().setToOrtho(false, width.toFloat(), height.toFloat())

    // rescale fonts
    scaleX = width.toFloat() / Config.screenWidth
    scaleY = height.toFloat() / Config.screenHeight
    val scale = minOf(scaleX, scaleY)
    gdxArrayOf<BitmapFont>().apply {
        Game.assets.getAll(BitmapFont::class.java, this)
        forEach { it.data.setScale(scale) }
    }
    gdxArrayOf<DistanceFieldFont>().apply {
        Game.assets.getAll(DistanceFieldFont::class.java, this)
        forEach { it.data.setScale(scale) }
    }
}

Это работает и выглядит великолепно, пока вы не измените размер окна. После изменения размера шрифты выглядят хорошо и автоматически подстраиваются под относительный размер окна, но FontActor имеет неправильный размер, потому что мой вызов setSize неправильный.

Исходное окно:

before horizontal stretch

После увеличения окна по горизонтали:

after horizontal stretch

Например, если Затем я масштабирую окно по горизонтали (что не влияет на размер мира, потому что я использую FitViewport), шрифт выглядит правильно, как и предполагалось. Однако значение layout.width, возвращаемое из draw(), изменяется, даже если размер текста на экране не изменился. После исследования я понял, что это из-за моего использования setScale, но простое деление ширины на коэффициент x-scaling не исправляет ошибку. И снова, если я удаляю свои setScale звонки, числа имеют смысл, но шрифт теперь сжат!

Еще одна стратегия, которую я попробовал, заключалась в преобразовании ширины / высоты в координаты экрана, а затем с использованием соответствующего проекта / unproject методы, чтобы получить ширину и высоту в мировых координатах. Это страдает от той же проблемы, показанной на изображениях.

Как я могу исправить свою математику?

Или есть ли более умный / простой способ реализации все это? (Нет, я не хочу Label, я просто хочу текстового актера.)

1 Ответ

1 голос
/ 24 февраля 2020

Одной из проблем был мой код масштабирования. Исправление состояло в том, чтобы изменить обновление камеры следующим образом:

context.inject<OrthographicCamera>().setToOrtho(false, stage.viewport.screenWidth.toFloat(), stage.viewport.screenHeight.toFloat())

, что приводит к тому, что моя текстовая камера соответствует камере мирового обзора. Я использовал весь экран для своих расчетов, поэтому растяжение.

Мои scaleX / Y вычисления были неверными по той же причине. После исправления обоих этих просчетов у меня получилось хорошо масштабировать FontActor с правильными границами в мировых координатах.

...