sqlite3 C++ラッパーライブラリ「SQLiteCpp」の使い方

りやさんです。
今日はsqlite c++ラッパーライブラリ「SQLiteCpp」を紹介しようと思います。

ゲームではマスターデータ、ユーザーデータ管理のために、ソースコード埋め込みではなくリソースファイルとしてデータを管理することが多いと思います。
形式が何であれ、XML、JSON、バイナリ、SQL…いろいろ選択肢はあります。

今回はSQLについて取り扱っていこうと思います。

SQLは膨大なデータを扱うのに優れていて、スレッドセーフであり、データの検索がとてもしやすい形式です。

C++カジュアルゲームでSQLを使う場合、ライブラリ単体で動作するSQLiteがオススメです。

ただ、公式のC言語向けSQLiteライブラリは、C言語ゆえの手続きじみた面倒な処理を書かなくてはなりません。

openしてclose。ポインタのポインタを渡したり、どこか懐かしい構文です。

prepareでクエリの最適化やバインディングを行う際は

うーん面倒くさい。
プログラマは怠惰で忘れっぽい生き物なので、こういうコードを書いてるといつか破綻しそうです。
オブジェクト指向っぽく書きたい。

そんな人のために、SQLiteには多くのC++ラッパーライブラリが存在します。

その中の一つ、「SQLiteCpp」を紹介します。

SQLiteCpp

非常にシンプルに作られており、とても使い易いライブラリです。

cmakeでビルドする方法が記載されていますが、xcodeではもっと簡単にプロジェクトに組み込めます。

SQLiteCppフォルダをプロジェクトに配置し
SQLiteCpp/include をHeader Serch Pathsに追加して
SQLiteCpp/src/*.cpp、SQLiteCpp/sqlite3.cファイルすべてをCompile Sourcesに追加するだけです。

SQLiteCppの使い方

インスタンスの作り方

コンストラクタやクエリは例外を投げるのでtry-catchした方がいいです。
コンストラクタは第4引数まで持ちます。

第一引数:ファイル名
第二引数:オープンタイプ

OPEN_READONLY : 読み込み専用
OPEN_READWRITE : 読み書き
OPEN_CREATE : なかったら作る
OPEN_URI : URIも許可する

複数のタイプを組み合わせることができます。(例:SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE)
デフォルト値: OPEN_READONLY

第三引数:データベースがロックされている時のタイムアウト時間[ms]
デフォルト値:0

第四引数: VFSの設定
独自のファイルシステムを指定する際に使う。
デフォルト値:空文字列

成功したかどうかさえわかれば良いタイプのクエリを送る(例: CREATE TABLE)

返り値:SQL文で変更された行の数

返り値でSQLITE_OKはかえってきません。
内部でSQLITE_OKかどうかをチェックし、例外を投げる機構が作られてるので、OKチェックをしたい場合はtry-catchしてください。

トランザクションの作成

明示的にトランザクションを使う場合はTransactionクラスを使います。

もしcommitせずにtransactionインスタンスが破棄された場合、デストラクタでロールバックが行われます。

データを取ってくるクエリ

Statementクラスを使います。

エラーをとりたい場合は同様にtry-catchします。

バインドはプレースホルダ「?」を使って行います。
プレースホルダインデックスは1から始まります。注意してください(本家sqlite3は0から)

getColumn関数は対応するインデックスのデータを取得しますが、返り値がColumnというデータラッパクラスです。
プリミティブ型になら何でもキャスト出来る型なので、インデックス等をよく確認するか、isIntegerメソッド等を利用するとデバッグに便利かもしれません。
非explicitキャスト演算子がオーバーロードされているため、以下の3文は等価です。

1行目が暗黙キャスト、2行目がC++風キャスト、3行目がC風キャスト。
すべて内部でgetIntメソッドを呼びます。
他の型も同様です。

また、bindは内部でprepareしてくれます。
内部で扱われてるsqlite_stmtラッパクラスのコンストラクタとデストラクタが、prepare-rest-finalizeをやってくれてるからです。

こんなところでしょうか。

他にもexecAndGetとかisOkとかありますが、結局組み合わせだったり単なる問い合わせやGetterなので解説の必要はないでしょう。

このように、使いづらいC言語ライブラリでも、C++でラップすることによってたちまち使いやすくなります。
オブジェクト指向言語は偉大ですね。

今回はこの辺で。

Cocos2d-xでGame Center(GameKit)、Google Play Games Servicesを使ってランキング機能を実装する

どうも、ころさめです
今回はCocos2d-xでゲーム内のランキング機能を実装したので、メモがてらここに貼っつけていきます
LobiなんかもCocos2d-xに対応していたりするので良いと思いますが、今回のアプリはカジュアルゲームだったので、ユーザー登録が必要ないGame CenterとPlay Games Servicesにしました
また、ここでは実装のみ紹介するので、コンソール側の登録などは以下などを参考にしてください

Game Center機能を実装する – Qiita
Google Play Game Servicesをとりあえず使ってみる(登録編) – Qiita

ちなみにログイン完了時のコールバック処理や認証失敗時のエラーダイアログ表示などは行っていないので、それが気になる方は各自で実装をお願いします

各OS共通

RankingController.h

全OS共通のヘッダです
シングルトンパターンを使っているのでどこからでも呼べるようにしています
各メソッドがvirtualなのはiOSでオーバーライドを行うからです

RankingController.cpp

全OS共通の実装です
getInstanceはiOSのみRankingControllerを継承したインスタンスを生成するためここでは実装しません
また、Android以外のOSでもビルドが通るように何もしないメソッドを用意しています(iOSはオーバーライドで実装)
AndroidのisLogin等の実装はRankingController_Android.cppで行います

iOS

RankingController_iOS.h

iOS用のヘッダです
RankingControllerの継承クラスと、Objective-CのGameCenterControllerクラスを宣言しています
GameKitを使用するので、Build PhasesのLink Binary With LibrariesからGameKit.frameworkを追加してください
GameCenterControllerクラスを用意する理由はGKGameCenterControllerDelegateを使用するためです

RankingController_iOS.mm

iOS用のObjective-C++の実装部分です
GameCenterControllerでは実際にGameKitを使ってログインやスコアの更新、ランキングの表示を行っています
LEADER_BOARD_IDにはiTunes Connectで設定したリーダーボードのIDを入れてください
スコア送信失敗時や未ログイン時のランキング表示失敗などのエラー表示は特に何もしていないので、必要な場合は何かしらの処理を入れてください
また、ランキングの集計期間を1日や1週間にしたい場合は、GKGameCenterViewControllerのleaderboardTimeScopeをGKLeaderboardTimeScopeTodayやGKLeaderboardTimeScopeWeekに変更してください
RankingController_iOSではGameCenterControllerの生成と呼び出しを行っています
ここでRankingControllerのgetInstanceの実装を行うことで、iOSでのみgetInstance時にRankingController_iOSを生成するようにしています

Android

RankingController_Android.cpp

RankingControllerのAndroidでの実装部分です
基本的にはPlayGamesController.javaのメソッドをJNI経由で呼んでいるだけです
PlayGamesControllerはシングルトンなので、staticのメソッドを呼ぶのではなく、PlayGamesControllerのインスタンスを取得してからそのインスタンスのメソッドを呼んでいます

PlayGamesController.java

AndroidのJava側の実装です
この実装を行う前に、PlayGamesServicesAPIを使うためにapp/build.gradleに以下を追加してください

後ろのバージョンはその時のGooglePlayAPIのバージョンに合わせてください
また、GooglePlayDeveloperConsoleで実績を5つ以上、リーダーボードを1つ以上設定した上でリソースを取得し、res/valuesに保存してください
その上で、AndroidManifest.xmlに以下を追加してください

この状態で上記PlayGamesControllerを実装して、R.string.leaderboard_の部分を設定したリーダーボードの名前に書き換えてください
ちなみにこのクラスはActivityではないのでonActivityResultが呼ばれないため、AppActivityに以下を追加してください

これらを実装した上で、C++部分で以下のような実装を追加してください

このようにすれば、iOSであればGameCenter、AndroidであればGooglePlayGamesServicesを使ってランキングの更新、ランキングの表示が行えると思います

Cocos2d-xでgetWritablePath以外のファイルパスを取得する

どうも、ころさめです
前回シェア機能を実装した時に、Androidにおいてシェアする画像の保存先がgetWritablePathだと他のアプリから画像が参照できず、画像のシェアができないといった問題がありました
また、iOSにおいてはデフォルトのgetWritablePathの保存先がDocumentsになっているため、ダウンロードしたファイルをgetWritablePathに保存しているとアプリ申請でリジェクトを食らうといったことがよくあります
なので、保存先のパスを取得する処理を簡単に実装したのでメモがてらここに貼ります

FilePath.h

各OS共通のヘッダです
AndroidにおいてはgetDocumentPathは正確にはgetFilesPathあたりの名前が正しいのですが、共通化するためiOSに合わせてます

FilePath_Apple.mm

iOS、Mac用の実装です
getWritablePathの実装をコピペしてNSCachesDirectoryを選べるようにしただけです
isPublic引数はAndroidのためのものなので無視しています

FilePath_Android.cpp

Android用の実装です
isPublicがtrueならExternalの方のファイルパスを返すようにしています

FilePath.java

AndroidのJavaの実装です
Externalのディレクトリがnullだと空白を返しているので注意
ここら辺適当な実装なので気になる方は各自で修正してください
また、Externalのディレクトリに書き込み・読み込みをする場合、以下の権限が必要になるのでAndroidManifest.xmlに以下を追加してください

おまけのFilePath.cpp

iOS、Mac、Android以外の場合の実装です
ビルドを通すためだけのものなので面倒なのでgetWritablePath返しちゃってます

以下この実装を使ったサンプル

こんな感じでやれば画面のスクリーンショット画像をキャッシュディレクトリに保存してLINEでシェアなんかできたりすると思います

Cocos2d-xでTwitter、Facebook、LINEの簡易的なシェア機能を実装する

どうも、ころさめです
なんかプロジェクトが変わるたびにいつも同じようなコードを書いている気がするので、シェアの機能を実装したついでにここにコードをメモしておきます
ちなみにSDKを使った本格的な実装は面倒だったのでopenURLやintentなどを使った簡易的なものになります
まずは基本のクラスから

Share.h

とりあえずシェアするものはメッセージと画像とURLにしています
LINEはそれぞれ画像かメッセージかのどちらか片方しか送れないので一つずつ関数を用意しています

Share.cpp

LINEのメッセージ送信のみiOSとAndroidで実装を共通化可能なためここで実装しています
LINEのメッセージはURLエンコーディングする必要があるのでエンコードしてからopenURLを叩いています
URLエンコードの実装はほぼStackOverflowからパクっています
iOSとAndroid以外では一応ビルドエラーが出ないようにしています(英語は適当)

ここから下はOSごとの実装になります

Share_iOS.mm

iOS側のObjective-C++の実装になります
TwitterとFacebookのシェアではSLComposeViewControllerを使っているため、必ずSocial.frameworkをBuild Phasesから追加してください
LINEの画像のシェアはline://のURLスキームでopenURLから行いますが、iOSの場合はペーストボード名をURLに含める必要があります

Share_Android.cpp

Android側のJNI他の実装になります
このファイルはiOSのビルド対象に含めないでください
AndroidのTwitterとFacebookのシェアはIntentを使ってシェアしますが、Facebookはメッセージのシェアは行えないので、画像かURLがあった場合のみシェアを行います
AndroidのLINEはURLにファイルパスを含めるだけで画像のシェアを行えます
ただ、Androidの画像のシェアはgetWritablePathで取得するファイルパスが他のアプリから参照できないため、Cocos2d-xで保存した画像をシェアする場合はファイルパスを工夫する必要があります

Share.java

AndroidのJNIで呼ぶJavaの実装になります
TwitterはURLがある場合はメッセージの後ろに空白を入れてURLを入れています
Facebookは画像とURLが両方ある場合は画像を優先してシェアするようにしています

これで一応一通りシェアが可能になるかと思います
最初はgithubで公開しようかと思ったのですが、既に似たようなものがあったのでやめました
Androidの画像のファイル保存先の取得については次の記事でやろうと思います

階層型ステートマシンによるステートデザインパターン

新卒のりやさんです。

こいつブログばっか書いてんな、もしかして暇なのか? とか思わないでください。

今回は、ゲームプログラミングの中でもとても重要なデザインパターン、ステートデザインパターンについて紹介しようかと思います。

通常、クラスというものは「属性」と「振る舞い」の二種類の要素を兼ね備えています。
一般に、データとインターフェースと呼ばれるものですね。

クラスは自身の状態を保持し、きっとカプセル化されたデータに応じて要求された操作に対し適切な振る舞いをすることでしょう。
そのようなコードは列挙体とswitch-caseで簡単に表現できます。

例として、村によくいる住人のオブジェクトを考えます。
住人は、走ったり、歩いたり、或いは居眠りをする挙動を考えます。

この程度のコードならまあ、これでいいんでしょうが、あまりにも拡張性に欠けます。
状態(State)は今3つだけですが、当然追加が発生することもありますし、遷移の順番も変わるかもしれません。

さらに、状態というものはネストすることがあります。例えば、「機嫌が良い/悪い」という状態が加わったらどうでしょう。「歩いている」と「機嫌が良い」は同時に発生しうる状態であり、両方とも別変数で管理し、switch-caseをネストさせて条件分岐させなくてはなりません。

そんなswitch-caseのコードのメンテナンスをしていると、とんでもないことになります(なりました)。

具体的にどうするべきかというと、「状態」というものをクラスにしカプセル化しましょう。
NPCクラス自身が次の状態は何か、今どうするべきかを判断する構造は良くないです。

NPCクラスが呼び出すのはStateのupdateだけ。
あとは勝手に状態遷移をやってくれそうな感じです。

ステートデザインパターンとしてはまだまだ実装すべき項目がありますが、コンセプトとしてはこの方針で問題なさそうです。

私が作成するステートマシンの基本要件は以下。

 

ステートマシンクラス

      1. ステートマシンは必ず「現在のステート」を持つ

2. ステートマシンは階層構造になっており、ステートマシンには親ステートマシンや子ステートマシンが存在する場合がある

3. ステートマシンは特定の間隔(ゲームであるならば、毎フレームが望ましい)でステートに次の遷移先ステートを問い合わせ、状態が更新される

4. 遷移先ステートが同一である場合、遷移は行わないと定義する(本来ステートマシンの元となる有限オートマトンにおいては、この操作は「自己遷移」と呼ばれるものだが、省略する)

5. ステートの遷移が行われた場合、遷移前のステートのイグジット処理と、遷移後のステートのエントリー処理を実行する

6. ステートマシンはイベントの通知を受けた時、現在のステートに処理すべきかどうか問い合わせる。この操作は、イベント処理するステートが見つかるまで子ステートマシンより再帰的に行われる

ステートクラス

      1. ステートは「次のステート」を持つ

2. ステートは「イベント」と「アクション/トランジション」のマップを持つ。ステートマシンより問い合わせが来た時、該当するイベントアクションが存在する場合、実行する

3. アクションは関数の実行、トランジションは遷移の発生である。ただし、拡張性のため、トランジションにはファクトリデザインパターンを採用する

 

そんなこんなで、実装したステートマシンが以下.
(業務のために本格的に作成したものですので、少し大規模になっています)

ステートマシンはupdate関数によってステートの状態を毎回チェックしています。
返ってきたステートが同一ではない場合、ステートの遷移を行います。
その際、ステートのentry関数とexit関数を実行します。

また、dispatchEvent関数によってイベントの処理を行います。
子ステートマシンに先に問い合わせた後、イベントが処理されるまで再帰的に実行されます。

また、メモリの安全性の考慮し、スマートポインタを使用します。

ステートクラスはアクションマップとトランジションマップを持ち、該当するイベントをprocessEvent関数によって実行します。
_nextメンバは次の遷移先ステートを表しており、update関数によって次のステートの問い合わせを受けた場合、_nextが存在するなら_nextを、存在しないならば自身を返します。

こんな感じで、実際にNPCが歩くだけのプログラムを作ってみました。
リポジトリを置いとくので、ステートマシンの使い方の参考がてらどうぞ!
cocos2dx v3.11が必要です。

https://github.com/Riyaaaaa/StateMachineSample

実行結果

ZjItNIl46X

こいつ…動くぞ!

素材提供者 直江ジャスティス様
(https://twitter.com/justicenaoe)

【Android】CrystaX NDKでgcc5を使いC++14のコードをコンパイルする【Cocos2dx】

どうも、ぺーぺーの新人のりやさんです

ちょこっとCosos2dxのゲームのAIを作る上でコンパイル時計算を使っていたら

コンパイラにconstexpr関数でnot return statementを使っていると怒られてしまいました。

Android NDKのgccにおいてc++14の機能は使えるものと使えないものがあることがわかり、gccのバージョンを調べてみると

なんと4.9! 最新のAndroid NDKでもこれは変わらず!

Relaxing requirements on constexpr functions:(constexpr関数の制限緩和)

に対応しているのはgcc5からなんですよね…

この機能によってconstexpr関数は

  • globally-visible side-effects, such as modification of an object of static storage duration (other than the object being constructed, if any);

  • expressions which cannot be evaluated during translation, such as dynamic memory allocation, comparisons with unspecified results and lvalue-to-rvalue conversions on objects which are neither constant nor created during the evaluation;

  • non-portable constructs, such as an attempt to violate type-safety or to inspect the underlying storage of the abstract machine (for instance, through a reinterpret_cast), or use of inline asm;

  • invocation of a function which is not constexpr; or

  • undefined behavior.

<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3597.htmlより引用>

(グローバル、スタティックオブジェクトへのアクセス/動的割り当て/型安全性に違反する行為(reinterpret_cast等)、インラインアセンブラの利用/非constexpr関数の呼び出し/未定義動作)
以上に該当しない場合はコンパイル時に評価されます。

つまり副作用がなければ何でもあり。

逆にC++11仕様の場合、関数内にはreturn statement以外かけません。forやwhileは使えないので再帰を駆使、コンテナは独自のイテレータを使う、参照を取れないので複数の値はタプルで返す等、地獄の実装になります。

絶対Cocos2dxでC++14コードをAndroid向けビルドしたい! そんな時は

Android NDKを消しとばしてCrystaX NDKを使いましょう! CrystaX NDK ならgcc5.3に対応しています! しかもgcc6への対応も意欲的!

ただし、推奨環境ではないので色々弄る必要があります。

 

MacOSX 環境でのCocos2dx向けCrystaX構築方法

1. まずbrewを用いてCrystaXをインストールします。

ターミナルを開きます。brewは適宜インストールしてください。

2. ~/.bash_profileを弄ります。

このプロファイルには、皆さんがcocos2dxのsetup.pyを実行した時に入力したNDK_ROOTなるものがexportされているはずです。

AndroidNDKからCrystaXに変えましょう。

 

この辺は環境依存なので自分のbrewのパッケージ保管場所を入力してください。

 

3. android-studioの設定をいじります。(既存のプロジェクトがある場合)

現在のcocos2dxプロジェクトを開いて、タブのFile->Project Structure->SDK Locatinに飛び、Android NDK locationに先ほどと同じパスを入力します。

 

4. コンパイラのバージョンを指定します。
cocos2dxはAndroidNDKを想定しているので、toolchainを4.9に指定しています。

cocos2d-x-3.11.1/tools/cocos2d-console/plugins/plugin_compile/build_android.py

を開き、206行目あたりにある

を4.9から5に書き換えます。

環境によってはまずい感じの変更ですが、慈悲はなし。C++14道は険しいのだ。

 

5. cocos2dxのフレームワークでなぜかビルドエラーが出るので修正します。

YOUR_PROJECT_ROOT/cocos/uiUIWebViewImpl-android.h/cppにおいて

uint32_tがビルドで死ぬので、全部intに書き変えましょう。これもまずそうな変更ですが、おそらく今時32bit環境のAndroidなんてないと思います! きっとintは4byteですよ…

 

さて、ここまでやれば

cocos run -p android –android-studio

で実行です!ビルドが通れば勝ち!

もしerrorが出てきても大丈夫。環境が変わりシンボルリンクがかなり厳しくなります。今errorを吐いているSTLは本当に必要なヘッダをincludeしていますか? functional, type_traits, utility等、使ったSTLの所属するヘッダはしっかりincludeしましょう。

 

return statement以外を持つconstexpr関数を書いてみたり、変数テンプレートを使ってみたり(gcc 5から対応した機能)して、gccのメジャーバージョンが5になってるか確認してみてくださいね。

(ちなみにCrystaX NDKにおけるconstexpr関数の挙動がおかしい感じ…誰か調査してください)

今回はこの辺で!

Cocos2d-x(JS)でCocosStudioのcsdファイル内csdファイルのアニメーションを取得、再生する その2

どうも、エンジニアのころさめです
前回からの続きで、今回は作成したCocosStudioのcsdファイル内csdのNodeのアニメーションをコード上で制御する方法を紹介します

まず、Nodeからアニメーション(ccs.ActionTimeline)を取得するために以下のようなメソッドを用意します

このメソッドを利用して、ccs.loadをしているところで以下のように実装します

この状態でproject.jsonやresource.jsなどを修正してから起動すると、子要素のアニメーションがループ再生されるのが確認できると思います

なぜこれで子要素のアニメーションが取得できるかというと、ccs.loadをした際に読み込むcsd(json)データが、CocosStudio v2以降のものであれば、timelineParser-2.x.jsが呼ばれ、そこで以下のような処理をしているからになります

読み込んだactionをnodeのタグと同じにした上でrunActionしているのがわかると思います
そのため、nodeからnodeのタグでgetActionByTagをするとccs.ActionTimelineが取得できるというわけです

これを応用して、以下のようなccs.loadのwrapperを用意すると非常に捗ります

こうすることにより、nodeとactionを分けずに扱えるため、actionが必要になった時だけutils.getActionTimeLineを呼ぶといった使い方ができるようになるため、オススメです

まだCocos2d-x(JS)とCocosStudioの両方を使って開発する人は少ないですが、慣れればC++やCocosBuilderよりもより早く開発できるようになるので、試してみる価値はあると思います

Cocos2d-x(JS)でCocosStudioのcsdファイル内csdファイルのアニメーションを取得、再生する その1

どうも、スパイラルセンスに入社してから1年半ぐらいのエンジニアのころさめです
直近の仕事がなくなったため社内ニートしようと思ってたらブログを書けと言われて渋々書いております
一応これまでCocos2d-xを使った開発に2年半ほど携わってきたので、私の記事はCocos2d-x関連の記事が多くなると思います

さて、最近まで私はCocos2d-x(JS)とCocos Studioを使って10ヶ月ほど開発を行っていました
ただ、Cocos2d-x(JS)用の新しいエディタであるCocos Creatorが発表されたにも関わらず、これまでにCocos StudioとCocos2d-x(JS)を利用した基本的な実装方法についての情報がweb上にほとんどありませんでした
しかしこの前、ようやく非常に参考になる情報が以下に掲載されました

Cocos2d-x (JS) ハンズオン #5 「Cocos StudioとCocos2d-x (JS)との連携」

基本的な実装方法についてはこれで十分なのですが、ちょっと変わったことをしようとするとどこにも情報はなく、自分で試したりCocos2d-xの実装を確認する必要があります
今回はそのうちの一つ、csdファイル内csdファイルのNodeのアニメーションを取得、再生する方法についてになります

まず、CocosStudioで以下のようにアニメーションを設定したcsdファイルを用意します

Icon

次に、アニメーションを設定したcsdファイルを別のcsdファイル表示時にドラッグ&ドロップします

drag

すると、csdファイル内に別のcsdファイルを子要素のNodeとして持たせることができます
Begin to record frameにチェックを入れて、この子要素のNested Actionという項目を設定すると、親のアニメーションの特定のタイミングで子要素をアニメーションさせることができます

nested_action

しかし、これだと親のアニメーションの時間が子要素より短い場合、子要素のアニメーションが途中で止まってしまったり、クリックイベントのタイミングでアニメーションを再生させたい場合などの時に、複雑なタイムラインになっていまいます

そのため、この子要素のアニメーションの再生をコード上で制御する必要があるのですが・・・長くなったので続きは次の記事で

WordPressにソースを貼り付けたいのでCrayon Syntax Highlighterを導入する

マッサンです。

技術系の記事をモリモリ書こうと思っておりますので、ソースコードを貼り付けることが多いかと思います。そこで、Crayon Syntax Highlighterを導入しました。

重いので軽い別のものにしたほうがよい、という記事を見かけましたが、かんたんに導入してみたかったのでこれにしました。

私は学生時代にC言語について学び、そこを源流としてきました。なので他の言語についてもCライクに記述することでものにしようとしてきました(今はもうちょっと進歩的です)。

こういった機能を使うとコピペしやすいので重宝しますね。