前書き
この記事は、2023のUnityアドカレの12/20の記事です。
今年は、完走賞に挑戦してみたいと思います。Qiita君ぬい欲しい!
TL;DR;
PackageManagerで↓これをインストール
https://github.com/wallstudio/UniQRCode.git?path=QRCodeSource/QRCodeDecoder/QRCodeDecoderLibrary
Texture texture = ...;
string text = await UniQRDecoder.Decode(texture);
Debug.Log(text);
はじめに
端末間でデータの転送をする手段は山ほどありますが、QRコードは、コネクションに必要な情報もありませんし、多人数に一気に読み取ってもらうこともできます。何より、「カメラを向ける」という直感的な選択&読み取り操作が便利です。
ゲーム内コンテンツとして使うのはもちろんですが、デバッグツールとの連携にも大活躍です。例えば、任意のユーザーでログインしたい場合や、ちょっとしたパラメータを流し込みたいなど。手でGUIDやパラメータをテキストとして打ち込む必要がなくなります。
しかし我らがUnityにはQRコードを読み取るAPIはありません。よく使われるライブラリとして、zxingなどが有名ですが、JavaなのでクロスプラットフォームではいくつかのライブラリやネイティブAPIを使い分ける必要があります。
C#フルマネージド
純粋なC#だけで出来たら、クロスプラットフォームのことはUnity層に丸投げすることができます。
そこで良さそうなライブラリを見つけました。
このライブラリは、C#だけで実装されています。なので、これをUnityに…
using System.Drawing.Imaging;
System.Drawing.Imaging
は、Windows専用です…Unityでも参照を追加すれば使うことができますが、Windows上限定、クロスプラットフォームでの実行はできません。
.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のふりをさせます。
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
UniQRDecoder
コンポーネントを適当なGameObjectに張り付けて、テクスチャをアタッチしたら再生。
画面にデコードされた文字列が出力されました。
カメラの入力
UnityにはカメラをそのままTextureとしてくれる、便利なAPIがあります。これをUniQRDecoderに食わせます。
var tex = new WebCamTexture(); // デフォルトのカメラ
await Task.Delay(1000);
var text = await UniQRDecoder.Decode(tex);
Debug.Log(text);
まとめ
Uzi-Granot/QRCodeを使って、C#オンリーでクロスプラットフォーム対応なQRデコードを、Unity上で実現することができました。理論上はSwitchなどでも利用できるはずです。
PackageManagerでぽちっと入れるだけの状態で公開しているので、良ければご活用ください!(MITライセンス)
ライセンス表記
Uzi-Granot/QRCode
# [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