「でぃーねっく」と読みます。6日目(12/6)の記事です。神戸異人館は何年か前に行ったのですが、確かFateの遠坂邸とかもありましたよね(Rewriteもアニメは見てました)。現状&まとめ終わりにはじめにKMC6年目のdamaです。Twitterと名乗っています。2回生となっているはずが、更に踏み外してフリーランスのようなことをしつつ自分のアプリも作っています。この記事を読むのが面倒な人は、とりあえずアプリだけインストールして行って下さると嬉しいです。Calendarの記事だけを書くのもこれで4年目になりますが、去年はこんな感じでした。今回のタイトルと矛盾していますね。脱Unity今までの記事でマルチプラットフォーム対応ゲームエンジンとしてヨイショしてきたUnityですが、色々あってオサラバすることにしたのでその経緯を綴っていこうと思います。若干話題が逸れますが、↑の去年の記事から引用します。そもそもプライバシーポリシーって何かと言うと、「私はあなたのこういう個人情報をこういう方法で使おうと思ってるんですが、いいですか?」という契約文書です。日本語だと個人情報保護方針というやつですね。法的な話もありますが、とりあえずPlayStoreやAppStoreでは、「アプリが個人情報を扱うならそれについてポリシーを示せ」と決められているのです。Analyticsを使っているので、早い内にポリシーを示すように更新するつもりです。dnekblogいつ削除祭りがあるのか分からないので、自分のためにもユーザーのためにも先んじて対応しておこうと思います。dnekblogそして10月のツイートです。UnambiSweeperのプライバシーポリシー更新をサボっていたのでAndroid版がストアから削除されてしまいました。新規インストールされる方は修正して再配信されるまで暫くお待ち下さい。ニジウメは更新してあるので大丈夫です。誠に申し訳ございませんでした🙇Unityつらいそういうわけで散々更新をサボりまくっていた私も流石にマズいと思い1年ぶりにUnityのプロジェクトを開いたのです。ビルドしてアプリを起動しプレイしようとレベル選択ボタンを押すと、・・・アプリが落ちる。1年前の私はリリース以降何かをイジってそのまま放置していたみたいなのですが、何をどうイジったのか全く分からない。エラーログを読んでも何故落ちるのか分からない。そもそもバージョン管理をしていなかったのが悪いのだが。ProGuard辺りの設定が面倒Androidのタッチレスポンスが若干遅い(マインスイーパーのようなアクションゲーにおいては致命的?)ScrollViewの動きがもっさりしているuGUIが微妙(リップルエフェクトが無いとか)PlayerPrefs(データのセーブ/ロードを扱うクラス)にBooleanが無い(Intで代用していた)などなど後の方はどうでも良いかもしれませんが、こんなものに時間を掛けるよりもネイティブでそれぞれ最適化した方が早く良いものができるだろ、と思いUnityを捨てる決意をした訳です。ちなみに言うと、最近お仕事でSwiftを触っているため学習コストが下がったというのもあります。Kotlin脱UnityということでiOSとAndroidをそれぞれネイティブで作っていくのですが、どちらから作るかといえば当然削除されてしまったAndroidです。今回は色々改めるということでKotlinデビューをすることにしました。Studioでも標準で使えるようになっており既存のJavaコードをKotlinに変換してくれる機能も付いています。一つ個人的に不満な点として、三項演算子がありません。bという感じになります。それでも型推論付き静的型付けとかnull安全とか文字列テンプレートとか総合的にはKotlinが勝っていると思います。1.3からstableになったCoroutinesというものがあります。blog.jetbrains.comこれは「特定のスレッドに束縛されない、中断可能な計算処理インスタンス」で、とりあえず非同期処理が簡単に書けて便利です。私はマインスイーパーソルバーをUIスレッドの外で動かしたり、マインスイーパーソルバー内で並列処理をするのに使っています。描画処理さて、脱Unityにおいて一番問題となるのが描画処理です。Javaでネイティブアプリとして書いていたのですが、当時描画に使っていたSurfaceViewがゴミであることが分かりUnityに移行した経緯があります。様々なView達ここにAndroidでの描画方法が大体まとまっています。View元々低速だったがAndroid4.0以降ハードウェアアクセラレーションが適用されてまあまあ速いSurfaceView別スレッドから描画できるので高速だったが、ハードウェアアクセラレーションが適用されないので今では通常のViewに負けているTextureViewSurfaceViewの代替としてAndroid4.0で登場し通常のViewに組み込める上にOpenGLで描画してくれるが、Bitmapを延々と生成するだけでメモリ消費が半端なく電力消費もヤバいAndroid7.0以降ではSurfaceViewもViewと同期できるようになりTextureViewは下位互換と化したというわけで結論としてはCustomViewを使え、もっと高速に描画したいなら今流行りのVulkanを使えということらしいです。私としては、VulkanはAndroid7.0以降が必要なので現在のシェアを考えると却下、CustomViewも十分な速度が出るか不安でした。GLSurfaceViewじゃあどうするのかと言うと、↑の記事で奇特と一蹴されてしまったGLSurfaceViewを使います。(ちゃんと設計すればTextureViewみたいにメモリも食わない)。最初に雰囲気を掴むのにこのサイトが良かったので紹介しておきます。ESにはいくつかバージョンがあり1.0と2.0以降でほとんど別物になってるようですが、2.0はAndroid2.2以降で使えるので今ならまず問題ないでしょう。Developers実際に学んでみたところ、確かにコーディング量自体は多いのですが、やっていること自体は合理的で納得できるものでした。少なくとも2Dの範囲であれば努力に見合う成果が得られると思います。VBO/IBOまた、↑の入門記事には載っていませんが、大量のマスを効率的に描画するのにVBOとIBOをガンガン使っています。インターネット上にあまり良い資料が無くて苦労したのでざっくりと使い方を書いておきます(入門記事を読んでいる前提で説明します)。けんごのお屋敷のJavaコードをKotlinに変換して使わせていただいています。id[0]}bufferにはBufferUtilで配列をconvertしたもの、sizeにはバッファーの1要素のバイト数(Floatなら4、Shortなら2)、targetにはVBOならGLES20.GL_ARRAY_BUFFER、IBOならGLES20.GL_ELEMENT_ARRAY_BUFFERを入れます。GLES20.GL_ARRAY_BUFFER)頂点座標はこのようにまとめておくと良いでしょう。0)みたいにすればOKです(最後は雰囲気で)。いうことで一番大事だと思ったのが、ソルバーの改良です。各盤面が論理的に解けるかどうかの判定を内部で行っています。このソルバーくんは論理的に解ける所まで解いていって、これ以上は論理的に考えても分からん、となったら運ゲーだと判定してくれます。逆に最後まで解けたら、これは運ゲーではないと判定します。実際にプレイヤーに盤面を提供する流れは、プレイヤーが最初に開けたいマスをタップするそのマスと周囲8マス以外にランダムに地雷をバラ撒くソルバーくんが解く運ゲーと判定されたら2に戻る、運ゲーではないと判定されたらプレイヤーに提供してゲーム開始となっています。ここで問題となるのは、このソルバーくんに論理的に解けるはずの盤面を解かせてみても運ゲーと判定してしまうことがあるということです。考えたら論理的に解けるはずでも「ちょっと自分の頭では分からんな」となることがある訳です。これの何が問題かと言うと、本来出題されるべき難しい盤面が出題されず、全体的なレベルが下がってしまっているということです。ソルバーの仕組みそもそもマインスイーパーのソルバーってどうやって実装するんでしょう?申し訳ないのですが、詳しいことは秘密です。おおまかなポイントを何個か紹介していきたいと思います。全探索まず、一番ナイーブな方法として全探索があります。どのパターンでも地雷が配置されないマスがあったらそこは安全と判断して開き、次の時点を考えるというのを繰り返すものです。これは絶対に運ゲーが排除できる単純かつ素晴らしい方法ですが、計算に物凄い時間が掛かる可能性があります。↓の盤面では、開いていないマスが421個、その中に地雷が99個隠れています。実際にはこうなっています。通りといったところです。少なくとも我々が生きている間に計算が終わることはないでしょう。隣接マスよく考えてみると、上の方の数字マスに隣接しているマス以外はどこに地雷があったところで運ゲーかどうかの判断には関わり得ないですよね?すると、今回考慮に入れるべきマスは数字マスに隣接している42マスとなり、この中に0〜42個を入れる組み合わせを考えれば良くなります。これなら約5日間で済むので、生きている内に分かりそうです。地雷数確定マスただ、生きている内と言っても1回プレイするのに何日も待ちたくは無いですよね。そこでもっと人間的な手法を導入してみましょう。開いていないマスの数と地雷数が一致する場合です。そこが地雷だと確定するのでその隣にも1があればそこに隣接する他のマスは全て安全となります。当たり前の話をしてしまいましたが、まずはこういう所から考えて、少しでも組み合わせを減らしていくことが大事です。隣接数字マス次に、ちょっとマインスイーパーをやったことがある人なら皆やっているであろう、数字マス同士の比較です。1が並んでいたらそれぞれの1の下に地雷があって2の下は安全、みたいなのがありますね。これをもう少し一般化することで、かなりのパターンが解けるようになります。今回の盤面であれば、自明なものもありますがとりあえず数字マスは全部で36個見えています。共通の隣接未開マスを持つ数字マス同士を2個ずつ比較していくと、大体100通りくらいに収まるでしょう。3個以上の隣接数字マスただ、この方法では解けないパターンが存在します。こんなパターンがあるとします。1これはどの2つの数字マス同士を比較しても情報が出ません。1これだけ推定できます。ここまで来れば恐らく他のマスも解けるでしょう。これで解決と思うかもしれませんが、この比較する数字マスを3個以上に増やしてしまうと、結局数字マスの組み合わせが爆発的に増えてしまい何日も掛かってしまうことになるのです。実は比較的効率良く組み合わせを探索する方法があります。イイ感じに真偽値を組み合わせた形に変形させることで高速に解けるようにするというものです。数独ソルバーなんかは大体このSATを使っているはずです。軽くて速いことで有名なMiniSatというSATソルバーがあるのですが、これのJavaラッパーがあったので使わせていただきました。github.com現時点で私以外⭐を付けていませんが、とても役に立っています。ありがとうございます。実はマインスイーパーにおける情報は数字マスだけではありません。残りの地雷数によって配置が確定することがあるのです。いうパターンがよくあります。壁これは一見すると運ゲーですが、残り地雷数が1個か3個の場合、1通りに確定します。2個の場合は運ゲーです。実はこの残り地雷数を考慮した処理が一番面倒で、ここが特に企業秘密になります。厳密モードこうしてソルバーくんは運ゲーの盤面と運ゲーでない盤面を完璧に判定することが出来るようになったのでした。無事出題される盤面の難易度は改善されたのですが、これにより「厳密モード」というものが実現可能になりました。「厳密モード」というのは簡単に言うと、運ゲープレイを禁止するモードです。地雷が無いマスであっても論理的に地雷が無いと分かるマスでなければタップした時点でゲームオーバーになります。2018年12月4日リツイートお願いします!!!こうして、より知的でスリリングなマインスイーパーが遊べるようになりました。↑のツイートにも書いていますが、スクショ共有機能を追加しました。2018年12月4日GLSurfaceViewと通常のViewでスクショの撮り方が異なるので実装がちょっと大変でした。遊び方動画Unity版のアプリにも遊び方の説明は載せていたのですが、文が淡々と箇条書きされていて読む気しないだろうなぁ、と思っていたので、頑張って動画にしてみました。www.youtube.com雑コラですが英語版も作ってあります。Xのフリートライアル版を使って作りました。動画制作って大変だなぁと思いました。現状&まとめ現状ですが、管理画面でまとまった統計が出なくなったので詳しくは計算していないです。Android版の総インストール数は18,000くらいだと思います。151)となっています。皆さんありがとうございます。iOSの方はまだまだ少ないので早くネイティブ化してユーザーを増やしたいところです。他にも色々アプリを作っていく予定なので、こういうアプリを作って欲しい等のご意見がありましたら、Twitterとかで教えて下さい。twitter.com終わりに長文駄文失礼致しました。最後にもう一度アプリへのリンクを載せておきますので、是非お試し下さい。明日の記事はid:CHY72さんの「太古のPython(Python0.9)を眺めてみる」です!Pythonはあまり触ったことがないですが、Pで始まる言語を見るとなんだか面白そうな予感がしてきますね。Adventarの他の記事はこちらからどうぞ。...2015-11-30初めまして。