提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在业余时间想自己用cocos2dx开发一款像素游戏,但是遇到了不少的坑,其中有一项就是字体方面的。使用一个像素TTF字体,但是始终很糊,便去找各种解决办法。下面是自己使用的方法。

一、关掉抗锯齿

核心函数:setAliasTexParameters();
之前做在使用一些像素材质时也用到过这个函数,cocos2dx正在很多都开启了自动抗锯齿,可以使用该函数关闭抗锯齿。
  • 对于label
    可以使用label->getFontAtlas()->setAliasTexParameters();[还需测试]
  • 对于TextFieldTTF
    可以对其进行继承,在每次draw之前对其生成的_batchNodes里面的每个SpriteBatchNode使用setAliasTexParameters();

二、生成更好的bitmap

我使用的像素字体,按照之前的理解,关闭抗锯齿应该就不会出现模糊问题了。可是字体虽然不是整体模糊了,却又很多的半像素块。于是开始了漫长的读源码之旅。

1.TTF字体

现在用的比较多的就是TTF矢量字体(轮廓字体)和点阵字体。
  • 点阵字体
    点阵字体描绘了用打字是用二维数组描绘了字体中对应部分的深度。故在缩放时容易失真产
    生成马赛克式的锯齿边缘,这时便可以用抗锯齿生成半透明块使其相对平滑。
  • ttf字体
    ttf字体是保存了每个字对应的轮廓,使用一系列数学运算来计算对应结果。这样在对应大小下的字便会生成对应大小的轮廓。使其在缩放时不易失真。

2.Hint

ttf字体在不同大小下回自动计算对应大小下的轮廓,可是最终显示到计算机屏幕上还是需要进行光栅化。在低像素下编有可能出现有些像素块对应不满一个格子的情况。就像下图一样:
图片来源:https://www.freetype.org/ttfautohint/
而hint(提示)技术就是想通过抗锯齿和网格对齐的方式来使这种情况减少。

在freetype2中,字体往往可以被autohint(自动)来获取不错的效果。
在cocos2dx源码中有类似这样的层级(label使用ttf或者textfieldttf):

[label]
		visit();
		updateContent();
		alignText();
			[FontAtlas]
			prepareLetterDefinitions();
				[CCFontFreeType]
				getGlyphBitmap();
					[freetype]
					FT_Load_Char()
					

而bitmap,用于保存每个像素深度值的数组便由FT_Load_Char()产生。cocos2dx随后便通过这个bitmap和相关的输出信息来生成对应的材质。而cocos2dx在使用FT_Load_Char()时吧autohint给关了,具体原因未知。于是我就试着把这个参数给设置为打开(将LOAD_NO_AUTOHINT标记去掉),好在改这个cpp所用重新编译时间不长。

3.更高清的bitmap

打开后发现情况并没有好了很多,虽然半透明块减少了很多,但是仍然有不少,而且部分地方出现了线条大小不一的情况。于是log输出了一下对应的bitmap值。结果却是有缺项。便又去看了看cocos2dx是如何设置对应参数生成bitmap的。随后发现最终生成的bitmap大小和之前调用的FT_SET_CHAR_SIZE有关,具体是写为固定为dpi的(72)和fontsize变量。但是不管调大fontsize和dpi对应的值,其最终渲染出来的字都会变大。于是便想到手动写一个类继承了TextFieldTTF,并将fontSize调大生成更高清的bitmap,但同时又通过管理scale来让字体大小和原来一样。

字体类的代码

只是大概思路,还有很多可以完善的地方:
#include "ui-UiTextFieldTTFNA.h"

Label * UiTextFieldTTFNA::create()
{
	CC_ASSERT(false, "");
	return nullptr;
}

float UiTextFieldTTFNA::getScale() const
{
	return Node::getScale() * _precisionScale;
}

void UiTextFieldTTFNA::setScale(float scale)
{
	Node::setScale(scale / _precisionScale);
}

float UiTextFieldTTFNA::getPrecisionScale() const
{
	return _precisionScale;
}

void UiTextFieldTTFNA::setPrecisionScale(float scale)
{
	auto fontSize = getFontSize();
	auto fontScale = getScale();
	_precisionScale = scale;
	setFontSize(fontSize);
	setScale(fontScale);
}

float UiTextFieldTTFNA::getFontSize() const
{
	auto oldConfig = getTTFConfig();
	auto fontSize = oldConfig.fontSize;

	return fontSize / _precisionScale;
}

void UiTextFieldTTFNA::setFontSize(float sizes)
{
	auto oldConfig = getTTFConfig();
	auto fontName = oldConfig.fontFilePath;
	auto fontSize = sizes * _precisionScale;
	TTFConfig ttfConfig(fontName, fontSize, GlyphCollection::DYNAMIC);
	
	setTTFConfig(ttfConfig);
}

void UiTextFieldTTFNA::draw(Renderer * renderer, const Mat4 & transform, uint32_t flags)
{
	for (auto batchNode : _batchNodes){
		auto texture0 = batchNode->getTextureAtlas()->getTexture();
		auto texture1 = batchNode->getTexture();
		
		texture0->setAliasTexParameters();
		texture1->setAliasTexParameters();
	}
	TextFieldTTF::draw(renderer, transform, flags);
}

UiTextFieldTTFNA * UiTextFieldTTFNA::textFieldWithPlaceHolder(const std::string & placeholder, const Size & dimensions, TextHAlignment alignment, const std::string & fontName, float fontSize)
{
	UiTextFieldTTFNA *ret = new (std::nothrow) UiTextFieldTTFNA();
	if (ret && ret->initWithPlaceHolder("", dimensions, alignment, fontName, fontSize))
	{
		ret->_precisionScale = 1.0f;
		ret->setPrecisionScale(3);
		ret->autorelease();
		if (placeholder.size() > 0)
		{
			ret->setPlaceHolder(placeholder);
		}
		return ret;
	}
	CC_SAFE_DELETE(ret);
	return nullptr;
}

UiTextFieldTTFNA * UiTextFieldTTFNA::textFieldWithPlaceHolder(const std::string & placeholder, const std::string & fontName, float fontSize)
{
	UiTextFieldTTFNA *ret = new (std::nothrow) UiTextFieldTTFNA();
	if (ret && ret->initWithPlaceHolder("", fontName, fontSize))
	{
		ret->_precisionScale = 1.0f;
		ret->setPrecisionScale(3);
		ret->autorelease();
		if (placeholder.size() > 0)
		{
			ret->setPlaceHolder(placeholder);
		}
		return ret;
	}
	CC_SAFE_DELETE(ret);
	return nullptr;
}

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐