【C# + Minecraft】ドット絵をプログラムに描かせる

目的

ドット絵に変換した画像をMinecraft上に描画する

1. 画像を用意する

今回はホロライブの雪花ラミィさんの画像をお借りしました。本人のTwitterにグリーンバックの画像がアップされています。 @yukihanalamy

このままでは解像度が高すぎて描画しきれないので、リサイズします。さらに、リサイズした画像を Minecraft のブロックに置き換える必要があります。色々やり方はあると思いますが、Webサイトに便利なツールがありました。

minecraft-dot.pictures

このツールを使うと、画像をMinecraftのブロックに置き換えたドット絵にしてくれます。変換後は画像の下にある csvダウンロード をクリックして csv 形式のファイルを開きます。1行目および1列目に番号が書かれているので消して、上書き保存しておきます。

2. CSVを読み込む

次に、CSVを読み込むプログラムを作ります。今回は C# のコンソールアプリケーションで作ります。

public class ReadFromCSV
{
    private List<List<string>> PixDataList = new List<List<string>>();

    public ReadFromCSV(string Source)
    {
        if (File.Exists(Source))
        {
            ConvertPixList(Source);
            Console.WriteLine("Convert Success.");
        }
        else
            throw new FileNotFoundException();
    }

    private void ConvertPixList(string Source)
    {
        using (StreamReader sr = new StreamReader(Source, Encoding.UTF8))
        {
            //最後の行まで読み込む
            while (!sr.EndOfStream)
            {
                //カンマで区切ったデータが格納される
                List<string> stringList = new List<string>();

                string line = sr.ReadLine();
                string[] readLine = line.Split(',');

                for (int i = 0; i < readLine.Length; i++)
                {
                    switch (readLine[i])
                    {
                        case "白色の羊毛":
                            readLine[i] = "minecraft:white_wool";
                            break;
                        case "薄灰色の羊毛":
                            readLine[i] = "minecraft:light_gray_wool";
                            break;
                        //ブロックの種類の数だけ分岐させる
                        default:
                            readLine[i] = "minecraft:air";
                            break;
                    }
                }

                stringList.AddRange(readLine);
                PixDataList.Add(stringList);
            }
        }
    }

    public List<List<string>> GetPixDataList()
    {
        return PixDataList;
    }
}

List<List<string>> は2次元のコレクションです。List の中に List を入れると、List<string> そのものを List で管理するので2次元配列のように扱うことができ、画像の水平・垂直方向におけるブロックの種類を保持できます。わざわざ GetPixDataList メソッドからこの List を返している理由は、アクセス制限をかけるためです。

3. 描画のコマンドリストを作る

上記で得られる List の中身は Minecraft のブロックのデータのみで、座標のデータは含まれていません。なので、この List に保持されたブロックを配置したい座標と対応させる処理を書きます。

class Program
{
    //main メソッド

    static async Task Drawing(int x, int y, int z, ReadFromCSV CsvData, Direction direction)
    {
        var CommandList = new List<string>();
        var PixDataList = CsvData.GetPixDataList();

        if (direction == Direction.Horizontal)
        {
            for (int i = 0; i < PixDataList.Count; i++)
            {
                for (int j = 0; j < PixDataList[i].Count; j++)
                {
                    CommandList.Add($"/setblock {x + i} {y} {z - j} {PixDataList[i][j]}");
                }
            }
        }
        else if (direction == Direction.Vertical)
        {
            for (int i = 0; i < PixDataList.Count; i++)
            {
                for (int j = 0; j < PixDataList[i].Count; j++)
                {
                    CommandList.Add($"/setblock {x + j} {y + i} {z} {PixDataList[i][j]}");
                }
            }
        }

        foreach (var item in CommandList)
        {
            await rcon.ConnectAsync();
            Console.WriteLine(item);
            await rcon.SendCommandAsync(item);
            //await Task.Delay(1);
        }

        await Task.Delay(2000);
    }
}

PixDataList に先ほど CSV から変換したブロックリストをコピーし、これを for 文で回します(コピー元が2次元Listなので2重 forが使えます)。座標が必要なので for の要素 i, j を座標にインクリメントして、ブロックを配置する座標を決めます。ちなみに、このメソッドの引数 x, y, z はプレイヤーの座標です。

これで座標と配置するブロックを結びつけられたので、この組み合わせを1つのコマンドとして CommandList に追加していきます。これは CSV の行数・列数(解像度)の分だけコマンドが追加されていきます。あとは、Foreach をつかってこのリストを回しつつ、コマンドを Minecraft に送信していけば描画されます。

メソッドの引数にある Direction は画像を配置する方向です。Horizontal と Vertical の2種類ありますが、それぞれ地上絵か立ち絵かの違いです。

public enum Direction
{
    Vertical,
    Horizontal
}

Vertical の場合、高さが 256 しかないので大きい画像は描画しきれません。従って、今回は Horizontal としています。

4. main メソッド

ドット絵に変換されたCSVをつかってインスタンスをつくり、そのインスタンスDrawing メソッドに代入して描画します。

class Program
{
    static RCON rcon = new RCON(IPAddress.Parse("127.0.0.1"), 25575, "minecraft");

    static void Main(string[] args)
    {
        //絵を描き始める座標
        int x = -850;
        int y = 80;
        int z = 650;

        string csv = @"C:\Users\...\hoge.csv";
        var CsvData = new ReadFromCSV(csv);

        Task.Run(async () => {
            await Drawing(x, y, z, CsvData, Direction.Horizontal);
        }).GetAwaiter().GetResult();
    }

    // Drawing メソッド
}

5. 実行結果

実行結果はこんな感じです。かわいい...。

f:id:takunology:20210201080041p:plain

動いているところはTwitterからどうぞ。

MinecraftConnection ライブラリに組み込むか検討中。できれば画像の変換部分は OpenCV を使って自らの手で実装していきたいですね。

参考サイト

参考になったサイトです。ありがとうございます。

vdlz.xyz