[cocos2dX] の CCLabelBMFont を マルチバイト文字列対応する

cocos2d-x

※今現在のバージョンではほぼ役に立たない情報なのでご注意下さいませ!

まだ未対応なようなので自力で修正してみた。

3バイト以下のUTF-8マルチバイト文字に対応。

kCCBMFontMaxChars 定数

カタカナなどの文字の設定を含む .fnt を読み込むと、こいつに関連して Assert される。

この値は扱えるユニコード値の限界を設定しているようで、65535にすればユニコードがほぼ使用できる…

そう思っていた時期が俺にもありました。

65535 に変更してみるものの表示されない。

コードを見てみるとなんか ユニコードを取得しようとしていると思われる場所で

unsigned short c = m_sString[ i ];

なんてコードが書かれている。char 値を unsigned short にキャストしても 0 ~ 127, 65408 ~ 65535 の値になるので ASCII 文字以外は思うようには表示されない。

そういうことで現時点では CCBMFontLabel は ASCII 文字列しか対応していないらしい。

使いたいなら 使ってない文字に頑張って割り当てるか、コードを マルチバイト対応にするか、修正されるのを待つ他にない。

ということで修正した。

元のソース

void CCLabelBMFont::createFontChars() メソッドだけを変更すればOK

	void CCLabelBMFont::createFontChars()
	{
		int nextFontPositionX = 0;
        int nextFontPositionY = 0;
		unsigned short prev = -1;
		int kerningAmount = 0;

		CCSize tmpSize = CCSizeZero;

        int longestLine = 0;
        unsigned int totalHeight = 0;

        unsigned int quantityOfLines = 1;

		unsigned int stringLen = m_sString.length();

        if (0 == stringLen)
        {
            return;
        }

        for (unsigned int i = 0; i < stringLen - 1; ++i)
        {
            unsigned short c = m_sString[ i ];
            if (c == '\n')
            {
                quantityOfLines++;
            }
        }

        totalHeight = m_pConfiguration->m_uCommonHeight * quantityOfLines;
        nextFontPositionY = -(m_pConfiguration->m_uCommonHeight - m_pConfiguration->m_uCommonHeight * quantityOfLines);

		for (unsigned int i= 0; i < stringLen; i++)
		{
			unsigned short c = m_sString[ i ];
			CCAssert( c < kCCBMFontMaxChars, "LabelBMFont: character outside bounds");

            if (c == '\n')
            {
                nextFontPositionX = 0;
                nextFontPositionY -= m_pConfiguration->m_uCommonHeight;
                continue;
            }

			kerningAmount = this->kerningAmountForFirst(prev, c);

			const ccBMFontDef& fontDef = m_pConfiguration->m_pBitmapFontArray[c];

			CCRect rect = fontDef.rect;

			CCSprite *fontChar;

			fontChar = (CCSprite*)(this->getChildByTag(i));
			if( ! fontChar )
			{
				fontChar = new CCSprite();
				fontChar->initWithBatchNodeRectInPixels(this, rect);
				this->addChild(fontChar, 0, i);
				fontChar->release();
			}
			else
			{
				// reusing fonts
				fontChar->setTextureRectInPixels(rect, false, rect.size);

				// restore to default in case they were modified
				fontChar->setIsVisible(true);
				fontChar->setOpacity(255);
			}

            float yOffset = (float)(m_pConfiguration->m_uCommonHeight) - fontDef.yOffset;
			fontChar->setPositionInPixels( ccp( nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width / 2.0f + kerningAmount,
				                                (float) nextFontPositionY + yOffset - rect.size.height/2.0f ) );

			//		NSLog(@"position.y: %f", fontChar.position.y);

			// update kerning
			nextFontPositionX += m_pConfiguration->m_pBitmapFontArray[c].xAdvance + kerningAmount;
			prev = c;

			// Apply label properties
			fontChar->setIsOpacityModifyRGB(m_bIsOpacityModifyRGB);
			// Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
			fontChar->setColor(m_tColor);

			// only apply opaccity if it is different than 255 )
			// to prevent modifying the color too (issue #610)
			if( m_cOpacity != 255 )
			{
				fontChar->setOpacity(m_cOpacity);
			}

            if (longestLine < nextFontPositionX)
            {
                longestLine = nextFontPositionX;
            }
		}

        tmpSize.width  = (float) longestLine;
        tmpSize.height = (float) totalHeight;

		this->setContentSizeInPixels(tmpSize);
	}

変更後のソース

void CCLabelBMFont::createFontChars()
	{
		int nextFontPositionX = 0;
        int nextFontPositionY = 0;
		unsigned short prev = -1;
		int kerningAmount = 0;

		CCSize tmpSize = CCSizeZero;

        int longestLine = 0;
        unsigned int totalHeight = 0;

        unsigned int quantityOfLines = 1;

    // ここにワイド文字列への変換処理を追加
    // 不正なデータや4バイト文字を発見した時点で変換を終了します(面倒なので)
    // https://ja.wikipedia.org/wiki/UTF-8 が超参考になった。
    wstring wstr;
    wstr.reserve(m_sString.size());
    unsigned char charValue;
    unsigned i, l, it;
    for ( i = 0, l = m_sString.size(), it = 0; i < l;) {
      charValue = m_sString[ i ];
      if ( (charValue & 128) == 0 ) {
        wstr[it++] = charValue;
        i += 1;
        continue;
      }
      if ( (charValue & 64) == 0 ) break;
      if ( (charValue & 32) == 0 ) {
        if ( (i + 1 >= l) || ((m_sString[ i + 1 ] & 192) != 128) ) break;
        wstr[it++] = ((charValue & 31) << 6) + (m_sString[ i + 1 ] & 63);
        i += 2;
        continue;
      }
      if ( (charValue & 16) == 0 ) {
        if ( (i + 2 >= l) || ((m_sString[ i + 1 ] & 192) != 128) || ((m_sString[ i + 2 ] & 192) != 128) ) break;
        wstr[it++] = ((charValue & 15) << 12) + ((m_sString[ i + 1 ] & 63) << 6) + (m_sString[ i + 2 ] & 63);
        i += 3;
        continue;
      }
      break;
    }

    // ここ書き換えた : 長さは wstr のものにする
		unsigned int stringLen = it;

        if (0 == stringLen)
        {
            return;
        }

        for (unsigned int i = 0; i < stringLen - 1; ++i)
        {
            // ここ書き換えた : m_sString を wstr に差し替え
            unsigned short c = wstr[ i ];
            if (c == L'\n')
            {
                quantityOfLines++;
            }
        }

        totalHeight = m_pConfiguration->m_uCommonHeight * quantityOfLines;
        nextFontPositionY = -(m_pConfiguration->m_uCommonHeight - m_pConfiguration->m_uCommonHeight * quantityOfLines);

		for (unsigned int i= 0; i < stringLen; i++)
		{
      // ここ書き換えた : m_sString を wstr に差し替え
			unsigned short c =wstr[ i ];
			CCAssert( c < kCCBMFontMaxChars, "LabelBMFont: character outside bounds");

            if (c == L'\n')
            {
                nextFontPositionX = 0;
                nextFontPositionY -= m_pConfiguration->m_uCommonHeight;
                continue;
            }

			kerningAmount = this->kerningAmountForFirst(prev, c);

			const ccBMFontDef& fontDef = m_pConfiguration->m_pBitmapFontArray[c];

			CCRect rect = fontDef.rect;

			CCSprite *fontChar;

			fontChar = (CCSprite*)(this->getChildByTag(i));
			if( ! fontChar )
			{
				fontChar = new CCSprite();
				fontChar->initWithBatchNodeRectInPixels(this, rect);
				this->addChild(fontChar, 0, i);
				fontChar->release();
			}
			else
			{
				// reusing fonts
				fontChar->setTextureRectInPixels(rect, false, rect.size);

				// restore to default in case they were modified
				fontChar->setIsVisible(true);
				fontChar->setOpacity(255);
			}

            float yOffset = (float)(m_pConfiguration->m_uCommonHeight) - fontDef.yOffset;
			fontChar->setPositionInPixels( ccp( nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width / 2.0f + kerningAmount,
				                                (float) nextFontPositionY + yOffset - rect.size.height/2.0f ) );

			//		NSLog(@"position.y: %f", fontChar.position.y);

			// update kerning
			nextFontPositionX += m_pConfiguration->m_pBitmapFontArray[c].xAdvance + kerningAmount;
			prev = c;

			// Apply label properties
			fontChar->setIsOpacityModifyRGB(m_bIsOpacityModifyRGB);
			// Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
			fontChar->setColor(m_tColor);

			// only apply opaccity if it is different than 255 )
			// to prevent modifying the color too (issue #610)
			if( m_cOpacity != 255 )
			{
				fontChar->setOpacity(m_cOpacity);
			}

            if (longestLine < nextFontPositionX)
            {
                longestLine = nextFontPositionX;
            }
		}

        tmpSize.width  = (float) longestLine;
        tmpSize.height = (float) totalHeight;

		this->setContentSizeInPixels(tmpSize);
	}
Share
関連記事