· 

個人開発でQRコードを扱う話【Unity】

■最初に…

こんにちは、Twitter(@hayadebi)で個人開発の活動をしている"ハヤデビ"と申します。

(まだまだ未熟な開発者です)

 

この記事では、個人開発でQRコード・Webカメラを使用した際に少し困ったことや、実際に使っているスクリプト・参考にしたサイトなどについての小話をしていきます。

また、完成後に公開する際、公開先ごとの必須事項・注意点(DLsiteやFreem等)も書きまとめてみます。

QRコードを使用して実際に作ったゲーム▼



話す順番:

1.困った事

2.参考サイト・使用スクリプト

3.公開先ごとの必須事項・注意点

 

4.終わりに



1.困った事

おもに困った事としては、

・PC"ゲーム開発"に適した参考サイトが少なかったこと

・QRコードを扱った作品で生じる、公開先ごとの必須事項と修正作業

です。

 

私が開発しながら調べた感じだと、

難しく技術的に書かれてるものは多くあっても、大半は初心者のゲーム開発だと扱いづらい印象でした。

 

なので以降からは、そういった事を解決できるように話していきます。

(自分でなるべく考えて書きたいから私は使わないけど、そういうこだわりが無い人はぶっちゃけChatGPTに頼れば良いと思う)


2.参考サイト・使用スクリプト

どんな開発でもそうですが、ひとつを参考にするだけだと解決は難しいと思います。

なので私の場合は、上記をそれぞれ照らし合わせ、嚙み合うように自分流で書いてます。

 

(※下記の中には、開発作品専用の汎用ではない変数もあるため、コピペだけでは実装できません。

コピペだけで解決したい場合は、上記参考サイトから)

 

実際に使用しているスクリプト達▼

QRコードのマネージャー的な役割

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using ZXing;
  5. using ZXing.QrCode;
  6. public class QRCodeHelper
  7. {
  8.     //参考サイト3.にて
  9. }

②QRコードから読み取ってファイル操作

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. using System;
  6. using System.Linq;
  7. using System.IO;
  8. using UnityEngine.SceneManagement;
  9. public class qr_imgread : MonoBehaviour
  10. {
  11.     string _result = null;
  12.     public WebCamTexture _webCam;//カメラ
  13.     public RawImage raw;//カメラに映った様子を表示するためのRawImage
  14.     private bool check_trg = false;
  15.     private string global_temp1;
  16.     private string global_temp2;
  17.     public Image btimg;
  18.     public Text bttxt;
  19.     private bool is_start = false;
  20.     public Text qrtxt;
  21.     private bool not_lost = false;
  22.     public FileManager fmanager;
  23.     private bool not_stageqrtrg = false;
  24.     public void ClickQR()
  25.     {
  26.         StartCoroutine(nameof(QRStart));
  27.     }
  28.     public IEnumerator QRStart()//カメラ起動
  29.     {
  30.         yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
  31.        
  32.         btimg.enabled = false;
  33.         bttxt.enabled = false;
  34.         GManager.instance.setmenu = 1;
  35.         if (Application.HasUserAuthorization(UserAuthorization.WebCam) == false)
  36.         {
  37.             Debug.LogFormat("no camera.");
  38.             yield break;
  39.         }
  40.         Debug.LogFormat("camera ok.");
  41.         WebCamDevice[] devices = WebCamTexture.devices;
  42.         if (devices == null || devices.Length == 0)
  43.             yield break;
  44.         _webCam = new WebCamTexture(devices[0].name, Screen.width, Screen.height, 12);
  45.         raw.texture = _webCam;
  46.         _webCam.Play();//このカメラ起動と似たような感じでStop()で止めれる
  47.         is_start = true;
  48.     }
  49.     void Update()
  50.     {
  51.         if (is_start && !check_trg && _webCam != null)
  52.         {
  53.             _result = QRCodeHelper.Read(_webCam);//QRかどうか判断するための
  54.             if (_result != null && QRCodeHelper.Read(_webCam) != null && _result != "error" && !check_trg)//QRだった場合に条件を通させる、エラーの時は通らない
  55.             {
  56.                 print(_result);
  57.                 string tmp = _result;
  58.                 System.IO.StringReader rs = new System.IO.StringReader(tmp);
  59.                 string line = null;
  60.                 int check_line = 0;
  61.                 bool stage_qrtrg = false;
  62.                 while ((line = rs.ReadLine()) != null)//本作では1行読み込んでステージデータかどうか判別してる
  63.                 {
  64.                     if (check_line == 0 && line == "stage")
  65.                         stage_qrtrg = true;
  66.                     else if (check_line == 0 && line != "stage" && !not_stageqrtrg)
  67.                     {
  68.                         not_stageqrtrg = true;
  69.                         GManager.instance.setrg = 1;
  70.                         if (GManager.instance.isEnglish == 0)
  71.                             qrtxt.text = "<color=red>ステージ専用の</color>QRコードを読み込んでね!";
  72.                         else
  73.                             qrtxt.text = "Read the QR code for the <color=red>stage</color>!";
  74.                     }
  75.                     check_line += 1;
  76.                 }
  77.                 if (stage_qrtrg)//ステージデータなら
  78.                 {
  79.                     check_trg = true;
  80.                     // 書き込み
  81.                     string path = Application.persistentDataPath + "/stage00.txt";
  82.                     bool isAppend = false; // 上書き or 追記
  83.                     using (var fs = new StreamWriter(path, isAppend, System.Text.Encoding.GetEncoding("UTF-8")))
  84.                     {
  85.                         fs.Write(tmp);
  86.                     }
  87.                     GManager.instance.setrg = 6;
  88.                     Instantiate(GManager.instance.all_ui[0], transform.position, transform.rotation);
  89.                     _webCam.Stop();
  90.                     _webCam = null;
  91.                     Invoke("SceneChange", 1);//書き込み後ステージシーンへ飛ぶ
  92.                 }
  93.             }
  94.         }
  95.     }
  96.     void SceneChange()
  97.     {
  98.         GManager.instance.setmenu = 0;
  99.         GManager.instance.walktrg = true;
  100.         GManager.instance.storymode = false;
  101.         GManager.instance.debug_trg = false;
  102.         GManager.instance.over = false;
  103.         GManager.instance.goal_num = 0;
  104.         SceneManager.LoadScene("loadstage");
  105.     }
  106.     private void Start()
  107.     {
  108.         if (SceneManager.GetActiveScene().name == "qrstage")
  109.         {
  110.             StartCoroutine(nameof(QRStart));
  111.         }
  112.     }
  113. }

QR画像作成後に共有ツイート

  1. using UnityEngine;
  2. using ZXing;
  3. using ZXing.QrCode;
  4. using System.IO;
  5. using System;
  6. using System.Collections;
  7. using System.Text;
  8. using UnityEngine.Networking;
  9. using UnityEngine.Events;
  10. //前提知識:参考サイト5
  11. public class qr_create : MonoBehaviour
  12. {
  13.     public static qr_create instance = null;
  14.     //public string content = "";
  15.     public string[] tags;
  16.     [Multiline]
  17.     public string qr_content = "";
  18.     [Multiline]
  19.     public string tweets = "";
  20.     
  21.     private void Start()
  22.     {
  23.         //if(content != "")
  24.         //{
  25.         // StartCoroutine(nameof(CreaterQR));
  26.         //}
  27.     }
  28.     public void outputread()//呼び出すことでQRコードを作成し、ツイートする
  29.     {
  30.         string path = Application.persistentDataPath + "/stage00.txt";
  31.         using (var fs = new StreamReader(path, System.Text.Encoding.GetEncoding("UTF-8")))
  32.         {
  33.             string tmp = fs.ReadToEnd();
  34.             qr_content = tmp;
  35.         }
  36.         StartCoroutine("CreaterQR");
  37.     }
  38.     public IEnumerator CreaterQR()
  39.     {
  40.         // 保存するQRコードの画像ファイル名
  41.         var path = Application.dataPath + "/QRCode.png";
  42.         // QR コードの画像の幅と高さ
  43.         var width = 256;
  44.         var height = 256;
  45.         var writer = new BarcodeWriter
  46.         {
  47.             Format = BarcodeFormat.QR_CODE,
  48.             Options = new QrCodeEncodingOptions
  49.             {
  50.                 Width = width,
  51.                 Height = height
  52.             }
  53.         };
  54.         var format = TextureFormat.ARGB32;
  55.         var texture = new Texture2D(width, height, format, false);
  56.         var colors = writer.Write(qr_content);
  57.         texture.SetPixels32(colors);
  58.         texture.Apply();
  59.         var bytes = texture.EncodeToPNG();
  60.         var imageBase64 = Convert.ToBase64String(bytes);
  61.         // Form Dataの作成
  62.         var formData = new WWWForm();
  63.         formData.AddField("image", imageBase64);
  64.         //imgurのリクエスト作成
  65.         var request = UnityWebRequest.Post("https://api.imgur.com/3/image", formData);
  66.         request.SetRequestHeader("AUTHORIZATION", "Client-ID " + "dd103802824b5f9");
  67.         // リクエスト実行
  68.         yield return request.SendWebRequest();
  69.         var response = JsonUtility.FromJson<Response>(request.downloadHandler.text);
  70.         string tempurl = response.data.link;
  71.         tempurl = tempurl.Remove(tempurl.Length - 4, 4);
  72.         tweets += tempurl + "%0a";
  73.         StartCoroutine(TweetWithScreenShot.TweetManager.TweetWithScreenShot(tweets));//参考サイト5のを呼び出してツイート
  74.     }
  75.     // アップロードAPIのレスポンスデータ(必要分のみ定義)
  76.     [Serializable]
  77.     private struct Response
  78.     {
  79.         [Serializable]
  80.         public struct Data
  81.         {
  82.             // アップロードされた画像URL
  83.             public string link;
  84.         }
  85.         public Data data;
  86.         public bool success;
  87.         public int status;
  88.     }
  89. }

Ads Image


①の重要部分は先人方の参考サイトに頼るとして…

以下は試行錯誤の際に困った箇所のみを抜粋します。

まずは②のQRコード読み取りについて見ていきましょう。

・13行目のpublic RawImage raw;には、あらかじめシーン内で作成していたRawImageをいれてください。

これでカメラに映った様子を、UIとして見えるようにできます。

(初見困り度Lv.1)

・46行目の_webCam.Play()でカメラが完全に起動します。

Play()と似たような感じで、Stop()でカメラを止めれます。

カメラの停止は目的のQRコードを読み込んだら、またはシーン移動前に絶対行いましょう。

Webカメラがつきっぱなしの状態だと、あまりよろしくないからですね。

(初見困り度Lv.2)

(ちなみに、②ではQRコード読み取り後にステージデータの準備をしています)

 

次は、③のQRコードの画像作成について見ていきます。

QR部分は良いのですが、imgurやツイート部分に関しては参考サイト5が前提知識となります。

public IEnumerator CreaterQR()でQRコード画像の作成と、imgurを利用して画像をツイートするようにしてます。

本来の参考サイト5ではゲーム画面をツイートするという趣旨のようですが、私の場合はステージデータのQRコードを共有するためにツイートさせるので…

作成したQRコード画像をBase64で変換し(60行目辺り)、imgurに対応させて共有ツイートするようにしています。

(初見困り度Lv.3)

 

困った部分を簡単に説明すると以上です。

基本的には参考サイトや自分が思うように書けば良いと思います。

ただ、上記に自分が記したことは実際に試行錯誤して初めて分かった事、苦戦したことなので、

皆さんも苦戦しないように気を付けましょう…(私はエラー地獄でした)

使いこなせれるようになれば、以下のようにバーコードバトラーやモンスターファームみたいな感覚でキャラクターも作れちゃいます▼



3.公開先ごとの必須事項・注意点

次に公開先ごとの必要事項、注意点について話していきましょう。
Webカメラを使用した作品を公開する場合、FreemやDLsiteといった公開先では事前の説明等をしないと、見送られる可能性があります。
よくよく考えるといきなりカメラを開かれるのは怖いですもんね…
私はその配慮が足らず、Freemでは2回見送られ、DLsiteでは確認のメールが来ました。
それと同様で、共有するためにTwitterなどのサイトを開く等の、何かしら通信する行為も事前に説明する必要があります。
説明の参考例画像▼

以下は各公開先での必須項目一覧です▼

公開先のサイト名

カメラ/サイトでの説明

カメラ/同梱READMEでの説明

通信/サイトでの説明

通信/同梱READMEでの説明

ゲーム内で確認画面表示

 Freem  必須 必須 必須 必須 必須
DLsite 必須 任意 必須 任意 任意
Itch.io 任意 任意 任意 任意 任意
BOOTH 任意 任意 任意 任意 任意

Freemは厳しく審査している感じで、ダメな場合は公開を見送られます。

DLsiteは比較的寛容で、ダメな場合は確認メールを送ってくれます。

その時に迅速な対処をすれば審査を通してくれました。

その他itch.ioとBOOTHは基本任意ですが、

ユーザーを安心させたいならちゃんと対処した方が良いでしょう。


4.終わりに

少し面倒だけど、扱えるようになればゲーム開発の幅が広がりそうですよね。

私の開発では、今後も作品によっては使っていくかもしれません。

皆さんもこれを機に使ってみませんか?

 

…以上を持ちまして、今回の小話を終わらせていただきます。

何かまた開発の小話ができそうになったら、新しいのを出すかもしれません。

 

ここまで見てくれてありがとうございました💦

お互いに個人開発を楽しみ頑張りましょう…