【发布时间】:2017-07-07 03:29:27
【问题描述】:
好的,我正在开发一个设置在计算机操作系统中的 MonoGame 游戏。正如预期的那样,它做了很多文本渲染。游戏中的操作系统允许用户自定义操作系统的几乎所有方面——人们已经为操作系统制作了皮肤,使其看起来像 Mac OS Sierra、自 95 年以来几乎所有主要的 Windows 版本、Xubuntu、Ubuntu 等等。
这个游戏以前是用 Windows 窗体编写的,但是我想实现一些在 WinForms 中根本不可能实现的功能。所以,我们决定从 WinForms 转移到 MonoGame,我们面临着一个巨大的问题。
我们制作的皮肤格式允许用户选择计算机上安装的任何字体,以用于各种元素,如标题栏文本、主 UI 文本、终端文本等。这在 WinForms 中很好,因为我们可以使用 System.Drawing呈现文本并允许在系统上使用任何 TrueType 字体。如果可以加载到System.Drawing.Font,就可以渲染了。
但是,MonoGame 使用不同的技术在屏幕上呈现文本。 SpriteFont 对象。问题是,似乎根本没有办法从用于在代码中生成System.Drawing.Fonts(家庭、大小、风格等)的相同数据动态生成SpriteFont。
因此,由于我似乎无法动态创建 SpriteFonts,因此在我的图形助手类(处理在当前图形设备上绘制纹理等而不需要到处复制粘贴代码)中,我有自己的 DrawString 和 MeasureString 方法使用 System.Drawing.Graphics 将文本合成到位图上,并将该位图用作纹理以绘制到屏幕上。
而且,这是我的代码。
public Vector2 MeasureString(string text, System.Drawing.Font font, int wrapWidth = int.MaxValue)
{
using(var gfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1)))
{
var s = gfx.SmartMeasureString(text, font, wrapWidth); //SmartMeasureString is an extension method I made for System.Drawing.Graphics which applies text rendering hints and formatting rules that I need to make text rendering and measurement accurate and usable without copy-pasting the same code.
return new Vector2((float)Math.Ceiling(s.Width), (float)Math.Ceiling(s.Height)); //Better to round up the values returned by SmartMeasureString - it's just easier math-wise to deal with whole numbers
}
}
public void DrawString(string text, int x, int y, Color color, System.Drawing.Font font, int wrapWidth = 0)
{
x += _startx;
y += _starty;
//_startx and _starty are used for making sure coordinates are relative to the clip bounds of the current context
Vector2 measure;
if (wrapWidth == 0)
measure = MeasureString(text, font);
else
measure = MeasureString(text, font, wrapWidth);
using (var bmp = new System.Drawing.Bitmap((int)measure.X, (int)measure.Y))
{
using (var gfx = System.Drawing.Graphics.FromImage(bmp))
{
var textformat = new System.Drawing.StringFormat(System.Drawing.StringFormat.GenericTypographic);
textformat.FormatFlags = System.Drawing.StringFormatFlags.MeasureTrailingSpaces;
textformat.Trimming = System.Drawing.StringTrimming.None;
textformat.FormatFlags |= System.Drawing.StringFormatFlags.NoClip; //without this, text gets cut off near the right edge of the string bounds
gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel; //Anything but this and performance takes a dive.
gfx.DrawString(text, font, new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B)), 0, 0, textformat);
}
var lck = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); //Lock the bitmap in memory and give us the ability to extract data from it so we can load it into a Texture2D
var data = new byte[Math.Abs(lck.Stride) * lck.Height]; //destination array for bitmap data, source for texture data
System.Runtime.InteropServices.Marshal.Copy(lck.Scan0, data, 0, data.Length); //cool, data's in the destination array
bmp.UnlockBits(lck); //Unlock the bits. We don't need 'em.
using (var tex2 = new Texture2D(_graphicsDevice, bmp.Width, bmp.Height))
{
for (int i = 0; i < data.Length; i += 4)
{
byte r = data[i];
byte b = data[i + 2];
data[i] = b;
data[i + 2] = r;
} //This code swaps the red and blue values of each pixel in the bitmap so that they are arranged as BGRA. If we don't do this, we get weird rendering glitches where red text is blue etc.
tex2.SetData<byte>(data); //Load the data into the texture
_spritebatch.Draw(tex2, new Rectangle(x, y, bmp.Width, bmp.Height), Color.White); //...and draw it!
}
}
}
我已经在缓存大量动态创建的纹理 - 游戏内程序的窗口缓冲区、皮肤纹理等,所以这些不会对性能造成严重影响,但是这个文本渲染代码会造成严重影响。我什至无法让游戏超过 29 FPS!
那么,有没有更好的方法在没有 SpriteFonts 的情况下进行文本渲染,如果没有,是否有任何方法可以通过指定字体系列、字体大小和样式(粗体、斜体、三振等)?
我想说我现在是 MonoGame 的中级,但我很难让 RenderTargets 工作 - 所以如果你想回答这个问题,请像在和幼儿园学生交谈一样回答。
任何帮助都将不胜感激,因为这是我游戏开发团队中的一个主要热点问题,您可能会在游戏的致谢中看到自己作为主要帮助:P
【问题讨论】:
-
听起来您面临着相当大的挑战。我不知道您的问题的确切解决方案,但如果它有帮助,欢迎您从 MonoGame.Extended BitmapFont 实现中借用一些代码。
-
谢谢,我用你的字体渲染代码替换了我的字体渲染代码,使用了 System.Drawing,我相信它在 MonoGame 中的字体渲染要好得多,尤其是对于亚洲字符。 MonoGame 对多字节 Unicode 字符的支持很差。
-
如果这个项目仍然与您相关,可能值得查看我为这个确切问题构建的一个类,它使用 GDI+,但与您的完全不同(更简单的方式)几乎没有提高游戏性能:github.com/Zintom/BitmapTextRenderer
标签: c# performance text fonts monogame