pkg_resources.VersionConflict: importlib-metadata エラーの対応

pipenv installをしたら、下記のエラーが発生した。

pkg_resources.VersionConflict: (importlib-metadata 4.11.3 (/home/user/.pyenv/versions/3.7.5/lib/python3.7/site-packages), Requirement.parse('importlib-metadata<2,>=0.12; python_version < "3.8"'))

原因はpipenvのバージョンが古いこと。
なので、下記コマンドでpipenvのバージョンを更新すればOK。

$ pip install -U pipenv

参考

Any package requiring importlib-metadata breaks locking · Issue #4476 · pypa/pipenv · GitHub

fishが原因でVcXsrvがError: Can't open display:を返す問題への対処法

概要

VcXsrvというのを使ってWSLからGUIを起動するための設定を行っていた。
これは、matplotlibPySimpleGUIなどを使う時に便利だ。
しかし、どれだけやってもError: Can't open display:と表示されてしまった。
結局、導入していたシェルfishの設定が問題だったことが分かり、解決した。

エラーの糸口

Error: Can't open display:の後には本来、172.17.240.1:0.0のようにディスプレイに接続するポート番号が表示されるはず。
しかし、これが表示されていなかった。
つまり、環境変数$DISPLAYに正常に値が設定されていない可能性が高い。

にも関わらずecho $DISPLAYを実行すると、ちゃんと172.17.240.1:0.0が表示される。
そこで、fishが原因なのではと思い、bashを実行後にecho $DISPLAYすると、何も表示されなかった。
どうもこれがまずそうだ。

解決方法

原因は、bashから見た時の環境変数が変わっていないことだ。
従って、~/.proifle環境変数設定を書き読み込ませることで解決を図る。

まず、↓のコマンドを実行し、デフォルトのシェルをbashに戻す。

$ chsh -s /bin/bash

つづいて、~/.profileに↓を追記。これで環境変数設定後にfishが自動起動される。

export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0
exec fish

※状況によっては~/.profileではなく~/.bash_profileに書く必要がある。
これは、~/.bash_profileがある時は~/.profileが呼ばれない為だ。
この辺は↓の記事を読むと良い。

blog1.mammb.com

Steamworks.NETをUnityに導入する

Steamworksとは

Steamにリリースをする際に利用するデベロッパーツールの名称。 基本的には、https://partner.steamgames.com のこと。

Steamworks SDKとは

Steamworksにビルドをアップロードするなど、ウェブで完結できないことをやる為のツール。ダウンロードして使用する。

Steamworks.NETとは

Steamworksをゲーム内から利用するために必要なツール。
これを通して、実績の解除やユーザデータ取得を行う。

Steamworks.NETをUnityに導入する

下記のサイトに従って進める。 steamworks.github.io

今回は、Unity InstructionsのOption A: Unity packageを利用する。
GitHubリリースページ に移動し、 Steamworks.NET_x.x.x.unitypackageをダウンロード。
これを開き、Unityにインポートする。

Getting Started

続いて、Unity内でセットアップをし、簡単なコードでテストする。 下記サイトに従って進める。

steamworks.github.io

シーンに新規GameObjectを作成し、SteamManager.csをAddCompoenetする。 この状態でゲームを再生すると、SteamのステータスがSpacewarをプレイ中に変わる。
SpacewarはSteamworksにデフォルト設定されているプロジェクト名なので少し気になるが問題ない。

続いて、下記のようなスクリプトを作成し、適当なGameObjectにAddComponent。

using UnityEngine;
using System.Collections;
using Steamworks;

public class SteamScript : MonoBehaviour {
    void Start() {
        if(SteamManager.Initialized) {
            string name = SteamFriends.GetPersonaName();
            Debug.Log(name);
        }
    }
}

すると、コンソールにSteamのプレイヤー名が表示されるはず。
表示されれば、無事にSteamworks.NETの導入は成功だ。

次のステップ

これでAPIをたたく準備は整った。
以降は下記のGetting Startedに書かれているAPIの使い方を理解するのがいいだろう。

steamworks.github.io

一癖あるので、色々調べながらになると思うが、これさえできればほぼ終わりなので踏ん張りどころ。
余裕があればこちらのサイトでも記事を書ければと思う。

【Unity】多言語対応をスプレッドシートで管理するシステム

多言語対応の問題点

UnityではTextMeshProなどを用いて文字を表示できます。
しかし多言語に対応する場合、例えば↓のようなスクリプトを書かなくてはいけません。

var lang = Application.systemLanguage;
if (lang == SystemLanguage.Japanese)
{
    text.text = "ヘンゼルのパンくず";
}
else if (lang == SystemLanguage.English)
{
    text.text = "English";
}

でもこれは大変面倒ですし、今後言語追加された場合など変更に弱いです。
さらに、文字列を企画担当の人が変更できないというのも問題になります。

対応方針

そこで今回は↓のように言語別の文字をにして管理し、

ラベル 日本語 英語
Title ヘンゼルのパンくず breadcrumbs
Menu メニュー Menu
Start ゲームスタート Start Game

スクリプトからは↓のように言語に寄って書き換えが必要ないシステムを作成します。

text.text = LanguageUtil.GetText(LangTextLabel.Title, Application.systemLanguage);

3分クッキング

ご自身で使う際は、コードをコピペしてご利用ください。
まずは、対応する言語リストを定義します。

using UnityEngine;

public static class AvailableLanguageList
{
    public static SystemLanguage[] AvailableLanguages = new SystemLanguage[]
    {
        SystemLanguage.Japanese,
        SystemLanguage.English,
    };
}

続いて、スプレッドシートに入力された言語ラベルをEnumに変換するスクリプトを作成します。
このファイルはEditorという名前のフォルダに入れると良いです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System;

public class LangTextEditorExtention : MonoBehaviour
{
    [MenuItem("多言語対応/言語シート変換")]
    public static void CreateLangEnumFile()
    {
        var resultStr = "";
        resultStr += "public enum LangTextLabel\n{\n";

        //プログレスバー表示
        float progress = 0;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);

        var langTextAsset = Resources.Load<TextAsset>("LanguageText");
        var reader = new StringReader(langTextAsset.text);
        var lineCount = 0;
        while (reader.Peek() != -1)
        {
            var line = reader.ReadLine().Split(',');
            if (lineCount == 0)
            {
                // ヘッダーは読まない
                lineCount++;
                continue;
            }

            var label = line[0];
            var labelSp = label.Split('.');
            for (int i = 0; i < labelSp.Length; i++)
            {
                // 頭文字を大文字に
                var chars = labelSp[i].ToCharArray();
                var initial = char.ToUpper(chars[0]);
                chars[0] = initial;
                labelSp[i] = new string(chars);
            }
            var enumKey = string.Join("", labelSp);
            resultStr += $"    {enumKey},\n";

            lineCount++;
        }

        resultStr += "}";

        progress = 0.5f;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);

        var stream = new StreamWriter($"{Application.dataPath}/Script/AutoGenerate/LangTextLabel.cs");
        stream.WriteLine(resultStr);
        stream.Flush();
        stream.Close();

        AssetDatabase.Refresh();

        LanguageUtil.Init();

        progress = 0.8f;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);

        // チェック
        foreach (SystemLanguage lang in AvailableLanguageList.AvailableLanguages)
        {
            foreach (LangTextLabel label in Enum.GetValues(typeof(LangTextLabel)))
            {
                Debug.Log($"{lang} {label} : {LanguageUtil.GetText(label, lang)}");
            }
        }

        progress = 1f;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);
        EditorUtility.ClearProgressBar();
    }
}

最後に、変換したEnumをもとに文字列を取得するためのユーティリティを作成します。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public static class LanguageUtil
{
    private static bool hasInit = false;
    private static Dictionary<SystemLanguage, Dictionary<string, string>> langDict = null;
    private static SystemLanguage[] availableLanguages = new SystemLanguage[]
    {
        SystemLanguage.Japanese,
        SystemLanguage.English,
    };

    public static void Init()
    {
        langDict = new Dictionary<SystemLanguage, Dictionary<string, string>>();
        foreach (SystemLanguage lang in availableLanguages)
        {
            langDict.Add(lang, new Dictionary<string, string>());
        }

        var langTextAsset = Resources.Load<TextAsset>("LanguageText");
        var reader = new StringReader(langTextAsset.text);
        var lineCount = 0;
        while (reader.Peek() != -1)
        {
            var line = reader.ReadLine().Split(',');
            if (lineCount == 0)
            {
                // ヘッダーは読まない
                lineCount++;
                continue;
            }

            var label = line[0];
            var labelSp = label.Split('.');
            for (int i = 0; i < labelSp.Length; i++)
            {
                // 頭文字を大文字に
                var chars = labelSp[i].ToCharArray();
                var initial = char.ToUpper(chars[0]);
                chars[0] = initial;
                labelSp[i] = new string(chars);
            }
            var langTextEnumKey = string.Join("", labelSp);

            for (int i = 0; i < availableLanguages.Length; i++)
            {
                var text = line[i + 1];
                langDict[availableLanguages[i]].Add(langTextEnumKey, text);
            }

            lineCount++;
        }

        hasInit = true;
    }

    public static string GetText(LangTextLabel label, SystemLanguage language)
    {
        if (!hasInit)
            Init();

        return langDict[language][label.ToString()];
    }
}

以上で準備完了です。

使い方

ステップ1 スプレッドシートを作成

f:id:tmls:20220306134604p:plain

こんな感じのスプレッドシートもしくはエクセルファイルを作成します。

1列目(ラベル):Enumに変換される言語ラベルです。.を使って文字を区切ることもできます。
2列目以降:各言語に対応する文字列です。言語の順番は`AvalableLanguageListで定義した順にしてください。
備考:対応言語以降の行は自由に使ってください。

ステップ2 スプレッドシートをUnityにインポート

f:id:tmls:20220306135135p:plain

  1. スプレッドシート左上から、ファイル > ダウンロード > カンマ区切り形式(.csv)を選択してください。
  2. ダウンロードされたファイルをAssets/Resourcesフォルダに入れてください。
  3. ファイル名をLanguageTextに変更して下さい。

ステップ3 言語シート変換

Unityの上部のバーから、多言語対応 > 言語シート変換を選択してください。

f:id:tmls:20220306133829p:plain

ステップ4

スクリプトから下記の要領で言語別のテキストを取得できます。
EnumキーLangTextLabelはステップ3で自動生成されたものです。

var text = LanguageUtil.GetText(LangTextLabel.MenuStageTitle, Application.systemLanguage);

最後に

なにかあればコメントでお知らせください。

WSLでvim-lsp-settingsを使うと上手くいかないときの対応

問題

vim-lsp-settingsをLSP環境で使用し,:LspInstallServerを実行してもPlease do :LspInstallServer to enable Language Serverが表示される.

解決方法

.vimrcに下記を記述する.

let g:lsp_settings_extra_paths=['/mnt/c/Users/[ユーザー名]']

解説

WSL環境で:LspInstallServerを実行すると,サーバーが/mnt/c/Users/[ユーザー名]/にインストールされる場合があるようだ.
/mnt/c/Users/[ユーザー名]とは,WindowsのCドライブ直下のユーザーディレクトリのことである.

この位置にインストールされてしまうと,vim-lsp-settingsが実行可能ファイルとして認識してくれないため,手動でパスを追加すると解決できる.

しかし,本来であればインストール先を変える対応をすべきであり,その方法については私も調査中だ.

メモ

deinで管理しているプラグイン~/.cache/dein/repos/github.com/にある.
この中のvim-lsp-settingsの中身をいじって今回は解決した.
中身をいじった結果を反映させるには,:call dein#recache_runtimepath()を実行する必要がある.

パッケージリストを消したのにapt updateでFailed to fetchエラーがでる

sudo apt updateを実行した際、Failed to Fetchエラーが発生した。
下記のように、aptのパッケージリストを削除すれば直るという記事もあるが私はダメだった。

qiita.com

ダウンロードサーバーの変更

sudo apt updateで参照するサーバーを変更することで解決した。
Ubuntuであれば、

  1. Software & Updatesを開く
  2. Download fromの項目を選択し、サーバーを選ぶ

サーバーは、日本であればhttp://ubuntutym.u-toyama.ac.jp/ubuntuあたりがよいだろう。

詳しくは以下のページを参考に。

linuxfan.info