UnityでC#だけでQRを読み込む

# 前書き

この記事は、2023のUnityアドカレの12/20の記事です。
今年は、完走賞に挑戦してみたいと思います。Qiita君ぬい欲しい！

https://qiita.com/advent-calendar/2023/present-calendar#qiita_award

# TL;DR;

PackageManagerで[↓これ](https://github.com/wallstudio/UniQRCode.git?path=QRCodeSource/QRCodeDecoder/QRCodeDecoderLibrary
)をインストール

```
https://github.com/wallstudio/UniQRCode.git?path=QRCodeSource/QRCodeDecoder/QRCodeDecoderLibrary
```

```cs
Texture texture = ...;
string text = await UniQRDecoder.Decode(texture);
Debug.Log(text);
```

# はじめに

端末間でデータの転送をする手段は山ほどありますが、QRコードは、コネクションに必要な情報もありませんし、多人数に一気に読み取ってもらうこともできます。何より、「カメラを向ける」という直感的な選択＆読み取り操作が便利です。

ゲーム内コンテンツとして使うのはもちろんですが、デバッグツールとの連携にも大活躍です。例えば、任意のユーザーでログインしたい場合や、ちょっとしたパラメータを流し込みたいなど。手でGUIDやパラメータをテキストとして打ち込む必要がなくなります。

しかし我らがUnityにはQRコードを読み取るAPIはありません。よく使われるライブラリとして、[zxing](https://github.com/zxing/zxing)などが有名ですが、JavaなのでクロスプラットフォームではいくつかのライブラリやネイティブAPIを使い分ける必要があります。

# C#フルマネージド

純粋なC#だけで出来たら、クロスプラットフォームのことはUnity層に丸投げすることができます。

そこで良さそうなライブラリを見つけました。

https://github.com/Uzi-Granot/QRCode

このライブラリは、C#だけで実装されています。なので、これをUnityに…

```cs:QRCodeSource/QRCodeDecoder/QRCodeDecoderLibrary/QRDecoder.cs
using System.Drawing.Imaging;
```

`System.Drawing.Imaging`は、Windows専用です…Unityでも参照を追加すれば使うことができますが、Windows上限定、クロスプラットフォームでの実行はできません。

https://dotnet.microsoft.com/ja-jp/download/dotnet/6.0

# .NET Standard2.0で動くようにする

.NET Standardは、.NETのサブセットで、あらゆる.NETのランタイムの共通集合的なターゲットAPIです。つまり、.NET Standardの範囲内のAPIしか使っていないのならば、ほぼすべての.NET上で動きますぜ！という規格です。こればっかりは、ちゃんとUnityも含まれています😓


今回はQRコードをデコードしたいだけなので、まずは、余計なクラスファイルを消します。

- Camera.cs
- DirectShowLib.cs
- SampleGrabberHelper.cs

この三つのファイルは不要です。Unity側に同等のAPIがあるからです。

次に、`System.Drawing.Imaging`の顔でもあるBitmapクラスの互換品を作ります。ランタイムに依存しない、「配列」をラップして、Bitmapのふりをさせます。

```csharp
public enum ImageLockMode { ReadOnly }
public enum PixelFormat { Format24bppRgb }

public class Bitmap
{
    public (byte r, byte g, byte b)[] Data { get; }
    public int Width { get; set; }
    public int Height { get; set; }

    public Bitmap(string fileName) => throw new NotImplementedException();

    public Bitmap((byte r, byte g, byte b)[] data, int width) => (Data, Width, Height) = (data, width, data.Length / width);

    public BitmapData LockBits(Rectangle rectangle, ImageLockMode readOnly, PixelFormat format24bppRgb)
    {
        var handle = GCHandle.Alloc(Data, GCHandleType.Pinned);
        var ptr = handle.AddrOfPinnedObject();
        return new BitmapData(handle, ptr, Width * 3);
    }

    public void UnlockBits(BitmapData bitmapData) => bitmapData.Handle.Free();
}

public readonly struct BitmapData
{
    public readonly GCHandle Handle;
    public readonly IntPtr Scan0;
    public readonly int Stride;
    public BitmapData(GCHandle reference, IntPtr scan0, int stride) => (Handle, Scan0, Stride) = (reference, scan0, stride);
}

public readonly struct Rectangle
{
    public readonly int X, Y, ImageWidth, ImageHeight;
    public Rectangle(int x, int y, int imageWidth, int imageHeight) => (X, Y, ImageWidth, ImageHeight) = (x, y, imageWidth, imageHeight);
}
```

あとはコンパイルエラーを起こすusingや、メソッド（使わない）を消したりして完成です。

# Unityへのインポート

ここまでの修正をしたものをGithubに上げましたので、PackageManagerから参照してください。

```
https://github.com/wallstudio/UniQRCode.git?path=QRCodeSource/QRCodeDecoder/QRCodeDecoderLibrary
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/2c47cb11-83ca-9d5a-1b2a-1d8a4b3e45ad.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/179692be-8872-560b-f4c0-0d04d5a0467e.png)

`UniQRDecoder`コンポーネントを適当なGameObjectに張り付けて、テクスチャをアタッチしたら再生。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/ad0f2511-34be-1c55-0397-48d81c1192cc.png)

画面にデコードされた文字列が出力されました。

# カメラの入力

UnityにはカメラをそのままTextureとしてくれる、便利なAPIがあります。これをUniQRDecoderに食わせます。

https://docs.unity3d.com/ja/2021.2/ScriptReference/WebCamTexture.html

```cs
var tex = new WebCamTexture(); // デフォルトのカメラ
await Task.Delay(1000);
var text = await UniQRDecoder.Decode(tex);
Debug.Log(text);
```

# まとめ

[Uzi-Granot/QRCode](https://github.com/Uzi-Granot/QRCode)を使って、C#オンリーでクロスプラットフォーム対応なQRデコードを、Unity上で実現することができました。理論上はSwitchなどでも利用できるはずです。

PackageManagerでぽちっと入れるだけの状態で公開しているので、良ければご活用ください！（MITライセンス）

https://github.com/wallstudio/UniQRCode/tree/main

# ライセンス表記

<details><summary>Uzi-Granot/QRCode</summary>

```
# [Uzi-Granot/QRCode](https://github.com/Uzi-Granot/QRCode/)

The Code Project Open License (CPOL) 1.02

https://www.codeproject.com/Articles/1250071/QR-Code-Encoder-and-Decoder-Csharp-Class-Library-f

https://www.codeproject.com/info/cpol10.aspx
```

</details>