Я потратил несколько разочаровывающих часов, пытаясь реализовать (как я думал, будет) простой 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](https://i.stack.imgur.com/binva.png)
После увеличения окна по горизонтали:
![after horizontal stretch](https://i.stack.imgur.com/SHF10.png)
Например, если Затем я масштабирую окно по горизонтали (что не влияет на размер мира, потому что я использую FitViewport
), шрифт выглядит правильно, как и предполагалось. Однако значение layout.width
, возвращаемое из draw()
, изменяется, даже если размер текста на экране не изменился. После исследования я понял, что это из-за моего использования setScale
, но простое деление ширины на коэффициент x-scaling не исправляет ошибку. И снова, если я удаляю свои setScale
звонки, числа имеют смысл, но шрифт теперь сжат!
Еще одна стратегия, которую я попробовал, заключалась в преобразовании ширины / высоты в координаты экрана, а затем с использованием соответствующего проекта / unproject методы, чтобы получить ширину и высоту в мировых координатах. Это страдает от той же проблемы, показанной на изображениях.
Как я могу исправить свою математику?
Или есть ли более умный / простой способ реализации все это? (Нет, я не хочу Label
, я просто хочу текстового актера.)