🐳

fluentui-emojiの絵文字をUnityで読み込む

2022/08/21に公開

マイクロソフトが提供しているオープンソースの絵文字データのFluent Emojiを、UnityのTextMeshProで読み込むための手順をご紹介します。

大まかな流れは以下の通りです。

  1. Fluent EmojiをClone
  2. SVG画像をPNG画像に変換
  3. Pythonスクリプトを実行してアトラスを作成
  4. Full Emoji Support Apiの変換ツールで、TextMeshPro用のSpriteAssetを作成
  5. SpriteAssetを修正して、TMP_Emoji Text UGUI コンポーネントにアタッチ

1. fluentui-emojiをClone

以下のサイトから、絵文字データをCloneします。
https://github.com/microsoft/fluentui-emoji

2. SVGファイルをPNGに変換

fluentui-emojiには、3DFlatColorHigh Contrastの4種類の絵文字データが入っています。3DはPNG画像なので、変換がほぼ必要ない[1]ですが、その他の絵文字データがSVG画像なので、PNG画像に変換が必要です。Photoshopや、AffinityPhotoなどのツールを用いて、一括変換してください[2]

3. Pythonスクリプトを実行してアトラスを作成

以下のPythonスクリプトを、Cloneしたfluentui-emojiディレクトリに作成して、実行してください。Pythonの環境設定は割愛します。

# fluentui-emojiのデータから、TMP Full Emoji Support Api用のアトラスを作成するスクリプト
# デフォルトでは1アトラスあたり、256個の絵文字が入ります。
# 1つのアトラスに入りきらない場合は、複数のアトラスを生成します。

import os
import json
from PIL import Image, UnidentifiedImageError

# 定数
ASSET_PATH = './assets'  # 絵文字アセットのパス
EMOJI_TYPE = '3D'  # 絵文字のタイプ: 3D, Color, Flat, High Contrast
EMOJI_DIR = '3D'  # アトラスの生成ディレクトリ名、アトラス画像の末尾にも適用される
ATLAS_IMAGE_SIZE = 4096  # アトラスの画像サイズ
EMOJI_IMAGE_SIZE = 256  # 絵文字の画像サイズ

# 絵文字ディレクトリ一覧を取得
emojiDirs = os.listdir(ASSET_PATH)

# 絵文字数
emojiCount = 0

# アトラスの列数=行数
atlasColumnCount = int(ATLAS_IMAGE_SIZE / EMOJI_IMAGE_SIZE)
print("AtlasColumnCount: " + str(atlasColumnCount))

# アトラスに入る最大絵文字数
maxEmojiCountPerAtlas = int(ATLAS_IMAGE_SIZE / EMOJI_IMAGE_SIZE) ** 2
print("MaxEmojiCountPerAtlas: " + str(maxEmojiCountPerAtlas))

# アトラスのJson配列。アトラスごとにjsonを生成する。
atlasJsons = []

print("---- Scan Metadata ----")

for emojiDirName in emojiDirs:
    # 各絵文字の内部ディレクトリ一覧を取得
    emojiDataDirs = os.listdir(os.path.join(ASSET_PATH, emojiDirName))

    # メタデータのjsonを読み込む
    metaDataPath = os.path.join(ASSET_PATH, emojiDirName, 'metadata.json')
    metadataJson = {}
    with open(metaDataPath, encoding='utf-8') as jsonFile:
        metadataJson = json.load(jsonFile)

    # アトラスのID。絵文字がどのアトラスに入るか決める。
    atlasId = int(emojiCount / maxEmojiCountPerAtlas)

    # アトラス用のJsonがなければ生成
    if len(atlasJsons) < atlasId + 1:
        atlasJsons.append([])

    # Jsonにパラメータを追加
    emojiJson = {}
    emojiJson["name"] = metadataJson["cldr"]
    # Full Emoji Support Apiではimage名からコードポイントを取得する。
    emojiJson["image"] = str(metadataJson["unicode"]).replace(" ", "-")
    emojiJson["sheet_x"] = int((emojiCount - atlasId * maxEmojiCountPerAtlas) % atlasColumnCount)
    emojiJson["sheet_y"] = int((emojiCount - atlasId * maxEmojiCountPerAtlas) / atlasColumnCount)

    # スキントーンの有無を調べる。スキントーン対応絵文字は未対応。
    hasSkintones = False
    if "unicodeSkintones" in metadataJson:
        hasSkintones = True

    # 画像ディレクトリパスを生成
    imageDir = os.path.join(ASSET_PATH, emojiDirName, EMOJI_TYPE)
    # 画像ディレクトリが存在しなければ、パスを修正。スキントーン絵文字の場合は、Defaultディレクトリに画像が入っている。
    if not os.path.isdir(imageDir):
        imageDir = os.path.join(ASSET_PATH, emojiDirName, 'Default', EMOJI_TYPE)

    if not os.path.isdir(imageDir):
        # 画像ディレクトリが存在しなければ、空のパスをJsonに設定。
        emojiJson["imagePath"] = ""
    else:
        imagePath = ""
        imageDirFiles = os.listdir(imageDir)
        for f in imageDirFiles:
            if f.endswith(".png"):  # PNGのみサポートする。
                imagePath = f
                break
        imagePath = os.path.join(imageDir, imagePath)
        emojiJson["imagePath"] = imagePath

    emojiCount += 1

    # 絵文字のJsonをアトラスのJsonに追加
    atlasJsons[atlasId].append(emojiJson)

print("EmojiCount: " + str(emojiCount))

# 出力ディレクトリを作成
outputDir = "output" + "/" + EMOJI_DIR
os.makedirs("output", exist_ok=True)
os.makedirs(outputDir, exist_ok=True)

atlasCount = int(emojiCount / maxEmojiCountPerAtlas) + 1
print("AtlasCount: " + str(atlasCount))

# アトラス画像リスト
atlasList = []

print("---- Generate Atlas ----")

for atlasId in range(atlasCount):
    atlas = Image.new('RGBA', (ATLAS_IMAGE_SIZE, ATLAS_IMAGE_SIZE), (0, 0, 0, 0))
    atlasList.append(atlas)
    atlasJson = atlasJsons[atlasId]
    # Json出力
    with open(outputDir + "/atlas" + format(atlasId, '0>2') + "_" + EMOJI_DIR + ".json", "w") as f:
        json.dump(atlasJson, f, indent=4)

    # 絵文字の画像をアトラスに貼り付ける。
    for emojiJson in atlasJson:
        if not emojiJson["imagePath"] == "":
            try:
                image = Image.open(emojiJson["imagePath"]).convert('RGBA')
                x = emojiJson["sheet_x"]
                y = emojiJson["sheet_y"]
                atlas.paste(image, (x * EMOJI_IMAGE_SIZE, y * EMOJI_IMAGE_SIZE))
            except UnidentifiedImageError as e:
                print(e)

print("---- Write Atlas ----")

# アトラス画像を出力
for atlasId in range(atlasCount):
    atlas = atlasList[atlasId]
    atlas.save(outputDir + "/atlas" + format(atlasId, '0>2') + "_" + EMOJI_DIR + ".png", format="png")

print("---- Finished ----")

成功すると、output/3Dディレクトリに、以下のようなファイルが生成されます。

4. Full Emoji Support Apiの変換ツールで、TextMeshPro用のSpriteAssetを作成

Unityプロジェクトを新規作成して、Full Emoji Support Apiを導入します。

生成した、PNG画像とJsonをUnityプロジェクトに入れて、Jsonファイルの拡張子を.txtに変更します。

PNG画像のMaxSizeがデフォルトだと2048になっているので、全て4096に変更します。

Window > TextMeshPro > Sprite Emoji Importerから、Sprite Emoji Importerウィンドウを開き、以下の画像のように設定し、Create Sprite Assetボタンを押して、成功したら、Save Sprite Assetボタンを押して、アセットを保存します。


(Glyph Scaleの効果がよくわからなかったので、誰か教えてください…)

これをアトラスの枚数分繰り返します。これでスプライトアセットは完成ですが、便利に使うために、もう少し設定が必要です。

6. SpriteAssetを修正して、TMP_Emoji Text UGUIコンポーネントにアタッチ

最初に作成したスプライトアセット(atlas00_3D)のFallback Sprite Assetsに、その他のスプライトアセットを追加します。

これは、最初のスプライトアセットに絵文字が存在しない場合に、他のアセットを探しに行くための設定です。以上で設定は完了です。

TMP_Emoji Text UGUI コンポーネントのExtra Settingsにある、Sprite Assetに最初に作成したスプライトアセット(atlas00_3D)をアタッチすると絵文字を表示できるようになります[3]

Pythonスクリプトを改造すると、他の種類の絵文字を出力したり、画像サイズの変更もできますので、ぜひお試しください!

脚注
  1. なぜか、2文字だけSVGになっているので変換が必要でした。 ↩︎

  2. Pythonのsvglibだと、グラデーションに未対応だったので手動変換が必要でした。 ↩︎

  3. 通常のTextMeshPro - Text(UI)コンポーネントでは、一部の絵文字が正しく表示されないことがあります。 ↩︎

Discussion