tag:crieit.net,2005:https://crieit.net/tags/Unity/feed 「Unity」の記事 - Crieit Crieitでタグ「Unity」に投稿された最近の記事 2023-01-22T07:25:33+09:00 https://crieit.net/tags/Unity/feed tag:crieit.net,2005:PublicArticle/18369 2023-01-17T23:44:33+09:00 2023-01-22T07:25:33+09:00 https://crieit.net/posts/Unity-63c6b45134197 Unityで〇×ゲームを作ろうぜ(^~^)? <h1 id="教育目的"><a href="#%E6%95%99%E8%82%B2%E7%9B%AE%E7%9A%84">教育目的</a></h1> <p>プログラミング・スキルの普及のために、 <strong>この記事の内容は 改変・転載許可</strong></p> <p>成果物: 📖 <a target="_blank" rel="nofollow noopener" href="https://play.unity.com/mg/other/tic-tac-toe-19">Tic tac toe</a><br /> コード: 📖 <a target="_blank" rel="nofollow noopener" href="https://github.com/muzudho/Tic-Tac-Toe">Tic-Tac-Toe</a></p> <h1 id="📅2023-01-17 tue"><a href="#%F0%9F%93%852023-01-17+tue">📅2023-01-17 tue</a></h1> <p>📖 <a target="_blank" rel="nofollow noopener" href="https://diamond.jp/articles/-/293032">アマゾン創業者が部下に繰り返し続けた「過酷な一言」</a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ジェフ・ペゾスは モックアップの時点で完成度高くないと 企画も通さないらしいぜ。迷惑だよな」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 どう迷惑を受けたんだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 モックアップ作りから妥協しない姿勢が Amazon の強さの秘訣だ、みたいな論調の記事を読むと<br /> だったら もう モックアップ作るの いいかな、 弱くていいや、 と なにもかも 嫌になってしまう」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 個人サークルの1人が言う モックアップ と、<br /> 140万人ほどの社員がいる企業の社長が言う モックアップ を同じように扱おうとするのが 性格が意固地なのよ。<br /> モックアップに完成度を求めていないケースだってあるのだから、<br /> 特定の社長に最適化せず もっと言葉を適当に使えばいいのよ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 これより Unity を使って 〇×ゲームの <strong>やっつけモックアップ</strong> を作る。<br /> 今回の趣旨は 技術 を何も説明せず、 <strong>雰囲気</strong> で書く <strong>ヒカルの碁方式</strong> だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 Qiita と Zenn で規約違反になるから Crieit でやるのか」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 いや、思想違反」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 ガイドラインぐらい 程度は違えど どこにでもあるのだから 守ればいいのよ」</p> <p><a href="https://crieit.now.sh/upload_images/2e5165998982b193ab74bd431677b3c463c6786034a57.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2e5165998982b193ab74bd431677b3c463c6786034a57.png?mw=700" alt="202301_unity_17-1925-unity-hub.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これ、 Unity Hub (ユニティ ハブ)。<br /> なんか並んでる1つ1つは レッスンの残骸とか、 手指の練習の残骸とか、 まあゲーム1個分の何かだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 なるほど 画面を見せていくわけか YouTube でやれだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 嫌だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/da79b837b2d1c27b0c12026ca61a4d2e63c67d1eddb14.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/da79b837b2d1c27b0c12026ca61a4d2e63c67d1eddb14.png?mw=700" alt="202301_unity_17-1948-unity-hub.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 最初は とにかく プロジェクト名 を付けろだぜ。<br /> 偉いさんは SEO や name conventions を考えた いい感じの名前を 付けたいと考えるかもしれないが<br /> わたしの流儀は 『<strong>あの世に持っていけるものは何もなし</strong>』 だぜ。 ひねらず 付けて 進め」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 現世利益に興味を持てだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 墓碑銘は 『墓』 にしましょう」</p> <p><a href="https://crieit.now.sh/upload_images/dca1d1612054eed72e65fd8edae4db9163c681aec2427.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/dca1d1612054eed72e65fd8edae4db9163c681aec2427.png?mw=700" alt="202301_unity_17-2007-unity-editor-4k.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 プロジェクトを開くと Unity Editor(ユニティー・エディター)が出てくる。<br /> わたしは 4K(よんけー)ディスプレイ を使っているので 大きな添付画像になってしまうから、<br /> 以降は 適当にウィンドウを縮めて スクリーンショットを貼っていくぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 そこらへんの You Tube で ゲーム開発動画が たくさんあるから 読者諸君は 詳しくは勝手にググれだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/b1208b1930d629b7b0f52526f137768763c683190425b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b1208b1930d629b7b0f52526f137768763c683190425b.png?mw=700" alt="202301_unity_17-2013-unity-editor.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 〇×ゲーム作るんだったら 盤が要るだろ。 平面(へいめん;Plane プレーン)を置こうぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 ぜったい Unity Learn で 企画書を書けだの、設計を練れだの レッスン受けたのにな。<br /> プログラマー・スキル・レベル剥奪だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 設計を <strong>信じて</strong> ないのよ。 プロジェクトは <strong>崩壊</strong> すると思ってるから」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 じゃあ お父んは何を信じて Unity Editor なんか開いたのか?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 あっ、そういえば 〇 も × も 素材が無いぜ!」</p> <p><a href="https://crieit.now.sh/upload_images/56224887f1f14703631daf92c56c60c163c686aa966fd.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/56224887f1f14703631daf92c56c60c163c686aa966fd.png?mw=700" alt="202301_unity_17-2029-paint.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 わたしには モデルを作るスキル無いので <strong>Windows Paint</strong> で描くぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 行き当たりばったりだな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 <strong>行動してから考える人</strong> ですからね」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 お父んの頭の中に <strong>効率</strong> と <strong>戦略</strong> が無いことは 分かった」</p> <p><a href="https://crieit.now.sh/upload_images/b245b1033bc447474657a73da8f0a3d063c689e834a76.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b245b1033bc447474657a73da8f0a3d063c689e834a76.png?mw=700" alt="202301_unity_17-2042-texture.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 画像を プレーンに ドラッグ&ドロップ すれば テクスチャーを貼るの完了だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 Unity、 3Dが得意なのに ペラペラの平面に Windows Paint の画像 貼り付けるの<br /> Unity の無駄遣いだよな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 まだ <strong>行動の途中</strong> で、 <strong>考える</strong> ところまで 行ってませんからね」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 そう言えば、盤に 黒くて太い線を引きたいけど、 黒い太線を引くのも めんどくさいんだよな。<br /> それに 盤のマスをクリックしたかどうか 範囲を調べるのも めんどくさい……」</p> <p><a href="https://crieit.now.sh/upload_images/9421ea9d329c2b7c858cec945bf66a2463c68ea5f25ac.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9421ea9d329c2b7c858cec945bf66a2463c68ea5f25ac.png?mw=700" alt="202301_unity_17-2103-board.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 そこで プレーンを9枚 置いて、マスの代わりとしようぜ!」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 お父んの優先順位の中で、 <strong>アートワーク</strong> の順位は下の方にあることが分かった」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 <strong>花より団子</strong> の人ですからね。 〇×ゲームができれば なんでもいいのよ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 しかし どういう感じにすりゃ 〇×ゲーム を作れるのか よく分からんな」</p> <p><a href="https://crieit.now.sh/upload_images/e76e051e4ca0d6826bf887f00081995663c690e599bb2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e76e051e4ca0d6826bf887f00081995663c690e599bb2.png?mw=700" alt="202301_unity_17-2112-save.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ここで一旦 プロジェクトを保存して Unity Editor を終了するぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 考えるのをやめたか」</p> <p><a href="https://crieit.now.sh/upload_images/78f8b0f0d241d331b0f871af138d71dd63c692607b16d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/78f8b0f0d241d331b0f871af138d71dd63c692607b16d.png?mw=700" alt="202301_unity_17-2118-git-hub.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 わたしの Git Hub を開くんだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 ソースを クラウドに保存するんだな」</p> <p><a href="https://crieit.now.sh/upload_images/d7e1f433cec10e67a6fd6f7708ebb1fd63c695088d51d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d7e1f433cec10e67a6fd6f7708ebb1fd63c695088d51d.png?mw=700" alt="202301_unity_17-2130-git-hub.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 クラウド上に リポジトリを作るぜ。 リポジトリというのは ファイル置き場 ぐらいの意味だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/78c3a895040604584341ce5dea1c133f63c6958da4783.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/78c3a895040604584341ce5dea1c133f63c6958da4783.png?mw=700" alt="202301_unity_17-2132-git-hub.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 GitHub Desktop for Windows というデスクトップ・アプリケーションと連動するんで、<br /> ローカルPCの さっきの Tic-Tac-Toe のファイルを、<br /> いったんローカルPC側のリポジトリで コミットし、<br /> 続いて クラウド上のリポジトリへ プッシュするぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 何を言っているか分からないが まあ がんばれだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/cff3d25f89ac497217eb2149186843d063c696eab9243.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cff3d25f89ac497217eb2149186843d063c696eab9243.png?mw=700" alt="202301_unity_17-2138-git-hub.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 リモートに ファイルが置かれたな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 本当は <strong>コード・レビュー</strong> を入れて、コメントをちゃんと書いてコミットして使うものなんだけど、<br /> やらないのよ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 やらないだろうな」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 ドカスカ プッシュしろだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 Git Hub にソースをプッシュしたから、<br /> ソースを壊してしまったときは それより前のバージョンへ 巻き戻すことが可能になったわね」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 適当に どこかに戻せれば 十分だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 <strong>戻してから考える</strong> んだろうなあ」</p> <p><a href="https://crieit.now.sh/upload_images/36cbafb83b53d783dff08f1c43dd490063c699bc7af0c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/36cbafb83b53d783dff08f1c43dd490063c699bc7af0c.png?mw=700" alt="202301_unity_17-2148--script-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 マス(※英語でSquare スクウェア)をマウスでクリックしたときに 〇×を付けたいんだろ。<br /> とりあえず Square という名前で C#(しーしゃーぷ)スクリプトを作ろうぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 Git Hub で、いつでもリセットできるから お父んの <strong>とりあえず進もうぜ、困ったら戻ろうぜ法</strong> が完成だな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 頼りない名前の法だなあ」</p> <p><a href="https://crieit.now.sh/upload_images/3fa88c8f92255224c570079d46754b4163c69b387964a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3fa88c8f92255224c570079d46754b4163c69b387964a.png?mw=700" alt="202301_unity_17-2156--visual-studio-2022.png" /></a></p> <p>補足:📖<a target="_blank" rel="nofollow noopener" href="https://bluebirdofoz.hatenablog.com/entry/2021/08/06/224723">Unity 2020でスクリプトの日本語が文字化けするのを修正する</a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これが Visual Studio 2022 (びじゅある すたじお にせんにじゅうに)というデスクトップ・アプリケーションだぜ。<br /> こんな画面出てきても、これから何かけばいいか分からないだろ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 何でも書いたらいいんじゃないか」</p> <p><a href="https://crieit.now.sh/upload_images/24d07c88f1842294746d34bf1241343e63c69c0b46a09.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/24d07c88f1842294746d34bf1241343e63c69c0b46a09.png?mw=700" alt="202301_unity_17-2200--intellisense.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 そう!<br /> 何もないところのうち いい感じのところに <code>mouse</code> と打鍵すれば、<br /> わたしが書きたいものを予測して候補が出てくる。 これが Visual Studio の利点である インテリセンス(IntelliSense) という AI だぜ。<br /> 1990年代後半にはすでにあった」</p> <p><a href="https://crieit.now.sh/upload_images/34f1000cfeb8c366f19f51b78d72ae7263c69cd4e183b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/34f1000cfeb8c366f19f51b78d72ae7263c69cd4e183b.png?mw=700" alt="202301_unity_17-2203--intellisense-and-tab-key.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 そしてその候補で合ってたら、キーボード上の <code>tab</code> キーを1回押せだぜ。<br /> スケルトン・コードを書いてくれる。<br /> スケルトンというのは、穴埋め文章の穴じゃない方だな」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 適当の達人だな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 <strong>いい感じのところ</strong> がどこなのか説明してくれないから 真似するの無理よ」</p> <p><a href="https://crieit.now.sh/upload_images/2cc0eda1e6415e062a66acb20dd3136563c69eac7f066.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2cc0eda1e6415e062a66acb20dd3136563c69eac7f066.png?mw=700" alt="202301_unity_17-2211--debug-log.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 何書けばいいか分からないから、とりあえず <code>Debug.Log("なんちゃら")</code> を書けだぜ。<br /> このテクは デバッグ・ライト(Debug write)と言う」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 <code>とりあえず</code> が多いな」</p> <p><a href="https://crieit.now.sh/upload_images/75faac30c36ba4ba1c7bb68e4cd5e2bd63c69ff696edf.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/75faac30c36ba4ba1c7bb68e4cd5e2bd63c69ff696edf.png?mw=700" alt="202301_unity_17-2216--attach-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 で、スクリプトは書いただけでは動かない。<br /> マス(※画面上では Square 0 ~ Square 8)というゲーム・オブジェクトに <code>Square</code> スクリプトを持たせる。<br /> この操作を <strong>アタッチ</strong> (Attach) と呼ぶ」</p> <p><a href="https://crieit.now.sh/upload_images/bc791fb6d8c0f03cf281096e707aee3963c6a0e73c7bc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/bc791fb6d8c0f03cf281096e707aee3963c6a0e73c7bc.png?mw=700" alt="202301_unity_17-2221--main-camera.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 カメラの位置も あんまりだったので、 Main Camera の位置を調整する」</p> <p><a href="https://crieit.now.sh/upload_images/689215a1bd5999604abfd11ea9bfc47663c6a185f1a4f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/689215a1bd5999604abfd11ea9bfc47663c6a185f1a4f.png?mw=700" alt="202301_unity_17-2223--play-button-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ゲーム・ビューも好きなようにいじって、 Unity Editor の上の方に置いてある再生ボタンを押せだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/6382644361ffc4a7ca350aa1fab50dc663c6a238d65d8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6382644361ffc4a7ca350aa1fab50dc663c6a238d65d8.png?mw=700" alt="202301_unity_17-2225--game-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 するとゲーム画面になる。<br /> 黄緑色のマスをクリックしたら、 Unity Editor の左下に 『マウスボタンを押しました』と出てきた。<br /> これで動作確認完了だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 マスがクリックできることまでは 分かったな。<br /> そこから どうするのか 分からんけど」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 クリックしたマスの上に <strong>〇のカード</strong> が飛んでくりゃ よくない?」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 じゃあ <strong>〇のカード</strong> 5枚、 <strong>×のカード</strong> 4枚を予め作っておけばいいのかだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 めんどくさ……。 マスの表面のテクスチャーを貼り替えりゃよくないかだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 あらゆる発想が めんどくささに 飲み込まれて 消えていくわね」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 お父ん ぜったい あらゆる企画マンと 会話 合わないよな」</p> <p><a href="https://crieit.now.sh/upload_images/d4b0239491b633b45978aabcec182b2363c6a74939ebd.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d4b0239491b633b45978aabcec182b2363c6a74939ebd.png?mw=700" alt="202301_unity_17-2247--game-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 とりあえず <code>GameManager</code> C#スクリプトをアタッチした <code>Game Manager</code> ゲーム・オブジェクトを作れだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/1a28ff9f8b864330aa2c0c1844d31bc463c6a959def75.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/1a28ff9f8b864330aa2c0c1844d31bc463c6a959def75.png?mw=700" alt="202301_unity_17-2255--serialize-fields-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameManager</code> C#スクリプトの いい感じのところに、 いい感じに マテリアルを3つ書くと」</p> <p><a href="https://crieit.now.sh/upload_images/2de173c976531d0d5cbd065508111dd463c6aaa6484d0.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2de173c976531d0d5cbd065508111dd463c6aaa6484d0.png?mw=700" alt="202301_unity_17-2302--serialize-fields-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 マテリアルを ドラッグ&ドロップで 設定できる欄が できるから」</p> <p><a href="https://crieit.now.sh/upload_images/78930db4c79978e8bdfd93c4288d523c63c6ab7a77387.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/78930db4c79978e8bdfd93c4288d523c63c6ab7a77387.png?mw=700" alt="202301_unity_17-2304--drag-and-drop-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 がんばって ドラッグ&ドロップ しろだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/53c01fd961d8da3ff7df5f03a0b95d4f63c6af8a7896d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/53c01fd961d8da3ff7df5f03a0b95d4f63c6af8a7896d.png?mw=700" alt="202301_unity_17-2323--remove-game-object.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Nought</code> ゲーム・オブジェクトと、 <code>Cross</code> ゲーム・オブジェクトは 結局使わないので 消せだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 行き当たりばったりの ひずみ ここに出るの わらう」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 バーチャル空間だから 無駄な発注を やりたい放題よ 徒労が増えるだけで」</p> <p><a href="https://crieit.now.sh/upload_images/ec6c2c82bedb979dddb61d51a2d6262c63c6b10871287.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ec6c2c82bedb979dddb61d51a2d6262c63c6b10871287.png?mw=700" alt="202301_unity_17-2329--game-manager-script-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameObject</code> C#スクリプトに 実質5行ほど コードを書き足したぜ」</p> <p><a href="https://crieit.now.sh/upload_images/573b97fd7aa0321e4dcc7f9c1453660463c6b1a5dec95.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/573b97fd7aa0321e4dcc7f9c1453660463c6b1a5dec95.png?mw=700" alt="202301_unity_17-2331--square-script-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Square</code> C#スクリプトに 実質4行ほど コードを書き足したり、編集したりしたぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 なんの説明もないところが 今回の記事の趣旨だな」</p> <p><a href="https://crieit.now.sh/upload_images/b2527f32608c7e60fde65460925ede9363c6b2422a553.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b2527f32608c7e60fde65460925ede9363c6b2422a553.png?mw=700" alt="202301_unity_17-2335--game.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 だが、もう ○、× を交互に置けるぜ」</p> <p><a href="https://crieit.now.sh/upload_images/cb30c0bd6325ff2725fca14350817cb963c6b29c3e07a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cb30c0bd6325ff2725fca14350817cb963c6b29c3e07a.png?mw=700" alt="202301_unity_17-2336--illegal-move.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 👆 もう印を置いてあるところを またクリックして 印を変えることができるぜ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 イリーガル・ムーブ(非合法手)のチェックはしてないからな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 しなきゃ いけなくない?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 プロフェッショナルなプログラマーなら 必ず 修正するし、<br /> レッスン中なら 不具合の修正は ほっといて 後回しにしろだぜ。<br /> 不具合の修正は 気持ちが乗らないからな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 不具合の修正より、気持ちがノるか ノらないかが 優先されるのね」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 ぜったい わらう」</p> <h1 id="📅2023-01-18 wed"><a href="#%F0%9F%93%852023-01-18+wed">📅2023-01-18 wed</a></h1> <p><a href="https://crieit.now.sh/upload_images/2930527603b56f9501ba38fb6261523763c7cc6f9327f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2930527603b56f9501ba38fb6261523763c7cc6f9327f.png?mw=700" alt="202301_unity_18-1938--win.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 勝ったとき 『勝ち!』 って出てくれば 気持ちがノるだろ。<br /> まずは その前に 勝ったかどうか判定するプログラムを書こうぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 プログラムらしい話に進むな」</p> <h1 id="📅2023-01-20 fri"><a href="#%F0%9F%93%852023-01-20+fri">📅2023-01-20 fri</a></h1> <p><a href="https://crieit.now.sh/upload_images/58b88b80e68beab7957f71b1dfb4d09963ca6cacb9905.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/58b88b80e68beab7957f71b1dfb4d09963ca6cacb9905.png?mw=700" alt="202301_unity_20-1927--texts.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 テキストをまず作っておこうぜ。<br /> 普段は非表示にしておいて、必要な時に表示すればいい」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 仕込みだな」</p> <p><a href="https://crieit.now.sh/upload_images/453e3f50e25cc2cf71388449fffeb89863ca6e1758670.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/453e3f50e25cc2cf71388449fffeb89863ca6e1758670.png?mw=700" alt="202301_unity_20-1932--judge-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 勝敗判定を どこに どういう風に書けばいいのか分からない。<br /> 分からなかったら <code>なんとかManager</code> C# スクリプトを作って、 <code>なんとか Manager</code> ゲーム・オブジェクトにアタッチすればいいぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 お父んは『いいぜ』と言うが、 ダメだったら戻ればいいスタイルだからな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 迷路をとりあえず進んでるだけよね」</p> <p><a href="https://crieit.now.sh/upload_images/d49d8ec8fa095da5453453bc7350b1f063ca705abb1b9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d49d8ec8fa095da5453453bc7350b1f063ca705abb1b9.png?mw=700" alt="202301_unity_20-1942--position-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Position</code> (ポジション) C# スクリプトもいるかもしれない。スケルトンを置いておこう」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 要るか 要らないか 分からなくても スケルトンを置くのが お父んの開発スタイルだよな」</p> <p><a href="https://crieit.now.sh/upload_images/ea0f4b5c750c6a4df951f955fb37922163ca73383e610.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ea0f4b5c750c6a4df951f955fb37922163ca73383e610.png?mw=700" alt="202301_unity_20-1954--piece-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Piece</code> (ピース;駒)イナム(enum;列挙)型も作っとこ。 あとで使うだろ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 どんどんタネを仕込んで あとで 組み合わせる やり方か」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 ちゃんと 組み合わさるか 事前に検討して 必要なものだけを洗い出すステップを やらないのよ、 ノらないから」</p> <p><a href="https://crieit.now.sh/upload_images/023631a9ab938a649ad0429d6a39baf963ca7554bda5b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/023631a9ab938a649ad0429d6a39baf963ca7554bda5b.png?mw=700" alt="202301_unity_20-2003--position-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ポジションに <code>Piece</code> 型の配列を持たせようぜ。<br /> これ、 Unity Editor で いじれるようにしようかな、 <code>[SerializeField]</code> アノテーション付けたろ」</p> <p><a href="https://crieit.now.sh/upload_images/252f0e41583f338fc7285b0add77b88363ca767936725.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/252f0e41583f338fc7285b0add77b88363ca767936725.png?mw=700" alt="202301_unity_20-2008--position-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 やっぱ <code>Position Manager</code> ゲーム・オブジェクトも作って <code>Position</code> C#スクリプトをアタッチしようぜ?<br /> 盤を編集でけて お得だろ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 お得かどうかは関係ないんじゃないか? 損だったら戻ればいいのだから 進めば」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 最後に まとめ切れるかどうか 関係無いですからね、この開発手法」</p> <p><a href="https://crieit.now.sh/upload_images/e59dd91012834bef3cf71adfa54745f363ca7e4bcd629.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e59dd91012834bef3cf71adfa54745f363ca7e4bcd629.png?mw=700" alt="202301_unity_20-2029--position-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 Position は <code>Clear</code> 関数や、 <code>SetPiece</code> 関数を使うだろ。<br /> class の public 修飾子も消して internal アクセスにしておこうぜ。<br /> <code>Linq</code> は パフォーマンスが心配なんで使わないぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 それが 何かの説明は しないんだな」</p> <p><a href="https://crieit.now.sh/upload_images/56780ac58daaed10cf8fbd182bf975ac63ca820f38da9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/56780ac58daaed10cf8fbd182bf975ac63ca820f38da9.png?mw=700" alt="202301_unity_20-2058--position-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameManager</code> C#スクリプトに入ってた <code>movesCount</code> フィールドは、 <code>Position</code> クラスにあった方がいいので 移動した。<br /> <code>MovesCount</code> アクセッサ―も追加したぜ」</p> <p><a href="https://crieit.now.sh/upload_images/8472827be429f7c7ffc143dcae62304363ca82eca337a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8472827be429f7c7ffc143dcae62304363ca82eca337a.png?mw=700" alt="202301_unity_20-2101--game-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>movesCount</code> フィールドを <code>Position</code> クラスに持っていかれた <code>GameManager</code> クラスの方には、<br /> <code>Position</code> インスタンスにアクセスできるように プロパティを用意し、参照箇所も <code>Position</code> プロパティに変更するぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 フーン」</p> <p><a href="https://crieit.now.sh/upload_images/7e8098840e9502ddb11e5646e365036f63ca87381a010.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7e8098840e9502ddb11e5646e365036f63ca87381a010.png?mw=700" alt="202301_unity_20-2118--game-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Square 0</code> ゲーム・オブジェクトの <code>0</code> の部分だけ取るように 正規表現(せいきひょうげん;RegularExpression)を書き、<br /> Position インスタンスにセットするように書いてみた」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 へぇ」</p> <p><a href="https://crieit.now.sh/upload_images/c083c8e49b3f50ffd18b3c5a5ec1deb563ca88abb236e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c083c8e49b3f50ffd18b3c5a5ec1deb563ca88abb236e.png?mw=700" alt="202301_unity_20-2124--game-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/964dc85dc239408d4a0659f38e97265f63ca88a20914c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/964dc85dc239408d4a0659f38e97265f63ca88a20914c.png?mw=700" alt="202301_unity_20-2126--game-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これで、マスをクリックすると、○と×が入っているのが分かるな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 <code>Cross</code> (×)と <code>Nought</code> (○)が逆よ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 ぬぎぎぎぎ!」</p> <p><a href="https://crieit.now.sh/upload_images/f344f1f4be82d910dc0454a9c47e6fcf63ca8b9c02102.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f344f1f4be82d910dc0454a9c47e6fcf63ca8b9c02102.png?mw=700" alt="202301_unity_20-2138--increment-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 インクリメントは最後にやれだぜ!」</p> <p><a href="https://crieit.now.sh/upload_images/ce8085b2de91764bfd51080dd49ddd2c63ca8de9f2a7f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ce8085b2de91764bfd51080dd49ddd2c63ca8de9f2a7f.png?mw=700" alt="202301_unity_20-2147--win-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Position</code> インスタンスが盤を表している。<br /> <code>Element 1</code> と <code>Element 4</code> と <code>Element 7</code> に <code>Nought</code> が入っていれば、3つの○が並んだということだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 タテ、ヨコ、ナナメに同じピースが3つ並んでいることを判定できる数式があるの?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 ○×ゲームぐらいの 盤サイズの小ささなら 全パターン網羅して ハードコーディングしたった方が早いぜ」</p> <p><a href="https://crieit.now.sh/upload_images/43cc4c9e0f2e90035b53dd2196093b5863ca91cb73814.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/43cc4c9e0f2e90035b53dd2196093b5863ca91cb73814.png?mw=700" alt="202301_unity_20-2205--getPiece-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Position</code> クラスに <code>GetPiece</code> メソッドが無いと 盤のマスを見れないので追加する」</p> <p><a href="https://crieit.now.sh/upload_images/31de2697202386450ac4eabe458ab79f63ca9386479dd.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31de2697202386450ac4eabe458ab79f63ca9386479dd.png?mw=700" alt="202301_unity_20-2212--winPattern-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 例えば 3つ並んだケースの一覧とか 要るだろ。<br /> 要ると思ったものを 予め 作っておくんだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 要ると分かってから 作った方が良くないか?」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 ノらないから そんなこと しないのよ」</p> <p><a href="https://crieit.now.sh/upload_images/01f51fc47fa0b9c43a35d149ff30087763ca9637d41df.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/01f51fc47fa0b9c43a35d149ff30087763ca9637d41df.png?mw=700" alt="202301_unity_20-2224--gameResult-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 対局結果も要りそうだ。作っとこ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 この開発手法では 工数の見積もりを取れないのでは?」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 設計してないんだから 見積もりがあるはずないじゃない」</p> <p><a href="https://crieit.now.sh/upload_images/c922eaf9cf8969f362750b014da5bbe463ca99c66c6f4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c922eaf9cf8969f362750b014da5bbe463ca99c66c6f4.png?mw=700" alt="202301_unity_20-2239--judge-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 勝敗判定を書いている途中に思ったんだが、負けかどうかは判定してないな」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 じゃあ 『勝敗判定』ではなくて 『勝ったか判定』だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/efa76cffa229b0c955b3339c063b980e63ca9a7870fb7.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/efa76cffa229b0c955b3339c063b980e63ca9a7870fb7.png?mw=700" alt="202301_unity_20-2242--game-results-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 対局結果から 負け を削除しとこ。<br /> <code>GameResult</code> も こっそり <code>GameResults</code> に変えた」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 勝ち しか無かったら、どっちが勝ったのか 分からなくない?」</p> <p><a href="https://crieit.now.sh/upload_images/e38d41b0977e95fa77b09201bdc047d363ca9ba59be3d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e38d41b0977e95fa77b09201bdc047d363ca9ba59be3d.png?mw=700" alt="202301_unity_20-2247--nought-win-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 まさか <code>Lose</code> を使う必要がないとは 思わなかったぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 ダメだったら戻ればいい の現場だな」</p> <p><a href="https://crieit.now.sh/upload_images/97f9d27dfc4306b493cca306fd3764e463ca9e7fbf504.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/97f9d27dfc4306b493cca306fd3764e463ca9e7fbf504.png?mw=700" alt="202301_unity_20-2259--judge-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>JudgeManager</code> で対局結果を見れたら デバッグが楽だと思ったから フィールドと プロパティ付けたろ。<br /> <code>winPatterns</code> も こっそり <code>static readonly</code> 付けたろ」</p> <p><a href="https://crieit.now.sh/upload_images/2d95b91a7c98f4a9e2e41b73f98daa8e63ca9fc8d3c7b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2d95b91a7c98f4a9e2e41b73f98daa8e63ca9fc8d3c7b.png?mw=700" alt="202301_unity_20-2303--setup-judge-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 対局結果を返すのではなく、対局結果を フィールドに入れるように変更。<br /> 名前も <code>DoJudge</code> から <code>SetupJudge</code> に変更」</p> <p><a href="https://crieit.now.sh/upload_images/18436c094d87ab26624c5a191be8cb9a63caa24b83f92.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/18436c094d87ab26624c5a191be8cb9a63caa24b83f92.png?mw=700" alt="202301_unity_20-2313--game-manager-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameManager</code> クラスの中で <code>JudgeManager</code> インスタンスの <code>SetupJudge</code> メソッドを使ってみよう」</p> <p><a href="https://crieit.now.sh/upload_images/fd84dfe1bd18626e80df82dede04376163caa3463b496.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/fd84dfe1bd18626e80df82dede04376163caa3463b496.png?mw=700" alt="202301_unity_20-2318--game-result-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ちゃんと <code>Win</code> が入ってるな」</p> <p><a href="https://crieit.now.sh/upload_images/0a2f8158bada9bd73f084948a5e8b6e663caa41acd392.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0a2f8158bada9bd73f084948a5e8b6e663caa41acd392.png?mw=700" alt="202301_unity_20-2323--draw-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 でも <code>Draw</code> 判定が 10手目にされたので 1 引いとこ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 10手目なんか 無いんだけどな」</p> <p><a href="https://crieit.now.sh/upload_images/5f942bd0acf741f5c794a176c2c0d1e163caa4e697d35.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5f942bd0acf741f5c794a176c2c0d1e163caa4e697d35.png?mw=700" alt="202301_unity_20-2326--disable-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 対局結果のゲーム・オブジェクトのアクティベートのチェックを外して、存在していないことにしよ」</p> <p><a href="https://crieit.now.sh/upload_images/eefdf7b664eaffd0cfcc579c1df18b2c63caaa83f0517.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/eefdf7b664eaffd0cfcc579c1df18b2c63caaa83f0517.png?mw=700" alt="202301_unity_20-2350--setActive-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 存在しないゲーム・オブジェクトだったら、 Unity は見つけられないので、<br /> <code>[SerializeField]</code> アノテーションを利用して ゲーム・オブジェクトにアクセスする仕組みを 仕込むぜ。<br /> ついでに <code>SetActive( )</code> メソッドを使って、ゲーム・オブジェクトのアクティベートのチェックを入れるコードも書いておくぜ」</p> <p><a href="https://crieit.now.sh/upload_images/b07ed7d1b7fdb61f7981e037b630889763caab93b1b6e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b07ed7d1b7fdb61f7981e037b630889763caab93b1b6e.png?mw=700" alt="202301_unity_20-2353--drag-and-drop-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ドラッグ&ドロップしておくぜ」</p> <p><a href="https://crieit.now.sh/upload_images/329d19ce582e5c2a630423b9b438e83763caabfa17794.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/329d19ce582e5c2a630423b9b438e83763caabfa17794.png?mw=700" alt="202301_unity_20-2357--draw.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 対局結果が出るようになったぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 配色わる」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 文字のような○×の上に 文字 出してるしな」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 他の 盤ゲームが 対局結果を どう表示しているか 見てこいだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/b4e9a03e8b2b92c2c6ac182fd0c7795d63cab0c74edd6.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b4e9a03e8b2b92c2c6ac182fd0c7795d63cab0c74edd6.png?mw=700" alt="202301_unity_21-0018--front-cover.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 わたしに美術はできないので、終局時に 半透明のフロント・カバーが かかるようにしよう!」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 確かに お父んは 企画 通らなさそう」</p> <p><a href="https://crieit.now.sh/upload_images/f92106e82b3b38ae3d71d8d5d7aa167d63cab2900a908.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f92106e82b3b38ae3d71d8d5d7aa167d63cab2900a908.png?mw=700" alt="202301_unity_21-0025--front-cover.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これで 十分 十分」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 ビデオゲームは 見た目のできが 内容のできだと 思われるのよ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 ノらないところは やらないでおこうぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 他の人はむしろ 目に付く 見た目を いじりたがるんだけどな」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 次は リスタート・ボタン を付けようぜ?」</p> <p>📅2023-01-21 sat 00:31 end</p> <h1 id="📅2023-01-22 sun 01:34 start"><a href="#%F0%9F%93%852023-01-22+sun+01%3A34+start">📅2023-01-22 sun 01:34 start</a></h1> <p><a href="https://crieit.now.sh/upload_images/78687b8432bea2b1de689b029c12b63963cc15a1e794d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/78687b8432bea2b1de689b029c12b63963cc15a1e794d.png?mw=700" alt="202301_unity_22-0139--button-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 2020年代の GUI というと Web クライアントや、 Windows デスクトップ・アプリケーションでは 進歩、淘汰の激戦区だが、<br /> Unity に付いてる GUI は 1990年代かな、というぐらいクラシックなものなので あまり気合を入れずに使うぜ」</p> <p><a href="https://crieit.now.sh/upload_images/50c1df4bf970fdd0d3c937f25efa6a3a63cc17061c2a3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/50c1df4bf970fdd0d3c937f25efa6a3a63cc17061c2a3.png?mw=700" alt="202301_unity_22-0149--restart-button.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 リスタート・ボタンの見た目は こんなんでいいんじゃないか?」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 美術の欠如している わたしたちは これでいきましょう」</p> <p><a href="https://crieit.now.sh/upload_images/d9e033a7f670c032b4b81c7f0c35a93e63cc18ed731c5.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d9e033a7f670c032b4b81c7f0c35a93e63cc18ed731c5.png?mw=700" alt="202301_unity_22-0153--judge-manager-clear-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>JudgeManager</code> に <code>Clear</code> メソッドを付けるぜ」</p> <p><a href="https://crieit.now.sh/upload_images/6ddd934f79d571f7a04970139006e2e263cc1a13069dc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6ddd934f79d571f7a04970139006e2e263cc1a13069dc.png?mw=700" alt="202301_unity_22-0157--position-clear-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Position</code> の <code>Clear</code> メソッドで <code>moveCount</code> フィールドを ゼロ初期化してなかったので するぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 仕込みをしてるんだな」</p> <p><a href="https://crieit.now.sh/upload_images/afded1cca00b5fb9e22b1de62444fbe463cc1b282c0ba.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/afded1cca00b5fb9e22b1de62444fbe463cc1b282c0ba.png?mw=700" alt="202301_unity_22-0203--game-manager-clear-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameManager</code> に <code>Clear</code> メソッドを付けるぜ。<br /> この中で <code>Position</code> インスタンスと、 <code>JudgeManager</code> インスタンスの <code>Clear</code> メソッドを呼び出すとともに、<br /> 対局結果のテキストと、盤に被せた半透明の幕のアクティベートのチェックを外して、ゲーム中に存在しない扱いにするぜ」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 また、 <code>Start</code> メソッドで <code>Clear</code> メソッドを呼び出した」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 それは要るのか?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 シーン・ビューで テキストの表示のアクティベートのチェックを外すの忘れたりしたまま リリース したくないだろ」</p> <p><a href="https://crieit.now.sh/upload_images/75297f996e3c2b66b190d7b968f2deb163cc1d2fd0114.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/75297f996e3c2b66b190d7b968f2deb163cc1d2fd0114.png?mw=700" alt="202301_unity_22-0210--restart-button-event-listener-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ボタンを押したら、 <code>Game Manager</code> ゲーム・オブジェクトの持っている <code>GameManager</code> C#スクリプトの <code>Clear</code> メソッドが<br /> 呼び出されるように マウス操作で紐づけるぜ。<br /> この技術の名前は イベント・リスナー(Event Listener)だぜ」</p> <p><a href="https://crieit.now.sh/upload_images/960ff4734ed2fa874c4cdf281482561963cc1f569021a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/960ff4734ed2fa874c4cdf281482561963cc1f569021a.png?mw=700" alt="202301_unity_22-0220--restart-button-activate-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 リスタート・ボタン自体のアクティベートのチェックを オン/オフする仕組みを忘れてた 追加しよ」</p> <p><a href="https://crieit.now.sh/upload_images/cb9ad218d655d73225ba46413293bae063cc1fcf563a4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cb9ad218d655d73225ba46413293bae063cc1fcf563a4.png?mw=700" alt="202301_unity_22-0223--restart-button-attach-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 だんだん ごつく なってきたな」</p> <p><a href="https://crieit.now.sh/upload_images/c5eda7ca58d37f30211ef5691b0570d263cc2049d4646.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c5eda7ca58d37f30211ef5691b0570d263cc2049d4646.png?mw=700" alt="202301_unity_22-0225--restart-button-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 でも リスタート・ボタンが出てきたぜ。押してみよ」</p> <p><a href="https://crieit.now.sh/upload_images/ead782d753e02a782fb7211a04e5ce0f63cc20b97d0dc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ead782d753e02a782fb7211a04e5ce0f63cc20b97d0dc.png?mw=700" alt="202301_unity_22-0227--restart.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 👆 盤はクリアーされてないぜ? なぜだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 WPF (ダブリュー・ピー・エフ)の MVVM (エム・ブイ・ブイ・エム)の ViewModel (ビュー・モデル)に慣れた癖で忘れていたが、<br /> Unity では データをクリアーしても、 シーンは連動していないのだった」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 これは ゲーム・プログラミングなのよ。<br /> デスクトップ・アプリケーションのような 処理が重たい技術は 流行らないのよ」</p> <p><a href="https://crieit.now.sh/upload_images/61794a1c8ab0302cca5de97aa854ca1a63cc23083bc03.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/61794a1c8ab0302cca5de97aa854ca1a63cc23083bc03.png?mw=700" alt="202301_unity_22-0237--square-texture-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 マスの材質を 緑色の素材に貼り替えたろ」</p> <p><a href="https://crieit.now.sh/upload_images/321e17162027b27e8b09100960d878c463cc2377e299c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/321e17162027b27e8b09100960d878c463cc2377e299c.png?mw=700" alt="202301_unity_22-0239--game.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 貼り替わったぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 これで ○×ゲームは 卒業かだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 まだ イリーガル・ムーブ を禁止していないわよ?」</p> <p><a href="https://crieit.now.sh/upload_images/0f5381ee3c886ecac19851e6fbf3d06263cc287177e3f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0f5381ee3c886ecac19851e6fbf3d06263cc287177e3f.png?mw=700" alt="202301_unity_22-0255--static-validator-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 じゃあ バリデーター(Validator)を作ればいいだろ。<br /> そのマスに置けるかどうかだけ チェックすればいいのかだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/92d5be737e7fabe80de4379cee1afe9863cc293aa5ac3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/92d5be737e7fabe80de4379cee1afe9863cc293aa5ac3.png?mw=700" alt="202301_unity_22-0303--square-number-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 あっ、 <code>Square 0</code> みたいなゲーム・オブジェクトの名前しか取れね。 <code>0</code> みたいなマス番号取れね」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 不便よねえ」</p> <p><a href="https://crieit.now.sh/upload_images/1a0544f9a279f3230c1301fd39ab03e363cc2c2eefb05.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/1a0544f9a279f3230c1301fd39ab03e363cc2c2eefb05.png?mw=700" alt="202301_unity_22-0316--static-helper-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 じゃあ ヘルパー関数 作ればいいんだぜ。<br /> <code>GameManager</code> で 似たようなコード前に作ったから 引っこ抜いて 共通利用できるようにするぜ」</p> <p><a href="https://crieit.now.sh/upload_images/25ab84d4d7a1a6cd3a9e83ddeaaab04763cc2cc39c532.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/25ab84d4d7a1a6cd3a9e83ddeaaab04763cc2cc39c532.png?mw=700" alt="202301_unity_22-0319--static-helper-using-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 既存のコードは見やすくなるし」</p> <p><a href="https://crieit.now.sh/upload_images/dd6d8e90a9d3159a86525f8a8f0bcce163cc2d944a966.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/dd6d8e90a9d3159a86525f8a8f0bcce163cc2d944a966.png?mw=700" alt="202301_unity_22-0322--validation-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 新しいコードは書きやすくなる」</p> <p><a href="https://crieit.now.sh/upload_images/60fe586a47a2db0904567902a2c9be6563cc2e1851f76.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/60fe586a47a2db0904567902a2c9be6563cc2e1851f76.png?mw=700" alt="202301_unity_22-0325--validated.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これで 非合法手 は防げたんじゃないか?」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 <code>Nought win</code> のあとに まだ <code>X</code> を置けるんじゃない?」</p> <p><a href="https://crieit.now.sh/upload_images/45bc60ee82810b1689bd7db0acf8f17c63cc2eaa1ff2f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/45bc60ee82810b1689bd7db0acf8f17c63cc2eaa1ff2f.png?mw=700" alt="202301_unity_22-0327--front-cover-validated.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 半透明の青い膜が被っていて、 マスはクリックできないから<br /> 対局終了後に マスをクリックすることは でけないぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 じゃあ いいかあ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 これで 完成か?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 コード・レビュー しようぜ?<br /> プログラミングの へたくそなところがある」</p> <p><a href="https://crieit.now.sh/upload_images/93c46e9caa50384440e11f96be525a4863cc319c2e606.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/93c46e9caa50384440e11f96be525a4863cc319c2e606.png?mw=700" alt="202301_unity_22-0338--code-review-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 例えば 『駒を置く』は入力だが、 『対局結果を表示する』は出力だぜ。<br /> 入力メソッドが 出力してるなんて <strong>イケてないぜ</strong>」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 判断基準は イケてるか イケてないか なのね」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 プログラマーの気分 を 重要視してるんだな。 その点では Ruby に似ているな。お父んのポリシーが 分かってきたぜ」</p> <p><a href="https://crieit.now.sh/upload_images/dcf063f10bc127633a82b4ac518bdf9e63cc34196c725.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/dcf063f10bc127633a82b4ac518bdf9e63cc34196c725.png?mw=700" alt="202301_unity_22-0338--code-review-2.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 『出力』の部分を <code>DoMove</code> メソッドから外に出したいが、<br /> <code>piece</code> 変数が <code>DoMove</code> メソッドに束縛されているから、 <code>piece</code> 変数を 自由変数に変える方法を考えようぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 なんだか分からないが 任せたぜ」</p> <p><a href="https://crieit.now.sh/upload_images/c1d5e1b43dc8e561ee02ffb725bc0fab63cc35c039941.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c1d5e1b43dc8e561ee02ffb725bc0fab63cc35c039941.png?mw=700" alt="202301_unity_22-0356--turn-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Position</code> クラスに 手番(Turn)を持たせようぜ。<br /> 初期値は <code>Nought</code>」</p> <p><a href="https://crieit.now.sh/upload_images/195449b0412ed3ed8ed1bd073d34b7dd63cc382bb0e7f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/195449b0412ed3ed8ed1bd073d34b7dd63cc382bb0e7f.png?mw=700" alt="202301_unity_22-0407--next-turn-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 手番を追加する <code>NextTurn</code> メソッドもいるや。追加しとこ」</p> <p><a href="https://crieit.now.sh/upload_images/1fa124b13275ff6cd3bd126bcad894f863cc3b47d88cc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/1fa124b13275ff6cd3bd126bcad894f863cc3b47d88cc.png?mw=700" alt="202301_unity_22-0419--increment-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 インクリメントも <code>NextTurn</code> メソッドの中で やってまお。<br /> <code>MovesCount</code> プロパティーのセッター(set)も要らなくなったから、短く書いたろ」</p> <p><a href="https://crieit.now.sh/upload_images/56a4e1c235b838654a76607f748e856463cc3c3fd58c8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/56a4e1c235b838654a76607f748e856463cc3c3fd58c8.png?mw=700" alt="202301_unity_22-0424--doMove-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これで <code>DoMove</code> の中から <code>piece</code> 変数が消えた。 代わりに <code>Position</code> に依存するようになったぜ」</p> <p><a href="https://crieit.now.sh/upload_images/469795d0c7599136285d74061502743a63cc3d9ab5433.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/469795d0c7599136285d74061502743a63cc3d9ab5433.png?mw=700" alt="202301_unity_22-0430--update-game-result-view-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 こうやって <code>UpdateGameResultView</code> メソッドに切り分けることがでけたな。<br /> しかし <code>DoMove</code> メソッドを実行すると <code>UpdateGameResultView</code> メソッドまで 実行されてしまうのは イケてないな」</p> <p><a href="https://crieit.now.sh/upload_images/4f9dae601fd513d6b7b9fa01efc982b163cc3ed3d1db7.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4f9dae601fd513d6b7b9fa01efc982b163cc3ed3d1db7.png?mw=700" alt="202301_unity_22-0435--increment-validation-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 あっ いけね!<br /> 対局が終了しているときは <code>NextTurn</code> しないように バリデーション・チェックしようぜ」</p> <p><a href="https://crieit.now.sh/upload_images/cb10a1947cd056c2cd3508e5d97047c963cc43ad828ce.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cb10a1947cd056c2cd3508e5d97047c963cc43ad828ce.png?mw=700" alt="202301_unity_22-0455--setup-judge-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>JudgeManagement</code> クラスの <code>SetupJudge</code> メソッドを、<br /> 変更があったかどうか返すように 変更するぜ」</p> <p><a href="https://crieit.now.sh/upload_images/77b0bce7e897089265c514e85b805a9b63cc459f0674d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/77b0bce7e897089265c514e85b805a9b63cc459f0674d.png?mw=700" alt="202301_unity_22-0501--dirty-judgement-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 判定に変更があれば <code>dirtyJudgement</code> フラグを立て、<br /> <code>dirtyJudgement</code> フラグが立っているときだけ <code>UpdateGameResultView</code> メソッドは働き、<br /> 働いたら <code>dirtyJudgement</code> フラグは下ろす、という風に作るぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 めんどくさ」</p> <p><a href="https://crieit.now.sh/upload_images/f05cfdfc9b70aeee067b451cbf2bd85063cc466561424.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f05cfdfc9b70aeee067b451cbf2bd85063cc466561424.png?mw=700" alt="202301_unity_22-0508--update-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これで <code>UpdateGameResultView</code> メソッドの呼び出しを<br /> <code>DoMove</code> メソッドの外に出して、<br /> <code>Update</code> メソッドの中へ 引っ越すことがでけたぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e9d0130a57419ab5322235a11af424aa63cc47025acfa.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e9d0130a57419ab5322235a11af424aa63cc47025acfa.png?mw=700" alt="202301_unity_22-0511--game.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 👆 そんなけ コードをいじっても ゲームは何にも変わらないじゃない。<br /> コードを イケてるようにすることに 何の意味があんの?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 ワザを安定して出せるようになると、<br /> もっと 大きなワザ を出せるようになる。大きなワザ を出せるようにするために コードを掃除してるんだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/2e593ba5feb02e0738cfa2fb4011953963cc4a0e8d1bb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2e593ba5feb02e0738cfa2fb4011953963cc4a0e8d1bb.png?mw=700" alt="202301_unity_22-0523--clear-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 👆 <code>GameManager</code> クラスの <code>Clear</code> メソッドの中で 画面表示を切り替えているのは 掃除しないのかだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 する」</p> <p><a href="https://crieit.now.sh/upload_images/88d97e1865734efba49ccdfa518874f963cc4b7c55a46.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/88d97e1865734efba49ccdfa518874f963cc4b7c55a46.png?mw=700" alt="202301_unity_22-0530--set-piece-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Position</code> クラスの <code>SetPiece</code> メソッドも、変更があったかどうか返すようにしようぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/e07525efd0469ebda5b7e6f539e0c0b463cc4e4c7c95e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e07525efd0469ebda5b7e6f539e0c0b463cc4e4c7c95e.png?mw=700" alt="202301_unity_22-0541--dirty-squares-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 マスをクリックして、絵柄に変化があったときだけ <code>dirtySquares</code> セットに マス番号を追加することにするぜ」</p> <p><a href="https://crieit.now.sh/upload_images/15a38f0fb40ec207b0a520bf537ec21e63cc51bc332de.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/15a38f0fb40ec207b0a520bf537ec21e63cc51bc332de.png?mw=700" alt="202301_unity_22-0553--update-square-view-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 これで <code>DoMove</code> メソッドの中から 表示をコントロールするコードは 消えてなくなっただろ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 入力メソッドの中では 出力はしないようにしたんだな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 <code>dirtyなんちゃら</code> フラグが 入力メソッドと 出力メソッドの 橋渡しをしているのね」</p> <p><a href="https://crieit.now.sh/upload_images/a6fbbab7afa09885fd05604f9f33fed463cc5506f12e7.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a6fbbab7afa09885fd05604f9f33fed463cc5506f12e7.png?mw=700" alt="202301_unity_22-0610--clear-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameManager</code> クラスの <code>Clear</code> メソッドの中にあったコードも、 <code>dirty</code> フラグを立てるだけで よくなったぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 これで 表示周りのコードの クリーンナップ は終わりか?」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 まだある」</p> <p><a href="https://crieit.now.sh/upload_images/672ce92be3f8d4c0705ae7af00349f2163cc573604ecc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/672ce92be3f8d4c0705ae7af00349f2163cc573604ecc.png?mw=700" alt="202301_unity_22-0615--update-square-view-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameObject.Find</code> メソッドは 処理に時間がかかるらしい。<br /> できれば <code>Start</code> メソッドで1回使ったあとは 使わなくて済むようにしたいぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 やってくれだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/12dccad101ef2a1ddad99cb01eaf263c63cc596e3b9fc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/12dccad101ef2a1ddad99cb01eaf263c63cc596e3b9fc.png?mw=700" alt="202301_unity_22-0628--go-squares-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>GameManager</code> クラスの <code>Start</code> メソッドが呼び出された時点で、<code>GameObject.Find</code> を先にしてしまって、<br /> ゲーム・オブジェクトをメモリに入れておけばいいぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 他に どこを クリーンナップ するんだぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/2499ffd82d3fa1b280b726a28b989d2863cc5bc683af9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2499ffd82d3fa1b280b726a28b989d2863cc5bc683af9.png?mw=700" alt="202301_unity_22-0639--accessor-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 例えば <code>get</code> アクセッサ―で 変数をリターンしているだけのプロパティなんかは……」</p> <p><a href="https://crieit.now.sh/upload_images/f48e3580044e3463bd047fb77d8aaebc63cc5c34246ca.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f48e3580044e3463bd047fb77d8aaebc63cc5c34246ca.png?mw=700" alt="202301_unity_22-0639--lambda-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ラムダ式と同じなんだったら、書き方が短いラムダ式にするとかかな。<br /> パフォーマンスに違いがでるのか 知らんけど」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 パフォーマンスの測定をするほどの 速度が必要なアプリケーションじゃないから<br /> ちょっとぐらいパフォーマンスが違っても 違いは分かんないわねえ」</p> <p>📅2023-01-22 sun 06:45</p> <p><a href="https://crieit.now.sh/upload_images/dd5c0611c4a2069bc403705dcec376d163cc5edf40669.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/dd5c0611c4a2069bc403705dcec376d163cc5edf40669.png?mw=700" alt="202301_unity_22-0650--webgl.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 WebGL 形式で 実行ファイルを出力してみようぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/5820d70f390ed9efa99f7fc3170a006d63cc5f838f6f3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5820d70f390ed9efa99f7fc3170a006d63cc5f838f6f3.png?mw=700" alt="202301_unity_22-0655--live-server-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 Visual Studio Code に <code>Live Server</code> エクステンション入れてると ローカルWebサーバー起ちあがるんで、<br /> Tic Tac Toe の <code>index.html</code> を開いてみようぜ?」</p> <p><a href="https://crieit.now.sh/upload_images/a02822505cb829b8b9e81b95a832c81663cc600399cb5.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a02822505cb829b8b9e81b95a832c81663cc600399cb5.png?mw=700" alt="202301_unity_22-0657--unity-webgl-player.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 開けたな」</p> <p><a href="https://crieit.now.sh/upload_images/016f52d3015877c784babfdcf95c33e963cc603a15235.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/016f52d3015877c784babfdcf95c33e963cc603a15235.png?mw=700" alt="202301_unity_22-0659--nought-win.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 プレイできるぜ」</p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 ○×ゲームできても ビデオゲームって感じ しないけどな」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 2か月 Unity Lesson でビギナーコースを受けて、○×ゲームを作るのに 3日間かかるようでは 気が遠くなるわよね」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 仕込みが なんにも無いからな。<br /> 重要なのは 制作進行を覚えて 素材の発注を見積もることだぜ」</p> <p><a href="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/31f0f35be3a4b6b05ce597c7aab702b763c675227892a.png?mw=700" alt="202108__character__12--ohkina-hiyoko-futsu2.png" /></a><br /> 「 UI も タイトル画面も 何もかもがなくて ゲーム開発の全体像はまだ見えないわね」</p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 まあ、先に進もうぜ」</p> <p>📖 <a target="_blank" rel="nofollow noopener" href="https://play.unity.com/">Unity Play</a></p> <p><a href="https://crieit.now.sh/upload_images/2e03353eda777c0aaef26bc50f2bc56563cc628e945c7.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2e03353eda777c0aaef26bc50f2bc56563cc628e945c7.png?mw=700" alt="202301_unity_22-0708--unity-play.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 <code>Unity Play</code> という Web サイトがある」</p> <p><a href="https://crieit.now.sh/upload_images/66f72f9e7f507940c520af0c6924c53563cc62df7e773.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/66f72f9e7f507940c520af0c6924c53563cc62df7e773.png?mw=700" alt="202301_unity_22-0710--unity-play-upload.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 ここには、作ったゲームをアップロードするページがある」</p> <p><a href="https://crieit.now.sh/upload_images/8fea3b36e8b9abfe086af975a588a62d63cc636b45695.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8fea3b36e8b9abfe086af975a588a62d63cc636b45695.png?mw=700" alt="202301_unity_22-0711--game-file-1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 というわけで、 WebGL のファイルが入ったフォルダーを <code>.zip</code> 圧縮し……」</p> <p><a href="https://crieit.now.sh/upload_images/2ab88554a86131ecb1332bd9478ee12963cc64de36293.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2ab88554a86131ecb1332bd9478ee12963cc64de36293.png?mw=700" alt="202301_unity_22-0718--play-unity.png" /></a></p> <p>📖 <a target="_blank" rel="nofollow noopener" href="https://play.unity.com/mg/other/tic-tac-toe-19">Tic tac toe</a></p> <p><a href="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5b53e954894672b36c716412a272826b63c674b756465.png?mw=700" alt="202101__character__31--ramen-tabero-futsu2.png" /></a><br /> 「 👆 アップロード完了。 Play Unity に置いたぜ」</p> <p><a href="https://crieit.now.sh/upload_images/8dac93438803f00459ce08cde74dcd2963cc658770762.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8dac93438803f00459ce08cde74dcd2963cc658770762.png?mw=700" alt="202301_unity_22-0721--play-unity-game.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e846bc7782a0e037a1665e6b3d51b02463c6750a6308a.png?mw=700" alt="202101__character__28--kifuwarabe-futsu.png" /></a><br /> 「 👆 まあ ○×ゲーム しかできないんだけどな」</p> <p>📅2023-01-22 sun 07:22 end</p> <p><おわり></p> むずでょ tag:crieit.net,2005:PublicArticle/17602 2021-08-16T02:31:05+09:00 2021-08-22T07:44:40+09:00 https://crieit.net/posts/unity-ios-android-secret-manager 📔Unity で iOS/Android アプリの設定値をセキュアに扱う方法 <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>iOS/Android でユーザーの情報をセキュアに扱う必要があったので、調査したところ Android には <a target="_blank" rel="nofollow noopener" href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences">EncryptedSharedPreferences</a> が存在することを知りました。iOS には <a target="_blank" rel="nofollow noopener" href="https://developer.apple.com/documentation/security/keychain_services">Keychain Services</a> が存在します。</p> <p>今回は Unity の iOS/Android プラットフォーム上で設定値を保存するための実装を行う必要があったので、Unity から扱えるようネイティブプラグインを作成しました。今後もこういった要望はありそうでしたので、記事として手順や内容を書き記しておくことにしました。</p> <p>本記事内で紹介しているコードは下記にアップ済みです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/nikaera/Unity-iOS-Android-SecretManager-Sample">https://github.com/nikaera/Unity-iOS-Android-SecretManager-Sample</a></p> <h1 id="動作環境"><a href="#%E5%8B%95%E4%BD%9C%E7%92%B0%E5%A2%83">動作環境</a></h1> <ul> <li>MacBook Air (M1, 2020)</li> <li>Unity 2020.3.15f2</li> <li>Android 6.0 以上 <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences">EncryptedSharedPreferences</a> が使用可能なバージョン</li> </ul></li> </ul> <h1 id="Android のネイティブプラグインを作成する"><a href="#Android+%E3%81%AE%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">Android のネイティブプラグインを作成する</a></h1> <p>Android 環境ではまず <a target="_blank" rel="nofollow noopener" href="https://github.com/googlesamples/unity-jar-resolver">External Dependency Manager for Unity</a> を利用して、Unity の Android ネイティブプラグインで <code>EncryptedSharedPreferences</code> 利用可能にします。</p> <h2 id="(追記) Gradle を利用したライブラリのインストール方法"><a href="#%28%E8%BF%BD%E8%A8%98%29+Gradle+%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%9F%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%96%B9%E6%B3%95">(追記) Gradle を利用したライブラリのインストール方法</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://twitter.com/shiena">shiena</a> さんにご教授いただいたのですが、<a target="_blank" rel="nofollow noopener" href="https://zenn.dev/shiena/articles/unity-sqlcipher#gradle%E3%82%92%E5%88%A9%E7%94%A8">こちらの記事</a>のように Gradle を利用することでも簡易にライブラリの取り込みが可能なようでした。</p> <p>手順は上記の記事をご参照いただくとして、Gradle を利用する方法で外部ライブラリを取り込む際の <code>Assets/Plugins/Android/mainTemplate.gradle</code> および <code>Assets/Plugins/Android/gradleTemplate.properties</code> は下記になります。</p> <p>```diff gradle:Plugins/Android/mainTemplate.gradle<br /> dependencies {<br /> implementation fileTree(dir: 'libs', include: ['*.jar'])<br /> + implementation 'androidx.security:security-crypto:1.1.0-alpha03'<br /> <strong>DEPS</strong>}</p> <p>android {</p> <pre><code><br />```diff properties:Assets/Plugins/Android/gradleTemplate.properties org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M org.gradle.parallel=true android.enableR8=**MINIFY_WITH_R_EIGHT** + android.useAndroidX=true unityStreamingAssets=.unity3d**STREAMING_ASSETS** **ADDITIONAL_PROPERTIES** </code></pre> <p><strong>Gradle を利用した方法でライブラリを利用される際は、次の <code>External Dependency Manager for Unity で必要なパッケージをインストールする</code> の手順はスキップ可能です。<code>EncryptedSharedPreferences を利用するためのネイティブコードを追加する</code> のステップから進めてください。</strong></p> <p><code>External Dependency Manager for Unity</code> を利用する方法だと、取り込み先プロジェクト内でライブラリの競合が発生する恐れがあります。Gradle を利用する方法であれば回避が可能です。<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p> <h2 id="External Dependency Manager for Unity で必要なパッケージをインストールする"><a href="#External+Dependency+Manager+for+Unity+%E3%81%A7%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B">External Dependency Manager for Unity で必要なパッケージをインストールする</a></h2> <p><code>External Dependency Manager for Unity</code> をインポートするため <a target="_blank" rel="nofollow noopener" href="https://github.com/googlesamples/unity-jar-resolver/blob/master/external-dependency-manager-latest.unitypackage">unitypackage</a> をダウンロードして、<strong><code>EncryptedSharedPreferences</code> を導入したい Unity プロジェクトを開いてから <code>unitypackage</code> をクリックすることで、<code>External Dependency Manager for Unity</code> を Unity プロジェクトにインポートします。</strong></p> <p><img src="https://i.gyazo.com/1af7cdf4d7d5749e59e151eef1ca5493.png" alt="ダウンロードした <code>unitypackage</code> をクリックして Unity プロジェクトに External Dependency Manager for Unity をインポートする" /></p> <p>Unity プロジェクトの <code>Build Settings</code> からプラットフォームは Android に切り替えておきます。<code>Enable Android Auto-resolution?</code> というダイアログの選択肢はどちらを選んでも構いません。<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p> <p>External Dependency Manager for Unity で各種パッケージを管理する方法は <a target="_blank" rel="nofollow noopener" href="https://github.com/googlesamples/unity-jar-resolver#android-resolver-usage">README</a> に記載がある通り、<strong><code>*Dependencies.xml</code> というファイルを <code>Editor</code> フォルダに配置することで可能になります。</strong></p> <p>今回は <code>EncryptedSharedPreferences</code> を導入するため、下記の xml ファイルを <code>Editor</code> フォルダ内に配置します。</p> <pre><code class="xml"><!-- Assets/Editor/AndroidPluginDependencies.xml --> <?xml version="1.0" encoding="utf-8"?> <dependencies> <androidPackages> <!-- 本記事ではバージョン 1.1.0-alpha03 を利用している --> <androidPackage spec="androidx.security:security-crypto:1.1.0-alpha03"> <androidSdkPackageIds> <!-- Google の Maven リポジトリからインストールするため、 extra-google-m2repository を指定する --> <androidSdkPackageId>extra-google-m2repository</androidSdkPackageId> </androidSdkPackageIds> </androidPackage> </androidPackages> </dependencies> </code></pre> <p>その後、<strong>Unity メニューから <code>Assets -> External Dependency Manager -> Android Resolver -> Force Resolve</code> を選択して、<code>Assets/Editor/AndroidPluginDependencies.xml</code> の内容を元に <code>EncryptedSharedPreferences</code> を利用するのに必要なパッケージを自動で <code>Assets/Plugins/Android</code> フォルダにダウンロードします。</strong></p> <p><img src="https://i.gyazo.com/df394e15149e54dae3e9a81848512ee9.png" alt="1. Unity メニューから <code>Assets -> External Dependency Manager -> Android Resolver -> Force Resolve</code> を選択する" /><br /> <strong>1. Unity メニューから <code>Assets -> External Dependency Manager -> Android Resolver -> Force Resolve</code> を選択する</strong></p> <p><img src="https://i.gyazo.com/f6d2ec95ef9c2afdc857fecef2b165e5.png" alt="2. 実行に成功すると EncryptedSharedPreferences を利用するのに必要なライブラリ群が <code>Assets/Plugins/Android</code> フォルダに配置される" /><br /> <strong>2. 実行に成功すると EncryptedSharedPreferences を利用するのに必要なライブラリ群が <code>Assets/Plugins/Android</code> フォルダに配置される</strong></p> <p>ここまで来ればあとは Android ネイティブコードを <code>Assets/Plugins/Android</code> フォルダ内に配置して Unity 側から叩けるようにするだけです。</p> <h2 id="EncryptedSharedPreferences を利用するためのネイティブコードを追加する"><a href="#EncryptedSharedPreferences+%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">EncryptedSharedPreferences を利用するためのネイティブコードを追加する</a></h2> <p>早速下記の Android ネイティブコードを <code>Assets/Plugins/Android~~<del>/SecretManager.java</code> に配置します。</p> <pre><code class="java">// Assets/Plugins/Android/SecretManager.java package com.nikaera; import com.unity3d.player.UnityPlayerActivity; import java.lang.Exception; // External Dependency Manager for Unity によって、 // 必要な jar が含まれているため EncryptedSharedPreferences の利用が可能になる import androidx.security.crypto.EncryptedSharedPreferences; import androidx.security.crypto.MasterKey; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Bundle; import android.util.Log; public class SecretManager { private SharedPreferences sharedPreferences; public SecretManager(Context context) { try { // EncryptedSharedPreferences で設定値を保存する際に用いる、 // 暗号鍵を扱うためのラッパークラスをデフォルト設定で作成する MasterKey masterKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); // EncryptedSharedPreferences のインスタンスを生成する // コンストラクタで作成した masterKey を指定している this.sharedPreferences = EncryptedSharedPreferences.create( context, context.getPackageName(), masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); } catch (Exception e) { e.printStackTrace(); } } /** * 指定したキーで値を保存する関数 * @param key 値を保存する際に用いるキー * @param value 保存したい値 * @return boolean 値の保存に成功したかどうか */ public boolean put(String key, String value) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(key, value); return editor.commit(); } /** * 指定したキーで保存した値を取得する関数 * `put` 関数で保存した値を取得するのに利用する * @param key 取得したい値のキー * @return string キーに紐づく値、存在しなければ空文字が返却される */ public String get(String key) { return sharedPreferences.getString(key, null); } /** * 指定したキーで値を削除する関数 * @param key 削除したい値のキー * @return boolean 値の削除に成功したかどうか */ public boolean delete(String key) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.remove(key); return editor.commit(); } } </code></pre> <p>その後、上記を Unity スクリプトから実行可能にするための C# クラスを作成します。本記事ではファイルを <code>Assets/Scripts/EncryptedSharedPreferences.cs</code> に配置します。</p> <pre><code class="csharp">// Assets/Scripts/EncryptedSharedPreferences.cs</del><del> using UnityEngine; /// <summary> /// 利用するネイティブコードは <c>Assets/Plugins/Android/SecretManager.java</c> に記載 /// </summary> /// <remarks> /// <a href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences">EncryptedSharedPreferences</a> /// </remarks> class EncryptedSharedPreferences { private readonly AndroidJavaObject _secretManager; public EncryptedSharedPreferences() { // コンストラクタで com.nikaera.SecretManager のインスタンス生成を行う var activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer") .GetStatic<AndroidJavaObject>("currentActivity"); var context = activity.Call<AndroidJavaObject>("getApplicationContext"); _secretManager = new AndroidJavaObject("com.nikaera.SecretManager", context); } public bool Put(string key, string value) { return _secretManager.Call<bool>("put", key, value); } public string Get(string key) { return _secretManager.Call<string>("get", key); } public bool Delete(string key) { return _secretManager.Call<bool>("delete", key); } } </code></pre> <p>あとは用途に応じて下記のようなコードで設定値の保存や取得などを行えます。</p> <pre><code class="csharp">// ... var _sharedPreferences = new EncryptedSharedPreferences(); // name をキーとして値を nikaera で保存する _sharedPreferences.Put("name", "nikaera"); // name をキーとして値を取得する var name = _sharedPreferences.Get("name"); // "nikaera" が出力される Debug.Log(name); // name をキーとして値を削除する _sharedPreferences.Delete("name"); // ... </code></pre> <h1 id="iOS のネイティブプラグインを作成する"><a href="#iOS+%E3%81%AE%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">iOS のネイティブプラグインを作成する</a></h1> <p>iOS の場合は外部ライブラリを利用しないため、<code>External Dependency Manager for Unity</code> は利用しません。<strong>本来であれば Swift で信頼できる外部フレームワークを取り込み利用できると良さそうですが、今回は Objective-C でネイティブプラグインを書いていきます。</strong><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p> <h2 id="Keychain Services を利用するためのネイティブコードを追加する"><a href="#Keychain+Services+%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">Keychain Services を利用するためのネイティブコードを追加する</a></h2> <p>早速下記の iOS ネイティブコードを <code>Assets/Plugins/iOS/KeychainService.mm</code> に配置します。</p> <pre><code class="objc">// Assets/Plugins/iOS/KeychainService.mm</del>~~ // Keychain Services を利用するために Security フレームワークを利用する #import <Security/Security.h> extern "C" { // 指定したキーで値を保存する関数 // - param // - dataType: 値を保存する際に用いるキー // - value: 保存したい値 // - return // - 保存時のステータスコードを返却する (0 以外は失敗) int addItem(const char *dataType, const char *value) { NSMutableDictionary* attributes = nil; NSMutableDictionary* query = [NSMutableDictionary dictionary]; NSData* sata = [[NSString stringWithCString:value encoding:NSUTF8StringEncoding] dataUsingEncoding:NSUTF8StringEncoding]; [query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; [query setObject:(id)[NSString stringWithCString:dataType encoding:NSUTF8StringEncoding] forKey:(id)kSecAttrAccount]; OSStatus err = SecItemCopyMatching((CFDictionaryRef)query, NULL); if (err == noErr) { attributes = [NSMutableDictionary dictionary]; [attributes setObject:sata forKey:(id)kSecValueData]; [attributes setObject:[NSDate date] forKey:(id)kSecAttrModificationDate]; err = SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attributes); return (int)err; } else if (err == errSecItemNotFound) { attributes = [NSMutableDictionary dictionary]; [attributes setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; [attributes setObject:(id)[NSString stringWithCString:dataType encoding:NSUTF8StringEncoding] forKey:(id)kSecAttrAccount]; [attributes setObject:sata forKey:(id)kSecValueData]; [attributes setObject:[NSDate date] forKey:(id)kSecAttrCreationDate]; [attributes setObject:[NSDate date] forKey:(id)kSecAttrModificationDate]; err = SecItemAdd((CFDictionaryRef)attributes, NULL); return (int)err; } else { return (int)err; } } // 指定したキーで値を取得する関数 // - param // - dataType: 値を取得する際に用いるキー // - return // - キーに紐づく値、存在しなければ空文字が返却される char* getItem(const char *dataType) { NSMutableDictionary* query = [NSMutableDictionary dictionary]; [query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; [query setObject:(id)[NSString stringWithCString:dataType encoding:NSUTF8StringEncoding] forKey:(id)kSecAttrAccount]; [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; CFDataRef cfresult = NULL; OSStatus err = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)&cfresult); if (err == noErr) { NSData* passwordData = (__bridge_transfer NSData *)cfresult; const char* value = [[[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding] UTF8String]; char *str = strdup(value); return str; } else { return NULL; } } // 指定したキーで値を削除する関数 // - param // - dataType: 値を削除する際に用いるキー // - return // - 保存時のステータスコードを返却する (0 以外は失敗) int deleteItem(const char *dataType) { NSMutableDictionary* query = [NSMutableDictionary dictionary]; [query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; [query setObject:(id)[NSString stringWithCString:dataType encoding:NSUTF8StringEncoding] forKey:(id)kSecAttrAccount]; OSStatus err = SecItemDelete((CFDictionaryRef)query); if (err == noErr) { return 0; } else { return (int)err; } } } </code></pre> <p><code>Keychain Services</code> は <code>Security</code> フレームワークを利用するため、<strong><code>KeychainService.mm</code> に対して <code>Security</code> フレームワークの依存関係を設定する必要があります。</strong></p> <p><img src="https://i.gyazo.com/ba82aaced24b83b37bf8c63e1ee7142f.png" alt="<code>KeychainService.mm</code> で <code>Security</code> フレームワークの利用を可能にする" /><br /> <strong><code>KeychainService.mm</code> で <code>Security</code> フレームワークの利用を可能にする</strong></p> <p>その後、上記を Unity スクリプトから実行可能にするための C# クラスを作成します。本記事ではファイルを <code>Assets/Scripts/KeychainService.cs</code> に配置します。</p> <pre><code class="csharp">// Assets/Scripts/KeychainService.cs using System.Runtime.InteropServices; /// <summary> /// 実装は <c>Assets/Plugins/iOS/KeychainService.mm</c> に記載 /// </summary> /// <remarks> /// <a href="https://developer.apple.com/documentation/security/keychain_services">Keychain Services</a> /// </remarks> class KeychainService { #if UNITY_IOS [DllImport("__Internal")] private static extern int addItem(string dataType, string value); [DllImport("__Internal")] private static extern string getItem(string dataType); [DllImport("__Internal")] private static extern int deleteItem(string dataType); #endif public bool Put(string key, string value) { #if UNITY_IOS // 返却されるステータスが 0 なら成功 return addItem(key, value) == 0; #endif } public string Get(string key) { #if UNITY_IOS return getItem(key); #else return null; #endif } public bool Delete(string key) { #if UNITY_IOS // 返却されるステータスが 0 なら成功 return deleteItem(key) == 0; #endif } } </code></pre> <p>あとは用途に応じて下記のようなコードで設定値の保存や取得などを行えます。</p> <pre><code class="csharp">// ... var _keychainService = new KeychainService(); // name をキーとして値を nikaera で保存する _keychainService.Put("name", "nikaera"); // name をキーとして値を取得する var name = _keychainService.Get("name"); // "nikaera" が出力される Debug.Log(name); // name をキーとして値を削除する _keychainService.Delete("name"); // ... </code></pre> <h1 id="(余談) インターフェースで iOS/Android のふるまいを共通化する"><a href="#%28%E4%BD%99%E8%AB%87%29+%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%A7+iOS%2FAndroid+%E3%81%AE%E3%81%B5%E3%82%8B%E3%81%BE%E3%81%84%E3%82%92%E5%85%B1%E9%80%9A%E5%8C%96%E3%81%99%E3%82%8B">(余談) インターフェースで iOS/Android のふるまいを共通化する</a></h1> <p>このままだとプラットフォームを切り替える毎にコードを書き直さないとならないので、インターフェースを利用して共通化を行います。</p> <pre><code class="csharp">public interface ISecretManager { /// <summary> /// 指定したキーで値を保存する関数 /// </summary> /// <param name="key">キー</param> /// <param name="value">値</param> /// <returns>保存に成功したかどうか</returns> bool Put(string key, string value); /// <summary> /// 指定したキーの値を取得する関数 /// </summary> /// <param name="key">キー</param> /// <returns>指定したキーで設定された値、無ければ null</returns> string Get(string key); /// <summary> /// 指定したキーの値を削除する関数 /// </summary> /// <param name="key">キー</param> /// <returns>削除に成功したかどうか</returns> bool Delete(string key); } </code></pre> <p>その後、<code>Assets/Scripts/EncryptedSharedPreferences.cs</code> および <code>Assets/Scripts/KeychainService.cs</code> を下記の通り <code>ISecretManager</code> の実装に紐付けます。</p> <pre><code class="csharp">// Assets/Scripts/EncryptedSharedPreferences.cs using UnityEngine; /// <summary> /// 利用するネイティブコードは <c>Assets/Plugins/Android/SecretManager.java</c> に記載 /// </summary> /// <remarks> /// <a href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences">EncryptedSharedPreferences</a> /// </remarks> class EncryptedSharedPreferences: ISecretManager { private readonly AndroidJavaObject _secretManager; public EncryptedSharedPreferences() { var activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer") .GetStatic<AndroidJavaObject>("currentActivity"); var context = activity.Call<AndroidJavaObject>("getApplicationContext"); _secretManager = new AndroidJavaObject("com.nikaera.SecretManager", context); } #region ISecretManager public bool Put(string key, string value) { return _secretManager.Call<bool>("put", key, value); } public string Get(string key) { return _secretManager.Call<string>("get", key); } public bool Delete(string key) { return _secretManager.Call<bool>("delete", key); } #endregion } </code></pre> <pre><code class="csharp">// Assets/Scripts/KeychainService.cs using System.Runtime.InteropServices; /// <summary> /// 実装は <c>Assets/Plugins/iOS/KeychainService.mm</c> に記載 /// </summary> /// <remarks> /// <a href="https://developer.apple.com/documentation/security/keychain_services">Keychain Services</a> /// </remarks> class KeychainService: ISecretManager { #if UNITY_IOS [DllImport("__Internal")] private static extern int addItem(string dataType, string value); [DllImport("__Internal")] private static extern string getItem(string dataType); [DllImport("__Internal")] private static extern int deleteItem(string dataType); #endif // KeychainService.mm に定義した関数を呼び出す #region ISecretManager public bool Put(string key, string value) { #if UNITY_IOS return addItem(key, value) == 0; #else return false; #endif } public string Get(string key) { #if UNITY_IOS return getItem(key); #else return null; #endif } public bool Delete(string key) { #if UNITY_IOS return deleteItem(key) == 0; #else return false; #endif } #endregion } </code></pre> <p>あとは上記をよしなに利用可能な <code>SecretManager</code> クラスを作成します。</p> <pre><code class="csharp">// Assets/Scripts/SecretManager.cs using UnityEngine; /// <summary> /// <em>Editor 利用時のみ PlayerPrefs を利用する</em> /// </summary> /// <remarks><see cref="KeychainService" />, <see cref="EncryptedSharedPreferences" /></remarks> public static class SecretManager { #if UNITY_EDITOR #elif UNITY_ANDROID private static ISecretManager _instance = new EncryptedSharedPreferences(); #elif UNITY_IOS private static ISecretManager _instance = new KeychainService(); #endif public static bool Put(string key, string value) { #if UNITY_EDITOR PlayerPrefs.SetString(key, value); PlayerPrefs.Save(); return true; #elif UNITY_IOS || UNITY_ANDROID return _instance.Put(key, value); #else Debug.Log("Not Implemented."); return false; #endif } public static string Get(string key) { #if UNITY_EDITOR return PlayerPrefs.GetString(key); #elif UNITY_IOS || UNITY_ANDROID return _instance.Get(key); #else Debug.Log("Not Implemented."); return null; #endif } public static bool Delete(string key) { #if UNITY_EDITOR PlayerPrefs.DeleteKey(key); PlayerPrefs.Save(); return true; #elif UNITY_IOS || UNITY_ANDROID return _instance.Delete(key); #else Debug.Log("Not Implemented."); return false; #endif } } </code></pre> <p>これでプラットフォーム間の実装差異を気にすることなく、下記のような記述で設定値の保存や取得などを行えます。<strong>iOS/Android 以外のプラットフォームで追加実装したい場合は<a target="_blank" rel="nofollow noopener" href="https://docs.unity3d.com/ja/2021.1/Manual/PlatformDependentCompilation.html">プラットフォーム依存コンパイル</a>と <code>ISecretManager</code> の実装クラスを新たに作成することで簡単に追加できます。</strong></p> <pre><code class="csharp">// ... // name をキーとして値を nikaera で保存する SecretManager.Put("name", "nikaera"); // name をキーとして値を取得する var name = SecretManager.Get("name"); // "nikaera" が出力される Debug.Log(name); // name をキーとして値を削除する SecretManager.Delete("name"); // ... </code></pre> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>今回は iOS/Android で設定値をセキュアに扱うための方法についてまとめてみました。実際は <code>Keychain Services</code> 周りは実装が大変なので、<code>External Dependency Manager for Unity</code> とか使って <a target="_blank" rel="nofollow noopener" href="https://github.com/kishikawakatsumi/KeychainAccess">KeychainAccess</a> のような外部ライブラリを利用する構成のほうが良いと思われます。</p> <p>本記事の内容に誤りがあったり、実際にはセキュアな実装ができていない等々あれば是非コメントでご指摘いただけますと幸いです。</p> <h1 id="参考リンク"><a href="#%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF">参考リンク</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.android.com/topic/security/data?hl=ja">Android デベロッパー  |  Android Developers</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences?hl=ja">EncryptedSharedPreferences  |  Android デベロッパー  |  Android Developers</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.apple.com/documentation/security/keychain_services">Keychain Services | Apple Developer Documentation</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/masaki_shoji/items/6c512c7ebb30a13cda1d">SharedPreferences を自前で難読化するのはもう古い?これからは EncryptedSharedPrefenreces を使おう - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/261d42c57207e4b7002a">iOS のキーチェーンについて - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/nyhk-oi/items/189236d0627d43e7d658">Unity で IOS にセキュアに値を保存するには KeyChain を使おう - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/googlesamples/unity-jar-resolver">googlesamples/unity-jar-resolver: Unity plugin which resolves Android & iOS dependencies and performs version management</a></li> </ul> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p>逆に <code>External Dependency Manager for Unity</code> を利用する方法のメリットは、UnityPackage などでライブラリとして配布する際に、ライブラリを動作させるのに必要な外部パッケージも同梱した状態で配布が可能になるなどがあります。(当然ライセンスには気を付ける必要がありますが...) <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>パッケージの依存関係を自動で解決するかどうかという選択肢になります。本記事では明示的に Resolve を実行するため <code>Disable</code> でも <code>Enable</code> でも進行上の問題はありません。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:3" role="doc-endnote"> <p><a target="_blank" rel="nofollow noopener" href="https://cocoapods.org/">CocoaPods</a> もサポートされているようなので、iOS でも Android 同様、外部ライブラリを取り込むのは簡単にできそうでした。例えば <a target="_blank" rel="nofollow noopener" href="https://github.com/kishikawakatsumi/KeychainAccess">KeychainAccess</a> とか使いたい。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> nikaera tag:crieit.net,2005:PublicArticle/17282 2021-05-26T22:30:30+09:00 2021-05-26T22:30:30+09:00 https://crieit.net/posts/unity-gameci-github-actions GameCI で Unity の CI 環境を GitHub Actions で構築する <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>先日同僚が Unity の CI 環境を構築するためのライブラリである <a target="_blank" rel="nofollow noopener" href="https://game.ci/">GameCI</a> について教えてくれました。早速 GameCI の GitHub Actions を利用して、サンプルプロジェクトで色々動作検証してみたところ、Unity の CI 環境を楽に構築できることが分かりました。</p> <p>もちろん、<a target="_blank" rel="nofollow noopener" href="https://unity3d.com/jp/unity/features/cloud-build">Unity Cloud Build</a> を利用すれば CI 環境の構築は以前から楽にできました。しかし、選択肢の 1 つとして GameCI を持っておくことで、<strong>サクッと GitHub Actions に統合する形で Unity の CI 環境を導入できるのは他には無いメリットを感じました。</strong></p> <p>本記事で紹介しているソースコード、及び検証時に利用したプロジェクトは GitHub にアップ済みですので、手っ取り早く内容を把握されたい方は下記をご参照くださいませ。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/nikaera/Unity-GameCI-Sample">https://github.com/nikaera/Unity-GameCI-Sample</a></p> <p>業務でも利用できそうなので、GameCI を利用して CI 環境を構築する手順を記事でまとめました。</p> <h1 id="GameCI に備わっている機能紹介"><a href="#GameCI+%E3%81%AB%E5%82%99%E3%82%8F%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E6%A9%9F%E8%83%BD%E7%B4%B9%E4%BB%8B">GameCI に備わっている機能紹介</a></h1> <p>GameCI には現状下記の GitHub Actions が用意されているようです。</p> <div class="table-responsive"><table> <thead> <tr> <th>機能</th> <th>概要</th> </tr> </thead> <tbody> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/activation">Activation</a></td> <td>Unity ライセンスを任意の Unity バージョンで発行する</td> </tr> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/test-runner">Test runner</a></td> <td>Unity の PlayMode 及び EditMode のテストを実行する (テスト結果の出力にも対応)</td> </tr> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/builder">Builder</a></td> <td>任意の Platform ビルドを実行する (<a target="_blank" rel="nofollow noopener" href="https://docs.github.com/ja/actions/guides/storing-workflow-data-as-artifacts">アーティファクト</a> 利用でダウンロードも可能)</td> </tr> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/returning-a-license">Returning a license</a></td> <td>Unity ライセンスの返却ができる (Professional License のみ対応)</td> </tr> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/remote-builder">Remote builder</a></td> <td>GitHub Actions のスペックでは満足のいくビルドができない際に AWS 環境でハイスペックなマシンを用意してビルドできる。ビルドのためのインフラ構築には <a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/cloudformation/">AWS CloudFormation</a> を使用している (現在は AWS のみ対応。今後 GCP, Azure にも対応予定とのこと)</td> </tr> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/deployment/android">Deployment</a></td> <td>Unity ビルドを各種 Platform 向けにデプロイする (iOS 及び Android のみ記載あり。厳密に言うと <code>Builder</code> でビルド出力した内容を <a target="_blank" rel="nofollow noopener" href="https://fastlane.tools/"><code>fastlane</code></a> を用いてデプロイするためのワークフロー紹介になっている)</td> </tr> </tbody> </table></div> <p>上記を見ると既に <strong>GameCI には開発者として Unity CI に欲しい機能は最低限揃っているように見受けられました。</strong> また本記事では、今後機会があれば試してみたいと考えていますが <strong>Remote builder 及び Deployment</strong> については言及していません。</p> <p>今回は実例を交えながら <strong>Activation 及び Test runner、Builder、Returning a license</strong> の使用方法について紹介していきます。</p> <h2 id="Activation: GameCI で必要となる Unity License のアクティベーションを行う"><a href="#Activation%3A+GameCI+%E3%81%A7%E5%BF%85%E8%A6%81%E3%81%A8%E3%81%AA%E3%82%8B+Unity+License+%E3%81%AE%E3%82%A2%E3%82%AF%E3%83%86%E3%82%A3%E3%83%99%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A1%8C%E3%81%86">Activation: GameCI で必要となる Unity License のアクティベーションを行う</a></h2> <p>GameCI で Unity ライセンスをアクティベートするには <a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/activation">Activation</a> を利用します。早速ドキュメントの手順に沿って作業を進めていきます。</p> <p>まず CI を導入したい GitHub 上の Unity プロジェクトの <code>.github/workflows</code> 内に Unity ライセンスアクティベート用のワークフローファイルを作成します。</p> <pre><code class="yml"># .github/workflows/activation.yml name: Acquire activation file on: workflow_dispatch: {} jobs: activation: name: Request manual activation file 🔑 runs-on: ubuntu-latest steps: # GameCI の Activation を利用して alf ファイルを発行する - name: Request manual activation file id: getManualLicenseFile uses: game-ci/unity-request-activation-file@v2 with: # Unity プロジェクトのバージョンを指定する # ProjectSettings/ProjectVersion.txt に記載されているバージョンを入力すれば OK unityVersion: 2020.3.5f1 # Upload artifact (Unity_v20XX.X.XXXX.alf) - name: Expose as artifact uses: actions/upload-artifact@v2 with: name: $<span>{</span><span>{</span> steps.getManualLicenseFile.outputs.filePath <span>}</span><span>}</span> path: $<span>{</span><span>{</span> steps.getManualLicenseFile.outputs.filePath <span>}</span><span>}</span> </code></pre> <p>その後、<a target="_blank" rel="nofollow noopener" href="https://docs.github.com/ja/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/managing-the-default-branch-name-for-your-repositories#about-management-of-the-default-branch-name">デフォルトブランチ</a> にプッシュして GitHub Actions で実行可能にしたら、下記手順に従い Unity ライセンスファイルのアクティベート及びダウンロードを行います。</p> <p><img src="https://i.gyazo.com/bd276ca6dcf6a2c12ce9ff9569e08ce3.png" alt="1. ブラウザから GitHub リポジトリにアクセスして、Unity ライセンスアクティベート用のワークフローを実行して <code>alf</code> ファイルを生成する" /><br /> <strong>1. ブラウザから GitHub リポジトリにアクセスして、Unity ライセンスアクティベート用のワークフローを実行して <code>alf</code> ファイルを生成する</strong></p> <p><img src="https://i.gyazo.com/2271b3cb35efc7f1c9d51702662cdac9.png" alt="2. ワークフローの実行に成功したら、該当項目をクリックして詳細画面に遷移する" /><br /> <strong>2. ワークフローの実行に成功したら、該当項目をクリックして詳細画面に遷移する</strong></p> <p><img src="https://i.gyazo.com/71b9dff8266c9bc990e1b709a5191535.png" alt="3. <code>Artifacts</code> の項目から <code>alf</code> ファイルをダウンロードする" /><br /> <strong>3. <code>Artifacts</code> の項目から <code>alf</code> ファイルをダウンロードする</strong></p> <p><img src="https://i.gyazo.com/cfec48fc58f2a17560ea2e7d0f71cc41.png" alt="4. [Unity license manual activation webpage](https://license.unity3d.com) からログインして <code>alf</code> ファイルをアップロードする" /><br /> <strong>4. <a target="_blank" rel="nofollow noopener" href="https://license.unity3d.com">Unity license manual activation webpage</a> からログインして <code>alf</code> ファイルをアップロードする</strong></p> <p><img src="https://i.gyazo.com/72239a40ef5b2474f34c814a68f8c61d.png" alt="5. Unity ライセンスの用途に応じて適切な選択肢を入力する (本記事では Personal ライセンスを選択)" /><br /> <strong>5. Unity ライセンスの利用用途に応じて適切な選択肢を入力する (本記事では Personal ライセンスを選択)</strong></p> <p><img src="https://i.gyazo.com/9ddc63dc321a68986bfedfb8a97c8f00.png" alt="6. <code>Download license</code> ボタンをクリックして <code>ulf</code> ファイルをダウンロードする" /><br /> <strong>6. <code>Download license</code> ボタンをクリックして <code>ulf</code> ファイルをダウンロードする</strong></p> <p>これで Unity ライセンスファイルのアクティベートは完了です。<strong>次にアクティベートしたライセンスファイルを GitHub リポジトリの <a target="_blank" rel="nofollow noopener" href="https://docs.github.com/ja/actions/reference/encrypted-secrets">Secrets</a> に登録して、GameCI で PlayMode 及び EditMode のテストが実行できるようにしていきます。</strong></p> <p><em><code>alf</code> 拡張子のファイルがライセンスリクエストファイルを指していて、Unity ライセンスの発行に必要となるファイルです。<code>ulf</code> 拡張子のファイルが Unity ライセンスのファイルです。<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></em></p> <h2 id="Test runner: PlayMode 及び EditMode テストを実行して結果を参照する"><a href="#Test+runner%3A+PlayMode+%E5%8F%8A%E3%81%B3+EditMode+%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%A6%E7%B5%90%E6%9E%9C%E3%82%92%E5%8F%82%E7%85%A7%E3%81%99%E3%82%8B">Test runner: PlayMode 及び EditMode テストを実行して結果を参照する</a></h2> <p>GitHub Actions 上でテストを実行するために、<strong>先ほどアクティベートした Unity ライセンスの情報を ワークフロー上で扱えるようにする必要があります。そのため、まずは Secrets に <code>ulf</code> ファイルの内容を登録することから始めます。</strong></p> <p><img src="https://i.gyazo.com/e126ae5e2fe9339d56047b8497808100.png" alt="1. Unity ライセンスの情報登録のため、Github リポジトリの <code>Secrets</code> 登録画面に遷移する" /><br /> <strong>1. Unity ライセンスの情報登録のため、Github リポジトリの <code>Secrets</code> 登録画面に遷移する</strong></p> <p><img src="https://i.gyazo.com/f52356a229caa4e31e3ae8268d53a4e6.png" alt="2. GameCI はライセンス情報参照のため <code>Secrets</code> の <code>UNITY_LICENSE</code> を参照する。そのため、<code>Name</code> を <code>UNITY_LICENSE</code> で <code>Value</code> に <code>ulf</code> ファイルの中身をコピー & ペーストしておく" /><br /> <strong>2. GameCI はライセンス情報参照のため、デフォルト設定では <code>Secrets</code> の <code>UNITY_LICENSE</code> を参照する。そのため、<code>Name</code> が <code>UNITY_LICENSE</code>、<code>Value</code> には <code>ulf</code> ファイルの中身を登録する<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></strong></p> <p>上記作業で GameCI でテストやビルド実行を行える環境が整ったので、動作検証のためテスト実行用のワークフローファイルを作成します。</p> <pre><code class="yml"># .github/workflows/test.yml name: Run EditMode and PlayMode Test on: workflow_dispatch: {} jobs: test: name: Run EditMode and PlayMode Test runs-on: ubuntu-latest steps: # actions/checkout@v2 を利用して作業ディレクトリに # Unity プロジェクトの中身をダウンロードしてくる - name: Check out my unity project. uses: actions/checkout@v2 # GameCI の Test runner を利用して # EditMode 及び PlayMode のテストを実行する - name: Run EditMode and PlayMode Test uses: game-ci/unity-test-runner@v2 env: # 2. の手順で Secrets に登録した Unity ライセンスの情報を指定する UNITY_LICENSE: $<span>{</span><span>{</span> secrets.UNITY_LICENSE <span>}</span><span>}</span> # もし Professional license を使いたい場合は、 # メールアドレス、パスワード、シリアルナンバーを入力する必要がある # ref: https://game.ci/docs/github/test-runner#professional-license # UNITY_EMAIL: $<span>{</span><span>{</span> secrets.UNITY_EMAIL <span>}</span><span>}</span> # UNITY_PASSWORD: $<span>{</span><span>{</span> secrets.UNITY_PASSWORD <span>}</span><span>}</span> # UNITY_SERIAL: $<span>{</span><span>{</span> secrets.UNITY_SERIAL <span>}</span><span>}</span> with: projectPath: . # テストの実行結果もみたい場合は githubToken を指定する # secrets.GITHUB_TOKEN は Secrets 未登録でも利用可能 githubToken: $<span>{</span><span>{</span> secrets.GITHUB_TOKEN <span>}</span><span>}</span> # Unity プロジェクトのバージョンを指定する # ProjectSettings/ProjectVersion.txt に記載されているバージョンを入力すれば OK unityVersion: 2020.3.5f1 # 実行したいテストの種類を指定できる # 指定可能な値は All, PlayMode, EditMode # testMode: All # テスト実行時に利用したい Docker イメージを明示的に指定できる # customImage: 'unityci/editor:2020.1.14f1-base-0' # テストの実行結果をアーティファクトにアップロードしてダウンロードして参照できるようにする - uses: actions/upload-artifact@v2 if: always() with: name: Test results path: artifacts </code></pre> <p>上記のワークフローファイルを GitHub Actions 上で動作検証する際の手順は下記になります。</p> <p><img src="https://i.gyazo.com/9bace70734f1e99955f5b9aa068b31de.png" alt="1. Unity のテストを実行するためのワークフローを選択して実行する" /><br /> <strong>1. Unity のテストを実行するためのワークフローを選択して実行する</strong></p> <p><img src="https://i.gyazo.com/ffc31b5919431fedb516f1932a13ce62.png" alt="2. ワークフローの実行が成功したら、詳細画面に遷移した後、<code>Test Results</code> の項目からテストの実行結果を確認する" /><br /> <strong>2. ワークフローの実行が成功したら、詳細画面に遷移した後、<code>Test Results</code> の項目からテストの実行結果を確認する</strong></p> <p>テスト実行用のワークフローファイルでは <a target="_blank" rel="nofollow noopener" href="https://docs.github.com/ja/actions/reference/events-that-trigger-workflows#manual-events"><code>workflow_dispatch</code></a> で実行可能にしていますが、<code>pull_request</code> を利用すればプルリク時にテストを実行させることが可能になります。</p> <h2 id="Builder: プロジェクトのビルドを実行して出力結果を確認する"><a href="#Builder%3A+%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E3%83%93%E3%83%AB%E3%83%89%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%A6%E5%87%BA%E5%8A%9B%E7%B5%90%E6%9E%9C%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B">Builder: プロジェクトのビルドを実行して出力結果を確認する</a></h2> <p>GameCI にはプロジェクトのビルドを行うための GitHub Actions も用意されています。<strong>実際に GameCI で WebGL ビルドを行いその内容を GitHub Pages で確認できるようにして動作検証していきます。</strong></p> <p>早速 WebGL ビルドを行うためのワークフローファイルを作成していきます。</p> <pre><code class="yml"># .github/workflows/webgl_build.yml name: Run the WebGL build on: workflow_dispatch: {} jobs: build: name: Run the WebGL build runs-on: ubuntu-latest steps: # actions/checkout@v2 を利用して作業ディレクトリに # Unity プロジェクトの中身をダウンロードしてくる - name: Check out my unity project. uses: actions/checkout@v2 # GameCI の Builder を利用して、 # Unity プロジェクトのビルドを実行する - name: Run the WebGL build uses: game-ci/unity-builder@v2 env: UNITY_LICENSE: $<span>{</span><span>{</span> secrets.UNITY_LICENSE <span>}</span><span>}</span> with: # 今回は WebGL ビルドを行いたいため WebGL を指定する # WebGL 以外の指定可能な値は下記に記載の値が利用可能 # ref: https://docs.unity3d.com/ScriptReference/BuildTarget.html targetPlatform: WebGL unityVersion: 2020.3.5f1 # Builder で出力した WebGL ビルドを GitHub Pages にデプロイする - name: Deploy to GitHub Pages uses: JamesIves/[email protected] with: # GitHub Pages デプロイ用の Orphan ブランチ名を指定する branch: gh-pages # デプロイ用ビルドフォルダパスを指定する # GameCI の Builder はデフォルトでは build フォルダにビルド内容を出力する folder: build # Builder で出力した WebGL ビルドをアーティファクトでダウンロード可能にする - name: Upload the WebGL Build uses: actions/upload-artifact@v2 with: name: Build path: build </code></pre> <p>上記のワークフローファイルを GitHub Actions 上で動作検証する際の手順は下記になります。</p> <p><img src="https://i.gyazo.com/b8f43baef2e2edbf8d5510ce8f58172d.png" alt="1. Unity の WebGL ビルドを実行するためのワークフローを実行する" /><br /> <strong>1. Unity の WebGL ビルドを実行するためのワークフローを実行する</strong></p> <p><img src="https://i.gyazo.com/a102d1f5cb4d0581746770d1d9cad965.png" alt="2. ワークフローの実行が成功したら、詳細画面に遷移した後、ビルド内容が正常か確認する" /><br /> <strong>2. ワークフローの実行が成功したら、詳細画面に遷移した後、ビルド内容が正常そうか確認する</strong></p> <p><img src="https://i.gyazo.com/7d39c035b52ba3862d5dad18213a2041.png" alt="3. ビルド内容を確認するための GitHub Pages の設定を Settings から行う" /><br /> <strong>3. ビルド内容を確認するための GitHub Pages の設定を Settings から行う</strong></p> <p><img src="https://i.gyazo.com/b2e1bd86ca9d3b3f207a5ddbb1de3ba7.png" alt="4. GitHub Pages でブラウザから WebGL ビルドの動作確認をする" /><br /> <strong>4. GitHub Pages でブラウザから WebGL ビルドの動作確認をする</strong></p> <p><strong>上記のように Builder を利用することで WebGL ビルドの成否及び、最新のビルド内容を常に GitHub Pages で見られるようにできます。</strong> すると WebGL ビルドが正常かどうかの確認が常に GitHub Pages を見れば把握できるようになるため、<a target="_blank" rel="nofollow noopener" href="https://unityroom.com/unity1weeks">Unity1 週間ゲームジャム</a> などに参加する際で便利に活用できそうです。</p> <p><em>WebGL ビルドを行う際、Unity バージョンやアセットの対応状況によっては正しくブラウザ上で動作しないビルドが出力されます。<strong>ただし、ブラウザで発生するエラー内容によっては WebGL のビルド設定を見直すだけで解決できる場合があります。</strong> 例えば <code>unityframework is not defined</code> というエラーが発生した際は、<a target="_blank" rel="nofollow noopener" href="https://qiita.com/aguroshou0413/items/1451a6779a92acb96b78">この記事</a> のように WebGL の <code>Build Settings</code> を見直すことで解決できる場合があります。</em></p> <p><em>私の環境では <a target="_blank" rel="nofollow noopener" href="https://github.com/JamesIves/github-pages-deploy-action"><code>JamesIves/github-pages-deploy-action</code></a> で GitHub Pages へのデプロイを行った際、<strong>デフォルトでは <code>/WebGL/WebGL</code> フォルダにビルド内容が出力されました。そのため、ブラウザから WebGL ビルドにアクセスする際、<code><GitHub Pages の URL>/WebGL/WebGL</code> のような URL にアクセスする必要がありました。</strong></em></p> <h2 id="Returning a license: GameCI で利用している Unity License を返却する"><a href="#Returning+a+license%3A+GameCI+%E3%81%A7%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B+Unity+License+%E3%82%92%E8%BF%94%E5%8D%B4%E3%81%99%E3%82%8B">Returning a license: GameCI で利用している Unity License を返却する</a></h2> <p><strong>通常利用することは無いと<a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/github/returning-a-license">公式サイト</a>にも書かれていますが、Professional License の返却も GameCI で行うことが可能です。</strong> 今回は Personal License を利用したため使用しませんでしたが、下記をワークフローのステップに組み込むことで Professional License を返却できるようです。</p> <pre><code class="yml"># ... # どこかのタイミングでライセンスのアクティベートを行う - name: Activate Unity uses: game-ci/unity-activate@v1 env: UNITY_LICENSE: $<span>{</span><span>{</span> secrets.UNITY_LICENSE <span>}</span><span>}</span> #... # ステップの最後などに game-ci/unity-return-license@v1 を呼び出して # アクティベート済みのライセンスを返却する - name: Return license uses: game-ci/unity-return-license@v1 if: always() #... </code></pre> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>以前 Unity コマンドを駆使して自分で CI 環境を構築した経験があるのですが、<br /> GameCI を利用した方が全然楽に Unity CI 環境構築を GitHub Actions 上で行えました。</p> <p>ちなみに <a target="_blank" rel="nofollow noopener" href="https://game.ci/docs/docker/docker-images">GameCI で利用されている Docker イメージ</a> は以前からよく使われていた <a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/r/gableroux/unity3d/">gableroux/unity3d</a> が元になっているようでした。ってか <a target="_blank" rel="nofollow noopener" href="https://gableroux.com/about/">GabLeRoux さんのホームページ</a> を見たら、GameCI の開発を始めた方のようでした。すごい。</p> <p>本記事が GitHub Actions で Unity CI 環境構築を始めようとしている方の助けになれれば幸いです。</p> <h1 id="参考リンク"><a href="#%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF">参考リンク</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://game.ci/">GameCI - The fastest and easiest way to automatically test and build your game projects</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://unity3d.com/jp/unity/features/cloud-build">Services - Cloud Build - Unity</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/cloudformation/">AWS CloudFormation(テンプレートを使ったリソースのモデル化と管理)| AWS</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://fastlane.tools/">fastlane - App automation done right</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.github.com/ja/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/managing-the-default-branch-name-for-your-repositories#about-management-of-the-default-branch-name">リポジトリのデフォルトブランチ名を管理する - GitHub Docs</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://blog.yucchiy.com/2019/01/08/how-to-get-unity-free-license/">Unity でパーソナルライセンスのシリアルナンバーを発行する | Yucchiy's Note</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://license.unity3d.com">Unity license manual activation webpage</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.github.com/ja/actions/reference/encrypted-secrets">暗号化されたシークレット - GitHub Docs</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.unity3d.com/ScriptReference/BuildTarget.html">Unity - Scripting API: BuildTarget</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://unityroom.com/unity1weeks">Unity 1 週間ゲームジャム | フリーゲーム投稿サイト unityroom</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/aguroshou0413/items/1451a6779a92acb96b78">Unity2020 WebGL 9 割まで読み込めるがアプリが起動しない不具合の解決方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/marketplace/actions/deploy-to-github-pages">Deploy to GitHub Pages · Actions · GitHub Marketplace</a></li> </ul> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p><code>alf</code> ファイル及び <code>ulf</code> ファイルの実態は XML ファイルです。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>適当なテキストエディタで <code>ulf</code> ファイルを開き全文をコピー & ペーストします。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> nikaera tag:crieit.net,2005:PublicArticle/16720 2021-03-06T10:10:58+09:00 2021-03-06T10:10:58+09:00 https://crieit.net/posts/Unity-6042d6a2eaa82 Unityで複数の入力方法に対応するタイピングゲームを作ろう <h2 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h2> <p>はじめまして、情報系の大学に通っているプログラム初心者の大学生です。</p> <p>2年前にゼミ内で小さなゲームジャムが開催され、タイピングゲームを作ることになったのですが、ネット上で紹介されている既存のタイピングゲームの制作方法では入力方法に以下のような問題があると思いました。</p> <blockquote> <p><strong>1. 単語を打つ手段があらかじめ決まっている。</strong><br /> 例 : "し"と打つときに"si"と打たなければいけなく、"shi"には対応していない。</p> <p><strong>2.単語を別々に入力できない。</strong><br /> 例 : "ちゃ"と打つときに"ち"と"ゃ"を別々にタイプできない。</p> </blockquote> <p>そこで今回はどんな打ち方にも対応できるタイピングゲームの制作に挑戦してみました。<br /> この記事では仕組みとプログラムに関して紹介していきます、参考にしていただければ幸いです。</p> <h2 id="参考にした作品"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E4%BD%9C%E5%93%81">参考にした作品</a></h2> <p>今回制作したプログラムでは「寿司打」のタイピングシステムを参考にしています。</p> <blockquote> <h5 id="寿司打"><a href="#%E5%AF%BF%E5%8F%B8%E6%89%93">寿司打</a></h5> <p><a target="_blank" rel="nofollow noopener" href="http://typingx0.net/sushida/play.html">こちらからプレイできます。</a></p> </blockquote> <p>どのようなシステムかというと</p> <blockquote> <p><a href="https://crieit.now.sh/upload_images/fa30eaea18027ff67c2127f6331493a76041ad3275209.PNG" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/fa30eaea18027ff67c2127f6331493a76041ad3275209.PNG?mw=700" alt="キャプチャ1.PNG" /></a><br /> ゲーム画面ではこのようにタイプする文字と入力方法が表示されています。<br /> <strong>この状態で"し"を"si"ではなく"shi"と入力しようとすると...</strong></p> <p><a href="https://crieit.now.sh/upload_images/bebc0f0840b25025f197a22b24571bb26041ae559e71d.PNG" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/bebc0f0840b25025f197a22b24571bb26041ae559e71d.PNG?mw=700" alt="キャプチャ3.PNG" /></a><br /> <strong>"sh"と入力した時点で1つ目の"し"の入力方法が"shi"に置き換わります。</strong><br /> (2つ目の"し"の入力方法は変わらず表示されている。)</p> </blockquote> <p>他にも</p> <blockquote> <p><a href="https://crieit.now.sh/upload_images/2f27d696448dc1a99860914e503c80d76041b0d905c92.PNG" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2f27d696448dc1a99860914e503c80d76041b0d905c92.PNG?mw=700" alt="キャプチャ4.PNG" /></a><br /> <strong>"ック"を入力する際に、"xtu"と"ku"で別々に入力しようとすると...</strong></p> <p><a href="https://crieit.now.sh/upload_images/f414647101ae6ff6105459a3b582bd3b6041b26740466.PNG" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f414647101ae6ff6105459a3b582bd3b6041b26740466.PNG?mw=700" alt="キャプチャ5.PNG" /></a><br /> <strong>"xtu"の"x"を入力した時点で”ッ”と"ク"を別々と考えて入力方法を置き換えています。</strong></p> </blockquote> <h2 id="プログラム"><a href="#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0">プログラム</a></h2> <p>筆者なりに上記のシステムを再現しようとすると以下のようなプログラムになりました。</p> <h3 id="Kanji.txt"><a href="#Kanji.txt">Kanji.txt</a></h3> <blockquote> <p>タイピングゲーム内で使う文章をそのまま入力します。<br /> 一度のタイピングで入力する長さを決めて改行してください。</p> </blockquote> <pre><code>別に君を求めてないけど 横にいられると思いだす。 君のドルチェ&ガッパーナの その香水のせいだよ。 </code></pre> <h3 id="Japanese.txt"><a href="#Japanese.txt">Japanese.txt</a></h3> <blockquote> <p>Kanji.txtの内容を全てひらがなにして入力してください。</p> </blockquote> <pre><code>べつにきみをもとめてないけど よこにいられるとおもいだす。 きみのどるちぇあんどがっぱーなの そのこうすいのせいだよ。 </code></pre> <h3 id="KanjiFuri.txt"><a href="#KanjiFuri.txt">KanjiFuri.txt</a></h3> <blockquote> <p>Kanji.txt内の文字をひらがなにした時、何文字になるかを入力してください(ひらがなの場合は1)</p> </blockquote> <pre><code>21212111111 211111121111 2111113111111 1122111112 </code></pre> <h3 id="Dictionary.cs"><a href="#Dictionary.cs">Dictionary.cs</a></h3> <blockquote> <p>全ての入力方法を連想配列で整理しています。<br /> 下の方にある関数はKeyかValueを渡すことで紐づけられた要素を全て取り出すことができます</p> </blockquote> <pre><code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class Dictionary : MonoBehaviour { //デフォルトの入力方法 public readonly Dictionary<string, string> dic = new Dictionary<string, string>() { {"あ", "a"},{"い", "i"},{"う", "u"},{"え", "e"},{"お", "o"}, {"か", "ka"},{"き", "ki"},{"く", "ku"},{"け", "ke"},{"こ", "ko"}, {"さ", "sa"},{"し", "si"},{"す", "su"},{"せ", "se"},{"そ", "so"}, {"た", "ta"},{"ち", "ti"},{"つ", "tu"},{"て", "te"},{"と", "to"}, {"な", "na"},{"に", "ni"},{"ぬ", "nu"},{"ね", "ne"},{"の", "no"}, {"は", "ha"},{"ひ", "hi"},{"ふ", "hu"},{"へ", "he"},{"ほ", "ho"}, {"ま", "ma"},{"み", "mi"},{"む", "mu"},{"め", "me"},{"も", "mo"}, {"や", "ya"},{"ゆ", "yu"},{"よ", "yo"}, {"ら", "ra"},{"り", "ri"},{"る", "ru"},{"れ", "re"},{"ろ", "ro"}, {"わ", "wa"},{"を", "wo"},{"ん", "n"}, {"が", "ga"},{"ぎ", "gi"},{"ぐ", "gu"},{"げ", "ge"},{"ご", "go"}, {"ざ", "za"},{"じ", "zi"},{"ず", "zu"},{"ぜ", "ze"},{"ぞ", "zo"}, {"だ", "da"},{"ぢ", "di"},{"づ", "du"},{"で", "de"},{"ど", "do"}, {"ば", "ba"},{"び", "bi"},{"ぶ", "bu"},{"べ", "be"},{"ぼ", "bo"}, {"ぱ", "pa"},{"ぴ", "pi"},{"ぷ", "pu"},{"ぺ", "pe"},{"ぽ", "po"}, {"ぁ","xa" },{"ぃ","xi" },{"ぅ","xu" },{"ぇ","xe" },{"ぉ","xo" }, {"っ", "xtu"}, {"ゃ","xya" },{"ゅ","xyu" },{"ょ","xyo"}, {"きゃ","kya"},{"きぃ","kyi"},{"きゅ","kyu"},{"きぇ","kye"},{"きょ","kyo"}, {"しゃ","sya"},{"しぃ","syi"},{"しゅ","syu"},{"しぇ","she"},{"しょ","syo"}, {"ちゃ","tya"},{"ちぃ","tyi"},{"ちゅ","tyu"},{"ちぇ","tye"},{"ちょ","tyo"}, {"にゃ","nya"},{"にぃ","nyi"},{"にゅ","nyu"},{"にぇ","nye"},{"にょ","nyo"}, {"ひゃ","hya"},{"ひぃ","hyi"},{"ひゅ","hyu"},{"ひぇ","hye"},{"ひょ","hyo"}, {"みゃ","mya"},{"みぃ","myi"},{"みゅ","myu"},{"みぇ","mye"},{"みょ","myo"}, {"りゃ","rya"},{"りぃ","ryi"},{"りゅ","ryu"},{"りぇ","rye"},{"りょ","ryo"}, {"ぎゃ","gya"},{"ぎぃ","gyi"},{"ぎゅ","gyu"},{"ぎぇ","gye"},{"ぎょ","gyo"}, {"じゃ","zya"},{"じぃ","zhi"},{"じゅ","zyu"},{"じぇ","zye"},{"じょ","zyo"}, {"ぢゃ","dya"},{"ぢぃ","dyi"},{"ぢゅ","dyu"},{"ぢぇ","dye"},{"ぢょ","dyo"}, {"びゃ","bya"},{"びぃ","byi"},{"びゅ","byu"},{"びぇ","bye"},{"びょ","byo"}, {"てゃ","tha"},{"てぃ","thi"},{"てゅ","thu"},{"てぇ","the"},{"てょ","tho"}, {"うぁ","wha"},{"うぃ","whi"},{"うぇ","whe"},{"うぉ","who"}, {"でゃ","dha"},{"でぃ","dhi"},{"でゅ","dhu"},{"でぇ","dhe"},{"でょ","dho"}, {"くぁ","qa"},{"くぃ","qi"},{"くぇ","qe"},{"くぉ","qo"}, {"ふぁ","fa"},{"ふぃ","fi"},{"ふぇ","fe"},{"ふぉ","fo"}, {"ヴぁ","va"},{"ヴぃ","vi"},{"ヴ","vu"},{"ヴぇ","ve"},{"ヴぉ","vo"}, {"ぴゃ","pya"},{"ぴぃ","pyi"},{"ぴゅ","pyu"},{"ぴぇ","pye"},{"ぴょ","pyo"}, {"、","," },{"。","."},{"「","["},{"」","]"}, }; //デフォルトではない入力方法 public readonly Dictionary<string, string> Epicdic = new Dictionary<string, string>() { {"ca","か" },{"ci","し" },{"cu","く" },{"ce","せ" },{"co","こ" }, {"cha","ちゃ"},{"chi","ち"},{"chu","ちゅ"},{"che","ちぇ"},{"cho","ちょ"}, {"cya","ちゃ"},{"cyi","ちぃ"},{"cyu","ちゅ"},{"cye","ちぇ"},{"cyo","ちょ"}, {"fu","ふ"}, {"ja","じゃ"},{"ji","じ"},{"ju","じゅ"},{"je","じぇ"},{"jo","じょ"}, {"la","ぁ" },{"li","ぃ" },{"lu","ぅ" },{"le","ぇ" },{"lo","ぉ" }, {"lya","ゃ" },{"lyu","ゅ" },{"lyo","ょ" }, {"ltu", "っ"}, {"nn","ん" }, {"qu","く" },{"qyi","くぃ"},{"qye","くぇ"}, {"sha","しゃ" },{"shi","し"},{"shu","しゅ"},{"she","しぇ"},{"sho","しょ"}, {"tsu","つ"}, {"yi","い"},{"ye","え"}, }; //渡した要素と紐づいている要素をdicから探し、リストにして返します。 public KeyValuePair<string, string> SearchdicKey(string key) { var dicpair = new KeyValuePair<string, string>(); foreach (KeyValuePair<string, string> pair in dic) { if (key == pair.Key) { dicpair = pair; } } return dicpair; } //渡した要素と紐づいている要素をdicから探し、リストにして返します。 public KeyValuePair<string, string> SearchdicValue(string value) { var dicpair = new KeyValuePair<string, string>(); foreach (KeyValuePair<string, string> pair in dic) { if (value == pair.Value) { dicpair = pair; } } return dicpair; } //渡した要素と紐づいている要素を両方のdicから探し、リストにして返します。 public List<KeyValuePair<string, string>> SearchTotaldicKey(string key) { var dicpair = new List<KeyValuePair<string, string>>(); foreach (KeyValuePair<string, string> pair in Epicdic) { if (key == pair.Value) { dicpair.Add(new KeyValuePair<string, string>(pair.Value, pair.Key)); } } foreach (KeyValuePair<string, string> pair in dic) { if (key == pair.Key) { dicpair.Add(pair); } } return dicpair; } //渡した要素と紐づいている要素を両方のdicから探し、リストにして返します。 public List<KeyValuePair<string, string>> SearchTotaldicValue(string value) { var dicpair = new List<KeyValuePair<string, string>>(); foreach (KeyValuePair<string, string> pair in Epicdic) { if (value == pair.Value) { dicpair.Add(pair); } } foreach (KeyValuePair<string, string> pair in dic) { if (value == pair.Key) { dicpair.Add(new KeyValuePair<string, string>(pair.Value, pair.Key)); } } return dicpair; } } </code></pre> <h3 id="SetList.cs"><a href="#SetList.cs">SetList.cs</a></h3> <blockquote> <p>次にタイピングするKanjij.txtと同じ行にあるJapanese.txtとKanjiFuri.txtの文字列をSetLists()に渡し、それぞれを区分しながらListに要素を追加していきます。</p> <p>・Japanese.txtは一度に入力可能でありながら、文字列が最長になるように区分し、FuriListに追加していきます。<br /> ・KanjiFuri.txtは1つの数字ごとに文字列を区分しながらKanjiFuriListに追加していきます。<br /> ・また、FuriListに要素を追加しながらDictionary.dicから入力方法を取得し、RomaListに追加していきます。</p> </blockquote> <pre><code>using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SetList : MonoBehaviour { private SetText ST; private Dictionary Dic; public List<string> RomaList; public List<string> FuriList; public List<int> KanjiFuriList; void Awake() { ST = GetComponent<SetText>(); Dic = GetComponent<Dictionary>(); } private string FuriStr = ""; private string RomaStr = ""; //FuriStrとRomaStrの内容をそれぞれのリストに入れて初期化する。 private void AddList() { FuriList.Add(FuriStr); RomaList.Add(RomaStr); FuriStr = ""; RomaStr = ""; } //渡されたJapanese.txtの一行を一文字づつ仕分けるよう public void SetLists(string Roma , string KanjiFuri) { for(int i = 0; i < Roma.Length - 1; i++) { var chr = Roma[i].ToString(); switch (chr) { case "っ": FuriStr += chr; if (i != Roma.Length - 1) { switch(Roma[i + 1].ToString()) { case "あ": case "い": case "う": case "え": case "お": case "な": case "に": case "ぬ": case "ね": case "の": case "ん": RomaStr += Dic.SearchdicKey(Roma[i + 1].ToString()).Value; AddList(); break; default: RomaStr += Dic.SearchdicKey(Roma[i + 1].ToString()).Value.Substring(0,1); break; } } else { FuriStr += chr; AddList(); } break; case "ぁ": case "ぃ": case "ぅ": case "ぇ": case "ぉ": case "ゃ": case "ゅ": case "ょ": if (i != 0 && FuriStr != "") { if (Dic.dic.ContainsKey(FuriStr + chr)) { FuriStr += chr; RomaStr += Dic.SearchdicKey(Roma[i - 1].ToString() + chr).Value; AddList(); } else { AddList(); FuriStr += chr; RomaStr += Dic.SearchdicKey(chr).Value; AddList(); } } else { FuriStr += chr; RomaStr += Dic.SearchdicKey(chr).Value; AddList(); } break; case "ん": FuriStr += chr; RomaStr += Dic.SearchdicKey(chr).Value; if (i != Roma.Length - 1) { switch(Roma[i + 1].ToString()) { case "あ": case "い": case "う": case "え": case "お": case "な": case "に": case "ぬ": case "ね": case "の": case "ん": case "や": case "ゆ": case "よ": case "ゃ": case "ゅ": case "ょ": RomaStr += "n"; break; } } else { RomaStr += "n"; } AddList(); break; default: if (i != Roma.Length - 1) { switch (Roma[i + 1].ToString()) { case "ぁ": case "ぃ": case "ぅ": case "ぇ": case "ぉ": case "ゃ": case "ゅ": case "ょ": FuriStr += chr; break; default: FuriStr += chr; RomaStr += Dic.SearchdicKey(chr).Value; AddList(); break; } } else { FuriStr += chr; RomaStr += Dic.SearchdicKey(chr).Value; AddList(); } break; } } int num = 0; for(int i = 0; i < KanjiFuri.Length - 1; i++) { num += KanjiFuri[i] - '0'; KanjiFuriList.Add(num); } } } </code></pre> <h3 id="SetText.cs"><a href="#SetText.cs">SetText.cs</a></h3> <blockquote> <p>txtファイルや表示中の文字列の行数、打ち終わった字数などを管理しています。</p> </blockquote> <pre><code>using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SetText : MonoBehaviour { private SetList SL; private TypingSystem TS; [SerializeField] private TextAsset Furigana = null; //ここにKanjiFuri.txtを入れる [SerializeField] private TextAsset Hiragana = null; //ここにJapanese.txtを入れる [SerializeField] private TextAsset Kanji = null; //ここにKanji.txtを入れる [SerializeField] private List<string> FuriList = new List<string>(); [SerializeField] private List<string> HiraList = new List<string>(); [SerializeField] private List<string> KanjiList = new List<string>(); [SerializeField] private Text RomaText = null; //ここにKanjiFuri.txtの内容を表示するためのテキストを入れる [SerializeField] private Text FuriText = null; //ここにJapanese.txtの内容を表示するためのテキストを入れる [SerializeField] private Text KanjiText = null; //ここにKanji.txtの内容を表示するためのテキストを入れる [SerializeField] private int ColumunNum = -1; //現在打っている文字列の行数、最初に+1するため最初は-1にする private int SetColumunNum { get { return ColumunNum; } set { ColumunNum = value; SL.SetLists(HiraList[ColumunNum],FuriList[ColumunNum]); KanjiProp = 0; ListProp = 0; FuriProp = 0; RomaProp = 0; } } public int Romanum { get; private set; } public int Furinum { get; private set; } public int Kanjinum { get; private set; } public int Listnum { get; private set; } //メインの文字の設定 private int KanjiProp { get { return Kanjinum; } set { Kanjinum = value; var kanjifuristr = "<color=\"red\">"; kanjifuristr += KanjiList[ColumunNum].Insert(Kanjinum, "</color>"); KanjiText.text = kanjifuristr; } } //フリガナの文字の設定 private int FuriProp { get { return Furinum; } set { Furinum = value; var furistr = "<color=\"red\">"; furistr += HiraList[ColumunNum].Insert(Furinum, "</color>"); FuriText.text = furistr; while (Furinum >= SL.KanjiFuriList[Kanjinum]) { KanjiProp++; } } } //SetList.FuriListの何個目の要素を打っているか private int ListProp { get { return Listnum; } set { if(value == SL.FuriList.Count) { ResetText(); return; } else if(value != 0) { FuriProp += SL.FuriList[Listnum].Length; } Listnum = value; } } //ローマ字の設定 public int RomaProp { get { return Romanum; } set { Romanum = value; if (Romanum == SL.RomaList[Listnum].Length) { ListProp++; TS.ResetTypedStr(); RomaProp = 0; } else { var romastr = "<color=\"red\">"; for (int i = 0; i < SL.RomaList.Count; i++) { var str = SL.RomaList[i]; if (i != Listnum) { romastr += str; } else { romastr += str.Insert(Romanum, "</color>"); } } RomaText.text = romastr; } } } private void Start() { SL = GetComponent<SetList>(); TS = GetComponent<TypingSystem>(); SetStrList(Hiragana, HiraList); SetStrList(Furigana, FuriList); SetStrList(Kanji, KanjiList); ResetText(); void SetStrList(TextAsset textAsset, List<string> list) { var array = textAsset.text.Split('\n'); list.AddRange(array); } } //テキストを初期化する private void ResetText() { SL.FuriList.Clear(); SL.RomaList.Clear(); SL.KanjiFuriList.Clear(); SetColumunNum++; } } </code></pre> <h3 id="TypingSystem.cs"><a href="#TypingSystem.cs">TypingSystem.cs</a></h3> <blockquote> <p>txtファイルや表示中の文字列の行数、打ち終わった字数などを管理しています。</p> </blockquote> <pre><code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class TypingSystem : MonoBehaviour { private SetList SL; private SetText ST; private Dictionary Dic; private string Typedstr; public void ResetTypedStr() { Typedstr = ""; } private void Awake() { ST = GetComponent<SetText>(); SL = GetComponent<SetList>(); Dic = GetComponent<Dictionary>(); } string[] keys = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "-", ",",".","[","]"}; //keys内にあるキーを打った時 void Update() { foreach (string key in keys) { if (Input.GetKeyDown(key)) { TypeJudge(key); } } } public void TypeJudge(string type) { //打っている途中の文字と現在打った文字をつなげる var JudgeStr = Typedstr + type; bool jud = false; //打った文字が n で 前の文字が nn じゃない場合 if (type == "n" && SL.RomaList[ST.Listnum] == "n") { SL.RomaList[ST.Listnum] = "nn"; RightEnter(); return; } //正しく打てている場合 if (type == SL.RomaList[ST.Listnum][ST.RomaProp].ToString()) { RightEnter(); } else { //Dictionaryの中のEpicDic内の文字を打とうとしている場合 for (int i = SL.FuriList[ST.Listnum].Length; i != 0 && !jud ; i--) { var SearchStr = SL.FuriList[ST.Listnum].Substring(0,i); if (i != 1 && SL.FuriList[ST.Listnum][0].ToString() == "っ") { SearchStr = SearchStr.Substring(1, SearchStr.Length-1); } var list = Dic.SearchTotaldicKey(SearchStr); foreach (var d in list) { var MatchStr = ""; if (i != 1 && SL.FuriList[ST.Listnum][0].ToString() == "っ") { MatchStr += d.Value.Substring(0, 1); } if (JudgeStr.Length <= MatchStr.Length + d.Value.Length && !jud) { MatchStr += d.Value; if (JudgeStr == MatchStr.Substring(0, JudgeStr.Length)) { //print(MatchStr + "で見つかりました"); TypeMatch(i , MatchStr); } } } } //該当した文字が無い場合(ミスタイプしている場合) if(!jud) { MissEnter(); } } //EpicDic内にある文字が正しく打てている時に呼び出される //打った文字とまだ打っていない文字を切り分ける作業をする void TypeMatch(int num, string MatchStr) { jud = true; var Furistr = SL.FuriList[ST.Listnum]; SL.FuriList[ST.Listnum] = Furistr.Substring(0, num); SL.RomaList[ST.Listnum] = MatchStr; if(Furistr.Length != num) { SL.FuriList.Insert(ST.Listnum + 1, Furistr.Substring(num)); SL.RomaList.Insert(ST.Listnum + 1, Dic.SearchdicKey(SL.FuriList[ST.Listnum + 1]).Value); } Typedstr = JudgeStr; RightEnter(); } //ミスタイプしたとき void MissEnter() { print("ミスしました!"); } //正しく打てているとき void RightEnter() { Typedstr = JudgeStr; ST.Romanum++; } } } </code></pre> <h2 id="実際に動かしてみる"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AB%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">実際に動かしてみる</a></h2> <p>上にあるプログラムを全て同じオブジェクトにアタッチし、以下の画像のように設定すると動きます。<br /> <a href="https://crieit.now.sh/upload_images/003fafc46d6d8a6b98c5c0b37c69efcc6042d4410d2b1.PNG" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/003fafc46d6d8a6b98c5c0b37c69efcc6042d4410d2b1.PNG?mw=700" alt="キャプチャ.PNG" /></a></p> <h2 id="完成した作品"><a href="#%E5%AE%8C%E6%88%90%E3%81%97%E3%81%9F%E4%BD%9C%E5%93%81">完成した作品</a></h2> <p>そしてこのプログラムを組み込んで実際に完成した作品がこちらになります。</p> <blockquote> <h5 id="ケロツグ -雨乞いタイピング-"><a href="#%E3%82%B1%E3%83%AD%E3%83%84%E3%82%B0+-%E9%9B%A8%E4%B9%9E%E3%81%84%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0-">ケロツグ -雨乞いタイピング-</a></h5> <p><a target="_blank" rel="nofollow noopener" href="https://game.nicovideo.jp/atsumaru/games/gm13342">Webからのプレイはこちらから。</a><br /> <a target="_blank" rel="nofollow noopener" href="https://drive.google.com/drive/folders/1rJD0wXc3cIBas-bnDUSY_-UlANOCqGbo?usp=sharing">プロジェクトのダウンロードはこちらから</a></p> </blockquote> <p>芥川龍之介の「蛙」の文をひたすら打つタイピングゲームです。<br /> 文字を打つたびに蛙が増えていき、正確なタイピングを続けると制限時間が増えたりします。</p> <h2 id="最後に"><a href="#%E6%9C%80%E5%BE%8C%E3%81%AB">最後に</a></h2> <p>作るのは苦戦しましたが内容は結構シンプルな構造になっていると思います。<br /> この方法で作っていると対応していないキーが出てくると思います。<br /> 見つけた場合はDisctionary.csの方に書き足して頂けると対応されると思います。</p> <p>最後まで読んでいただきありがとうございました。</p> かの tag:crieit.net,2005:PublicArticle/16261 2020-12-05T16:08:03+09:00 2020-12-10T08:30:04+09:00 https://crieit.net/posts/dc0eb0f0a74f6e15e362e1f384d28f0d 柔軟な入力方法に対応したタイピングゲームの作り方 <p>この記事は<a target="_blank" rel="nofollow noopener" href="https://adventar.org/calendars/5226" target="_blank">「Unityゲーム開発者ギルド2 Advent Calender 2020」</a>12日目の記事です。</p> <h2 id="まえがき"><a href="#%E3%81%BE%E3%81%88%E3%81%8C%E3%81%8D">まえがき</a></h2> <p>おはようございます。こんにちは。こんばんは。そしてはじめまして。アキオと申します。ちょうど記事を公開した12月12日が私の誕生日でして、今回で26歳になりました。この26歳の間はとにかくUnityとRailsを触って、様々なゲームやサービスを多数公開運営したいと思います。</p> <p>早速質問ですが、皆様はタイピングゲームはお好きでしょうか?</p> <p>最近はPC離れも加速してモバイルゲームが主流となった今、タイピングゲームは減少傾向です。けれでもPCメインのunityroomは、同じPCメインのタイピングゲームとの相性は抜群です。特にunity1weekに毎回参加している人々は、タイピングゲームを作ってみたいとは思ったことがあるはずです。</p> <p>けれどもunity1weekで300~500近い作品が公開される中でタイピングゲームは多くて3作品しかありません。全体では8000近いゲームがunity1weekで公開されていますが。タイピングゲームは1%しか存在しません。</p> <p>では、なぜ相性抜群のタイピングゲームがunityroomに少ないかというと<br /> 「実力差が大きい」「ありがちなゲームになる」という理由もあるかと思いますが、何よりも「作るのが難しい」のではないかと私は思います。</p> <p>というのも現在ネット上に公開されている日本語入力のタイピングゲームのほぼ全てが、「し」を「shi」、「つ」を「tsu」と入力できます。</p> <p>そのような柔軟な入力方法を実装するのが難しいから、タイピングゲームが少ないのではないかと私は思います。</p> <p>逆を言えば、その柔軟な入力方法に対応していないタイピングゲームの作成は簡単だと私は思います。けれどもそれでは「やりにくい」「今時のタイピングゲームではない」という感想を抱えて、作るのをためらうのではないでしょうか?</p> <p>なので、今回はunityroom等にタイピングゲームを増やすべく、その柔軟な入力方法に対応したタイピングゲームの作り方を紹介したいと思います。</p> <p>対象読者は、ある程度Unityが触れて、TextMeshPro等などが使えることを想定しています。なのでちょっと説明が雑かもしれませんが、ご了承願います。</p> <h2 id="とりあえずUnityを起動してタイピングゲームを作ろう!"><a href="#%E3%81%A8%E3%82%8A%E3%81%82%E3%81%88%E3%81%9AUnity%E3%82%92%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%86%EF%BC%81">とりあえずUnityを起動してタイピングゲームを作ろう!</a></h2> <p>まず普段通りにプロジェクトを作成します。(2Dでも3Dでも可能)今回はTextMeshProを使いますので、TextMeshProをインポートして、TextMeshProのGameObject「TextMeshProTitle」と「TextMeshProRoman」のオブジェクトを作成します。</p> <p>そして「TypingManager.cs」を作成します。そして空のGameObject「TypingManager」をHierarchyにアタッチして、そのGameObjectに先ほど作成した「TypingManager.cs」をInspector上でアタッチします。</p> <p>ここからは、延々と「TypingManager.cs」にコードを書きます。</p> <h3 id="タイピングゲームの要となる「OnGUI()」を定義しよう!"><a href="#%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0%E3%82%B2%E3%83%BC%E3%83%A0%E3%81%AE%E8%A6%81%E3%81%A8%E3%81%AA%E3%82%8B%E3%80%8COnGUI%28%29%E3%80%8D%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%97%E3%82%88%E3%81%86%EF%BC%81">タイピングゲームの要となる「OnGUI()」を定義しよう!</a></h3> <p>Unityではスクリプトの生成時に最初から存在する<code>Update()</code>というイベント関数が存在しますが、これは毎フレーム呼び出される関数なので、1フレームあたりに2回以上の入力が発生すると、2回目以降のキー入力が検知できないので、基本使うべきではありません。<br /> なので今回は、Unityでは標準で<code>OnGUI()</code>というイベント関数を使います。この関数はキーボードやマウスのクリックならびにクリック解除の時に呼び出されます。今回はあくまでもキーの入力時のみに処理を行いたいので<code>if</code>を使って以下のように書きます。</p> <pre><code class="csharp">private void OnGUI() { if (Event.current.type == EventType.KeyDown) { // キーが入力された時に処理される。 } } </code></pre> <p><code>OnGUI()</code>関数に<code>Event.current.type == EventType.KeyDown</code>という条件式を書けば、キーの入力時のみ<code>true</code>になり、処理が実行されるようになります。</p> <p>もう早速ですが、タイピングゲームに使われる<code>OnGUI()</code>の中身を全部書きましょう。以下のようになります。</p> <pre><code class="csharp">private void OnGUI() { if (Event.current.type == EventType.KeyDown) { switch(InputKey(GetCharFromKeyCode(Event.current.keyCode))) { case 1: case 2: _romanIndex++; if(_roman[_romanIndex] == '@') { InitializeQuestion(); } else { _textMeshProRoman.text = GenerateRomanText(); } break; case 3: // ここにミスタイプ時の処理を記述する break; } } } </code></pre> <p>当然ですが<code>InputKey()</code>や<code>GetCharFromKeyCode()</code>、<code>InitializeQuestion()</code>、<code>GenerateRomanText()</code>ならびに<code>_roman[]</code>、 <code>_romanIndex</code>、 <code>_textMeshProRoman</code>は未定義ですので、このままでは動作しません。なのでそれぞれのメソッドや変数の解説をしながら実装したいと思います。</p> <h3 id="KeyCodeをcharに変換する「GetCharFromKeyCode()」関数を実装しよう!"><a href="#KeyCode%E3%82%92char%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B%E3%80%8CGetCharFromKeyCode%28%29%E3%80%8D%E9%96%A2%E6%95%B0%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%82%88%E3%81%86%EF%BC%81">KeyCodeをcharに変換する「GetCharFromKeyCode()」関数を実装しよう!</a></h3> <p>キーが入力され<code>OnGUI()</code>が実行されると<code>Event.current.keyCode</code>に入力されたキーコードが格納されます。型は<code>KeyCode</code>です。<br /> この型はタイピングゲームのアルゴリズム実装には向いていないので、<code>KeyCode</code>を<code>char</code>に変換する関数<code>GetCharFromKeyCode()</code>を実装しましょう。<br /> 以下のようになります。今回はShift入力は省略しました。</p> <pre><code class="csharp">char GetCharFromKeyCode(KeyCode keyCode) { switch (keyCode) { case KeyCode.A: return 'a'; case KeyCode.B: return 'b'; case KeyCode.C: return 'c'; case KeyCode.D: return 'd'; case KeyCode.E: return 'e'; case KeyCode.F: return 'f'; case KeyCode.G: return 'g'; case KeyCode.H: return 'h'; case KeyCode.I: return 'i'; case KeyCode.J: return 'j'; case KeyCode.K: return 'k'; case KeyCode.L: return 'l'; case KeyCode.M: return 'm'; case KeyCode.N: return 'n'; case KeyCode.O: return 'o'; case KeyCode.P: return 'p'; case KeyCode.Q: return 'q'; case KeyCode.R: return 'r'; case KeyCode.S: return 's'; case KeyCode.T: return 't'; case KeyCode.U: return 'u'; case KeyCode.V: return 'v'; case KeyCode.W: return 'w'; case KeyCode.X: return 'x'; case KeyCode.Y: return 'y'; case KeyCode.Z: return 'z'; case KeyCode.Alpha0: return '0'; case KeyCode.Alpha1: return '1'; case KeyCode.Alpha2: return '2'; case KeyCode.Alpha3: return '3'; case KeyCode.Alpha4: return '4'; case KeyCode.Alpha5: return '5'; case KeyCode.Alpha6: return '6'; case KeyCode.Alpha7: return '7'; case KeyCode.Alpha8: return '8'; case KeyCode.Alpha9: return '9'; case KeyCode.Minus: return '-'; case KeyCode.Caret: return '^'; case KeyCode.Backslash: return '\\'; case KeyCode.At: return '@'; case KeyCode.LeftBracket: return '['; case KeyCode.Semicolon: return ';'; case KeyCode.Colon: return ':'; case KeyCode.RightBracket: return ']'; case KeyCode.Comma: return ','; case KeyCode.Period: return '_'; case KeyCode.Slash: return '/'; case KeyCode.Underscore: return '_'; case KeyCode.Backspace: return '\b'; case KeyCode.Return: return '\r'; case KeyCode.Space: return ' '; default: return '\0'; } } </code></pre> <p>(誰かこのコードを簡潔にできるなら教えて)</p> <p>ひどいコードですね(笑)でも私の技術力ではこうなってしまいました。こうやって、<code>KeyCode</code>を<code>char</code>に変換しているわけです。今回はShiftキーによる大文字入力には対応していません。Functionキーなどには入力対応しておらず、それらが入力された場合にも<code>OnGUI()</code>が実行され<code>Event.current.keyCode</code>に格納され、上記の関数が実行されますが、その場合はnull文字<code>\0</code>を返しています。</p> <h3 id="「InputKey()」を実装しよう!"><a href="#%E3%80%8CInputKey%28%29%E3%80%8D%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%82%88%E3%81%86%EF%BC%81">「InputKey()」を実装しよう!</a></h3> <p>さて、<code>OnGUI()</code>関数を確認すると、<code>GetCharFromKeyCode()</code>の<code>char</code>の返り値が、<code>InputKey()</code>の引数となっているわけですが、この関数は入力が正しいか否かを判断します。この関数の返り値は<code>int</code>で、正しい入力があれば<code>1</code>を、ミスタイプであれば<code>3</code>を返し、null文字ならば<code>0</code>を返します。</p> <pre><code class="csharp">int InputKey(char inputChar) { char currentChar = _roman[_romanIndex]; if(inputChar == '\0') { return 0; } if(inputChar == currentChar) { return 1; } return 3; } </code></pre> <p>返り値の<code>2</code>については、後ほどの「柔軟な入力方法」によって解説します。</p> <p>ところで「<code>_roman</code>とか<code>_romanIndex</code>って何?」と思った方、今から解説します。</p> <h3 id="タイピングの状態を格納するインスタンス変数を作成しよう!"><a href="#%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0%E3%81%AE%E7%8A%B6%E6%85%8B%E3%82%92%E6%A0%BC%E7%B4%8D%E3%81%99%E3%82%8B%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%A4%89%E6%95%B0%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%82%88%E3%81%86%EF%BC%81">タイピングの状態を格納するインスタンス変数を作成しよう!</a></h3> <p>タイピングゲームを実装するからには、当然タイピング用の状態を格納する変数が必要になります。<br /> なので今回は<code>_roman</code>と<code>romanIndex</code>のインスタンス変数を定義しましょう。</p> <pre><code class="csharp">public class TypingManager : MonoBehaviour { private List<char> _roman = new List<char>(); private int _romanIndex = 0; } </code></pre> <p><code>_roman</code>はタイピングの処理に用いられる<code>List<char></code>のインスタンス変数で、頻繁に<code>Add()</code>や<code>Clear()</code>を用いますので、<code>List<T></code>となっております。<br /> そして<code>_romanIndex</code>は<code>_roman</code>の参照に用いられるだけの<code>int</code>型のインスタンス変数です。</p> <h3 id="問題を初期化する「InitializeQuestion()」関数を実装しよう!"><a href="#%E5%95%8F%E9%A1%8C%E3%82%92%E5%88%9D%E6%9C%9F%E5%8C%96%E3%81%99%E3%82%8B%E3%80%8CInitializeQuestion%28%29%E3%80%8D%E9%96%A2%E6%95%B0%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%82%88%E3%81%86%EF%BC%81">問題を初期化する「InitializeQuestion()」関数を実装しよう!</a></h3> <p>どんなタイピングゲームにも「問題の初期化」というのは必ず存在しますので、それを行うための関数<code>InitializeQuestion()</code>を実装します。</p> <pre><code class="csharp">void InitializeQuestion() { Question question = _questions[UnityEngine.Random.Range(0, _questions.Length)]; _romanIndex = 0; _roman.Clear(); char[] characters = question.roman.ToCharArray(); foreach(char character in characters) { _roman.Add(character); } _roman.Add('@'); _textMeshProTitle.text = question.title; _textMeshProRoman.text = GenerateRomanText(); } </code></pre> <p><code>Clear()</code>メソッドで<code>_roman</code>の中身を空にし、その後<code>question</code>の<code>roman</code>プロパティ(<code>string</code>型)を<code>ToCharArray()</code>メソッドで<code>char</code>型の配列に変換して、<code>foreach</code>で<code>_roman</code>に次から次へと<code>Add()</code>メソッドで追加します。</p> <p>そして<code>_roman</code>の最後に<code>@</code>を追加します。この<code>@</code>が「タイピングの終わり」であることを示します。</p> <p>ところで<code>Question</code>が登場しました。タイピングゲームにはタイピング用の文字列のリストが必要になります。</p> <p>なので<code>Question</code>クラスを作成して、<code>[SerializeField]</code>でインスペクタ上から文字列を編集できるようにします。</p> <p><code>_textMeshProTitle</code>や<code>_textMeshProRoman</code>は、画面に表示するためのTextMeshProのオブジェクトが格納されたインスタンス変数です。<br /> ついでに、TextMeshProのGameObjectを取得するコードも<code>Start()</code>関数等に追加します。</p> <pre><code class="csharp">using System; //追加する using System.Collections; using System.Collections.Generic; using TMPro; //これも追加する // 以下の追加する [Serializable] public class Question { public string title; public string roman; } // ここまで public class TypingManager : MonoBehaviour { // 以下を追加する [SerializedField] Question[] _questions = new Question[12]; //お好きな数字をどうぞ private TextMeshProUGUI _textMeshProTitle; private TextMeshProUGUI _textMeshProRoman; // ここまで void Start() { // 以下を追加する _textMeshProTitle = GameObject.Find("TextMeshProTitle").GetComponent<TextMeshProUGUI>(); _textMeshProRoman = GameObject.Find("TextMeshProRoman").GetComponent<TextMeshProUGUI>(); InitializeQuestion(); // ここまで } } </code></pre> <h3 id="表示用のテキストを作る「GenerateRomanText()」関数を実装しよう!"><a href="#%E8%A1%A8%E7%A4%BA%E7%94%A8%E3%81%AE%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E3%82%8B%E3%80%8CGenerateRomanText%28%29%E3%80%8D%E9%96%A2%E6%95%B0%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%82%88%E3%81%86%EF%BC%81">表示用のテキストを作る「GenerateRomanText()」関数を実装しよう!</a></h3> <p>タイピングゲームには当たり前のように、入力後の文字と入力前の文字で色が異なりますので、それを実装するための関数を実装します。</p> <pre><code class="csharp">string GenerateRomanText() { string text = "<style=typed>" for (int i = 0; i < _roman.Count; i++) { if (_roman[i] == '@') { break; } if (i == _romanIndex) { text += "</style><style=untyped>" } text += _roman[i]; } text += "</style>" return text; } </code></pre> <p>TextMeshProにはタグ機能が存在しており、特定の部分のみスタイルを変更する「」を使用しました。</p> <p>ただ、このままでは画面上に「」などが表示されてしまいます。なのでUnityエディタ上で「Project Settings」→「TextMeh Pro」の「Settings」→「Default Style Sheet」の「Default Style Sheet (TMP_StyleSheet)」をダブルクリックして、Inspector上で以下の図のようにタグを追加します。<br /> <a href="https://crieit.now.sh/upload_images/e215fa1cf26a117976f2c709592c0e015fcc2305f0a7c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e215fa1cf26a117976f2c709592c0e015fcc2305f0a7c.png?mw=700" alt="image" /></a></p> <p>こうすることによって、「」は表示されなくなり、タイピング済の文字とそうでない文字のスタイルが変わります。</p> <h3 id="とりあえず完成だ!"><a href="#%E3%81%A8%E3%82%8A%E3%81%82%E3%81%88%E3%81%9A%E5%AE%8C%E6%88%90%E3%81%A0%EF%BC%81">とりあえず完成だ!</a></h3> <p>最後にUnityエディタ上の「Hierarchy」から「TypingManager」を選択して、Questionsの値をセットすれば完成です。<br /> (Romanのセットには最短になるようセットします(「chi」→「ti」、「zyo」→「jo」等))<br /> <a href="https://crieit.now.sh/upload_images/aa91cb22d34f84c15cd717790448cf615fcc24c3406f5.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/aa91cb22d34f84c15cd717790448cf615fcc24c3406f5.png?mw=700" alt="image" /></a></p> <p>これで「Play」を実行しましょう!</p> <h2 id="柔軟な入力方法に対応させよう!"><a href="#%E6%9F%94%E8%BB%9F%E3%81%AA%E5%85%A5%E5%8A%9B%E6%96%B9%E6%B3%95%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%95%E3%81%9B%E3%82%88%E3%81%86%EF%BC%81">柔軟な入力方法に対応させよう!</a></h2> <p>タイピング機能はうまく動作しましたか?<br /> (動作しなかった場合、自力でソースコード等を修正できますか?🙇‍♂️)</p> <p>しかし先ほど作成したタイピングゲームは柔軟な入力方法には対応していません。<br /> このままでは、非常に操作性の悪いタイピングゲームとなってしまい、今時リリースできるようなものではありません。</p> <p>なので本題である柔軟な入力方法に対応させます。</p> <h3 id="WindowsとMacで異なる入力方法"><a href="#Windows%E3%81%A8Mac%E3%81%A7%E7%95%B0%E3%81%AA%E3%82%8B%E5%85%A5%E5%8A%9B%E6%96%B9%E6%B3%95">WindowsとMacで異なる入力方法</a></h3> <p>タイピングの入力方法については、以下のURLが参考になります。</p> <p>Windows:<br /> <a target="_blank" rel="nofollow noopener" href="https://www.cc.saga-u.ac.jp/system/CenterSystem/ime_romaji.htm">https://www.cc.saga-u.ac.jp/system/CenterSystem/ime_romaji.htm</a></p> <p>Mac:<br /> <a target="_blank" rel="nofollow noopener" href="https://support.apple.com/ja-jp/guide/japanese-input-method/jpim10277/6.2.1/mac/10.15">https://support.apple.com/ja-jp/guide/japanese-input-method/jpim10277/6.2.1/mac/10.15</a></p> <p>よくよく確認すると、WindowsとMacでは入力方法が異なることがわかります。(例:Windowsでは「ca」と入力できるが、Macではできない)<br /> もしもWindowsおよびMac専用のソフトとしてリリースするのであれば、両方に対応する必要はないと思いますが、WebGLでビルドしてunityroomにアップロードする場合、WindowsとMacを区別する必要があります<br /> 再び「TypingManager.cs」を編集します。</p> <pre><code class="csharp">public class TypingManager : MonoBehaviour { private bool _isWindows; //追加する private bool _isMac; //追加する void Start() { // 以下を追加する if(SystemInfo.operatingSystem.Contains("Windows")) { _isWindows = true; } if(SystemInfo.operatingSystem.Contains("Mac")) { _isMac = true; } // ここまで InitializeQuestion(); } } </code></pre> <h3 id="柔軟な入力方法に対応するアルゴリズム"><a href="#%E6%9F%94%E8%BB%9F%E3%81%AA%E5%85%A5%E5%8A%9B%E6%96%B9%E6%B3%95%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0">柔軟な入力方法に対応するアルゴリズム</a></h3> <p>現在の日本語入力タイピングにおいて、柔軟な入力方法に対応させるアルゴリズムはいくつか存在します。<br /> 今回は「前後比較法(私が勝手に名付けた方法です)」を使います。</p> <p>「前後比較法」は、今入力すべき文字とその文字の前後を比較することによって「どのひらがなを入力するのか」を判断して、入力してもOKな文字を抽出するアルゴリズムです。</p> <p>例えば「うちわ(utiwa)」を考えましょう。</p> <p>まず最初の入力文字が「u」なので、入力するひらがなは「う」であることが判明します。「う」の別入力が「wu」であるので「w」を入力しても構わないことがわかります。</p> <p>当然ですが、別入力を行いますとローマ字を改良する必要があります。今回の例で「w」を入力するとローマ字表記は「wutiwa」に変化します。</p> <p>次の入力文字も「u」ですが、前の文字が「w」なので「う」の入力途中であることが判明します。よって今回は「w」を入力することができません。</p> <p>そして「u」の入力後、入力文字が「t」になります。この入力文字の前が母音で、後の文字が「i」なので「ち」を入力することが判明します。同様に「ち」の別入力は「chi」なので「c」を入力してもOKとなります。</p> <p>もちろん「c」を入力した後は「wuchiwa」に変形する必要があります。</p> <p>こんな感じでほかの文字に対応させることができます。</p> <h3 id="「InputKey()」関数を大規模改造する。"><a href="#%E3%80%8CInputKey%28%29%E3%80%8D%E9%96%A2%E6%95%B0%E3%82%92%E5%A4%A7%E8%A6%8F%E6%A8%A1%E6%94%B9%E9%80%A0%E3%81%99%E3%82%8B%E3%80%82">「InputKey()」関数を大規模改造する。</a></h3> <p>上記のアルゴリズムを用いて<code>InputKey()</code>を改良します。<br /> 以下のコードを解説するとかなり長いので省略します。</p> <pre><code class="csharp">int InputKey(char inputChar) { char prevChar3 = _romanIndex >= 3 ? _roman[_romanIndex - 3] : '\0'; char prevChar2 = _romanIndex >= 2 ? _roman[_romanIndex - 2] : '\0'; char prevChar = _romanIndex >= 1 ? _roman[_romanIndex - 1] : '\0'; char currentChar = _roman[_romanIndex]; char nextChar = _roman[_romanIndex + 1]; char nextChar2 = nextChar == '@' ? '@' : _roman[_romanIndex + 2]; if (inputChar == '\0') { return 0; } if (inputChar == currentChar) { return 1; } //「い」の曖昧入力判定(Windowsのみ) if (_isWindows && inputChar == 'y' && currentChar == 'i' && (prevChar == '\0' || prevChar == 'a' || prevChar == 'i' || prevChar == 'u' || prevChar == 'e' || prevChar == 'o')) { _roman.Insert(_romanIndex, 'y'); return 2; } if (_isWindows && inputChar == 'y' && currentChar == 'i' && prevChar == 'n' && prevChar2 == 'n' && prevChar3 != 'n') { _roman.Insert(_romanIndex, 'y'); return 2; } if (_isWindows && inputChar == 'y' && currentChar == 'i' && prevChar == 'n' && prevChar2 == 'x') { _roman.Insert(_romanIndex, 'y'); return 2; } //「う」の曖昧入力判定(「whu」はWindowsのみ) if (inputChar == 'w' && currentChar == 'u' && (prevChar == '\0' || prevChar == 'a' || prevChar == 'i' || prevChar == 'u' || prevChar == 'e' || prevChar == 'o')) { _roman.Insert(_romanIndex, 'w'); return 2; } if (inputChar == 'w' && currentChar == 'u' && prevChar == 'n' && prevChar2 == 'n' && prevChar3 != 'n') { _roman.Insert(_romanIndex, 'w'); return 2; } if (inputChar == 'w' && currentChar == 'u' && prevChar == 'n' && prevChar2 == 'x') { _roman.Insert(_romanIndex, 'w'); return 2; } if (_isWindows && inputChar == 'h' && prevChar2 != 't' && prevChar2 != 'd' && prevChar == 'w' && currentChar == 'u') { _roman.Insert(_romanIndex, 'h'); return 2; } //「か」「く」「こ」の曖昧入力判定(Windowsのみ) if (_isWindows && inputChar == 'c' && prevChar != 'k' && currentChar == 'k' && (nextChar == 'a' || nextChar == 'u' || nextChar == 'o')) { _roman[_romanIndex] = 'c'; return 2; } //「く」の曖昧入力判定(Windowsのみ) if (_isWindows && inputChar == 'q' && prevChar != 'k' && currentChar == 'k' && nextChar == 'u') { _roman[_romanIndex] = 'q'; return 2; } //「し」の曖昧入力判定 if (inputChar == 'h' && prevChar == 's' && currentChar == 'i') { _roman.Insert(_romanIndex, 'h'); return 2; } //「じ」の曖昧入力判定 if (inputChar == 'j' && currentChar == 'z' && nextChar == 'i') { _roman[_romanIndex] = 'j'; return 2; } //「しゃ」「しゅ」「しぇ」「しょ」の曖昧入力判定 if (inputChar == 'h' && prevChar == 's' && currentChar == 'y') { _roman[_romanIndex] = 'h'; return 2; } //「じゃ」「じゅ」「じぇ」「じょ」の曖昧入力判定 if (inputChar == 'z' && prevChar != 'j' && currentChar == 'j' && (nextChar == 'a' || nextChar == 'u' || nextChar == 'e' || nextChar == 'o')) { _roman[_romanIndex] = 'z'; _roman.Insert(_romanIndex + 1, 'y'); return 2; } //「し」「せ」の曖昧入力判定(Windowsのみ) if (_isWindows && inputChar == 'c' && prevChar != 's' && currentChar == 's' && (nextChar == 'i' || nextChar == 'e')) { _roman[_romanIndex] = 'c'; return 2; } //「ち」の曖昧入力判定 if (inputChar == 'c' && prevChar != 't' && currentChar == 't' && nextChar == 'i') { _roman[_romanIndex] = 'c'; _roman.Insert(_romanIndex + 1, 'h'); return 2; } //「ちゃ」「ちゅ」「ちぇ」「ちょ」の曖昧入力判定 if (inputChar == 'c' && prevChar != 't' && currentChar == 't' && nextChar == 'y') { _roman[_romanIndex] = 'c'; return 2; } //「cya」=>「cha」 if (inputChar == 'h' && prevChar == 'c' && currentChar == 'y') { _roman[_romanIndex] = 'h'; return 2; } //「つ」の曖昧入力判定 if (inputChar == 's' && prevChar == 't' && currentChar == 'u') { _roman.Insert(_romanIndex, 's'); return 2; } //「つぁ」「つぃ」「つぇ」「つぉ」の分解入力判定 if (inputChar == 'u' && prevChar == 't' && currentChar == 's' && (nextChar == 'a' || nextChar == 'i' || nextChar == 'e' || nextChar == 'o')) { _roman[_romanIndex] = 'u'; _roman.Insert(_romanIndex + 1, 'x'); return 2; } if (inputChar == 'u' && prevChar2 == 't' && prevChar == 's' && (currentChar == 'a' || currentChar == 'i' || currentChar == 'e' || currentChar == 'o')) { _roman.Insert(_romanIndex, 'u'); _roman.Insert(_romanIndex + 1, 'x'); return 2; } //「てぃ」の分解入力判定 if (inputChar == 'e' && prevChar == 't' && currentChar == 'h' && nextChar == 'i') { _roman[_romanIndex] = 'e'; _roman.Insert(_romanIndex + 1, 'x'); return 2; } //「でぃ」の分解入力判定 if (inputChar == 'e' && prevChar == 'd' && currentChar == 'h' && nextChar == 'i') { _roman[_romanIndex] = 'e'; _roman.Insert(_romanIndex + 1, 'x'); return 2; } //「でゅ」の分解入力判定 if (inputChar == 'e' && prevChar == 'd' && currentChar == 'h' && nextChar == 'u') { _roman[_romanIndex] = 'e'; _roman.Insert(_romanIndex + 1, 'x'); _roman.Insert(_romanIndex + 2, 'y'); return 2; } //「とぅ」の分解入力判定 if (inputChar == 'o' && prevChar == 't' && currentChar == 'w' && nextChar == 'u') { _roman[_romanIndex] = 'o'; _roman.Insert(_romanIndex + 1, 'x'); return 2; } //「どぅ」の分解入力判定 if (inputChar == 'o' && prevChar == 'd' && currentChar == 'w' && nextChar == 'u') { _roman[_romanIndex] = 'o'; _roman.Insert(_romanIndex + 1, 'x'); return 2; } //「ふ」の曖昧入力判定 if (inputChar == 'f' && currentChar == 'h' && nextChar == 'u') { _roman[_romanIndex] = 'f'; return 2; } //「ふぁ」「ふぃ」「ふぇ」「ふぉ」の分解入力判定(一部Macのみ) if (inputChar == 'w' && prevChar == 'f' && (currentChar == 'a' || currentChar == 'i' || currentChar == 'e' || currentChar == 'o')) { _roman.Insert(_romanIndex,'w'); return 2; } if (inputChar == 'y' && prevChar == 'f' && (currentChar == 'i' || currentChar == 'e')) { _roman.Insert(_romanIndex,'y'); return 2; } if (inputChar == 'h' && prevChar != 'f' && currentChar == 'f' && (nextChar == 'a' || nextChar == 'i' || nextChar == 'e' || nextChar == 'o')) { if (_isMac) { _roman[_romanIndex] = 'h'; _roman.Insert(_romanIndex + 1, 'w'); } else { _roman[_romanIndex] = 'h'; _roman.Insert(_romanIndex + 1, 'u'); _roman.Insert(_romanIndex + 2, 'x'); } return 2; } if (inputChar == 'u' && prevChar == 'f' && (currentChar == 'a' || currentChar == 'i' || currentChar == 'e' || currentChar == 'o')) { _roman.Insert(_romanIndex, 'u'); _roman.Insert(_romanIndex + 1, 'x'); return 2; } if (_isMac && inputChar == 'u' && prevChar == 'h' && currentChar == 'w' && (nextChar == 'a' || nextChar == 'i' || nextChar == 'e' || nextChar == 'o')) { _roman[_romanIndex] = 'u'; _roman.Insert(_romanIndex + 1, 'x'); return 2; } //「ん」の曖昧入力判定(「n'」には未対応) if (inputChar == 'n' && prevChar2 != 'n' && prevChar == 'n' && currentChar != 'a' && currentChar != 'i' && currentChar != 'u' && currentChar != 'e' && currentChar != 'o' && currentChar != 'y') { _roman.Insert(_romanIndex, 'n'); return 2; } if (inputChar == 'x' && prevChar != 'n' && currentChar == 'n' && nextChar != 'a' && nextChar != 'i' && nextChar != 'u' && nextChar != 'e' && nextChar != 'o' && nextChar != 'y') { if (nextChar == 'n') { _roman[_romanIndex] = 'x'; } else { _roman.Insert(_romanIndex, 'x'); } return 2; } //「きゃ」「にゃ」などを分解する if (inputChar == 'i' && currentChar == 'y' && (prevChar == 'k' || prevChar == 's' || prevChar == 't' || prevChar == 'n' || prevChar == 'h' || prevChar == 'm' || prevChar == 'r' || prevChar == 'g' || prevChar == 'z' || prevChar == 'd' || prevChar == 'b' || prevChar == 'p') && (nextChar == 'a' || nextChar == 'u' || nextChar == 'e' || nextChar == 'o')) { if (nextChar == 'e') { _roman[_romanIndex] = 'i'; _roman.Insert(_romanIndex + 1, 'x'); } else { _roman.Insert(_romanIndex, 'i'); _roman.Insert(_romanIndex + 1, 'x'); } return 2; } //「しゃ」「ちゃ」などを分解する if (inputChar == 'i' && (currentChar == 'a' || currentChar == 'u' || currentChar == 'e' || currentChar == 'o') && (prevChar2 == 's' || prevChar2 == 'c') && prevChar == 'h') { if (nextChar == 'e') { _roman.Insert(_romanIndex,'i'); _roman.Insert(_romanIndex + 1, 'x'); } else { _roman.Insert(_romanIndex, 'i'); _roman.Insert(_romanIndex + 1, 'x'); _roman.Insert(_romanIndex + 2, 'y'); } return 2; } //「しゃ」を「c」で分解する(Windows限定) if (_isWindows && inputChar == 'c' && currentChar == 's' && prevChar != 's' && nextChar == 'y' && (nextChar2 == 'a' || nextChar2 == 'u' || nextChar2 == 'e' || nextChar2 == 'o')) { if (nextChar2 == 'e') { _roman[_romanIndex] = 'c'; _roman[_romanIndex + 1] = 'i'; _roman.Insert(_romanIndex + 1, 'x'); } else { _roman[_romanIndex] = 'c'; _roman.Insert(_romanIndex + 1, 'i'); _roman.Insert(_romanIndex + 2, 'x'); } return 2; } //「っ」の分解入力判定 if ((inputChar == 'x' || inputChar == 'l') && (currentChar == 'k' && nextChar == 'k' || currentChar == 's' && nextChar == 's' || currentChar == 't' && nextChar == 't' || currentChar == 'g' && nextChar == 'g' || currentChar == 'z' && nextChar == 'z' || currentChar == 'j' && nextChar == 'j' || currentChar == 'd' && nextChar == 'd' || currentChar == 'b' && nextChar == 'b' || currentChar == 'p' && nextChar == 'p')) { _roman[_romanIndex] = inputChar; _roman.Insert(_romanIndex + 1, 't'); _roman.Insert(_romanIndex + 2, 'u'); return 2; } //「っか」「っく」「っこ」の特殊入力(Windows限定) if (_isWindows && inputChar == 'c' && currentChar == 'k' && nextChar == 'k' && (nextChar2 == 'a' || nextChar2 == 'u' || nextChar2 == 'o')) { _roman[_romanIndex] = 'c'; _roman[_romanIndex + 1] = 'c'; return 2; } //「っく」の特殊入力(Windows限定) if (_isWindows && inputChar == 'q' && currentChar == 'k' && nextChar == 'k' && nextChar2 == 'u') { _roman[_romanIndex] = 'q'; _roman[_romanIndex + 1] = 'q'; return 2; } //「っし」「っせ」の特殊入力(Windows限定) if (_isWindows && inputChar == 'c' && currentChar == 's' && nextChar == 's' && (nextChar2 == 'i' || nextChar2 == 'e')) { _roman[_romanIndex] = 'c'; _roman[_romanIndex + 1] = 'c'; return 2; } //「っちゃ」「っちゅ」「っちぇ」「っちょ」の曖昧入力判定 if (inputChar == 'c' && currentChar == 't' && nextChar == 't' && nextChar2 == 'y') { _roman[_romanIndex] = 'c'; _roman[_romanIndex + 1] = 'c'; return 2; } //「っち」の曖昧入力判定 if (inputChar == 'c' && currentChar == 't' && nextChar == 't' && nextChar2 == 'i') { _roman[_romanIndex] = 'c'; _roman[_romanIndex + 1] = 'c'; _roman.Insert(_romanIndex + 2, 'h'); return 2; } //「l」と「x」の完全互換性 if (inputChar == 'x' && currentChar == 'l') { _roman[_romanIndex] = 'x'; return 2; } if (inputChar == 'l' && currentChar == 'x') { _roman[_romanIndex] = 'l'; return 2; } return 3; } </code></pre> <p>こうすることによって、「し」を「shi」、「ふ」を「fu」と入力できるようになります。<br /> 実際、上記のコードは完璧ではありませんが、これでもゲームとして十分だと思います。</p> <h2 id="さいごに"><a href="#%E3%81%95%E3%81%84%E3%81%94%E3%81%AB">さいごに</a></h2> <p>私は文章下手なので、少々読みにくい点はあったかもしれませんが、それでも、長々とした記事をお読みいただき、誠にありがとうございます。</p> <p>上記のコードは自由にアレンジしても、そのままコピペしても構いません。<br /> むしろ、それでタイピングゲームの製作に関心を持ってもらえたら幸いです。</p> <h2 id="サンプル"><a href="#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB">サンプル</a></h2> <p>サンプルはunityroom上にて公開しております。<br /> なお、今回はミスタイプ数などの要素が加わています。</p> <p><a target="_blank" rel="nofollow noopener" href="https://unityroom.com/games/typingsample" target="_blank">https://unityroom.com/games/typingsample</a></p> <h2 id="GitHub"><a href="#GitHub">GitHub</a></h2> <p>内容は、上記のサンプルのプロジェクトです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/AkioMabuchi/SampleTyping" target="_blank">https://github.com/AkioMabuchi/SampleTyping</a></p> アキオ tag:crieit.net,2005:PublicArticle/16252 2020-12-03T07:00:09+09:00 2020-12-03T07:00:09+09:00 https://crieit.net/posts/Unity3D Unity3D上でマウスポインタの位置のグリッド座標を簡単に取得する <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>いつもお世話になっております。<br /> この度アドベントカレンダーに参加したいなと思いまして、<br /> <a href="https://crieit.net/advent-calendars/2020/crieit">なんでも Advent Calendar 2020</a>に記事を投稿することにしました。<br /> 普段はnoteやTwitterで活動しております。</p> <p>さて、今回のテーマは「Unity3D上でマウスカーソルの位置のグリッド座標を得られるようにする」というものですが、言葉だけつらつら並べてもわかりにくいのでサンプル画像を用意しました。最終的にこんな感じで選択できるようになります。</p> <p><a href="https://crieit.now.sh/upload_images/cd4a538e455b1b9f8f63504745e403815fc64e03e0852.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cd4a538e455b1b9f8f63504745e403815fc64e03e0852.gif?mw=700" alt="グリッド座標取得サンプル" /></a></p> <p>この画像のようにマウスカーソルの位置の座標を光らせられるようにしましょう。<br /> (キャラクター移動方法などは省略致します。ご了承下さい。また地形に高低差がある場合、この方法では上手くいかないと思います)</p> <h1 id="使用する機能"><a href="#%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E6%A9%9F%E8%83%BD">使用する機能</a></h1> <p>上記を実現する方法としてパッと思いつくのはPlaneやCubeを敷き詰めて全てにコライダーコンポーネントをアタッチし、マウスカーソルが当たっているかどうかを都度計算するという方法ですが、これはコストがかかり過ぎるので実用向きではありません。</p> <p>ならばどうするかと言うと、Unityが標準でサポートしている<strong>Grid</strong>と<strong>Tilemap</strong>を使います。<br /> これはUnity2Dでメインに使われている機能になります。普通はその名の通りタイル(マップチップ)を敷き詰めてフィールドを表現する機能なのですが、これを活用することで3D上でも簡単にグリッド座標を取得することができるようになります。</p> <h1 id="大まかな手順"><a href="#%E5%A4%A7%E3%81%BE%E3%81%8B%E3%81%AA%E6%89%8B%E9%A0%86">大まかな手順</a></h1> <p>大まかには以下の手順で進めていきます。</p> <ul> <li><strong>PlaneとGrid、Tilemapオブジェクトの用意</strong></li> <li>Plane上にカーソルが乗っている時、光らせる為の小さいPlane(=SelectPlane)を表示</li> <li>Plane上からカーソルが外れた際、SelectPlaneを非表示にする</li> <li><strong>RaycastでPlane上のマウスカーソルのある位置を取得する</strong></li> <li><strong>その地点をTilemapのWorldToCellメソッドでグリッド座標に変換</strong></li> <li>更にCellToWorldメソッドでもう一度ワールド座標に変換し、SelectPlaneのpositionに代入</li> </ul> <p>とりあえず以上になります。なおグリッド座標を単純に取得したいだけなら太字部分だけを行えばOKです。</p> <h1 id="実際の手順"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AE%E6%89%8B%E9%A0%86">実際の手順</a></h1> <p>それでは行って参りましょう。</p> <h2 id="PlaneとGrid、Tilemapオブジェクトの用意"><a href="#Plane%E3%81%A8Grid%E3%80%81Tilemap%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E7%94%A8%E6%84%8F">PlaneとGrid、Tilemapオブジェクトの用意</a></h2> <p>まずは空のGameObjectを一つ作成し、その子要素に「3Dオブジェクト」-「平面」を1枚用意します。大きさや位置などは任意で変更して下さい。<br /> 更にGameObjectの階層下に「2Dオブジェクト」-「タイルマップ」を作成します。<br /> (光らせたい方は1グリッドのサイズ/10の大きさで平面をもう一つ作成します=SelectPlane<br /> 任意でマテリアルを変更しておきましょう。位置のY座標を0.01程度上げておき、MeshColliderコンポーネントを外しておきます。また、通常は非表示にしておくといいです)<br /> するとヒエラルキータブはこんな感じになるはずです。</p> <p><a href="https://crieit.now.sh/upload_images/78ca287e8ef30417ef8f6487671401eb5fc66556759b3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/78ca287e8ef30417ef8f6487671401eb5fc66556759b3.png?mw=700" alt="ヒエラルキータブ" /></a></p> <p>そしてGridオブジェクトとTilemapオブジェクトを以下のように変更して下さい。</p> <p><a href="https://crieit.now.sh/upload_images/16c3c47de13cedc0a77e8b35a1a7fac65fc663a7ddd70.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/16c3c47de13cedc0a77e8b35a1a7fac65fc663a7ddd70.png?mw=700" alt="Gridオブジェクト" /></a></p> <p><a href="https://crieit.now.sh/upload_images/856929aa9995b605b3375464c29bb29f5fc663e0d87e1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/856929aa9995b605b3375464c29bb29f5fc663e0d87e1.png?mw=700" alt="Tilemapオブジェクト" /></a></p> <p>一応全て載せていますが、基本はGridコンポーネントのセルスウィズルを「<strong>XZY</strong>」にすることと、Tilemapコンポーネントの向きを「<strong>XZ</strong>」にすることが大事です。</p> <p>あとは任意ですがカメラの向きも変えておくと確認しやすいかもしれません。</p> <p>これでオブジェクト側の用意は終わりました。</p> <h2 id="SelectPlaneの表示/非表示"><a href="#SelectPlane%E3%81%AE%E8%A1%A8%E7%A4%BA%2F%E9%9D%9E%E8%A1%A8%E7%A4%BA">SelectPlaneの表示/非表示</a></h2> <p>それではスクリプトを書いていきます。まずは手始めにSelectPlaneの表示と非表示から。<br /> OnGridPointerスクリプトを作成して下さい。そこに以下のように記述します。</p> <pre><code>using UnityEngine; public class OnGridPointer : MonoBehaviour { [SerializeField] private GameObject selectPlane; private void OnMouseEnter() { selectPlane.SetActive(true); } private void OnMouseExit() { selectPlane.SetActive(false); } } </code></pre> <p>ビルドしたらPlaneにアタッチし、Select PlaneにSelectPlaneオブジェクトを設定しておきます。<br /> 実行してみます。以下のようにマウスカーソルが上に乗ったらSelectPlaneオブジェクトが表示され、外れたら非表示になればOKです。</p> <p><a href="https://crieit.now.sh/upload_images/43e295b50c83690e1ac8075d03e6f5675fc669b655454.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/43e295b50c83690e1ac8075d03e6f5675fc669b655454.gif?mw=700" alt="SelectPlaneの表示・非表示" /></a></p> <h2 id="マウスカーソルのあるPlane上の位置の取得"><a href="#%E3%83%9E%E3%82%A6%E3%82%B9%E3%82%AB%E3%83%BC%E3%82%BD%E3%83%AB%E3%81%AE%E3%81%82%E3%82%8BPlane%E4%B8%8A%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%81%AE%E5%8F%96%E5%BE%97">マウスカーソルのあるPlane上の位置の取得</a></h2> <p>マウスカーソルのあるPlane上の位置はRaycastを飛ばすことで取得します。<br /> OnGridPointerクラスを以下のように変更します。</p> <pre><code>// メソッドを追加 private void OnEnter() { if (Camera.main == null) return; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { selectPlane.transform.position = hit.point + new Vector3(0, 0.01f, 0); } } private void OnMouseOver() { OnEnter(); } // メソッドを変更 private void OnMouseEnter() { selectPlane.SetActive(true); OnEnter(); } </code></pre> <p>試しにSelectPlaneオブジェクトの位置を変更することで実際に取得できているか確認できるようにしています。<br /> また、そのままだとPlaneオブジェクトと重なってしまってSelectPlaneオブジェクトが埋もれてしまう為、若干上に座標をずらしています。Y座標に0.01足しているのはその為です。<br /> 実行してみます。以下のようにマウスカーソル位置にSelectPlaneオブジェクトが移動すると思います。</p> <p><a href="https://crieit.now.sh/upload_images/77e8992c128b2f4fa9a6ec969a9d5c5f5fc67ad3ecbf0.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/77e8992c128b2f4fa9a6ec969a9d5c5f5fc67ad3ecbf0.gif?mw=700" alt="マウスカーソルのPlane上の位置" /></a></p> <h2 id="グリッド座標を取得し、その位置に移動"><a href="#%E3%82%B0%E3%83%AA%E3%83%83%E3%83%89%E5%BA%A7%E6%A8%99%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%80%81%E3%81%9D%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%81%AB%E7%A7%BB%E5%8B%95">グリッド座標を取得し、その位置に移動</a></h2> <p>最後にグリッド座標を取得し、SelectPlaneオブジェクトをその位置に移動させましょう。<br /> OnGridPointerクラスを以下のように変更します。</p> <pre><code>// スクリプトの最初に以下を追記 using UnityEngine.Tilemaps; // パラメーターを追加 [SerializeField] private Tilemap tilemap; // メソッドを変更 private void OnEnter() { if (Camera.main == null) return; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3Int gridPos = tilemap.WorldToCell(hit.point); Vector3 complementPos = new Vector3(tilemap.cellSize.x / 2, 0.01f, tilemap.cellSize.y / 2); Vector3 worldPos = tilemap.CellToWorld(gridPos) + complementPos; selectPlane.transform.position = worldPos; } } </code></pre> <p>ビルドしたら、コンポーネントのTIlemapにはTilemapオブジェクトを設定して下さい。</p> <p>一度グリッド座標に直してから再度ワールド座標に直すことで、位置を正規化しています。<br /> ただしこれで取得できるのはあくまでグリッドの左上の座標の為、1グリッドのサイズ(=tilemap.cellSize)/2を足して中央にずらす必要があります。<br /> 実行してみます。以下のようにグリッド座標単位でSelectPlaneオブジェクトが移動できればOKです。</p> <p><a href="https://crieit.now.sh/upload_images/90687487aceb0989193ade5f6ceb61585fc68206bbb55.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/90687487aceb0989193ade5f6ceb61585fc68206bbb55.gif?mw=700" alt="グリッド座標移動" /></a></p> <h1 id="終わりに"><a href="#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB">終わりに</a></h1> <p>Unity3D上でマウスカーソルのある位置のグリッド座標を取得できました。これを応用すれば最初の画像のようにキャラクターをその位置に移動させたりといったことが可能になると思います。RPGやローグライクゲームなどにいかがでしょうか。</p> <p>最後に今回の参加について。もともとネタがなかったのでアドベントカレンダーには参加しないつもりだったんですが、もしかしたらこのネタはいいかもしれないと思い結局参加してしまいました。<br /> 検索しても出てこなかったので多分二番煎じではないと......ないといいですね。<br /> (もしネタ被りしていたらすみません......!)</p> <p>ここまでお付き合い下さり、ありがとうございました。</p> <h1 id="おまけ:最小のコード"><a href="#%E3%81%8A%E3%81%BE%E3%81%91%EF%BC%9A%E6%9C%80%E5%B0%8F%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89">おまけ:最小のコード</a></h1> <p>もしSelectPlaneを使わず、グリッド座標のみを取得したい場合、コードは以下のようになると思います。</p> <pre><code>using UnityEngine; using UnityEngine.Tilemaps; public class OnGridPointer : MonoBehaviour { [SerializeField] private Tilemap tilemap; public Vector3Int GetGridPointer() { if (Camera.main == null) return new Vector3Int(); Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3Int gridPos = tilemap.WorldToCell(hit.point); return gridPos; } return new Vector3Int(); } } </code></pre> <p>ご査収下さい。</p> みにに tag:crieit.net,2005:PublicArticle/16032 2020-08-17T08:18:58+09:00 2020-08-17T08:18:58+09:00 https://crieit.net/posts/Unity-WebGL UnityのWebGLで日本語が表示されない時の対策 <p>「unity1week」と言うUnity1週間ゲームジャムにビジュアルノベル を作って参加したのですが、WebGLにビルドした時に日本語が表示されないと言う問題がおきました。<br /> その時の対処法です。</p> <h2 id="原因:WebGLだと日本語が表示されないフォントがある"><a href="#%E5%8E%9F%E5%9B%A0%EF%BC%9AWebGL%E3%81%A0%E3%81%A8%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%8C%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%81%8C%E3%81%82%E3%82%8B">原因:WebGLだと日本語が表示されないフォントがある</a></h2> <p>原因は初期設定のフォント。<br /> Unityのフォントの初期設定が「Arial」とフォントなのですが、このテキストは日本語フォントが含まれていないそうです。<br /> Unityの開発画面上では、違うフォントを自動で適応してくれるので問題なく表示されますが、これがWebGLでは適応してくれないので空白になってしまうのです。<br /> テキストノベルなのに文字がない…。なんてことになってしまいます。</p> <h2 id="解決方法:日本語テキストに対応しているフォントに変える"><a href="#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95%EF%BC%9A%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%81%AB%E5%A4%89%E3%81%88%E3%82%8B">解決方法:日本語テキストに対応しているフォントに変える</a></h2> <p>解決方法はシンプルにArialから日本語に対応しているフォントに変更することです。<br /> 調べてみると、フォントは、google notoの<a target="_blank" rel="nofollow noopener" href="https://www.google.com/get/noto/#sans-jpan">Noto Sans CJK JP</a>を利用することが多いようです。<br /> こちらをダウンロード、プロジェクトに入れて、テキストのフォントを変えればオッケーです。</p> <p>詳細はこの記事がわかりやすいです。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/tsubaki_t1/items/93e4b91b830729cd93a4">UnityのWebGLで日本語を表示する</a></p> <p>日本人のUnity開発者には初歩的な知識のようですが、初めてだったので結構苦戦しました。<br /> Unityでノベルゲームを作る人は注意してください。</p> <p>作ったゲームはこちら</p> <p><a target="_blank" rel="nofollow noopener" href="https://unityroom.com/games/katonobo-fueru">ビジュアルノベル 「増える」</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://katonobo.com/">ブログ</a>書いてます。</p> katonobo tag:crieit.net,2005:PublicArticle/16019 2020-07-28T20:55:49+09:00 2020-07-28T20:56:40+09:00 https://crieit.net/posts/Visual-Studio-Code-UnityEngine-UI Visual Studio Code内でUnityEngine.UIなどへの参照ができない(インテリジェンスが働かない)場合の修正方法 <p>Unity 2019.3.10f1で確認しています。<br /> 2019.4以降では解決しているかもしれません。</p> <h2 id="発生する現象"><a href="#%E7%99%BA%E7%94%9F%E3%81%99%E3%82%8B%E7%8F%BE%E8%B1%A1">発生する現象</a></h2> <p>Unity2019でプロジェクトを作成するとUnityではエラーが発生していないにも関わらず、Visual Studio Code内でUsing UnityEngine.UIなどへ参照ができない現象が発生します。</p> <p><a href="https://crieit.now.sh/upload_images/2c49c6834fd352a33cedc850cc0eab665f201218275ca.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2c49c6834fd352a33cedc850cc0eab665f201218275ca.png?mw=700" alt="image" /></a><br /> ↑画像のようにUIの下に赤線が表示される</p> <p>UnityEngin.UIにあるImageも正常に参照できておらずimage.まで打っても候補が表示されない。<br /> <a href="https://crieit.now.sh/upload_images/053dd0bbc7386336b12c79ffa17d4f315f20122ddff4b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/053dd0bbc7386336b12c79ffa17d4f315f20122ddff4b.png?mw=700" alt="image" /></a></p> <h3 id="表示されるエラー内容"><a href="#%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%82%8B%E3%82%A8%E3%83%A9%E3%83%BC%E5%86%85%E5%AE%B9">表示されるエラー内容</a></h3> <p><a href="https://crieit.now.sh/upload_images/3a4a5795e73e6e46b9335ed1172d253d5f2011105d415.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3a4a5795e73e6e46b9335ed1172d253d5f2011105d415.png?mw=700" alt="image" /></a></p> <pre><code>Using directive is unnecessary. [Assembly-CSharp]csharp(IDE0005) Unnecessary using directive. [Assembly-CSharp]csharp(CS8019) The type or namespace name 'UI' does not exist in the namespace 'UnityEngine' (are you missing an assembly reference?) [Assembly-CSharp]csharp(CS0234) </code></pre> <h2 id="解決方法"><a href="#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95">解決方法</a></h2> <p>Package ManagerからVisual Studio Code Editorのパッケージのバージョンを上げる<br /> メニューのWindowからPackage Managerを開きVisual Studio Code Editorを選択します。<br /> <a href="https://crieit.now.sh/upload_images/9cae6712694566728ba164b07b0de6a55f2011a170923.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9cae6712694566728ba164b07b0de6a55f2011a170923.png?mw=700" alt="image" /></a><a href="https://crieit.now.sh/upload_images/efb9498b362bae3503042cf8d346d1d45f2011ac96429.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/efb9498b362bae3503042cf8d346d1d45f2011ac96429.png?mw=700" alt="image" /></a><br /> 初期は1.1.4になっているはずなので、1.2.0を選び右下の<strong>Update to 1.2.0</strong>ボタンを押して更新します。</p> <p>更新後はVisual Studio Codeを再起動してください。</p> <p>再起動後も参照ができないままのときは、少し時間をおいてからVisual Studio Codeを再起動してみてください。</p> block tag:crieit.net,2005:PublicArticle/15396 2019-09-15T11:15:26+09:00 2019-09-15T11:15:26+09:00 https://crieit.net/posts/AI-5d7d9ebe642fc 最新AIを使って人の顔の大きさを数値化するアプリをつくった話 <h2><strong>このアプリを作った理由</strong></h2> <p>顔の大きさが気になる人は多いと思っています。</p> <p><a href="https://crieit.now.sh/upload_images/af4421e09dc052a0c8a8dc4118c4c0145d7d9c8f6790b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/af4421e09dc052a0c8a8dc4118c4c0145d7d9c8f6790b.png?mw=700" alt="image.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/af4421e09dc052a0c8a8dc4118c4c0145d7d9cb436578.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/af4421e09dc052a0c8a8dc4118c4c0145d7d9cb436578.png?mw=700" alt="image.png" /></a></p> <p>図からわかるように、顔の大きさや輪郭が気になる人は8割ぐらいいることがわかります(女性)<br /> これらから下のように考えました</p> <hr /> <p><a href="https://crieit.now.sh/upload_images/af4421e09dc052a0c8a8dc4118c4c0145d7d9d371e249.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/af4421e09dc052a0c8a8dc4118c4c0145d7d9d371e249.png?mw=700" alt="image.png" /></a></p> <p>しかし、その顔の大きさを測る<strong>便利で簡単な方法</strong>が確立されていないと感じこのアプリを作りました<br /> <a target="_blank" rel="nofollow noopener" href="https://play.google.com/store/apps/details?id=com.Miyagin.Face_shape">作ったアプリ</a><br /> (対応デバイスはアンドロイドの新しい機種です)</p> <hr /> <p>ボタンを押すだけで、即座に顔の表面積を計算してくれます<br /> <a href="https://crieit.now.sh/upload_images/26e8288460c7657ef3f7ba30d1ba87e65d7d9e68be90f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/26e8288460c7657ef3f7ba30d1ba87e65d7d9e68be90f.png?mw=700" alt="無題1.png" /></a></p> <p>それらを記録してグラフを表示したり、<br /> 他のユーザがどのくらいの大きさなのか、知ることができます。<br /> 平均や偏差値もしることができます</p> <p><a href="https://crieit.now.sh/upload_images/eb6ebc840e21d6c4219808104c0d2ac05d7d9d96d5b8b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/eb6ebc840e21d6c4219808104c0d2ac05d7d9d96d5b8b.png?mw=700" alt="topbar.png" /></a></p> <p>他の人との差を知ったり、ダイエットに役立てたりすることができると思います</p> <p>ぜひ使ってください(レビューをもらいたいです)</p> miyagin15 tag:crieit.net,2005:PublicArticle/15098 2019-06-12T19:42:11+09:00 2019-06-12T19:43:56+09:00 https://crieit.net/posts/Unity-by-5d00d703d1fa9 【Unity】大学生でも出来る全部無料のゲーム開発環境(おすすめアセットなど) by 大学生ゲーム制作者 <p>2019/5/20</p> <p>おはようございます。</p> <p>ゲーム作りも面白いけど、機械学習とか極めてみるのも面白そうだなあと思っている、<strong>大学生ゲーム制作者の村人U</strong>です。</p> <p> </p> <p><strong>VR</strong>とか<strong>MR</strong>もいいですね。</p> <p> </p> <p>さて、今日はゲームの<strong>開発環境(基本的にUnityのアセット)</strong>について書こうと思います。</p> <p><strong>開発環境</strong>といっても、ゲームを作る為に使っているツールの話で、<strong>開発者向けの内容</strong>になってしまいます。</p> <p> </p> <p>実はゲーム開発を開始した段階で、一度こういう記事は書いたのですが、<strong>結構変化があった</strong>のでその辺についても触れながら、今の開発環境を改めて書き直そうと思います。</p> <p><a target="_blank" rel="nofollow noopener" href="https://questgames.hatenablog.com/entry/2019/02/24/012001">無料かつ個人(チーム)でのゲーム開発環境</a></p> <p> </p> <p>ちなみに、紹介するものは<strong>全部無料</strong>で利用出来ます。</p> <p> </p> <h1 id="Unity"><a href="#Unity">Unity</a></h1> <p>ゲーム制作者お馴染みのゲームエンジン。</p> <p><strong>これが無ければ始まらない。</strong></p> <p> </p> <p>これ一つでPCゲームから、スマホゲーム、VRまで作る事が出来てしまいます。</p> <p> <br /> <img src="https://cdn-ak.f.st-hatena.com/images/fotolife/Q/QuestGames/20190520/20190520165515.png" alt="ゲーム機同時にこういうの見た事ないですか?" /></p> <p>↑ゲーム起動時にこういうの見た事ないですか?<br />  </p> <p>使った事はないのですが、対抗馬に<strong>UnrealEngine</strong>というのがあって、イメージとしては<strong>UnrealEngineは3Dゲームや映像制作に強く、プログラミングがそこまでできなくても扱えます。</strong></p> <p><strong>Unityは2Dでも3Dでも幅広く扱えて、頑張れば何でも出来る</strong>というイメージです。</p> <p> </p> <p>また、ネットの情報量はUnityの方が多いような気がします。</p> <p> </p> <p>Unityについては↓の記事が分かりやすいです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/nmxi/items/7950fb12ef925efa276d">今日からはじめるUnity - Qiita</a></p> <p> </p> <h1 id="Photon2"><a href="#Photon2">Photon2</a></h1> <p>Unityのアセットの1つです。</p> <p>Unityに入れて使います。</p> <p>昔は以下の機能が別々に分けられていたのですが、今は1つのPhoton2にまとめられています。</p> <p> </p> <h2 id="Photon Unity Networking (PUN)"><a href="#Photon+Unity+Networking+%28PUN%29">Photon Unity Networking (PUN)</a></h2> <p><strong>オンライン通信</strong>を行う為に使用します。</p> <p>マルチプレイや通信対戦が可能になります。</p> <p> </p> <p>今回はリアルタイム通信で<strong>位置同期やダメージの同期、マッチングなど</strong>に使っています。</p> <p>ラグが大きいかなぁと心配していたのですが、頑張れば白猫プロジェクトくらいのゲームが作れると思います。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.assetstore.unity3d.com/#!/content/119922?aid=1011l7wAm">PUN2 - FREE AssetStore</a><br />  </p> <h2 id="PhotonChat"><a href="#PhotonChat">PhotonChat</a></h2> <p>PUNは常に様々な通信が必要な場合に使いますが、<strong>文字のやりとりや数回程度の通信であればPhotonChat</strong>が利用できます。</p> <p> </p> <p>PUNでもチャット機能は実装できるのですが、PhotonChatはチャットに特化していてPhoton2より楽に実装出来ます。</p> <p> </p> <p>また、荒野行動のように<strong>フレンドにリアルタイムでチーム招待を送る機能</strong>などは、このPhotonChatで行う事が出来ます。</p> <p> </p> <p>Photon2については↓の記事が分かりやすいです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://connect.unity.com/p/pun2deshi-meruonraingemukai-fa-ru-men-sono1">PUN2で始めるオンラインゲーム開発入門【その1】 - Unity Connect</a></p> <p> </p> <h1 id="PlayFab"><a href="#PlayFab">PlayFab</a></h1> <p><strong>サーバーにデータを置きたい場合</strong>などに利用します。</p> <p>俗にmBaaSと呼ばれるサービスです。</p> <p> </p> <p><strong>所持コイン、魔法石、フレンド、プレイ時間などのユーザーデータをサーバーで管理する事ができます。</strong></p> <p>Unityとの相性も良いです。</p> <p>ただし、ネットにはほぼ英語の情報しかないです。</p> <p> </p> <p>僕の知っている限り、このようなサービスは他にもNifty Cloud Mobile Backend (NCMB)やfirebaseなどがあります。</p> <p>僕も初めはNCMBを使っていたのですが、PlayFabに移行しました。</p> <p>理由は後ほど説明しますが、初心者の方にはNCMBをおすすめします。</p> <p> </p> <p>PlayFabについては、↓の記事が導入を書いてくれています。</p> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ume67026265/items/c89b391e36855f45586f">PlayFabでUnityを動かしてみる -その1下準備編- - Qiita</a></p> <p>↓の記事では実際に使ってみた感想やまとめを書きました。</p> <p><a target="_blank" rel="nofollow noopener" href="https://questgames.hatenablog.com/entry/2019/04/07/122135">無料で使えるmBaaSのPlayFabをプレイヤーデータ管理に使ってみた by 大学生ゲーム制作者</a></p> <p> </p> <h1 id="Social Connector"><a href="#Social+Connector">Social Connector</a></h1> <p>こちらもUnityのアセットです。</p> <p><strong>ツイッターやLineなどのSNSと連携したい時に使います。</strong></p> <p> </p> <p>ゲーム内からSNSのアプリを起動し、用意した定型文やスクリーンショットの投稿をしてもらう事が出来ます。</p> <p> </p> <p>しかし、<strong>SNS連携はURL Schemeを利用する方法もあり、ツイッターかLineであればこのURL Schemeを利用する方が簡単です。</strong></p> <p>このSocialConnectorはその他のSNSにも幅広く対応出来るというのが魅力かなぁと思っています。</p> <p> </p> <p>SocialConnectorについては↓の記事が分かりやすいです。</p> <p><a target="_blank" rel="nofollow noopener" href="http://nn-hokuson.hatenablog.com/entry/2018/05/16/195527">【Unity】Social ConnectorでTwitterにスクショを投稿する</a></p> <p> </p> <p> </p> <h1 id="Nifty Cloud Mobile Backend (NCMB)"><a href="#Nifty+Cloud+Mobile+Backend+%28NCMB%29">Nifty Cloud Mobile Backend (NCMB)</a></h1> <p>↑で説明したPlayFabのように、<strong>サーバーでデータを管理</strong>する時に使います。</p> <p> </p> <p>サーバーにデータを保存、サーバーからデータを取得などの機能があります。</p> <p>また、Unityとの相性も良いです。</p> <p> </p> <p>PlayFabと違うのは、機能の数と無料枠です。</p> <p><strong>PlayFabはフレンド機能、グループ機能、ログイン機能、アイテム機能などが事前に備わっている、要はゲーム特化になっている</strong>のに対し、</p> <p><strong>NCMBは、エクセルのようなマスがあるだけの、シンプルで幅広い用途で利用できる</strong>のが特徴です。</p> <p> </p> <p>また、↓の記事でも書きましたが、NCMBは<strong>無料枠</strong>が少し足りない可能性があります。</p> <p>対してPlayFabはNCMBのような制限は無いので、ユーザーの数が増えたとしても問題ありません。</p> <p> </p> <p><a target="_blank" rel="nofollow noopener" href="https://questgames.hatenablog.com/entry/2019/04/04/220926">Unityでリアルタイムフレンド申請の実装方法を考えた話 by 大学生ゲーム制作者</a></p> <p> </p> <p>ただ、PlayFabは多機能な分複雑な上、<strong>日本語の情報が少ない</strong>ので、扱うのが少し難しいと思います。</p> <p><strong>なので初心者の方であれば、シンプルで日本語の情報が多い、NCMBをおすすめします。</strong></p> <p> </p> <p>NCMBについては↓の記事が分かりやすいです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://tama-lab.net/2018/06/%E3%80%90unity%E3%80%91ncmb%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E4%BF%9D%E5%AD%98%E3%80%81%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B/">【Unity】NCMBを使ってサーバにデータを保存、取得する方法 | tama-lab</a></p> <p> </p> <p>さて。</p> <p>今回は、Unityで使用しているアセットを中心に今の僕の開発環境について書きました。</p> <p>まだ今後、課金機能、ガチャ機能などを作る予定なのでその時に使ったものについてはまた後日書きたいと思います。</p> <p> </p> <p>こちらからは以上でーす。ではでは。</p> QuestGames tag:crieit.net,2005:PublicArticle/15062 2019-06-06T02:40:29+09:00 2019-06-06T02:42:32+09:00 https://crieit.net/posts/1-5cf7fe8d65c49 アニメーションについて1 <h2 id="前置き"><a href="#%E5%89%8D%E7%BD%AE%E3%81%8D">前置き</a></h2> <p>Unityを触る上でアニメーションはとても大事。らしい。<br /> <strong>ま僕アニメーション部分担当した事ないですがw</strong><br /> なので先ずはアニメーションの基礎知識を学ぶのが必要らしいので↓のサンプルを元に勉強して行く。<br /> (動作させた動画も撮ったけどどこにあげていいものかよく分からんので一旦保留。)</p> <h2 id="3DRunゲーム"><a href="#3DRun%E3%82%B2%E3%83%BC%E3%83%A0">3DRunゲーム</a></h2> <p><a href="https://crieit.now.sh/upload_images/51c1291d585b4555565f2d721e894d975cf7f6d608113.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/51c1291d585b4555565f2d721e894d975cf7f6d608113.png?mw=700" alt="スクリーンショット 2019-06-06 1.56.00.png" /></a><br /> (これ画像サイズ調節できる機能ないんか・・?)</p> <p>障害物をぴょんぴょん乗り越えて行くオートスクロール系の3Drun。<br /> これにはどんな技術が使われてるんだろうか。</p> <h2 id="Unityアニメーション基礎知識"><a href="#Unity%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%9F%BA%E7%A4%8E%E7%9F%A5%E8%AD%98">Unityアニメーション基礎知識</a></h2> <h3 id="Mecanim(メカニム)"><a href="#Mecanim%EF%BC%88%E3%83%A1%E3%82%AB%E3%83%8B%E3%83%A0%EF%BC%89">Mecanim(メカニム)</a></h3> <p>・UnityにはMecanimと呼ばれるアニメーションシステムがある。<br /> ・Unity 4.0から導入されたアニメーションの機能。(ちなみに今はUnityの最新は2019)<br /> ・これ、スクリプト書かずにアニメーションが作れる(!?)らしい。</p> <h2 id="閑話"><a href="#%E9%96%91%E8%A9%B1">閑話</a></h2> <p>という事でMecanimを利用する事でプログラマーに頼らずアニメーション担当のスタッフだけでアニメーション部分の作成が可能なのだ!素晴らしい。分担って最高。</p> <p>という事で<strong>アニメーションについて</strong>では基本的にコードは書かず、<br /> Unityの機能Mecanimと仲良くなって行こうと思います。<br /> 本格的にUnityのAnimetorコンポーネントとか触って行くのはまた次回。</p> <p>Mecanimについての詳しい説明は<a target="_blank" rel="nofollow noopener" href="https://shade3d.jp/training/unity/tips_2.html">参考サイト</a>の方を見てください。ここに限らずググれば色々出てきます。多分触って覚えた方が分かりやすい感じですが。<br /> <strong>ってあれ?この参考サイトにアニメーションの作り方書いてある?w</strong></p> <p>^o^・・・</p> <p>ま、まぁ僕の方でもある程度掻い摘んで載せて行きます。<br /> 了</p> <h3 id="参考サイト"><a href="#%E5%8F%82%E8%80%83%E3%82%B5%E3%82%A4%E3%83%88">参考サイト</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://shade3d.jp/training/unity/tips_2.html">Mecanimについて</a></p> じょんむる tag:crieit.net,2005:PublicArticle/15060 2019-06-05T02:03:50+09:00 2019-06-05T02:14:48+09:00 https://crieit.net/posts/Unity-5cf6a4765192f Unityでゲーム作る(方針のみ) <h2 id="Unityでゲーム作りたい"><a href="#Unity%E3%81%A7%E3%82%B2%E3%83%BC%E3%83%A0%E4%BD%9C%E3%82%8A%E3%81%9F%E3%81%84">Unityでゲーム作りたい</a></h2> <p>「ゲーム作るかー」と思ってても中々やらんし、ゲーム案件最近触ってないんで勉強がてらやっていきます。<br /> 先ずは記事だけ。明日から触って行く。<br /> 暇があったら別記事でhtml5ゲームの方も作ります。今はunity。</p> <h2 id="技術とか知見になる予定のもの"><a href="#%E6%8A%80%E8%A1%93%E3%81%A8%E3%81%8B%E7%9F%A5%E8%A6%8B%E3%81%AB%E3%81%AA%E3%82%8B%E4%BA%88%E5%AE%9A%E3%81%AE%E3%82%82%E3%81%AE">技術とか知見になる予定のもの</a></h2> <ul> <li>Unity</li> <li>UniRx</li> <li>2D/3D</li> <li>AR/VR</li> </ul> <h2 id="閑話"><a href="#%E9%96%91%E8%A9%B1">閑話</a></h2> <blockquote> <p><strong>俺の考えてるゲーム絶対面白いのに作る時間ないが?</strong><br /> <strong>作り方わからんが?</strong></p> </blockquote> <p>と多少プログラム触ってきたけど「おし、作るか!」から「はい完成!」とはいかないゲーム。<br /> WebGL?iOS?Android?Steam?とかプラットフォーム選別から始めちゃうと「あ、だり」となってやらない事も多い。<br /> <strong>「Unityのチュートリアル一通りやったぞwふんw」</strong><br /> って人には不要な記事かもしれない。</p> <p>基本参考書の順番で作って行く。<br /> 2Dアクションゲームが本当は作りたいけど参考書の順番で作って行く。</p> <h2 id="参考書"><a href="#%E5%8F%82%E8%80%83%E6%9B%B8">参考書</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www.borndigital.co.jp/book/6633.html">Unityゲーム プログラミング・バイブル</a></p> じょんむる tag:crieit.net,2005:PublicArticle/15035 2019-05-28T19:37:44+09:00 2019-05-28T19:37:44+09:00 https://crieit.net/posts/PlayFab-Unity-PlayFab 【PlayFab入門】UnityでPlayFabサーバーとデータの送受信 <p>2019/4/28</p> <p>おはようございます。</p> <p>「1日3食、夜は寝る」は誰が決めたんだろうと思っている、大学生ゲーム制作者の村人Uです。</p> <p> </p> <p>今回は、<strong>Azure</strong>が運営する<strong>PlayFab</strong>というサービスを利用して、<strong>サーバーでデータ管理</strong>をする方法について書きます。</p> <p>説明は<strong>Unity</strong>を使用したものになりますが、使い方はUnity以外でもあまり変わりません。</p> <p> </p> <p><strong>PlayFab</strong>については、別の記事に書きましたので今回は実際の使い方について触れていきます。</p> <p> </p> <p><a target="_blank" rel="nofollow noopener" href="https://questgames.hatenablog.com/entry/2019/04/07/122135" target="_blank" rel="noopener">無料で使えるmBaaSのPlayFabをプレイヤーデータ管理に使ってみた by 大学生ゲーム制作者 - QUEST LAB</a></p> <p> </p> <p> </p> <h1 id="サーバーにログインする"><a href="#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AB%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%99%E3%82%8B">サーバーにログインする</a></h1> <p> <br /> <strong>PlayFab</strong>では、ユーザーがサーバーに必ず<strong>ログイン</strong>する必要があります。</p> <p>といっても、ユーザーにIDとパスワードを決めてもらう必要はなく、<strong>こちらで勝手に決めたIDでログイン</strong>ができます。</p> <pre><code class="cs">//using PlayFab.ClientModels;をスクリプトの最初に記述して下さい public static void LoginPlayFab(Action resultCallback = null) { var request = new LoginWithCustomIDRequest { CustomId = SystemInfo.deviceUniqueIdentifier, CreateAccount = true}; PlayFabClientAPI.LoginWithCustomID(request, result => OnLoginSuccess(result, resultCallback), OnLoginFailure); } </code></pre> <p>上記の例で、<strong>LoginPlayFab</strong>関数を使う事でPlayFabにログインする事ができます。</p> <p>なかなかヘンテコな書き方があって、見慣れないかもしれませんがPlayFabを使う場合は今後もこんな感じで書いていきます。</p> <pre><code class="cs"> var request = new LoginWithCustomIDRequest { CustomId = SystemInfo.deviceUniqueIdentifier, CreateAccount = true}; </code></pre> <p>この部分では、ログインするための<strong>リクエスト文</strong>を作成しています。</p> <p>この中の<strong>CustomId</strong>がユーザーを識別するIDで、<strong>ユーザー毎にユニークな(唯一の)ID</strong>を指定する必要があります。</p> <p>今回の場合は、<strong>SystemInfo.deviceUniqueIdentifier という端末ごとに違うID</strong>を取得して利用しています。</p> <p>このように、<strong>CustomId</strong>や<strong>CreateAccount</strong>のようなPlayFabさんが事前に決めた変数名に値を代入する事でPlayFabのサーバーにどんな指示を送るか決める事が出来ます。</p> <p>注)これらは、<strong>using PlayFab.ClientModels;</strong> がスクリプトの最初に記述されている必要があります。</p> <h1 id="サーバーにデータを送信する"><a href="#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B">サーバーにデータを送信する</a></h1> <p>サーバーへのログインに成功したら、<strong>データを送信</strong>する事が出来るようになります。</p> <pre><code class="cs">public static void SendUserData(){ PlayFabClientAPI.UpdateUserData( new UpdateUserDataRequest(){ Data = new Dictionary<string, string>() { {"Coin", "10"} <span>}</span><span>}</span>, result => Debug.Log("Successfully updated user data"), error => { Debug.Log("Got error setting user data"); Debug.Log(error.GenerateErrorReport()); }); } </code></pre> <p>これまたヘンテコなのですが、↑では<strong>PlayFabClientAPI.UpdateUserData</strong>という一つの関数しか使っていません。</p> <p>このように関数を使う事によって、今回の場合サーバーに、「<strong>キーが"Coin"、値が"10"</strong>」のデータを送信する事が出来ます。</p> <p>ちなみに、この関数は↓のような引数を取っています。</p> <pre><code class="cs">PlayFabClientAPI.UpdateUserData(UpdateUserDataRequest request , Action<UpdateUserDataResult> resultCallback, Action<PlayFabError> errorCallback); </code></pre> <p>今回の場合、この中の<strong>request</strong>の部分が</p> <pre><code class="cs">new UpdateUserDataRequest(){ Data = new Dictionary<string, string>() { {"Coin", "10"} <span>}</span><span>}</span> </code></pre> <p>に当たり、<strong>resultCallback</strong>が</p> <pre><code class="cs"> result => Debug.Log("Successfully updated user data") </code></pre> <p>に当たり、<strong>errorCallback</strong>が</p> <pre><code class="cs">error => { Debug.Log("Got error setting user data"); Debug.Log(error.GenerateErrorReport()); } </code></pre> <p>に当たります。</p> <h1 id="サーバーからデータを受け取る"><a href="#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%8B%E3%82%89%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B">サーバーからデータを受け取る</a></h1> <p>サーバーへの送信とほぼ同じ感覚で出来ます。 </p> <pre><code class="cs">public static void GetUserData(){ PlayFabClientAPI.GetUserData( new GetUserDataRequest() { PlayFabId = PlayerData.Instance.MyPlayFabID, Keys = null }, OnGetUserData }, (error) => { Debug.Log("Got error retrieving user data:"); Debug.Log(error.GenerateErrorReport()); }); } public static void OnGetUserData(GetUserDataResult result){ if (result.Data == null || !result.Data.ContainsKey("Coin")) Debug.Log("No Coin"); else result => Debug.Log(result.Data["Coin"].Value); } </code></pre> <p>↑の例では、PlayFabサーバーから<strong>ユーザーデータ</strong>を取得し<strong>「Coin」というキーを持つデータの値</strong>を取り出しています。</p> <p>ここでもサーバーにデータを送信する時と似た感じで、次のような関数を使っています。</p> <pre><code class="cs">PlayFabClientAPI.GetUserData(GetUserDataRequest request , Action<GetUserDataResult> resultCallback, Action<PlayFabError> errorCallback </code></pre> <p>送信時と大きく違うのは、この関数の引数の内<strong>resultCallback</strong>に当たる部分が<strong>OnGetUserData</strong>になっているという事です。</p> <p>見てもらえれば分かるように、<strong>resultCallback</strong>の型は <strong>Action</strong> になっています。</p> <p>この <strong>Action</strong> という型は、「<strong>GetUserDataResult</strong>を引数にとる関数」を表します。</p> <p>なので、今回の場合<strong>OnGetUserData</strong>という<strong>GetUserDataResultを引数に持つ関数</strong>を指定する事が出来ます。</p> <p>そしてこの<strong>resultCallback</strong>という引数ですが、これは<strong>PlayFabからデータの取得が成功した時に呼び出される関数</strong>になります。</p> <p>つまり、今回の場合<strong>データの取得が完了したらOnGetUserDataという関数が呼び出される</strong>ことになります。</p> <p>またその時に引数に渡される<strong>GetUserDataResult</strong>に様々な情報が格納されています。</p> <p>実際には、上記のように<strong>result.Data</strong>にデータが<strong>Dictionary</strong>の形で格納されているのでそれを利用する事が多いと思います。</p> <h1 id="もっと知りたい"><a href="#%E3%82%82%E3%81%A3%E3%81%A8%E7%9F%A5%E3%82%8A%E3%81%9F%E3%81%84">もっと知りたい</a></h1> <p>今回書いたのは、正確には<strong>PlayFabの中のごく一部</strong>の機能である<strong>PlayerData</strong>についてです。</p> <p>PlayFabには<strong>データの管理</strong>の方法だけでも</p> <ul> <li>Player Data</li> <li>Read Only Data</li> <li>Internal Data</li> <li>Group Data</li> <li>Shared Group Data</li> </ul> <p>など、様々なデータの扱い方があります。</p> <p>複雑に感じるかもしれませんが、逆に言うとそれだけ<strong>機能が充実</strong>しているという事でもあると思います。</p> <p>詳しい説明は↓の<strong>PlayFabのドキュメント</strong>に載っています。</p> <p>現時点(2019/4/28)で全て<strong>英語</strong>ですが、<strong>グーグル翻訳</strong>を使えば割と読めちゃいます。</p> <p>最近のグーグル翻訳は凄いですね。</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/gaming/playfab/?#pivot=documentation&panel=playfab" target="_blank" rel="noopener">Azure PlayFab のドキュメント - PlayFab | Microsoft Learn</a></p> QuestGames tag:crieit.net,2005:PublicArticle/15002 2019-05-20T17:56:38+09:00 2019-05-20T17:56:38+09:00 https://crieit.net/posts/mBaaS-PlayFab-by 無料で使えるmBaaSのPlayFabをプレイヤーデータ管理に使ってみた by 大学生ゲーム制作者 <p>ご存知の方は少ないかもしれませんが、サーバーの知識いらずで<strong>サーバーでデータ管理</strong>できるサービスがあります。</p> <p> </p> <p>そのサービスは<strong>mBaaS</strong>などと呼ばれています。</p> <p>特に<strong>Unity</strong>を使う個人開発者は、<strong>Nifty Cloud Mobile Backend (NCMB)</strong> を使う人が多いイメージです。</p> <p> </p> <p>しかしどうやら、同様の機能をもつ<strong>PlayFab</strong>というサービスもあるようです。</p> <p>さらに最近、どんどん便利になってきているらしい。</p> <p>2018年1月に<strong>Microsoftに買収された</strong>とか。</p> <p> </p> <p>今回は、そんなPlayFabを実際に使ってみた<strong>感想</strong>と<strong>まとめ</strong>を書いてみようと思います。<br />  </p> <h1 id="PlayFabで出来る事"><a href="#PlayFab%E3%81%A7%E5%87%BA%E6%9D%A5%E3%82%8B%E4%BA%8B">PlayFabで出来る事</a></h1> <p>そもそもPlayFabで何が出来るかというと、</p> <ol> <li>サーバーで<strong>データ管理</strong></li> <li>ログインなど<strong>プレイヤー管理</strong></li> <li><strong>フレンド</strong>機能</li> <li><strong>SNS</strong>連携</li> <li><strong>グループ、ギルド</strong>機能</li> <li><strong>アイテム</strong>管理</li> <li><strong>課金</strong>機能</li> <li><strong>サーバーサイドのスクリプト</strong>も実装可能</li> <li><strong>統計</strong>をカスタマイズして見れる</li> </ol> <p>こんな感じの事が出来ます。<br /> これらの機能を<strong>いい感じに</strong>管理してくれているのが、<strong>PlayFab</strong>の特徴です。</p> <p>今回僕が使ったのは、1 ~ 3の機能だけですが十分便利でした。</p> <p>また、<strong>現在はプレ版</strong>ですが今後のアップデートで<strong>リアルタイム通信</strong>関係の機能も追加されるようです。</p> <p> </p> <h1 id="PlayFabの利用例"><a href="#PlayFab%E3%81%AE%E5%88%A9%E7%94%A8%E4%BE%8B">PlayFabの利用例</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://playfab.com/games/">https://playfab.com/games/</a>のページに載っていますが、</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.minecraft.net/ja-jp/">MineCraft</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ubisoft.co.jp/r6s/">Rainbow Six Siege</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.angrybirds.com/games/angry-birds-seasons/">Angry Birds Seasons</a></li> </ul> <p>など、<strong>有名なゲーム</strong>の利用例があるようです。</p> <p>世界的に有名なゲームの名前があるので、<strong>信用できるサービス</strong>と言えます。</p> <p>また、PlayFabを調べていると2015年の記事も見つかるので<strong>海外では割と長い事使われていそう</strong>です。</p> <p> </p> <h1 id="PlayFabの利用価格"><a href="#PlayFab%E3%81%AE%E5%88%A9%E7%94%A8%E4%BE%A1%E6%A0%BC">PlayFabの利用価格</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://playfab.com/pricing/">https://playfab.com/pricing/</a>のページに載っています。</p> <p>無料枠では、</p> <ul> <li><strong>一部の機能</strong>が利用できない</li> <li><strong>MAU</strong>(月のアクセスユーザー数)の<strong>制限なし</strong></li> <li><strong>データ容量、APIリクエスト回数制限</strong>に関する<strong>記述はなさそう</strong></li> </ul> <p> </p> <p>一部の機能が利用できない事についてですが、<strong>有料枠の機能は僕が見ても何に使う機能か分からない</strong>レベルの物が多かったです。</p> <p>この記事で書いてきた機能については<strong>おそらく全て無料で利用可能</strong>です。</p> <p>正直、<strong>無料枠で十分</strong>だと思っています。</p> <p>(まだ実際に運用している訳ではないので、断言はできません)</p> <p> </p> <h1 id="その他サービスとの比較"><a href="#%E3%81%9D%E3%81%AE%E4%BB%96%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%A8%E3%81%AE%E6%AF%94%E8%BC%83">その他サービスとの比較</a></h1> <p><strong>その他サービス</strong>と言っても、僕は<strong>NCMB</strong>しか使ったことないのですが...</p> <p><strong>PlayFab</strong>を使う<strong>メリット</strong>は、</p> <ul> <li>ユーザー管理が<strong>楽</strong>かつ<strong>セキュア</strong>(既に必要な機能が揃っている)</li> <li><strong>ゲームに特化</strong>している</li> <li>有名な利用例もあり、<strong>信用できる</strong></li> </ul> <p><strong>デメリット</strong>は、</p> <ul> <li>ドキュメントが<strong>英語しかない</strong></li> <li>使い方が<strong>やや複雑</strong>(導入コストが高い)</li> <li>ゲーム特化な分、<strong>自由度が低い</strong></li> </ul> <p> </p> <p><strong>NCMB</strong>などの場合、ログイン機能やフレンド機能だけでも<strong>結構大変</strong>です。</p> <p>まして、<strong>アイテム交換</strong>などの機能を作ろうと思うと<strong>骨が折れます</strong>。</p> <p><strong>PlayFab</strong>ではその辺の事は既に用意されているので、<strong>メソッドを使うだけ</strong>で便利です。</p> <p> </p> <p>にも関わらず<strong>日本でPlayFabはあまり浸透していません</strong>。</p> <p>おそらく一番の原因は、<strong>日本語のドキュメントがない</strong>事です。</p> <p>僕も調べていて、導入部分だけの記事くらいしか見つかりませんでした。</p> <p> </p> <p>しかし、<strong>Microsoft</strong>がつい最近<strong>新しいドキュメント</strong>を公開していて、それを見ると<strong>日本語に対応してくれそうな雰囲気</strong>を出しています。(言語で日本語が選択できる)</p> <p>今後、期待できるかもしれません。</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/gaming/playfab/#pivot=documentation&panel=playfab">PlayFabの新しいドキュメントはこちら</a></p> <p> </p> <p>また<strong>PlayFab</strong>は仕組みを<strong>理解するまで少し時間がかかります</strong>。</p> <p>機能が設計されている為、<strong>NCMB</strong>ほどユーザーの<strong>自由なデータ管理も出来ません</strong>。</p> <p>融通が利かなくて困るケースも出てくるかと思います。</p> <p> </p> <h1 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h1> <p>現状をまとめると、<strong>英語がよめるなら機能が豊富なPlayFabはおすすめ</strong>。</p> <p>厳しければ、<strong>シンプルイズベストのNCMB</strong>や<strong>応用性の高いFirebase</strong>などを使った方が良いと思います。</p> <p> </p> <p>また、まだ機能が完全に完成していない部分もあって<strong>今後の進展</strong>に期待といった感じです。</p> <p><strong>日本語ドキュメントが公開された辺りから、使い始めてみる</strong>と良いのではないでしょうか。</p> <p>もしかしたら僕がPlayFabの記事を書くかもしれませんが。</p> <p>その時はよしなに。ではでは。</p> <p> </p> <p> </p> QuestGames tag:crieit.net,2005:PublicArticle/14994 2019-05-18T19:44:40+09:00 2019-05-18T19:44:40+09:00 https://crieit.net/posts/Unity-by Unityでリアルタイムフレンド申請の実装方法を考えた話 by 大学生ゲーム制作者 <p><strong>フレンド申請</strong>に限らず、<strong>チームに招待</strong>する機能を作りたい。</p> <p>申請ボタンを押したら即座に相手に通知が行って、<strong>承認</strong>か<strong>拒否</strong>を選べる機能が欲しい!</p> <p> </p> <p>フレンドのデータベースには、Nifty Cloud Mobile Backend (<strong>NCMB</strong>)を利用する予定。</p> <p>なので始めはNCMBを使った実装を考えたけれど、どうにも問題があった。</p> <p> </p> <p>そこで最も良い方法を調べて、考えた。</p> <p>結論から言うと、最後に書く<strong>PhotonChatを使うのが良さそう</strong>。</p> <p>(まだ実際に使ったわけではないので、確定ではないのですが)</p> <p> </p> <h1 id="NCMBでデータストアを監視して実装"><a href="#NCMB%E3%81%A7%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B9%E3%83%88%E3%82%A2%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%97%E3%81%A6%E5%AE%9F%E8%A3%85">NCMBでデータストアを監視して実装</a></h1> <p>NCMBに新しく、フレンド申請クラスを追加してそれを監視する。</p> <p>フレンド申請クラスには、<strong>申請先のユーザーID, 申請元のユーザーID</strong>をデータとして入れるようにする。 </p> <p><strong>新しくデータが追加された時に、自分に対する申請が無いかをチェックする</strong>事でフレンド申請の機能にする方法。</p> <h3 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h3> <p><strong>5秒に1度</strong>くらいのペースで新規データをチェックしないとリアルタイム性に欠ける。</p> <p>データが追加された時にUnity側の関数を呼び出す機能があれば良いのだが、どうやらそれは無さそう。</p> <p>もし、毎日100人がプレイしたとするとNCMBの無料枠<strong>1,000,000API</strong>リクエストは</p> <p><strong>1,000,000(回) / 30 (日) / 100 (人) = 333 (回)</strong></p> <p><strong>333 (回) × 5 (秒) / 60 (秒) = 27.75 (分)</strong></p> <p>となり、<strong>一人当たり一日28分以上プレイすると無料枠を超える事になる</strong>。</p> <p>NCMBの無料枠を超えると、5万円 / 月 かかるのでこんなオーバーの仕方ではもったいなさすぎる。</p> <p> </p> <h1 id="NCMBでプッシュ通知で実装"><a href="#NCMB%E3%81%A7%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E9%80%9A%E7%9F%A5%E3%81%A7%E5%AE%9F%E8%A3%85">NCMBでプッシュ通知で実装</a></h1> <p><strong>NCMB</strong>の機能を使って、特定の人にフレンド申請の<strong>プッシュ通知</strong>を送る。</p> <p>プッシュ通知とは<strong>iPhone</strong>とか<strong>Android</strong>でゲームを起動してなくても来る通知の事。</p> <h3 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h3> <p>そもそもプッシュ通知の本来の使い方と違う気がする。</p> <p>プッシュ通知を利用する場合、NCMBではiPhoneかAndroidで<strong>実機ビルドをする必要がある</strong>。(エディタ上では使えない)</p> <p>また、iOSでは確かAppleDeveloperの有料アカウントが必要など非常に<strong>面倒事が多い</strong>。</p> <p>一番の問題は<strong>エディタ上で使えない為、デバッグが大変すぎる</strong>という事。</p> <p> </p> <h1 id="Photon Unity Networkingで個人ルームに入室して実装"><a href="#Photon+Unity+Networking%E3%81%A7%E5%80%8B%E4%BA%BA%E3%83%AB%E3%83%BC%E3%83%A0%E3%81%AB%E5%85%A5%E5%AE%A4%E3%81%97%E3%81%A6%E5%AE%9F%E8%A3%85">Photon Unity Networkingで個人ルームに入室して実装</a></h1> <p>Photon Unity Networking (<strong>PUN</strong>) で<strong>RPC</strong>を利用する方法。</p> <ol> <li>メニュー画面に入った時点で、自分一人のルームを作成する</li> <li>NCMBで保存されている申請を送りたい人のPhotonUserIDでFindFriend関数を使う</li> <li>Photonのフレンドリストが更新されるので申請を送りたい人を探し、その個人部屋に入室する</li> <li>RPCを使って申請のやりとりを行う</li> </ol> <h3 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h3> <p><strong>常に個人用のルームに入室</strong>している必要があり、<strong>無駄</strong>。</p> <p><strong>複数人同時</strong>に申請を送る場合、ルームがわけわからなくなる。</p> <p>申請をする為だけに人のルームに勝手にはいる事になり、何か違う。</p> <p>実装がなかなかに面倒くさい。</p> <p> </p> <h1 id="Photon Unity Networkingでフレンドの招待ルームを監視して入室"><a href="#Photon+Unity+Networking%E3%81%A7%E3%83%95%E3%83%AC%E3%83%B3%E3%83%89%E3%81%AE%E6%8B%9B%E5%BE%85%E3%83%AB%E3%83%BC%E3%83%A0%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%97%E3%81%A6%E5%85%A5%E5%AE%A4">Photon Unity Networkingでフレンドの招待ルームを監視して入室</a></h1> <p>上記同様<strong>PUN</strong>を利用するが、こちらは<strong>チーム申請</strong>の際の話。</p> <p><strong>既にフレンドリストが存在</strong>していれば、</p> <ol> <li>招待する側はカスタムプロパティで<strong>Invite</strong>っていう名前の<strong>ルームを作る</strong></li> <li>ルームに入れる人を<strong>チーム申請したい人のUserIDのみに制限する</strong></li> <li>全てのプレイヤーはFindFriend関数ですべてのオンラインのフレンドを取得している</li> <li>全てのプレイヤーは<strong>オンラインのフレンドがInviteという名前のルームを作成した場合、そのルームへの参加を試みる</strong></li> <li>参加に成功すればそれがチームになる</li> </ol> <h3 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h3> <p>常にFindFriend関数を更新する必要がある。</p> <p><strong>非常に無駄。</strong></p> <p><strong>実装が大変。</strong></p> <p> </p> <h1 id="NCMBの代わりにPlayFabを使ってみる"><a href="#NCMB%E3%81%AE%E4%BB%A3%E3%82%8F%E3%82%8A%E3%81%ABPlayFab%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B">NCMBの代わりにPlayFabを使ってみる</a></h1> <p>最近サービスが充実してきた<strong>PlayFab</strong>というサービスがあるらしい。</p> <p>日本ではまだ全然広まっていないが、マイクラなど有名なゲームが採用しているようだ。</p> <p>機能は、<strong>NCMB</strong>のように<strong>mBaaS</strong>だと思われる。</p> <p>NCMBとの違いは、<strong>よりゲームに特化した機能が多い</strong>事だ。</p> <p>機能が豊富なので<strong>NCMB</strong>の問題を解決できるかと期待したが、</p> <p>『普通そういう使い方はしません。</p> <p>今後のアップデートでそういう事が出来るようになる可能性が高いけど、今はまだNCMBと同じ方法でしか出来ないよ』</p> <p>と書かれていて残念。</p> <h3 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h3> <p>今回の件に関しては<strong>NCMB同様の問題で解決できそうにない</strong>。</p> <p>また、まだ<strong>日本語のドキュメントが皆無</strong>と言ってい良い。</p> <p>英語読むの大変。</p> <p>でもNCMBやめてデータベースとして利用したいと思えるほど便利そうだった。</p> <p> </p> <h1 id="NCMBの代わりにFirebaseを使ってみる"><a href="#NCMB%E3%81%AE%E4%BB%A3%E3%82%8F%E3%82%8A%E3%81%ABFirebase%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B">NCMBの代わりにFirebaseを使ってみる</a></h1> <p>NCMBに似たサービスでもう一つ、<strong>Firebase</strong>なるものがあるようだ。</p> <p><strong>Firebase</strong>では<strong>招待の機能</strong>があってそれを使えば実現できる可能性がある。</p> <p> </p> <h3 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h3> <p>Firebaseはそれなりにサーバーサイドの<strong>知識</strong>が要りそう。</p> <p>Unityとの連携に<strong>時間的コスト</strong>がかかりそうだったから後回しにした。</p> <p>現状、次のPhoton Chatでの実装の方が楽そうだったのでそれ以上は調べていない。</p> <p> </p> <h1 id="Photon Chatで実装"><a href="#Photon+Chat%E3%81%A7%E5%AE%9F%E8%A3%85">Photon Chatで実装</a></h1> <p><strong>PlayFab</strong>の質問ページで僕と同じ事を考えている人がいた。</p> <p>その答えは、「無理だから、<strong>ExitGames</strong>とか使ってね。」</p> <p>という事だった。</p> <p> </p> <p><strong>ExitGames</strong>ってなんだ?ってなって、調べたら<strong>Photon</strong>だった。</p> <p>「だから無理だって!!」</p> <p>と思ったが調べるとPhotonChatなるものがあるらしい。</p> <p> </p> <p><strong>Photon Chat</strong>を使えば楽にリアルタイムでテキストデータのやり取りが出来るらしい。</p> <p>確かに、フレンド申請は単純な文字のやり取りにすぎない。</p> <p>そんなに難しい話じゃないやんけ!</p> <ol> <li><strong>NCMB</strong>でプレイヤーデータの中に<strong>PhotonChatのユーザーID</strong>を持っておく</li> <li>フレンド申請したいプレイヤーを検索で見つけ、そのプレイヤーのPhotonChatのユーザーIDで<strong>プライベートチャットを開始</strong></li> <li>チャットで<strong>招待メッセージ</strong>を送信</li> <li>招待メッセージを受け取ったらそれに対する回答を送信</li> <li>承認された場合、<strong>NCMB</strong>のフレンドリストに<strong>プレイヤーIDを追加</strong></li> </ol> <p> </p> <p>チームの場合も同様。</p> <p>という事でこれから実装していこうと思います。</p> <p>(絶対一筋縄じゃいかない)</p> <p> </p> <p> </p> QuestGames tag:crieit.net,2005:PublicArticle/14985 2019-05-14T18:55:13+09:00 2019-05-14T18:55:13+09:00 https://crieit.net/posts/Unity-Photon2-Photon2 【Unity】Photon2でチームマッチメイキングをする方法【Photon2】 <p>2019/3/13</p> <p>制作中のゲーム <strong>『Play the Fox』</strong> で <strong>マッチング</strong>の機能を作った。</p> <p>今回はその<strong>実装方法</strong>を書きます。</p> <p>実装した内容は以下</p> <ul> <li><p><strong>完全ランダム</strong>なマッチメイキング</p></li> <li><p><strong>1vs1</strong>及び<strong>2vs2</strong>及び<strong>8人乱闘</strong>のゲームモードに対応</p></li> <li><p><strong>2vs2</strong>ではランダムに<strong>チームを組んで</strong>マッチング</p></li> </ul> <p>今回は、<strong>Unity</strong>の<strong>Photon2</strong>を使う。...無料だから。</p> <h1 id="仕組みを解説"><a href="#%E4%BB%95%E7%B5%84%E3%81%BF%E3%82%92%E8%A7%A3%E8%AA%AC">仕組みを解説</a></h1> <p><a href="https://crieit.now.sh/upload_images/8ef4b9f750ab3eb0afcbe9893080201c5cda8f4138857.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8ef4b9f750ab3eb0afcbe9893080201c5cda8f4138857.png?mw=700" alt="20190313114119.png" /></a></p> <p>雑な図解。</p> <p>これは<strong>2vs2</strong>だけど、他も<strong>ほぼ同じ</strong>仕組み。</p> <ol> <li><p>最初に<strong>味方を見つける</strong>ためのマッチングをする。</p></li> <li><p>必要人数見方が見つかったらそれを<strong>チーム</strong>とする。</p></li> <li><p>そのチームの<strong>リーダーが相手を見つける</strong>マッチングをする。</p></li> <li><p>プレイヤーが必要人数揃ったら<strong>ゲーム開始</strong></p></li> </ol> <p>といった流れ。</p> <h1 id="実際のスクリプト"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88">実際のスクリプト</a></h1> <pre><code class="cs">using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; using ExitGames.Client.Photon; using Photon.Realtime; using Photon.Pun; using Enums; using UnityEditor; public class PTFNetworkManager : MonoBehaviourPunCallbacks { private static PTFNetworkState state = PTFNetworkState.Idle; public string leaderUserID; private string[] teamUserIDs; public static PTFNetworkState State { get { return state; } } void Update() { switch (state) { case PTFNetworkState.FindAlly: if (PhotonNetwork.InRoom) { if (PhotonNetwork.CurrentRoom.PlayerCount == PhotonNetwork.CurrentRoom.MaxPlayers && PhotonNetwork.MasterClient.UserId != null) { //人数が揃ったらリーダーとチームメンバーを設定 leaderUserID = PhotonNetwork.MasterClient.UserId; SetTeamUserIDsWithCurrentRoom(); //準備完了フラグを立てる var properties = new ExitGames.Client.Photon.Hashtable(); properties.Add( "foundAlly", true ); PhotonNetwork.LocalPlayer.SetCustomProperties(properties); //ステート切り替え state = PTFNetworkState.FoundAlly; } } break; case PTFNetworkState.FoundAlly: //全員が設定を終えたら退室 if (CheckAllPlayerFoundAlly()) { Debug.Log("Found Ally!"); PhotonNetwork.LeaveRoom(); PhotonNetwork.JoinLobby(); state = PTFNetworkState.FindOpponent; } break; case PTFNetworkState.FindOpponent: //リーダージャナイバアイ if (leaderUserID != PhotonNetwork.LocalPlayer.UserId) { PhotonNetwork.FindFriends(new string[1]{ leaderUserID }); } if (PhotonNetwork.InRoom) { //部屋に入ったら準備完了 Debug.Log("Ready! Waiting others or start!"); state = PTFNetworkState.Ready; } break; case PTFNetworkState.Ready: //人数が揃ったら開始するとか。 break; } } public override void OnPlayerPropertiesUpdate(Player target, ExitGames.Client.Photon.Hashtable changedProps) { Debug.Log("target is " + target + "\n changed props are" + changedProps); } /// <summary> /// 現在の設定でマッチメイキングを開始 /// </summary> public static void StartMatchMaking() { //ステートは味方を探す state = PTFNetworkState.FindAlly; ConnectToMaster(); } private static void ConnectToMaster(){ string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); if (!PhotonNetwork.IsConnected) { PhotonNetwork.ConnectUsingSettings(); Debug.Log("Connecting to master..."); }else{ PhotonNetwork.JoinLobby(); } } public override void OnConnectedToMaster(){ PhotonNetwork.JoinLobby(); Debug.Log("Joining Lobby..."); } /// <summary> /// ロビーに入ったらとりあえず見方を探す /// </summary> public override void OnJoinedLobby() { Debug.Log("Connected Master Server!"); if (state == PTFNetworkState.FindAlly) { FindAlly(); }else if (state == PTFNetworkState.FindOpponent) { FindOpponent(); } } private void FindAlly() { string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); //ここから見方を探す //味方のプレイヤーの人数 byte expectedMaxPlayers = 1; //想定プレイヤー //GameMode(gm)とFindType(ft)でソート ExitGames.Client.Photon.Hashtable expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "ally"} }; switch (gameMode) { case "Skirmish": expectedMaxPlayers = 1; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "ally"} }; break; case "Solo": expectedMaxPlayers = 1; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "solo" }, {"ft", "ally"} }; break; case "Duo": expectedMaxPlayers = 2; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "duo" }, {"ft", "ally"} }; break; } PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers); } /// <summary> ///相手を探す /// リーダーのみ利用。 /// </summary> private void FindOpponent() { Debug.Log("leaderUserID : " + leaderUserID); Debug.Log("myUserID : " + PhotonNetwork.LocalPlayer.UserId); if (leaderUserID != PhotonNetwork.LocalPlayer.UserId) return; Debug.Log("I am leader! I start finding opponent!!"); string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); //ここから相手を探す //全体のプレイヤーの人数 byte expectedMaxPlayers = 1; //想定プレイヤー //GameMode(gm)とFindType(ft)でソート ExitGames.Client.Photon.Hashtable expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "opponent"} }; switch (gameMode) { case "Skirmish": expectedMaxPlayers = 8; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "opponent"} }; break; case "Solo": expectedMaxPlayers = 2; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "solo" }, {"ft", "opponent"} }; break; case "Duo": expectedMaxPlayers = 4; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "duo" }, {"ft", "opponent"} }; break; } //ランダムマッチメイキング PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, MatchmakingMode.FillRoom, TypedLobby.Default, null, teamUserIDs); //ステートは相手を探す state = PTFNetworkState.FindOpponent; } /// <summary> /// ルーム入室失敗時、ステートにあった部屋を作成する /// </summary> /// <param name="returnCode"></param> /// <param name="message"></param> public override void OnJoinRandomFailed(short returnCode, string message){ Debug.Log("Faild to Join Room!"); Debug.Log(message); string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); //ルームオプションの設定 string findType = ""; if (state == PTFNetworkState.FindAlly) { findType = "ally"; }else if (state == PTFNetworkState.FindOpponent) { findType = "opponent"; } byte expectedMaxPlayers = 1; ExitGames.Client.Photon.Hashtable expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" } }; switch (gameMode) { case "Skirmish": if (state == PTFNetworkState.FindAlly) { expectedMaxPlayers = 1; }else if (state == PTFNetworkState.FindOpponent) { expectedMaxPlayers =8; } expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", findType} }; break; case "Solo": if (state == PTFNetworkState.FindAlly) { expectedMaxPlayers = 1; }else if (state == PTFNetworkState.FindOpponent) { expectedMaxPlayers =2; } expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "solo" }, {"ft", findType} }; break; case "Duo": if (state == PTFNetworkState.FindAlly) { expectedMaxPlayers = 2; }else if (state == PTFNetworkState.FindOpponent) { expectedMaxPlayers =4; } expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "duo" }, {"ft", findType} }; break; } RoomOptions roomOptions = new RoomOptions(); roomOptions.CustomRoomPropertiesForLobby = new string[]{"gm", "ft"}; roomOptions.CustomRoomProperties = expectedCustomRoomProperties; roomOptions.MaxPlayers = expectedMaxPlayers; roomOptions.PublishUserId = true; Debug.Log(roomOptions.CustomRoomProperties); //ルームの作成 PhotonNetwork.CreateRoom("", roomOptions, null, teamUserIDs); Debug.Log("Created Room!"); } public override void OnJoinedRoom() { Debug.Log("Joined Room!"); Debug.Log(PhotonNetwork.CurrentRoom.CustomProperties); if (state == PTFNetworkState.FindAlly) { var properties = new ExitGames.Client.Photon.Hashtable(); properties.Add( "foundAlly", false ); PhotonNetwork.SetPlayerCustomProperties(properties); } } private void SetTeamUserIDsWithCurrentRoom() { teamUserIDs = new string[PhotonNetwork.PlayerListOthers.Length]; for (int i=0; i<PhotonNetwork.PlayerListOthers.Length; i++) { teamUserIDs[i] = PhotonNetwork.PlayerListOthers[i].UserId; } } public override void OnFriendListUpdate(List<FriendInfo> friendList) { foreach (FriendInfo friendInfo in friendList) { if (friendInfo.UserId == leaderUserID && state == PTFNetworkState.FindOpponent) { PhotonNetwork.JoinRoom(friendInfo.Room); } } } private bool CheckAllPlayerFoundAlly() { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player player in playerList) { if (player.CustomProperties["foundAlly"] == null) { return false; } if (!(bool)player.CustomProperties["foundAlly"]) { return false; } } return true; } } </code></pre> <h1 id="雑なコード解説(需要あれば書き足します)"><a href="#%E9%9B%91%E3%81%AA%E3%82%B3%E3%83%BC%E3%83%89%E8%A7%A3%E8%AA%AC%EF%BC%88%E9%9C%80%E8%A6%81%E3%81%82%E3%82%8C%E3%81%B0%E6%9B%B8%E3%81%8D%E8%B6%B3%E3%81%97%E3%81%BE%E3%81%99%EF%BC%89">雑なコード解説(需要あれば書き足します)</a></h1> <p><strong>StartMatchMaking()</strong> を呼び出すと<strong>マッチングが開始</strong>する。</p> <p><strong>Photon</strong>に関してだけ<strong>使っている順番</strong>に書くと、</p> <pre><code class="cs">PhotonNetwork.ConnectUsingSettings(); PhotonNetwork.JoinLobby(); </code></pre> <p><strong>ゲームモードが同じ、見方探し中のルーム</strong>を探す。</p> <pre><code class="cs">PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers); </code></pre> <p>参加可能なルームが<strong>無い場合</strong>、</p> <pre><code class="cs">PhotonNetwork.CreateRoom("", roomOptions, null, teamUserIDs); </code></pre> <p>味方が<strong>全員揃ったら</strong>、</p> <pre><code class="cs">PhotonNetwork.LeaveRoom(); PhotonNetwork.JoinLobby(); </code></pre> <p><strong>ゲームモードが同じ、相手探し中のルーム</strong>を探す。</p> <pre><code class="cs">PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, MatchmakingMode.FillRoom, TypedLobby.Default, null, teamUserIDs); </code></pre> <p>参加可能なルームが<strong>無い場合</strong>、</p> <pre><code class="cs">PhotonNetwork.CreateRoom("", roomOptions, null, teamUserIDs); </code></pre> <p>ルームに入室したらステートをReadyに変えて<strong>マッチング完了</strong>。</p> <p>後は<strong>人が揃うのを待ったり</strong>、<strong>強制的にゲーム始めたり</strong>お好きにどうぞ。</p> <h1 id="あとがき"><a href="#%E3%81%82%E3%81%A8%E3%81%8C%E3%81%8D">あとがき</a></h1> <p>これは完全に<strong>ランダムでしかない</strong>。</p> <p>今後、<strong>フレンドとチームを組んだり</strong>出来るようにする予定。</p> <p>その辺に関しては先にフレンド関係のシステムを作ってからやるのでもう少し先になりそう。</p> <p>やり方としては、最初の<strong>味方探しのJoinRandomRoomに条件を加えるだけ</strong>のはず。</p> QuestGames tag:crieit.net,2005:PublicArticle/14981 2019-05-12T18:44:46+09:00 2019-05-12T18:44:46+09:00 https://crieit.net/posts/Unity-Sprite UnityでSpriteアニメーションを画像差し替え可能な実装をする <p>2019/3/8</p> <p>そろそろアニメーションをつけてかっこよくしたいという事で、その辺の機能を作った。</p> <p>でもUnityで<strong>Spriteアニメーション</strong>を行う機能が充実していなかった。</p> <p>色んな記事を見ても<strong>汎用性が低そう</strong>な実装が多い。</p> <p> </p> <p>そこで今回は<strong>Animationからスクリプトの関数を呼び出し、Animatorで管理</strong>する方法で実装してみた。</p> <p> </p> <p>利点は2つ。</p> <ul> <li><p>通常通り<strong>アニメーターが利用できる</strong>のでGUIでステート管理が可能。</p></li> <li><p>似たアニメーションを違うキャラクター用に<strong>スプライトの差し替え</strong>が可能。<br />  </p></li> </ul> <p>本当に簡単で申し訳ないが、作成方法を書いておこう。</p> <p> </p> <h1 id="スプライトの切り替えをするスクリプトを作成する"><a href="#%E3%82%B9%E3%83%97%E3%83%A9%E3%82%A4%E3%83%88%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88%E3%82%92%E3%81%99%E3%82%8B%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">スプライトの切り替えをするスクリプトを作成する</a></h1> <p><strong>スプライトの切り替え</strong>を行うスクリプトを用意する。</p> <p>今後UnityのAnimationからAnimationSpriteNextの関数を呼ぶことになる。</p> <pre><code class="cs"><br />public Sprite[] animationSprites; private int animation_index; void AnimationSpriteNext(){ GetComponent<SpriteRenderer>().sprite = animationSprites[animation_index]; animation_index = (animation_index + 1) % (animationSprites.Length); } </code></pre> <p>こんな感じの内容が書かれたスクリプトを書く。</p> <p>スクリプトを作成したら、<strong>アニメーションさせたいオブジェクトにアタッチ</strong>してスクリプトは終わり。</p> <h1 id="Animationを作る"><a href="#Animation%E3%82%92%E4%BD%9C%E3%82%8B">Animationを作る</a></h1> <p>アニメーションさせたいオブジェクトに<strong>新規AnimatorとAniamationを作成</strong>してコンポーネントに。</p> <p><strong>Animationウィンドウ</strong>で、右クリック → <strong>Add Animation Event</strong></p> <p>追加したAnimation Eventをクリックして、Inspectorで先ほど作成した<strong>AnimationSpriteNext関数を選択</strong>。</p> <p>必要なコマ数イベントを作成して、AnimationSpriteNextを呼ぶ事でアニメーションになる。</p> <h1 id="インスペクターでアニメーションさせるスプライトを設定"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%9A%E3%82%AF%E3%82%BF%E3%83%BC%E3%81%A7%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%95%E3%81%9B%E3%82%8B%E3%82%B9%E3%83%97%E3%83%A9%E3%82%A4%E3%83%88%E3%82%92%E8%A8%AD%E5%AE%9A">インスペクターでアニメーションさせるスプライトを設定</a></h1> <p>上記のスクリプトを付けたオブジェクトのInspectorを見る。</p> <p>Inspectorでこのスクリプトの<strong>animationSpritesにアニメーションに必要なスプライトを設定</strong>する。</p> <p>以上で設定は終わり。</p> <p>これで再生して無事動けば完成だ。</p> <p>僕が実際に利用する際は、<strong>animationSpritesはデータベースから参照</strong>している。</p> <p>ちなみに、データベースにはScriptableObjectを使用した。</p> <h1 id="応用"><a href="#%E5%BF%9C%E7%94%A8">応用</a></h1> <p>UnityのAnimationを使うので、<strong>追加でα値を変えるアニメーションが簡単</strong>に追加出来る。</p> <p>また、通常のアニメーションとして<strong>アニメーターで管理</strong>出来る。</p> <p>その為移動ステート、スキルステート、待機ステートなどを作って遷移規則を作ってしまえばいい感じに動く。</p> <p>Animation Eventは<strong>何も関数を呼ばないとエラー</strong>になるので、僕はEndAnimation()という<strong>空の関数</strong>を作って最後に呼ぶことにしている。</p> <h1 id="コメント"><a href="#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">コメント</a></h1> <p>正直悪くはないが、<strong>これじゃない感</strong>もある。</p> <p>特に、後から見直した時にどうなってんのか分からなくなりそうなのが怖い。</p> <p>一度アニメーション全部を管理するスクリプトを書こうと思ったけど、<br /> 結局アニメーターみたいな<strong>GUIが欲しい!ってなって詰んだ</strong>。orz</p> QuestGames tag:crieit.net,2005:PublicArticle/14968 2019-05-06T18:38:40+09:00 2019-05-06T18:38:40+09:00 https://crieit.net/posts/391ed4e36c77da173aeca2b2b761e0de 進みすぎてもはや何やったか覚えてない <p>2019/2/27</p> <p>ゲームシステムが面白いか確認したいから、</p> <p>早く一通り遊べるプロトタイプを作れという事になった。</p> <p> </p> <h1 id="やった事"><a href="#%E3%82%84%E3%81%A3%E3%81%9F%E4%BA%8B">やった事</a></h1> <ul> <li>環境配置フェーズに環境を置けるようにした</li> <li>金の仕組みを作った</li> <li>EPが自動回復するようにした</li> </ul> <h1 id="環境を置けるようにした"><a href="#%E7%92%B0%E5%A2%83%E3%82%92%E7%BD%AE%E3%81%91%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%97%E3%81%9F">環境を置けるようにした</a></h1> <p>環境配置フェーズ中に左下にある環境マーカーを掴んで離すことで置けるようにした。</p> <p>マーカーが置けない場所、置ける場所をUnityのLayerで分けた。</p> <p>指を離したタイミングでレイを飛ばして、下に置けないものがないかを確認する。</p> <p>これで今後出てくるチェス要素に対応できる。</p> <p>スマホでは指で環境マーカーが見えなくなるかと思ったけど、意外と大丈夫そうだった。</p> <p>よく考えるとパズドラも掴んでるドロップは見えないけど何とかなってる。</p> <p> </p> <h1 id="金の仕組みを作った"><a href="#%E9%87%91%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%9F">金の仕組みを作った</a></h1> <p>環境の変化(水→木→土→金)の最終状態が金だ。</p> <p>金は集めた数によって環境配置フェーズに置ける環境の数が増える。</p> <p>今は、金3枚集める毎に木、土、火が解放されていく。</p> <p>この仕組みを完成させた。</p> <p> </p> <h1 id="EPが自動回復するようにした"><a href="#EP%E3%81%8C%E8%87%AA%E5%8B%95%E5%9B%9E%E5%BE%A9%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%97%E3%81%9F">EPが自動回復するようにした</a></h1> <p>スキル発動に使うEPを自動回復するようにした。</p> <p>これでスキルの連続使用はできない。</p> <p>回復量は完全にEPがなくなった状態から4秒でスキルが使えるようになるくらい。</p> <p>あまりゲームスピードを上げすぎないようにしている。</p> <p>この辺は今後の調整が聞くように設計しておこう。</p> <p> </p> <h1 id="コメント"><a href="#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">コメント</a></h1> <p>今の段階では進捗がありすぎて、技術的な記事を書ける量ではない。</p> <p>もう少し落ち着いてきたら開発に関するニッチな記事も書いていこー。</p> QuestGames tag:crieit.net,2005:PublicArticle/14966 2019-05-05T21:22:28+09:00 2019-05-06T17:29:52+09:00 https://crieit.net/posts/ef01df3638bd8f1f8783109e6f06d8e5 リアルタイム通信におけるダメージ処理の同期、ラグへの対策方法 <p>2019/2/26</p> <p>隙間時間にちょこちょこ進めた。</p> <p>やった事は、</p> <ul> <li><p>開始のタイミングの同期</p></li> <li><p>ダメージ処理の同期</p></li> <li><p>プレイヤーデータの管理</p></li> </ul> <p>の3つ。</p> <h1 id="開始のタイミングの同期"><a href="#%E9%96%8B%E5%A7%8B%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E5%90%8C%E6%9C%9F">開始のタイミングの同期</a></h1> <p>FightステートとArrangeステートの切り替えのタイミングを同期した。</p> <p>RPCで実装していて、マスタークライアントのステートが切り替わるときに合わせる。</p> <p>プレイ時間を、roomのカスタムプロパティを用いて記録するようにした。</p> <p>が、まだどう使うかは分からない。</p> <h1 id="ダメージ処理の同期"><a href="#%E3%83%80%E3%83%A1%E3%83%BC%E3%82%B8%E5%87%A6%E7%90%86%E3%81%AE%E5%90%8C%E6%9C%9F">ダメージ処理の同期</a></h1> <p>ダメージを与えたときにRPCで同期するようにした。</p> <p>そも、ダメージ処理の方法を2つ考えていた。</p> <ol> <li><p>自分が攻撃を当てた時に、与えたダメージ同期する</p></li> <li><p>自分が攻撃を食らった時に、食らったダメージを同期する。</p></li> </ol> <p>ラグを考慮した時に起こるデメリットはそれぞれ、</p> <ol> <li><p>敵の攻撃をよけたのにラグのせいでダメージを食らう</p></li> <li><p>敵に攻撃を当てたのにラグのせいでダメージが入らない</p></li> </ol> <p>このゲームでは、攻撃を「避ける」よりも「当てる」ことに注意が向いていると思った。</p> <p>より注意が向いている方を気持ちよくしたいので、今回は2を採用してみた。</p> <p>他のfpsゲームとかも見ているとこうなっているような気がする。</p> <h1 id="プレイヤーデータの管理"><a href="#%E3%83%97%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E7%AE%A1%E7%90%86">プレイヤーデータの管理</a></h1> <p>プレイヤーのデータはサーバーで管理する。</p> <p>今回使うのはNCMB(Nifty Cloud Mobile Backend)だ。</p> <p>タイトル画面で、プレイヤーデータの照合。</p> <p>初プレイの場合は新規作成する。</p> <p>作成したプレイヤーデータに対してNCMBでIDが自動生成される。</p> <p>そのIDをPlayerPrefsで保存しておく事で2回目以降のプレイではそのデータを利用する。</p> <p>あとは、作成したデータベースに必要に応じてカラムを追加するようにした。</p> <p>今のところ、</p> <ul> <li><p>プレイヤー名</p></li> <li><p>プレイヤーID(ncmbのObjectidで代用)</p></li> <li><p>使用キャラクター</p></li> <li><p>解放済みキャラクター</p></li> <li><p>コイン</p></li> <li><p>ランク</p></li> <li><p>プレイ時間</p></li> </ul> <p>をデータとしてサーバーに保存するつもりだ。</p> <p>今はプレイたー名のみ保存できる状態。</p> <h2 id="コメント"><a href="#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">コメント</a></h2> <p>BorderLands2楽しい。</p> QuestGames tag:crieit.net,2005:PublicArticle/14965 2019-05-05T21:22:04+09:00 2019-05-06T17:28:13+09:00 https://crieit.net/posts/70fdc71af10c0c9d722e1e33594d280b 【開発2日目】いろいろ同期 <p>2019/2/26</p> <p>色んな同期処理について考えて、ちょっと実装した。</p> <h3 id="同期できたもの"><a href="#%E5%90%8C%E6%9C%9F%E3%81%A7%E3%81%8D%E3%81%9F%E3%82%82%E3%81%AE">同期できたもの</a></h3> <ul> <li>技の発動</li> <li>環境の変化</li> <li>HP</li> <li>EP</li> </ul> <p>今日は、上記の<strong>4つ</strong>の同期に成功した。</p> <p>全て<strong>Photon</strong>を使った<strong>同期処理</strong>を行う。</p> <p>技の発動と環境の変化は<strong>RPC</strong>で、</p> <p>HPとEPはPlayerの<strong>Custom Propaties</strong>を使った。</p> <h3 id="これから同期するもの"><a href="#%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E5%90%8C%E6%9C%9F%E3%81%99%E3%82%8B%E3%82%82%E3%81%AE">これから同期するもの</a></h3> <ul> <li>開始のタイミング</li> <li>残り時間</li> <li>スキルによるダメージ</li> </ul> <p>今思いつく範囲では、こんな感じ。</p> <p>開始のタイミングと残り時間の同期は早めに行いたい。</p> <p>ダメージの同期は、先にスキルを作ってからになりそうだ。</p> <h3 id="コメント"><a href="#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88">コメント</a></h3> <p>今のところ、<strong>順調</strong>だ。</p> <p><strong>技発動</strong>の同期は地味に2時間くらい悩んだ...。</p> QuestGames