ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity×会計#7-1 言語切替スイッチ抜粋 (Unity 2019.4.4f1)

f:id:tomo_mana:20211220010753p:plain

ボタンを押した時に、文字列が日本語から英語に(またはその逆に)切り替わるようにします。


※今回の記事は、前回の記事からの抜粋です。
tomo-mana.hatenablog.com

構成

言語切り替えボタンと、切り替える対象となるテキスト、ボタンで構成されていることを想定します。

f:id:tomo_mana:20210824063441p:plain
想定される構成(ボタン、テキスト)

言語切り替えのボタンを押した時に、対象のコンポーネントにメッセージが飛んで、言語毎の表示に切り替わるようにします。
メッセージにはExecuteEventsを使用します。

クラス関係

文字列IDと文字列(GlobalString)、メッセージ(ILangSwitchHandler)を受け取るコンポーネントLangSwitchComponent)で構成します。
メッセージを飛ばすボタンはグループにします(LangSwitch)。

f:id:tomo_mana:20210819004358p:plain
クラス図 (LangSwitch)

コンポーネントは、任意のテキストまたはボタンに取り付けることができます。表示する文字は文字列ID(enum NAME_ID)で指定します。
テキストはTextMeshProを想定しています。

オブジェクトの構成

f:id:tomo_mana:20210824070025p:plain
オブジェクト⇒Hierarchy⇒コードの位置

コード

言語テーブル (GlobalString)

public enum LANG {
    LANG_JPN = 0,
    LANG_ENG,
    LANG_MAX
};
public enum NAME_ID {
    NAME_ID_CASH = 0,
    NAME_ID_FLA,
    :
    NAME_ID_MAX
};

public class GlobalString
{
    public class LangNames {
        public string jpn;
        public string eng;
        
        public LangNames(string jp, string en)
        {
            this.jpn = jp;
            this.eng = en;
        }
    };
    static LangNames[] langNames = {
        new LangNames( "現金", "Cash" ),
        new LangNames( "流動資産", "Flex.Assets" ),
        :
    };
    public static LANG selectedLang = LANG.LANG_JPN;
    public static string GetStrLang( NAME_ID id )
    {
        if( selectedLang == LANG.LANG_ENG ){
            return (langNames[(int)id]).eng;
        } else {
            return (langNames[(int)id]).jpn;
        }
    }
}

スイッチ(LangSwitch)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

// メッセージハンドラ
public interface ILangSwitchHandler : IEventSystemHandler
{
    void LangSelected(LANG selectedLang);
}
public class LangSwitch : MonoBehaviour
{
    void ILangSwitchHandlerCallback(ILangSwitchHandler receiver, BaseEventData eventData)
    {
        receiver.LangSelected( GlobalString.GetSelectedLang() );
    }
    void ChangeLanguage( GameObject gameobject )
    {
        // 子から先に(再帰)
        foreach( Transform child in gameobject.transform ){
            ChangeLanguage( child.gameObject );
        }
        // 言語切り替わったメッセージ
        ExecuteEvents.Execute<ILangSwitchHandler>(
            target: gameobject,
            eventData: null,
            functor: ILangSwitchHandlerCallback
        );
    }
    void ButtonClicked(int buttonNo)
    {
        GlobalString.SetSelectedLang( (LANG)buttonNo );
        foreach( GameObject root in gameObject.scene.GetRootGameObjects() )
        {
            if( root.GetComponent<Canvas>() ){
                foreach( Transform child in root.transform ){
                    ChangeLanguage( child.gameObject );
                }
            }
        }
    }
    // Start is called before the first frame update
    void Start()
    {
        int i = 0;
        Button button;
        foreach( Transform child in gameObject.transform ){
            button = child.gameObject.GetComponent<Button>();
            if( !ReferenceEquals(button, null) ){
                int ii = i;
                button.onClick.AddListener(() => ButtonClicked(ii));
                i++;
            }
        }
        ButtonClicked(0);
    }
}

コンポーネント(TextMeshPro または Button - TextMeshPro)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class LangSwitchComponent : MonoBehaviour, ILangSwitchHandler
{
    [SerializeField]
    NAME_ID label = default;
    
    TextMeshProUGUI textArea;
    
    public void SetLanguage()
    {
        if( !ReferenceEquals( textArea, null ) ){
            textArea.text = GlobalString.GetStrLang( label );
        }
    }
    
    public void LangSelected(LANG selectedLang)
    {
        SetLanguage();
    }
    
    // Start is called before the first frame update
    void Start()
    {
        if( (textArea = gameObject.GetComponent<TextMeshProUGUI>()) == null ){
            foreach( Transform trans in gameObject.transform ){
                if( trans.gameObject.GetComponent<TextMeshProUGUI>() ){
                    textArea = trans.gameObject.GetComponent<TextMeshProUGUI>();
                    break;
                }
            }
        }
        if( textArea != null ){
            SetLanguage();
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

コンポーネントは取り付けた後、文字列IDだけ選択しておきます。

f:id:tomo_mana:20210818223634p:plain
LangSwitchComponent - 文字列IDの指定

拡張 (ILangSwitchHandlerインターフェース)

LangSwitch.csLangSwitchComponent が自分の下位層にあるすべてのTextMeshProUGUIを捜索してテキスト領域を書き変える処理をします。同じTMProパッケージでもTextMeshProUGUI でない InputFieldなどには対応していません(継承関係はそのうち調べます)。これらのクラスにも言語切替を通知するには、通知を受け取りたいゲームオブジェクトに ILangSwitchHandler を実装します。void LangSelected( LANG ) インターフェースで、言語が切り替わった(または据え置きかもしれませんが)ことを知ることができます。

課題

●あまり長い文字列向けにはなっていない(容量の最適化)
printf() などで使われるようなフォーマット指定子などは対応していない

(以上)