20200117のUnityに関する記事は7件です。

スマホアプリ開発者のための2019年動向まとめ

はじめに

2019年は新元号「令和」のスタートやラグビーワールドカップなどなんとなくめでたい年でした。年始まで忙しかったので2020年の1月中旬ですが2019年のスマホアプリ業界をこっそりまとめます。

個人的には2019年はAndroidアプリを中心にネイティブアプリ開発保守をしていました。
スマホアプリ開発に携わっていない人もこの記事を見れば2019年のいろいろが分かるかも。
振り返り用に「ふむふむ」「へー」程度に見ていただければ幸いです。

【前年】
スマホアプリ開発者のための2018年動向まとめ
Unityでスマホアプリ(特にプラグイン)を開発している人のための2017年対応まとめ
Unityでスマホアプリ(特にプラグイン)を開発している人のための2016年対応まとめ

通信回線について

約10年に1度の通信回線アップデート、5Gがついにはじまりました。
2019年4月に米Verizonが開始し、9月に米Qualcommが開催した「2019 Future of 5G」では半年以内に30以上の通信事業者が5Gを開始または開始予定としていることが発表されました。

スクリーンショット 2020-01-15 22.01.12.png

https://www.verizon.com/about/our-company/5g
https://www.qualcomm.com/documents/2019-future-5g-presentation

日本ではdocomo、KDDIが2019年9月にプレサービスを開始し、2020年を目処に商用サービス開始予定です。高速、大容量、低遅延、多接続、低消費電力などがキーワードとしてあげられています。

https://www.nttdocomo.co.jp/corporate/technology/rd/tech/5g/
https://www.softbank.jp/biz/5g/
https://biz.kddi.com/5g/

また、3Gの停波のお知らせを公開し、auは2022年、SoftBankは2024年、docomoは2026年に停波となることが決定されました。これによってガラケーやらくらくホンを使用していたシニア層がスマホに大量流入することが予想されます。

https://www.nttdocomo.co.jp/info/news_release/2019/10/29_00.html
https://news.kddi.com/kddi/corporate/newsrelease/2018/11/16/3428.html
https://www.softbank.jp/mobile/network/3g-end/

端末について

Appleからは例年通り9月にiPhone 11, iPhone 11 Pro, iPhone 11 Pro Maxが発売、Googleからは5月にPixel3a, 10月にPixel4が発売されました。
iPhoneもPixelも基本的にはカメラ機能推しで「夜景モード」という言葉は今年浸透した印象です。

2019年は数少ないですがGalaxyやLGから折りたたみ端末(Foldable端末)が発売されました。Microsoftからは「Surface Duo」というAndroid端末が発表され2020年のホリデーシーズンに発売予定です。

5G対応の端末が2019年から発売され2019年12月現在は約30機種に増えました。2020年は対応機種が増え日本からも発売されることが予定されています。

2019年のスマホサービスについて

決済サービスの増加

個人的に大きいと思ったスマホアプリのニュースは2019年2月のPaypay100億円キャンペーンでした。
各店のPOSも多くの決済方法に対応し、生活が一変しました。
非接触方式に比べるとアプリ立ち上げやバーコード提示など不便な印象でしたが、
増税時のキャッシュバックキャンペーンなども後押しし、バーコード/QRコード決済が増えました。
Paypay、LINE Pay、メルペイ、Origami Pay、Kyash、ファミペイ、Origami Pay、楽天ペイ、セブンペイなどいろんなペイの選択肢が増えました(そして消えたサービスもありました)

スマホゲームについて

2019年は日本の大手企業のリリースが多い年でした。
任天堂からはマリオカートツアー、ドクターマリオワールド、ポケモンマスターズと2019年で3本のリリースがありました。
また、ドラゴンクエストウォークなどの位置ゲームに関して世界的にリリースが多く、Googleからゲーム事例の記事が投稿されています。

魅力あるゲーム体験を実現するGoogle Maps Platformの新機能と最新ゲーム事例
https://developers-jp.googleblog.com/2019/12/new-features-contextualized-gameplay-and-new-games-built-google-maps.html

また、ゲームのサービス形態が変化した年でした。Appleからは定額制サービスのApple Arcade、GoogleからはクラウドゲームのStadiaが発表されサービス開始しました。

子供向けプライバシーの保護の強化

スマホに限らずですが、YouTubeで子供向けプライバシー保護の取り扱いが大きく変更されました。
子供向けであると判断された、あるいはそう申告されたすべてのコンテンツについて、視聴者は無条件で子供と見なされるようになりました。

https://youtube.googleblog.com/2019/09/an-update-on-kids.html
https://youtube.googleblog.com/2020/01/better-protecting-kids-privacy-on-YouTube.html

子供向けプライバシー保護の強化はYouTubeのみでなくGoogle Play Serviceにも実施され、
自分が公開している子供向けアプリもGoogle Play Serviceを実装しているという理由で2019年に突然リジェクトされました。(参考: 2019年以降、GooglePlayの子供向けゲームアプリに○○を実装してはいけない)

iOSも例外ではなく9月に「子ども向け」カテゴリガイドラインが更新されました。
2020年3月3日までは対応の猶予期間となっています。

すべての新規Appはこのガイドラインに従う必要があります。また、できるだけ早く既存のAppを更新することをお勧めします。既存のAppにこのガイドラインを適合するのにさらに時間が必要な場合を考慮し、コンプライアンスに準拠させるために2020年3月3日までさらに6か月の猶予期間を設けることにしました。

iOS

iOS13

iOS 13をプレビュー
https://www.apple.com/jp/newsroom/2019/06/apple-previews-ios-13/
iOS 13 Release Notes
https://developer.apple.com/documentation/ios_ipados_release_notes/ios_13_release_notes

2019年9月19日にiOS13の提供が開始されました。
SwiftUI、ダークモード、SF SymbolsなどUI周りが大きく変更されました。
ただしSwiftUIで開発したアプリはiOS13以降のみの対応となるため、完全移行は数年かかりそうです。
iPad向けのOSがiOSからiPadOSになり、iPad用AppをmacOSへ用意に移植できるProject CatalystもWWDC19で発表されました。

Submit Your iOS Apps to the App Store
https://developer.apple.com/news/?id=09102019a&1568158483
Modernizing Your UI for iOS 13
https://developer.apple.com/videos/play/wwdc2019/224/

iOSアップデートにより、要件も更新されました。
2020年4月からiOS13および3rd-genのiPad Pro対応必須となり、同時に以下を要件としています。

  • LaunchScreenのStoryboard化
  • 複数サイズのサポート
  • 分割スクリーンのマルチタスク化

スクリーンショット 2020-01-15 22.19.12.png

参考:

iOSのその他対応

その他にもPUSH通知、UIWebView受付終了の告知がされています。

Apple Push Notification service(APNs)は、2020年11月以降レガシーバイナリプロトコルをサポートしなくなります。

App Storeは、2020年4月からUIWebViewを使った新しいAppの受付を終了し、2020年12月からUIWebViewを使ったAppのアップデートの受付を終了します。

Android

Android10

ようこそ!Android10
https://developers-jp.googleblog.com/2019/09/android-10.html

2019年9月3日にAndroid10の提供が開始されました。
ジェスチャーナビゲーション対応、5G対応、折りたたみ画面対応、セキュリティ強化などが含まれています。

詳細なこちらへ > Android 10対応したので個人的まとめ (RSSリーダーアプリ編)

APIレベル28(Android 9)以上が必須に

2018年までの必須APIレベルは26でしたが今年は28になりました。
APIレベル28の変更点として、デフォルトのネットワークセキュリティ構成がhttps通信のみ許可になりました。
http通信を行うにはドメインをホワイトリストに追加するなどの対応が必要になります。

2019年のターゲットAPIレベル要件の拡大について
https://developers-jp.googleblog.com/2019/03/2019-api.html

AndroidでのTLS採用に関する最新情報
https://developers-jp.googleblog.com/2020/01/android-tls.html

また、2019年8月以降64ビットが必須要件になりました。
Unity5.6以前を使用したゲームの場合は2021年8月までの延長期間が与えられています。

アプリを64ビット要件に対応させましょう
https://developers-jp.googleblog.com/2019/02/get-your-apps-ready-for-64-bit.html

2年連続でAPIレベルが夏から秋にかけてAPIレベルの底上げを行っています。
例年通りだと2020年も行われることが予想されます。
旧サポートライブラリの最終バージョンが28なので、
APIレベル29対応では必然的にAndroidX対応が必要となります。
余裕があるときにAndroidX対応をしていたほうが良さそうです。

Android関連のドキュメントの拡充

2019年1月にAndroidのCodelabが公開されました。
基礎コースと応用コースが用意され、学習が容易になりました。

https://developer.android.com/courses

AndroidXはドキュメントページで最新バージョンがわかるようになった他、
Feedでのバージョン情報提供もはじまりました。
SlackなどでFeedを登録しておけば最新ライブラリの情報がキャッチ可能になります。

https://developer.android.com/jetpack/androidx/versions
https://developer.android.com/feeds/android-release-notes.xml

Androidのその他

2018年末のKotlin1.3でコルーチンが正式対応となり、Google Developers Blogでもコルーチン関連の記事が公開されました。
プロジェクトの動的モジュール化やコード圧縮ツールR8も注目のトピックとしてあげられています。

また、Project Marbleという以前からのAndroid Studio改善の取り組みが反映された、Android Studio 3.5がリリースされました。
詳細はこちら >Android Studio3.5の変更点をもっと詳しく知りたい..!

2020年春に登場するKotlin1.4

Kotlin 1.4の計画および将来的な展望
https://blog.jetbrains.com/jp/2020/01/16/2731

12月に行われたKotlinConf 2019でKotlin1.4の展望が発表されました。
(ちょうど昨日に日本語版blogが更新されました!)
マルチプラットフォーム化やKotlinのSAM変換についての変更などがあります。

Flutter

2019年はFlutter関連のニュースも多くありました。
6月にFlutter for Web発表、12月にはFlutter単独で技術イベントFlutter Interactが行われ注目度は今年も上がっています。

Flutter Interact
https://developers.google.com/events/flutter-interact

Flutterの追い風(?)でDartもバージョンアップが頻繁に行われ、
2018年末の最新バージョンは2.0でしたが2019年末はDart2.7になり、
コレクション機能や拡張メソッド対応など一年で様変わりしました。
DartPadというDartを気軽に試すことができるツールも登場しました。

DartPad
https://dartpad.dev/

Flutterはコンテストも多く開催し、
5月には5KBで作るFlutter Create、11月には時計を作るFlutter Clockを発表しました。
# Flutter Clockの参加締切は2020年1月20日までなのでまだ間に合います。

React NativeやFlutterのUI構築手法

React NativeやFlutterのUI構築手法はネイティブ開発に影響を与え
2019年に「Jetpack Compose」「SwiftUI」が続けて発表されています。
UIはコードベースでプレビューを確認しながら作るという手法が近くに一般化しそうです。

スクリーンショット 2020-01-17 20.31.13.png
SwiftUI

スクリーンショット 2020-01-17 20.31.13.png
Jetpack Compose

Unity

スマホ関係者はUnite 2019の基礎公演、開発者は開発ロードマップ最新情報のYouTube動画を見るのがおすすめです。
基礎公演40:00ではVR/ARや自動運転、アニメーション活用に関して、
非ゲーム分野でシミュレーターとしてのUnity活用概要があります。

Unite 2019 基礎公演
https://youtu.be/zLQ9oY08p84?t=2405

Unity開発ロードマップ最新情報では直近のUnity更新内容が紹介されています。

Unity開発ロードマップ最新情報
https://www.youtube.com/watch?v=o9EJu4LRIdY

DOTS (Data-Oriented Technology Stack)

DOTS - Unity の新しいマルチスレッド対応の Data-Oriented Technology Stack
https://unity.com/ja/dots

UnityはUnityを根幹から変える取り組みが行われており、DOTSに置き換わります。

  • Entity 1.0 (2020.1〜)
  • DOTS NetCode (2020.1〜)
  • DOTS Audio
  • DOTS Physics
  • DOTS Animation

エディタUIも再設計されビジュルスクリプティング機能もベータ公開予定。
映像向けパイプラインにはPythonサポートされます。
その他として、Serializeが進化してリファレンスがSerialize可能になりましたうれしい。

モバイル向けの変更

モバイル向けに以下がロードマップの動画内で発表されています。
Unity as a LibraryはUnityとネイティブアプリの関係性を大きく変化させそうです。

  • Project Tiny: Instant Runのような仕組みを提供。
  • Unity Distribution Portal: Apple, Google以外のプラットフォームにも複数ゲーム配信ハブシステムも提供。
  • Unity as a Library: ネイティブアプリ向けにUnityで作ったアプリをライブラリとして使用できる機能。

まとめ

2019年はだいたい以下のような感じでした(個人の感想です)

  • 2019年のスマホは決済サービスが流行(増税の影響?)
  • 2019年のスマホゲームは最大手参入と新サービス形態(Apple Arcade, Stadio)。
  • 2019年は子供向けプライバシー強化が大きく行われた
  • 2020年はとにかく5Gを中心に変化
  • ネイティブのUI開発は旧手法・新手法(SwiftUI, Jetpack Compose)・別手法(Flutter, React Native)から選択する必要あり。
  • Unityは新手法のDOTSに改装中

告知

2月のDroidKaigi 2020でビジュアルリグレッションテストについて話します。まだ資料できてません。
https://droidkaigi.jp/2020/accepted/

この記事が参考になったら「いいね」を、あとで読む場合は「ストック」を、投げ銭したくなったらPaypayで以下をおねがいします。
PNGイメージ.png

今年もよろしくおねがいいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MacのUnityで空エラーがでた場合の対処方法(Unity2019.3.0f5)

概要

Unity2019.3.0f5をMacで起動したら、以下のような空のエラーが発生してビルドできなくなりました。

image.png

この投稿は、その対処法になります。

動作環境

  • Unity2019.3.0f5
  • Mac OSX Catalina 10.15.2

Windowsの場合

Windowsの場合も、同じようなエラーが起きてたみたいです。
その場合は以下のやり方でなおるそうです。

https://helpdesk.unity3d.co.jp/hc/ja/articles/360037037212-%E7%A9%BA%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%81%A6%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%8F%E3%81%AA%E3%82%8B%E5%95%8F%E9%A1%8C%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

今回の対応も似たようなやり方です。

本題

とりあえず原因を特定させるためにEditor.logを確認します。

-----CompilerOutput:-stdout--exitcode: 137--compilationhadfailure: True--outfile: Temp/UnityEngine.UI.dll
-----CompilerOutput:-stderr----------
Failed to initialize CoreCLR, HRESULT: 0x80004005
-----EndCompilerOutput---------------

以下のようなエラーがでてた場合Unityの中にある「csc」ファイルが何らかの理由で正常に動作しなくなってるっぽいです。
(本当はそれをなおしたかったけど断念…)

というわけで、応急処置

対象のUnityの「パッケージの内容を表示」とすると、ディレクトリ内部にアクセスできます。
そこから、 Contents -> Tools -> RoslynScripts -> unity_csc.sh を編集します。

image.png

と、その前にMacの中にcscがあるか確認。
このcscは、C#のコンパイラだそうです。

cscがあるか探す
$ find /Library/Frameworks -name csc
/Library/Frameworks/Mono.framework/Versions/5.18.0/bin/csc
/Library/Frameworks/Mono.framework/Versions/5.18.0/Commands/csc
/Library/Frameworks/Mono.framework/Versions/6.6.0/bin/csc
/Library/Frameworks/Mono.framework/Versions/6.6.0/Commands/csc
/Library/Frameworks/Mono.framework/Versions/5.16.0/bin/csc
/Library/Frameworks/Mono.framework/Versions/5.16.0/Commands/csc

ない場合はどうするべきかはわかりません。
あれば、以下のように書き換えたら動くようになります。

unity_csc.shの23行目あたり
#eval "\"$CSC_NET_CORE\" /shared "$@"" #コメントアウト
eval "\"/Library/Frameworks/Mono.framework/Versions/Current/bin/csc\" /shared "$@"" #書き換え

これで、ぼくは動くようになりました。
よかったよかった(´・ω・`)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity][Fee]自作Jsonパーサー似の互換性をチェックする

はじめに

自作ライブラリ開発日記です。
https://github.com/bluebackblue/fee_core

要約

数値

  • decimalは未対応

リスト

  • Generic.Dictionaryはkeyの型がstringの場合のみ対応
  • Collections.ArrayListは未対応

ネスト

  • workpoolで実装しているのである程度まではネストしても大丈夫。

Enum

  • 属性指定で文字列としてJSON化。

  • メンバー変数探査でSystem.IntPtr / System.UIntPtrは除外する。
  • 継承元探査でSystem.Objectは除外する。

1:数値の最大値

最大値が設定されている変数のJSON化

コード

public class Item
{
    public bool value_bool;
    public sbyte value_sbyte;
    public byte value_byte;
    public short value_short;
    public ushort value_ushort;
    public int value_int;
    public uint value_uint;
    public long value_long;
    public ulong value_ulong;
    public char value_char;
    public float value_float;
    public double value_double;
    public decimal value_decimal;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.value_bool = true;
        t_item_from.value_sbyte = sbyte.MaxValue;
        t_item_from.value_byte = byte.MaxValue;
        t_item_from.value_short = short.MaxValue;
        t_item_from.value_ushort = ushort.MaxValue;
        t_item_from.value_int = int.MaxValue;
        t_item_from.value_uint = uint.MaxValue;
        t_item_from.value_long = long.MaxValue;
        t_item_from.value_ulong = ulong.MaxValue;
        t_item_from.value_char = char.MaxValue;
        t_item_from.value_float = float.MaxValue;
        t_item_from.value_double = double.MaxValue;
        t_item_from.value_decimal = decimal.MaxValue;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

value_decimalはlongの範囲内に丸めている。

{
    "value_decimal":9223372036854775807,
    "value_double":1.7976931348623157e+308,
    "value_float":3.4028234663852886e+038,
    "value_char":65535,
    "value_ulong":18446744073709551615,
    "value_long":9223372036854775807,
    "value_uint":4294967295,
    "value_int":2147483647,
    "value_ushort":65535,
    "value_short":32767,
    "value_byte":255,
    "value_sbyte":127,
    "value_bool":true
}

2:数値の最小値

最小値が設定されている変数のJSON化

コード

public class Item
{
    public bool value_bool;
    public sbyte value_sbyte;
    public byte value_byte;
    public short value_short;
    public ushort value_ushort;
    public int value_int;
    public uint value_uint;
    public long value_long;
    public ulong value_ulong;
    public char value_char;
    public float value_float;
    public double value_double;
    public decimal value_decimal;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.value_bool = false;
        t_item_from.value_sbyte = sbyte.MinValue;
        t_item_from.value_byte = byte.MinValue;
        t_item_from.value_short = short.MinValue;
        t_item_from.value_ushort = ushort.MinValue;
        t_item_from.value_int = int.MinValue;
        t_item_from.value_uint = uint.MinValue;
        t_item_from.value_long = long.MinValue;
        t_item_from.value_ulong = ulong.MinValue;
        t_item_from.value_char = char.MinValue;
        t_item_from.value_float = float.MinValue;
        t_item_from.value_double = double.MinValue;
        t_item_from.value_decimal = decimal.MinValue;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

value_decimalはlongの範囲内に丸めている。

{
    "value_decimal":-9223372036854775808,
    "value_double":-1.7976931348623157e+308,
    "value_float":-3.4028234663852886e+038,
    "value_char":0,
    "value_ulong":0,
    "value_long":-9223372036854775808,
    "value_uint":0,
    "value_int":-2147483648,
    "value_ushort":0,
    "value_short":-32768,
    "value_byte":0,
    "value_sbyte":-128,
    "value_bool":false
}

3:文字1

特殊文字が設定されているstringのJSON化

コード

public class Item
{
    /** 文字。
    */
    public string value_string;

    /** ベル。
    */
    public string value_bell;

    /** ヌル。
    */
    public string value_null;

    /** バックスペース。
    */
    public string value_backspace;

    /** ラインフィード
    */
    public string value_linefeed;

    /** タブ
    */
    public string value_tab;

    /** ダブルクォーテーション。
    */
    public string value_double_quotation;

    /** シングルクォーテーション。
    */
    public string value_single_quotation;

    /** バックスラッシュ。
    */
    public string value_back_slash;

    /** キャリッジリターン。
    */
    public string value_carriage_return;

    /** スラッシュ。
    */
    public string value_slash;

    /** ニューページ。
    */
    public string value_new_page;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        //文字。
        t_item_from.value_string = "aあア亜ぁアA\u3042";

        //ベル。
        t_item_from.value_bell = "\a";

        //ヌル。
        t_item_from.value_null = "-\0-";

        //バックスペース。
        t_item_from.value_backspace = "\b";

        //ラインフィード。
        t_item_from.value_linefeed = "\r";

        //タブ。
        t_item_from.value_tab = "\t";

        //ダブルクォーテーション。
        t_item_from.value_double_quotation = "\"";

        //シングルクォーテーション。
        t_item_from.value_single_quotation = "'";

        //バックスラッシュ。
        t_item_from.value_back_slash = "\\";

        //キャリッジリターン。
        t_item_from.value_carriage_return = "\n";

        //スラッシュ。
        t_item_from.value_slash = "/";

        //ニューページ。
        t_item_from.value_new_page = "\f";
    }

Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

対応した「\」「\"」「\n」「\0」「\'」はエスケープ文字が付いているが、それ以外はそのままにしている。

{
    "value_new_page":"?",               //000C そのまま
    "value_slash":"/",                  //002F そのまま
    "value_carriage_return":"\n",       //000A ==> \n
    "value_back_slash":"\\",            //005C ==> \\
    "value_single_quotation":"\'",      //0027 ==> \'
    "value_double_quotation":"\"",      //0022 ==> \"
    "value_tab":"   ",                  //0009 そのまま
    "value_linefeed":"?",               //000D そのまま
    "value_backspace":"?",              //0008 そのまま
    "value_null":"\0",                  //0000 ==> \0
    "value_bell":"?",                   //0007 そのまま
    "value_string":"aあア亜ぁアAあ"       //0061 3042 30A2 4E9C 3041 FF71 0041 3042  
}

4:文字2

「エスケープ文字が設定されているJSON文字列」のインスタンス化

コード

public class Item
{
    /** UTF16。
    */
    public string value_u;

    /** 対応していないエスケープシーケンス。
    */
    public string value_x;
}

public static void Main()
{
    string t_jsonstring = "{\"value_u\":\"\\u3042\",\"value_x\":\"\\x3042\"}";
    Item t_item_to = Fee.JsonItem.Convert.JsonStringToObject<Item>(t_jsonstring);

結果

対応していないエスケープシーケンスはそのまま文字データとして出力している。

    //UTF16。
    t_item_to.value : "\u3042"

    //対応していないエスケープシーケンス。
    t_item_to.value : "\\x3042"

5:Enum

EnumのJSON化

コード

public enum Type
{
    Value = 0,
}

public enum Type_Byte : byte
{
    Min = byte.MinValue,
    Max = byte.MaxValue,
}

public enum Type_SByte : sbyte
{
    Min = sbyte.MinValue,
    Max = sbyte.MaxValue,
}

public enum Type_Short : short
{
    Min = short.MinValue,
    Max = short.MaxValue,
}

public enum Type_Ushort : ushort
{
    Min = ushort.MinValue,
    Max = ushort.MaxValue,
}

public enum Type_Int : int
{
    Min = int.MinValue,
    Max = int.MaxValue,
}

public enum Type_Uint : uint
{
    Min = uint.MinValue,
    Max = uint.MaxValue,
}

public enum Type_Long : long
{
    Min = long.MinValue,
    Max = long.MaxValue,
}

public enum Type_Ulong : ulong
{
    Min = ulong.MinValue,
    Max = ulong.MaxValue,
}

public class Item
{
    /** 型指定なし。
    */
    public Type type;

    /** 型指定。最小値。
    */
    public Type_Byte type_byte_min;
    public Type_SByte type_sbyte_min;
    public Type_Short type_short_min;
    public Type_Ushort type_ushort_min;
    public Type_Int type_int_min;
    public Type_Uint type_uint_min;
    public Type_Long type_long_min;
    public Type_Ulong type_ulong_min;

    /** 型指定。最大値。
    */
    public Type_Byte type_byte_max;
    public Type_SByte type_sbyte_max;
    public Type_Short type_short_max;
    public Type_Ushort type_ushort_max;
    public Type_Int type_int_max;
    public Type_Uint type_uint_max;
    public Type_Long type_long_max;
    public Type_Ulong type_ulong_max;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.type = Type.Value;

        t_item_from.type_byte_min = Type_Byte.Min;
        t_item_from.type_sbyte_min = Type_SByte.Min;
        t_item_from.type_short_min = Type_Short.Min;
        t_item_from.type_ushort_min = Type_Ushort.Min;
        t_item_from.type_int_min = Type_Int.Min;
        t_item_from.type_uint_min = Type_Uint.Min;
        t_item_from.type_long_min = Type_Long.Min;
        t_item_from.type_ulong_min = Type_Ulong.Min;

        t_item_from.type_byte_max = Type_Byte.Max;
        t_item_from.type_sbyte_max = Type_SByte.Max;
        t_item_from.type_short_max = Type_Short.Max;
        t_item_from.type_ushort_max = Type_Ushort.Max;
        t_item_from.type_int_max = Type_Int.Max;
        t_item_from.type_uint_max = Type_Uint.Max;
        t_item_from.type_long_max = Type_Long.Max;
        t_item_from.type_ulong_max = Type_Ulong.Max;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
    "type_ulong_max":18446744073709551615,
    "type_long_max":9223372036854775807,
    "type_uint_max":4294967295,
    "type_int_max":2147483647,
    "type_ushort_max":65535,
    "type_short_max":32767,
    "type_sbyte_max":127,
    "type_byte_max":255,
    "type_ulong_min":0,
    "type_long_min":-9223372036854775808,
    "type_uint_min":0,
    "type_int_min":-2147483648,
    "type_ushort_min":0,
    "type_short_min":-32768,
    "type_sbyte_min":-128,
    "type_byte_min":0,
    "type":0
}

6:構造体

構造体のJSON化

コード

public struct Item
{
    public int value;
};

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.value = 1;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
    "value":1
}

7:Generic.Dictionary(key = string)

Generic.Dictionary(key = string)のJSON化

コード

public static void Main()
{
    System.Collections.Generic.Dictionary<string,int> t_item_from = new System.Collections.Generic.Dictionary<string,int>();
    {
        t_item_from.Add("value_1",1);
        t_item_from.Add("value_2",2);
        t_item_from.Add("value_3",3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.Dictionary<string,int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
    "value_3":3,
    "value_2":2,
    "value_1":1
}

8:Generic.List

Generic.ListのJSON化

コード

public static void Main()
{
    System.Collections.Generic.List<int> t_item_from = new System.Collections.Generic.List<int>();
    {
        t_item_from.Add(1);
        t_item_from.Add(2);
        t_item_from.Add(3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.List<int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    1,
    2,
    3
]

9:System.Array

System.ArrayのJSON化

コード

public static void Main()
{
    int[] t_item_from = new int[3]{
        1,
        2,
        3,
    };

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<int[]>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    1,
    2,
    3
]

10:Ignore

「Fee.JsonItem.Ignore」属性が指定された変数はJSON化しない。

コード

public class Item
{
    [Fee.JsonItem.Ignore]
    public int ignore;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.ignore = 999;
    };

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{}

11:クラスネスト

クラスの中にクラスがある型のJSON化

コード

public class Item
{
    public int value;
    public Item item;
    public Item()
    {
    }
    public Item(int a_value,Item a_item)
    {
        this.value = a_value;
        this.item = a_item;
    }
}

public static void Main()
{
    Item t_item_from = new Item(0,null);
    {
        Item t_current_item = t_item_from;
        for(int ii=0;ii<13;ii++){
            t_current_item.value = ii;
            t_current_item.item = new Item(-1,null);
            t_current_item = t_current_item.item;
        }
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
  "item":{
    "item":{
      "item":{
        "item":{
          "item":{
            "item":{
              "item":{
                "item":{
                  "item":{
                    "item":{
                      "item":{
                        "item":{
                          "item":{
                            "value":-1
                          },
                          "value":12
                        },
                        "value":11
                      },
                      "value":10
                    },
                    "value":9
                  },
                  "value":8
                },
                "value":7
              },
              "value":6
            },
            "value":5
          },
          "value":4
        },
        "value":3
      },
      "value":2
    },
    "value":1
  },
  "value":0
}

12:構造体ネスト

構造体の中に構造体がある型のJSON化

コード

using ITEM_TYPE = Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<Test_12.Item<int>>>>>>>>>>>>;

public struct Item<T>
{
    public int value;
    public T item;
}

public static void Main()
{
    ITEM_TYPE t_item_from = new ITEM_TYPE();
    {
        t_item_from.value = 0;
        t_item_from.item.value = 1;
        t_item_from.item.item.value = 2;
        t_item_from.item.item.item.value = 3;
        t_item_from.item.item.item.item.value = 4;
        t_item_from.item.item.item.item.item.value = 5;
        t_item_from.item.item.item.item.item.item.value = 6;
        t_item_from.item.item.item.item.item.item.item.value = 7;
        t_item_from.item.item.item.item.item.item.item.item.value = 8;
        t_item_from.item.item.item.item.item.item.item.item.item.value = 9;
        t_item_from.item.item.item.item.item.item.item.item.item.item.value = 10;
        t_item_from.item.item.item.item.item.item.item.item.item.item.item.value = -1;
        t_item_from.item.item.item.item.item.item.item.item.item.item.item.item = -1;
    }

結果

{
  "item":{
    "item":{
      "item":{
        "item":{
          "item":{
            "item":{
              "item":{
                "item":{
                  "item":{
                    "item":{
                      "item":{
                        "item":-1,
                        "value":-1
                      },
                      "value":10
                    },
                    "value":9
                  },
                  "value":8
                },
                "value":7
              },
              "value":6
            },
            "value":5
          },
          "value":4
        },
        "value":3
      },
      "value":2
    },
    "value":1
  },
  "value":0
}

13:Generic.Listネスト

Generic.Listの中にGeneric.Listがある型のJSON化

コード

using ITEM_TYPE = System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>>>>>>>;

public static void Main()
{
    ITEM_TYPE t_item_from = new ITEM_TYPE();
    {
        t_item_from.Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>>>>>>());
        t_item_from[0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>>>>>());
        t_item_from[0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>>>>());
        t_item_from[0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>>>());
        t_item_from[0][0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>>());
        t_item_from[0][0][0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>>());
        t_item_from[0][0][0][0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>>());
        t_item_from[0][0][0][0][0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>>());
        t_item_from[0][0][0][0][0][0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.List<int>>>());
        t_item_from[0][0][0][0][0][0][0][0][0].Add(new System.Collections.Generic.List<System.Collections.Generic.List<int>>());
        t_item_from[0][0][0][0][0][0][0][0][0][0].Add(new System.Collections.Generic.List<int>());
        t_item_from[0][0][0][0][0][0][0][0][0][0][0].Add(new int());
        t_item_from[0][0][0][0][0][0][0][0][0][0][0][0] = -1;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<ITEM_TYPE>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
  [
    [
      [
        [
          [
            [
              [
                [
                  [
                    [
                      [
                        -1
                      ]
                    ]
                  ]
                ]
              ]
            ]
          ]
        ]
      ]
    ]
  ]
]

14:Generic.Dictionary(key = string)ネスト

Generic.Dictionary(key = string)の中にGeneric.Dictionary(key = string)がある型のJSON化

コード

using ITEM_TYPE = System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>>>>>>>;

public static void Main()
{
    ITEM_TYPE t_item_from = new ITEM_TYPE();
    {
        t_item_from.Add("list0",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>>>>>>());
        t_item_from["list0"].Add("list1",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>>>>>());
        t_item_from["list0"]["list1"].Add("list2",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>>>>());
        t_item_from["list0"]["list1"]["list2"].Add("list3",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>>>());
        t_item_from["list0"]["list1"]["list2"]["list3"].Add("list4",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"].Add("list5",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"]["list5"].Add("list6",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"]["list5"]["list6"].Add("list7",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"]["list5"]["list6"]["list7"].Add("list8",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"]["list5"]["list6"]["list7"]["list8"].Add("list9",new System.Collections.Generic.Dictionary<string,System.Collections.Generic.Dictionary<string,int>>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"]["list5"]["list6"]["list7"]["list8"]["list9"].Add("list10",new System.Collections.Generic.Dictionary<string,int>());
        t_item_from["list0"]["list1"]["list2"]["list3"]["list4"]["list5"]["list6"]["list7"]["list8"]["list9"]["list10"].Add("value",-1);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<ITEM_TYPE>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
  "list0":{
    "list1":{
      "list2":{
        "list3":{
          "list4":{
            "list5":{
              "list6":{
                "list7":{
                  "list8":{
                    "list9":{
                      "list10":{
                        "value":-1
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

15:System.Arrayネスト

System.Arrayの中にSystem.Arrayがある型のJSON化

コード

public static void Main()
{
    int[][][][][][][][][][][][] t_item_from = null;
    {
        t_item_from = new int[1][][][][][][][][][][][];
        t_item_from[0] = new int [1][][][][][][][][][][];
        t_item_from[0][0] = new int [1][][][][][][][][][];
        t_item_from[0][0][0] = new int [1][][][][][][][][];
        t_item_from[0][0][0][0] = new int [1][][][][][][][];
        t_item_from[0][0][0][0][0] = new int [1][][][][][][];
        t_item_from[0][0][0][0][0][0] = new int [1][][][][][];
        t_item_from[0][0][0][0][0][0][0] = new int [1][][][][];
        t_item_from[0][0][0][0][0][0][0][0] = new int [1][][][];
        t_item_from[0][0][0][0][0][0][0][0][0] = new int [1][][];
        t_item_from[0][0][0][0][0][0][0][0][0][0] = new int [1][];
        t_item_from[0][0][0][0][0][0][0][0][0][0][0] = new int [1];
        t_item_from[0][0][0][0][0][0][0][0][0][0][0][0] = -1;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<int[][][][][][][][][][][][]>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
  [
    [
      [
        [
          [
            [
              [
                [
                  [
                    [
                      [
                        -1
                      ]
                    ]
                  ]
                ]
              ]
            ]
          ]
        ]
      ]
    ]
  ]
]

16:null処理1

nullが設定されている型のJSON化

コード

public class Item
{
    public Item value_class;
    public string value_string;
    public System.Collections.Generic.List<int> value_list;
    public System.Collections.Generic.Dictionary<string,int> value_dictionary;
    public Item[] value_array;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.value_class = null;
        t_item_from.value_string = null;
        t_item_from.value_list = null;
        t_item_from.value_dictionary = null;
        t_item_from.value_array = null;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

nullのメンバー変数は出力しないようにしている。

{}

17:null処理2

要素にnullが設定されているGeneric.List、Generic.Dictionary(key = string)、System.ArrayのJSON化

コード

public class Item
{
    public System.Collections.Generic.List<Item> value_list;
    public System.Collections.Generic.Dictionary<string,Item> value_dictionary;
    public Item[] value_array;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.value_list = new System.Collections.Generic.List<Item>();
        t_item_from.value_list.Add(null);
        t_item_from.value_list.Add(null);
        t_item_from.value_list.Add(null);

        t_item_from.value_dictionary = new System.Collections.Generic.Dictionary<string,Item>();
        t_item_from.value_dictionary.Add("value_1",null);
        t_item_from.value_dictionary.Add("value_2",null);
        t_item_from.value_dictionary.Add("value_3",null);

        t_item_from.value_array = new Item[3];
        t_item_from.value_array[0] = null;
        t_item_from.value_array[1] = null;
        t_item_from.value_array[2] = null;
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

要素がnullの場合は出力するようにしている。

{
    "value_array":[
        null,
        null,
        null
    ],
    "value_dictionary":{
        "value_3":null,
        "value_2":null,
        "value_1":null
    },
    "value_list":[
        null,
        null,
        null
    ]
}

18:継承

継承元にprivateメンバー変数がある型のJSON化

コード

public class Item_Base_Base_Base
{
    readonly public int pub_4;
    readonly protected int pro_4;
    readonly private int pri_4;

    public Item_Base_Base_Base()
    {
        this.pub_4 = 14;
        this.pro_4 = 24;
        this.pri_4 = 34;
    }
}

public class Item_Base_Base : Item_Base_Base_Base
{
    readonly public int pub_3;
    readonly protected int pro_3;
    readonly private int pri_3;

    public Item_Base_Base()
        :
        base()
    {
        this.pub_3 = 13;
        this.pro_3 = 23;
        this.pri_3 = 33;
    }
}

public class Item_Base : Item_Base_Base
{
    readonly public int pub_2;
    readonly protected int pro_2;
    readonly private int pri_2;

    public Item_Base()
        :
        base()
    {
        this.pub_2 = 12;
        this.pro_2 = 22;
        this.pri_2 = 32;
    }
}

public class Item : Item_Base
{
    readonly public int pub_1;
    readonly protected int pro_1;
    readonly private int pri_1;

    public Item()
        :
        base()
    {
        this.pub_1 = 11;
        this.pro_1 = 21;
        this.pri_1 = 31;
    }
}

public static void Main()
{
    Item t_item_from = new Item();

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    Item t_item_to = Fee.JsonItem.Convert.JsonStringToObject<Item>(t_jsonstring);
}

結果

{
    "pri_4":34,
    "pro_4":24,
    "pub_4":14,
    "pri_3":33,
    "pro_3":23,
    "pub_3":13,
    "pri_2":32,
    "pro_2":22,
    "pub_2":12,
    "pri_1":31,
    "pro_1":21,
    "pub_1":11
}

19:Generic.IDictionary(key = string)

コード

public class Item
{
    public System.Collections.Generic.SortedDictionary<string,int> sorted_dictionary;
    public System.Collections.Generic.SortedList<string,int> sorted_list;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.sorted_dictionary = new System.Collections.Generic.SortedDictionary<string,int>();
        t_item_from.sorted_dictionary.Add("value_1",1);
        t_item_from.sorted_dictionary.Add("value_2",2);
        t_item_from.sorted_dictionary.Add("value_3",3);

        t_item_from.sorted_list = new System.Collections.Generic.SortedList<string,int>();
        t_item_from.sorted_list.Add("value_1",1);
        t_item_from.sorted_list.Add("value_2",2);
        t_item_from.sorted_list.Add("value_3",3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
    "sorted_list":{
        "value_3":3,
        "value_2":2,
        "value_1":1
    },
    "sorted_dictionary":{
        "value_3":3,
        "value_2":2,
        "value_1":1
    }
}

20:Generic.Stack

コード

public static void Main()
{
    System.Collections.Generic.Stack<int> t_item_from = new System.Collections.Generic.Stack<int>();
    {
        t_item_from.Push(1));
        t_item_from.Push(2));
        t_item_from.Push(3));
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.Stack<int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    3,
    2,
    1
]

21:Generic.LinkedList

コード

public static void Main()
{
    System.Collections.Generic.LinkedList<int> t_item_from = new System.Collections.Generic.LinkedList<int>();
    {
        t_item_from.AddLast(1);
        t_item_from.AddLast(2);
        t_item_from.AddLast(3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.LinkedList<int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    1,
    2,
    3
]

22:Generic.HashSet

コード

public static void Main()
{
    System.Collections.Generic.HashSet<int> t_item_from = new System.Collections.Generic.HashSet<int>();
    {
        t_item_from.Add(1);
        t_item_from.Add(2);
        t_item_from.Add(3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.HashSet<int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    3,
    2,
    1
]

23:Generic.Queue

コード

public static void Main()
{
    System.Collections.Generic.Queue<int> t_item_from = new System.Collections.Generic.Queue<int>();
    {
        t_item_from.Enqueue(1);
        t_item_from.Enqueue(2);
        t_item_from.Enqueue(3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.Queue<int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    1,
    2,
    3
]

24:Generic.SortedSet

コード

public static void Main()
{
    System.Collections.Generic.SortedSet<int> t_item_from = new System.Collections.Generic.SortedSet<int>();
    {
        t_item_from.Add(1);
        t_item_from.Add(2);
        t_item_from.Add(3);
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<System.Collections.Generic.SortedSet<int>>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

[
    1,
    2,
    3
]

25:Enum(属性指定)

コード

public enum Type
{
    Type_A,
    Type_B,
    Type_C = 100,
}

public class Item_Type
{
    public Type value_type;

    public Item_Type()
    {
    }

    public Item_Type(Type a_value_type){
        this.value_type = a_value_type;
    }
}

public class Item
{
    public Type value_type;

    [Fee.JsonItem.EnumString]
    public Type value_type_int;

    [Fee.JsonItem.EnumInt]
    public Type value_type_string;

    [Fee.JsonItem.EnumString]
    public System.Collections.Generic.List<Type> list_type;

    [Fee.JsonItem.EnumString]
    public System.Collections.Generic.Dictionary<string,Type> dictionary_type;

    [Fee.JsonItem.EnumString]
    public System.Collections.Generic.List<Item_Type> list_item;

    [Fee.JsonItem.EnumString]
    public System.Collections.Generic.Dictionary<string,Item_Type> dictionary_item;
}

public static void Main()
{
    Item t_item_from = new Item();
    {
        t_item_from.value_type = Type.Type_A;

        t_item_from.value_type_int = Type.Type_B;

        t_item_from.value_type_string = Type.Type_C;

        t_item_from.list_type = new System.Collections.Generic.List<Type>();
        t_item_from.list_type.Add(Type.Type_A);
        t_item_from.list_type.Add(Type.Type_B);
        t_item_from.list_type.Add(Type.Type_C);

        t_item_from.dictionary_type = new System.Collections.Generic.Dictionary<string,Type>();
        t_item_from.dictionary_type.Add("value_1",Type.Type_A);
        t_item_from.dictionary_type.Add("value_2",Type.Type_B);
        t_item_from.dictionary_type.Add("value_3",Type.Type_C);

        t_item_from.list_item = new System.Collections.Generic.List<Item_Type>();
        t_item_from.list_item.Add(new Item_Type(Type.Type_A));
        t_item_from.list_item.Add(new Item_Type(Type.Type_B));
        t_item_from.list_item.Add(new Item_Type(Type.Type_C));

        t_item_from.dictionary_item = new System.Collections.Generic.Dictionary<string,Item_Type>();
        t_item_from.dictionary_item.Add("value_1",new Item_Type(Type.Type_A));
        t_item_from.dictionary_item.Add("value_2",new Item_Type(Type.Type_B));
        t_item_from.dictionary_item.Add("value_3",new Item_Type(Type.Type_C));
    }

    Fee.JsonItem.JsonItem t_jsonitem = Fee.JsonItem.Convert.ObjectToJsonItem<Item>(t_item_from);
    string t_jsonstring = t_jsonitem.ConvertJsonString();
}

結果

{
    "dictionary_item":{
        "value_3":{
            "value_type":100
        },
        "value_2":{
            "value_type":1
        },
        "value_1":{
            "value_type":0
        }
    },
    "list_item":[
        {
            "value_type":0
        },
        {
            "value_type":1
        },
        {
            "value_type":100
        }
    ],
    "dictionary_type":{
        "value_3":"Type_C",
        "value_2":"Type_B",
        "value_1":"Type_A"
    },
    "list_type":[
        "Type_A",
        "Type_B",
        "Type_C"
    ],
    "value_type_string":100,
    "value_type_int":"Type_B",
    "value_type":0
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OnPopulateMeshで動的に生成したメッシュが更新されなくて焦った話

はじめに

 内容はタイトルの通りです。Graphicを継承したクラスを作り、uGUIで動的にメッシュを生成しておえかきをしたのはよかったのですが、Inspector上で値を変更するとメッシュに反映されるけど、スクリプト内で変更するとメッシュが更新されなくて焦りました。
 なお、Unityのバージョンは2019.3.0f3です。

動的にメッシュを生成する

 その前に、メッシュを動的に生成したい人へそのやり方を書いていきます。わかる方はここを飛ばしてください。参考資料は以下のものがいいと思います。(ちょっと古いけど)

 まず、Graphicを継承したクラスを作り、Canvasの子孫にあるGameObjectにアタッチします。最低限こんな感じ。

sample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

//RequireComponentで要求しているものは、最低限描写をするのに必要なものなのでつけてる。
[RequireComponent(typeof(CanvasRenderer))]
[RequireComponent(typeof(RectTransform))]
public class Sample : Graphic
{

}

 そして、メッシュを作るにはOnPopulateMeshをオーバーライドします。

sample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(CanvasRenderer))]
[RequireComponent(typeof(RectTransform))]
public class Sample : Graphic
{
    protected override void OnPopulateMesh(VertexHelper vh)
    {

    }
}

 オーバーライドしたOnPopulateMeshの中にメッシュを生成するコードを書くのですが、方法が二種類あります。

三角形を元に生成

 こちらの利点は、最適化をしやすいところで、欠点は三角形に分割するのがやや面倒な点です。

sample.cs
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        /*メッシュや頂点情報を消去。キャッシュできるならいらない。
          パフォーマンス的にもキャッシュできるならしたほうが良い。
          ただし、毎回生成するのであれば頂点数の上限(65000)に達するので必要。*/
        vh.Clear();
        //頂点情報のstruct
        UIVertex v = UIVertex.simpleVert;
        v.color = Color.Red;

        //いい感じの位置に頂点置く
        v.position = CreatePos(0, 0);
        //頂点情報登録
        vh.AddVert(v);

        v.position = CreatePos(0, 1);
        vh.AddVert(v);
        v.position = CreatePos(1, 0);
        vh.AddVert(v);

        //メッシュの三角形生成。引数は登録した頂点のインデックス。
        vh.AddTriangle(0, 1, 2);
    }

    //いい感じの位置変換関数。左下(0,0)、右上(1,1)として入力するとちょうどよく変換される。
    private Vector2 CreatePos(float x, float y)
    {
        Vector2 p = Vector2.zero;
        p.x -= rectTransform.pivot.x;
        p.y -= rectTransform.pivot.y;
        p.x += x;
        p.y += y;
        p.x *= rectTransform.rect.width;
        p.y *= rectTransform.rect.height;
        return p;
    }

四角形を元に生成

 こちらの利点は、やや楽な場合が多く、欠点は最適化できないというところです。

sample.cs
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        /*メッシュや頂点情報を消去。キャッシュできるならいらない。
          パフォーマンス的にもキャッシュできるならしたほうが良い。
          ただし、毎回生成するのであれば頂点数の上限(65000)に達するので必要。*/
        vh.Clear();
        //頂点情報のstruct
        UIVertex v1 = UIVertex.simpleVert;
        v1.color = Color.Red;
        UIVertex v2 = UIVertex.simpleVert;
        v2.color = Color.Red;
        UIVertex v3 = UIVertex.simpleVert;
        v3.color = Color.Red;
        UIVertex v4 = UIVertex.simpleVert;
        v4.color = Color.Red;

        //いい感じの位置に頂点置く。
        v1.position = CreatePos(0, 0);
        v2.position = CreatePos(0, 1);
        v3.position = CreatePos(1, 1);
        v4.position = CreatePos(1, 0);


        //メッシュの四角形生成。四角形を一周する順番の配列でないとおかしなことになるので注意
        vh.AddUIVertexQuad(new UIVertex[]{v1, v2, v3, v4});
    }

    //いい感じの位置変換関数。左下(0,0)、右上(1,1)として入力するとちょうどよく変換される。
    private Vector2 CreatePos(float x, float y)
    {
        Vector2 p = Vector2.zero;
        p.x -= rectTransform.pivot.x;
        p.y -= rectTransform.pivot.y;
        p.x += x;
        p.y += y;
        p.x *= rectTransform.rect.width;
        p.y *= rectTransform.rect.height;
        return p;
    }

原因

 英語でググったらすぐ出てきました。原因は、OnPopulateMeshというのは更新が必要な時にしか実行されず、Inspector上では変数が変更されれば更新が必要なフラグが立つが、シ-ン上では開始時のみ自動でフラグが立ち、以降は変数が変更されてもフラグが立たないということでした。

解決法

 ようは、更新したいときにフラグを立てればよいです。

    SetVerticesDirty();

 上の関数を実行するとフラグが立つようです。
 ただし、一つ気をつけなければいけないことがあり、メッシュ更新中に再度この関数を実行するとエラーが起こるので、適切なタイミングで実行する必要があります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# & Unityで小型衛星姿勢制御ツールを作ってみた①

趣味で宇宙開発を行う団体「リーマンサット・プロジェクト(Ryman Sat Project=rsp.)」がお送りする新春アドベントカレンダーです。
9日目の記事です。インデックス記事はこちら。

こんにちは。超小型人工衛星「RSP-01」の姿勢制御を担当している よねやん(@ytakuro0926)です。

前々からUnityに興味があって、現状の姿勢制御のシミュレーションツールをUnityで実装して姿勢状態を3Dで表現したら色々と分かりやすくなるのではと思いました。
というのも、姿勢制御は最初とっつきにくいので汗
もちろん、ある程度理解している人に向けて定量的な解析にも使うので3Dと一緒にグラフも表示したいと思っています。

コンセプトとしては

小型衛星開発をする学部生が使いやすく且つ実用的であること!

コンセプトにのっとっているため、大型の衛星に需要のある軌道計算やスラスター考慮はいったん拡張開発においておこうと思っています。
軌道計算は同じくリーマンサットのだいごさんにお願いしようかな。。。

当初現在考えていた機能はこんな感じです。

ツール全般機能

 ・CADデータを読み込ませて実際の衛星の見た目で3D表現する。

衛星データ解析機能

 ・衛星から落としてきたジャイロデータを読み込ませてUnityで姿勢を表現する。

姿勢制御シミュレーション機能

 ・衛星のパラメータを入れて姿勢制御のシミュレーションをする。
 ・CADデータを読み込ませてユーザーの物理量の入力を省略する。

この記事を書く前からUnityをインストールしていじっていたのでインストール方法はスキップします。すみません。興味のある方はぐぐってください。私は本買って勉強しています。

さて、Unityの画面はこんな感じ。
unity画面.png

Unityの画面でオブジェクト(画像でいうところの球体や立方体)を作成して、ここに色や物理演算のクラスを設定。
んでそれらのオブジェクトにC#で書いたソースコードを食わせて具体的な動きの指示を与えるらしい。

それで色々とみてみると、オブジェクトの中にカメラがあり、どうやら複数もカメラの設定ができそう。
これなら衛星を見つめるメインカメラと衛星搭載を模擬したサブカメラを同時に表示できそう。

オブジェクトの設定やコンパイルはある程度わかったのでUIの設定を勉強したら実装にうつっていこうかなと思ってます。

んで、軽くUIを勉強してみて、とりあえずデザインのプロトタイプだけさっくりと作成。
プロト画面.png

イメージとしては右側にインプットの項目をおいて、メインに衛星を置いて、その下に定量的なグラフを表示しようかなと。
できたら上の方に衛星に搭載したカメラのビューをおけたらなと思ってみたり。

具体的な実装は次回投稿にしようと思います。

リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。
他では経験できない「宇宙開発プロジェクト」に誰もが携わることができます。
興味を持たれた方は https://www.rymansat.com/join からお気軽にどうぞ。

次回は@OzoraKoboさんの「RSP-00「激レア」開発記 制作の軌跡」です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DoozyUIを使ってボタンの長押し中に定期的にイベントを発行する

はじめに

この記事では次のAssetを利用しています。

DoozyUIで長押しを検知する

DoozyUIを使うと、かなり簡単にボタンの長押しを検知することができます。
これを応用することで、複雑そうな挙動もかなり簡単に実現することができます。

UpDown.gif
(長押し中は定期的にイベント発行を行う例)

長押しイベントをまず取得する

DoozyUIで「長押し」が検知できるようにしてみましょう。

やり方としては次の2ステップ。

1. InspectorViewからOnLongClickを有効にする

UIButtonコンポーネントのOnLongClickの左側のトグルを押して有効化します。
これを有効にしないとスクリプトからイベントが取得できません。

OnLongClick.png

なお、Register Intervalの部分が長押し判定されるまでの秒数です。

2. スクリプトからイベントを取得する

こんな感じでOK。

using Doozy.Engine.UI;
using UniRx;
using UnityEngine;

public class LongClickSample : MonoBehaviour
{
    /// <summary>
    /// DoozyUIのUIButtonコンポーネント
    /// </summary>
    [SerializeField] private UIButton _uiButton;

    void Start()
    {
        // 長押し
        _uiButton
            .OnLongClick
            .OnTrigger
            .Event
            .AsObservable()
            .Subscribe(_ =>
            {
                Debug.Log("OnLongClick!");
            }).AddTo(this);

        // uGUIのButtonはプロパティからアクセス可能
        _uiButton.Button.OnClickAsObservable().Subscribe(_ => { }).AddTo(this);
    }
}

拡張メソッド経由にする

Observable化するのがちょっと冗長なので、次のような拡張メソッドを用意する。

using System;
using Doozy.Engine.UI;
using UniRx;

namespace Samples.Utils
{
    public static class UIButtonBehaviorExtension
    {
        public static IObservable<Unit> AsObservable(this UIButtonBehavior b)
        {
            return b.OnTrigger.Event.AsObservable();
        }
    }
}

そうすると拡張メソッド経由で呼び出せるのでちょっと記述が減る。

using Doozy.Engine.UI;
using Samples.Utils;
using UniRx;
using UnityEngine;

public class LongClickSample : MonoBehaviour
{
    /// <summary>
    /// DoozyUIのUIButtonコンポーネント
    /// </summary>
    [SerializeField] private UIButton _uiButton;

    void Start()
    {
        // 長押し
        _uiButton
            .OnLongClick
            .AsObservable() //拡張メソッド
            .Subscribe(_ => { Debug.Log("OnLongClick!"); }).AddTo(this);
    }
}

長押しをしている間、一定間隔でイベントを発行させる

ボタンを押しっぱなしにしている間は一定間隔でイベントを発行できると便利です。

UpDown.gif

この機能があれば、たとえば「長押しをしている間は連続で数字が変化する」といった機能が簡単に実装できるようになります。

作り方

1. InspectorViewからOnPointerUpを有効にする

ボタンを離したときにイベントを止める必要があるので、OnPointerUpイベントが発行されるようにしておきます。

OnPointerUp.png

2. 拡張メソッド用意する

次のような拡張メソッドを用意します。

using System;
using Doozy.Engine.UI;
using UniRx;

namespace Samples.Utils
{
    public static class UIButtonExtension
    {
        public static IObservable<Unit> OnLongPressAsObservable(this UIButton b, TimeSpan timeSpan)
        {
            var longPress = b.OnLongClick.AsObservable();
            var up = b.OnPointerUp.AsObservable();

            return longPress.Take(1)
                .ContinueWith(_ => Observable.Interval(timeSpan).TakeUntil(up))
                .AsUnitObservable()
                .RepeatSafe();
        }
    }
}

3. OnLongPressAsObservableを使う

あとは拡張メソッド経由でObservableを取得すればOKです。

using System;
using Doozy.Engine.UI;
using Samples.Utils;
using TMPro;
using UniRx;
using UnityEngine;

public class UpDownSample : MonoBehaviour
{
    [SerializeField] private UIButton _upButton;
    [SerializeField] private UIButton _downButton;
    [SerializeField] private TextMeshProUGUI _text;

    private IntReactiveProperty Count = new IntReactiveProperty(0);

    private void Start()
    {
        // 変化した数値をTextに反映
        Count.Subscribe(x => _text.text = x.ToString()).AddTo(this);

        //----------------------------

        // 長押しのUp!!!
        _upButton
            // 拡張メソッド, 引数で長押し中に発行されるイベントの間隔を指定できる
            .OnLongPressAsObservable(TimeSpan.FromMilliseconds(100))
            .Subscribe(_ => Count.Value++).AddTo(this);

        // 長押しのDown!!!
        _downButton
            // 拡張メソッド, 引数で長押し中に発行されるイベントの間隔を指定できる
            .OnLongPressAsObservable(TimeSpan.FromMilliseconds(100))
            .Subscribe(_ => Count.Value--).AddTo(this);

        // ----------------------------

        // 単発 Up
        _upButton.Button.OnClickAsObservable()
            .Subscribe(_ => Count.Value++).AddTo(this);

        // 単発 Down
        _downButton.Button.OnClickAsObservable()
            .Subscribe(_ => Count.Value--).AddTo(this);
    }
}

まとめ

Doozy UIを使うとかなり簡単に各種イベントが取得できるようになります。
そこにUniRxを組み合わせると、だいたいのことは少ないコード量で実現できるのでいいですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでOculus IntegrationのSampleFrameworkを使ってOculus Questのハンドトラッキングを試す (2)

はじめに

前回は、トリガーアクションでキューブの色を変えました。
UnityでOculus IntegrationのSampleFrameworkを使ってOculus Questのハンドトラッキングを試す

今回は、SampleFrameworkのHandsを使って、ピンチ幅の取得してみます。
ついでにAll Valid Tools Tagsを変更し、それぞれどのような動きになるかも試します。

Handsで使えそうなもの

Hands.cs

表示タイプを切り替える
Hands.Instance.SwitchVisualization();

左右取得
Hands.Instance.LeftHand
Hands.Instance.RightHand

Hand.cs

Handsから左右のhandを取得し、それぞれに実行できる

ピンチ幅取得
hand.PinchStrength(OVRPlugin.HandFinger.Middle)

スケルトン表示
hand.PinchStrength(OVRPlugin.HandFinger.Middle)

ピンチ操作

MyHand.csスクリプトを作り、Handsに追加しておく

MyHand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OculusSampleFramework;

public class MyHand : MonoBehaviour
{
    private Renderer renderer;

    void Start()
    {
        renderer = GameObject.Find("CubeButtonRay").GetComponent<Renderer>();
    }

    void Update()
    {
        if (Hands.Instance.LeftHand.PinchStrength(OVRPlugin.HandFinger.Middle) > 0.9f)
        {
            renderer.material.color = Color.black;
        }
        else if (renderer.material.color == Color.black)
        {
            renderer.material.color = Color.white;
        }
    }
}

using OculusSampleFramework で Handsが使える
中指をピンチしたらキューブの色を黒にする
離したら白にする

ダウンロード (7).gif

表示タイプを切り替えてみる

ButtonController の Interactable State Change で Hands の SwitchVisualization を呼び出す

image.png

キューブを選択したら手の表示が切り替わる

ダウンロード (6).gif

All Valid Tools Tags

前回はすべてAllにしていたので、キューブを複製し、Ray,Poke,Pinchをそれぞれ配置してみました

image.png

image.png

All

RayでもPokeでも動く

Ray

ちょっと離れないと選択できない
線が当たると吸い付くので意外と選択しやすかった

ダウンロード (4).gif

Poke

直接触ると発火する
線が当たっても吸い付かない

ダウンロード (5).gif

Pinch

どうやって動くの?

さいごに

あとは自由に作ってみよう!
ハンドトラッキングのメニュー表示をかっこよくしたい。
アバターの移動はどうやって操作しよう。
手を見失ったときの挙動を考えたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む