D.N.A.のおぼえがき 2022-06-19T01:46:16+09:00 dnasoftwares Hatena::Blog hatenablog://blog/6435988827676755066 覚書:exFATのドライブにUnityのプロジェクト置いて編集しようとするとgit経由のパッケージ取得に失敗する hatenablog://entry/13574176438103506643 2022-06-19T01:46:16+09:00 2022-06-19T01:59:22+09:00 マジ覚書レベルなんですが真夜中にドハマリしたのでメモ 問題 exFATのドライブ(SDカード)にgitで取得するパッケージへの参照を含むunityのプロジェクトを置きます(問題が起きたプロジェクトはgitからcloneしたやつ) そのプロジェクトを適切なバージョンのunityで開けます インポートが走るとまもなく「fatal error : not in a git directory」というエラーとともにパッケージ取得が失敗します not in a git directory=gitリポジトリでないフォルダでgitリポジトリを操作しようとした???どゆこと? ということで死ぬほどはまっていた… <p>マジ覚書レベルなんですが真夜中にドハマリしたのでメモ</p> <div class="section"> <h4>問題</h4> <ol> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/exFAT">exFAT</a>のドライブ(SDカード)にgitで取得するパッケージへの参照を含むunityのプロジェクトを置きます(問題が起きたプロジェクトはgitからcloneしたやつ)</li> <li>そのプロジェクトを適切なバージョンのunityで開けます</li> <li>インポートが走るとまもなく「fatal error : not in a git directory」というエラーとともにパッケージ取得が失敗します</li> </ol><p>not in a git directory=git<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>でないフォルダでgit<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>を操作しようとした???どゆこと?<br /> ということで死ぬほどはまっていたんですがどうやら<strong><a class="keyword" href="http://d.hatena.ne.jp/keyword/exFAT">exFAT</a></strong>ってとこがダメだったくさい。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D1%A1%BC%A5%DF%A5%C3%A5%B7%A5%E7%A5%F3">パーミッション</a>関連で何かあるみたいだけどログが出ないので不明。<br /> 参考資料はこのへん。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F72389135%2Funity-cant-add-git-packages-on-external-ssd-fatal-not-in-a-git-directory" title="Unity cant add git packages on external SSD. FATAL: not in a git directory" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://stackoverflow.com/questions/72389135/unity-cant-add-git-packages-on-external-ssd-fatal-not-in-a-git-directory">stackoverflow.com</a></cite></p><p></p> </div> <div class="section"> <h4>対処</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/NTFS">NTFS</a>のドライブ上にプロジェクトを置く。それができないなら自分のプロファイルの.gitconfigに次のセクションと設定を追加する。</p> <pre class="code lang-gitconfig" data-lang="gitconfig" data-unlink><span class="synStatement">[safe]</span> <span class="synIdentifier">directory</span> = * </pre><p>……あんまりすぎる対処法ですが、これが嫌なら<a class="keyword" href="http://d.hatena.ne.jp/keyword/exFAT">exFAT</a>を改めて<a class="keyword" href="http://d.hatena.ne.jp/keyword/NTFS">NTFS</a>とかにするしかなさそうです。</p> </div> dnasoftwares #無人化すべきである ~ゲームを「全自動」で展示するためのまとめ hatenablog://entry/26006613804810961 2021-09-05T00:13:05+09:00 2021-09-05T00:16:19+09:00 インディ・同人ゲーム界隈も、最近はCOVID-19のせいですっかり展示会イベントが減ってしまいました。 2020年は秋のデジゲー博だけが唯一の実会場イベントという状態でしたが、2021年になってからは行政やイベント会場からイベント開催のガイドラインが出されるようになり、その範囲内でイベントをやる、という方向に進むようになってきました。(それもデルタ株の流行でまた吹き飛びそうなわけですが……)で、久しぶりにイベントがあるとこんな事案が発生するのです。リモートでゲームを展示するときは…一定時間放置されてるとタイトルに戻す処理が必要なことがわかった…(Bitsummitの現地の様子を見ながら)— お… <p>インディ・同人ゲーム界隈も、最近はCOVID-19のせいですっかり展示会イベントが減ってしまいました。<br /> 2020年は秋のデジゲー博だけが唯一の実会場イベントという状態でしたが、2021年になってからは行政やイベント会場からイベント開催の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AC%A5%A4%A5%C9%A5%E9%A5%A4%A5%F3">ガイドライン</a>が出されるようになり、その範囲内でイベントをやる、という方向に進むようになってきました。(それもデルタ株の流行でまた吹き飛びそうなわけですが……)</p><p>で、久しぶりにイベントがあるとこんな事案が発生するのです。</p><p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">リモートでゲームを展示するときは…一定時間放置されてるとタイトルに戻す処理が必要なことがわかった…(Bitsummitの現地の様子を見ながら)</p>&mdash; おかず (@pafuhana1213) <a href="https://twitter.com/pafuhana1213/status/1433273452471865347?ref_src=twsrc%5Etfw">2021年9月2日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>実は2018年のBitsummitの時期に<a class="keyword" href="http://d.hatena.ne.jp/keyword/PlatineDispositif">PlatineDispositif</a>の紫雨さんがこんな記事をあげておられました。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fmurasame.hatenablog.jp%2Fentry%2F20180520%2F1526825746" title="全自動はんなり紀行 - 緑色異星人年代記" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://murasame.hatenablog.jp/entry/20180520/1526825746">murasame.hatenablog.jp</a></cite></p><p>……歴史は繰り返すんだな、って。</p><p>ということでここ数日で再燃した「展示あるある」とその対策をツイートなどからまとめてみます。</p> <div class="section"> <h3>現地の様子</h3> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Bitsummitの一日目、おつかれさまでした!リモート参加なので現地の様子は分からないのですが、こんな感じでブースが設営されているようです!嬉しい!<a href="https://twitter.com/hashtag/BitSummit?src=hash&amp;ref_src=twsrc%5Etfw">#BitSummit</a> <a href="https://twitter.com/hashtag/%E5%8F%8C%E8%85%95%E3%81%AE%E3%82%BD%E3%83%AB%E3%83%80%E3%83%BC%E3%83%88?src=hash&amp;ref_src=twsrc%5Etfw">#双腕のソルダート</a> <a href="https://t.co/1KfDGM1GNR">pic.twitter.com/1KfDGM1GNR</a></p>&mdash; ぼっち猫🐾双腕のソルダート 開発中 (@BocchiNeko_UE) <a href="https://twitter.com/BocchiNeko_UE/status/1433405014999846914?ref_src=twsrc%5Etfw">2021年9月2日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>今年のBitsummitは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%DF%A4%E4%A4%B3%A4%E1%A4%C3%A4%BB">みやこめっせ</a>に会場こそありましたが、一般客はすべてストリーミングイベントの視聴やSteamでの体験版プレイなどで楽しんで貰う形として入場は不可、実会場はメディアとビジネス関係者限定ということになりました。さらに開催前に緊急事態宣言が発令されたことで、さらなる人数制限がかかったようで、ビジネスチケットの発売が早期にクローズされていました。</p><p>で、展示者もまた実会場とリモート参加の二択となっており、リモート参加の場合、事前にソフトを運営に送って現地ボランティアがセットアップし、当日もボランティアがアテンドを行っていたようです。ただ、つきっきりという感じではなく割と放置状態のマシンも多かったようです。</p><p>ということで放置対策をしていないと大変辛い展示になっていたわけですが……。</p><p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">放置したらPV流れるようにはしてたのですが…ぐはー!</p>&mdash; おかず (@pafuhana1213) <a href="https://twitter.com/pafuhana1213/status/1433401058521333766?ref_src=twsrc%5Etfw">2021年9月2日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>……対策をしてみてもそれを上回る放置をされてしまうのでありました。</p> </div> <div class="section"> <h3>放置の傾向と対策</h3> <p>もっとも困るのは<strong>「ゲーム途中での放置」</strong>です。ほったらかしにされると次のプレイヤーは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>も何も無い状態で続きをするしかなくなり、ゲームへの印象が猛烈に弱まります。そのため前のプレイヤーが諦めた場合はさっさとタイトルに戻っていてほしいのですが……</p><p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ソフトリセットも用意されてなくてアテンドの人がゲームオーバーまでプレイしてるとこもたまに見かける光景…。<br>ソフトリセット操作組み込んでメインループで無操作時間の検出→ソフトリセット発動の仕組みは展示時には必ず入れたいところ。 <a href="https://t.co/vphMMStMVc">https://t.co/vphMMStMVc</a></p>&mdash; あんころむらさめ (@_murasame) <a href="https://twitter.com/_murasame/status/1433278227066200066?ref_src=twsrc%5Etfw">2021年9月2日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>展示を前提にせず作業してると意外とソフトリセット機能とか実装しなかったり、後回しにしてたりするんですよね……。<br /> どのみちゲーム中断とかポーズとかはあった方が良い機能なのと、中断もポーズも後にすればするほど書くべき後始末コードが増える傾向があるので、早い内に仕込んでおくのが良いと思います。<br /> で、展示特有の機能として紫雨さんが挙げている「無操作検知でソフトリセット」も展示<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%B5%BF%CD">無人</a>化にあっては非常に大事です。<br /> 単に30秒くらい入力なしの時間を検知したらソフトリセットしてタイトルに戻せばいいだけ、ではありますが、「イベントシーンの文字送りでの放置が検知できてなかった」「ステージクリア後の放置が略」的な事案がよくあるので、デモ系も放置して大丈夫なように仕込みましょう。無入力でもイベントがちゃんと進んで終わるのが理想。</p> <ul> <li><strong>ゲーム途中での放置への対策:パッド操作を常に監視しておき、一切の入力がない状態が30秒ぐらい続いたらソフトリセットをかけてタイトルに戻す</strong></li> </ul><p>常にお客さんが並ぶほど盛況ならいいんですが、今回みたいに人がまばらだと<strong>タイトルで放置される</strong>パターンは日常茶飯事となります。<br /> タイトルで放置されてると動いてる状態の絵を見せらないわけで、はじめから遊ぶ気満々の人はともかく、ちょっとぶらぶらと流し見しているようなお客さんには見逃されてしまうかもしれません。<br /> そこで入力を記録してリプレイ……なんてのは地獄しかないのでここは動画・PVを仕込むのがよいでしょう。タイトルで放置したときにPV再生するぐらいならローコストに実装できるかと思いますが、<strong>全画面でPV流してるだけだとPVだけの展示と勘違いされて遊ばれない</strong>罠もありうるので、ちゃんとゲームプレイ可能な旨の表示をするのが良いと思います。その点双腕のソルダートは<em>タイトル画面の一部</em>にPVを表示しっぱなしにするようになっていて良い作りでした。</p> <ul> <li><strong>タイトルで放置されているときの対策:PVを用意して流せるようにする。ただし、プレイアブル台であることも主張しないと誤解される。</strong></li> </ul> </div> <div class="section"> <h3>その他、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%B5%BF%CD">無人</a>化のために必要なこと</h3> <p>後は正直紫雨さんのまとめがシッカリしてるのでみんな読んでって感じなのですがこっちでも書くと、</p> <ul> <li><strong>口頭説明をせずに済むように<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>をしっかり作る。ゲーム内ヒントも多めに。</strong>アテンドしてると間違いなく喉が1日でお亡くなりになります。このご時世だと飛沫飛ばす量も減らしたいですね。</li> <li><strong>プレイ時間を短くする・ゲーム側で管理する</strong>。イベント開催時間5時間で、1プレイ15分だと20人しか遊べません。やはり1プレイ5~10分で。できればゲーム内通しての時間制限をつけて、時間が来たら即THANK YOU FOR PLAYINGにするぐらいの仕込みがあってもいいかも。アテンドが常駐するなら<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AD%A5%C3%A5%C1%A5%F3%A5%BF%A5%A4%A5%DE%A1%BC">キッチンタイマー</a>があればいいんですが。</li> <li><strong>(多言語対応の場合)言語選択をデモの冒頭に仕込む</strong> 外国の参加者さんに対応するのが楽になります。</li> </ul><p>是非<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D9%A5%ED%A5%C3%A5%D1">デベロッパ</a>ー諸氏にはこの辺を覚えていただいて、イベントで喉枯らしたり脚ガクガクになったりせず、健康にイベントを終えられるようにして欲しいと思います。</p> </div> dnasoftwares コナミ「カードコネクト」が実はトレカのネットプリントだったという話をダシにアーケードでのオンデマンド印刷の歴史を整理しようと試みる(きっと不完全) hatenablog://entry/26006613615148982 2020-08-18T08:45:57+09:00 2020-08-18T08:51:58+09:00 先週末、突如タイムラインにオンデマンドなトレカを刷ったというツイートが流れてきました。!?なにこれは。たのしそう https://t.co/zMnGsC619e— おめが( ゜ヮ゜)ノ (@Omegamega) 2020年8月15日 見た瞬間、コナミの「カードコネクト」というカードベンダーに任意の絵柄をアップロードして印刷する機能があったことを思い出しまして、おめがさんにレスしたところみんなでいろいろ調べる流れとなり カードコネクトのWebサイトで任意の絵柄(表・裏・ホログラムのマスク、ラミネートのマスク)をアップロード可能(KONAMI IDログイン不要) 出てきたQRコードをゲームセンター… <p>先週末、突如タイムラインにオンデマンドなトレカを刷ったというツイートが流れてきました。<blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">!?なにこれは。たのしそう <a href="https://t.co/zMnGsC619e">https://t.co/zMnGsC619e</a></p>&mdash; おめが( ゜ヮ゜)ノ (@Omegamega) <a href="https://twitter.com/Omegamega/status/1294506585771765760?ref_src=twsrc%5Etfw">2020年8月15日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <br /> 見た瞬間、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>の「カードコネクト」というカードベンダーに任意の絵柄をアップロードして印刷する機能があったことを思い出しまして、おめがさんにレスしたところみんなでいろいろ調べる流れとなり</p> <ul> <li>カードコネクトのWebサイトで任意の絵柄(表・裏・ホログラムのマスク、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E9%A5%DF%A5%CD%A1%BC%A5%C8">ラミネート</a>のマスク)をアップロード可能(<a class="keyword" href="http://d.hatena.ne.jp/keyword/KONAMI">KONAMI</a> IDログイン不要)</li> <li>出てきた<a class="keyword" href="http://d.hatena.ne.jp/keyword/QR%A5%B3%A1%BC%A5%C9">QRコード</a>をゲームセンターにある「カードコネクト」に読ませると印刷可能</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/QR%A5%B3%A1%BC%A5%C9">QRコード</a>は所有者制限などはなく、画像としてシェアすれば誰でも「カードコネクト」で印刷できる</li> <li>唯一有効期限が14日と設定されているほか、当然だが他人の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%A2%CD%F8%CA%AA">権利物</a>は登録禁止</li> <li>印刷代はホロなし100円、ホロありで300円(現金 or PASELI払い)</li> </ul><p>とまあ、結構至れり尽くせりの仕様で、いわゆる「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CD%A5%C3%A5%C8%A5%D7%A5%EA%A5%F3%A5%C8">ネットプリント</a>」的な使い方ができると盛り上がりました。</p><p>で、その中でこういう「ゲームから独立したカードベンダー」の歴史的話題となりまして<blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://t.co/HWFDVqSHo3">https://t.co/HWFDVqSHo3</a><a href="https://t.co/BYmobdlFdC">https://t.co/BYmobdlFdC</a><br>カードコネクト、去年リリースで1年ぐらい経ってるっぽい?<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>の艦これACカード排出システムを独立&amp;対抗させたやつかなーと思ったら、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>は更に一年前2018年に同種のカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーをリリースしてた</p>&mdash; おめが( ゜ヮ゜)ノ (@Omegamega) <a href="https://twitter.com/Omegamega/status/1294642059102437378?ref_src=twsrc%5Etfw">2020年8月15日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>なんかの縁だろうしということでちょっと「トレカのオンデマンド印刷」の歴史を調べてみることにしました。<br /> 一応タイムラインには雑に書いていましたが、改めて。</p> <div class="section"> <h4>前史:印刷済みトレカを使う<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%B1%A1%BC%A5%C9%A5%B2%A1%BC%A5%E0">アーケードゲーム</a></h4> <p>リアルな<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%EC%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0%A5%AB%A1%BC%A5%C9">トレーディングカード</a>を使うゲーム自体はかなり前から存在しています。<br /> ジャンルの先駆けは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>の「<a class="keyword" href="http://d.hatena.ne.jp/keyword/WORLD%20CLUB%20Champion%20Football">WORLD CLUB Champion Football</a>」(2002年)かと思います。11枚の選手カードをフィールドに直接置いてフォーメーションを指示するなど直感的UIは革新的なもので、このシリーズは2020年のいまでも「<a class="keyword" href="http://d.hatena.ne.jp/keyword/WCCF">WCCF</a> FOOTISTA」として継続しています。<br /> その後も<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>は継続的に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%EC%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0%A5%AB%A1%BC%A5%C9">トレーディングカード</a>を併用する<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%B1%A1%BC%A5%C9%A5%B2%A1%BC%A5%E0">アーケードゲーム</a>をリリースします。キッズ向け初の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%EC%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0%A5%AB%A1%BC%A5%C9%A5%B2%A1%BC%A5%E0">トレーディングカードゲーム</a>にして、長期ヒットを生んだ「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B9%C3%C3%EE%B2%A6%BC%D4%A5%E0%A5%B7%A5%AD%A5%F3%A5%B0">甲虫王者ムシキング</a>」(2003年1月)や、ファンタ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A1%BC">ジー</a>寄りの世界観と乱入対戦をより全面に押し出すような作りにした「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%F4%A5%A1%A5%ED%A5%F3%A4%CE%B8%B0">アヴァロンの鍵</a>」(2003年2月<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%ED%A5%B1%A5%C6">ロケテ</a>、7月稼働)がそれぞれヒットします。ただ「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%F4%A5%A1%A5%ED%A5%F3%A4%CE%B8%B0">アヴァロンの鍵</a>」の方はゲーム性が大概<b><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>しく</b>(強制的に対人戦になるうえダメージを受けると直接プレイ時間が縮む。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BD%E9%BF%B4%BC%D4%BC%ED%A4%EA">初心者狩り</a>もあった)、当初はウケたものの長持ちはしなかったと思います。<br /> その後、ダンジョン<a class="keyword" href="http://d.hatena.ne.jp/keyword/RPG">RPG</a>「<a class="keyword" href="http://d.hatena.ne.jp/keyword/Quest%20of%20D">Quest of D</a>」(2004年)ではアイテムを「Dフォースカード」としてトレカ化する試みが行われましたが、デッキのプレイヤー間使い回し、レアカードの払い出し法則が知られたことによる一部ユーザーの張り付き連コ、及び運営の対応のまずさや一部カードの価値が極端に高まる<b>難易度設定の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>しさ</b>でプレイヤー離れを引き起こしたとされています。</p><p>2005年には<a class="keyword" href="http://d.hatena.ne.jp/keyword/WCCF">WCCF</a>のカード配置を発展させ、カードを直接動かして操作する「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BB%B0%B9%F1%BB%D6%C2%E7%C0%EF">三国志大戦</a>」がデビューします。(2005年3月稼働)これも長期ヒットを飛ばし、途中「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%EF%B9%F1%C2%E7%C0%EF">戦国大戦</a>」(2010年11月稼働、2017年2月サービス終了)として新シリーズに展開したりしながら継続しています。また、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A5%F3%A5%D7%A5%EC%A5%B9%A5%C8">バンプレスト</a>「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B5%A1%C6%B0%C0%EF%BB%CE%A5%AC%A5%F3%A5%C0%A5%E00079%A5%AB%A1%BC%A5%C9%A5%D3%A5%EB%A5%C0%A1%BC">機動戦士ガンダム0079カードビルダー</a>」(2004年12月)、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%A6%A5%A7%A5%A2%A1%A6%A5%A8%A5%CB%A5%C3%A5%AF%A5%B9">スクウェア・エニックス</a>の「ロード・オブ・<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%F4%A5%A1%A1%BC%A5%DF%A5%EA%A5%AA%A5%F3">ヴァーミリオン</a>」(2008年6月~2019年8月サービス終了)など、似たUIを持ついくつかのフォロワーも登場しました。</p><p>いずれのゲームでもカードを巡ってのトラブルは少なからず存在しており、「レア抜き」が疑われる事例や特定カードを目当てとした「掘り」などもあったといいます。</p> </div> <div class="section"> <h4>オンデマンド印刷へ</h4> <p>ゲームセンターでのオンデマンド印刷といえばアトラスの「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%EA%A5%F3%A5%C8%B6%E6%B3%DA%C9%F4">プリント倶楽部</a>」(1995年)で既に行われていたわけですが、印画紙印刷を<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%EC%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0%A5%AB%A1%BC%A5%C9">トレーディングカード</a>に応用するア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%C7%A5%A2">イデア</a>が実際に世に出たのは2012年<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>「モンスター烈伝 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%EC%A5%AB%A5%D0%A5%C8%A5%EB">オレカバトル</a>」が最初のようです(2012年3月)。QRコードを印刷し、それを筐体で読むことでカード上のキャラをゲームに登場させられるという仕組みでした。この時点で既に<b>カードとプレイヤーの紐付け</b>がある程度行われており、他人が印刷したカードは「かりモン(借り物+モンスター)」と呼ばれ、使用制限が発生するようになっていました。カード自体の印刷品質は既に十分高かったようですが、紙質はちょっと薄めで、また元がロール紙なので払い出しの時点でちょっと反っていました。後、2015年にはSOUND VOLTEX IIIに「SOUND VOLTEX GENERATOR -REAL MODEL-」というカードプリンタが追加され(2015年3月11日)、ゲーム内の楽曲ジャケットやキャラカードが印刷できるようになりました。中には「PUR」というレアリティ付きのものがあり、これを引き当てるとゲーム内のナビゲーター「ネメシスクルー」を変更できるという趣向がありました。なお、SDVXのカード用紙は当初<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%EC%A5%AB%A5%D0%A5%C8%A5%EB">オレカバトル</a>と共有しており、裏面の柄が<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%EC%A5%AB%A5%D0%A5%C8%A5%EB">オレカバトル</a>のままのカードが出ていたそうです。(2016年頭から<a class="keyword" href="http://d.hatena.ne.jp/keyword/KONAMI">KONAMI</a>ロゴのみの用紙が出回り始めた模様)</p><p>一方<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>も同じ2015年6月、既に稼働中だった「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BD%E9%B2%BB%A5%DF%A5%AF">初音ミク</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/Project%20DIVA">Project DIVA</a> Arcade Future Tone」に「フォトスタジオ」として<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B0%F5%BA%FE%B5%A1">印刷機</a>能を追加。こちらは台紙が「シール」になっており、レイアウトを変えることで複数サイズのシールを一気に印刷できるようになっているそうです。PV鑑賞中に複数枚<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A1%BC%A5%F3%A5%B7%A5%E7%A5%C3%A5%C8">スクリーンショット</a>を取ることができたので、好きな写真を複数並べてまとめてシール化できるようになっています。また、ゲーム内のアイコンやロゴを用いたデコレーションも可能とのこと。このあたりには「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%EA%A5%F3%A5%C8%B6%E6%B3%DA%C9%F4">プリント倶楽部</a>」の息吹が感じられます。</p><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>のオンデマンドトレカ印刷としては2016年の「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B4%CF%A4%B3%A4%EC%A5%A2%A1%BC%A5%B1%A1%BC%A5%C9">艦これアーケード</a>」(2016年4月稼働)がその発端でしょうか。印刷のベースとなるカード自体が<a class="keyword" href="http://d.hatena.ne.jp/keyword/IC%A5%AB%A1%BC%A5%C9">ICカード</a>になっており、反りの無い高品質な出力、さらにホロの印刷にはじめて対応しました。また他人のカードであっても使用制限が緩いようで、カードショップでの中古市場ができあがり、いわゆる<a class="keyword" href="http://d.hatena.ne.jp/keyword/SSR">SSR</a>的立ち位置である「中破ホロ」(キャラ絵が中破絵のカード)はそれはもう高値での取引がされているとかなんとか。</p><p>同じ2016年12月には「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BB%B0%B9%F1%BB%D6%C2%E7%C0%EF">三国志大戦</a>」の第2期が稼働開始。こちらのオンデマンドは両面印刷に対応しており、これまでの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BB%B0%B9%F1%BB%D6%C2%E7%C0%EF">三国志大戦</a>同様に片面にイラスト、もう片面で各種情報の記載がなされるようになっています。こちらの使用制限は比較的厳格で、筐体でのトレード機能の範囲でしかトレードもできず不評を買っていたようです。後に改善され、今はトレードの健全さと自由度がある程度両立されているようです。また、ICチップでは無く特殊インクによるコード印刷が使われているらしく、どうしてもカードを擦る事の多い<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BB%B0%B9%F1%BB%D6%C2%E7%C0%EF">三国志大戦</a>においてはカードの耐久性が問題視されています。対処としてはスリーブを装着するしかないのですが、万が一に備えカードの再印刷が機能として備え付けられています。</p> </div> <div class="section"> <h4>独立ベンダー化</h4> <div class="section"> <h5><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>のカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ー</h5> <p>2018年7月に「maimai」「CHUNITHM」に続く<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BB%A5%B2%A1%BC">音ゲー</a>「オンゲキ」が稼働開始。同時に「カードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ー」という独立したカード販売機が稼働を開始します。<br /> カードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーの当時の機能としては、</p> <ul> <li>オンゲキで入手したデジタルカードを実際に印刷して入手する「カードプリント」</li> <li>オンゲキで使用できるカードをガチャって印刷する「ガ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E3%A5%D7%A5%EA%A5%F3">チャプリン</a>ト」</li> </ul><p>の2つで、あくまでもオンゲキの補助という立ち位置のものでした。<br /> オンゲキはゲーム中に入手したカードでデッキを構築し、そのデッキの「攻撃力」とプレイ精度で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BB%A5%B2%A1%BC">音ゲー</a>のスコアが決まるシステムです。(デッキに関係ない精度のみで決まるスコアも別途記録されます)<br /> で、ゲーム内で入手したカードをカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーで「印刷」するとレベル上限が解放されデッキ強化が促進されるというシステムになっています。(オンゲキPLUS以後はゲーム内アイテムでもレベル解放可能)<br /> ところが、初代オンゲキは本当にカードを印刷する以外のカード強化の手段がなく、カードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーに100円を払わない限りデッキ強化がすぐ頭打ちして解禁が止まるため、補助的な立ち位置に見えて実際は使用必須同然の作りになっていました。<br /> 「ガ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E3%A5%D7%A5%EA%A5%F3">チャプリン</a>ト」も初代オンゲキでは曲者で、正直ガチャ引かないと後半の解禁、というか<b><a href="https://dic.nicovideo.jp/a/titania">Titania</a>で勝つ</b>戦力作るのが大変という事がありまして……(実際はゲーム中で入手できるカードやイベントの報酬を集めればある程度戦力はまとまるが、イベントを走るコストやカード入手までのプレイ曲数考えたらガチャに走った方がマシという可能性があった)初代オンゲキはまあ大概<b><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>しい</b>ゲームでした。「オンゲキPLUS」「~SUMMER」と急激に改善されて今では相当楽になってます。</p><p>後、2018年10月からは「CHUNITHM <a class="keyword" href="http://d.hatena.ne.jp/keyword/AMAZON">AMAZON</a>」がカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーの対応機種に追加されました。こちらはガチャがメインとなっており、</p> <ul> <li>ゲーム本編で入手不可になったキャラをガチャで引く</li> <li>あるいはガチャ限定のレアキャラを引き当てる</li> </ul><p>という趣向になっています。カードを引いた時点で<a class="keyword" href="http://d.hatena.ne.jp/keyword/aime">aime</a>アカウントで関連付くCHUNITHMのゲームデータに保存され、<a class="keyword" href="http://d.hatena.ne.jp/keyword/QR">QR</a>の読み取り等もなくゲームで使用可能になります。<br /> もう一つ特徴的なのは印刷されたカード自体を使って遊ぶ「<a href="https://chunithm.sega.jp/play/taisen/">&#x30C1;&#x30E5;&#x30A6;&#x30CB;&#x30BA;&#x30E0;&#x5927;&#x6226;</a>」という遊び方の提案がなされているところにあります。</p><p>2019年7月には「maimai でらっくす」が稼働しカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーと連動を開始。こちらでは解禁促進や上位難易度のプレイ制限撤廃などの効果がある「でらっくすパス」を購入する形になっており、カードの印刷=パスの購入となります。特にガチャ要素はありません。</p> </div> <div class="section"> <h5><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>のカードコネクト</h5> <p>で、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>の独立型カード販売機である「カードコネクト」ですが、2019年12月23日に稼働開始します。<br /> カードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーの後追いという形での稼働ではありましたが、両面をオンデマンド印刷可能とスペックもカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーより上、ついでに対応機種数も圧倒的で</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/BEMANI">BEMANI</a>現行機種一通り</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%A4%A5%BA%A5%DE%A5%B8%A5%C3%A5%AF%A5%A2%A5%AB%A5%C7%A5%DF%A1%BC">クイズマジックアカデミー</a></li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%CB%E3%BF%FD%B3%CA%C6%AE%B6%E6%B3%DA%C9%F4">麻雀格闘倶楽部</a></li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DC%A5%F3%A5%D0%A1%BC%A5%AC%A1%BC">ボンバーガー</a>ル</li> <li>エルド<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ラウン(メダルゲー)</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%D2%A4%CA%A5%D3%A5%BF%A2%F6">ひなビタ♪</a>、ダンキラ</li> </ul><p>と、あらかたの現行<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>コンテンツは大概対応しています。<br /> ただ、対応具合はまちまち、というかそもそもゲームと連携していないヤツも多く、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DD%A5%C3%A5%D7%A5%F3">ポップン</a>、<a class="keyword" href="http://d.hatena.ne.jp/keyword/DDR">DDR</a>、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DC%A5%F3%A5%D0%A1%BC%A5%AC%A1%BC">ボンバーガー</a>ル、エルド<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ラウンやキャラコンテンツの場合は事実上ただのカードダスです。<br /> SOUND VOLTEXのガチャについてはゲーム機でのオンデマンド同様「PUR」を引けばネメシスクルーが解禁されます。それ以外の場合はアピールカードが解禁されます。</p><p>珍しいのは「プロフィールリント」と「メモリアルプリント」で、ゲーム機のプレイデータを元に名刺のようにプロフィールを印刷したり、なんらかのメモリアルを達成した記念のカードを印刷することができます。<br /> プロフィールの方は名刺サイズなこともあるので交流会での名刺交換に使うという手もありそうです。(量産するには高いけど)<br /> メモリアルについてはまだ「<a class="keyword" href="http://d.hatena.ne.jp/keyword/beatmaniaIIDX">beatmaniaIIDX</a>の段位突破記念」と「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CB%E3%BF%FD%B3%CA%C6%AE%B6%E6%B3%DA%C9%F4">麻雀格闘倶楽部</a>のプロ本人マッチング記念(またはプロ勝利記念)」しかありませんが、一種のリアル記念品になるので面白いと思います。</p><p>そんなわけで稼働当初は新機軸はあれど、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%AC">セガ</a>のカードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ーの後追いという印象は拭えず微妙なところだったのですが、2020年3月25日になって突然「<b><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%EA%A5%B8%A5%CA%A5%EB%A5%D7%A5%EA%A5%F3%A5%C8">オリジナルプリント</a></b>」がリリースされ、「トレカの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CD%A5%C3%A5%C8%A5%D7%A5%EA%A5%F3%A5%C8">ネットプリント</a>」という新たな役割が立ち上がったのでした。</p> </div> </div> <div class="section"> <h3>まとめ</h3> <p>2002年ぐらいから始まったリアルトレカを用いるアーケード<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%B8%A5%BF%A5%EB%A5%B2%A1%BC%A5%E0">デジタルゲーム</a>の流れはロングランシリーズを複数生む大ヒットになりましたが、リ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%EB%A5%AB%A1%BC%A5%C9">アルカード</a>であるがゆえにゲームセンターでのトラブルが絶えず、カードのオンデマンド化は強力なトラブル解決策となりました。<br /> 2012年に業界初のオンデマンド印刷採用<a class="keyword" href="http://d.hatena.ne.jp/keyword/TCG">TCG</a>が登場。最初はフォトプリンタをベースとしていたようですが、後にトレカサイズの専用用紙や、ICチップを埋め込んだカードが作れるようになり、急激にクォリティも向上、オンデマンド印刷のカードゲームも一般化しました。<br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BB%A5%B2%A1%BC">音ゲー</a>とリアル<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%EC%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0%A5%AB%A1%BC%A5%C9">トレーディングカード</a>の融合を試みた「オンゲキ」&「カードメ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%AB">イカ</a>ー」はカード生成が必須すぎる仕様が不評で軌道修正。そこに後追いとなった<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>はゲームのプロフィールやメモリアル要素を前面に出した「カードコネクト」を展開、さらにユーザーによる完全データ入稿さえ受け入れる「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%EA%A5%B8%A5%CA%A5%EB%A5%D7%A5%EA%A5%F3%A5%C8">オリジナルプリント</a>」を開始して大きく差別化しています。<a class="keyword" href="http://d.hatena.ne.jp/keyword/BEMANI">BEMANI</a>他<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%CA%A5%DF">コナミ</a>製品のあるゲーセンなら高確率でカードコネクトがあるという、意外と高い普及度も見逃せない可能性があります。<br /> 今後はどうなるんでしょうか、ベンダーが独立して存在することで、ゲーム筐体への追加装置を入れずともカード絡みの追加コンテンツを用意出来るわけですが、まあ、最後にはガチャなんですかねやっぱし……ただメモリアルプリントなどガチャだけでない使い方が模索されてるのはいいことだと思います。</p> </div> dnasoftwares 『エア搬入T.T.』のPV撮影におけるCinemacineとUnity Recorderについて hatenablog://entry/26006613569091145 2020-05-20T16:50:22+09:00 2020-05-20T16:54:02+09:00 GWの「エアコミケ」期間中にunityで『エア搬入TT』というレースゲーを作ったことを前回の記事でお話いたしました。 dnasoftwares.hatenablog.com 今週は「博麗神社例大祭」中止に伴う「エア例大祭」をやるぞー、ということでツインメッセとか?w— ほうじょうたんさま@次は… (@HojoTansama) 2020年5月4日 大ボスからネタ(と資料)を振られてしまったので作っておりました。紆余曲折七転八倒しましたが、当初の例大祭の設営搬入をやっているぐらいの時間帯に新バージョンを公開できました。 www.dna-softwares.com……なぜかunityroomで起動し… <p>GWの「エア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>」期間中にunityで『エア搬入TT』というレースゲーを作ったことを前回の記事でお話いたしました。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdnasoftwares.hatenablog.com%2Fentry%2F2020%2F05%2F06%2F171620" title="エアコミケの空気にあてられて一人ゲームジャムを敢行した話 - D.N.A.のおぼえがき" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://dnasoftwares.hatenablog.com/entry/2020/05/06/171620">dnasoftwares.hatenablog.com</a></cite><br /> 今週は「<a href="https://www.reitaisai.com/">&#x535A;&#x9E97;&#x795E;&#x793E;&#x4F8B;&#x5927;&#x796D;</a>」中止に伴う「エア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CE%E3%C2%E7%BA%D7">例大祭</a>」をやるぞー、ということで<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ツインメッセとか?w</p>&mdash; ほうじょうたんさま@次は… (@HojoTansama) <a href="https://twitter.com/HojoTansama/status/1257252938105290753?ref_src=twsrc%5Etfw">2020年5月4日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <br /> 大ボスからネタ(と資料)を振られてしまったので作っておりました。</p><p>紆余曲折<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%B7%C5%BE%C8%AC%C5%DD">七転八倒</a>しましたが、当初の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CE%E3%C2%E7%BA%D7">例大祭</a>の設営搬入をやっているぐらいの時間帯に新バージョンを公開できました。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.dna-softwares.com%2Fu%2Frts%2F" title="Unity WebGL Player | 【エア例大祭】エア搬入T.T." class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.dna-softwares.com/u/rts/">www.dna-softwares.com</a></cite></p><p>……なぜかunityroomで起動しないバイナリになってしまったので、自分のサークルのサイト上での公開ですが。<br /> 今度naichiさんに聞かなきゃ……</p> <div class="section"> <h3>Unity上でカッコイイ映像素材をつくる</h3> <p>ゲームが完成したらやはりPVを作りたいのです。というよりPV作らないと今のご時世、拡散されない可能性が高いです。<br /> せっかくレースゲーなんで、理想は高くこんな感じで。</p><p><iframe width="480" height="270" src="https://www.youtube.com/embed/fzHtV3Qc-IE?start=92&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/fzHtV3Qc-IE?t=92">youtu.be</a></cite></p><p>カメラワークの参考も兼ねて最近ずっと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D9%A5%B9%A5%C8%A5%E2%A1%BC%A5%BF%A5%EA%A5%F3%A5%B0">ベストモータリング</a>/ホットバージョンの動画ばっかり見てるんですよ、テレビ流し見感覚で見られるので良い。</p><p>幸いUnityには良い感じのカメラワークを作ってくれるパッケージと動画を撮るパッケージのそれぞれが公式で供給されています。それが表題のCinemacineとUnity Recorderです。</p> <div class="section"> <h4>Cinemachine</h4> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Funity.com%2Fja%2Funity%2Ffeatures%2Feditor%2Fart-and-design%2Fcinemachine" title="Cinemachine | Unity" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://unity.com/ja/unity/features/editor/art-and-design/cinemachine">unity.com</a></cite><br /> プロシージャルに特定の物体を追うカメラワークを作れる便利なパッケージですが、カメラをスイッチングさせるのはどうもTimelineあたりを使わせる前提のようです。<br /> ClearShotを使えば「被写体が障害物で隠れたら別アングルにする」という処理は自動化できますが、イベント会場ってそんなに障害物があるわけじゃないので、一つのカメラに固定されがちになる可能性が高くあまり期待できません。<br /> そこで、今回は自前でカメラをアクティベートするスイッチを作ります。</p> </div> <div class="section"> <h4>Unity Recorder</h4> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.unity3d.com%2FPackages%2Fcom.unity.recorder%402.2%2Fmanual%2Findex.html" title="Unity Recorder User Manual | Unity Recorder | 2.2.0-preview.4" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://docs.unity3d.com/Packages/com.unity.recorder@2.2/manual/index.html">docs.unity3d.com</a></cite><br /> Gameビューを録画するだけでなく<b>任意のレンダーテクスチャをキャプチャ</b>できるのが大きい。今回はこの機能に着目しました。</p><p>これら2点を組み合わせて、<b>普通の通しプレイをCinemacineで追い、ゲームの裏でキャプチャする</b>という仕組みをこさえることとしました。</p> </div> <div class="section"> <h4>プランニング</h4> <p>まずは録画に使うシーンを複製して新しいシーンをこさえます。色々付け足すので、ゲーム本体とは分離しましょう。</p><p>カメラを置く場所を固定しなければならないので、事前に走行ルートを決めてしまいます。ルートを決めたらチェックポイントやカーブの走行ラインを想定してVirtualCameraを配置。</p><p><figure class="figure-image figure-image-fotolife" title="緑線→想定ルート 白矢印→チェックポイント カメラと歯車のアイコン→CinemacineVirtualCameraの設置場所"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200517/20200517002248.png" alt="f:id:dnasoftwares:20200517002248p:plain" title="f:id:dnasoftwares:20200517002248p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>緑線→想定ルート 白矢印→チェックポイント 赤丸数字→通過順 カメラと歯車のアイコン→CinemacineVirtualCameraの設置場所</figcaption></figure></p><p>あとはこれを車両が近づいたときアクティベートしていけば、それっぽいカメラワークができるはず、というわけです。</p> </div> <div class="section"> <h4>スイッチの準備</h4> <p>指定オブジェクトが特定の範囲に入る、はTriggerを使えば実現できそうです。こんなコードを用意しました。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synType">public</span> <span class="synType">class</span> VCamActivationBoundary : MonoBehaviour { [SerializeField] <span class="synType">private</span> Cinemachine.CinemachineVirtualCamera vCamTocativate; <span class="synComment">// この判定でON/OFFするVirtualCameraの参照</span> [SerializeField] <span class="synType">private</span> <span class="synType">bool</span> activateOnlyOnce; <span class="synComment">// 一度きり?(falseだと何度出入りしてもスイッチングする。trueの場合は最初の一度だけ)</span> <span class="synType">private</span> <span class="synType">bool</span> _activated; <span class="synComment">// 一度スイッチングした?</span> <span class="synType">private</span> <span class="synType">void</span> Start() { vCamTocativate.enabled = <span class="synConstant">false</span>; <span class="synComment">// 初期状態はカメラOFFから</span> } <span class="synType">private</span> <span class="synType">void</span> OnTriggerEnter(Collider other) { <span class="synStatement">if</span> (other.gameObject.layer!=LayerNames.Vehicle &amp;&amp; other.gameObject.layer!=LayerNames.VehicleBody ) <span class="synStatement">return</span>; <span class="synComment">// ゲーム固有処理。自車両のオブジェクト以外がトリガーに触れてもなにもしない</span> <span class="synStatement">if</span> (activateOnlyOnce &amp;&amp; _activated) <span class="synStatement">return</span>; <span class="synComment">// 一度きり反応の設定がONで既に一度スイッチングしてるならなにもしない</span> vCamTocativate.enabled = <span class="synConstant">true</span>; <span class="synComment">// VirtualCamera有効</span> _activated = <span class="synConstant">true</span>; <span class="synComment">// 一度スイッチングした</span> Debug.Log(vCamTocativate.name+<span class="synConstant">&quot; Activated&quot;</span>); } <span class="synType">private</span> <span class="synType">void</span> OnTriggerExit(Collider other) { <span class="synStatement">if</span> (other.gameObject.layer!=LayerNames.Vehicle &amp;&amp; other.gameObject.layer!=LayerNames.VehicleBody) <span class="synStatement">return</span>; vCamTocativate.enabled = <span class="synConstant">false</span>; <span class="synComment">// VirtualCamera無効</span> Debug.Log(vCamTocativate.name+<span class="synConstant">&quot; Deactivated&quot;</span>); } } </pre><p>これで、VirtualCameraがTriggerに入ったらON、出たらOFFの動作が実装できました。<br /> 実際の走行ルートを考えてカメラのスイッチング範囲を設定し、カメラをリンクさせていきます。いずれのカメラ範囲にも入っていない場合は自車両視点のカメラを適当に選んで貰うようにします。これにはClearShotが好適でしょう。<br /> 車両手前からルーズに捉えるビュー、車の真上から車両周辺を広く捉えるビュー、一人称視点を用意し、ランダムで選ばせます。プライオリティを最低にして常駐させておけば、他のカメラが全てOFFのときは自動的に車両追尾になるというわけです。<br /> 残りのVirtualCameraは適当にプライオリティを設定しますが、スイッチング範囲が重なったとき、基本的には古いカメラよりは新しいカメラの視点の方が優先されるべきだろうと思いますので、スイッチングの順番にVirtualCameraのプライオリティを上げていけば良いと思います。(最初のカメラ=最低、でそれから順番に1ずつ足す)</p> </div> <div class="section"> <h4>動作テストとキャプチャ</h4> <p>さて実装したらテストをしなければなりませんが、ゲームを遊びつつCinemacineの生成結果をプレビューする必要があるわけです。流石にCinemacineの画面だけ見てプレイするのはただのラジコン操作以上の難しさなので、ここではゲーム画面は元のまま残しつつ、Cinemacineの出力を小さくプレビューさせようと思います。<br /> といってもやることは単純で</p> <ul> <li>出力したい動画の縦横と同じサイズのレンダーテクスチャアセットを新規作成</li> <li>Cinemacineで制御する録画用カメラの出力先をレンダーテクスチャにする</li> <li>そのレンダーテクスチャをUI Raw ImageでゲームUIと一緒に表示する</li> </ul><p>だけです。Unity Recorderではレンダーテクスチャをキャプチャ(録画)対象にできるので、これでUnity Recorderの準備も一緒にできたようなものです。<br /> 操作方法に従い適当に録画設定を作成。「Capture」を「Render Texture Asset」にして、先ほどのレンダーテクスチャを指定。あとはSTART RECORDINGを押してゲームを遊べば外部視点が勝手に録画されるという寸法です。<br /> 録画を始めずに単に実行すればテストになりますので、カメラワークや反応範囲を調整して、出来たと思ったら録画ONにして通しプレイ。事故や曲がるところの間違いに気をつけつつ走りきったらあとは適当な編集ツールで好きにしましょう。</p><p>実際にカメラが動いている様子をエディタビューごとキャプチャしてみました。スイッチング範囲も可視化されているのでカメラの切り替わりがわかりやすいかなと思います。<br /> <iframe width="560" height="315" frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/cqH9Ds8QnU8"></iframe><br><a href="https://youtube.com/watch?v=cqH9Ds8QnU8">エア搬入T.T. PV(?)メイキング</a><br /> </p> </div> <div class="section"> <h4>まとめ</h4> <p>この方策、比較的元のゲームに手を付けずに実装できるのでとっかかりは楽です。ただ、如何せん毎回手操作が必要なので全く同じ動作が再現できないのが厳しいところ。<br /> マジメにやるならリプレイシステムの実装も必要ですが、UnityでRigidbody絡んで可変<a class="keyword" href="http://d.hatena.ne.jp/keyword/FPS">FPS</a>でリプレイとか地獄しか見えませんね……<br /> ゲームのジャンルによりますが、簡易なPVであればこういった手法で素材映像を作るのも悪くないのではと思います。<br /> ただ、Unity Recorderがちょっと重いらしく、音声を同時キャプチャすると高確率で動画とズレるのが大問題。PVで効果音がほとんど入ってないのはそこが理由です。(そもそもキャプチャの段階で音声を録音していない)<br /> この辺はもうちょい正確な方法を探しています。</p><p>記事中ではエア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>用の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>会場を模した東京コースを使いましたが、同じ手法で静岡コースにカメラを配して作った新しいPVではさらに事後の編集も多めにしています。<br /> <iframe width="560" height="315" frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/vq7oVuuLVys"></iframe><br><a href="https://youtube.com/watch?v=vq7oVuuLVys">【エア例大祭】エア搬入T.T. 静岡コースPV</a><br /> </p> <ul> <li>ちょっと早回し(×1.1~×1.2)にして間延びを抑える</li> <li>長距離移動シーンはカッコイイ所だけ残す</li> <li>スピード感あるシーンが欲しかったのでチェックポイントを一部無視</li> <li>普通のゲームプレイも録画して一部に混ぜる</li> </ul><p>最初のPVよりはまあまあメリハリある動画になったかなと思います。</p><p>というわけで課題はあるものの動画素材をそこそこ早く用意するにはいい手じゃないかと思っています。<br /> 3DゲームのPV作成で悩んだらCinemacine+UnityRecorderでコンピュータ任せのカメラワーク、それなりにお勧めです。</p> </div> </div> dnasoftwares エアコミケの空気にあてられて一人ゲームジャムを敢行した話 hatenablog://entry/26006613562499186 2020-05-06T17:16:20+09:00 2020-05-06T17:22:50+09:00 最近は麻雀のバージョンアップを続けておりましたが、このSTAY HOME週間は唐突にこんなゲームを作っておりました。 【エアコミケ98】『エア搬入T.T.』プロモーション映像 ゲームはこっちから遊べます。 unityroom.com連休ラストでもありますし、以前の1週間ゲームジャムのときのように振り返りをしてみようと思います。 きっかけ 耳にタコが出来るほどSTAY HOMEを呼びかけられて何かとテンションも下がりがちな連休()前・4月最終日の木曜深夜、こんな2つのツイートが流れてきました。ということで、本当は誰もいませんし、電気も付いてないと思いますが、これ以後、その手のツッコミはナシの方向… <p>最近は<a href="https://thmj.dna-softwares.com/">&#x9EBB;&#x96C0;</a>のバージョンアップを続けておりましたが、このSTAY HOME週間は唐突にこんなゲームを作っておりました。<br /> <iframe width="560" height="315" frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/sBS-D7igIiI"></iframe><br><a href="https://youtube.com/watch?v=sBS-D7igIiI">【エアコミケ98】『エア搬入T.T.』プロモーション映像</a><br /> ゲームはこっちから遊べます。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Funityroom.com%2Fgames%2Fdnas_aircarry" title="【エアコミケ】エア搬入T.T. | フリーゲーム投稿サイト unityroom" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://unityroom.com/games/dnas_aircarry">unityroom.com</a></cite></p><p>連休ラストでもありますし、以前の1週間ゲームジャムのときのように振り返りをしてみようと思います。</p> <div class="section"> <h3 id="きっかけ">きっかけ</h3> <p>耳にタコが出来るほどSTAY HOMEを呼びかけられて何かとテンションも下がりがちな連休()前・4月最終日の木曜深夜、こんな2つのツイートが流れてきました。<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ということで、本当は誰もいませんし、電気も付いてないと思いますが、これ以後、その手のツッコミはナシの方向で……お願いします。青海展示棟のシャッターが開きました。企業ブースは設営前日夜から前倒しで設営が始まります。脳内でお好きなBGMを鳴らしてご覧下さい。<a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a> <a href="https://t.co/gWgeb83TRN">pic.twitter.com/gWgeb83TRN</a></p>&mdash; C98は中止★<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%C3%A5%AF%A5%DE%A1%BC%A5%B1%A5%C3%A5%C8">コミックマーケット</a>準備会 (@comiketofficial) <a href="https://twitter.com/comiketofficial/status/1255871250146025472?ref_src=twsrc%5Etfw">2020年4月30日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">最近オイラよくやるんですが、Zoom会議やZoom飲み会をやる時に、背後の同人誌在庫段ボールの山を見せると、ウケが狙えるので、お勧めだゾ。(涙目) <a href="https://t.co/1UZ0wTyFrc">pic.twitter.com/1UZ0wTyFrc</a></p>&mdash; 土田善紀@再び金欠へ (@tsuchidasama) <a href="https://twitter.com/tsuchidasama/status/1255883783586234368?ref_src=twsrc%5Etfw">2020年4月30日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>このツイートと動画と在庫の山を見ててふと、14年前の夏コミ(C70)で頒布したゲームのことを思い出しまして……<figure class="figure-image figure-image-fotolife" title="まわるめいどさんをねみぎ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20060718/20060718083754.jpg" alt="f:id:dnasoftwares:20060718083754j:plain" title="f:id:dnasoftwares:20060718083754j:plain" class="hatena-fotolife" itemprop="image"></span><figcaption><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%DE%A4%EF%A4%EB%A4%E1%A4%A4%A4%C9%A4%B5%A4%F3%A4%F2%A4%CD%A4%DF%A4%AE">まわるめいどさんをねみぎ</a></figcaption></figure><iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fnemigi.dna-softwares.com%2F" title="まわるめいどさんをねみぎ" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://nemigi.dna-softwares.com/">nemigi.dna-softwares.com</a></cite></p><p>ゲーム本体はまだおうちのPCにあるんですが残念ながら起動しなくなってました。さておきこのゲーム内の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DF%A5%CB%A5%B2%A1%BC%A5%E0">ミニゲーム</a>で<b>「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>の島中や壁を模したコースを走って、指定したスペースに停める」</b>というものがありました。<br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DF%A5%CB%A5%B2%A1%BC%A5%E0">ミニゲーム</a>なのでカーブ一つ曲がってブレーキ程度のものだったんですが、「今だったらこれ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>会場フル再現できるのでは?」とふと閃きます。</p><p>当初の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>の日程は<br /> </p> <ul> <li>5/1 前日設営</li> <li>5/2 初日</li> <li>5/3 2日目</li> <li>5/4 3日目 (前日搬入はこの日が最後)</li> <li>5/5 最終日</li> </ul><p>ということで、エア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>はこの日程で開催している体でアレコレツイートするから、ついでに他のユーザーも参加している体でツイートしてくれとのこと。<br /> まあ、出かけることもできないなら腕試しとしてこの「車両搬入」をゲーム化してみようか、とチャレンジすることにしました。<br /> リリースまでの道<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CE%A4%EA%A4%F2">のりを</a>ダラダラと日記的に書き連ねてみます。</p> <ul class="table-of-contents"> <li><a href="#きっかけ">きっかけ</a></li> <li><a href="#-1日目-430深夜">-1日目 (4/30深夜)</a></li> <li><a href="#0日目51">0日目(5/1)</a></li> <li><a href="#1日目52">1日目(5/2)</a></li> <li><a href="#2日目53">2日目(5/3)</a></li> <li><a href="#3日目54-公開まで">3日目(5/4) 公開まで</a></li> <li><a href="#走り終わって">走り終わって</a></li> <li><a href="#おまけMesh-Bakerで机モデルを統合したら">おまけ:Mesh Bakerで机モデルを統合したら</a></li> </ul> </div> <div class="section"> <h3 id="-1日目-430深夜">-1日目 (4/30深夜)</h3> <ul> <li>モデル作成</li> <li>テスト用レベルの作成</li> <li>トラックの挙動決定</li> </ul><p>閃いた時間が23時とかだったので取りあえず眠くなるまでやってみるべーとせこせこモデル作成開始。<br /> 一応<a href="http://digigame-expo.org/">&#x30A4;&#x30D9;&#x30F3;&#x30C8;&#x4E3B;&#x50AC;</a>もやっておりますので親の顔より見た長机の寸法など調べるまでもありません。(え?今年の日程?……緊急事態宣言の先行き次第じゃないスかね……)<br /> 1800W×450D×700H、だいたいのイベントの長机はこの仕様ですので、パパッと作成します。</p><p>小物の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>ですが、今回はAsset Forgeというのを使いました。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fassetforge.io%2F" title="Asset Forge • Kitbashing tool" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://assetforge.io/">assetforge.io</a></cite><br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506002031.png" alt="f:id:dnasoftwares:20200506002031p:plain" title="f:id:dnasoftwares:20200506002031p:plain" class="hatena-fotolife" itemprop="image"></span><br /> (図は適当に作ったダンジョンの一室的なもの。3~5分あればこのぐらいはすぐ)<br /> 要するに<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3みたいな感じでパーツくっつけてモデルつくるヤツなんですが、パーツの種類にバリエーションが多く、多彩なジャンルのモデルを作れます。SFに限らず現代・中世などのモデルも可。<br /> FBXを直接エクスポート可能、かつUnity向け設定プリセットがあり<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>が終わったらすぐさまUnityに持ち込めるのも強みか。</p><p>机の次は搬入の主役?たる段ボール。ピコ手から大手まで積み具合を変えて複数、さっくり完成。<br /> 情景パーツだけじゃなくて主人公たるトラックの方も作らねばなりませんが、こちらは素材をそのまま使ってしまう方向に。</p><p>Asset Forge作者のKenney氏はゲーム向け素材集も出しており、この中にちょうど手頃な車両モデルがあったため使うことにしました。<br /> <a href="https://kenney.itch.io/kenney-game-assets-3">Kenney Game Assets 3</a><br /> 買うだけ買って全然使えてなかったのでいい機会でした。プロトタイピングやゲームジャムではこういうモデルが超役に立ちます。</p><p>さて、Unity上で長机を並べるわけですが、1.8mをびっしり並べるというのは若干めんどくさいのでアセットに頼ることにします。<br /> <iframe src="https://assetstore.unity.com/linkmaker/embed/package/99591/widget-wide-light?_aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe></p><p>コライダーに沿ったり物理に従って良い感じにオブジェクトを撒いたりできるツールなんですが、配置機能の一つに等間隔配置があり、コレなら島作るのほぼ一瞬じゃん!(島の端っこの回転だけは手作業だけど)ってことで採用。<br /> これで適当に島を並べてイベント会場っぽくして、テストフィールドを準備。</p><p>この段階で既に複数のサークルに荷物を配達するゲームということだけは固まっていたので、先に トラックの挙動を作ってしまおうと思いました。ここは自作で良い感じに出来る気がしなかったので、最初からアセット買う気満々でした。<br /> アーケード挙動のアセット複数が候補に挙がりましたが、とにかく「壁ヒットの判定が車両形状通りちゃんと取れる」かどうかを調べて(カジュアル向けの車両挙動のアセットは球を転がすことで近似してるようで、特に側面の判定が見た目通りになってないことがありました)たどり着いたのが下のアセット。<br /> <iframe src="https://assetstore.unity.com/linkmaker/embed/package/142918/widget-wide-light?_aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe><br /> 無料版もあったんですが、5$ならいいかーってノリで購入。変なマネージャーやらへの依存もなくこちらで用意したモデルへの組込はあっさり完了。パラメーターの決定にはやや苦労しましたが最終的には遊びやすい形に収まってくれました。ジャンプとかブーストとかいう機能がありましたが当然いらないので削除。代わりに完全停止できるよう「ブレーキ」を実装。<br /> この手の車両コントローラってブレーキからそのまま滑らかにバックに移行するヤツばっかりで「完全に止まる操作」って意外と入ってないこと多いですよね……</p><p>あと停車目標によさげなオブジェクトを準備。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%EC%A5%A4%A5%B8%A1%BC%A5%BF%A5%AF%A5%B7%A1%BC">クレイジータクシー</a>みたいに地面から枠が伸びてる感じがよかったんですが、自前ではしっくりこなかったためこちらもアセットに頼る方向へ。</p><p><iframe src="https://assetstore.unity.com/linkmaker/embed/package/109286/widget-wide-light?_aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe><br /> 抽象的な方がいいかな?ってことでフラットポリゴンなこれ(例によって購入済みだったのに使いどころが略)を採用。ちょうど都合良く「Checkpoint」なるパーティクルループがあったので有り難く使うことに。他のエフェクトもすべてこのアセットで揃えています。</p><p>以上の情景パーツを並べて下のスクショのようになったのが午前5時。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506004545.png" alt="f:id:dnasoftwares:20200506004545p:plain" title="f:id:dnasoftwares:20200506004545p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>いける、と判断してスクショをツイートして、スタッフの知人に「BS東館の資料とか持ってないですかね?」とDM飛ばして就寝。<br /> (<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D3%A5%C3%A5%B0%A5%B5%A5%A4%A5%C8">ビッグサイト</a>のWebサイトに図面があったんですが、気づいたのは寝て起きた後のことでした)</p> </div> <div class="section"> <h3 id="0日目51">0日目(5/1)</h3> <ul> <li>本番用マップの作成</li> <li>トラックのPrefabでテスト</li> <li>エフェクト類準備</li> <li>HUDのモック作成</li> <li>ゲーム内情報を格納するコードの作成</li> </ul><p>起きたら知人が過去イベントの図面を見せてくれました。これで456ホールの寸法がわかったので<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>可能に。<br /> 早速<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>なんですが、マジメにやってたんじゃ終わるわけがないので「 ドア・お手洗い、壁面の看板、天井など全部無視」と、とにかく搬入に関係しないものは全部無視の方向で決定。<br /> 割り切ると決まれば簡単です。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Adobe%20Illustrator">Adobe Illustrator</a>で図面をトレスして壁と柱のパスを作り、<a class="keyword" href="http://d.hatena.ne.jp/keyword/SVG">SVG</a>出力して<a class="keyword" href="http://d.hatena.ne.jp/keyword/Metasequoia">Metasequoia</a>で読み込み。パスを押し出せばあっという間に東ホールのできあがりであります。</p><p><figure class="figure-image figure-image-fotolife" title="図面の上から壁・柱のパスを起こす(マゼンタのところ)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506013205.png" alt="f:id:dnasoftwares:20200506013205p:plain" title="f:id:dnasoftwares:20200506013205p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>図面の上から壁・柱のパスを起こす(マゼンタのところ)</figcaption></figure><figure class="figure-image figure-image-fotolife" title="SVG出力してMetasequoiaに送った状態(暗いところは床用のポリゴン)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506013617.png" alt="f:id:dnasoftwares:20200506013617p:plain" title="f:id:dnasoftwares:20200506013617p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption><a class="keyword" href="http://d.hatena.ne.jp/keyword/SVG">SVG</a>出力して<a class="keyword" href="http://d.hatena.ne.jp/keyword/Metasequoia">Metasequoia</a>に送った状態(暗いところは床用のポリゴン)</figcaption></figure><figure class="figure-image figure-image-fotolife" title="上に押し出せばごらんのとおり"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506013825.png" alt="f:id:dnasoftwares:20200506013825p:plain" title="f:id:dnasoftwares:20200506013825p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>上に押し出せばごらんのとおり</figcaption></figure></p><p>これをUnityに持って行って、図面の寸法と一致するようにスケーリング調整。<br /> で、サークル配置図を参考に島の数等を調べ、机を並べたのでひとまず<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>に進捗をアップしたんですが、まだ違和感バリバリです。</p><p><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">東456の現在の様子です <a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a> <a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://t.co/JAldeLBnuQ">pic.twitter.com/JAldeLBnuQ</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1256120038299336709?ref_src=twsrc%5Etfw">2020年5月1日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>短辺側の壁サークルと島が近すぎる気がするし、全体的に何かすごい違和感が……と思っていたらスタッフ経験者諸氏から<b>「ホール出入口とシャッターの間の島間隔は他より広い」</b>と指摘が。より正確なサークル配置図も見せて貰って、そういえばそうだった、ってことで各所微調整して下図の通りに。</p><p><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">甚だ怪しいけどとりあえずそれっぽくした <a href="https://t.co/sL94KC617H">pic.twitter.com/sL94KC617H</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1256133473640693761?ref_src=twsrc%5Etfw">2020年5月1日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>これで机配置を固定して、作っておいた車のPrefabとかをおいて実際に走らせてみました。</p><p><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">エア設営が完了したらエア車両搬入が行われるのは確定的に明らか<a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a> <a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://t.co/Adw1fcFSsi">pic.twitter.com/Adw1fcFSsi</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1256136937762512896?ref_src=twsrc%5Etfw">2020年5月1日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>走りにくいということもなさそうなのでこのまま続行。停車目標へのインタ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ションから実装していきます。ここは特に新しいこともなし。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D0%A5%C3%A5%B0">デバッグ</a>でわかりやすいように、停車目標とリンクしている荷物オブジェクトの間に線を引くようにしたぐらいでしょうか。</p><p><figure class="figure-image figure-image-fotolife" title="停車目標に止まると矢印の先にあるオブジェクトがPopする(エディタ上では荷物は表示したまま)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506022057.png" alt="f:id:dnasoftwares:20200506022057p:plain" title="f:id:dnasoftwares:20200506022057p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>停車目標に止まると矢印の先にあるオブジェクトがPopする(エディタ上では荷物は表示したまま)</figcaption></figure></p><p>Unityのフォーラムの「<a href="https://forum.unity.com/threads/debug-drawarrow.85980/">Did you ever wish there was a Debug.DrawArrow()?&#xFF08;&#x610F;&#x8A33;&#xFF1A;Debug.DrawArrow()&#x3068;&#x304B;&#x6B32;&#x3057;&#x304F;&#x306D;&#xFF1F;&#xFF09;</a>」に書かれていたコードを使っています。なるほど便利だわ。オブジェクトのリンク漏れ・リンク間違いがすぐわかって良い感じです。</p><p>そのまま他のルールに関わるオブジェクトを作成。</p> <ul> <li>障害物にぶつかったときのエフェクト。火花っぽい見た目で、ペナルティであると明確にわかる色に。これはPolygon Arsenalの中ではピッタリ合う感じのエフェクトがなかったので、近いイメージのPrefabから改変して作成。</li> <li>ゴール地点のエフェクト。これはわかりやすいのがあったのでそのまま使用。</li> </ul><p>で、ゲームルールに目処が立ったところでHUDのモックを作成。モックと言っても値の更新の実装を後回しにするだけでUnity上で配置します。<br /> デフォルトの画像リソース使いたくないマンなので先日セールだったこれを動員。<br /> <iframe src="https://assetstore.unity.com/linkmaker/embed/package/154574/widget-wide-light?_aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe><br /> 定価99$ですが先日のセールで半額でした。アイコンは微妙ですがUIパーツとカラー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%A6%A5%A9%A5%C3%A5%C1">スウォッチ</a>が大変良い。WebでいうBootstrap的な基本配色にまとめられるので、なんとなくUIが引き締まります。<br /> フォント類はmojimo-game。ゴール演出用のテロップなどもこのタイミングで作成しました。あと、レーダー。常時見られる地図がないと絶対難しい、ということでミニマップの導入は必須事項と思っておりました。<br /> <iframe src="https://assetstore.unity.com/linkmaker/embed/package/33729/widget-wide-light?_aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe><br /> ミニマップはこちらのアセットを使用。取りあえず設定済みのprefabを<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>に置いて、レーダーに表示させたいオブジェクトに<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>一つ貼ればもう準備OK……と導入がむちゃくちゃ楽で、外観の変更も簡単だったので助かりました。<br /> UIを作るとゲーム中通して保持すべき変数が自然と浮かび上がるわけで、ここでゲーム通して参照される変数を入れるシングルトンクラス(ウチではGameEnvironmentと呼称しています)を作成。<br /> ウチのUnity作品ではほぼ必ず存在する由緒正しき存在です。シングルトンに加えDontDestroyOnLoadを入れており、タイトルおよびゲーム中を通して同じオブジェクトを参照できるという形にしてあります。シーンを跨ぐ変数やシーン間でのパラメータ渡しなどに使用しています。<br /> static変数よりはマシかなーと思ってやってるのですがどうなんですかね。</p><p>ここまで出来たところで取りあえず進捗ツイート用のGIFアニメを撮ったんですが、</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506022633.gif" alt="f:id:dnasoftwares:20200506022633g:plain" title="f:id:dnasoftwares:20200506022633g:plain" class="hatena-fotolife" itemprop="image"></span></p><p>どうせならこれは実際の搬入時間帯にツイートした方が面白いなと思ってツイートせず就寝。(身内のDiscordと<a class="keyword" href="http://d.hatena.ne.jp/keyword/Facebook">Facebook</a>には貼った)</p> </div> <div class="section"> <h3 id="1日目52">1日目(5/2)</h3> <ul> <li>台車モデルの作成</li> <li>ゲームシーケンスの作成</li> <li>「ご注意」画面の作成</li> <li>全体マップ画面&走行記録表示</li> <li>タイトル画面</li> <li>ツイート機能実装(途中)</li> </ul><p>開催!コミ!ケット!(300人のスパルタ人に任せた動画のアレ)ってなもんで盛り上がる<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%C3%A5%B7%A5%E5%A5%BF%A5%B0">ハッシュタグ</a>を眺めながら作業。Discordで「前日搬入あるある」として「他社の搬入車両」「邪魔なチラシ台車」などのネタを得たので、まずは台車から。適当にモノタロウのサイトで一般的な台車の寸法を調べ、Asset Forgeでさっくり作成。<figure class="figure-image figure-image-fotolife" title="台車色々"><div class="images-row mceNonEditable"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506023036.png" alt="f:id:dnasoftwares:20200506023036p:plain" title="f:id:dnasoftwares:20200506023036p:plain" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506023043.png" alt="f:id:dnasoftwares:20200506023043p:plain" title="f:id:dnasoftwares:20200506023043p:plain" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506023047.png" alt="f:id:dnasoftwares:20200506023047p:plain" title="f:id:dnasoftwares:20200506023047p:plain" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506023050.png" alt="f:id:dnasoftwares:20200506023050p:plain" title="f:id:dnasoftwares:20200506023050p:plain" class="hatena-fotolife" itemprop="image"></span></div><figcaption>台車色々</figcaption></figure></p><p>流石に台車がトラックとぶつかって動かないハズはあるめえよってことで、ぶつけたら滑る物体としてRigidbodyを設定。<br /> Kenney Game Assets 3からトラック以外の車両アセットも引っ張り出して台車と一緒に障害物として設置、チェックポイントを設定。</p><p>このあたりからゲームの情報を格納するシングルトンクラスやらArborのステートマシンやらを用意して、一繋がりのゲームにするべく<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を書きます。(エア)搬入時間帯にUIも動き出したので一度MP4でキャプチャして投稿。<br /> ついでにネタ半分の「ご注意」画面を作成しこれも投稿。</p><p><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">華麗なエア搬入の様子です<a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a><a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://t.co/XTeXRR6373">pic.twitter.com/XTeXRR6373</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1256508076347146240?ref_src=twsrc%5Etfw">2020年5月2日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a><a href="https://twitter.com/hashtag/%E3%81%AF%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#はい</a> <a href="https://t.co/WP83xUi1r8">pic.twitter.com/WP83xUi1r8</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1256508077987094529?ref_src=twsrc%5Etfw">2020年5月2日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>これがエア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>公式にRTされてバズりました。反応も悪くなくモチベーションアップ。<br /> ゲームとしては繋がったんですが、レーダーだけではまだチェックポイントがわかりにくいような気がしたため、開始前にブリーフィングを行うように決定。<br /> さらに走行ルート自由というシステムなので、プレイヤーごとに走行ルートの違いをアピールできるよう、ゴールまたは時間切れの後に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D6%A5%EA">デブリ</a>ーフィングとして走行したルートを表示するのを決定。</p><p>走行ルートの記録ですが難しい事はやっておりません。一定時間間隔でプレイヤーキャラのワールド座標と向きを記録。ほか、マップにプロットしたいイベントが発生したときも時刻と座標を記録するというだけです。<br /> あとはその記録を最初から順番にプロットすればOK。</p><p><figure class="figure-image figure-image-fotolife" title="開始前、チェックポイントの位置を示すマップ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506111842.png" alt="f:id:dnasoftwares:20200506111842p:plain" title="f:id:dnasoftwares:20200506111842p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>開始前、チェックポイントの位置を示すマップ</figcaption></figure><figure class="figure-image figure-image-fotolife" title="ゴール後は走行ルートとクラッシュ箇所を表示"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506111839.png" alt="f:id:dnasoftwares:20200506111839p:plain" title="f:id:dnasoftwares:20200506111839p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>ゴール後は走行ルートとクラッシュ箇所を表示</figcaption></figure></p><p>こういった流れの途中でタイトル画面も作成。ゲーム中UIにテイストを合わせつつさっくりと。</p><p>一般公開にあたっては何らかの形で記録を競い合う要素がないと面白くないと思っておりましたが、ランキング実装はだいぶ厳しいのでやるならツイートと思っておりました。<br /> ちょっと調べたら画像付きツイートができるというので、imgurを使うタイプのサンプルを使ってみることに。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fttyyamada%2FTweetWithScreenShotInWebGL" title="ttyyamada/TweetWithScreenShotInWebGL" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/ttyyamada/TweetWithScreenShotInWebGL">github.com</a></cite></p><p>エディタ上ではツイートもアップロードも無事動いたのを確認できたので<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>書き出してみたら……なんでかツイートダイアログがでない。(ログではNullReferenceだかを吐いて止まっている)<br /> というところで意味がわからなくなって一旦ふて寝。</p> </div> <div class="section"> <h3 id="2日目53">2日目(5/3)</h3> <ul> <li>身内向けテスト</li> <li>ブリーフィング画面やゲーム画面での負荷低減策</li> <li>ツイート機能の続き</li> <li>効果音とBGMの選定</li> </ul><p>とりあえずツイート機能と効果音以外は無事動いたのでDiscordで身内テストを開始。</p><p>ツイートの方は結局原因がよくわからずじまいで、別の方のパッケージに切替えることに。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fnir-takemi%2FUnityTweet" title="nir-takemi/UnityTweet" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/nir-takemi/UnityTweet">github.com</a></cite></p><p>流石に心配になったので一度テストシーンを作成して<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>書き出しもチェック。こんどは大丈夫ということで本組込。<br /> 「prefab(ylib > UnityTweet > Resources > Prefabs > GO_TweetImgur)を<a class="keyword" href="http://d.hatena.ne.jp/keyword/tweet">tweet</a>したいscene上に置く」という説明になっていますが、ほかのGameObjectの子に置くと正常に動きません。<br /> ちょっと色気を出してGameEnvironmentの下に置いたらだめだったよ……</p><p>統合<a class="keyword" href="http://d.hatena.ne.jp/keyword/GPU">GPU</a>のユーザーから重いと指摘があったので負荷低減策を検討。<br /> Profilerにかけてみたところ</p> <ul> <li>Batchesが2000とか3000とか</li> <li>Setpassが数百回</li> <li>机のMaterialが2個に分かれててバッチングが全然できてない</li> <li>背景にライトマップとLightProbeを使っていたのだがLightProbeが細かすぎて参照するProbeが変わりすぎてバッチングができてない</li> </ul><p>という惨憺たる状態だったので</p> <ul> <li>Staticの設定をマジメに実施しStatic BatchingやDynamic Batchingを入れた</li> <li>Mesh Materializerでマテリアルを統合(本当はPro Drawcall Optimizerを使うつもりだったがいつの間にかDeprecatedにされてた……)</li> <li>机のLightMapも焼く(Lightmapの計算時間とサイズが爆上がりするのでやりたくなかったけどやむなし)</li> </ul><p>と言う風にして一応軽量化。絵作りに使っていたPostprocessing Stackを全OFF出来た方が良かった気がするが、ひとまずは乗せるエフェクトを減らすオプションまで。</p><p>さらにブリーフィングの画面も重い。そりゃそうですよカリングのできないホール全域のリアルタイム描画ですからね……ただ、こっちは明確に軽くする手段があって、ブリーフィングのマップではオブジェクトは止まったままなのだから<b>一度レンダーテクスチャに焼き付けてしまえばいい</b>のです。<br /> 考え方としては「背景とステージ上の各要素(①)と、マップの記号(②)の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>を分ける」「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>を分けた上で、①は1回だけ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>してレンダーテクスチャに保存、あとは①のレンダー結果を敷いてその上に②を<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>」といったところです。説明が長くなりそうなので取りあえずは概念図と結果だけ貼っておきます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506144857.png" alt="f:id:dnasoftwares:20200506144857p:plain" title="f:id:dnasoftwares:20200506144857p:plain" class="hatena-fotolife" itemprop="image"></span><br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506145725.png" alt="f:id:dnasoftwares:20200506145725p:plain" title="f:id:dnasoftwares:20200506145725p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>ともかくこれで劇的に軽くなりました。まだ詰めるなら机全部のメッシュ統合は当然考えるべきでしょうが上記の通り普段使いしてたPro Drawcall OptimizerがDeprecatedにされておりひとまず見送り。(さっき別のアセットでBakeを試みました。結果は巻末で)</p><p>夕方からはライトマップの設定調整。512x512のテクスチャが20枚越えというのがアレだったので2048x2048に変更してパラメタ調整して云々。最終的には2枚に収まってはくれましたが、詰め込み過ぎの影響か机のフチに謎の輝きが発生したりと、焼き直し回数が多くなってしまいました。</p><p>なんとかライトマップを倒してようやく効果音とBGMの選定を開始。細かく言うことはないんですけど、アセットストアの効果音集って実際に聞く為に一度インポートしないといけないわけですが、2GBとかのMEGA BUNDLE的なヤツだとダウンロードから展開まで数分、さらにインポートで30分とかかかって心が折れそうでした。効果音系のアセットは<b>買ったらまず空プロジェクトなどに展開しておく</b>のをお勧めします……。実物を聞きながら選定したいときにメチャクチャ時間無駄使いしてしまうので。</p> </div> <div class="section"> <h3 id="3日目54-公開まで">3日目(5/4) 公開まで</h3> <ul> <li>効果<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BB%C1%C8">音組</a>込</li> <li>時間ができてしまったのでステージ増加</li> <li>公開</li> </ul><p>unityの効果<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BB%C1%C8">音組</a>込で毎度困るのが</p> <ul> <li>特定のGameObjectに紐付かないシステム系の音の取り扱い</li> <li>シーンを跨いで鳴って欲しい音をどこで保持するか</li> </ul><p>じゃないかと個人的に思っています。<br /> 今回はメテオフォールで一躍時の人となって今は<a href="https://unityroom.com/games/stayhome">&#x1F98D;</a>の人として有名(?)なEIKI`さんの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>をベースに実装しました。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Feiki.hatenablog.jp%2Fentry%2FSimpleAudioManager" title="秒でオーディオを実装する - 実践ゲーム製作メモ帳2" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://eiki.hatenablog.jp/entry/SimpleAudioManager">eiki.hatenablog.jp</a></cite></p><p>BGMも複数鳴らしたかったので下記の通りのコードに変更。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synComment">//--------------------------------------------------------------------------------</span> <span class="synComment">// - AudioManager -</span> <span class="synComment">//--------------------------------------------------------------------------------</span> <span class="synComment">//</span> <span class="synComment">// オーディオ全般</span> <span class="synComment">//</span> <span class="synComment">//--------------------------------------------------------------------------------</span> <span class="synStatement">using</span> UnityEngine; <span class="synType">namespace</span> DNASoftwares.ComikeCarryIn { <span class="synType">public</span> <span class="synType">enum</span> ESeID { _None, Decide, Cancel, Select, SignalCount, SignalStart, CheckPoint, CheckPointLast, Launch, _Max } <span class="synType">public</span> <span class="synType">enum</span> EMusicID { _None, Title, Briefing, Stage0, Stage1, Stage2, Finish, TimeOver, Result, _Max } <span class="synComment">//--------------------------------------------------------------------------------</span> <span class="synType">public</span> <span class="synType">class</span> AudioManager : SingletonMonoBehaviour&lt;AudioManager&gt; { <span class="synType">const</span> <span class="synType">int</span> cSEMax = (<span class="synType">int</span>) ESeID._Max; <span class="synType">const</span> <span class="synType">int</span> cBGMMax = (<span class="synType">int</span>) EMusicID._Max; AudioSource mBGM = <span class="synConstant">null</span>; AudioSource[] mSEList = <span class="synStatement">new</span> AudioSource[cSEMax]; AudioSource[] mBGMList = <span class="synStatement">new</span> AudioSource[cBGMMax]; [SerializeField] <span class="synType">private</span> <span class="synType">bool</span> _noBGM; <span class="synComment">// BGMオフの設定はワンタッチで出来るようにしとく (動画素材録画用)</span> <span class="synComment">//----------------------------------------------------------------------------</span> <span class="synType">void</span> Awake() { <span class="synStatement">base</span>.Awake(); DontDestroyOnLoad(gameObject); var t = transform; <span class="synStatement">for</span> (ESeID e = ESeID._None + <span class="synConstant">1</span>; e &lt; ESeID._Max; e++) { mSEList[(<span class="synType">int</span>) e] = GetComponentSafe&lt;AudioSource&gt;(t, e.ToString()); } <span class="synStatement">for</span> (EMusicID e = EMusicID._None + <span class="synConstant">1</span>; e &lt; EMusicID._Max; e++) { mBGMList[(<span class="synType">int</span>) e] = GetComponentSafe&lt;AudioSource&gt;(t, e.ToString()); } } <span class="synComment">//----------------------------------------------------------------------------</span> <span class="synType">public</span> <span class="synType">void</span> PlaySE(ESeID id) { <span class="synStatement">if</span> (mSEList[(<span class="synType">int</span>) id] != <span class="synConstant">null</span>) mSEList[(<span class="synType">int</span>) id].Play(); } <span class="synComment">//----------------------------------------------------------------------------</span> <span class="synType">public</span> <span class="synType">void</span> PlayBGM(EMusicID id) { <span class="synStatement">if</span> (_noBGM) <span class="synStatement">return</span>; <span class="synStatement">if</span> (mBGM != <span class="synConstant">null</span> &amp;&amp; mBGM.isPlaying) mBGM.Stop(); <span class="synComment">// 先に鳴っているBGMを止める</span> mBGM = mBGMList[(<span class="synType">int</span>) id]; <span class="synComment">// 後で止められるように今鳴らし始めたBGMは覚えておく</span> <span class="synStatement">if</span> (mBGM != <span class="synConstant">null</span>) mBGM.Play(); } <span class="synType">public</span> <span class="synType">void</span> StopBGM() { <span class="synStatement">if</span> (_noBGM) <span class="synStatement">return</span>; <span class="synStatement">if</span> (mBGM == <span class="synConstant">null</span>) <span class="synStatement">return</span>; mBGM.Stop(); mBGM = <span class="synConstant">null</span>; } <span class="synComment">//----------------------------------------------------------------------------</span> <span class="synComment">// コンポーネントの取得</span> <span class="synType">private</span> Type GetComponentSafe&lt;Type&gt;(Transform parent, <span class="synType">string</span> gameobject_name) <span class="synStatement">where</span> Type : Component { Transform temp = parent.Find(gameobject_name); <span class="synStatement">if</span> (!temp) <span class="synStatement">return</span> <span class="synConstant">null</span>; <span class="synStatement">return</span> temp.GetComponent&lt;Type&gt;(); } } } </pre><p>効果音の選定は散々苦労しましたが実装はあっさり。<br /> 時間ができてしまったので難易度を下げた初級コースをちょいちょいと作成。もうちょっと説明を詳しく入れるとかしてもよかったんですけどそこまでは時間がなかった。<br /> UI周りはArborであらかたなんとかなってしまうので助かってますが、モーダル的なものを実装するときは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a> Groupの「Intractable」とか「Block Raycast」も変更したいところ、Arbor標準のBehaviorではここに手が届かないので取り急ぎ自作。</p><p>それでもなんとか3日目の搬入時間帯には間に合わせたのでした……。(動画撮影で手間取って予定から20分遅れたけど)<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">「今日の搬入はキミがハンドルヨロシクねー」<br><br>【エア搬入T.T.】公開しました<a href="https://t.co/Hw7Q7i7JJe">https://t.co/Hw7Q7i7JJe</a><br>みんなもエア搬入しよう!<a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a><a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E6%90%AC%E5%85%A5?src=hash&amp;ref_src=twsrc%5Etfw">#エア搬入</a><a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://t.co/B0EDWbx1Qd">pic.twitter.com/B0EDWbx1Qd</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1257224619511046144?ref_src=twsrc%5Etfw">2020年5月4日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p>このあとPVを作ろうと思い始めてまた夜更かししてしまうわけですがそこは別記事にします。もうすでに長いし。</p> </div> <div class="section"> <h3 id="走り終わって">走り終わって</h3> <p>久しぶりの超<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C3%BB%B5%F7%CE%A5%C1%F6">短距離走</a>ゲーム開発でしたが、最初から最後までかなりモチベーション高く走りきることができました。</p> <ul> <li>時間が無いことがわかりきっていた上、 「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>会場をトラックで疾走して荷物を搬入する」という見せたい構図がはっきりしてたのでそれに関係の薄い要素を全部切り捨てる勇気が持てた</li> <li>オレオレマネージャなどを主張しない使いやすいアセットに恵まれた</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>に投稿するとけっこう反応が貰えた</li> </ul><p>おかげさまで多くの方に遊んで頂けたようで、中にはこちらの想像を超えるプレイングで最速をたたき出すプレイヤーも……<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">【エア搬入T.T.】DAY 1・東456ホール の搬入を時間内に完了した!<br>タイム:1分30秒83 ミス:0回<br>ルート変えてかなり短縮できた。1分30秒切りも見えてきたけど、ここから先の切り詰めはかなりきつそう。<a href="https://t.co/NA5rKFpz1p">https://t.co/NA5rKFpz1p</a> <a href="https://t.co/0UJ2PFgpbz">https://t.co/0UJ2PFgpbz</a> <a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E6%90%AC%E5%85%A5TT?src=hash&amp;ref_src=twsrc%5Etfw">#エア搬入TT</a> <a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%82%B3%E3%83%9F%E3%82%B1?src=hash&amp;ref_src=twsrc%5Etfw">#エアコミケ</a> <a href="https://twitter.com/hashtag/unityroom?src=hash&amp;ref_src=twsrc%5Etfw">#unityroom</a></p>&mdash; まいさん@エア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a> (@maisan_t) <a href="https://twitter.com/maisan_t/status/1257614576092209154?ref_src=twsrc%5Etfw">2020年5月5日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <br /> 壁サー前をバックで疾走するトラックを想像するとジワジワ来ます。</p><p>なお、<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ラストステージを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D3%A5%C3%A5%B0%A5%B5%A5%A4%A5%C8">ビッグサイト</a>東ホールの最終日東ホールにして、「最終日の東ホールは本当に緊張する場面なんだ」って言わせたい</p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1257252398029889537?ref_src=twsrc%5Etfw">2020年5月4日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><p><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">各会場を根城にしてるオンリーでネタにしていいところは机配置図見せてください(えー</p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1257251958097735680?ref_src=twsrc%5Etfw">2020年5月4日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ツインメッセとか?w</p>&mdash; ほうじょうたんさま@次は… (@HojoTansama) <a href="https://twitter.com/HojoTansama/status/1257252938105290753?ref_src=twsrc%5Etfw">2020年5月4日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">つまり今から1週間半でエア搬入<a class="keyword" href="http://d.hatena.ne.jp/keyword/RTS">RTS</a>エディションですねなるほど?</p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/1257253161770741761?ref_src=twsrc%5Etfw">2020年5月4日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <br /> ……もうちょっとだけ続くんじゃ。</p> </div> <div class="section"> <h3 id="おまけMesh-Bakerで机モデルを統合したら">おまけ:Mesh Bakerで机モデルを統合したら</h3> <p><iframe src="https://assetstore.unity.com/linkmaker/embed/package/5017/widget-wide-light?_aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe></p><p><figure class="figure-image figure-image-fotolife" title="左:ビフォー 右:アフター"><div class="images-row mceNonEditable"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506171242.png" alt="f:id:dnasoftwares:20200506171242p:plain" title="f:id:dnasoftwares:20200506171242p:plain" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20200506/20200506171247.png" alt="f:id:dnasoftwares:20200506171247p:plain" title="f:id:dnasoftwares:20200506171247p:plain" class="hatena-fotolife" itemprop="image"></span></div><figcaption>左:ビフォー 右:アフター</figcaption></figure></p><p>Box Colliderが統合できないためGameObjectは減らせなかった(統合前モデルのMeshRendererだけ無効にした)のにこの数字。ウーン効果てきめん……あとでビルドしてアップデートします。</p> </div> dnasoftwares フォントバカとしてmojimo-gameがどれだけ神なのかを語る hatenablog://entry/10257846132681287896 2018-12-06T23:09:23+09:00 2018-12-09T13:00:45+09:00 今日(2018年12月6日)の18時ごろ、フォントワークス社からインディゲーム開発者向けフォント提供プランとして「mojimo-game」が発表されました。元々デザイナー・イラストレーター・同人誌作家向けとして提供されていた「mojimo-manga」をゲーム制作者向けにパッケージしたもの、という感じですが、とにかくかつてない低価格なのにメチャクチャ緩いライセンスとあまりに至れり尽くせりなので勢い余って紹介エントリを書くことにしました。 ちなみに自分は2012年に開催された全ゲ連という勉強会で「あたしってフォントバカ」なる出落ちみたいな題名でゲーム組み込みのフォントについて紹介しております。残… <p>今日(2018年12月6日)の18時ごろ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a>社からインディゲーム開発者向けフォント提供プランとして「<a href="https://mojimo.jp/game/">mojimo-game</a>」が発表されました。</p><p>元々デザイナー・<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%E9%A5%B9%A5%C8%A5%EC%A1%BC%A5%BF">イラストレータ</a>ー・同人誌作家向けとして提供されていた「<a href="https://mojimo.jp/manga/">mojimo-manga</a>」をゲーム制作者向けにパッケージしたもの、という感じですが、とにかくかつてない低価格なのにメチャクチャ緩いライセンスとあまりに至れり尽くせりなので勢い余って紹介エントリを書くことにしました。<br /> ちなみに自分は2012年に開催された全ゲ連という勉強会で<b>「あたしってフォントバカ」</b>なる出落ちみたいな題名でゲーム組み込みのフォントについて紹介しております。残念ながら当時のスライドは見つかりませんでしたが、そのときの講演のメモを公開されている方がいらっしゃったのでリンクしておきます。このメモでスライドの内容をほぼ網羅してるいかと思います。</p><p><iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fd.hatena.ne.jp%2Fnakamura001%2F20120225%2F1330175196" title="第11回 全日本学生ゲーム開発者連合(全ゲ連) 交流会に行って来ました(その1) - 強火で進め" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://d.hatena.ne.jp/nakamura001/20120225/1330175196">d.hatena.ne.jp</a></cite><br /> </p> <div class="section"> <h3>で、何がそんなに至れり尽くせりなのか</h3> <p>とりあえず<a href="https://mojimo.jp/license/game/">mojimo-game&#x306E;&#x4F7F;&#x7528;&#x8A31;&#x8AFE;</a>を読んで欲しいわけです。<br /> 注目は以下の部分。</p> <blockquote> <p>フォントを画像化してコンテンツ内で表示 ○<br /> フォントを使用したゲームを日本国内/海外で販売する ○<br /> <b>ゲームプレイヤーの文字入力が可能な部分で使用  ○</b><br /> <b>フォントから抽出したアウトラインデータのままで使用 ○</b><br /> <b>フォントファイル自体をゲームプログラムで使用 ○</b></p> </blockquote> <p>キモは後半の三行です。プレイヤーの文字入力に使えるのみならず、アウトラインデータのまま使用すること、フォントファイル自体をゲームプログラムで使用することが許諾されるのは大変貴重といえます。これが年額4800円とかマジありえん。</p><p>主立った他の定額制フォントサービスと比べてみます。</p> <table> <tr> <td>ブランド名</td> <td>費用(1PC/1年のとき)</td> <td>フォント数</td> <td>ゲームでの利用</td> </tr> <tr> <td><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%EA%A5%B5%A5%EF">モリサワ</a>パスポート</td> <td>初年49,800円、更新48,000円</td> <td>1000以上</td> <td>画像化可、実行時組み替え不可、ユーザー入力不可、フォント組み込み不可</td> </tr> <tr> <td>Morisawa App Tools ONE</td> <td>年額72,000円</td> <td>341</td> <td>画像化可、ユーザー入力可、フォント組み込みも可、<br/><b>ただし<a class="keyword" href="http://d.hatena.ne.jp/keyword/APP%20STORE">APP STORE</a>Ⓡ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Play">Google Play</a>Ⓡで配信されるものに限る</b></td> </tr> <tr> <td>ダイナフォント DynaSmartV</td> <td>年額41,500円</td> <td>1882</td> <td>画像化可、ユーザー入力不可、フォント組み込み不可</td> </tr> <tr> <td>ダイナフォント DynaSmartV ゲーム拡張オプション</td> <td>DynaSmartVの契約<br/>+年額30000円</td> <td>1882</td> <td>画像化、組み込み、ユーザー入力可</td> </tr> <tr> <td><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>(ゲーム業界プラン)</td> <td>入会金30,000円<br/>年額36,000円</td> <td>日本語528+欧文5723<br/>+その他言語100ぐらい</td> <td>画像化可、ユーザー入力は場合により可、組み込み不可</td> </tr> <tr> <td><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a><br/>(ゲーム業界プラン拡張ライセンス)</td> <td><a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>(ゲーム業界プラン)の契約<br/>+年額100,000円</td> <td><a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>と同じ</td> <td>画像化、組み込み、ユーザー入力可</td> </tr> <tr> <td><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a> mojimo-game</td> <td><b>年額4,800円</b></td> <td>12</td> <td>画像化、<b>組み込み、ユーザー入力可(<a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>拡張ライセンスと同じ範囲の許諾)</b></td> </tr> </table><p>年額7万以上が普通という中、mojimo-gameがいかに衝撃価格なのかがよくわかると思います。<br /> 特に昨今は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B2%A1%BC%A5%E0%A5%A8%A5%F3%A5%B8%A5%F3">ゲームエンジン</a>の台頭もあってUI用にフォントを組み込むのも一般的な選択肢になりつつありますし、フォントファイルそのものの組み込みが許諾されているのは非常に強いと思います。</p> </div> <div class="section"> <h3>使えるフォントはどうか</h3> <p>さて、いくらライセンスが緩くてもフォントの選択肢が狭ければ意味がありません。mojimo-gameで提供されるフォントは12種、他の定額制フォントサービスと比べると1桁2桁少なく微妙に見えるかもしれません。<br /> しかしこのライセンスで特に許諾されることがらを考えると、12種類のフォントの「選ばれし者」感が判っていただけるのではないかと思います。ざっくり各フォントの見どころをご紹介します。</p> <div class="section"> <h4>UD明朝-M / DB</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E6%A5%CB%A5%D0%A1%BC%A5%B5%A5%EB%A5%C7%A5%B6%A5%A4%A5%F3">ユニバーサルデザイン</a>として可読性を高めた<b><a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%C0%C4%AB%C2%CE">明朝体</a></b>。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a>といえば筑紫明朝や<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%E5%A5%A6%A5%DF%A5%F3">リュウミン</a>ですが、特にディスプレイでの表示にも向くUDに絞っての収録です。ノベルゲームのテキストとしても好適でしょう。</p> </div> <div class="section"> <h4>ニュー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%ED%A5%C0%A5%F3">ロダン</a>-M / B</h4> <p>ゴナとか新ゴとかあのあたりに近い<b>今時のゴシック体枠</b>。<a class="keyword" href="http://d.hatena.ne.jp/keyword/FF15">FF15</a>の日本語テキストがこのフォントらしいです。メインのフォントとして使ってもよし、B(ボールド)なら加工してテロップなどにも使えるでしょう。</p> </div> <div class="section"> <h4>UD丸ゴ_スモール-M / B</h4> <p><b>丸ゴシック枠</b>。元々<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>にはスーラという丸ゴシックがありますが、より字面の大きいUDの方がゲーム向きでしょう。これと別に「UD丸ゴ_ラージ」もあるんですが、こっちは枠一杯にツメた見出し向けの書体。本文として長く読むにはUD丸ゴ_スモールがいいでしょう。ゆるめのノベル、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%C9%A5%D9%A5%F3%A5%C1%A5%E3%A1%BC%A5%B2%A1%BC%A5%E0">アドベンチャーゲーム</a>のテキスト、システムメッセージなどでもよさそうです。</p> </div> <div class="section"> <h4><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%B6%A5%F3%A5%CC">セザンヌ</a>-M</h4> <p><b>昔ながらのゴシック体枠</b>。システムメッセージなどに加え、取扱説明書などの印刷物にもよさげ。これだけ1ウェイトのみ提供ですが、ボールドが欲しかったらニュー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%ED%A5%C0%A5%F3">ロダン</a>を選んでもいいんじゃないかな。見出しニュー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%ED%A5%C0%A5%F3">ロダン</a>B、本文<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%B6%A5%F3%A5%CC">セザンヌ</a>でも良い感じだと思います。</p> </div> <div class="section"> <h4>ハミング-M、スキップ-M</h4> <p>ちょっと小洒落た感じの文字・<b>デザイン系枠</b>。明朝とゴシックのあいのこみたいな感じ。確か<a class="keyword" href="http://d.hatena.ne.jp/keyword/Fate/Grand%20Order">Fate/Grand Order</a>の各種メッセージがスキップ-Bあたりを使ってたんじゃないかなと。ファンタ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A1%BC">ジー</a>もののシステムフォントに合いそう。ハミング-Mはスキップの角を丸くした感じ。ゲームの雰囲気に合わせて選びたいところ。</p> </div> <div class="section"> <h4>ドットゴシック16-M、ドット明朝16-M</h4> <p>ドット絵・<b>デザイン系枠</b>。レトロっぽいゲーム感出したいときにはいいんじゃないかなあ……ドットバイドットにしづらい印象があって殆ど使ってません( 正直ドット文字は<a href="http://jikasei.me/">&#x81EA;&#x5BB6;&#x88FD;&#x30D5;&#x30A9;&#x30F3;&#x30C8;&#x5DE5;&#x623F;</a>さんとこのKHドットフォントと自家製ドットフォントシリーズが強い(バリエーションもライセンスも)ので……</p> </div> <div class="section"> <h4>コミックレゲエ-B</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%D1%A5%AF">インパク</a>ト・<b>デザイン系枠</b>。12書体で明らかに異彩を放つとんがったフォント。確か<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BF%A5%A4%A5%C8%A1%BC">タイトー</a>のLeft4Dead(アーケード)のメインのフォントがこれだったはず。テロップでドーンと使うのには一番向いてそう。</p><p><hr/></p><p>というわけで、ゲームのシステムメッセージやノベルの本文などに使える「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%C0%C4%AB%C2%CE">明朝体</a>」「今時のゴシック」「昔ながらのゴシック」「丸ゴシック」が一揃いあることに加え、装飾したりアクセントとして使える「デザイン系」もざっくりではありますが揃っているということで、通り一遍のゲーム制作ではまず困らないんじゃなかろうかと思います。本当に絶対必要なものだけに絞った感じが「選ばれし者」感というわけです。<s>ドットフォントはやや微妙だけど</s><br /> </p> </div> </div> <div class="section"> <h3>買うべきか?</h3> <p>ゲーム制作者なら買って損はしません。特にUnity、<a class="keyword" href="http://d.hatena.ne.jp/keyword/UE4">UE4</a>など「フォントを組み込める<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B2%A1%BC%A5%E0%A5%A8%A5%F3%A5%B8%A5%F3">ゲームエンジン</a>」を使っているならフル活用できるでしょう。ローレゾ表現・ドット絵が主戦場の方は若干厳しいかもしれませんがまあまあ使いどころはあると思います。</p><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>契約済みの方は「<a class="keyword" href="http://d.hatena.ne.jp/keyword/LETS">LETS</a>拡張ライセンスのフォント限定版が95%OFF」と考えましょう。フォントは大きく限定されるものの、上記の通りこのライセンスが必要になるような局面には必要十分なフォントが揃っています。</p><p>というわけで画像も貼らず勢いだけで書いてしまいました。完全に早口で推しを語るヲタク状態ですが何かの参考になれば幸い。<br /> そのうち画像とか増やすかも。</p> </div> dnasoftwares 人生で初めてGameJamに参加した話 hatenablog://entry/17391345971652958126 2018-06-11T06:24:59+09:00 2018-06-11T06:25:59+09:00 UnityRoomで催されている「Unity1週間ゲームジャム」に参加してみたのでその記録。 Unity1週間ゲームジャムのルール 月曜日0時にお題発表 同じ週の日曜20時が締め切り(UnityRoomに登録) WebGLで出力して提出する いままでの開催はだいたい仕事ヤバい期間と被ってて手も付けらずという感じだったのですが、今回はちょっと時間が取れそうな雰囲気だったので一念発起しました。 ……まあ、月曜の夜までお題発表されてたこと忘れてたんですけどね…… ゲームの内容 できたゲームはこちら。 - 敵弾にカスると反撃のショットが撃てる(自動照準) - 自機の中心に当たるとゲームオーバー - 敵… <p>UnityRoomで催されている「<a href="https://unityroom.com/unity1weeks">Unity1週間ゲームジャム</a>」に参加してみたのでその記録。</p> <h2>Unity1週間ゲームジャムのルール</h2> <ul> <li>月曜日0時にお題発表</li> <li>同じ週の日曜20時が締め切り(UnityRoomに登録)</li> <li><strong><a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>で出力して提出する</strong></li> </ul> <p>いままでの開催はだいたい仕事ヤバい期間と被ってて手も付けらずという感じだったのですが、今回はちょっと時間が取れそうな雰囲気だったので一念発起しました。</p> <p>……まあ、月曜の夜までお題発表されてたこと忘れてたんですけどね……</p> <h2>ゲームの内容</h2> <p><span itemscope itemtype="http://schema.org/Photograph"><a href="http://f.hatena.ne.jp/dnasoftwares/20180610234732" class="hatena-fotolife" itemprop="url"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20180610/20180610234732.png" alt="f:id:dnasoftwares:20180610234732p:image:h300" title="f:id:dnasoftwares:20180610234732p:image:h300" class="hatena-fotolife" style="height:300px" itemprop="image"></a></span> <span itemscope itemtype="http://schema.org/Photograph"><a href="http://f.hatena.ne.jp/dnasoftwares/20180610234734" class="hatena-fotolife" itemprop="url"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20180610/20180610234734.png" alt="f:id:dnasoftwares:20180610234734p:image:h300" title="f:id:dnasoftwares:20180610234734p:image:h300" class="hatena-fotolife" style="height:300px" itemprop="image"></a></span></p> <p>できたゲームは<a href="https://unityroom.com/games/dnas_counter_shooter">こちら</a>。 - 敵弾にカスると反撃のショットが撃てる(自動照準) - 自機の中心に当たるとゲームオーバー - 敵は4種+ボスをそれなりに難易度カーブを考えつつ出す - ボス倒したらランク上げてループ</p> <p>「ぎりぎり」がお題ということで弾にカスって何かする<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>寄りのものを想定。 DNAさんは<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>との相性が致命的に悪く、これまでに作ったUnityアプリは<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>だと起動すらしないという残念っぷりだったので、とにかくゲーム本体はなるたけ手早く(でもそれなりにウチっぽさは出す)、<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>で確実に動かすためテストは着実に、という方針をとりました。</p> <p>ブラウザゲーなので操作は少なく、でもただの避けゲーはありきたり、ということでカスリで撃ち返し弾を放つ以外の攻撃方法がない<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>、ということに決定。</p> <p>連続カスリでたーのしーって感じを出すためコンボ、連続カスリでの攻撃力強化(弾数UP)などお勤めの行き帰りで方針を決定し、帰宅して実装のスタイルで進めました。</p> <h2>アセットと<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>対策</h2> <p>とにかく<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>エクスポートできなければ話になりませんが、かといってUnity標準のSkyboxまんまとかArialとかも使いたくない、ということで、ともかく作りたい画面を作ったら、新しいアセットを入れたら、その他なんか変更があったらまずBuildしてブラウザで走るか確認、という地道な手順で開発を進めました。時短に使えるものはなんでも、ということで買ったきりだったアセットもかなり投入しています。</p> <ul> <li>背景は<a href="https://www.assetstore.unity3d.com/#!/content/10109?aid=1011lGom">AllSky</a>の宇宙テクスチャ</li> <li>フォントは<a href="https://itch.io">itch.io</a>で買ったフォントパックから</li> <li>ゲームの状態遷移はお馴染み<a href="https://www.assetstore.unity3d.com/#!/content/112239?aid=1011lGom">Arbor 3</a></li> <li>敵出現順の管理に <a href="https://www.assetstore.unity3d.com/#!/content/26875">Advanced Spawn Manager</a> なるアセットを投入(時間あるなら自作するんですけどね……)</li> <li>自機の攻撃力を示す(かつ、カスリ有効範囲を示す)円ゲージは<a href="https://www.assetstore.unity3d.com/#!/content/68570?aid=1011lGom">Amplify Shader Editor</a>で自作</li> <li>あとAAとBloomが欲しかったので<a href="https://www.assetstore.unity3d.com/#!/content/83912?aid=1011lGom">Post Processing Stack</a>も投入</li> </ul> <p>幸い<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>との相性BADなアセットはなく、なんとかスムーズに作業は進みました。 唯一、Trail Rendererだけはなんでか不思議な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>になってしまったので泣く泣く削除。自機ショットに尾が付いたらキレイダナーとか思ってたんですが無念。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>部分に関係はありませんがBGMもアセットストアで購入したものを使っています。</p> <iframe src="https://api.assetstore.unity3d.com/affiliate/embed/package/32526/widget-wide?aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>で動かないとか<a class="keyword" href="http://d.hatena.ne.jp/keyword/WebGL">WebGL</a>でしか起こらないトラブルなどはなかったのは幸いでした。最後、SpawnManagerに起因するコルーチン無限ループのフリーズが発生して頭を抱えましたが、Panic buttonを導入してとりあえず無限ループを止めつつ原因究明。原因は判ったんですがこのアセットはどうも不親切さが……やっぱりSpawnerは自作した方がいいんだろうなあ…… コルーチン使いの人はPanic Button導入は必須です。マジ。「応答なし」から復活できるとか神ですよ神。</p> <iframe src="https://api.assetstore.unity3d.com/affiliate/embed/package/40167/widget-wide?aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe> <h2>実装のスケジュールについて</h2> <p>各日の作業内容を掘り返してみます。</p> <ul> <li>6/5 …… 方針決定、UIデザインだけがっつり</li> <li>6/6 …… 円形ゲージの実装、自機と敵モデル作成、インポート</li> <li>6/7 …… ザコ1種実装、(Spawner探しに難儀)</li> <li>6/8 …… ザコ2種実装、Advance Spawn Managerの使用開始、タイトル・ゲームオーバー画面</li> <li>6/9 …… ザコ1種+ボス実装、敵配置完成</li> <li>6/10 …… エフェクト・効果音載せ、画面写真等撮影、投稿</li> </ul> <p>見事にゲームバランス調整の時間がとれてません。ちょっと他の仕様盛りすぎたかなーというのと、前述の通り新たに導入したSpawnerでトラブル発生したりで時間を食いすぎた気がします。けっこう改造したし。 エフェクトと効果音は前に作ってた<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>のエフェクトを丸ごと移植。こういうときは過去の資産が役に立ちます。</p> <h2>まとめ/感想</h2> <p>ここまで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C3%BB%B5%F7%CE%A5%C1%F6">短距離走</a>でゲーム作ったのは10年以上前のSuica32(Gates32というMADビデオみたいな縦<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>のパロ)以来な気がします。 手癖でできるからと<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>の文脈のゲームにしましたが次はもうちょい普段やらないジャンルにしたいかも、でももっと手を抜かないと1週間で終わらなくなりそうな…… 今度はもうちょっと仕様をしっかり絞りたいなと思いました。 あとすぐ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%B1%A1%BC%A5%C9%A5%B2%A1%BC%A5%E0">アーケードゲーム</a>みたいにしようとするクセも直したい。</p> dnasoftwares SS6Player使ってみたよレポ② 実行時のセル差し替え hatenablog://entry/17391345971635373158 2018-04-18T11:35:20+09:00 2018-04-18T11:42:44+09:00 前回の記事でSS6Playerは良い感じだよという話をしたんですが、一件紹介し忘れていた技術ネタがあったので投下しておこうと思います。対局開始時のテロップもSpriteStudioで製作したんですが、「東一局」等の表示はプログラム側で制御できるのではないかと思い、マニュアルを漁ったところAPIはあるようだが説明がない。とはいえそんなに難しいものではなさそうなので、IntelliSenseを頼りにがんばってみることにしました。 あんまりもったいぶるもんではないので、とりあえずソースコード全文いきます。 KyokuStartTelopController.cs using System.Colle… <p><a href="http://dnasoftwares.hatenablog.com/entry/2018/03/31/163438">&#x524D;&#x56DE;&#x306E;&#x8A18;&#x4E8B;</a>でSS6Playerは良い感じだよという話をしたんですが、一件紹介し忘れていた技術ネタがあったので投下しておこうと思います。</p><p>対局開始時のテロップもSpriteStudioで製作したんですが、「東一局」等の表示はプログラム側で制御できるのではないかと思い、マニュアルを漁ったところ<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>はあるようだが説明がない。とはいえそんなに難しいものではなさそうなので、IntelliSenseを頼りにがんばってみることにしました。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20180418/20180418114219.png" alt="f:id:dnasoftwares:20180418114219p:plain" title="f:id:dnasoftwares:20180418114219p:plain" class="hatena-fotolife" itemprop="image"></span><br /> あんまりもったいぶるもんではないので、とりあえず<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>全文いきます。</p> <div class="section"> <h3>KyokuStartTelopController.cs</h3> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System.Collections; <span class="synStatement">using</span> System.Collections.Generic; <span class="synStatement">using</span> System.Linq; <span class="synStatement">using</span> DNASoftwares.Mahjong; <span class="synStatement">using</span> UnityEngine; <span class="synType">namespace</span> DNASoftwares.Mahjong { <span class="synType">public</span> <span class="synType">class</span> KyokuStartTelopController : MonoBehaviour { <span class="synType">private</span> Script_SpriteStudio6_Root _ss; <span class="synType">private</span> <span class="synType">int</span>[] _kyoku_cell = <span class="synStatement">new</span> <span class="synType">int</span>[<span class="synConstant">4</span>]; <span class="synType">private</span> <span class="synType">int</span>[] _wind_cell = <span class="synStatement">new</span> <span class="synType">int</span>[<span class="synConstant">3</span>]; <span class="synType">private</span> <span class="synType">int</span>[] _honba_cell = <span class="synStatement">new</span> <span class="synType">int</span>[<span class="synConstant">10</span>]; <span class="synType">private</span> <span class="synType">int</span>[] _yourwind_cell = <span class="synStatement">new</span> <span class="synType">int</span>[<span class="synConstant">4</span>]; <span class="synType">private</span> <span class="synType">int</span> _kyoku_sprite; <span class="synType">private</span> <span class="synType">int</span> _wind_sprite; <span class="synType">private</span> <span class="synType">int</span>[] _honba_sprite=<span class="synStatement">new</span> <span class="synType">int</span>[<span class="synConstant">2</span>]; <span class="synType">private</span> <span class="synType">int</span> _honba_footer_sprite; <span class="synType">private</span> <span class="synType">int</span> _yourwind_sprite; <span class="synComment">//セルの名称</span> <span class="synType">public</span> <span class="synType">string</span>[] KyokuWindCellNames = {<span class="synConstant">&quot;wind_east&quot;</span>, <span class="synConstant">&quot;wind_south&quot;</span>, <span class="synConstant">&quot;wind_west&quot;</span>}; <span class="synComment">//○一局 の場風の部分</span> <span class="synType">public</span> <span class="synType">string</span> KyokuCellNameFormat = <span class="synConstant">&quot;kyoku_{0}&quot;</span>; <span class="synComment">// 東○局 の漢数字</span> <span class="synType">public</span> <span class="synType">string</span> HonbaCellNameFormat = <span class="synConstant">&quot;honba_{0}&quot;</span>; <span class="synComment">// ○○本場 の数字</span> <span class="synType">public</span> <span class="synType">string</span>[] YourWindCellNames = {<span class="synConstant">&quot;yourwind_east&quot;</span>, <span class="synConstant">&quot;yourwind_south&quot;</span>, <span class="synConstant">&quot;yourwind_west&quot;</span>, <span class="synConstant">&quot;yourwind_north&quot;</span>}; <span class="synComment">//「あなたは○家です」の表示</span> <span class="synComment">//各部位のスプライトパーツ名(SpriteStudio側で決めたのを転記)</span> <span class="synType">public</span> <span class="synType">string</span> KyokuWindSpriteName = <span class="synConstant">&quot;kyoku_wind&quot;</span>; <span class="synType">public</span> <span class="synType">string</span> KyokuNumberSpriteName = <span class="synConstant">&quot;kyoku_num&quot;</span>; <span class="synType">public</span> <span class="synType">string</span>[] HonbaSpriteNames = {<span class="synConstant">&quot;honba_1&quot;</span>,<span class="synConstant">&quot;honba_10&quot;</span>}; <span class="synType">public</span> <span class="synType">string</span> HonbaFooterSpriteName = <span class="synConstant">&quot;honba_foot&quot;</span>; <span class="synType">public</span> <span class="synType">string</span> YourWindSpriteName = <span class="synConstant">&quot;wind&quot;</span>; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { _ss = GetComponent&lt;Script_SpriteStudio6_Root&gt;(); Library_SpriteStudio6.Data.CellMap c = _ss.DataGetCellMap(<span class="synConstant">0</span>); <span class="synComment">//都度参照だと重そうなのでチップ名からIDを引いておく</span> <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; _kyoku_cell.Length; i++) { _kyoku_cell[i] = c.IndexGetCell(<span class="synType">string</span>.Format(KyokuCellNameFormat, i + <span class="synConstant">1</span>)); } <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; _wind_cell.Length; i++) { _wind_cell[i] = c.IndexGetCell(KyokuWindCellNames[i]); } <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; _honba_cell.Length; i++) { _honba_cell[i] = c.IndexGetCell(<span class="synType">string</span>.Format(HonbaCellNameFormat,i)); } <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; _yourwind_cell.Length; i++) { _yourwind_cell[i] = c.IndexGetCell(YourWindCellNames[i]); } <span class="synComment">//パーツ名も都度参照は重そうなんでIDを引いとく</span> _wind_sprite = _ss.IDGetParts(KyokuWindSpriteName); _kyoku_sprite = _ss.IDGetParts(KyokuNumberSpriteName); <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; _honba_sprite.Length; i++) { _honba_sprite[i] = _ss.IDGetParts(HonbaSpriteNames[i]); } _honba_footer_sprite = _ss.IDGetParts(HonbaFooterSpriteName); _yourwind_sprite = _ss.IDGetParts(YourWindSpriteName); <span class="synComment">//卓の様子をみて表示を直す</span> <span class="synComment">//一旦再生させてからセルを変更しないとリセットされちゃう</span> _ss.AnimationPlay(<span class="synConstant">0</span>,-<span class="synConstant">1</span>,<span class="synConstant">1</span>); <span class="synComment">//局(Taku.Instance.CurrentWind には場風が入ってる 0=東 1=南 2=西)</span> <span class="synStatement">if</span> (_wind_sprite &gt;= <span class="synConstant">0</span>) { _ss.CellChangeParts(_wind_sprite, <span class="synConstant">0</span>, _wind_cell[(<span class="synType">int</span>) Taku.Instance.CurrentWind], Library_SpriteStudio6.KindIgnoreAttribute.NON); } <span class="synComment">// Taku.Instance.CurrentKyoku は何局目かが入ってる(1~4)</span> <span class="synStatement">if</span> (_kyoku_sprite&gt;= <span class="synConstant">0</span>) { _ss.CellChangeParts(_kyoku_sprite, <span class="synConstant">0</span>, _kyoku_cell[Taku.Instance.CurrentKyoku-<span class="synConstant">1</span>], Library_SpriteStudio6.KindIgnoreAttribute.NON); } <span class="synComment">//~~本場(Taku.Instance.Honba には積み棒の数が入ってる)</span> <span class="synComment">// 0本場なら関係するスプライトを全部隠す</span> <span class="synStatement">if</span> (Taku.Instance.Honba == <span class="synConstant">0</span>) { <span class="synStatement">foreach</span> (var i <span class="synStatement">in</span> _honba_sprite) { _ss.HideSet(i, <span class="synConstant">true</span>); } _ss.HideSet(_honba_footer_sprite, <span class="synConstant">true</span>); } <span class="synStatement">else</span> { <span class="synComment">//1本以上積まれてるなら~~本場を表示</span> var tmp = Taku.Instance.Honba; <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; _honba_sprite.Length; i++) { var num = tmp % <span class="synConstant">10</span>; tmp = tmp / <span class="synConstant">10</span>; <span class="synStatement">if</span> (num==<span class="synConstant">0</span> &amp;&amp; tmp == <span class="synConstant">0</span>) { _ss.HideSet(_honba_sprite[i], <span class="synConstant">true</span>); } <span class="synStatement">else</span> { _ss.CellChangeParts(_honba_sprite[i], <span class="synConstant">0</span>, _honba_cell[num], Library_SpriteStudio6.KindIgnoreAttribute.NON); } } } <span class="synComment">//プレイヤーの自風(&quot;あなたは○家です&quot;)</span> <span class="synComment">//人間が操作している席を取得し、その席の自風を得て、自風のenumをintにキャスト(0,1,2,3が東南西北に対応)</span> Seat seat = Taku.Instance.Seats .FirstOrDefault(x =&gt; x.Algorithm == Seat.AlgorithmType.HumanInterface) ?? CameraViewController.Instance.TargetSeat; _ss.CellChangeParts(_yourwind_sprite, <span class="synConstant">0</span>, _yourwind_cell[(<span class="synType">int</span>)seat.MyWind], Library_SpriteStudio6.KindIgnoreAttribute.NON); } } } </pre><p>パーツを操作する前にAnimationPlayメソッドをまず呼ぶというところさえ間違わなければ多分そんな難しいところはないです。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">public</span> <span class="synType">bool</span> CellChangeParts(<span class="synType">int</span> idParts, <span class="synType">int</span> indexCellMap, <span class="synType">int</span> indexCell, Library_SpriteStudio6.KindIgnoreAttribute ignoreAttribute); </pre><p>CellChangePartsメソッドでスプライトパーツのチップを差し替えられます。</p> <ul> <li>idParts ……パーツID。名前で引きたかったらIDGetParts(name)で名前からIDを取得すること</li> <li>indexCellMap ……セルマップID。セルマップ1つだけなら0で可、-1にすると今パーツが使っているセルマップを指定</li> <li>indexCell ……セルID。名前で引きたかったらIndexGetCell(name)でID取得。-1にするとアニメーションに元々設定されていたセルを指定</li> <li>ignoreAttribute ……これがいまいちわからんのだが「Library_SpriteStudio6.KindIgnoreAttribute.NON」としておけばとりあえず何事もない</li> </ul><p>実行時のセル指定変更は色々な応用が利くのですがマニュアルが間に合ってないのはとても残念なのでここだけ説明しました。参考になれば幸いです。</p> </div> dnasoftwares SS6Player使ってみたよレポ hatenablog://entry/17391345971631025025 2018-03-31T16:34:38+09:00 2018-04-03T17:27:22+09:00 ウェブテクノロジ製のスプライトアニメーションツール、OPTPiX SpriteStudioは公式でUnity用プレイヤーを用意しているのが特徴ですが、一時このUnity用プレイヤーが超クセモノということで若干騒がれました。 既に古い内容なので当時の記事等にリンクはしませんが「再生の忠実さを求めるあまりパフォーマンスが出ない(特にモバイル)」「セットアップが面倒」「uGUI/nGUIと相性が悪い」という点が大きかったようです。で、SpriteStudioがVer6になって、この公式プレイヤーの作りが大きく見直され、曰く「fps評価で1.5〜2倍程度の速度向上」「uGUI/nGUIとの相性がよくな… <p>ウェブテクノロジ製のスプライトアニメーションツール、<a href="http://www.webtech.co.jp/spritestudio/">OPTPiX SpriteStudio</a>は公式でUnity用プレイヤーを用意しているのが特徴ですが、一時このUnity用プレイヤーが超クセモノということで若干騒がれました。<br /> 既に古い内容なので当時の記事等にリンクはしませんが「再生の忠実さを求めるあまりパフォーマンスが出ない(特にモバイル)」「セットアップが面倒」「uGUI/nGUIと相性が悪い」という点が大きかったようです。</p><p>で、SpriteStudioがVer6になって、この公式プレイヤーの作りが大きく見直され、曰く「<a class="keyword" href="http://d.hatena.ne.jp/keyword/fps">fps</a>評価で1.5〜2倍程度の速度向上」「uGUI/nGUIとの相性がよくなった」らしいのでちょうど当時進行中だったうちのプロジェクトで試してみたのでした。</p> <div class="section"> <h3>「うちのプロジェクト」</h3> <p>とりあえず現状(3月末時点)の動画をごらんください。<br /> ※まだBGMと効果音は入れてませんので無音は仕様です</p><p><iframe width="560" height="315" frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/YjKs5nAuza8"></iframe><br><a href="https://youtube.com/watch?v=YjKs5nAuza8">4/3人打ち麻雀「New Standard Mahjong」(WIP)</a></p><br /> <p>何の変哲もない4or3人打ち麻雀ゲームです。まだWebページの一枚すらない。<br /> SpriteStudioを使っている部分としては</p> <ul> <li>メインメニュー左上のヘッダ表示</li> <li>「対局開始」などのテロップ類</li> <li>「ポン」「リーチ」「ロン」などの発声文字</li> </ul><p>と大きく分けて3箇所あります。<br /> テロップ類は調整→確認の周期が短ければ短いほどよい、ということでこういうアニメーションツールは絶対欲しい所でした。<br /> デザインとプログラムを分離しておけばいざという時にはデザイナーさんにお願いもし易いことでしょう。</p> </div> <div class="section"> <h3>SpriteStudio側での作業</h3> <p>実は試用ライセンスが切れててスクショ撮り損ねたんですけども(<br /> SpriteStudio側の作業は基本大したことをしていません。パーティクルエフェクトとか使えるものは色々使っています。メッシュはようわからんかった。</p><p>Unity用プレイヤーに読ませるにあたっては別にSpriteStudio側で専用のエクスポートを行ったりする必要はありません。SpriteStudio側では普通にプロジェクトを保存して終わりです。</p> </div> <div class="section"> <h3>Unity側での作業</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a>のSS6PlayerForUnityの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>から最新版プレイヤーを取得します。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2FSpriteStudio%2FSS6PlayerForUnity%2Fwiki" title="SpriteStudio/SS6PlayerForUnity" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/SpriteStudio/SS6PlayerForUnity/wiki">github.com</a></cite></p><p>unitypackageを入れるとUnity Editorのメニューに「Tool」→「SpriteStudio6」と項目が生えてインポーターが動作します。</p><p>インポートの際注意したいのがアセットの保存場所指定です。</p> <blockquote cite="https://github.com/SpriteStudio/SS6PlayerForUnity/wiki/%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E6%89%8B%E9%A0%86#project%E3%82%A6%E3%82%A3%E3%83%B3%E3%83%89%E3%82%A6%E3%81%AE%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6"> <p>「One Column Layout」では、ツリー上でそのまま選択すれば選択状態になります。<br /> 一方「Two Column Layout」では、ツリー上で親フォルダを選択した後、右のペインに表示されているフォルダを選択することで選択状態になります。</p> <cite><a href="https://github.com/SpriteStudio/SS6PlayerForUnity/wiki/%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E6%89%8B%E9%A0%86#project%E3%82%A6%E3%82%A3%E3%83%B3%E3%83%89%E3%82%A6%E3%81%AE%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">&#x30C7;&#x30FC;&#x30BF;&#x306E;&#x30A4;&#x30F3;&#x30DD;&#x30FC;&#x30C8;&#x624B;&#x9806; &middot; SpriteStudio/SS6PlayerForUnity Wiki &middot; GitHub</a></cite> </blockquote> <p>Two Column Layoutを常用してると右ペインで選択を忘れて怒られることが多いです。注意したいところ。<br /> <br /> あとは.sspjを指定すれば勝手にプロジェクト内のアニメーションやチップ類を変換してPrefabにしてくれます。ここは楽。<br /> どうせならここで変換設定をScriptableObjectあたりでくるんでくれたら次回からの変換楽なのになーと思ったり。(バッチ機能はあるんですけど「バッチリストを書くのがめんどうだった」というクズい理由で使わないままズルズルと……)<br /> ScriptableObjectのインスペクタに「この設定で変換」みたいなボタンおいといてボタン一発ドーン、だとわたしは嬉しいです。</p><p>おいといて、変換されたPrefabはもうそのまま利用できるので、</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>を置く</li> <li>UIカメラを設定する</li> <li>生成されたPrefabを<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>の中に置く</li> <li><b>Prefabのレイヤー指定をカメラのCulling maskと合わせる</b></li> </ul><p>以上4つの設定をすれば画面に出ます。最初の2つはこれに限らずやることだと思うので、実質2手ぐらいで普通にアニメーションが画面に出てくれます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20180331/20180331141743.gif" alt="f:id:dnasoftwares:20180331141743g:plain" title="f:id:dnasoftwares:20180331141743g:plain" class="hatena-fotolife" itemprop="image"></span></p><p>実際の組み込みではカメラにCulling maskを使ってると思いますので、ちゃんと表示したいカメラに合わせてレイヤー設定は変えましょう。レイヤー=Defaultのまま作業してUIカメラに絵がでねえぞ????とか散々やりました。</p><p>あと、Prefabの場所ですがマニュアル<a class="keyword" href="http://d.hatena.ne.jp/keyword/Wiki">Wiki</a>から引用。</p> <blockquote cite="https://github.com/SpriteStudio/SS6PlayerForUnity/wiki/%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%86%8D%E7%94%9F#%E5%88%B6%E5%BE%A1%E3%83%97%E3%83%AC%E3%83%8F%E3%83%96%E3%81%AE%E5%BD%B9%E5%89%B2%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6"> <p>・制御用プレハブを作成した場合<br /> インポート時に「Create Control-Prefab」をチェックしていた場合は、インポート時に指定したアセットフォルダの直下に「ssae名_Control」という名前のプレハブがあります。</p><p>・制御用プレハブを作成しなかった場合<br /> インポート時に「Create Control-Prefab」をチェックしていない場合は、インポート時に指定したアセットフォルダの下に「PrefabAnimation」というフォルダがあります。<br /> その中にssaeと同じ名前のプレハブがあります。</p> <cite><a href="https://github.com/SpriteStudio/SS6PlayerForUnity/wiki/%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%86%8D%E7%94%9F#%E5%88%B6%E5%BE%A1%E3%83%97%E3%83%AC%E3%83%8F%E3%83%96%E3%81%AE%E5%BD%B9%E5%89%B2%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">&#x30A2;&#x30CB;&#x30E1;&#x30FC;&#x30B7;&#x30E7;&#x30F3;&#x306E;&#x518D;&#x751F; &middot; SpriteStudio/SS6PlayerForUnity Wiki &middot; GitHub</a></cite> </blockquote> <p>フォルダ「PrefabAnimation」の下にあるPrefabがアニメーションの実体です。</p> </div> <div class="section"> <h3>もうちょっと実用的にする</h3> <p>とりあえずスプライトは出ました。出ましたがこれではあんまり実践的ではありません。</p> <div class="section"> <h4>ループ一回で自動消滅してほしい</h4> <p>生成直後のPrefabは無限ループ再生の設定になっていますが、大抵は1ループして消えるとかしてほしいわけです。<br /> ループの設定は生成されたスプライトの制御パラメタで変更できます。<br /> フォルダ「PrefabAnimation」の下にあるPrefabをインスペクタで覗くと「Script_Sprite Studio 6_Root」なる<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>が下がっているのがわかります。</p><p><figure class="figure-image figure-image-fotolife" title="Script_SpriteStudio6_Root のインスペクタ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20180331/20180331163236.png" alt="f:id:dnasoftwares:20180331163236p:plain" title="f:id:dnasoftwares:20180331163236p:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>Script_SpriteStudio6_Root のインスペクタ</figcaption></figure></p><p>一番下に「Number of Plays」という項目があり、「(1: No Loop / 0: Infinite Loop)」と説明もついてます。初期値は0なので無限ループになります。<br /> ここを1にすると再生は1回きりとなりますが、最後のフレームが出たままになってしまいます。<br /> どうせなら最後のフレームを再生し終わったら自動で消えてほしいところです。これは数行の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>で実装できます。</p> <div class="section"> <h5>SS6_AutoDestroy.cs</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; [RequireComponent(<span class="synStatement">typeof</span>(Script_SpriteStudio6_Root))] <span class="synType">public</span> <span class="synType">class</span> SS6_AutoDestroy : MonoBehaviour { <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { var rt = GetComponent&lt;Script_SpriteStudio6_Root&gt;(); rt.FunctionPlayEnd += PlayEndFunction; <span class="synComment">// 再生終了時のコールバック関数を登録</span> } <span class="synType">public</span> <span class="synType">bool</span> PlayEndFunction(Script_SpriteStudio6_Root scriptRoot, GameObject objectControl) { <span class="synStatement">return</span> <span class="synConstant">false</span>; <span class="synComment">//falseを返すと消滅</span> } } </pre><p>この<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>を「Script_Sprite Studio 6_Root」と一緒に張り付けておくと、再生終了時に自動で自分自身をDestroyするようになります。<br /> コン<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%ED%A1%BC%A5%EB">トロール</a>Prefabを通した場合でもちゃんとコン<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%ED%A1%BC%A5%EB">トロール</a>Prefabごと消えるので安心。</p> </div> </div> <div class="section"> <h4>イントロ→ループ→アウトロをやりたい</h4> <p>単なる無限ループではなく、イントロ→ループ型のループ再生もやってほしいと思うのです。本作では「対局開始」の表示やタイトル画面のヘッダ部分などがイントロ→ループの構造になっています。ついでにループ脱出して消滅するアニメ(アウトロ)もやりたい。</p><p>アニメーションにラベルを仕込んでおいてラベル通過をコールバックで検知できたら、と思ったらそれはできないようなので、結局今回はアニメーションをイントロ・ループ・アウトロに分割し、イントロの再生終了後はループのアニメーションを延々再生、アウトロへの移行は外部から関数呼び出しで、という処理にしました。<br /> (Script_SpriteStudio6_Root.AnimationPlayの引数で再生開始・終了位置をラベルで指定できるのに今気づいたのですが、これ使えばこんな回りくどいことしなくてよかったような……)</p> <div class="section"> <h5>SS6_IntroLoop.cs</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System; <span class="synStatement">using</span> UnityEngine; [RequireComponent(<span class="synStatement">typeof</span>(Script_SpriteStudio6_Root))] <span class="synType">public</span> <span class="synType">class</span> SS6_IntroLoop : MonoBehaviour { <span class="synType">public</span> <span class="synType">string</span> TrackNameLoop = <span class="synConstant">&quot;_loop&quot;</span>; <span class="synComment">// ループ部分のアニメーション名</span> <span class="synType">public</span> <span class="synType">bool</span> UseOutroAnimation = <span class="synConstant">false</span>; <span class="synComment">// アウトロアニメーションがあるのか?</span> <span class="synType">public</span> <span class="synType">string</span> TrackNameOutro = <span class="synConstant">&quot;_outro&quot;</span>; <span class="synComment">// アウトロのアニメーション名</span> [NonSerialized] <span class="synType">public</span> <span class="synType">bool</span> ExitLoop = <span class="synConstant">false</span>; <span class="synComment">// trueにすると今のループを再生しきった後アウトロに移行、または消滅</span> <span class="synType">public</span> <span class="synType">enum</span> LoopState { Intro, Loop, Outro }; <span class="synType">private</span> LoopState _state = LoopState.Intro; <span class="synType">private</span> Script_SpriteStudio6_Root _rt; <span class="synType">private</span> <span class="synType">int</span> _idxLoopAnimation; <span class="synType">private</span> <span class="synType">int</span> _idxOutroAnimation; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { _rt= GetComponent&lt;Script_SpriteStudio6_Root&gt;(); _rt.FunctionPlayEnd += LoopBackFunction; <span class="synComment">// 先に各アニメーションの名称からインデックスを引いておく</span> _idxLoopAnimation = _rt.IndexGetAnimation(TrackNameLoop); <span class="synStatement">if</span> (UseOutroAnimation) { _idxOutroAnimation = _rt.IndexGetAnimation(TrackNameOutro); } <span class="synComment">//初期状態はイントロ</span> _state = LoopState.Intro; } <span class="synType">private</span> <span class="synType">bool</span> LoopBackFunction(Script_SpriteStudio6_Root scriptroot, GameObject objectcontrol) { <span class="synComment">//各アニメーションの末尾に来た時、次のアニメーションを決定する</span> <span class="synStatement">switch</span> (_state) { <span class="synStatement">case</span> LoopState.Intro: <span class="synStatement">case</span> LoopState.Loop: <span class="synComment">//イントロ,ループ→ループ(orアウトロ)</span> <span class="synStatement">if</span> (ExitLoop) { <span class="synComment">//ExitLoop==trueならアウトロへ</span> <span class="synStatement">if</span> (UseOutroAnimation) { _rt.AnimationPlay(-<span class="synConstant">1</span>,_idxOutroAnimation,<span class="synConstant">1</span>); <span class="synComment">// アウトロ再生</span> _state = LoopState.Outro; <span class="synStatement">break</span>; } <span class="synStatement">else</span> <span class="synStatement">return</span> <span class="synConstant">false</span>; } <span class="synStatement">else</span> { _rt.AnimationPlay(-<span class="synConstant">1</span>,_idxLoopAnimation,<span class="synConstant">1</span>); <span class="synComment">//ループアニメの再生</span> _state = LoopState.Loop; } <span class="synStatement">break</span>; <span class="synStatement">case</span> LoopState.Outro: <span class="synComment">//アウトロが終わったら消滅</span> <span class="synStatement">return</span> <span class="synConstant">false</span>; <span class="synStatement">default</span>: <span class="synStatement">break</span>; } <span class="synStatement">return</span> <span class="synConstant">true</span>; } <span class="synType">public</span> <span class="synType">void</span> ExitLoopNow() { <span class="synComment">//呼んだ時点で強制的にアウトロに移る or 消去</span> <span class="synStatement">if</span> (_state == LoopState.Outro) <span class="synStatement">return</span>; <span class="synStatement">if</span> (UseOutroAnimation) { _rt.AnimationPlay(<span class="synConstant">0</span>,_idxOutroAnimation,<span class="synConstant">1</span>); _state = LoopState.Outro; } <span class="synStatement">else</span> { <span class="synStatement">if</span>(_rt.InstanceGameObjectControl!=<span class="synConstant">null</span>) <span class="synComment">//コントロールPrefabがある場合はInstanceGameObjectControlにその参照がある</span> { Destroy(_rt.InstanceGameObjectControl); <span class="synComment">//これでコントロールPrefabごと消える</span> } <span class="synStatement">else</span> { Destroy(gameObject); } } } } </pre><p>ループ処理をScript_SpriteStudio6_Rootに任せず全部自力でやり、コールバック関数で次のアニメを指示する、という仕組みがキモです。<br /> 実際に使うときは、</p> <ul> <li>SS6_IntroLoopをScript_SpriteStudio6_Rootと一緒のPrefabにアタッチ</li> <li>Script_SpriteStudio6_Rootの方は <ul> <li>再生回数を1にしておく</li> <li>「Animation Name」(再生するアニメーション名)をイントロ部分のアニメーションにする</li> </ul></li> <li>SS6_IntroLoopのインスペクタでは <ul> <li>TrackNameLoopにループ部分のアニメーション名をセット</li> <li>アウトロのアニメがある場合は、UseOutroAnimationをチェックし、TrackNameOutroにアニメーション名を指定</li> </ul></li> </ul><p>とすればイントロつきループができます。<br /> ループを脱出してアニメーションを消すときは次のどちらかで。</p> <ul> <li>GetComponent<SS6_IntroLoop>.ExitLoop=trueみたいにすれば今のループを再生しきってからアウトロへ移行または消去</li> <li>GetComponent<SS6_IntroLoop>.ExitLoopNow()とすればすぐにアウトロへ移行または即消去</li> </ul><p>これで意外と便利に使えてます。まあループ構造をデフォルトで世話してくれるととても楽でいいんですけど、自力でもそこまで難しくはないです。</p> </div> </div> <div class="section"> <h4>アニメーションのスケールがうまくいかない?</h4> <p>Script_SpriteStudio6_Rootは内部でTransformをいじっている(アニメーションにおけるRootパーツのアニメーションが反映される)関係で、Script_SpriteStudio6_RootのあるGameObjectのスケーリングなどは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>から触ることができません。<br /> アニメーション全体をスケール・回転させたい場合は、マニュアルの<a href="https://github.com/SpriteStudio/SS6PlayerForUnity/wiki/アニメーションの再生#制御プレハブの役割について">&#x5236;&#x5FA1;&#x30D7;&#x30EC;&#x30CF;&#x30D6;&#x306E;&#x5F79;&#x5272;&#x306B;&#x3064;&#x3044;&#x3066;</a>にあるように、制御プレハブの段で変形させましょう。これに気づかず半日ぐらいハマってました。</p> </div> </div> <div class="section"> <h3>結論</h3> <p>SS5PlayerForUnityは使っていなかった(そのときは自前エンジンに自前でSpriteStudioプレイヤー入れてましたが……)ので旧Verとくらべてどうか、とは語れないのですが、uGUIとの親和性も良く、2つほど自作<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>をつけるだけで実用上もほぼ問題なく使えるようになりました。</p><p>ただ、現状だと<a href="http://www.webtech.co.jp/help/faq_post/ver6_guide/#indie">SpriteStudio&#x306E;Indie&#x30E9;&#x30A4;&#x30BB;&#x30F3;&#x30B9;&#x306F;SS6&#x306B;&#x9069;&#x7528;&#x3067;&#x304D;&#x307E;&#x305B;&#x3093;</a>。<br /> SpriteStudio5相当の機能しか使えないとかでもいいんでSS6の再生エンジン使わせていただきたい……なんとか……</p> </div> dnasoftwares DoGA-L3で「取り急ぎ」作ったカッコいいモデルをもっと使いやすくする hatenablog://entry/10328749687220601532 2017-03-15T03:07:08+09:00 2017-03-15T03:08:22+09:00 前の記事ではDoGA-L3とPLAY Animation importer for Unityを用いて、DoGA-L3のモデルをUnityにインポートし、それを動かすところまでやりました。 dnasoftwares.hatenablog.comただ、現状のモデルはゲームで使うには一抹の不安が残ります。手修正やエディタ拡張アセットを利用して、もうちょっと良い感じにモデルを修正してみましょう。 マテリアルをStandardに揃える テクスチャの濃度設定とか密度設定とかは無視されている メッシュを統合する Pro Draw Call Optimizerの罠 テクスチャと色設定を合成できない テクスチ… <p>前の記事では<a href="http://doga.jp/2010/programs/dogal/index.html">DoGA-L3</a>と<a href="http://doga.jp/PLAY/unity-plugin-jp/">PLAY Animation importer for Unity</a>を用いて、<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3のモデルをUnityにインポートし、それを動かすところまでやりました。<br /> <iframe src="http://dnasoftwares.hatenablog.com/embed/2017/02/06/014249" title="DoGA-L3で「取り急ぎ」かっこいいモデルを作ってUnityで動かして遊ぶ - D.N.A.のおぼえがき" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://dnasoftwares.hatenablog.com/entry/2017/02/06/014249">dnasoftwares.hatenablog.com</a></cite></p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312192549.png" alt="f:id:dnasoftwares:20170312192549p:plain" title="f:id:dnasoftwares:20170312192549p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>ただ、現状のモデルはゲームで使うには一抹の不安が残ります。手修正やエディタ拡張アセットを利用して、もうちょっと良い感じにモデルを修正してみましょう。</p> <ul class="table-of-contents"> <li><a href="#マテリアルをStandardに揃える">マテリアルをStandardに揃える</a></li> <li><a href="#テクスチャの濃度設定とか密度設定とかは無視されている">テクスチャの濃度設定とか密度設定とかは無視されている</a></li> <li><a href="#メッシュを統合する">メッシュを統合する</a></li> <li><a href="#Pro-Draw-Call-Optimizerの罠">Pro Draw Call Optimizerの罠</a><ul> <li><a href="#テクスチャと色設定を合成できない">テクスチャと色設定を合成できない</a></li> <li><a href="#テクスチャのあるマテリアルとテクスチャのないマテリアルで別のメッシュになる">テクスチャのあるマテリアルとテクスチャのないマテリアルで別のメッシュになる</a></li> <li><a href="#Emissionはアトラスに反映されない">Emissionはアトラスに反映されない</a></li> </ul> </li> <li><a href="#テクスチャに色設定を焼き付けBakeるエディタ拡張作った">テクスチャに色設定を焼き付け(Bake)るエディタ拡張作った</a></li> <li><a href="#仕上げ">仕上げ</a></li> <li><a href="#まとめ">まとめ</a></li> </ul> <div class="section"> <h3 id="マテリアルをStandardに揃える">マテリアルをStandardに揃える</h3> <p>インポート直後は全てのマテリアルにLegacy Diffuseのシェーダーが割り当ててあります。折角Unity5なんですからStandardにしましょう。Assetsフォルダ直下に、l3pファイル名と同じ名前のフォルダができており、そこにマテリアル、テクスチャ、メッシュが入っていますのでここのマテリアルをいじります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312192625.png" alt="f:id:dnasoftwares:20170312192625p:plain" title="f:id:dnasoftwares:20170312192625p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>Metalicのパラメタなどはお好きにどうぞ。<br /> また、「yellg」など「~~<b>lg</b>_uv_material」で終わる名称のマテリアルは本来自己発光のマテリアルです。こちらでも光らせましょう。<br /> AlbedoからEmissionに色をコピー(Emissionのスポイトを押してAlbedoの色を拾う)します。ちょっと明るさを1以上にして、カメラの<a class="keyword" href="http://d.hatena.ne.jp/keyword/HDR">HDR</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>を有効にしてBloomのイメージエフェクトを適当に盛ったらこんな感じ。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312192816.png" alt="f:id:dnasoftwares:20170312192816p:plain" title="f:id:dnasoftwares:20170312192816p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3 id="テクスチャの濃度設定とか密度設定とかは無視されている">テクスチャの濃度設定とか密度設定とかは無視されている</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3ではテクスチャの濃度設定ができました。薄く模様を張るといった表現ができたんですが、Unityへのインポート時にこのパラメタは無視されてしまいます。<br /> 気になる場合は自力でテクスチャを薄くしてしまいましょう。テクスチャを複製して適当なエディタで白を重ねればそれっぽくなります。<br /> このモデルでも濃度設定を使っていたので自分で白を載せました。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312193745.png" alt="f:id:dnasoftwares:20170312193745p:plain" title="f:id:dnasoftwares:20170312193745p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>同様に密度設定も無視されますのでこちらも注意が必要です。</p> </div> <div class="section"> <h3 id="メッシュを統合する">メッシュを統合する</h3> <p>1モデルのために10以上のGameObjectがぶら下がる構図ははあまり良い感じがしません。メッシュがまとまっていないことでメッシュコライダーの作成ができないなどの弊害もあります。<br /> メッシュをBakeできるツールで統合してしまうのがいいでしょう。ついでにマテリアルも統合してしまえばSetPassの低減につながります。<br /> ウチではPro Draw Call Optimizerを使っています。</p><p><iframe src="https://api.assetstore.unity3d.com/affiliate/embed/package/54360/widget-wide?aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe></p><p>ローポリな表現がお好きで、テクスチャがいらない場合はMesh Materializerでも面白いです。マテリアルの色設定やテクスチャの色を元に頂点カラーに変換しつつメッシュを統合してくれます。<br /> ついでにスムージングも切ったり入れたり自由自在。往年の「生ポリゴン(もはや死語ですね……)」な感じが出せます。</p><p><iframe src="https://api.assetstore.unity3d.com/affiliate/embed/package/28583/widget-wide?aid=1011lGom" style="width:600px; height:130px; border:0px;"></iframe></p><p>さてここではPro Draw Call Optimizer(以下<a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>)で進める事にします。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>のウィンドウで当該GameObject以下を指定します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312195514.png" alt="f:id:dnasoftwares:20170312195514p:plain" title="f:id:dnasoftwares:20170312195514p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>また、Settingsで「Generate Prefabs for objects」を選ばないとモデルデータをシーンの外に出すことができません(メッシュデータがファイルとして生成されない)。使い回すモデルの場合はチェックを忘れずに。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312195626.png" alt="f:id:dnasoftwares:20170312195626p:plain" title="f:id:dnasoftwares:20170312195626p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>あと、<b>モデルが原点以外にある場合はかならず一度原点に戻してください。</b>そのままだと原点がズレます。<br /> ということで諸々設定してBakeしたら統合済みのモデルに置き換わり……</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170312/20170312200305.png" alt="f:id:dnasoftwares:20170312200305p:plain" title="f:id:dnasoftwares:20170312200305p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>……なんでか色設定が飛んでしまいました。どういうことでしょうか。ほかにもいくつか怪しい点があります。</p> </div> <div class="section"> <h3 id="Pro-Draw-Call-Optimizerの罠">Pro Draw Call Optimizerの罠</h3> <div class="section"> <h4 id="テクスチャと色設定を合成できない">テクスチャと色設定を合成できない</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>は可能な限りマテリアルをシェーダ単位で結合し、テクスチャも1枚のアトラスに統合してくれます。テクスチャを使わない単色のマテリアルは単色テクスチャを生成してアトラス内に統合してくれるのですが、ここに罠がありまして、「テクスチャと色指定が同時に設定されている場合はテクスチャのみ考慮して色設定が破棄される」という仕様になっております。本来ならテクスチャと色設定を合成(普通は乗算ですね)して着色済みテクスチャを作ってほしいところです。</p><p>これだけは手作業でどうにかするのはだいぶ厳しい、ということでエディタ拡張を作りました。他の罠にも対処してから作業してほしいので、エディタ拡張の話は後に回します。</p> </div> <div class="section"> <h4 id="テクスチャのあるマテリアルとテクスチャのないマテリアルで別のメッシュになる">テクスチャのあるマテリアルとテクスチャのないマテリアルで別のメッシュになる</h4> <p>これはつまりテクスチャのないマテリアルにもテクスチャを設定してしまえばいいので、白の適当な(16x16ぐらいでいいはず)テクスチャを用意してテクスチャのないマテリアルのAlbedoに割り付けます。</p> </div> <div class="section"> <h4 id="Emissionはアトラスに反映されない">Emissionはアトラスに反映されない</h4> <p>これは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>の仕様と思われるので、一度Bakeした後、アトラス画像をいじってEmission用のアトラスを作る方向でなんとかします。</p> </div> </div> <div class="section"> <h3 id="テクスチャに色設定を焼き付けBakeるエディタ拡張作った">テクスチャに色設定を焼き付け(Bake)るエディタ拡張作った</h3> <p>さて、「テクスチャと色設定を合成できない」問題への対処としては「事前にテクスチャと色情報を合成して新しいテクスチャに置き換えておく」ということになるのは先ほど申し上げた通りですが、手動でどうにかするのはちょっと無理があります。そんなわけで、自動的に色を合成してくれるエディタ拡張を自作しました。当該エディタ拡張は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Github">Github</a>で公開しております。</p><p><iframe src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fdnasoftwares%2Fmaterial-texture-baker%2F" title="dnasoftwares/material-texture-baker" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/dnasoftwares/material-texture-baker/">github.com</a></cite></p><p>普通は<a href="https://github.com/dnasoftwares/material-texture-baker/raw/master/MaterialTextureBaker_v100.unitypackage">UnityPackage&#x3060;&#x3051;&#x30C0;&#x30A6;&#x30F3;&#x30ED;&#x30FC;&#x30C9;</a>すれば大丈夫。</p><p>プロジェクトにimportするとWindowメニューに項目が増えます。「D.N.A. Softwares」→「Material Texture Baker」を選びます。</p><p>使い方は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>に近い感じに揃えました。(あくまで感じなのでデザインとかは違いますが……)</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170315/20170315023540.png" alt="f:id:dnasoftwares:20170315023540p:plain" title="f:id:dnasoftwares:20170315023540p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>同じように親のGameObjectを選択し「+Children」を押すと子のメッシュを全選択してくれます。<br /> あとは「Bake!」を押せば勝手にテクスチャに色が合成され、メッシュのマテリアルが更新されます。</p><p>処理前と処理後でテクスチャと色の設定が変わっていることを確認してください。<br /> (Bake!の下のリストを展開するとテクスチャと色のペアを確認できます)</p><p>Bake前↓<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170315/20170315023548.png" alt="f:id:dnasoftwares:20170315023548p:plain" title="f:id:dnasoftwares:20170315023548p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>Bake後↓<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170315/20170315023551.png" alt="f:id:dnasoftwares:20170315023551p:plain" title="f:id:dnasoftwares:20170315023551p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>これで準備<a class="keyword" href="http://d.hatena.ne.jp/keyword/OK">OK</a>、今度こそ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>でメッシュをまとめます。こんどは綺麗に色も残るはずです。</p> </div> <div class="section"> <h3 id="仕上げ">仕上げ</h3> <p>あとはEmission用のAtlasを作りましょう。Atlasになったテクスチャを複製して、光らせたい色以外を真っ黒に塗りつぶすだけですので簡単。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Pdc">Pdc</a>の出力一式はAssetフォルダ直下の「<i>(編集中のシーン名)</i>-atlas」のフォルダに保存されています。<br /> Albedo用のAtlasでは逆にEmissionに該当する部分を黒にします。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170315/20170315030458.jpg" alt="f:id:dnasoftwares:20170315030458j:plain" title="f:id:dnasoftwares:20170315030458j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>あと、Metalic,Smoothnessの設定が飛んでしまうのでこちらも要再調整。<br /> 場合によってはAlbedoのAtlasをさらにコピーして他の要素用のマップを作ってもいいでしょう。</p><p>これでできあがり。<br /> 両方でSetpass Callsを比べると59→27と32減。めでたしめでたし。</p><p>Before↓<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170315/20170315024450.png" alt="f:id:dnasoftwares:20170315024450p:plain" title="f:id:dnasoftwares:20170315024450p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>After↓<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170315/20170315024457.png" alt="f:id:dnasoftwares:20170315024457p:plain" title="f:id:dnasoftwares:20170315024457p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3 id="まとめ">まとめ</h3> <p>というわけでちょっと手はかかりますが、工夫次第で十分実用できるモデルが作れます。<br /> 最適化作業はちょっとややこしいですが、慣れれば<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EB%A1%BC%A5%C1%A5%F3%A5%EF%A1%BC%A5%AF">ルーチンワーク</a>です。(マテリアルに白テクスチャをセットするあたりもエディタ拡張で自動化できますね……)<br /> ライセンス的にも個人制作にはかなり優しいのが嬉しいです。<br /> また、パーツデータは自作してインポートすることもできます。パーツの一部だけ自分で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>して、あとは既存パーツと合わせたりすればかゆいところにも手が届くでしょう。ローポリ系アセットと組み合わせても違和感なく繋がりますので是非活用してください。</p> </div> dnasoftwares DoGA-L3で「取り急ぎ」かっこいいモデルを作ってUnityで動かして遊ぶ hatenablog://entry/10328749687211092663 2017-02-06T01:42:49+09:00 2017-02-06T21:01:57+09:00 !ライセンスについてDoGA様から返答がありましたので追記しております。(2/6更新)気がついたら前回の投稿から1年が経とうとしていて焦っているDNAです。Twitterでは18年前に作ったゲームをリメイクできないかなーとちょこちょこと進捗(してない)動画を上げていたりしましたが、その中で愛用しているDoGAさんの「PLAY Animation Importer for Unity」が昨年12月に更新されて、大変使い勝手がよくなったので、というか私の要望に応えてバージョンアップしてくださいましたので、お礼になるかわからないのですがDoGA-L3を改めて紹介してみたいと思います。 DoGA-L3… <p><b><span style="color: #ffff00">!ライセンスについて<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>様から返答がありましたので追記しております。(2/6更新)</span></b></p><p>気がついたら前回の投稿から1年が経とうとしていて焦っているDNAです。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>では<a href="http://www.vector.co.jp/soft/win95/game/se211809.html">18&#x5E74;&#x524D;&#x306B;&#x4F5C;&#x3063;&#x305F;&#x30B2;&#x30FC;&#x30E0;</a>をリメイクできないかなーとちょこちょこと進捗(してない)動画を上げていたりしましたが、その中で愛用している<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>さんの「<a href="http://doga.jp/PLAY/unity-plugin-jp/">PLAY Animation Importer for Unity</a>」が昨年12月に更新されて、大変使い勝手がよくなったので、というか私の要望に応えてバージョンアップしてくださいましたので、お礼になるかわからないのですが<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3を改めて紹介してみたいと思います。</p> <ul class="table-of-contents"> <li><a href="#DoGA-L3とは">DoGA-L3とは</a></li> <li><a href="#試す">試す</a></li> <li><a href="#とりあえずモデル作る">とりあえずモデル作る</a></li> <li><a href="#Unityのプロジェクトを仕込む">Unityのプロジェクトを仕込む</a></li> <li><a href="#インポートしてみる">インポートしてみる</a></li> <li><a href="#使う">使う</a><ul> <li><a href="#メッシュがマテリアル単位で別のGameObjectに分割されている">メッシュがマテリアル単位で別のGameObjectに分割されている</a></li> <li><a href="#謎の回転と拡大がついている">謎の回転と拡大がついている</a></li> </ul> </li> </ul> <div class="section"> <h3 id="DoGA-L3とは"><a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3とは</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3は<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-Lシリーズという<a class="keyword" href="http://d.hatena.ne.jp/keyword/CG%A5%A2%A5%CB%A5%E1%A1%BC%A5%B7%A5%E7%A5%F3">CGアニメーション</a>入門ツールの一つです。<br /> Webサイト(<a href="http://doga.jp/2010/programs/dogal/index.html">CGA&#x5165;&#x9580;&#x30AD;&#x30C3;&#x30C8; DOGA-L &#x30B7;&#x30EA;&#x30FC;&#x30BA;</a>)の紹介によれば、</p> <blockquote> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/DOGA">DOGA</a>-Lシリーズは、まったく初心者の方でも、 手軽に<a class="keyword" href="http://d.hatena.ne.jp/keyword/CG%A5%A2%A5%CB%A5%E1%A1%BC%A5%B7%A5%E7%A5%F3">CGアニメーション</a> (以下CGAに略す) の楽しさを体験していただくことを目的に開発された、CGA入門システムです。</p> </blockquote> <p>……だそうです。実際機能もかなり絞られていますが、3DCGアニメを制作するのに最低限必要な機能は揃っています。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002223.gif" alt="f:id:dnasoftwares:20170129002223g:plain" title="f:id:dnasoftwares:20170129002223g:plain" class="hatena-fotolife" itemprop="image"></span><br /> たくさんある半完成のパーツを組み合わせてモデルを作り……</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002318.png" alt="f:id:dnasoftwares:20170129002318p:plain" title="f:id:dnasoftwares:20170129002318p:plain" class="hatena-fotolife" itemprop="image"></span><br /> モデルを配置してシーンやアニメーションを作ります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002404.jpg" alt="f:id:dnasoftwares:20170129002404j:plain" title="f:id:dnasoftwares:20170129002404j:plain" class="hatena-fotolife" itemprop="image"></span><br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/Direct3D">Direct3D</a>でリアルタイムプレビューできるほか専用レンダラーで高画質出力もできます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002759.png" alt="f:id:dnasoftwares:20170129002759p:plain:w300" title="f:id:dnasoftwares:20170129002759p:plain:w300" class="hatena-fotolife" style="width:300px" itemprop="image"></span> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002935.png" alt="f:id:dnasoftwares:20170129002935p:plain:w300" title="f:id:dnasoftwares:20170129002935p:plain:w300" class="hatena-fotolife" style="width:300px" itemprop="image"></span> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002937.png" alt="f:id:dnasoftwares:20170129002937p:plain:w300" title="f:id:dnasoftwares:20170129002937p:plain:w300" class="hatena-fotolife" style="width:300px" itemprop="image"></span> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129002938.png" alt="f:id:dnasoftwares:20170129002938p:plain:w300" title="f:id:dnasoftwares:20170129002938p:plain:w300" class="hatena-fotolife" style="width:300px" itemprop="image"></span><br /> パーツはカテゴリごとに整理されており総数はそこそこあります。発想次第で色々作れます。<br /> 元々15年以上前のツールなだけに、そのころのスペックに合わせたポリゴン予算でパーツが作られています。どのパーツも良い感じのローポリ感があります。<br /> プラモデルで既製品のパーツを借りてディテールアップしたり別のモデルを作ることをキットバッシング(Kitbashing)というそうですが、まさにキットバッシングを3DCGでやるツールといえるでしょう。</p><p>で、このモデルを作る部分(パーツ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%BB%A5%F3%A5%D6%A5%E9">アセンブラ</a>)のデータをUnityにインポートできるようにするのが<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>謹製のPLAY Animation Importer for Unityになります。</p><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3他、系列の各製品のライセンスを購入すると各製品のパーツ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%BB%A5%F3%A5%D6%A5%E9">アセンブラ</a>で製作したモデルデータをUnityにインポートできるようになります。ちょっとクセのある出力になっていますが、他のエディタ拡張アセットと組み合わせたりなんだりで綺麗にまとめることができますので、併せて紹介したいと思います。</p><p>本稿では画面写真や操作は<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3を前提に進めます。(DNAが持ってるのが<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3のライセンスなので……)本当は<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L1,L2,L3とステップアップする設計なんですが、Unityが扱える方なら<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3もすぐ慣れるでしょう。詳しい説明はしませんので<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>のサイト等を見ながら進めてください。分からないと思った場合はより初心者向けの<a href="http://cganime.jp/totekan/">&#x3068;&#x3066;&#x304B;&#x3093;CG</a>で進めてもいいかもしれません。いずれにしろ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%A2%A5%A6%A5%A7%A5%A2">シェアウェア</a>なので試用して問題なければライセンスを購入してください。<br /> なお、通常のライセンスは非商用利用に限っての許諾となっており、商用、対価を取る用途には使えません。<br /> ただし、<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3に限り商用利用可能なプロフェッショナルライセンスが設定されているほか、<span style="color: #ffff00"><u>同人ゲームにおいてはパブリッシャーを通さない範囲での頒布は通常ライセンスの購入で許諾されるとのことです。</u></span>パブリッシャーを通しての販売を行う場合には別途お問い合わせくださいとのことでした。(<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>様ご返答ありがとうございました。)<br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>の掲示板ではインポーターを使ってインポートしたモデルの使用について<a href="http://doga.jp/tkbbs/tkbbs.cgi?bbs=doga&number=10163">&#x898B;&#x89E3;&#x304C;&#x793A;&#x3055;&#x308C;&#x3066;&#x3044;&#x307E;&#x3059;&#x3002;</a></p> <blockquote> <p><a href="http://doga.jp/2010/reading/diary/201407.html#20140730kama">http://doga.jp/2010/reading/diary/201407.html#20140730kama</a><br /> > この<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>によって、L3等の形状データをUnityで自作の<br /> > ゲームに利用するだけでなく、L3等で作ったデータを<br /> > Unity アセットストアで販売することが可能になります。</p><p>日記でかまださんがこう書いている通り、<br /> 商用ライセンスは不要でUnityアセットストアでの販売が可能です。</p><p>これまでも L シリーズのデータを使って同人ソフトの開発・販売は認めてきましたがその方針を維持しています。<br /> (明らかに同人の規模を超える、また商業ベースだと商用ライセンスの導入をお願いしています)</p> </blockquote> <p>ということで取り急ぎ作ったモデルに愛着が沸いたらそのまま完成まで使いましょう。</p> </div> <div class="section"> <h3 id="試す">試す</h3> <p>前述の通りUnityへのインポートの条件として製品の購入が必要ですが、購入前にとりあえずインストールして手触りを確かめるのがいいでしょう。<br /> <a href="http://doga.jp/2010/programs/dogal/index.html">CGA&#x5165;&#x9580;&#x30AD;&#x30C3;&#x30C8; DOGA-L &#x30B7;&#x30EA;&#x30FC;&#x30BA;</a>のページの「<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3」内「ダウンロード」でダウンロードページに進みます。お目当ては<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>部分であり、動画を出力するわけではないので「新規インストール 最小限」のEXE一つで大丈夫。<br /> バージョンが2008とかなっててちょっとびっくりしますが、Windows10でも特に問題なく動作するはずです。何か挙動不審があったら<a href="http://doga.jp/2010/programs/beta/dogal3/index.html#l3download">DOGA-L3 &beta;</a>から2010年のβ版も入れるといいかもしれません。</p><p>動作に問題が無ければライセンスを購入して登録してください。</p> </div> <div class="section"> <h3 id="とりあえずモデル作る">とりあえずモデル作る</h3> <p>というわけでモデルを作ります。パーツ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%BB%A5%F3%A5%D6%A5%E9">アセンブラ</a>でもっそもっそパーツを盛りましょう。色・テクスチャもインポーターは読み取ってくれるので気兼ねなく。ただし表面材質の設定は追従しない(すべてLegacy Diffuseでインポートされる)ので、ここはインポート後にどうにかします(次の記事で書きます)。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129194802.png" alt="f:id:dnasoftwares:20170129194802p:plain" title="f:id:dnasoftwares:20170129194802p:plain" class="hatena-fotolife" itemprop="image"></span><br /> ここでは弊サークルで以前作ったモデルを使います。上のGIFアニメで出てるやつですね。いまちびちびリメイクしようとしてるの自機(のデータがなくなったので、当時の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%C0%A5%EA%A5%F3%A5%B0">レンダリング</a>画像を見て再度作ったもの)です。ちょうどUnityインポートで引っかかる色々な要素が混ざってるので好適かなと。</p><p>モデルの保存時など特別なことをする必要はなく、普通に保存して問題ありません。<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>のデータファイルの場所は、標準では「(<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3のインストールフォルダ)\data」です。ファイル<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BB%A5%EC%A5%AF%A5%BF">セレクタ</a>ではどのフォルダでも指定できますので、たとえばUnityプロジェクトのフォルダにしてまとめてバージョン管理下に置くことも一応できます。</p> </div> <div class="section"> <h3 id="Unityのプロジェクトを仕込む">Unityのプロジェクトを仕込む</h3> <p>ここでは新規のプロジェクトを作ります。もちろんインポート先のプロジェクトがすでにあるならそっちを開いていいでしょう。<br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>の<a href="http://doga.jp/PLAY/unity-plugin-jp/">PLAY Animation Importer for Unity</a>から、最新版のunitypackage(20161223)をダウンロードしてインポートします。<br /> <a href="http://u3d.as/a11">Asset Store&#x306B;&#x3082;&#x3042;&#x308B;</a>のですが、こちらはまだバージョンが上がってないようです。</p><p>インポートと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>が終わったら「Window」メニューに「PLAY Animation (TotekanCG) Importer」が増えていることと、それを選んだら開くインポーターのウィンドウで<br /> 「Installed <a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a> Products」の欄に登録したツールが表示されていることを確認してください。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129201026.png" alt="f:id:dnasoftwares:20170129201026p:plain" title="f:id:dnasoftwares:20170129201026p:plain" class="hatena-fotolife" itemprop="image"></span><br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170129/20170129201139.png" alt="f:id:dnasoftwares:20170129201139p:plain" title="f:id:dnasoftwares:20170129201139p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3 id="インポートしてみる">インポートしてみる</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%AF%A5%B9%A5%D7%A5%ED%A1%BC%A5%E9">エクスプローラ</a>で<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>のデータフォルダを開けます。中にある拡張子.l3pのファイルをUnityのインポーターのウィンドウにドラッグ&ドロvップ。「Import tasks」リストにファイルが追加されるはずです。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170206/20170206004936.png" alt="f:id:dnasoftwares:20170206004936p:plain" title="f:id:dnasoftwares:20170206004936p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Scaleの値がやたら小さかったりするのが気になるかもしれませんが、あまり気にせず「Import All」でインポートを開始してしまいましょう。材質の多いモデルだと10~20秒程度時間がかかります。オブジェクトはシーンの原点に生成されます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20170206/20170206005008.png" alt="f:id:dnasoftwares:20170206005008p:plain" title="f:id:dnasoftwares:20170206005008p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>また、モデルのPrefabがAssetsフォルダの直下に生成されます。ただし、このモデルには極めて変なクセがついています。後述。<br /> <br /> </p> </div> <div class="section"> <h3 id="使う">使う</h3> <p>折角飛行機のモデルなんですから飛ばしてみましょう。毎度おなじみテラシュールブログにちょうどいい記事がありますので参考にします。<br /> <iframe src="http://tsubakit1.hateblo.jp/embed/2016/12/30/233000" title="【Unity】サクっと飛行機の挙動を追加して飛び回る - テラシュールブログ" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://tsubakit1.hateblo.jp/entry/2016/12/30/233000">tsubakit1.hateblo.jp</a></cite></p><p>基本的にはこの記事通りに進行すればモデルを空に飛ばせます。ただし上の記事通りに作業を進めようとすると数点引っかかると思います。</p> <div class="section"> <h4 id="メッシュがマテリアル単位で別のGameObjectに分割されている">メッシュがマテリアル単位で別のGameObjectに分割されている</h4> <p>何かしらのメッシュ統合アセットを使う必要があります。メッシュコライダの作成がうまくいかないと思いますのでとりあえず近似のBoxやCylinderなどを組み合わせましょう。</p> </div> <div class="section"> <h4 id="謎の回転と拡大がついている">謎の回転と拡大がついている</h4> <p>角度は親も子も全部0にすれば元通り、拡大も親子とも1倍に戻せば元通りになります。割と謎。</p><p>両方設定して適当に床を置いてモデル位置を調整、カメラは手抜きですがとりあえず飛行機の子にして勝手に追従させましょう。<br /> 背景が寂しいのでMaruchuさんのTekitouCityGeneratorを使います。<br /> <iframe src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fmany.chu.jp%2FUnity%2FTekitouCityGenerator%2F" title="UnityTIPS : Tekitou City Generator / 適当な街並みジェネレータ" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://many.chu.jp/Unity/TekitouCityGenerator/">many.chu.jp</a></cite></p><p>パラメータをわちゃわちゃ調整しつつできあがったのがこちら。<br /> <iframe width="420" height="315" frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/3mH3akk45SQ"></iframe><br><a href="https://youtube.com/watch?v=3mH3akk45SQ">とてかんCGインポーターで作ったモデルを飛ばす(1)</a></p><p>とりあえずとしてはこれでいいんですが、もうちょっと色々できますので以下次号。</p> </div> </div> dnasoftwares Projectorでデカールもどき hatenablog://entry/10328537792364242684 2016-02-21T18:53:11+09:00 2016-02-23T10:49:39+09:00 突然地味なUnityネタが沸いてくるD.N.A.のおぼえがきですいかがお過ごしでしょうか。※今回の記事では利用例のテクスチャにハムコロ様のSTG素材を使用しています。 いきさつ 縦STGの試作を延々続けておりますが、地味に困ってるのが「Terrainの上の地上物の破壊跡の処理」です。 板ポリゴンにテクスチャを張っただけでは、Terrainにめり込んだりして見た目がよろしくありません。そういうときはDecal(オブジェクトの表面に沿って別のテクスチャを張る)を使うのですが、 Decalを実現できるアセットというといくつかあるものの…… Decal System Pro 95ドル Simple d… <p>突然地味なUnityネタが沸いてくるD.N.A.のおぼえがきですいかがお過ごしでしょうか。</p><p>※今回の記事では利用例のテクスチャに<a href="http://homepage2.nifty.com/hamcorossam/">&#x30CF;&#x30E0;&#x30B3;&#x30ED;&#x69D8;</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>素材を使用しています。</p> <div class="section"> <h3>いきさつ</h3> <p>縦<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>の試作を延々続けておりますが、地味に困ってるのが「Terrainの上の地上物の破壊跡の処理」です。<br /> 板ポリゴンにテクスチャを張っただけでは、Terrainにめり込んだりして見た目がよろしくありません。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20160221/20160221182338.png" alt="f:id:dnasoftwares:20160221182338p:plain" title="f:id:dnasoftwares:20160221182338p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>そういうときはDecal(オブジェクトの表面に沿って別のテクスチャを張る)を使うのですが、<br /> Decalを実現できるアセットというといくつかあるものの……</p> <ul> <li><a href="http://u3d.as/5eb">Decal System Pro</a> 95ドル</li> <li><a href="http://u3d.as/6gt">Simple decal system</a> 無料だがTerrainに追従できない</li> </ul><p>と、やることに対して$がかかりすぎる感じだったり微妙な物足りなさがあったり……と悩んでいたのですが、<br /> 良く考えればUnity標準機能のProjectorがあるじゃない、ということでProjectorでなんとかしてみようと試みたのでした。</p> </div> <div class="section"> <h3>シェーダーいじり</h3> <p>Standard Assetで入手できるProjector用のシェーダーは、</p> <ul> <li>Multiply</li> <li>Light</li> </ul><p>の2つだけです。MultiplyもLightもテクスチャの色をそのまま貼り付けられないので、これらを元にテクスチャの色をブレンドせず貼り付けられるシェーダーを書きます。以下、ProjectorLight.shaderを元に改造したコード。</p> <div class="section"> <h4>リスト ProjectorDecal.shader</h4> <pre class="code lang-cpp" data-lang="cpp" data-unlink>Shader <span class="synConstant">&quot;Projector/Decal&quot;</span> { Properties { _Color(<span class="synConstant">&quot;Main Color&quot;</span>, Color) = (<span class="synConstant">1</span>,<span class="synConstant">1</span>,<span class="synConstant">1</span>,<span class="synConstant">1</span>) _ShadowTex (<span class="synConstant">&quot;Cookie&quot;</span>, 2D) = <span class="synConstant">&quot;gray&quot;</span> {} } Subshader { Tags {<span class="synConstant">&quot;Queue&quot;</span>=<span class="synConstant">&quot;Transparent&quot;</span>} Pass { ZWrite Off ColorMask RGB Blend SrcAlpha OneMinusSrcAlpha <span class="synComment">//ブレンドを通常のアルファブレンドに</span> Offset -<span class="synConstant">1</span>, -<span class="synConstant">1</span> CGPROGRAM <span class="synPreProc"> #pragma vertex vert</span> <span class="synPreProc"> #pragma fragment frag</span> <span class="synPreProc"> #pragma multi_compile_fog</span> <span class="synPreProc"> #include </span><span class="synConstant">&quot;UnityCG.cginc&quot;</span> <span class="synType">struct</span> v2f { float4 uvShadow : TEXCOORD0; UNITY_FOG_COORDS(<span class="synConstant">2</span>) float4 pos : SV_POSITION; }; float4x4 _Projector; float4x4 _ProjectorClip; v2f vert (float4 vertex : POSITION) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, vertex); o.uvShadow = mul (_Projector, vertex); UNITY_TRANSFER_FOG(o,o.pos); <span class="synStatement">return</span> o; } fixed4 _Color; sampler2D _ShadowTex; fixed4 frag (v2f i) : SV_Target { fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow)); texS.rgb *= _Color.rgb; <span class="synComment">// texS.a = 1.0-texS.a; //デカールの場合はアルファ値はそのままでいいので削除</span> fixed4 res = texS; UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(<span class="synConstant">1</span>,<span class="synConstant">1</span>,<span class="synConstant">1</span>,<span class="synConstant">1</span>)); <span class="synStatement">return</span> res; } ENDCG } } } </pre><p>いじったのは</p> <ul> <li>FallOffの設定はいらないのでパラメータごとバッサリ削除。</li> <li>Blendをアルファブレンドに。</li> <li>アルファ値を反転する処理がいらないので削除(このままだと透明と不透明が反転してしまう)</li> </ul><p>の3点。これでうまくいきました。</p> </div> </div> <div class="section"> <h3>配置してみる</h3> <p>実際にシーンに置いてみます。Projectorを配置して地面に向け、下記のようにプロパティを設定。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20160221/20160221182426.png" alt="f:id:dnasoftwares:20160221182426p:plain" title="f:id:dnasoftwares:20160221182426p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>Orthographicを有効にして地面との距離によって<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%AB%A1%BC%A5%EB">デカール</a>が変化しないようにします。Materialには先ほどのシェーダを指定したマテリアルを用意して割り当て。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Cookie">Cookie</a>として破壊跡の絵を用意。Ignore Layersで背景以外全部を指定し変なところに跡がつかないように。テクスチャの方もWrap Modeを「<a class="keyword" href="http://d.hatena.ne.jp/keyword/Clamp">Clamp</a>」にするのを忘れずに。</p><p>実際にはこんな感じになります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20160221/20160221182953.png" alt="f:id:dnasoftwares:20160221182953p:plain" title="f:id:dnasoftwares:20160221182953p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>ちゃんとTerrainに沿っていることがわかります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20160221/20160221183002.png" alt="f:id:dnasoftwares:20160221183002p:plain" title="f:id:dnasoftwares:20160221183002p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>まとめ</h3> <p>簡易的な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%AB%A1%BC%A5%EB">デカール</a>表現としてはこれでいいでしょう。ただ、板ポリならShader次第でなんでもありだった凝った表現ができないのと、ライティングを一切考慮していないままなので、当然ライティングに一切追従せず、使いどころは極めて限定されます。やっておいてなんですが基本は板ポリでしのぎつつ、必要なところだけProjectorにするといった方がいいでしょう。</p><p>あと、誰かTerrainにDecal張れるアセットご存じでしたら教えてください。(Asset Storeの説明文だとそのへんがわからないヤツが多くて突撃しづらい……)</p> </div> dnasoftwares Arbor: State Diagram Editorを試す - 縦STGの敵機を作ってみる hatenablog://entry/6653458415120170215 2015-09-05T14:22:55+09:00 2015-09-05T14:30:10+09:00 ケットシーウェアさんのArbor: State Diagram Editorというアセットを試食してみたのでその記録。 前置き Unityで2.5D縦STGを作ろうという実験を半年以上超スローペースで続けているのですが、以前からずーっと困っているのが敵キャラの挙動です。単純な地上敵ならパス+スクリプトで弾を適当に撃つ、ぐらいで済むわけですが、中型機やボスキャラのように手の込んだものが必要となるとスクリプト直書きはちょっときついし増産にも向かない、ということで、ビジュアルスクリプティングでどうにかならんものか、とアレコレしていました。Arborの前にはド定番のPlayMakerでいくつか作ってみ… <p><a href="http://caitsithware.com/wordpress/">&#x30B1;&#x30C3;&#x30C8;&#x30B7;&#x30FC;&#x30A6;&#x30A7;&#x30A2;</a>さんの<a href="http://www.assetstore.unity3d.com/#!/content/17883">Arbor: State Diagram Editor</a>というアセットを試食してみたのでその記録。</p> <div class="section"> <h3>前置き</h3> <p>Unityで2.5D縦<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>を作ろうという実験を半年以上超スローペースで続けているのですが、以前からずーっと困っているのが敵キャラの挙動です。単純な地上敵ならパス+<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>で弾を適当に撃つ、ぐらいで済むわけですが、中型機やボスキャラのように手の込んだものが必要となると<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>直書きはちょっときついし増産にも向かない、ということで、ビジュアル<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C6%A5%A3%A5%F3%A5%B0">スクリプティング</a>でどうにかならんものか、とアレコレしていました。</p><p>Arborの前にはド定番の<a href="https://www.assetstore.unity3d.com/jp/#!/content/368">PlayMaker</a>でいくつか作ってみたのですが、</p> <ul> <li>ステートにぶら下げるアクションがデフォルトで凄く揃ってる</li> <li>アクションのパラメータを変数で与えられるので外部パラメタの導入もしやすい</li> </ul><p>その一方で、</p> <ul> <li>全部アクションで書いちゃうと冗長になって後から何やってたのか読めなく</li> <li>ありもののアクションの組み合わせばかりだとステート遷移が増えがちで見通しがドンドン悪くなる</li> <li>かといって新規にアクションを作るのも面倒(独自のお作法が多い)</li> </ul><p>ということでちょっと使いにくいと感じていたところでした。</p><p>ちょうどそこにキャンペーンでArborが試せるということで、試食してみた次第です。</p><p>……本当は8月中に上げるはずだったんですが……</p> </div> <div class="section"> <h3>導入</h3> <p>アセットストアで購入してインポート。難しい事はありませんが、更新時は必ずArborのフォルダを削除してからインポートするようにと指示があります。<br /> また、1.7.4での話ですが<a href="https://www.assetstore.unity3d.com/jp/#!/content/16888">Pro Draw call Optimizer Light</a>と一部クラスが名前衝突していて<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>が通らなくなっていました。<br /> Pro Draw call Optimizer Lightを削除すれば問題なし。</p> </div> <div class="section"> <h3>試食</h3> <div class="section"> <h4>下ごしらえ</h4> <p>まず敵機を用意します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20150905/20150905112240.png" alt="f:id:dnasoftwares:20150905112240p:plain" title="f:id:dnasoftwares:20150905112240p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>どこにでもある中型機です。<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3で組み立ててPLAY Animation Importer for Unityでインポートしました。<iframe src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fdoga.jp%2FPLAY%2Funity-plugin-jp%2F" title="CGアニメソフト とてかんCG PLAY Animation Importer for Unity (日本語)" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"><a href="http://doga.jp/PLAY/unity-plugin-jp/">CGアニメソフト とてかんCG PLAY Animation Importer for Unity (日本語)</a></iframe><cite class="hatena-citation"><a href="http://doga.jp/PLAY/unity-plugin-jp/">doga.jp</a></cite></p><p>Unity5にまだ対応してないので、Unity4.6でインポート→スケール調整→まとめてunitypackageに→Unity5に持ち込み と作業します。</p><p>で、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>をペタペタと。オレオレ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>がありますが本筋ではないので下記説明程度で。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20150905/20150905112544.jpg" alt="f:id:dnasoftwares:20150905112544j:plain" title="f:id:dnasoftwares:20150905112544j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>敵機の基本的な動作は専用<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>に分離。Arborで動かすのはあくまで移動と攻撃だけに絞ります。</p> </div> <div class="section"> <h4>ステートマシンをつくる</h4> <p>よくある中型機として、出現→攻撃→逃走 の流れを作ります。今回は攻撃1種だけでは単純すぎるのでライフの残量をみて発狂フェイズに移行するようにしました。<br /> 先に完成図を挙げます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20150905/20150905125129.jpg" alt="f:id:dnasoftwares:20150905125129j:plain" title="f:id:dnasoftwares:20150905125129j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>「開始ステート」になっている左上の状態からスタートして、コード内で状態遷移が発生したら矢印の先に進みます。<br /> ステートごとに1つ以上の「挙動」を追加できます。挙動1つ=MonoBehavior1つ みたいなノリです。実際はStateBehaviorというクラスの派生クラスになります。<br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の作成は普通のコード作成同様にプロジェクトビューで。プロジェクトビューの右クリックメニュー、「Create」の内側に「Arbor」が増えており、そこからStateBehaviorの雛形を新規作成できます。<br /> 開始ステートはAppearとあるように出現処理です。EnemyMid2Appearというコードをひっつけています。<br /> 独自のコントローラを使ってるのでアレですが説明を入れてるのでそういう処理をしていると適当に理解いただければと思います。</p> <div class="section"> <h5>EnemyMid2Appear.cs</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; <span class="synStatement">using</span> Arbor; <span class="synStatement">using</span> DG.Tweening; <span class="synType">public</span> <span class="synType">class</span> EnemyMid2Appear : StateBehaviour { <span class="synComment">//次のステート(エディタで設定する)</span> <span class="synType">public</span> StateLink NextState; <span class="synComment">//コントローラへの参照</span> <span class="synType">private</span> EnemyController _ctrl; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start() { } <span class="synComment">// Use this for enter state</span> <span class="synType">public</span> <span class="synType">override</span> <span class="synType">void</span> OnStateBegin() { transform.eulerAngles=<span class="synStatement">new</span> Vector3(<span class="synConstant">0</span>,<span class="synConstant">0</span>,<span class="synConstant">0</span>); _ctrl = GetComponent&lt;EnemyController&gt;(); _ctrl.LocalVelocity = <span class="synStatement">new</span> Vector3(<span class="synConstant">0</span>, <span class="synConstant">0</span>, -<span class="synConstant">58.0f</span>); <span class="synComment">//この機体に画面下に向けて降りてくるベクトルを設定</span> StartCoroutine(StateThread()); <span class="synComment">//以下コルーチン任せ</span> } IEnumerator StateThread() { <span class="synStatement">while</span> (transform.localPosition.z&gt;=<span class="synConstant">80.0f</span>) <span class="synComment">//この機体が画面上1/4ぐらいのラインに下ってくるまで</span> <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForFixedUpdate(); <span class="synComment">//待機</span> Transition(NextState); <span class="synComment">// 次ステートへ遷移</span> } <span class="synComment">// Use this for exit state</span> <span class="synType">public</span> <span class="synType">override</span> <span class="synType">void</span> OnStateEnd() { } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update() { } } </pre><p>ジャンプ先のステートを格納するpublic StateLink NextState;と実際に遷移を実行するTransition(NextState);がキモかと。<br /> StateLinkの変数を定義するとArbor Editorにリンクとして表示され、次のステートへ<a class="keyword" href="http://d.hatena.ne.jp/keyword/D%26D">D&D</a>の操作で矢印を引けるようになります。</p><p>普通にStartCoroutineが呼べるのでかなり書きやすいです。</p><p>図で飛び先となっているLoopステートの挙動はこちら。</p> </div> <div class="section"> <h5>EnemyMid2Loop.cs</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; <span class="synStatement">using</span> Arbor; <span class="synStatement">using</span> DG.Tweening; <span class="synType">public</span> <span class="synType">class</span> EnemyMid2Loop : StateBehaviour { <span class="synType">public</span> StateLink GoMadState; <span class="synComment">//発狂モード突入時の飛び先(エディタで設定)</span> <span class="synType">private</span> EnemyController _ctrl; <span class="synComment">//コントローラへの参照</span> <span class="synType">public</span> GameObject[] Firepoint; <span class="synComment">//敵弾の発射地点を示すGameObject(エディタで設定)</span> <span class="synType">public</span> GameObject BulletObject; <span class="synComment">//敵弾として吐き出すGameObject(エディタで設定)</span> [Range(<span class="synConstant">0</span>,<span class="synConstant">100</span>)] <span class="synType">public</span> <span class="synType">float</span> GoMadLifePercent=<span class="synConstant">30.0f</span>; <span class="synComment">//発狂モードに突入するライフ%値(0~100)</span> <span class="synType">private</span> Coroutine _cFireController; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { } <span class="synComment">// Use this for enter state</span> <span class="synType">public</span> <span class="synType">override</span> <span class="synType">void</span> OnStateBegin() { _ctrl = GetComponent&lt;EnemyController&gt;(); DOTween.To(() =&gt; _ctrl.LocalVelocity.z, x =&gt; _ctrl.LocalVelocity.z = x, <span class="synConstant">0</span>, <span class="synConstant">1.0f</span>); <span class="synComment">//前ステートでかかっていたzベクトル(縦方向速度)を1秒かけて0にする</span> _cFireController = StartCoroutine(FireControllerThread()); <span class="synComment">//攻撃制御開始</span> } <span class="synType">private</span> IEnumerator FireControllerThread() { <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(<span class="synConstant">0.4f</span>); <span class="synComment">//最初の1セットの前に0.4秒待つ</span> <span class="synStatement">while</span>(<span class="synConstant">true</span>) { Transform player = GameEnvironment.GetNearestPlayer(transform.position); <span class="synComment">//自分から近い方のプレイヤーの場所を得る</span> <span class="synStatement">for</span> (<span class="synType">int</span> i=<span class="synConstant">0</span>;i&lt;<span class="synConstant">3</span>;i++) { <span class="synComment">//FirePointそれぞれから……</span> <span class="synStatement">foreach</span>(GameObject fp <span class="synStatement">in</span> Firepoint) { <span class="synComment">//プレイヤーに向けて3Way弾を撃つ</span> <span class="synType">float</span> angle = DNASoftwares.Unity.MathEx.AimProjected (fp.transform.position, player.position); <span class="synComment">//近い方のプレイヤーの角度を得る</span> _ctrl.CreateBulletAbsAngle(BulletObject, fp.transform.position, angle, <span class="synConstant">110</span>); _ctrl.CreateBulletAbsAngle(BulletObject, fp.transform.position, angle-<span class="synConstant">22.5f</span>, <span class="synConstant">110</span>); _ctrl.CreateBulletAbsAngle(BulletObject, fp.transform.position, angle+<span class="synConstant">22.5f</span>, <span class="synConstant">110</span>); } <span class="synStatement">for</span> (<span class="synType">int</span> j = <span class="synConstant">0</span>; j &lt; <span class="synConstant">8</span>; j++) <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForFixedUpdate(); <span class="synComment">//8フレームおき</span> } <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(<span class="synConstant">1.0f</span>); <span class="synComment">//1セット撃ったら1秒待つ</span> } } <span class="synComment">// Use this for exit state</span> <span class="synType">public</span> <span class="synType">override</span> <span class="synType">void</span> OnStateEnd() { <span class="synComment">//遷移時には今の攻撃スレッドを停止させる</span> StopAllCoroutines(); } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { <span class="synComment">//ライフがしきい値を超えたら(_ctrl,LifePercentは自分の残ライフを百分率で返す)</span> <span class="synStatement">if</span> (_ctrl.LifePercent &lt;= GoMadLifePercent) { <span class="synComment">//発狂ステートへ</span> Transition(GoMadState); } } <span class="synComment">// このステートの間動き続ける</span> <span class="synType">void</span> FixedUpdate() { <span class="synComment">//横方向にゆらゆら動く動作</span> _ctrl.LocalVelocity.x = Mathf.Sin(_ctrl.TimeElapsed/<span class="synConstant">2.0f</span>*Mathf.PI)*<span class="synConstant">10.0f</span>; } } </pre><p>FixedUpdateも書けば普通に動いてくれます。MonoBehaviorとまったく同じ挙動です。<br /> また、StateLinkに限らずpublic変数はインスペクタ同様に表示・編集できますし<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%C8%A5%EA%A5%D3%A5%E5%A1%BC%A5%C8">アトリビュート</a>もバッチリです。新しい事を覚える必要がほとんどないので取り組みやすいと思います。</p><p>発狂後の方はやってること自体は弾撃ってるだけなので本筋と外れてしまうので省略。</p> </div> </div> <div class="section"> <h4>常駐ステートで割り込みをかける</h4> <p>一定時間で逃げる、という処理は色々書き方もあるかと思いますが、今回は「常駐ステート」を使ってみることにしました。</p><p>図の右下の緑色のステートが「常駐ステート」で、このステートの挙動は文字通り他のステートと別に常駐して動作します。<br /> ここにタイマーを仕込んでおけば出現から一定時間(ここではSeconnds=12なので12秒)経過すると指定ステートに割り込みで遷移してくれるという手はずです。</p><p>あくまで今のステートに割り込む形ですので、常駐ステートの先も並列実行になるわけではありません。</p><p>逃げる挙動はAppearの逆でしかないので省略。</p> </div> </div> <div class="section"> <h3>動かしてみる</h3> <p>実際に上図ステートマシンを動かした様子がこちらのツイートにあります。<br /> オレンジ色のステートがそのとき動いているステートです。<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">一定ダメージで攻撃変化、常駐ステートで逃走時間管理するように <a href="https://twitter.com/hashtag/unity3d?src=hash">#unity3d</a> <a href="http://t.co/RH7CZxegND">pic.twitter.com/RH7CZxegND</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/638147718540464129">2015, 8月 31</a></blockquote><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script><br /> </p> </div> <div class="section"> <h3>所感</h3> <p>ということで非常に取り組みやすいなと思いました。デフォルトで付属してくる挙動<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の量はPlayMakerには遠く及びませんが、自力でコードが書けるなら大した問題ではありませんし、<br /> MonoBehaviorと同じノリでコードが書けますのでむしろ取り組みやすいと思います。<br /> ステートをまたいだ変数の扱いなどが悩むところですが、専用に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>を作ればなんとかなるかと思います。RequireComponent属性で紐付けるとかすれば確実でしょう。</p><p>今回は手抜きして状態遷移とアクションを全部1<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>に押し込んでしまいましたが、状態遷移に関わる<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>と、実際になにか行動を起こすための<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は分割すると使い回しが効いていいと思います。</p><p>あと申し上げたいこととしては、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>が全編動画になっているのが個人的にはツラいです。動画だと自分のペースで進められないのと、Arborの場合はエディタの説明やら常駐ステートの説明などが動画の中に埋まっているため引用とかしづらい……(この記事書くのに困った)<br /> ただリファレンスマニュアルは別途しっかりまとまっているので1回通してしまえば後の苦労は無いかと思います。</p><p>定価$95のPlayMakerと比べてArborなら定価$19.99とほぼ1/5のお値段で必要十分な機能が揃えられるので、自分でコードが書ける上で状態遷移の部分をスッキリさせたいという向きには十分にオススメできると思います。<br /> <br /> </p> <ul> <li>アセットストアのページへ:<a href="http://www.assetstore.unity3d.com/#!/content/17883">Arbor by caitsithware</a></li> <li>作者:<a href="http://caitsithware.com/wordpress/">&#x30B1;&#x30C3;&#x30C8;&#x30B7;&#x30FC;&#x30A6;&#x30A7;&#x30A2;</a></li> <li>サポートページ:<a href="http://arbor.caitsithware.com/">http://arbor.caitsithware.com/</a></li> </ul> </div> dnasoftwares Unity5のStandardシェーダのパラメタをスクリプトからいじろうとして丸一日潰れた話 hatenablog://entry/8454420450088570698 2015-03-19T10:01:08+09:00 2015-03-19T10:07:49+09:00 Unity遊びの記録を書かずにまごまごしていたらUnity5になってしまって大変申し訳ない気分でいっぱいです。お詫びの印じゃないんですが今やっている別ネタで発生した小ネタについて書き残しておきます。誰か既にやってると思ったら誰も書いてない、という……Unity5を触った方ならご存じかと思いますが、Unity5からはだいたいの材質は物理ベースの統合シェーダ一つで表現されるようになりました。Unity - Manual: Standard Shaderdocs.unity3d.comこのパラメータの中には自己発光を表す「Emission」が含まれています。 Emissionの値をいじることでモデル… <p>Unity遊びの記録を書かずにまごまごしていたらUnity5になってしまって大変申し訳ない気分でいっぱいです。</p><p>お詫びの印じゃないんですが今やっている別ネタで発生した小ネタについて書き残しておきます。誰か既にやってると思ったら誰も書いてない、という……</p><p>Unity5を触った方ならご存じかと思いますが、Unity5からはだいたいの材質は物理ベースの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C5%FD%B9%E7%A5%B7%A5%A7%A1%BC%A5%C0">統合シェーダ</a>一つで表現されるようになりました。</p><p><iframe src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fdocs.unity3d.com%2FManual%2Fshader-StandardShader.html" title="Unity - Manual: Standard Shader" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"><a href="http://docs.unity3d.com/Manual/shader-StandardShader.html">Unity - Manual: Standard Shader</a></iframe><cite class="hatena-citation"><a href="http://docs.unity3d.com/Manual/shader-StandardShader.html">docs.unity3d.com</a></cite></p><p>このパラメータの中には自己発光を表す「Emission」が含まれています。<br /> Emissionの値をいじることでモデルが自ら光るような表現ができるわけですが、これを応用して<a class="keyword" href="http://d.hatena.ne.jp/keyword/STG">STG</a>での与ダメージ表現をやってみようと思ったわけです。</p><p>ざっくり実装手順を。</p><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>からマテリアルのパラメータをGet/Setするために、まずパラメータの内部名称を調べます。調べたいシェーダーを指定しているマテリアルをインスペクタで開き、インスペクタ右上隅の歯車マークをクリック。「Select Shader」を選びます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20150319/20150319032129.png" alt="f:id:dnasoftwares:20150319032129p:plain" title="f:id:dnasoftwares:20150319032129p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>そうするとシェーダーの情報が出ます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20150319/20150319032134.png" alt="f:id:dnasoftwares:20150319032134p:plain" title="f:id:dnasoftwares:20150319032134p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>左の赤枠、「_」から始まっている文字列がパラメータの名前です。<br /> 同じ行に型と、インスペクタでの表示名が出ています。表示名を参考に、いじりたいパラメタはどの名称かを調べます。</p><p>ここではエミッションをいじりたいので_Emissionなんたらというのを探しますが、<br /> どうやら_EmissionColorが探していたもののようです。</p><p>じゃあこの_EmissionColorなるパラメータをいじればよさそう、ということで下記のようなコードを試したのですが……</p> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synComment">//適当なMonoBehaviorの内側……</span> Renderer r = GetComponent&lt;Renderer&gt;; <span class="synComment">//Rendererコンポーネントを取得(Material取得のため)</span> r.material.SetColor(<span class="synConstant">&quot;_EmissionColor&quot;</span>, <span class="synStatement">new</span> Color(<span class="synConstant">1</span>,<span class="synConstant">0</span>,<span class="synConstant">0</span>); <span class="synComment">//とりあえずEmissionで真っ赤にしてみる</span> </pre><p>ところがこれだとうまくいきません。最初からEmissionが指定されているマテリアル以外では色がつかないのです。</p><p>これの理由がわからなくてオロオロしてたのですが、マニュアルやらシェーダーのソースやらを掘り返してようやく理由がわかりました。</p><p><iframe src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fdocs.unity3d.com%2FManual%2Fshader-StandardShader.html" title="Unity - Manual: Standard Shader" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"><a href="http://docs.unity3d.com/Manual/shader-StandardShader.html">Unity - Manual: Standard Shader</a></iframe><cite class="hatena-citation"><a href="http://docs.unity3d.com/Manual/shader-StandardShader.html">docs.unity3d.com</a></cite></p><p>キモの部分だけ一部引用</p> <blockquote> <p>Often it is convenient to keep most of a piece of shader code fixed but also allow slightly different shader “variants” to be produced. This is commonly called “mega shaders” or “uber shaders”, and is achieved by compiling the shader code multiple times with different preprocessor directives for each case.</p><p>In Unity this can be achieved by adding a #pragma multi_compile or #pragma shader_feature directive to a shader snippet. At runtime, the appropriate shader variant is picked up from the Material keywords (Material.EnableKeyword and DisableKeyword) or global shader keywords (Shader.EnableKeyword and DisableKeyword).</p> </blockquote> <p>全訳はしませんが、"#pragma multi_compile"あるいは"#pragma shader_feature"が<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>でいういわゆる#defineみたいなもので、キーワードの有効・無効を切り替えることでシェーダーの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>を分岐できるということのようです。んで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>からキーワードを有効、無効にする手段があってそれが<a href="http://docs.unity3d.com/Manual/shader-StandardShader.html">Material.EnableKeyword</a>と<a href="http://docs.unity3d.com/ScriptReference/Material.DisableKeyword.html">Material.DisableKeyword</a>だと。</p><p>シェーダーのソースはUnityのダウンロードページで「ADDITIONAL DOWNLOADS」から「Built in shaders」を選べば入手できます。</p><p>Standard.shaderの一部引用。</p> <pre class="code" data-lang="" data-unlink>#pragma shader_feature _NORMALMAP #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON #pragma shader_feature _EMISSION #pragma shader_feature _METALLICGLOSSMAP #pragma shader_feature ___ _DETAIL_MULX2 #pragma shader_feature _PARALLAXMAP</pre><p>「#pragma shader_feature _EMISSION」どうやらこのあたりらしい、ということで下記のように書き直してみたところ、期待通りに赤色に光ってくれました。</p> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synComment">//適当なMonoBehaviorの内側……</span> Renderer r = GetComponent&lt;Renderer&gt;; <span class="synComment">//Rendererコンポーネントを取得(Material取得のため)</span> r.material.EnableKeyword(<span class="synConstant">&quot;_EMISSION&quot;</span>); r.material.SetColor(<span class="synConstant">&quot;_EmissionColor&quot;</span>, <span class="synStatement">new</span> Color(<span class="synConstant">1</span>,<span class="synConstant">0</span>,<span class="synConstant">0</span>); <span class="synComment">//とりあえずEmissionで真っ赤にしてみる</span> </pre> <div class="section"> <h3>例</h3> <p>例として1秒おきにEmissionを切り替える→元にもどす、を繰り返すコードを用意しました。<br /> コルーチン使ってますけど別にいいよね?(1秒おきに云々とかUpdateで書くのめんどくさいし)</p> <div class="section"> <h5>動く方のコード</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; [RequireComponent(<span class="synStatement">typeof</span>(Renderer))] <span class="synType">public</span> <span class="synType">class</span> MaterialChangeTester : MonoBehaviour { <span class="synType">private</span> Renderer _renderer; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { _renderer = GetComponent&lt;Renderer&gt;(); StartCoroutine(BlinkerCoroutine()); } IEnumerator BlinkerCoroutine() { <span class="synComment">//こちらは動く例</span> <span class="synComment">//変更前のマテリアルのコピーを保存</span> var originalMaterial = <span class="synStatement">new</span> Material(_renderer.material); <span class="synStatement">for</span> (;;) { _renderer.material.EnableKeyword(<span class="synConstant">&quot;_EMISSION&quot;</span>); <span class="synComment">//キーワードの有効化を忘れずに</span> _renderer.material.SetColor(<span class="synConstant">&quot;_EmissionColor&quot;</span>, <span class="synStatement">new</span> Color(<span class="synConstant">1</span>, <span class="synConstant">0</span>, <span class="synConstant">0</span>)); <span class="synComment">//赤色に光らせる</span> <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(<span class="synConstant">1.0f</span>); <span class="synComment">//1秒待って</span> _renderer.material = originalMaterial; <span class="synComment">//元に戻す</span> <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(<span class="synConstant">1.0f</span>); <span class="synComment">//また1秒待ってくりかえし</span> } } } </pre> </div> <div class="section"> <h5>動かない方のコード</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; [RequireComponent(<span class="synStatement">typeof</span>(Renderer))] <span class="synType">public</span> <span class="synType">class</span> MaterialChangeTesterBadExample : MonoBehaviour { <span class="synType">private</span> Renderer _renderer; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { _renderer = GetComponent&lt;Renderer&gt;(); StartCoroutine(BlinkerCoroutine()); } IEnumerator BlinkerCoroutine() { <span class="synComment">//こちらは悪い例(変化しない)</span> <span class="synComment">//変更前のマテリアルのコピーを保存</span> var originalMaterial = <span class="synStatement">new</span> Material(_renderer.material); <span class="synStatement">for</span> (;;) { _renderer.material.SetColor(<span class="synConstant">&quot;_EmissionColor&quot;</span>, <span class="synStatement">new</span> Color(<span class="synConstant">1</span>, <span class="synConstant">0</span>, <span class="synConstant">0</span>)); <span class="synComment">//赤色に光らせたい</span> <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(<span class="synConstant">1.0f</span>); <span class="synComment">//1秒待って</span> _renderer.material = originalMaterial; <span class="synComment">//元に戻す</span> <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(<span class="synConstant">1.0f</span>); <span class="synComment">//また1秒待ってくりかえし</span> } } } </pre><p>それぞれ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Sphere">Sphere</a>に割り付けて、適当なStandardシェーダーをマテリアルにして実行したら下のような感じになります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20150319/20150319095404.gif" alt="f:id:dnasoftwares:20150319095404g:plain" title="f:id:dnasoftwares:20150319095404g:plain" class="hatena-fotolife" itemprop="image"></span><br /> 応用すればこんな表現に。(というかこれがやりたかった)<blockquote class="twitter-tweet" lang="ja"><p>やったー朝のヤツより眩しくないダメージ発光できたよー <a href="https://twitter.com/hashtag/unity3d?src=hash">#unity3d</a> <a href="http://t.co/qhS9ax6jSr">pic.twitter.com/qhS9ax6jSr</a></p>&mdash; D.N.A. (@dnasoftwares) <a href="https://twitter.com/dnasoftwares/status/578184232700289024">2015, 3月 18</a></blockquote><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script></p><p>というわけで参考になれば幸いです。</p> </div> </div> dnasoftwares Unityで2ヶ月あそんだ記録 (3)自機のコンポーネントとかGameObject構造とか hatenablog://entry/8454420450075508432 2014-11-28T00:49:13+09:00 2014-11-28T00:49:13+09:00 とりあえず最低限のネタを揃えたらモデルにコンポーネントをひっつけてコードを書く。まずは自機から。自機の挙動は下記のような感じにするつもりでいた。ここは最初から変更なし。 前進、後退、左右の各方向について移動性能に差がある(前進は早いが後退は遅いとか、前後より左右の方が素早く動けるとか、そういうアレ) 武器はガン(弾数無限でオーバーヒートがある)とミサイル(弾数有限で発射間隔がある。ロックオンする。複数発撃てる機体もあるかも) 出現・死亡処理などで独自の挙動が書けるようにデリゲートを用意する(※結局使わず) UnityにおけるC#の扱いがよくわかってなかったりとかあったので、派生クラスは余り使わ… <p>とりあえず最低限のネタを揃えたらモデルに<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>をひっつけてコードを書く。まずは自機から。</p><p>自機の挙動は下記のような感じにするつもりでいた。ここは最初から変更なし。</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C1%B0%BF%CA">前進</a>、後退、左右の各方向について移動性能に差がある(<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C1%B0%BF%CA">前進</a>は早いが後退は遅いとか、前後より左右の方が素早く動けるとか、そういうアレ)</li> <li>武器はガン(弾数無限でオーバーヒートがある)とミサイル(弾数有限で発射間隔がある。ロックオンする。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A3%BF%F4">複数</a>発撃てる機体もあるかも)</li> <li>出現・死亡処理などで独自の挙動が書けるようにデリゲートを用意する(※結局使わず)</li> </ul><p>Unityにおける<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>の扱いがよくわかってなかったりとかあったので、派生クラスは余り使わず<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>を<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A3%BF%F4">複数</a>くっつける設計にしていた。<br /> プレイヤー機の場合はこんな感じ。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20141127/20141127225108.jpg" alt="f:id:dnasoftwares:20141127225108j:plain" title="f:id:dnasoftwares:20141127225108j:plain" class="hatena-fotolife" itemprop="image"></span><br /> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>が<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A3%BF%F4">複数</a>に分かれているのは、自機を<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A3%BF%F4">複数</a>の機種から選べるようにしようか、とか、タイマンゲーにするつもりだったころにCPU操作に入れ替えができるように、とか考えていたころの名残。</p><p>移動はCharacterController<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>で。壁ズリとか何も考えずとも(いやそれなりに考える事はあるけど)そこそこちゃんと動いてくれるので助かる。ただ、Step Offsetをゼロにすると<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A3%BF%F4">複数</a>ポリゴンからなる床を滑らせたときに、平坦なはずの床でもひっかかって思い通りに動かなかったりするので0.05ぐらいにしておくのがよさそう。</p><p>自機弾の発射についてはUnity公式の2Dシューティング<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>の<a href="http://japan.unity3d.com/developer/document/tutorial/2d-shooting-game/game/04.html">&#x6575;&#x3092;&#x4F5C;&#x6210;&#x3057;&#x3088;&#x3046;</a>の部分を参考にした実装にしている。</p><p>各種座標をキャラのGameObjectの子として保持させ、実行時には子をtransform.GetChild(n)で調べ上げ、特定の名前を持つtransformを基準座標にして弾を生成する。弾の発射方向もこのGameObjectの向きで表せばいいのだから便利である。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20141127/20141127234303.jpg" alt="f:id:dnasoftwares:20141127234303j:plain" title="f:id:dnasoftwares:20141127234303j:plain" class="hatena-fotolife" itemprop="image"></span><br /> インポート時のデータ構造の関係でモデルデータが空のGameObjectの下にぶら下がる形になってしまっていた(上図でいえば"fourfoot"がモデル実体であり、親の"player"自身は何のレンダラも持っていない)が、回転等の中心、足の位置の微調整などはこっちの方がやりやすかったし結果オーライということで。</p> <div class="section"> <h5>BasePlayerBehavior.cs(抜粋)</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System.Collections.Generic; <span class="synStatement">using</span> System.Linq; <span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; <span class="synType">public</span> <span class="synType">delegate</span> IEnumerator PlayerStartDelegate(); <span class="synType">public</span> <span class="synType">delegate</span> IEnumerator PlayerDeadDelegate(); [RequireComponent(<span class="synStatement">typeof</span>(LifeProperty))] <span class="synType">public</span> <span class="synType">class</span> BasePlayerBehavior : MonoBehaviour { <span class="synComment">//イベントを別スクリプトから登録できるようにして登場時・死亡時処理を書けるように</span> <span class="synComment">//したかった(結局使ってない)</span> <span class="synType">public</span> <span class="synStatement">event</span> PlayerStartDelegate OnPlayerStart; <span class="synType">public</span> <span class="synStatement">event</span> PlayerDeadDelegate OnPlayerDead; <span class="synComment">//視点切り替え用の視点(カメラ)リスト</span> <span class="synType">public</span> List&lt;<span class="synType">string</span>&gt; ViewsList; <span class="synType">public</span> <span class="synType">int</span> CurrentViewIndex = <span class="synConstant">0</span>; <span class="synComment">//武器に関わるパラメタ 武器制御用のスクリプトに初期設定させる</span> [System.NonSerialized] <span class="synType">public</span> <span class="synType">float</span> PrimaryWeaponHeat = <span class="synConstant">0</span>; <span class="synComment">//ガンの現在のヒート値</span> [System.NonSerialized] <span class="synType">public</span> <span class="synType">float</span> PrimaryWeaponHeatMax = <span class="synConstant">40</span>; <span class="synComment">// ヒートの最大値(この値を超えるとオーバーヒート)</span> [System.NonSerialized] <span class="synType">public</span> <span class="synType">float</span> PrimaryWeaponCoolDownSpeed = <span class="synConstant">10</span>; <span class="synComment">// クールダウン速度(1秒間あたりに減少するヒート値)</span> [System.NonSerialized] <span class="synType">public</span> <span class="synType">int</span> SecondaryWeaponCapacity = <span class="synConstant">1</span>; <span class="synComment">// ミサイルの最大数</span> [System.NonSerialized] <span class="synType">public</span> <span class="synType">int</span> SecondaryWeaponAmmo = <span class="synConstant">1</span>; <span class="synComment">// ミサイルの現在の残弾数</span> [System.NonSerialized] <span class="synType">public</span> <span class="synType">float</span> SecondaryWeaponReloadTime = <span class="synConstant">0</span>; <span class="synComment">// ミサイルのリロード残り時間(0=発射可能)</span> <span class="synType">protected</span> <span class="synType">bool</span> PrimaryOverHeat = <span class="synConstant">false</span>; <span class="synComment">//ミサイルのロックオンに関わる処理・関数類は省略</span> <span class="synComment">// Use this for initialization</span> IEnumerator Start() { <span class="synStatement">if</span> (OnPlayerStart != <span class="synConstant">null</span>) { <span class="synStatement">yield</span> <span class="synStatement">return</span> StartCoroutine(OnPlayerStart()); } } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { <span class="synComment">//時間で変化するパラメータ(といってもガンのヒート値とミサイルのリロード時間だけだが)を処理</span> <span class="synStatement">if</span> (PrimaryWeaponHeat &gt; <span class="synConstant">0</span>) { PrimaryWeaponHeat -= PrimaryWeaponCoolDownSpeed*Time.deltaTime; <span class="synStatement">if</span> (PrimaryWeaponHeat &lt;= <span class="synConstant">0</span>) <span class="synComment">//ヒートが0になったらオーバーヒートフラグも下げるよ</span> { PrimaryWeaponHeat = <span class="synConstant">0</span>; PrimaryOverHeat = <span class="synConstant">false</span>; } } <span class="synStatement">if</span> (SecondaryWeaponReloadTime &gt; <span class="synConstant">0</span>) { SecondaryWeaponReloadTime -= Time.deltaTime; <span class="synStatement">if</span> (SecondaryWeaponReloadTime &lt; <span class="synConstant">0</span>) { SecondaryWeaponReloadTime = <span class="synConstant">0</span>; } } } <span class="synComment">//武器パラメータの初期設定</span> <span class="synType">public</span> <span class="synType">void</span> SetWeaponParams(<span class="synType">float</span> heatmax, <span class="synType">float</span> cooldown, <span class="synType">int</span> maxsecondary) { PrimaryWeaponCoolDownSpeed = cooldown; PrimaryWeaponHeatMax = heatmax; SecondaryWeaponCapacity = SecondaryWeaponAmmo = maxsecondary; } <span class="synComment">//ガンはオーバーヒートしてるかを返す(外部からパラメタを直で触る事故がないように隠蔽)</span> <span class="synType">public</span> <span class="synType">bool</span> IsPrimaryOverHeated { <span class="synStatement">get</span> { <span class="synStatement">return</span> PrimaryOverHeat; } } <span class="synComment">//ヒートを増加させる(オーバーヒートフラグの管理含む)</span> <span class="synType">public</span> <span class="synType">bool</span> AddHeat(<span class="synType">float</span> f) { PrimaryWeaponHeat += f; <span class="synStatement">if</span> (PrimaryWeaponHeat &gt;= PrimaryWeaponHeatMax) { PrimaryWeaponHeat = PrimaryWeaponHeatMax; PrimaryOverHeat = <span class="synConstant">true</span>; } <span class="synStatement">return</span> PrimaryOverHeat; } <span class="synComment">// 自機パラメータをどこからでも取得できるようにする関数</span> <span class="synType">static</span> <span class="synType">public</span> BasePlayerBehavior FindPlayer() { <span class="synStatement">return</span> GameObject.FindGameObjectWithTag(<span class="synConstant">&quot;Player&quot;</span>).GetComponent&lt;BasePlayerBehavior&gt;(); } } </pre> </div> <div class="section"> <h5>PlayerMovementController.cs (抜粋)</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System; <span class="synStatement">using</span> UnityEngine; [RequireComponent(<span class="synStatement">typeof</span>(CharacterController)), RequireComponent(<span class="synStatement">typeof</span>(BasePlayerBehavior))] <span class="synType">public</span> <span class="synType">class</span> PlayerMovementController : MonoBehaviour { <span class="synComment">//前進・後退・左右・旋回それぞれについて、最高速・加速度・減速度を定める</span> <span class="synType">public</span> <span class="synType">float</span> ForwardMaxSpeed = <span class="synConstant">24.0f</span>; <span class="synType">public</span> <span class="synType">float</span> ForwardAccel = <span class="synConstant">12.0f</span>; <span class="synType">public</span> <span class="synType">float</span> ForwardBrake = <span class="synConstant">40.0f</span>; <span class="synType">public</span> <span class="synType">float</span> BackwardMaxSpeed = <span class="synConstant">18.0f</span>; <span class="synType">public</span> <span class="synType">float</span> BackwardAccel = <span class="synConstant">10.0f</span>; <span class="synType">public</span> <span class="synType">float</span> BackwardBrake = <span class="synConstant">40.0f</span>; <span class="synType">public</span> <span class="synType">float</span> SlideMaxSpeed = <span class="synConstant">24.0f</span>; <span class="synType">public</span> <span class="synType">float</span> SlideAccel = <span class="synConstant">12.0f</span>; <span class="synType">public</span> <span class="synType">float</span> SlideBrake= <span class="synConstant">40.0f</span>; <span class="synType">public</span> <span class="synType">float</span> TurnMaxSpeed = <span class="synConstant">3.3f</span>; <span class="synType">public</span> <span class="synType">float</span> TurnAccel = <span class="synConstant">30.0f</span>; <span class="synType">public</span> <span class="synType">float</span> TurnBrake = <span class="synConstant">9.5f</span>; <span class="synComment">//前後、左右、回転それぞれの現在速度。</span> <span class="synType">protected</span> <span class="synType">float</span> ZCurrSpeed = <span class="synConstant">0.0f</span>; <span class="synType">protected</span> <span class="synType">float</span> XCurrSpeed = <span class="synConstant">0.0f</span>; <span class="synType">protected</span> <span class="synType">float</span> RCurrSpeed = <span class="synConstant">0.0f</span>; <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { CharacterController controller = GetComponent&lt;CharacterController&gt;(); <span class="synType">float</span> jX = Input.GetAxis(<span class="synConstant">&quot;Horizontal&quot;</span>); <span class="synType">float</span> jY = Input.GetAxis(<span class="synConstant">&quot;Vertical&quot;</span>); <span class="synType">float</span> jR = Input.GetAxis(<span class="synConstant">&quot;Rotation X&quot;</span>); <span class="synComment">//ジョイスティック入力→目標速度</span> <span class="synType">float</span> vZGoal = jY*(jY&gt;<span class="synConstant">0</span>?ForwardMaxSpeed:BackwardMaxSpeed); <span class="synComment">//前入力か後ろ入力かで最高速を切り替え</span> <span class="synType">float</span> vXGoal = jX*SlideMaxSpeed; <span class="synType">float</span> vRGoal = jR*TurnMaxSpeed; <span class="synComment">//目標速度と現在速度をマッチング</span> RCurrSpeed = Mathf.MoveTowards(RCurrSpeed, vRGoal, (is_braking(RCurrSpeed, vRGoal) ? TurnBrake : TurnAccel)*Time.deltaTime); XCurrSpeed = Mathf.MoveTowards(XCurrSpeed, vXGoal, (is_braking(XCurrSpeed, vXGoal) ? SlideBrake : SlideAccel)*Time.deltaTime); ZCurrSpeed = Mathf.MoveTowards(ZCurrSpeed, vZGoal, (jY &gt; <span class="synConstant">0</span> ? (is_braking(ZCurrSpeed,vZGoal)?ForwardBrake:ForwardAccel) : (is_braking(ZCurrSpeed,vZGoal)?BackwardBrake:BackwardAccel) ) *Time.deltaTime); <span class="synComment">//マッチング後の速度を各要素に加算</span> <span class="synComment">//回転</span> <span class="synType">float</span> ry = transform.eulerAngles.y; ry += RCurrSpeed*Time.deltaTime; transform.eulerAngles=<span class="synStatement">new</span> Vector3(<span class="synConstant">0</span>,ry,<span class="synConstant">0</span>); <span class="synComment">//移動 移動量をVector3にセットしてCharactorController.Moveで移動実行</span> Vector3 vmove = <span class="synStatement">new</span> Vector3(XCurrSpeed,<span class="synConstant">0</span>, ZCurrSpeed); vmove = transform.rotation*vmove; vmove += Physics.gravity; CollisionFlags f = controller.Move(vmove*Time.deltaTime); } <span class="synComment">//現在速度と目標速度を比較して「ブレーキをかけるのか」を判定する関数</span> <span class="synComment">//(加速度と減速度で違う値を当てるために使う)</span> <span class="synType">private</span> <span class="synType">static</span> <span class="synType">bool</span> is_braking(<span class="synType">float</span> current, <span class="synType">float</span> goal) { <span class="synStatement">if</span> (current &gt; <span class="synConstant">0</span>) { <span class="synStatement">if</span> (current &gt; goal) <span class="synStatement">return</span> <span class="synConstant">true</span>; } <span class="synStatement">else</span> { <span class="synStatement">if</span> (current &lt; goal) <span class="synStatement">return</span> <span class="synConstant">true</span>; } <span class="synStatement">return</span> <span class="synConstant">false</span>; } } </pre> </div> <div class="section"> <h5>WeaponControllerFourFoot.cs(抜粋)</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; <span class="synType">public</span> <span class="synType">class</span> WeaponControllerFourFoot : MonoBehaviour { <span class="synType">public</span> GameObject BulletObject; <span class="synComment">// 飛ばす弾のオブジェクト</span> <span class="synType">public</span> GameObject MissileObject; <span class="synComment">// ミサイルのオブジェクト</span> <span class="synType">public</span> <span class="synType">float</span> FireInterval = <span class="synConstant">0.1f</span>; <span class="synComment">// ガンの発射間隔</span> <span class="synType">public</span> <span class="synType">float</span> MissileInterval = <span class="synConstant">2.0f</span>; <span class="synComment">// ミサイルの発射間隔</span> <span class="synType">protected</span> <span class="synType">bool</span> isPrimaryFiring = <span class="synConstant">false</span>; <span class="synComment">//発射ルーチンの多重起動防止フラグ</span> <span class="synType">protected</span> <span class="synType">bool</span> isSecondaryFiring = <span class="synConstant">false</span>; <span class="synComment">// 同上</span> <span class="synType">private</span> BasePlayerBehavior _player = <span class="synConstant">null</span>; <span class="synComment">// 各種パラメタを突っ込んでいるBasePlayerBehaviorへの参照</span> <span class="synType">public</span> <span class="synType">int</span> MissileCapacity = <span class="synConstant">5</span>; <span class="synComment">//ミサイル最大保持数</span> <span class="synType">public</span> <span class="synType">float</span> MaxHeat = <span class="synConstant">50</span>; <span class="synComment">// ヒート限界値(ガンがオーバーヒートするまでの長さ 大きいほどオーバーヒートが遅い)</span> <span class="synType">public</span> <span class="synType">float</span> CooldownTime = <span class="synConstant">4</span>; <span class="synComment">// ヒートのクールダウン時間(ヒート最大から0に戻るまでの秒数で表現)</span> <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start() { _player = GetComponent&lt;BasePlayerBehavior&gt;(); <span class="synComment">// BasePlayerBehaviorを取得</span> _player.SetWeaponParams(MaxHeat, MaxHeat/CooldownTime, MissileCapacity); <span class="synComment">//BasePlayerBehaviorに各種パラメタを送信</span> } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { <span class="synStatement">if</span> (!_player.IsPrimaryOverHeated &amp;&amp; !isPrimaryFiring &amp;&amp; Input.GetButton(<span class="synConstant">&quot;Fire1&quot;</span>)) { StartCoroutine(<span class="synConstant">&quot;FirePrimary&quot;</span>); } <span class="synStatement">if</span> (Input.GetButton(<span class="synConstant">&quot;Fire2&quot;</span>)) { <span class="synStatement">if</span>(_player.SecondaryWeaponAmmo &gt; <span class="synConstant">0</span> &amp;&amp; !isSecondaryFiring &amp;&amp; _player.SecondaryWeaponReloadTime==<span class="synConstant">0</span>) StartCoroutine(<span class="synConstant">&quot;FireSecondary&quot;</span>); } } <span class="synComment">//ガンの発射処理</span> <span class="synComment">//発射処理はコルーチンでやるほうが間隔をおく処理を簡単に書ける</span> IEnumerator FirePrimary() { isPrimaryFiring = <span class="synConstant">true</span>; <span class="synComment">//ガン発射中フラグを立てる</span> <span class="synStatement">while</span> (Input.GetButton(<span class="synConstant">&quot;Fire1&quot;</span>)) <span class="synComment">//Fire1が押されてる間はひたすら連射</span> { <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; transform.childCount; i++) { Transform shotpos = transform.GetChild(i); <span class="synComment">// 自分の子GameObjectを順番に調べる</span> <span class="synStatement">if</span> (shotpos.name == <span class="synConstant">&quot;Cannon_spawner&quot;</span>) <span class="synComment">//そのオブジェクトの名前がCannon_spawnerだったら発射処理</span> { GameObject o = Instantiate(BulletObject, shotpos.position, shotpos.rotation) <span class="synStatement">as</span> GameObject; _player.AddHeat(<span class="synConstant">1.0f</span>); <span class="synComment">//ヒート値を足す</span> <span class="synStatement">if</span> (_player.IsPrimaryOverHeated) <span class="synComment">//オーバーヒートした?</span> { isPrimaryFiring = <span class="synConstant">false</span>; <span class="synStatement">yield</span> <span class="synStatement">break</span>; <span class="synComment">// 打ち方やめ(コルーチン脱出)</span> } <span class="synComment">//1発出したら発射間隔の時間だけ待つ、2箇所の発射点があれば交互に弾が出る仕掛け</span> <span class="synStatement">yield</span> <span class="synStatement">return</span> <span class="synStatement">new</span> WaitForSeconds(FireInterval); } } } isPrimaryFiring = <span class="synConstant">false</span>; } <span class="synComment">//ミサイルの発射処理は良く考えたらコルーチンでやる理由がなかった……</span> IEnumerator FireSecondary() { isSecondaryFiring = <span class="synConstant">true</span>; <span class="synStatement">for</span> (<span class="synType">int</span> i = <span class="synConstant">0</span>; i &lt; transform.childCount; i++) { Transform shotpos = transform.GetChild(i); <span class="synStatement">if</span> (shotpos.name == <span class="synConstant">&quot;Missile_spawner&quot;</span>) { GameObject o = Instantiate(MissileObject, shotpos.position, shotpos.rotation) <span class="synStatement">as</span> GameObject; <span class="synComment">//本当はロックオンしている敵をミサイルに教える処理があるが省略</span> } } _player.SecondaryWeaponAmmo--; <span class="synComment">//残弾数減らす</span> _player.SecondaryWeaponReloadTime=(MissileInterval); ミサイルのリロード時間を設定 isSecondaryFiring = <span class="synConstant">false</span>; <span class="synStatement">yield</span> <span class="synStatement">break</span>; } } </pre> </div> <div class="section"> <h5>LifeProperty.cs</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synType">public</span> <span class="synType">class</span> LifeProperty : MonoBehaviour { <span class="synType">public</span> <span class="synType">float</span> Life = <span class="synConstant">1.0f</span>; <span class="synType">private</span> <span class="synType">float</span> _maxLife = <span class="synConstant">1.0f</span>; <span class="synType">public</span> <span class="synType">float</span> MaxLife { <span class="synStatement">get</span> { <span class="synStatement">return</span> _maxLife; } } <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { <span class="synComment">//ライフの初期値を最大値ということにして記憶。</span> _maxLife = Life; } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { } <span class="synComment">//回復処理はこの関数を通す。ライフ値が初期のライフ値以上にならないようにしている</span> <span class="synType">public</span> <span class="synType">void</span> Recover(<span class="synType">float</span> inc) { Life += inc; Life = (Life &gt; MaxLife) ? MaxLife : Life; } } </pre> </div> dnasoftwares Unityで2ヶ月あそんだ記録 (2)ネタ決め、素材集め(最初期) hatenablog://entry/8454420450074607143 2014-11-21T11:50:50+09:00 2014-11-28T01:05:24+09:00 Unityで遊ぶといっても目的もなしに遊べるわけがないわけでして。とりあえずなんでこのネタになったのか、から。 ネタ決め 最初から色々やっても上手くいくわけがないので、とにかく手のかからないモノで一度やってみようということに。 初っ端から立体機動バリバリは死亡フラグなので平面上で完結するもの モーション云々が絡む人物・ロボット等は出さない(モデルもってないし) ということでとりあえず平面で撃ち合うシューティングという方向性だけ決めて、まず組めるところまで組んでしまおうということにした。当初は某アーケード戦車ゲーよろしくタイマンにするか、と思っていた。 素材集め モデル類 ウチはプリレンダのころ… <p>Unityで遊ぶといっても目的もなしに遊べるわけがないわけでして。とりあえずなんでこのネタになったのか、から。</p> <div class="section"> <h3>ネタ決め</h3> <p>最初から色々やっても上手くいくわけがないので、とにかく手のかからないモノで一度やってみようということに。</p> <ul> <li>初っ端から立体機動バリバリは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BB%E0%CB%B4%A5%D5%A5%E9%A5%B0">死亡フラグ</a>なので平面上で完結するもの</li> <li>モーション云々が絡む人物・ロボット等は出さない(モデルもってないし)</li> </ul><p>ということでとりあえず平面で撃ち合うシューティングという方向性だけ決めて、まず組めるところまで組んでしまおうということにした。当初は某アーケード戦車ゲーよろしくタイマンにするか、と思っていた。</p> </div> <div class="section"> <h3>素材集め</h3> <div class="section"> <h4>モデル類</h4> <p>ウチはプリレンダのころからちょくちょく<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3でキャラ作ってた(というかLシリーズ以外で作れる気がしない)ので、今回もなんとか使えないものかと試行錯誤。とてかんCGコンバータが出たので今後はUnityで完結するのだが、参考までに当時の変換手順を。</p><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3には組み立て後のモデルデータを.SUF(<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a> CGA Systemの標準モデル形式)で出力する機能がある。また、<a href="http://www.metaseq.net/">Metasequoia</a>(Ver3系)はSUF形式を読める貴重な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%E9%A1%BC">モデラー</a>で、出力形式も豊富。</p><p>ということで、</p> <ol> <li>L3でモデルを組み立ててSUF出力</li> <li>Metaseqで読み込んで適当な形式で再出力(ウチは<a class="keyword" href="http://d.hatena.ne.jp/keyword/DAE">DAE</a>を使用)</li> <li>それをUnityにインポートして</li> <li>マテリアル微調整で完了</li> </ol><p>というのが基本の手順だった。<br /> インポート直後はモデルの各面がスムージングされててあまり見た目よくないのでスムージングを切る。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/d/dnasoftwares/20141121/20141121111341.jpg" alt="f:id:dnasoftwares:20141121111341j:plain" title="f:id:dnasoftwares:20141121111341j:plain" class="hatena-fotolife" itemprop="image"></span><br /> Import SettingsでNormals、Tangentsをそれぞれ【Calculate】にし、Smoothing Angleを0にすればいい。</p><p>超単純な機銃の弾のモデル(正四面体をZ方向だけ超引き延ばして終了)などは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Metasequoia">Metasequoia</a>で直接<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>して出力。</p> </div> <div class="section"> <h4>エフェクト・スプライト</h4> <p>最初はとにかく「すぐ表示できること」を優先で、セールだかなんだかで買っていたWar FXを入れてた。<br /> <iframe src="http://hatenablog.com/embed?url=http%3A%2F%2Fu3d.as%2Fcontent%2Fjean-moreno-jmo-%2Fwar-fx%2F3SJ" title="War FX by Jean Moreno (JMO)" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"><a href="http://u3d.as/content/jean-moreno-jmo-/war-fx/3SJ">War FX by Jean Moreno (JMO)</a></iframe><br /> <a href="http://u3d.as/content/jean-moreno-jmo-/war-fx/3SJ">War FX by Jean Moreno (JMO) -- Unity Asset Store</a><br /> が、全体的にフラットシェーディングの絵作りなのにそこだけ今風というのもおかしいので、スプライトっぽさを出すべく2Dの爆発アニメを入れることに。</p><p>元絵はフリーやCC-BYとかの素材から。</p> <ul> <li><a href="http://opengameart.org/content/abuse-art">Abuse Art | OpenGameArt.org</a></li> <li><a href="http://www.lostgarden.com/2007/04/free-game-graphics-tyrian-ships-and.html">Lost Garden: Free game graphics: Tyrian ships and tiles</a></li> <li><a href="http://opengameart.org/content/141-military-icons-set">141 Military Icons Set | OpenGameArt.org</a>(武器・アイテムのアイコンとかはここから)</li> </ul><p>効果音・BGM類はリリース決めた後の最終盤まで入れてなかった。</p> </div> </div> dnasoftwares Unityで2ヶ月あそんだ記録 (1)とりあえず淡々とタイムラインを並べてみる hatenablog://entry/8454420450074521016 2014-11-20T01:30:57+09:00 2014-11-28T01:05:24+09:00 デジゲー博2014で「METROWARS」っていうゲームを(体験版で)出したんですがここ2ヶ月に何やってたのかをまとめて振り返ってみたいなと。数日間ダラダラと所感を書いてみる予定。 <p>デジゲー博2014で「<a href="http://www.dna-softwares.com/article/2263">METROWARS</a>」っていうゲームを(体験版で)出したんですがここ2ヶ月に何やってたのかをまとめて振り返ってみたいなと。数日間ダラダラと所感を書いてみる予定。</p><br /> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/TGS">TGS</a>が終わった後あたりから作業開始。</p> <ul> <li>9/24 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>立てる。昔バージョンアップの際にぶっ飛んで直してなかったプロジェクトのアセットを再利用。<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>のモデルを全部セットし直し。</li> <li>9/25 UnityVS入れてコード書き始め。自機挙動とカメラ。</li> <li>9/28 自機マシンガンの処理</li> <li>9/29 敵機。本を見てNavMeshに従い自機に寄る敵機の挙動を実装。</li> <li>9/30 Spawner Freeなるアセットで取り急ぎ敵機をランダム沸きにしてみる。前に買ってたWar FXでヒットマーク類。</li> <li>10/4 このあたりでNGUIによるHUD搭載。GameEnvironment(AIMSでいつもゲーム全体にまたがる変数をぶち込んでるテーブル名)を用意。死亡処理とカメラスイッチャの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A3%BF%F4">複数</a>カメラ切り替えの実装。</li> <li>10/9 ShaderForgeを買ってメッシュ半透明シェーダーを作る</li> <li>10/10 メッシュ半透明シェーダーを<a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a>で公開</li> </ul><p><iframe src="http://hatenablog.com/embed?url=https%3A%2F%2Fgithub.com%2Fdnasoftwares%2Funity-retro-mesh-shader" title="dnasoftwares/unity-retro-mesh-shader" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"><a href="https://github.com/dnasoftwares/unity-retro-mesh-shader">dnasoftwares/unity-retro-mesh-shader</a></iframe><br /> <a href="https://github.com/dnasoftwares/unity-retro-mesh-shader">dnasoftwares/unity-retro-mesh-shader &middot; GitHub</a></p> <ul> <li>10/11~10/21 この間パッタリ進捗が途絶えてる(デジゲー博準備の関係とか)</li> <li>10/23 とてかんCGのインポーターが<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3プロライセンスにも対応したので実戦投入</li> </ul><p><iframe src="http://hatenablog.com/embed?url=http%3A%2F%2Fdoga.jp%2FPLAY%2Funity-plugin-jp%2F" title="CGアニメソフト とてかんCG PLAY Animation Importer for Unity (日本語)" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"><a href="http://doga.jp/PLAY/unity-plugin-jp/">CGアニメソフト とてかんCG PLAY Animation Importer for Unity (日本語)</a></iframe><br /> <a href="http://doga.jp/PLAY/unity-plugin-jp/">CG&#x30A2;&#x30CB;&#x30E1;&#x30BD;&#x30D5;&#x30C8; &#x3068;&#x3066;&#x304B;&#x3093;CG PLAY Animation Importer for Unity (&#x65E5;&#x672C;&#x8A9E;)</a><br /> 今みたら11/2付けでメッシュ統合・ドローコール最適化入ってるじゃないですかーうわー</p> <ul> <li>10/25 ミサイルの実装開始</li> <li>10/26 アイテムの実装開始</li> <li>10/28 どうしても<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A9%A5%F3%A5%C8%A5%EF%A1%BC%A5%AF%A5%B9">フォントワークス</a>のフォントがゲームに合わなかったという理由でビットマップフォント自作用にfntファイルとテクスチャの雛形を作る<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C4%A1%BC%A5%EB">ツール</a>を制作開始</li> <li>10/31 とりあえず<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C4%A1%BC%A5%EB">ツール</a>できたっぽく</li> <li>11/3 タイトル画面実装</li> <li>11/6 RAIN AIを導入 新キャラを作ったが<a class="keyword" href="http://d.hatena.ne.jp/keyword/DoGA">DoGA</a>-L3インポーターの出力が癖の強いモデルだったので急遽メッシュ統合できるエディタ拡張を買ったり(前述の通り11/2の更新に気づいてない)</li> <li>11/7 敵AIをRAIN AIに変更完了</li> <li>11/8 Japanese <a class="keyword" href="http://d.hatena.ne.jp/keyword/Otaku">Otaku</a> Cityを導入 KGFMapSystemを導入</li> <li>11/9 ザコモデル追加 Japanese <a class="keyword" href="http://d.hatena.ne.jp/keyword/Otaku">Otaku</a> Cityベースで1ステージ完成</li> <li>11/10 サークルページで<a href="http://www.dna-softwares.com/article/2252">&#x9593;&#x306B;&#x5408;&#x3048;&#x3070;&#x30B2;&#x30FC;&#x30E0;&#x9812;&#x5E03;&#x3059;&#x308B;&#x5BA3;&#x8A00;</a>。</li> <li>11/11 ステージ2完成</li> <li>11/12 ステージ3完成・エンディング</li> <li>11/13 エフェクト追加・音実装・マスターアップ</li> <li>11/16 デジゲー博2014で頒布</li> </ul> dnasoftwares 夏休みの自由研究:「右スティック」の軸について調べてみる(おまけでボタン番号割り当ても) hatenablog://entry/11696248318756829197 2013-08-17T22:14:30+09:00 2013-08-30T14:23:14+09:00 ※間が開いてしまいましたが感想というかまとめを最後に書きました。 前置き AIMSの新バージョンで「アナログ入力」にも真面目に対応しよう、ということで折角なんでアナログ2軸×左右スティック分くらいはとれるようにしたいなと思ったわけです。 しかし左スティックはXY軸で決め打ちでいいとしても右スティックがわからない。なんか昔は軸定義がバラバラでなかなか悲惨だったと聞くので、ここらで真面目に調べてみるかと思いました。ちょうどこんなゲームをつくることになった流れでゲームパッドをいくつか買い込んだので、こいつらの状況を調べてみることにします。 軸だけじゃなんなので物理的なボタンの位置と番号割り当ての違い… <p><span style="color: #ff8080">※間が開いてしまいましたが感想というかまとめを最後に書きました。<br /> </span></p> <div class="section"> <h3>前置き</h3> <p>AIMSの新バージョンで「アナログ入力」にも真面目に対応しよう、ということで折角なんでアナログ2軸×左右スティック分くらいはとれるようにしたいなと思ったわけです。<br /> しかし左スティックはXY軸で決め打ちでいいとしても右スティックがわからない。なんか昔は軸定義がバラバラでなかなか悲惨だったと聞くので、ここらで真面目に調べてみるかと思いました。ちょうど<a href="http://www.dna-softwares.com/article/1747">&#x3053;&#x3093;&#x306A;&#x30B2;&#x30FC;&#x30E0;</a>をつくることになった流れでゲームパッドをいくつか買い込んだので、こいつらの状況を調べてみることにします。<br /> 軸だけじゃなんなので物理的なボタンの位置と番号割り当ての違いも見てみようかなとか。</p> </div> <div class="section"> <h3>方法</h3> <p>各パッドをUSBポートに繋ぎ、コントロールパネルの「デバイスとプリンター」から「ゲーム コントローラーの設定」を呼び出し、対象パッドのプロパティで軸を調べます。とっても簡単。</p> </div> <div class="section"> <h3>実験環境</h3> <dl> <dt>OS</dt> <dd><a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows%207">Windows 7</a>(<a class="keyword" href="http://d.hatena.ne.jp/keyword/x86">x86</a>)</dd> <dt>メモリ</dt> <dd>12GB<a href="#f1" name="fn1" title="PAE有効にして8GBをRAMディスクに使用">*1</a></dd> <dt>HDD</dt> <dd>たくさん</dd> <dt><a class="keyword" href="http://d.hatena.ne.jp/keyword/VGA">VGA</a></dt> <dd>それなりの<a class="keyword" href="http://d.hatena.ne.jp/keyword/GeForce">GeForce</a></dd> <dt>USB</dt> <dd>マザーから延びてケースに出ている<a class="keyword" href="http://d.hatena.ne.jp/keyword/USB2.0">USB2.0</a>ポート</dd> <dt>ドライバー類</dt> <dd>記載ない限りOSが自動的にインストールする標準ドライバーを使う</dd> </dl> </div> <div class="section"> <h3>被検体</h3> <p>実験に使ったのは以下のゲームパッド。</p> <div class="section"> <h4><a class="keyword" href="http://d.hatena.ne.jp/keyword/Logicool">Logicool</a> Rumble Gamepad F510</h4> <p>有線、振動付き、スイッチ切り替えでDInput、XInputどちらにも認識させられる。最近増えてきてますよね両対応品。<br /> 十字キーの扱いはMODEボタンでPOV、XY軸と切り替えることができる。</p> </div> <div class="section"> <h4><a class="keyword" href="http://d.hatena.ne.jp/keyword/Logicool">Logicool</a> Wireless Gamepad F710</h4> <p>F510の無線版といってよさそう。無線で電池駆動な以外の諸元はほぼ同じ。DInput/XInput切り替えも可能。</p> </div> <div class="section"> <h4>ELECOM JC-U3613M</h4> <p>エレコムのDInput/XInput両対応パッド。連射設定ができる。<br /> <b>十字キーがPOVに固定。</b>一頃昔の2Dゲームだとアナログスティックでデジタル操作をやるハメに……</p> </div> <div class="section"> <h4>iBUFFALO BSGP1204P</h4> <p>SAVIORというブランドのゲームパッド。PS3とPC両対応。PC接続時のみ、十字キーの扱いをPOVとXY軸で切り替えられる。</p> </div> <div class="section"> <h4>HORI HORIPAD3 PRO</h4> <p>ご存じホリのPS3用パッド。PS3用ではあるが、PCに挿しても動く。<a class="keyword" href="http://d.hatena.ne.jp/keyword/SIXAXIS">SIXAXIS</a>やDUALSHOCK3のように特殊なソフトで「起こす」必要もない。<br /> 斜め入力の感度設定や十字キーの傾き変更などユニークな機能満載。<br /> <b>十字キーがPOVに固定。</b>もともとPC用途は想定されてないからしょうがないね。</p> </div> </div> <div class="section"> <h3>結果</h3> <p>詰め込み詰め込みでちょっと見づらいですがご了承ください。<br /> 今回実験したパッドはすべて<a class="keyword" href="http://d.hatena.ne.jp/keyword/XBOX360">XBOX360</a>配列、または<a class="keyword" href="http://d.hatena.ne.jp/keyword/PlayStation">PlayStation</a>配列(押し込める2軸アナログ2本、十字キー、正面4ボタン、LR2組、START/SELECT)の構成のため、XInput(<a class="keyword" href="http://d.hatena.ne.jp/keyword/XBOX360">XBOX360</a>)でいう各ボタンの呼称、PS系でいう各ボタンの呼称、そしてその位置にあるボタンを押したときにコントロールパネルで光ったボタン番号、という組み合わせで表記しています。</p> <div class="section"> <h4>F510</h4> <p>DInputモードとXInputモードそれぞれにおいて、MODEボタン消灯時(MODE消)とMODEボタン点灯時(MODE点)の2モードでの割り当ての変化を見た。</p> <table align="center" > <col width="141" /> <col width="85" /> <col width="66" span="6" /> <col width="26" span="10" /> <col width="60" span="3" /> <tr> <th colspan="2">物理位置(XInput)</th> <th rowspan="2">右X軸</th> <th rowspan="2">右Y軸</th> <th rowspan="2">左X軸</th> <th rowspan="2">左Y軸</th> <th rowspan="2">十字<br /> 左右</th> <th rowspan="2">十字<br /> 上下</th> <th class="xinp_a">A</th> <th class="xinp_b">B</th> <th class="xinp_x">X</th> <th class="xinp_y">Y</th> <th>LB</th> <th>RB</th> <th>LT</th> <th>RT</th> <th>LS</th> <th>RS</th> <th>BACK</th> <th>START</th> <th>ガイド</th> </tr> <tr> <th colspan="2">物理位置(PS)</th> <th class="ps_x">×</th> <th class="ps_c">○</th> <th class="ps_s">□</th> <th class="ps_t">△</th> <th>L1</th> <th>R1</th> <th>L2</th> <th>R2</th> <th>L3</th> <th>R3</th> <th>SELECT</th> <th>START</th> <th>PS</th> </tr> <tr> <td rowspan="4">反応した<br /> 軸<br /> or ボタン番号</td> <td>D/MODE消</td> <td>Z軸</td> <td>Z回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>2</td> <td>3</td> <td>1</td> <td>4</td> <td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>なし</td> </tr> <tr> <td>D/MODE点</td> <td>Z軸</td> <td>Z回転</td> <td>POV左右</td> <td>POV上下</td> <td>X軸</td> <td>Y軸</td> <td>2</td> <td>3</td> <td>1</td> <td>4</td> <td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>なし</td> </tr> <tr> <td>X/MODE消</td> <td>X回転</td> <td>Y回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>1</td> <td>2</td> <td>3</td> <td>4</td> <td>5</td> <td>6</td> <td>Z+</td> <td>Z-</td> <td>9</td> <td>10</td> <td>7</td> <td>8</td> <td>ガイド</td> </tr> <tr> <td>X/MODE点</td> <td>X回転</td> <td>Y回転</td> <td>POV左右</td> <td>POV上下</td> <td>X軸</td> <td>Y軸</td> <td>1</td> <td>2</td> <td>3</td> <td>4</td> <td>5</td> <td>6</td> <td>Z+</td> <td>Z-</td> <td>9</td> <td>10</td> <td>7</td> <td>8</td> <td>ガイド</td> </tr> </table><p>DInputモードでは右スティックはZ軸とZ回転にアサイン、XInputだとX回転とY回転にアサインされるということで、モードにより右スティックの扱いが違うという結果に。</p><p>LTとRTはDInputではデジタルボタンの扱いだが、XInputだとアナログトリガーになる。ただし、DirectInputの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>からXInputモードの本機を見た場合、両方で同じZ軸を共有しているため、<b>LTとRTの同時押しを認識できない</b>(つねに両方の押し込み量の差がZ軸の値に表れる)XInputの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>では両方の押し込み量を個別に取得できるので同時押しでも拾える。DInputのゲームにXInputモードの本機を使う理由は皆無じゃなかろうか?</p> </div> <div class="section"> <h4>F710</h4> <p>F510同様、DInputとXInputそれぞれのモードでMODE点灯、消灯各状態での割り当ての変化を見た。</p> <table align="center" > <col width="141" /> <col width="85" /> <col width="66" span="6" /> <col width="26" span="10" /> <col width="60" span="3" /> <tr> <th colspan="2">物理位置(XInput)</th> <th rowspan="2">右X軸</th> <th rowspan="2">右Y軸</th> <th rowspan="2">左X軸</th> <th rowspan="2">左Y軸</th> <th rowspan="2">十字<br /> 左右</th> <th rowspan="2">十字<br /> 上下</th> <th class="xinp_a">A</th> <th class="xinp_b">B</th> <th class="xinp_x">X</th> <th class="xinp_y">Y</th> <th>LB</th> <th>RB</th> <th>LT</th> <th>RT</th> <th>LS</th> <th>RS</th> <th>BACK</th> <th>START</th> <th>ガイド</th> </tr> <tr> <th colspan="2">物理位置(PS)</th> <th class="ps_x">×</th> <th class="ps_c">○</th> <th class="ps_s">□</th> <th class="ps_t">△</th> <th>L1</th> <th>R1</th> <th>L2</th> <th>R2</th> <th>L3</th> <th>R3</th> <th>SELECT</th> <th>START</th> <th>PS</th> </tr> <tr> <td rowspan="4">反応した<br /> 軸<br /> or ボタン番号</td> <td>D/MODE消</td> <td>Z軸</td> <td>Z回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>2</td> <td>3</td> <td>1</td> <td>4</td> <td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>なし</td> </tr> <tr> <td>D/MODE点</td> <td>Z軸</td> <td>Z回転</td> <td>POV左右</td> <td>POV上下</td> <td>X軸</td> <td>Y軸</td> <td>2</td> <td>3</td> <td>1</td> <td>4</td> <td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>なし</td> </tr> <tr> <td>X/MODE消</td> <td>X回転</td> <td>Y回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>1</td> <td>2</td> <td>3</td> <td>4</td> <td>5</td> <td>6</td> <td>Z+</td> <td>Z-</td> <td>9</td> <td>10</td> <td>7</td> <td>8</td> <td>ガイド</td> </tr> <tr> <td>X/MODE点</td> <td>X回転</td> <td>Y回転</td> <td>POV左右</td> <td>POV上下</td> <td>X軸</td> <td>Y軸</td> <td>1</td> <td>2</td> <td>3</td> <td>4</td> <td>5</td> <td>6</td> <td>Z+</td> <td>Z-</td> <td>9</td> <td>10</td> <td>7</td> <td>8</td> <td>ガイド</td> </tr> </table><p>F510と同じ結果に。F510無線版と言い切ってよさそう。</p> </div> <div class="section"> <h4>JC-U3613M</h4> <p>モードとしてはDInputとXInputモードの変更のみ。</p> <table align="center" > <col width="141" /> <col width="85" /> <col width="66" span="6" /> <col width="26" span="10" /> <col width="60" span="3" /> <tr> <th colspan="2">物理位置(XInput)</th> <th rowspan="2">右X軸</th> <th rowspan="2">右Y軸</th> <th rowspan="2">左X軸</th> <th rowspan="2">左Y軸</th> <th rowspan="2">十字<br /> 左右</th> <th rowspan="2">十字<br /> 上下</th> <th class="xinp_a">A</th> <th class="xinp_b">B</th> <th class="xinp_x">X</th> <th class="xinp_y">Y</th> <th>LB</th> <th>RB</th> <th>LT</th> <th>RT</th> <th>LS</th> <th>RS</th> <th>BACK</th> <th>START</th> <th>ガイド</th> </tr> <tr> <th colspan="2">物理位置(PS)</th> <th class="ps_x">×</th> <th class="ps_c">○</th> <th class="ps_s">□</th> <th class="ps_t">△</th> <th>L1</th> <th>R1</th> <th>L2</th> <th>R2</th> <th>L3</th> <th>R3</th> <th>SELECT</th> <th>START</th> <th>PS</th> </tr> <tr> <td rowspan="2">反応した<br /> 軸<br /> or ボタン番号</td> <td>DInput</td> <td>Z軸</td> <td>Z回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>3</td> <td>4</td> <td>1</td> <td>2</td> <td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>9</td> <td>10</td> <td>11</td> <td>12</td> <td>13</td> </tr> <tr> <td>XInput</td> <td>X回転</td> <td>Y回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>1</td> <td>2</td> <td>3</td> <td>4</td> <td>5</td> <td>6</td> <td>Z+</td> <td>Z-</td> <td>9</td> <td>10</td> <td>7</td> <td>8</td> <td>ガイド</td> </tr> </table><p>DInput時の配列をF510/710と比べてみると軸はMODE消灯時のそれと同じであるが、<br /> 上面パッドボタンの番号振りがX→Y→A→Bの順で、BACKとSTART、LSRSの順番がF510/710とあべこべ。<br /> XInputオン時はF510/710とまったく同じ配列。</p> </div> <div class="section"> <h4>BSGP1204P</h4> <p>MODE赤=MODEボタンが赤色に光っている状態、MODE緑=MODEボタンが緑色に光っている状態。<br /> MODEボタンを押すことで赤と緑交互にモードが切り替わる。</p> <table align="center" > <col width="141" /> <col width="85" /> <col width="66" span="6" /> <col width="26" span="10" /> <col width="60" span="3" /> <tr> <th colspan="2">物理位置(XInput)</th> <th rowspan="2">右X軸</th> <th rowspan="2">右Y軸</th> <th rowspan="2">左X軸</th> <th rowspan="2">左Y軸</th> <th rowspan="2">十字<br /> 左右</th> <th rowspan="2">十字<br /> 上下</th> <th class="xinp_a">A</th> <th class="xinp_b">B</th> <th class="xinp_x">X</th> <th class="xinp_y">Y</th> <th>LB</th> <th>RB</th> <th>LT</th> <th>RT</th> <th>LS</th> <th>RS</th> <th>BACK</th> <th>START</th> <th>ガイド</th> </tr> <tr> <th colspan="2">物理位置(PS)</th> <th class="ps_x">×</th> <th class="ps_c">○</th> <th class="ps_s">□</th> <th class="ps_t">△</th> <th>L1</th> <th>R1</th> <th>L2</th> <th>R2</th> <th>L3</th> <th>R3</th> <th>SELECT</th> <th>START</th> <th>PS</th> </tr> <tr> <td rowspan="2">反応した<br /> 軸<br /> or ボタン番号</td> <td>MODE赤</td> <td>Z軸</td> <td>Z回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>3</td> <td>2</td> <td>4</td> <td>1</td> <td>7</td> <td>8</td> <td>5</td> <td>6</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>なし</td> </tr> <tr> <td>MODE緑</td> <td>なし</td> <td>なし</td> <td>なし</td> <td>なし</td> <td>X軸</td> <td>Y軸</td> <td>3</td> <td>2</td> <td>4</td> <td>1</td> <td>7</td> <td>8</td> <td>5</td> <td>6</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>なし</td> </tr> </table><p>MODE緑の時はアナログスティックが2本とも無効になる潔さが際立つ。上面ボタン配列はPSで言うと△→○→×→□の順で時計回りに割り当て。L2R2がボタン番号的に先になっているのも若干目立つ。</p> </div> <div class="section"> <h4>HORIPAD3 PRO</h4> <table align="center" > <col width="141" /> <col width="85" /> <col width="66" span="6" /> <col width="26" span="10" /> <col width="60" span="3" /> <tr> <th>物理位置(XInput)</th> <th rowspan="2">右X軸</th> <th rowspan="2">右Y軸</th> <th rowspan="2">左X軸</th> <th rowspan="2">左Y軸</th> <th rowspan="2">十字<br /> 左右</th> <th rowspan="2">十字<br /> 上下</th> <th class="xinp_a">A</th> <th class="xinp_b">B</th> <th class="xinp_x">X</th> <th class="xinp_y">Y</th> <th>LB</th> <th>RB</th> <th>LT</th> <th>RT</th> <th>LS</th> <th>RS</th> <th>BACK</th> <th>START</th> <th>ガイド</th> </tr> <tr> <th>物理位置(PS)</th> <th class="ps_x">×</th> <th class="ps_c">○</th> <th class="ps_s">□</th> <th class="ps_t">△</th> <th>L1</th> <th>R1</th> <th>L2</th> <th>R2</th> <th>L3</th> <th>R3</th> <th>SELECT</th> <th>START</th> <th>PS</th> </tr> <tr> <td>反応した<br /> 軸<br /> or ボタン番号</td> <td>Z軸</td> <td>Z回転</td> <td>X軸</td> <td>Y軸</td> <td>POV左右</td> <td>POV上下</td> <td>2</td> <td>3</td> <td>1</td> <td>4</td> <td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>11</td> <td>12</td> <td>9</td> <td>10</td> <td>13</td> </tr> </table><p>同じPS3対応のBSGP1204Pと比べるとアナログ軸は一致するものの上面ボタンに違いがある。□→×→○→△、<a class="keyword" href="http://d.hatena.ne.jp/keyword/XBOX360">XBOX360</a>ならXABYという順で番号が振ってある。L1R1L2R2の並び、L3R3とSELECT/STARTのボタン順は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Logicool">Logicool</a>パッドのDInputモードのそれと同じ。ただしこちらはガイドボタンに相当するPSボタンもボタン13として使用可能になっている。</p> </div> </div> <div class="section"> <h3>まとめ</h3> <p>右スティックについては横=Z軸、縦=Z回転でほぼ間違いなさそうなんですが(XInputパッドをDirectInputで読むのはLT,RTが使い物にならないのでアレとして)、ボタンは見事にバラバラな結果でして、やはりゲームパッドを推奨するならキーコンフィグは最優先で実装すべきといえるでしょう。</p><p>あと地味な点ですが、方向ボタン(十字キー)がPOVに固定されてるパッドが増えて来ているのも気になります。デジタル入力を拾う2DゲーなどではPOVの読み取りにも対応しておくのがユーザーにとって優しいんじゃないでしょうか。</p> </div><div class="footnote"> <p class="footnote"><a href="#fn1" name="f1" class="footnote-number">*1</a><span class="footnote-delimiter">:</span><span class="footnote-text">PAE有効にして8GBをRAMディスクに使用</span></p> </div> dnasoftwares .NET用ラバーバンドクラス hatenablog://entry/6435988827676788084 2013-04-04T17:24:51+09:00 2013-05-09T00:45:52+09:00 C#でツール作るたび毎回毎回ラバーバンドで苦労してる気がしてきたので、クラスにまとめて使い回そうともくろんでみたり。 ダウンロード DNASoftwares.Rubberband v1.0.1 説明とかめんどくさいのでソース見てください。動作サンプルもつけてます。VisualStudio2012用になってますが自力でソリューション作り直せばVS2010でも.NET 4.5以前でもイケるんじゃないでしょうか(少なくともライブラリの方は)。 例 ピクチャボックス pictureBox1 をフォーム Form1 に置いてるものとして、 下記のようなコードで動かします。 public partial … <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>でツール作るたび毎回毎回ラバーバンドで苦労してる気がしてきたので、クラスにまとめて使い回そうともくろんでみたり。</p> <ul> <li><a href="https://dl.dropbox.com/u/53239833/dnas-devel/Rubberband-1.0.1.zip">&#x30C0;&#x30A6;&#x30F3;&#x30ED;&#x30FC;&#x30C9; DNASoftwares.Rubberband v1.0.1</a></li> </ul><p>説明とかめんどくさいのでソース見てください。動作サンプルもつけてます。VisualStudio2012用になってますが自力でソリューション作り直せばVS2010でも.NET 4.5以前でもイケるんじゃないでしょうか(少なくともライブラリの方は)。</p> <div class="section"> <h4>例</h4> <p>ピクチャボックス pictureBox1 をフォーム Form1 に置いてるものとして、<br /> 下記のようなコードで動かします。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">public</span> <span class="synStatement">partial</span> <span class="synType">class</span> Form1 : Form { DNASoftwares.Rubberband rub; <span class="synType">public</span> Form1() { InitializeComponent(); <span class="synComment">//新しいインスタンスの作成。引数は矩形初期値、ハンドルをつける場所、可動範囲の順</span> rub = <span class="synStatement">new</span> DNASoftwares.Rubberband(<span class="synStatement">new</span> Rectangle(<span class="synConstant">100</span>, <span class="synConstant">100</span>, <span class="synConstant">250</span>, <span class="synConstant">250</span>), DNASoftwares.Rubberband.HandlePosition.All, <span class="synStatement">new</span> Rectangle(Point.Empty, pictureBox1.ClientSize)); toolStripComboBox1.SelectedIndex = <span class="synConstant">1</span>; } <span class="synType">private</span> <span class="synType">void</span> pictureBox1_MouseDown(<span class="synType">object</span> sender, MouseEventArgs e) { <span class="synComment">//基本こんな感じで、if(メソッド) コンテナ.Refresh(); とすればいいです。</span> <span class="synComment">//MouseDown イベントでは MouseDownEvent メソッドを呼びます</span> <span class="synStatement">if</span>(rub.MouseDownEvent(e)) pictureBox1.Refresh(); } <span class="synType">private</span> <span class="synType">void</span> pictureBox1_MouseMove(<span class="synType">object</span> sender, MouseEventArgs e) { Cursor c; <span class="synComment">//MouseMove イベントでは MouseMoveEvent メソッドを呼びます</span> <span class="synStatement">if</span> (rub.MouseMoveEvent(e, <span class="synStatement">out</span> c)) pictureBox1.Refresh(); <span class="synComment">// MouseMoveEvent は ラバーバンド上でのカーソル変更も支援できます。</span> pictureBox1.Cursor = c; } <span class="synType">private</span> <span class="synType">void</span> pictureBox1_MouseUp(<span class="synType">object</span> sender, MouseEventArgs e) { <span class="synComment">//MouseUp イベントでは MouseUpEvent メソッドを呼びます</span> <span class="synStatement">if</span>(rub.MouseUpEvent(e)) pictureBox1.Refresh(); } <span class="synType">private</span> <span class="synType">void</span> pictureBox1_Paint(<span class="synType">object</span> sender, PaintEventArgs e) { e.Graphics.FillRectangle(<span class="synStatement">new</span> SolidBrush(<span class="synStatement">this</span>.BackColor),e.ClipRectangle); <span class="synComment">//Paint イベントでは PaintEvent メソッドを呼びます。背景クリア等は各自。</span> rub.PaintEvent(e); } <span class="synComment">// 以下省略</span> </pre> </div> dnasoftwares 「おぼえがき」について hatenablog://entry/6435988827676755090 2012-02-21T16:45:52+09:00 2013-04-03T14:35:14+09:00 C++とかC#とかLuaのコードとか、140文字では書けない話をぶっ込む予定です。主にプログラムの話をしたい。普段についてはTwitterをごらんください。 <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>とか<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>とか<a class="keyword" href="http://d.hatena.ne.jp/keyword/Lua">Lua</a>のコードとか、140文字では書けない話をぶっ込む予定です。主にプログラムの話をしたい。</p><p>普段については<a href="http://twitter.com/#!/dnasoftwares">Twitter</a>をごらんください。</p> dnasoftwares