げぇむぷろぐらみんぐ 日々の生活で得た知識、経験を書きます 2023-02-24T23:22:33+09:00 siguma_sig Hatena::Blog hatenablog://blog/10328749687186749048 Unity URPでの画面キャプチャ用パスを用いたCameraCaptureBridgeについて hatenablog://entry/4207112889963229064 2023-02-24T23:22:33+09:00 2023-02-24T23:43:18+09:00 久しぶりのブログです。 UnityのURPで、カメラに映っている画面をキャプチャして取り扱えるようにしてくれるパスであるCapturePassを用いた、CameraCaptureBridgeについて紹介します。 検証環境は下記になります。 Unity2021.3.14f1 Universal RP 12.1.8 できること 下記のスクショのように、描画結果をRenderTextureに書き出したりできます。 RenderTextureに書き出す以外にも、特定のRenderTargetに出力することもできます。 使い方 まずはソースコード全文になります。 using UnityEngine; u… <p>久しぶりのブログです。<br/> UnityのURPで、カメラに映っている画面をキャプチャして取り扱えるようにしてくれるパスであるCapturePassを用いた、CameraCaptureBridgeについて紹介します。</p> <p>検証環境は下記になります。</p> <ul> <li>Unity2021.3.14f1</li> <li>Universal RP 12.1.8</li> </ul> <h1 id="できること">できること</h1> <p>下記のスクショのように、描画結果をRenderTextureに書き出したりできます。 RenderTextureに書き出す以外にも、特定のRenderTargetに出力することもできます。 <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20230223/20230223160754.png" width="1200" height="730" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="使い方">使い方</h1> <p>まずは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>全文になります。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> UnityEngine.Rendering; <span class="synComment">/// </span><span class="synIdentifier">&lt;</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synComment">/// 渡されたRenderTextureにキャプチャ結果を描画する</span> <span class="synComment">/// </span><span class="synIdentifier">&lt;/</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synType">public</span> <span class="synType">class</span> <span class="synType">ScreenCapture </span><span class="synStatement">:</span> MonoBehaviour { [SerializeField] <span class="synType">private</span> RenderTexture _targetRenderTexture; <span class="synType">private</span> Camera _targetCamera; <span class="synType">private</span> <span class="synType">void</span> Start() { CaptureStart(Camera.main); } <span class="synComment">/// </span><span class="synIdentifier">&lt;</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synComment">/// 指定したカメラが移しているもののキャプチャの開始</span> <span class="synComment">/// </span><span class="synIdentifier">&lt;/</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synComment">/// </span><span class="synIdentifier">&lt;</span><span class="synStatement">param</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;camera&quot;</span><span class="synIdentifier">&gt;</span><span class="synComment">キャプチャしたい描画対象を写しているカメラ</span><span class="synIdentifier">&lt;/</span><span class="synStatement">param</span><span class="synIdentifier">&gt;</span> <span class="synType">public</span> <span class="synType">void</span> CaptureStart(Camera camera) { _targetCamera <span class="synStatement">=</span> camera; CameraCaptureBridge.AddCaptureAction(_targetCamera, Capture); } <span class="synComment">/// </span><span class="synIdentifier">&lt;</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synComment">/// キャプチャ終了</span> <span class="synComment">/// </span><span class="synIdentifier">&lt;/</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synType">public</span> <span class="synType">void</span> CaptureEnd() { CameraCaptureBridge.RemoveCaptureAction(_targetCamera, Capture); } <span class="synComment">/// </span><span class="synIdentifier">&lt;</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synComment">/// キャプチャ処理</span> <span class="synComment">/// </span><span class="synIdentifier">&lt;/</span><span class="synStatement">summary</span><span class="synIdentifier">&gt;</span> <span class="synType">private</span> <span class="synType">void</span> Capture(RenderTargetIdentifier renderTargetIdentifier, CommandBuffer commandBuffer) { commandBuffer.Blit(renderTargetIdentifier, _targetRenderTexture); } } </pre> <p>上記のコードの中で重要なのは、キャプチャ開始部分の</p> <pre class="code lang-cs" data-lang="cs" data-unlink>CameraCaptureBridge.AddCaptureAction(_targetCamera, Capture); </pre> <p>キャプチャ終了部分の</p> <pre class="code lang-cs" data-lang="cs" data-unlink>CameraCaptureBridge.RemoveCaptureAction(_targetCamera, Capture); </pre> <p>になります。</p> <p>キャプチャをしたい対象のカメラと、そのキャプチャによって得られた<code>RenderTargetIdentifier</code> をどう取り扱うかのActionをコールバックで渡します。<br/> また、Removeするまではキャプチャをし続けられるので、不要になったタイミングでRemoveします。</p> <p>基本的な使い方はこれだけです。<br/> 以降では、どのようにしてCaptureされているかをURPの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>を覗きつつ、注意したほうが良さそうなことについても紹介します。</p> <h1 id="コードを追ってみる">コードを追ってみる</h1> <p>CameraCaptureBridgeのAddCaptureActionをまず見てみます。 すると、下記のような内容になっています。(<a href="https://github.com/Unity-Technologies/Graphics/blob/632f80e011f18ea537ee6e2f0be3ff4f4dea6a11/Packages/com.unity.render-pipelines.core/Runtime/Utilities/CameraCaptureBridge.cs#L49">コード</a>)</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">public</span> <span class="synType">static</span> <span class="synType">void</span> AddCaptureAction(Camera camera, Action&lt;RenderTargetIdentifier, CommandBuffer&gt; action) { actionDict.TryGetValue(camera, <span class="synStatement">out</span> <span class="synType">var</span> actions); <span class="synStatement">if</span> (actions <span class="synStatement">==</span> <span class="synConstant">null</span>) { actions <span class="synStatement">=</span> <span class="synStatement">new</span> HashSet&lt;Action&lt;RenderTargetIdentifier, CommandBuffer<span class="synStatement">&gt;&gt;</span>(); actionDict.Add(camera, actions); } actions.Add(action); } </pre> <p>これを見ると、登録したActionはactionDictというDictionaryに追加されているようです。</p> <p>なので、次はactionDictがどう使われているかを追ってみると、下記のようにDictionaryの内容をIEnumeratorとして返しているのがわかります。 (<a href="https://github.com/Unity-Technologies/Graphics/blob/632f80e011f18ea537ee6e2f0be3ff4f4dea6a11/Packages/com.unity.render-pipelines.core/Runtime/Utilities/CameraCaptureBridge.cs#L36">コード</a>)</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">public</span> <span class="synType">static</span> IEnumerator&lt;Action&lt;RenderTargetIdentifier, CommandBuffer<span class="synStatement">&gt;&gt;</span> GetCaptureActions(Camera camera) { <span class="synStatement">if</span> (<span class="synStatement">!</span>actionDict.TryGetValue(camera, <span class="synStatement">out</span> <span class="synType">var</span> actions) <span class="synStatement">||</span> actions.Count <span class="synStatement">==</span> <span class="synConstant">0</span>) <span class="synStatement">return</span> <span class="synConstant">null</span>; <span class="synStatement">return</span> actions.GetEnumerator(); } </pre> <p>さらに、上記のメソッドを呼んでいる箇所を探してみると、下記のようでした。 ここでは、CameraDataのcaptureActionsというものに取得した結果を渡しています。(<a href="https://github.com/Unity-Technologies/Graphics/blob/bd9fa3c4efdb6f9b825199e3d4fb5c7cf315538e/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipeline.cs#L1233">コード</a>)</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">static</span> <span class="synType">void</span> InitializeStackedCameraData(Camera baseCamera, UniversalAdditionalCameraData baseAdditionalCameraData, <span class="synStatement">ref</span> CameraData cameraData) { ... cameraData.captureActions <span class="synStatement">=</span> CameraCaptureBridge.GetCaptureActions(baseCamera); } </pre> <p>次に、<code>cameraData.captureActions</code>を使っているところを探してみると、CapturePassに辿り着きました。 (<a href="https://github.com/Unity-Technologies/Graphics/blob/6fdc7996098aa184495c0a3c0da10135bfe18340/Packages/com.unity.render-pipelines.universal/Runtime/Passes/CapturePass.cs#L9">コード</a>)</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">internal</span> <span class="synType">class</span> <span class="synType">CapturePass </span><span class="synStatement">:</span> ScriptableRenderPass { <span class="synType">public</span> <span class="synType">override</span> <span class="synType">void</span> Execute(ScriptableRenderContext context, <span class="synStatement">ref</span> RenderingData renderingData) { CommandBuffer cmdBuf <span class="synStatement">=</span> CommandBufferPool.Get(); <span class="synStatement">using</span> (<span class="synStatement">new</span> ProfilingScope(cmdBuf, m_ProfilingSampler)) { <span class="synType">var</span> colorAttachmentIdentifier <span class="synStatement">=</span> m_CameraColorHandle.Identifier(); <span class="synType">var</span> captureActions <span class="synStatement">=</span> renderingData.cameraData.captureActions; <span class="synStatement">for</span> (captureActions.Reset(); captureActions.MoveNext();) captureActions.Current(colorAttachmentIdentifier, cmdBuf); } context.ExecuteCommandBuffer(cmdBuf); CommandBufferPool.Release(cmdBuf); } } </pre> <p>上のコードを見ると、cameraDataに渡したcaptureActionsをforで順次回して実行していることがわかります。<br/> 上記の挙動から、注意するべきこととして、渡すAction内では<code>RemoveCaptureAction</code> を呼んではいけないことがわかります。<br/> もし呼んでしまうと、ループ中にEnumeratorに変更が加わってしまい、例外が発生してしまいます。 なので、もし1Frameだけキャプチャしたい、という場合にも渡すAction内でRemoveするのではなく、上記の処理が終わったタイミングで別途Removeをしてあげる必要があります。</p> <h1 id="まとめ">まとめ</h1> <p>今回は、URPで簡単にキャプチャ処理が実装できるCameraCaptureBridgeについて紹介しました。 こちらは、とても簡潔にキャプチャ処理ができる反面、キャプチャのタイミングを<code>RenderPassEvent.AfterRendering</code>以外にしたい場合などカスタマイズしたい場合には使用することができないので、使用機会は少ないかもしれませんが、求めている状況と合えばとても便利だと思います。</p> siguma_sig Firebase Cloud FirestoreとRealTimeDatabaseの違いを調べた hatenablog://entry/17680117127219182174 2019-07-20T20:05:16+09:00 2019-07-20T20:05:16+09:00 何かを調べたときにまとめながらのほうが勉強になるし、忘れても取り出せるようにしておきたいので、これからは気が向いたら調べたことをまとめます。 完全に自分用メモのため雑です。 Firebase Cloud Firestore データの持ち方 スケーラビリティ Unityで使えるかどうか まとめ Firebase Cloud Firestore 今まで使ってみてたのがFirebaseのRealTimeDatabaseでした。 Cloud FirestoreはRealTimeDatabaseよりも新しく、なんか強いらしいので、どんな違いがあるのか調べてみました。 データの持ち方 RealTimeDa… <p>何かを調べたときにまとめながらのほうが勉強になるし、忘れても取り出せるようにしておきたいので、これからは気が向いたら調べたことをまとめます。 完全に自分用メモのため雑です。</p> <ul class="table-of-contents"> <li><a href="#Firebase-Cloud-Firestore">Firebase Cloud Firestore</a><ul> <li><a href="#データの持ち方">データの持ち方</a></li> <li><a href="#スケーラビリティ">スケーラビリティ</a></li> <li><a href="#Unityで使えるかどうか">Unityで使えるかどうか</a></li> </ul> </li> <li><a href="#まとめ">まとめ</a></li> </ul> <h1 id="Firebase-Cloud-Firestore">Firebase Cloud Firestore</h1> <p>今まで使ってみてたのがFirebaseのRealTimeDatabaseでした。 Cloud FirestoreはRealTimeDatabaseよりも新しく、なんか強いらしいので、どんな違いがあるのか調べてみました。</p> <h2 id="データの持ち方">データの持ち方</h2> <p>RealTimeDatabaseは、データ全体を<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>のツリーとして保持する。 すべてのデータはキーと対となる値を持ち、それが階層構造になって保持される。</p> <p>具体的に例を出すと、以下のようになる。</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">users</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">hoge</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">firstname</span>&quot;: &quot;<span class="synConstant">hoge</span>&quot;, &quot;<span class="synStatement">lastname</span>&quot;: &quot;<span class="synConstant">maru</span>&quot;, &quot;<span class="synStatement">age</span>&quot;: <span class="synConstant">14</span> <span class="synSpecial">}</span>, &quot;<span class="synStatement">taro</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">firstname</span>&quot;: &quot;<span class="synConstant">taro</span>&quot;, &quot;<span class="synStatement">lastname</span>&quot;: &quot;<span class="synConstant">jiro</span>&quot;, &quot;<span class="synStatement">age</span>&quot;: <span class="synConstant">21</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> </pre> <p>上記のように、全てがキーと対となる値を持っており、その値の型はノードごとに異なっている。</p> <p>参考: <a href="https://firebase.google.com/docs/database/web/structure-data?hl=ja">&#x30C7;&#x30FC;&#x30BF;&#x30D9;&#x30FC;&#x30B9;&#x306E;&#x69CB;&#x9020;&#x5316; &nbsp;|&nbsp; Firebase Realtime Database &nbsp;|&nbsp; Firebase</a></p> <p>対して、Cloud Firestoreは、ドキュメント指向データベースであり、データはコレクションとドキュメントとフィールドからなる。 コレクションは、ドキュメントを保持し、ドキュメントはフィールドを保持するような階層型のデータ構造となっている。 コレクションがフィールドを持つことはできないが、ドキュメントがコレクションをサブコレクションとして持つことはできる。</p> <p>具体的に例を出すと以下のようになる。</p> <pre class="code" data-lang="" data-unlink>chatrooms(Col) roomA(Doc) name: &#34;hoge&#34; messages(Col) message1(Doc) &#34;title&#34;: &#34;fuga&#34;</pre> <p>サブコレクションを使用すると、ドキュメント内のデータをまとめられるので、データが取りやすくなるらしい。</p> <p>参考:</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ffirebase.google.com%2Fdocs%2Ffirestore%2Fdata-model%3Fhl%3Dja" title="Cloud Firestore データモデル  |  Firebase" 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://firebase.google.com/docs/firestore/data-model?hl=ja">firebase.google.com</a></cite></p> <h2 id="スケーラビリティ">スケーラビリティ</h2> <p>RealTimeDatabaseは、一つのDBで10万件までで、それ以上はシャーディングが必要となる。 対して、Cloud Firestoreは自動でスケーリングされる。</p> <h2 id="Unityで使えるかどうか">Unityで使えるかどうか</h2> <p>RealTimeDatabaseはUnity用の<a class="keyword" href="http://d.hatena.ne.jp/keyword/SDK">SDK</a>が公開されているため、利用可能となっている。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ffirebase.google.com%2Fdocs%2Fdatabase%2Funity%2Fstart%3Fhl%3Dja" title="Unity の Firebase Realtime Database を使ってみる  |  Firebase" 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://firebase.google.com/docs/database/unity/start?hl=ja">firebase.google.com</a></cite></p> <p>対して、Cloud Firestoreは<a class="keyword" href="http://d.hatena.ne.jp/keyword/SDK">SDK</a>が公開されておらず、そのままでは利用できない。 そのため、Cloud Functions経由で操作することになるらしい。(対応してほしい…)</p> <h1 id="まとめ">まとめ</h1> <p>RealTimeDatabaseに対して、Firestoreのほうがデータを綺麗に保持できそうな作りだと思いました。 とりあえず、普通に今後使う分にはFirestoreで良いのかな。 早くUnityに対応した<a class="keyword" href="http://d.hatena.ne.jp/keyword/SDK">SDK</a>を出してほしいなあ。 Unity対応の<a class="keyword" href="http://d.hatena.ne.jp/keyword/SDK">SDK</a>が出たらまた使ってみた記事を書こうと思います。</p> <p>参考:</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ffirebase.google.com%2Fdocs%2Fdatabase%2Frtdb-vs-firestore%3Fhl%3Dja" title="データベースの選択: Cloud Firestore または Realtime Database  |  Firebase" 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://firebase.google.com/docs/database/rtdb-vs-firestore?hl=ja">firebase.google.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdevelopers-jp.googleblog.com%2F2017%2F10%2Fcloud-firestore-for-rtdb-developers.html" title="Realtime Database デベロッパーのための Cloud Firestore" 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://developers-jp.googleblog.com/2017/10/cloud-firestore-for-rtdb-developers.html">developers-jp.googleblog.com</a></cite></p> siguma_sig 【Unity】Blenderからモデルを書き出してBlendShapeをやってみた hatenablog://entry/17680117127129891645 2019-05-12T17:03:32+09:00 2019-05-12T17:03:32+09:00 はじめに 動かした結果 BlendShapeとは BlenderでBlendShape作成 UnityでBlendShapeアニメーションする まとめ はじめに 最近社内勉強会などでBlendShapeという単語をよく聞くのですが、概念を知っているだけで動かしてみたことがなかったのでやってみました。 何事も試してみるのが大事。 環境は、 Unity2019.1.0f2 Blender v2.79 Blenderはほぼ触ったことないのでかなり古いかもしれないですが、動いたので一旦良しとします。 <ul class="table-of-contents"> <li><a href="#はじめに">はじめに</a></li> <li><a href="#動かした結果">動かした結果</a></li> <li><a href="#BlendShapeとは">BlendShapeとは</a></li> <li><a href="#BlenderでBlendShape作成">BlenderでBlendShape作成</a></li> <li><a href="#UnityでBlendShapeアニメーションする">UnityでBlendShapeアニメーションする</a></li> <li><a href="#まとめ">まとめ</a></li> </ul> <h1 id="はじめに">はじめに</h1> <p>最近社内勉強会などでBlendShapeという単語をよく聞くのですが、概念を知っているだけで動かしてみたことがなかったのでやってみました。 何事も試してみるのが大事。</p> <p>環境は、</p> <ul> <li>Unity2019.1.0f2</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Blender">Blender</a> v2.79</li> </ul> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Blender">Blender</a>はほぼ触ったことないのでかなり古いかもしれないですが、動いたので一旦良しとします。</p> <h1 id="動かした結果">動かした結果</h1> <p>実際にUnity上に持っていて、以下のような感じで動きました。 すごく単純なモデルなのでほぼ感動がないです…<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%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/s/siguma_sig/20190512/20190512170043.gif" alt="f:id:siguma_sig:20190512170043g:plain" title="f:id:siguma_sig:20190512170043g:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="BlendShapeとは">BlendShapeとは</h1> <p>BlendShapeとは、モデルのアニメーション手法の一つです。<br/> あるベースとなるモデルAに対して、そのモデルAの頂点を動かして別の形状にしたモデルBを用意し、AとBを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D6%A5%EC%A5%F3%A5%C9">ブレンド</a>しつつ変形させるアニメーションを行います。<br/> ゲームでは、よくフェイシャルのアニメーションに使用されるそうです。他にフェイシャルのアニメーションを行う手法としては、ボーンを変形させて行うものもあるそうです。</p> <p>参考に、Unityちゃんのフェイシャルのアニメーションは、BlendShapeを用いて実装されていました。</p> <h1 id="BlenderでBlendShape作成"><a class="keyword" href="http://d.hatena.ne.jp/keyword/Blender">Blender</a>でBlendShape作成</h1> <p>今回は、Unity上で実際にBlendShapeを試すために、まずは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Blender">Blender</a>でBlendShapeの値を持ったモデルを作成します。 以下のようにBaseを立方体として、Key1とKey2として少し崩した形のモデルを作成しました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20190512/20190512153325.png" alt="f:id:siguma_sig:20190512153325p:plain" title="f:id:siguma_sig:20190512153325p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>実際に作成した手順は以下です。</p> <p> 1.ShapeKeysの+ボタンを押してShapeを追加する</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20190512/20190512164349.png" alt="f:id:siguma_sig:20190512164349p:plain" title="f:id:siguma_sig:20190512164349p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p> 2.作成したShapeKeyを選択して、それぞれEditモードで頂点を動かす</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20190512/20190512164524.png" alt="f:id:siguma_sig:20190512164524p:plain" title="f:id:siguma_sig:20190512164524p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p> 3.再びObjectモードにして、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Value">Value</a>を動かして動作確認</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20190512/20190512164922.gif" alt="f:id:siguma_sig:20190512164922g:plain" title="f:id:siguma_sig:20190512164922g:plain" class="hatena-fotolife" itemprop="image"></span></p> <p> 4.Fbxを書き出し</p> <h1 id="UnityでBlendShapeアニメーションする">UnityでBlendShapeアニメーションする</h1> <p>1.先程書き出したFbxをUnityにインポートします。</p> <p>2.Fbxをシーン上にドロップし、インスペクタのSkinMeshRendererを確認</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20190512/20190512165635.png" alt="f:id:siguma_sig:20190512165635p:plain" title="f:id:siguma_sig:20190512165635p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>3.AnimatorControllerとAnimationClipを作成</p> <p>4.先程確認したSkinMeshRendererのBlendShapesという欄のKeyの値をAnimationClipで調整</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20190512/20190512165856.png" alt="f:id:siguma_sig:20190512165856p:plain" title="f:id:siguma_sig:20190512165856p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>5.Animation再生</p> <h1 id="まとめ">まとめ</h1> <p>BlendShapeをUnityで動かしてみました。 Unity上だとすごく簡単にBlendShapeができることがわかりました。</p> <p>3D系の経験値が著しく低いので、今後もこうやって知識として知っているがやってみたことのないことをいくつかやっていきたいです。</p> siguma_sig 【Unity】任意のコンポーネントがアタッチされたPrefab一覧を探す hatenablog://entry/10257846132647854317 2018-10-14T16:09:01+09:00 2018-10-14T16:09:19+09:00 はじめに 今回は、Unityで任意のコンポーネントがアタッチされたPrefab一覧を取得する方法についてまとめます。 Unity標準の機能だと、シーン上で任意のコンポーネントがアタッチされたオブジェクト一覧を探すことはできますが、Project全体から探すことはできないようでした。 そのため今回は、シェルスクリプトを用いて探す方法と、Unity上でエディタ拡張をすることによって探す方法を説明します。 今回の環境は、 Unity2018.1.1f1 MacOS High Sierra です。 シェルスクリプトによる方法 探し方としては、以下のようになります。 アタッチされていることを調べたいスク… <h1>はじめに</h1> <p>今回は、Unityで任意の<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>がアタッチされたPrefab一覧を取得する方法についてまとめます。</p> <p>Unity標準の機能だと、シーン上で任意の<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>がアタッチされたオブジェクト一覧を探すことはできますが、Project全体から探すことはできないようでした。</p> <p>そのため今回は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>を用いて探す方法と、Unity上でエディタ拡張をすることによって探す方法を説明します。</p> <p>今回の環境は、</p> <ul> <li>Unity2018.1.1f1</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/MacOS">MacOS</a> High <a class="keyword" href="http://d.hatena.ne.jp/keyword/Sierra">Sierra</a></li> </ul> <p>です。</p> <h1><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>による方法</h1> <p>探し方としては、以下のようになります。</p> <ol> <li> アタッチされていることを調べたい<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>のGUIDを調べる</li> <li>プロジェクト内のPrefab一覧を調べる</li> <li>Prefab一覧のmetaファイルで、1で調べたGUIDを持っているものがないか調べる</li> </ol> <p>Unityで管理されているものにはmetaファイルが存在し、その中でGUIDというユニークなIDを持っているため、それを利用して探したいものを特定することができます。</p> <p>具体的な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は以下です。</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synComment">#!/bin/sh</span> <span class="synIdentifier">GUID</span>=<span class="synSpecial">`</span><span class="synStatement">find</span><span class="synSpecial"> ./Assets -type f -name </span><span class="synPreProc">$1.meta</span><span class="synSpecial"> | xargs </span><span class="synStatement">grep</span><span class="synSpecial"> guid | </span><span class="synStatement">sed</span><span class="synSpecial"> -e </span><span class="synStatement">'</span><span class="synConstant">s/guid: //g</span><span class="synStatement">'</span><span class="synSpecial">`</span> <span class="synStatement">find</span> ./Assets <span class="synSpecial">-type</span> f <span class="synSpecial">-name</span> <span class="synStatement">&quot;</span><span class="synConstant">*.prefab</span><span class="synStatement">&quot;</span> <span class="synSpecial">-print0</span> | xargs <span class="synConstant">-0</span> <span class="synStatement">grep</span> <span class="synSpecial">-l</span> <span class="synPreProc">$GUID</span> </pre> <p>引数に渡されたファイル名のmetaファイルを探して、その中からguidが書かれている行を選択し、余計な文字列を除去してGUIDのみを取得して変数に格納します。 その後、格納したGUIDを持つPrefabを検索し、その名前を表示しています。 findとxargsにオプションがついているのは、以下の記事を参考に区切り文字をnull文字にしているからです。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fmaskedw%2Fitems%2F2dfdf6fa7eee991ddc45" title="findとxargsコマンドで-print0オプションを使う理由(改) - Qiita" 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://qiita.com/maskedw/items/2dfdf6fa7eee991ddc45">qiita.com</a></cite></p> <p>先程の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>をreference.shという名前で保存して、実際に実行してみた結果が以下です。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% sh reference.sh Test.cs ./Assets/AttachedPrefab.prefab ./Assets/SubAttachedPrefab.prefab </pre> <p>Test.csがアタッチされているPrefabを探して、その結果としてAttachedPrefab.prefabとSubAttachedPrefab.prefabが返されました。</p> <h1>エディタ拡張による方法</h1> <p>次に、Unityエディタ上で任意の<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>がアタッチされたPrefabを探す方法について説明します。</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>がアタッチされているPrefab一覧を探すためのエディタ拡張のコードです。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System.Collections.Generic; <span class="synStatement">using</span> UnityEditor; <span class="synStatement">using</span> UnityEngine; <span class="synType">namespace</span> Sample.Editor { <span class="synType">public</span> <span class="synType">class</span> ReferenceFinder : EditorWindow { <span class="synType">private</span> MonoScript _targetScript; <span class="synType">private</span> List&lt;GameObject&gt; _findAssets = <span class="synStatement">new</span> List&lt;GameObject&gt;(); <span class="synType">private</span> Vector2 _currentScrollPosition; [MenuItem(<span class="synConstant">&quot;Sample/ReferenceFinder&quot;</span>)] <span class="synType">private</span> <span class="synType">static</span> <span class="synType">void</span> Open() { GetWindow&lt;ReferenceFinder&gt;(<span class="synConstant">&quot;Reference Finder&quot;</span>); } <span class="synType">private</span> <span class="synType">void</span> OnGUI() { EditorGUILayout.BeginHorizontal(); _targetScript = EditorGUILayout.ObjectField(<span class="synConstant">&quot;TargetScript&quot;</span>, _targetScript, <span class="synStatement">typeof</span>(MonoScript), <span class="synConstant">false</span>) <span class="synStatement">as</span> MonoScript; EditorGUILayout.EndHorizontal(); <span class="synStatement">if</span> (_targetScript == <span class="synConstant">null</span> || !_targetScript.GetClass().IsSubclassOf(<span class="synStatement">typeof</span>(MonoBehaviour))) { EditorGUILayout.LabelField(<span class="synConstant">&quot;MonoBehaviourを継承したクラスを設定してください&quot;</span>); _targetScript = <span class="synConstant">null</span>; <span class="synStatement">return</span>; } <span class="synStatement">if</span> (_findAssets.Count &gt; <span class="synConstant">0</span>) { _currentScrollPosition = EditorGUILayout.BeginScrollView(_currentScrollPosition); <span class="synStatement">foreach</span> (var asset <span class="synStatement">in</span> _findAssets) { EditorGUILayout.ObjectField(asset.name, asset, <span class="synStatement">typeof</span>(GameObject), <span class="synConstant">false</span>); } EditorGUILayout.EndScrollView(); } <span class="synStatement">if</span> (GUILayout.Button(<span class="synConstant">&quot;Search&quot;</span>)) { _findAssets.Clear(); var guids = AssetDatabase.FindAssets(<span class="synConstant">&quot;t:GameObject&quot;</span>, <span class="synConstant">null</span>); <span class="synStatement">foreach</span> (var guid <span class="synStatement">in</span> guids) { <span class="synType">string</span> path = AssetDatabase.GUIDToAssetPath(guid); var loadAsset = AssetDatabase.LoadAssetAtPath&lt;GameObject&gt;(path); <span class="synStatement">if</span> (loadAsset.GetComponent(_targetScript.GetClass()) != <span class="synConstant">null</span>) { _findAssets.Add(loadAsset); } } } } } } </pre> <p>やっていることは、</p> <ol> <li>探したい<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>をMonoScriptとして受け取る</li> <li>Searchボタンが押されたらプロジェクト内のGameObjectのGUID一覧を取得</li> <li>GUIDからオブジェクトをロード</li> <li>オブジェクトに対して任意のScriptをGetComponentして取得できるかをチェック</li> <li>取得できたらリストに追加</li> </ol> <p>です。</p> <p>今回は、Prefabにアタッチできる<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>のみを指定できるようにしたかったため、MonoBehaviourを継承していない<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は選択しても検索できないようにしました。</p> <p>上記の拡張の結果以下のようなウィンドウで検索することができるようになりました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20181014/20181014155641.png" alt="f:id:siguma_sig:20181014155641p:plain" title="f:id:siguma_sig:20181014155641p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1>まとめ</h1> <p>今回は、プロジェクト内で任意の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>がアタッチされたPrefabの一覧を取得する方法について説明しました。</p> <p>今回の例では、Prefabの一覧のみを取得しましたが、任意の<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>なにか間違いや、もっと良い書き方があればぜひ教えてください!</p> siguma_sig Unityによる開発のときに使えるシェルスクリプト入門 hatenablog://entry/10257846132647279132 2018-10-07T16:00:59+09:00 2018-10-14T16:12:58+09:00 はじめに Unityで、あるスクリプトがアタッチされているPrefabをプロジェクト内からすべて見つけ出したいということがあったときに、Unityエディタの機能だと無さそうだったので先輩に相談しました。 すると、サクッとシェルスクリプトを書いて探し方を教えてくれて、かっけーとなったので僕も勉強してまとめてみました。 長くなってしまったので、今回は便利そうなコマンドのまとめだけになってしまいました。 シェルスクリプト初心者なので、もっとこう書くと簡潔だよとかあればどしどしご指摘お願いします。 使えそうなコマンド一覧 使えそうなコマンド一覧に関してどんなコマンドか紹介と、具体例を記述していきます。… <h1>はじめに</h1> <p>Unityで、ある<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>がアタッチされているPrefabをプロジェクト内からすべて見つけ出したいということがあったときに、Unityエディタの機能だと無さそうだったので先輩に相談しました。 すると、サクッと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>を書いて探し方を教えてくれて、かっけーとなったので僕も勉強してまとめてみました。</p> <p>長くなってしまったので、今回は便利そうなコマンドのまとめだけになってしまいました。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>初心者なので、もっとこう書くと簡潔だよとかあればどしどしご指摘お願いします。</p> <h1>使えそうなコマンド一覧</h1> <p>使えそうなコマンド一覧に関してどんなコマンドか紹介と、具体例を記述していきます。 コマンドの引数やオプションに取りうる詳細な情報は割愛します。</p> <h2>find</h2> <p>指定した<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ以下に存在するファイルや<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BA%C6%B5%A2">再帰</a>的に検索してくれるコマンドです。</p> <p>例えば、Unityでプロジェクトのルート<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リにいるとして、Resourcesの中身一覧を見たくなった場合には以下のようなコマンドでできます。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">find</span> Assets/Resources </pre> <ul> <li>ファイルだけ表示したくなった場合</li> </ul> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">find</span> Assets/Resources <span class="synSpecial">-type</span> f </pre> <ul> <li>.prefabのみ表示したくなった場合</li> </ul> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">find</span> Assets/Resources <span class="synSpecial">-name</span> <span class="synStatement">&quot;</span><span class="synConstant">*.prefab</span><span class="synStatement">&quot;</span> </pre> <h2>xargs</h2> <p>標準入力から扱いたいものを受け取って、それを指定されたコマンドの引数に渡すためのコマンドです。 かなりわかりづらいですが、コマンドとコマンド間の仲介役のような感じです。</p> <p>例えば、先程のfindと組み合わせて、Resources以下のどの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リがサイズが大きいかを調べたくなったとすると以下のようなコマンドでできます。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">find</span> Assets/Resources <span class="synSpecial">-type</span> d | xargs <span class="synStatement">du</span> </pre> <p>上記に関して、-typeの引数にdを指定すると<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リの一覧を得ることができます。それによって、得た<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リの一覧を<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リのサイズを調べるためのコマンドであるduの引数に渡しています。</p> <p>この例をもとにもっと詳しく説明すると、まずはじめのfindコマンドの結果が以下のようになったとします。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">find</span> Assets/Resources <span class="synSpecial">-type</span> d Assets/Resources/Sprites Assets/Resources/Animations Assets/Resources/Font </pre> <p>これらのfindした結果に対して、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リのサイズを調べるためには愚直にやると以下のように一つづつ調べることになります。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">du</span> Assets/Resources/Sprites % <span class="synStatement">du</span> Assets/Resources/Animations % <span class="synStatement">du</span> Assets/Resources/Font </pre> <p>これと同じように、xargsは標準入力から受け取った結果を直接duに渡してくれているため、一つのコマンドで完結します。</p> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/sed">sed</a></h2> <p>与えられた文字列を指定した処理を加えて標準出力できるコマンドです。 主に読み込んだテキストであったり、入力したコマンドの結果の一部に手を加えたい場合に用います。</p> <p>例えば、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>内のTestという文字列をTest2に置き換えたくなった場合には以下のようなコマンドでできます。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">sed</span> <span class="synSpecial">-i</span> <span class="synSpecial">-e</span> s/Test/Test2/g Hoge.cs </pre> <p>上記のコマンドでは、-iオプションによって直接<a class="keyword" href="http://d.hatena.ne.jp/keyword/Hoge">Hoge</a>.csの中身を書き換えています。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>だと、上のコマンドを実行した場合は、-iのあとをバックアップの拡張子としてバックアップを保存してしまうので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Hoge">Hoge</a>.cs-eというファイルができてしまいます。 それを防ぐには、バックアップの拡張子を以下のように空にすれば良いです。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">sed</span> <span class="synSpecial">-i</span> <span class="synStatement">''</span> <span class="synSpecial">-e</span> s/Test/Test2/g Hoge.cs </pre> <p>また、-iでなく以下のようにすれば、書き換えた結果を他のファイルに書き込むこともできます。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">sed</span> <span class="synSpecial">-e</span> s/Test/Test2/g Test.cs </pre> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/grep">grep</a></h2> <p>与えられた文字列から<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>に一致する行を出力できるコマンドです。 あるコマンドの結果を条件によって絞り込んで確認したい場合や、ファイル内に任意の文字列が含まれているかどうかの確認に使ったりできます。</p> <p>例えば、a.txt内でTestという文字列が含まれる行を調べたい場合は以下のようなコマンドでできます。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>% <span class="synStatement">grep</span> Test a.txt </pre> <p>また、今までのコマンド群を組み合わせて、Resourcesフォルダ内にあるPrefabのGUIDを調べるコマンドは以下のようになります。</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synStatement">find</span> Assets/Resources <span class="synSpecial">-name</span> <span class="synStatement">&quot;</span><span class="synConstant">*.prefab</span><span class="synStatement">&quot;</span> <span class="synSpecial">-type</span> f | xargs <span class="synStatement">grep</span> guid </pre> <h1>まとめ</h1> <p>よく使いそうなシェルコマンドについて調べてまとめてみました。 普段は<a class="keyword" href="http://d.hatena.ne.jp/keyword/grep">grep</a>くらいしか使っていなかったのですが、これらが使いこなせるようになると簡単なファイルの一括操作などが非常に楽にできるようになりそうなので、ちょっとずつ使うコマンドを増やしていきたいです。</p> siguma_sig 【Unity】FirebaseのRealTimeDatabaseを使ってみた hatenablog://entry/10257846132642640060 2018-09-30T21:38:22+09:00 2018-09-30T21:38:22+09:00 はじめに 直近でサーバーからのPushの実装が必要な案件があったのですが、サーバーサイドの知見はあまり持っておらずWebSocketを用いてある程度の同時接続に耐えうるように作るのは厳しそうだなと思ったので、 どうにかそこらへんから逃げつつ作れないかなと思い先輩に相談したところFirebaseのRealTimeDatabaseを勧められました。 実際にRealTimeDatabaseを使って実装してみたところ、想像以上に簡単にサーバーサイドの実装を終えることができたので書き残します。 クライアントサイドはUnityなので、今回はC#で記述しますがドキュメントを読んだ感じ他の言語でもほぼ同様なよ… <h1>はじめに</h1> <p>直近でサーバーからのPushの実装が必要な案件があったのですが、サーバーサイドの知見はあまり持っておらずWebSocketを用いてある程度の同時接続に耐えうるように作るのは厳しそうだなと思ったので、 どうにかそこらへんから逃げつつ作れないかなと思い先輩に相談したところFirebaseのRealTimeDatabaseを勧められました。</p> <p>実際にRealTimeDatabaseを使って実装してみたところ、想像以上に簡単にサーバーサイドの実装を終えることができたので書き残します。 クライアントサイドはUnityなので、今回は<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>で記述しますがドキュメントを読んだ感じ他の言語でもほぼ同様なようでした。</p> <h1>RealTimeDatabaseとは</h1> <p>FirebaseのRealTimeDatabaseは公式サイトによると、</p> <blockquote><p>NoSQL <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a> データベースでデータの保管と同期を行うことができます。データはすべてのクライアントにわたってリアルタイムで同期され、アプリがオフラインになっても、利用可能な状態を保ちます。</p></blockquote> <p>とあるように、リアルタイムに同期可能なデータベースです。</p> <p>クライアントから直接データベースへの書き込みや読み取りを行うような方法でやり取りを行うため、サーバー側は全く意識することなく実装を行うことができます。</p> <p>また、お値段も非常に安く、同時接続100までは無料で使用することができます。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ffirebase.google.com%2Fpricing%2F%3Fhl%3Dja" title="Firebase Pricing" 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://firebase.google.com/pricing/?hl=ja">firebase.google.com</a></cite></p> <h1>使い方</h1> <p>Unityプロジェクトに対するFirebaseの導入方法などは公式のサイトをご覧ください。</p> <p><a href="https://firebase.google.com/docs/database/unity/start?hl=ja">Unity &#x306E; Firebase Realtime Database &#x3092;&#x4F7F;&#x3063;&#x3066;&#x307F;&#x308B; &nbsp;|&nbsp; Firebase</a></p> <p>今回は、データの取得、更新と、リアルタイムに更新を受け取る方法について説明します。</p> <h2>データの取得</h2> <p>データの取得は、以下のようにGetValueAsyncというメソッドを用いて行うことができます。 今回は、コールバックによって結果を受け取っていて、taskが完了したタイミングで結果をLogとして表示しています。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>FirebaseDatabase.DefaultInstance.GetReference(<span class="synConstant">&quot;user&quot;</span>) .GetValueAsync().ContinueWith(task =&gt; { <span class="synStatement">if</span> (task.IsFaulted) { Debug.LogError(<span class="synConstant">&quot;not found users&quot;</span>); } <span class="synStatement">else</span> <span class="synStatement">if</span> (task.IsCompleted) { DataSnapshot snapShot = task.Result; Debug.Log(snapShot.Value); } }); </pre> <p>上記のコードは、以下のようなデータ構成だった場合、user以下すべてのデータを読み取ります。</p> <ul> <li>user <ul> <li>books</li> <li>animals</li> <li>age</li> </ul> </li> </ul> <p>すべてを読み取るのではなく、user以下のある特定のデータを読み取る場合は以下のようにします。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>FirebaseDatabase.DefaultInstance.GetReference(<span class="synConstant">&quot;user&quot;</span>) .Child(<span class="synConstant">&quot;age&quot;</span>) .GetValueAsync().ContinueWith(task =&gt; { <span class="synStatement">if</span> (task.IsFaulted) { Debug.LogError(<span class="synConstant">&quot;not found users&quot;</span>); } <span class="synStatement">else</span> <span class="synStatement">if</span> (task.IsCompleted) { DataSnapshot snapShot = task.Result; Debug.Log(Convert.ToInt32(snapShot.Value)); <span class="synComment">// user's age</span> } }); </pre> <p>このように、読み取りたいところまでChildで指定して行ってGetValueAsyncを呼べば、読み取りたいデータのみを読み取ることができます。 また、上記のコードを見るとわかるように、結果はすべてobject型で返されるため適宜変換が必要となるので注意してください。</p> <h2>データの書き込み</h2> <p>データの書き込みに関しても、読み取りとほぼ同様に書き込みたいところまでChildで指定していき、SetValueAsyncによって書き込みを行います。 以下に例を示します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>FirebaseDatabase.DefaultInstance.GetReference(<span class="synConstant">&quot;users&quot;</span>) .Child(<span class="synConstant">&quot;age&quot;</span>) .SetValueAsync(<span class="synConstant">21</span>); </pre> <p>こちらは、基本的には特に型変換もすることなく値を設定することができます。 すでにデータが存在していた場合は上書きされ、存在していなかった場合は新しく子要素として作られます。</p> <h2>データの更新通知</h2> <p>今回一番欲しかった機能である更新検知を行う方法について説明します。 仕組みとしては、ある値が変わったときに呼ばれるメソッドを登録しておくことができます。 以下のように記述することで、メソッドを登録できます。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>FirebaseDatabase.DefaultInstance.GetReference(<span class="synConstant">&quot;users&quot;</span>) .Child(<span class="synConstant">&quot;age&quot;</span>).ValueChanged += HandleAgeValueChanged; <span class="synType">private</span> <span class="synType">void</span> HandleAgeValueChanged(<span class="synType">object</span> sender, ValueChangedEventArgs args) { <span class="synStatement">if</span> (args.DatabaseError != <span class="synConstant">null</span>) { Debug.LogError(args.DatabaseError.Message); <span class="synStatement">return</span>; } Debug.Log(args.Snapshot.Value); <span class="synComment">// current age</span> } </pre> <p>登録されたメソッドの内部では、データ取得のときとほぼ同様に、SnapShotから現在の値を取得しています。 どこかでageに対して更新処理があった際には、自動的に上記のメソッドが呼ばれることになります。</p> <h1>まとめ</h1> <p>今回は、FirebaseのRealTimeDatabaseを用いてデータの取得、更新、更新通知を行う方法についてまとめました。 その他にも<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>としてデータの書き込みを行うこともできたり、以下の記事のようにリレーションを実現することもできるようです。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2F1amageek%2Fitems%2F64bf85ec2cf1613cf507" title="Firebase Realtime DBを実践投入するにあたって考えたこと - Qiita" 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://qiita.com/1amageek/items/64bf85ec2cf1613cf507">qiita.com</a></cite></p> <p>まだ実運用していないので、同時接続などの性能面での不安が残っているのですが、ここらへんに関しても学びがあったらまたまとめたいと思います。</p> siguma_sig 【Unity】ScreenSpace OverlayなCanvas上に3Dモデルを表示する hatenablog://entry/10257846132624382075 2018-09-08T23:26:54+09:00 2018-09-08T23:26:54+09:00 はじめに 通常、ScreenSpaceをOverlayに指定した場合は最前面にCanvas上のUIの描画が来るため、3Dモデルをさらにその上に表示することはできません。 今回は、Canvasの設定はOverlayにしておきたいが、3DモデルをUIの上に表示したい!という場合の対応方法の一つをやってみたのでまとめます。 完成形 以下のようにOverlayなCanvasのUI上に3D表示を行い、それを切り替えることができるようになります。 やり方 やり方は単純で、3Dモデルの描画をRenderTextureに行いCanvas上ではRawImageでそのRenderTextureの表示を行えば完了で… <h1>はじめに</h1> <p>通常、ScreenSpaceをOverlayに指定した場合は最前面に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>上のUIの描画が来るため、3Dモデルをさらにその上に表示することはできません。 今回は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>の設定はOverlayにしておきたいが、3DモデルをUIの上に表示したい!という場合の対応方法の一つをやってみたのでまとめます。</p> <h1>完成形</h1> <p>以下のようにOverlayな<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>のUI上に3D表示を行い、それを切り替えることができるようになります。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180908/20180908231417.gif" alt="f:id:siguma_sig:20180908231417g:plain" title="f:id:siguma_sig:20180908231417g:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1>やり方</h1> <p>やり方は単純で、3Dモデルの描画をRenderTextureに行い<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>上ではRawImageでそのRenderTextureの表示を行えば完了です。</p> <p>順を追って具体的に説明します。</p> <h3>RenderTextureの作成</h3> <p>Projectウィンドウで Create > RenderTextureで作成できます。 細かい設定が気になる方は<a href="https://docs.unity3d.com/ja/current/Manual/class-RenderTexture.html">こちら</a>を見てください。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180908/20180908232123.png" alt="f:id:siguma_sig:20180908232123p:plain" title="f:id:siguma_sig:20180908232123p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h3>3Dモデル描画用のカメラを作成</h3> <p>Hierarchy上でメニューから選択してCameraを新しく作ります。その際に、ClearFlagsをSolidColorにしてアルファ値を0にしておいてください。 また、先ほど作成したRenderTexutreをTargetTextureに設定してください。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180908/20180908232232.png" alt="f:id:siguma_sig:20180908232232p:plain" title="f:id:siguma_sig:20180908232232p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h3>RawImageの配置</h3> <p>Overlayな<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>上にRawImageを配置して、Textureに先程作成したRenderTextureを設定します。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180908/20180908232442.png" alt="f:id:siguma_sig:20180908232442p:plain" title="f:id:siguma_sig:20180908232442p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h3>カメラの微調整</h3> <p>これで新しく作成したカメラに描画されているものがUIとして表示されるようになります。 写したい3Dモデルが映るような位置に微調整をしましょう。</p> <h1>まとめ</h1> <p>同様な方法で、パーティクルに関してもOverlayな<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>の上に表示を行うことができます。 ただし、RenderTextureへの描画を行うためにCameraを一つ作成する必要があるため、パフォーマンスのことを考えるとUI上に3Dモデルをばんばん出したいという場合には向いていないかもしれません。</p> <p>OverlayのままUI上に3Dモデルを表示できるいい方法が他にあれば是非教えてください。</p> siguma_sig C#のIEquatableについて学び直したまとめ hatenablog://entry/10257846132614396794 2018-08-25T17:19:40+09:00 2018-08-25T17:19:58+09:00 はじめに 開発中にあるクラスにIEquatableが実装されていないと指摘を受けたのですが、それについて理解が曖昧なまま実装して修正してしまったので改めて学び直したメモを残します。 IEquatableインターフェイスとは 2つのオブジェクトが等しいかどうかを調べるEqualsメソッドを実装することを保証するためのインターフェイスです。 ここでいう2つのオブジェクトが等しいという場合の等しいがどんな状態を表すかが重要で、IEquatableを実装しなくてもクラス同士の比較自体はできます。 では、実装する場合としない場合で何が違うのかを比べてみます。 IEquatableを実装しない場合とする場… <h1>はじめに</h1> <p>開発中にあるクラスにIEquatableが実装されていないと指摘を受けたのですが、それについて理解が曖昧なまま実装して修正してしまったので改めて学び直したメモを残します。</p> <h1>IEquatable<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%D5%A5%A7%A5%A4%A5%B9">インターフェイス</a>とは</h1> <p>2つのオブジェクトが等しいかどうかを調べるEqualsメソッドを実装することを保証するための<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%D5%A5%A7%A5%A4%A5%B9">インターフェイス</a>です。</p> <p>ここでいう2つのオブジェクトが等しいという場合の等しいがどんな状態を表すかが重要で、IEquatableを実装しなくてもクラス同士の比較自体はできます。 では、実装する場合としない場合で何が違うのかを比べてみます。</p> <h2>IEquatableを実装しない場合とする場合の比較</h2> <p>実装しない場合、参照型であるクラスと値型になる構造体で挙動が変わります。 今回は参照型であるクラスについてのみ説明していきます。</p> <p>以下のようなクラスが存在するとします。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">namespace</span> Sample.Equals { <span class="synType">public</span> <span class="synType">class</span> EqualClass { <span class="synType">private</span> <span class="synType">int</span> _rank; <span class="synType">private</span> <span class="synType">string</span> _name; <span class="synType">public</span> EqualClass(<span class="synType">int</span> rank, <span class="synType">string</span> name) { _rank = rank; _name = name; } <span class="synType">public</span> <span class="synType">string</span> GetRank() { <span class="synStatement">return</span> $<span class="synConstant">&quot;{_name} : Rank {_rank}&quot;</span>; } } } </pre> <p>このとき、以下のような比較をするとコメントで示されたように、falseになります。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>EqualClass sample1 = <span class="synStatement">new</span> EqualClass(<span class="synConstant">0</span>, <span class="synConstant">&quot;hoge&quot;</span>); EqualClass sample2 = <span class="synStatement">new</span> EqualClass(<span class="synConstant">0</span>, <span class="synConstant">&quot;hoge&quot;</span>); Debug.Log(sample1.Equals(sample2)); <span class="synComment">// False</span> </pre> <p>ここで、IEquatable<T>を以下のように実装します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">public</span> <span class="synType">bool</span> Equals(EqualClass equalClass) { <span class="synStatement">if</span> (equalClass == <span class="synConstant">null</span>) { <span class="synStatement">return</span> <span class="synConstant">false</span>; } <span class="synStatement">return</span> (<span class="synStatement">this</span>._rank == equalClass._rank &amp;&amp; <span class="synStatement">this</span>._name == equalClass._name); } </pre> <p>そうすると、さっきの比較を行うと結果はtrueを返すようになります。</p> <p>このような挙動になる理由は、Equalsを実装する前にはオブジェクトの参照先を比較しています。 そのため、先程の例ではsample1とsample2で別の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>が作られているため、中の値が等しくてもfalseを返すようになっているのです。 しかし、Equalsを実装した場合はその中身でそれぞれのメンバの値を比較して結果を返しているため、値が等しければtrueを返します。</p> <p>このように、直接メンバの値によって比較したい場合は、IEquatable<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%D5%A5%A7%A5%A4%A5%B9">インターフェイス</a>を実装する必要があります。</p> <h1>IEquatableを実装すべき場合</h1> <p>先程説明したように、参照型のオブジェクトで値の比較をしたい場合はEqualsの実装が必要です。 では、このように値の比較をしたくなるのはどんな場合にあるのでしょうか?</p> <p>大きなものの一つに、ListやArray、Dictionaryといった<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A5%A7%A5%CD%A5%EA%A5%C3%A5%AF">ジェネリック</a>なコレクションクラスで利用したい場合が挙げられます。 例えば、先程のEqualClassを下記のようにListでまとめたかったとします。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>List&lt;EqualClass&gt; sampleList = <span class="synStatement">new</span> List&lt;EqualClass&gt;() { <span class="synStatement">new</span> EqualClass(<span class="synConstant">0</span>, <span class="synConstant">&quot;hoge&quot;</span>), <span class="synStatement">new</span> EqualClass(<span class="synConstant">1</span>, <span class="synConstant">&quot;fuga&quot;</span>), }; </pre> <p>ここで、rankが0で名前が<a class="keyword" href="http://d.hatena.ne.jp/keyword/hoge">hoge</a>だった人がリストの何番目に入っているか調べたくなったときに、以下のように記述した際に、IEquatableが実装されていないと見つからなかったことになり-1を返します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink>EqualClass hoge = <span class="synStatement">new</span> EqualClass(<span class="synConstant">0</span>, <span class="synConstant">&quot;hoge&quot;</span>); Debug.Log(sampleList.IndexOf(hoge)); <span class="synComment">// -1</span> </pre> <p>このように、意図せず比較を使っている場合もあるため常にIEquatableは実装したほうが良いと思います。</p> <p>余談ですが、Dictionaryのキーに使いたい場合は、IEquatable<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%D5%A5%A7%A5%A4%A5%B9">インターフェイス</a>の他にGetHashCode()もオー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A1%BC">バー</a>ライドする必要があります。</p> <h1>IEquatable<T>とIEquatable(Object)の違い</h1> <p>このように比較をする可能性のあるクラスには必ず実装しておいたほうが良さそうなIEquatableですが、二種類あります。 この2つの違いは何なのでしょうか?</p> <p>2つのEqualsは以下のようなメソッドになっています。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">bool</span> IEquatable&lt;T&gt;(T obj) <span class="synType">bool</span> IEquatable(<span class="synType">object</span> obj) </pre> <p>前者は先程まで説明していた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A5%A7%A5%CD%A5%EA%A5%C3%A5%AF">ジェネリック</a>なIEquatableで後者は引数にobject型を取るEqualsです。 これらの違いとしては、objectの方は比較の際にボクシングが発生するためパフォーマンス的にあまり良くないですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A5%A7%A5%CD%A5%EA%A5%C3%A5%AF">ジェネリック</a>な方はボクシングが発生しない分パフォーマンスが良くなります。</p> <p>一見objectの方はパフォーマンスも悪く、使うタイミングがないように思えますが、クラスによってはobjectの方のみを呼び出すようになっているものもあるため、常に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A5%A7%A5%CD%A5%EA%A5%C3%A5%AF">ジェネリック</a>の方を実装していたとしてもobjectの方も実装したほうが良いそうです。</p> <h1>まとめ</h1> <p>理解が曖昧だったEqualについて整理しました。 こうやってまとめて振り返ってみると、今回指摘されたのはListで利用する可能性のあるクラスだったからでした。 こんな感じで日々の開発の中でふと疑問に思ったことはしっかりと時間の取れるときに調べてまとめることでより理解を深めていきたいですね。 間違っているところがあれば、是非コメントください。</p> <h1>参考</h1> <p><a href="https://msdn.microsoft.com/ja-jp/library/ms131187(v=vs.110).aspx">IEquatable(T) &#x30A4;&#x30F3;&#x30BF;&#x30FC;&#x30D5;&#x30A7;&#x30A4;&#x30B9; (System)</a></p> <p><a href="https://dobon.net/vb/dotnet/beginner/equals.html">&#x81EA;&#x4F5C;&#x30AF;&#x30E9;&#x30B9;&#x306E;Equals&#x30E1;&#x30BD;&#x30C3;&#x30C9;&#x3092;&#x30AA;&#x30FC;&#x30D0;&#x30FC;&#x30E9;&#x30A4;&#x30C9;&#x3057;&#x3066;&#x3001;&#x7B49;&#x4FA1;&#x306E;&#x5B9A;&#x7FA9;&#x3092;&#x5909;&#x66F4;&#x3059;&#x308B; - .NET Tips (VB.NET,C#...)</a></p> siguma_sig クラッシュロワイヤルのAPIが公開された話 hatenablog://entry/10257846132612200177 2018-08-18T23:31:06+09:00 2018-08-18T23:31:06+09:00 はじめに 今週はじめにクラッシュロワイヤルのAPIが一部公開されたことがクラッシュロワイヤルの海外版アプリ内の告知で発表されていました。 それについてどれくらい使えるものだったのかと思ったことについて書きます。 公開されたもの APIはこちらでユーザー登録をすることで使えるようになります。 developer.clashroyale.com 現時点で取得できる情報は以下の一覧のようです。 クラン情報 クランの検索 任意のクランの詳細情報 クランメンバーのリスト クラン対戦の戦歴 現在行われているクラン対戦の情報 プレイヤー情報 プレイヤーの詳細情報 今後手に入る宝箱のリスト 最近のバトルの戦歴… <h1>はじめに</h1> <p>今週はじめにクラッシュロワイヤルの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>が一部公開されたことがクラッシュロワイヤルの海外版アプリ内の告知で発表されていました。 それについてどれくらい使えるものだったのかと思ったことについて書きます。</p> <h1>公開されたもの</h1> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>はこちらでユーザー登録をすることで使えるようになります。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdeveloper.clashroyale.com%2F%23%2F" title="Clash Royale API" 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://developer.clashroyale.com/#/">developer.clashroyale.com</a></cite></p> <p>現時点で取得できる情報は以下の一覧のようです。</p> <h3>クラン情報</h3> <ul> <li>クランの検索</li> <li>任意のクランの詳細情報</li> <li>クランメンバーのリスト</li> <li>クラン対戦の戦歴</li> <li>現在行われているクラン対戦の情報</li> </ul> <h3>プレイヤー情報</h3> <ul> <li>プレイヤーの詳細情報</li> <li>今後手に入る宝箱のリスト</li> <li>最近のバトルの戦歴</li> </ul> <h3>トーナメント情報</h3> <ul> <li>全トーナメントの名前</li> <li>トーナメントの詳細情報</li> </ul> <h3>カード情報</h3> <ul> <li>全カードの情報(コスト、アイコン、Maxレベル)</li> </ul> <h3>地域情報</h3> <ul> <li>全地域のID</li> <li>特定の地域の詳細情報</li> <li>特定の地域のクランランキング</li> <li>特定の地域のユーザーランキング</li> <li>特定の地域のクラン対戦ランキング</li> </ul> <h1>できそうなこと</h1> <p>基本的な情報はすべて取得できるようです。 取得できない情報を探すのが大変なぐらい公開されていそうでした。</p> <p>中でもびっくりしたのが、他のプレイヤーの戦歴を見れることです。 試合自体はオープンに行われており、誰でも観戦は自由にできるゲームなので公開しても問題ないのかもしれませんが、クラッシュロワイヤルはプロプレイヤーも多くいるゲームなので、そのプレイヤーたちのデッキであったり戦歴を見れるのは非常に嬉しいです。 これを使えば、有名プレイヤーたちのデッキの傾向や戦歴の分析ができそうです。</p> <h1>思ったこと</h1> <p>おそらくゲームの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を公開するというのは非常に珍しいのではないかと思います(他ゲームで公式で公開されているものがあれば知りたいです)。 公式サイトにも書かれていますが<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を公開することで熱心なユーザーが自発的に便利なアプリやサイトを開発してくれれば、そのゲームをやっている人はよりゲームを楽しめるようになり、それ自体が有名になればそれらのサイトやアプリをきっかけでゲームを始めるような人も出てくるようになると思います。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CB%A5%B3%A5%CB%A5%B3%C6%B0%B2%E8">ニコニコ動画</a>のようなユーザーによる盛り上げによって自然と人気になるスタイルは非常に良いと思いますし、今回のクラッシュロワイヤルの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>公開はそれを促す施策として非常に効果的だろうなと思いました。 ゲームは広告や<a class="keyword" href="http://d.hatena.ne.jp/keyword/SNS">SNS</a>運用で集客をするのが当たり前になっていると思いますが、このような熱心なユーザーを利用しての集客方法もあるのだなと新しく気づかされました。</p> <h1>まとめ</h1> <p>少し試してみたところ、有名プレイヤーの戦歴やどんなデッキを使っているのかも見れるようだったので有名プレイヤーたちのデッキの傾向や戦歴を見れるようなアプリを作ってみたくなりました。 こういったものが公開されるゲームが増えるとユーザーの熱によってゲームコミュニティ自体が広がって行くと思うので、どんどんやってほしいなあと思いました。</p> siguma_sig 【Unity】uGUIの当たり判定を広げる hatenablog://entry/10257846132610010499 2018-08-12T17:07:34+09:00 2018-08-17T10:10:40+09:00 はじめに かなり小さいサイズのボタンを作成する場合にImageのサイズは変えたくないけど、判定だけ少し広めに取りたいということがあります。 今回は、そのような場合に判定を広げる方法とその広げた判定がどんな感じになっているかをGizmoで見えるようにする方法を紹介します。 完成したもの 以下のような白いボタンに対して、判定が緑の範囲となるような感じで調整ができます。 ソースコード using UnityEngine; using UnityEngine.UI; namespace UIColliderSample { [RequireComponent(typeof(RectTransform)… <h1>はじめに</h1> <p>かなり小さいサイズのボタンを作成する場合にImageのサイズは変えたくないけど、判定だけ少し広めに取りたいということがあります。 今回は、そのような場合に判定を広げる方法とその広げた判定がどんな感じになっているかを<a class="keyword" href="http://d.hatena.ne.jp/keyword/Gizmo">Gizmo</a>で見えるようにする方法を紹介します。</p> <h1>完成したもの</h1> <p>以下のような白いボタンに対して、判定が緑の範囲となるような感じで調整ができます。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812162709.png" alt="f:id:siguma_sig:20180812162709p:plain" title="f:id:siguma_sig:20180812162709p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812162717.png" alt="f:id:siguma_sig:20180812162717p:plain" title="f:id:siguma_sig:20180812162717p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a></h1> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> UnityEngine.UI; <span class="synType">namespace</span> UIColliderSample { [RequireComponent(<span class="synStatement">typeof</span>(RectTransform))] [RequireComponent(<span class="synStatement">typeof</span>(CanvasRenderer))] <span class="synType">public</span> <span class="synType">class</span> InvisibleGraphic : Graphic { <span class="synType">private</span> RectTransform _rectTransform; <span class="synType">private</span> Rect _rect = <span class="synStatement">new</span> Rect(); <span class="synType">private</span> Rect GetRect() { <span class="synStatement">if</span> (_rectTransform == <span class="synConstant">null</span>) { _rectTransform = GetComponent&lt;RectTransform&gt;(); } _rect.center = (Vector2) _rectTransform.position + _rectTransform.rect.center; Vector2 size = _rectTransform.rect.size; size.x *= _rectTransform.lossyScale.x; size.y *= _rectTransform.lossyScale.y; _rect.size = size; <span class="synStatement">return</span> _rect; } <span class="synType">protected</span> <span class="synType">override</span> <span class="synType">void</span> OnPopulateMesh(VertexHelper vh) { <span class="synStatement">base</span>.OnPopulateMesh(vh); vh.Clear(); } <span class="synType">private</span> <span class="synType">void</span> OnDrawGizmos() { var rect = GetRect(); Gizmos.color = <span class="synStatement">new</span> Color(<span class="synConstant">0.0f</span>, <span class="synConstant">1.0f</span>, <span class="synConstant">0.0f</span>, <span class="synConstant">0.5f</span>); Gizmos.DrawCube(rect.center, rect.size); } } } </pre> <h1><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>の説明</h1> <p>判定を広げる部分に関してはkanさんのブログを参考にしました。 やっていることとしてはGraphicを継承することで判定を取れるようにして、そのままだと白い四角が描画されてしまうので以下の部分で頂点を空にすることで描画されないようにしています。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">protected</span> <span class="synType">override</span> <span class="synType">void</span> OnPopulateMesh(VertexHelper vh) { <span class="synStatement">base</span>.OnPopulateMesh(vh); vh.Clear(); } </pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Gizmo">Gizmo</a>の部分では、この<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>がアタッチされているオブジェクトのRectTransformを見て、そのサイズになるようにRectを表示しています。 RectTransformに対して正しいRectになるように、以下の部分でRectを作成しています。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">private</span> Rect GetRect() { <span class="synStatement">if</span> (_rectTransform == <span class="synConstant">null</span>) { _rectTransform = GetComponent&lt;RectTransform&gt;(); } _rect.center = (Vector2) _rectTransform.position + _rectTransform.rect.center; Vector2 size = _rectTransform.rect.size; size.x *= _rectTransform.lossyScale.x; size.y *= _rectTransform.lossyScale.y; _rect.size = size; <span class="synStatement">return</span> _rect; } </pre> <p>Rectのサイズはscaleも考慮する必要があるので、lossyScaleを掛けています。</p> <h1>使い方</h1> <p>作成した上記の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を当たり判定を広げたいuGUIに子オブジェクトを作成してそれにアタッチする。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812170505.png" alt="f:id:siguma_sig:20180812170505p:plain" title="f:id:siguma_sig:20180812170505p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1>まとめ</h1> <p>判定を広げるのをもっと簡単にできたらいいなあ…と思いました。 もしもっと良いやり方があれば教えてください!</p> <p>あとなにげに<a class="keyword" href="http://d.hatena.ne.jp/keyword/Gizmo">Gizmo</a>を初めていじりました。</p> <h1>参考</h1> <p><iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fkan-kikuchi.hatenablog.com%2Fentry%2FInvisibleGraphic" title="ボタンの当たり判定(タッチ範囲)だけ広げる【Unity】【uGUI】 - (:3[kanのメモ帳]" 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://kan-kikuchi.hatenablog.com/entry/InvisibleGraphic">kan-kikuchi.hatenablog.com</a></cite></p> siguma_sig 【Unity】Canvasでパフォーマンスに関わることについて調べてみた hatenablog://entry/10257846132607522874 2018-08-11T09:51:56+09:00 2018-08-17T10:10:53+09:00 はじめに 今後UI周りに広く関わることになったので、Canvasに関して気になってはいたけど調べていなかったことについて調べてみました。 目次は以下です。 はじめに CanvasとSpriteAtlasの関係 動くUIと動かないUIでCanvasを分ける まとめ (追記) DeepProfileによる検証し直し 参考 CanvasとSpriteAtlasの関係 はじめに、SpriteAtlasでまとめたSpriteを使うUIを複数Canvasに分ける場合と分けない場合でDrawCallにどんな違いが出るのかを調べました。 SpriteAtlasに関してはこちらを参照してください 描画したUIは… <h1 id="はじめに">はじめに</h1> <p>今後UI周りに広く関わることになったので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>に関して気になってはいたけど調べていなかったことについて調べてみました。</p> <p>目次は以下です。</p> <ul class="table-of-contents"> <li><a href="#はじめに">はじめに</a></li> <li><a href="#CanvasとSpriteAtlasの関係">CanvasとSpriteAtlasの関係</a></li> <li><a href="#動くUIと動かないUIでCanvasを分ける">動くUIと動かないUIでCanvasを分ける</a></li> <li><a href="#まとめ">まとめ</a></li> <li><a href="#追記-DeepProfileによる検証し直し">(追記) DeepProfileによる検証し直し</a></li> <li><a href="#参考">参考</a></li> </ul> <h1 id="CanvasとSpriteAtlasの関係"><a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>とSpriteAtlasの関係</h1> <p>はじめに、SpriteAtlasでまとめたSpriteを使うUIを複数<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>に分ける場合と分けない場合でDrawCallにどんな違いが出るのかを調べました。 SpriteAtlasに関しては<a href="https://docs.unity3d.com/ja/current/Manual/SpriteAtlas.html">こちら</a>を参照してください</p> <p>描画したUIは以下のようなものです。 表示されているものをすべて一つのSpriteAtlasにまとめました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804162857.png" alt="f:id:siguma_sig:20180804162857p:plain" title="f:id:siguma_sig:20180804162857p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li><p>アトラス化なし、Canvas1つ <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804162936.png" alt="f:id:siguma_sig:20180804162936p:plain" title="f:id:siguma_sig:20180804162936p:plain" class="hatena-fotolife" itemprop="image"></span></p></li> <li><p>アトラス化あり、Canvas1つ <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804162711.png" alt="f:id:siguma_sig:20180804162711p:plain" title="f:id:siguma_sig:20180804162711p:plain" class="hatena-fotolife" itemprop="image"></span></p></li> <li><p>アトラス化なし、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas3">Canvas3</a>つ <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804163023.png" alt="f:id:siguma_sig:20180804163023p:plain" title="f:id:siguma_sig:20180804163023p:plain" class="hatena-fotolife" itemprop="image"></span></p></li> <li><p>アトラス化あり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas3">Canvas3</a>つ <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804163039.png" alt="f:id:siguma_sig:20180804163039p:plain" title="f:id:siguma_sig:20180804163039p:plain" class="hatena-fotolife" itemprop="image"></span></p></li> </ul> <p>これらのことから、アトラス化により各UIで使われるSpriteが一つのSpriteAtlasになっていたとしても、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>が分かれていては別途DrawCallが発生してしまうことがわかります。 SpriteAtlasとして効果を発揮するならば、複数<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>を用いてUIを構成する場合は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>ごとにアトラス化するのが良さそうです。</p> <h1 id="動くUIと動かないUIでCanvasを分ける">動くUIと動かないUIで<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>を分ける</h1> <p>先程の例をみると<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>を分けることにはデメリットしかないように見えますが、Unityの<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>の仕様上<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>内に一つでも動くUIが存在すると<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>全体にRebuild処理が走り結構な負荷がかかるそうです。 そのため、動くUIと動かないUIに関しては<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>を分けるべきだと言われています。</p> <p>今回は、実際に動くUIと動かないUIを分割する場合としない場合でどれくらいの差があるのかを調べてみました。</p> <p>動くUIとしてx方向にscaleするアニメーションをしたImageを、動かないUIとしてただのImageを用意しました。</p> <ul> <li><p>動くUI × 1 と 動かないUI ×1000を同じ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>にしたとき <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804180403.png" alt="f:id:siguma_sig:20180804180403p:plain" title="f:id:siguma_sig:20180804180403p:plain" class="hatena-fotolife" itemprop="image"></span></p></li> <li><p>動くUI × 1 と 動かないUI×1000を別の<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>にしたとき <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180804/20180804180412.png" alt="f:id:siguma_sig:20180804180412p:plain" title="f:id:siguma_sig:20180804180412p:plain" class="hatena-fotolife" itemprop="image"></span></p></li> </ul> <p>差を見ると分かる通り、動くUIが一つあるだけでUI全体に対して描画の更新がかかっているようです。</p> <p>また、動くUIとはなんなのか曖昧だったので試してみたところ、ImageのColorを変えたり、enableを変更するだけでも更新がかかるようでした。</p> <p>大きく分けるならば、常時動いているようなUIとたまに動くUIと全く動かないUIをそれぞれ3つの<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>に分けてやるのが良さそうです。</p> <h1 id="まとめ">まとめ</h1> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>は分けすぎても良くないし、分けなくても良くないことがわかりました。 基本的には、常時動くUIとたまに動くUIと動かないUIで<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>を分けて、それぞれでアトラス化をしてやれば良さそうです。</p> <p>Profilerをあまり使い慣れていなくて計測が雑なので、もしもっと詳しく計測できる方法や間違っていることがあれば教えてください。</p> <h1 id="追記-DeepProfileによる検証し直し">(<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C4%C9%B5%AD">追記</a>) DeepProfileによる検証し直し</h1> <p>動くUIと動かないUIを<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>として分ける場合と分けない場合の違いについての計測で、DeepProfileを用いて計測し直した結果を残しておきます。</p> <ul> <li>動くUI × 1 と 動かないUI ×1000を同じ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>にしたとき</li> </ul> <p>DeepProfileのCalls <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812195419.png" alt="f:id:siguma_sig:20180812195419p:plain" title="f:id:siguma_sig:20180812195419p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>DeepProfileのHierarchy <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812195435.png" alt="f:id:siguma_sig:20180812195435p:plain" title="f:id:siguma_sig:20180812195435p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>動くUI × 1 と 動かないUI×1000を別の<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>にしたとき</li> </ul> <p>DeepProfileのCalls <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812195454.png" alt="f:id:siguma_sig:20180812195454p:plain" title="f:id:siguma_sig:20180812195454p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>DeepProfileのHierarchy <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180812/20180812195506.png" alt="f:id:siguma_sig:20180812195506p:plain" title="f:id:siguma_sig:20180812195506p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>DeepProfileで計測した結果が上記のようになりました。 動くUIを分けなかった場合、WaitForJobGroupIDで非常に時間がかかっていることがわかります。 実際は1000個も同じ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>にUIが存在することは殆どないと思いますが、分けると分けないとで処理時間がかなり変わってくることがはっきりとわかったのでやっぱり分けたほうが良さそうです。</p> <h1 id="参考">参考</h1> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Funity3d.com%2Fjp%2Flearn%2Ftutorials%2Ftopics%2Fbest-practices%2Ffundamentals-unity-ui%23fn-1" title="Unity UI の基礎 - 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://unity3d.com/jp/learn/tutorials/topics/best-practices/fundamentals-unity-ui#fn-1">unity3d.com</a></cite></p> siguma_sig エンジニア以外にも使ってほしい!PlantUMLのススメ hatenablog://entry/10257846132603338156 2018-07-21T23:20:35+09:00 2018-07-21T23:20:35+09:00 はじめに 最近一から設計を考えて実装をする機会が多く、実装前にチームメンバーに自分の考えを共有しなければならないことが多くありました。 その際に、PlantUMLによって自分の考えを図示してから話し合いを行うことで様々な利点があったので共有します。 まだPlantUML歴2週間程度のひよっこなので間違ってるところがあったらぜひ教えてください。 (書いてから思いましたが、UMLをオススメする記事になってしまった気がします) UML PlantUMLについて説明をする前に、UMLについての説明をします。 UMLとはWikipediaから引用すると、 統一モデリング言語(とういつモデリングげんご、U… <h1>はじめに</h1> <p>最近一から設計を考えて実装をする機会が多く、実装前にチームメンバーに自分の考えを共有しなければならないことが多くありました。 その際に、PlantUMLによって自分の考えを図示してから話し合いを行うことで様々な利点があったので共有します。</p> <p>まだPlantUML歴2週間程度の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%D2%A4%E8%A4%C3%A4%B3">ひよっこ</a>なので間違ってるところがあったらぜひ教えてください。</p> <p>(書いてから思いましたが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>をオススメする記事になってしまった気がします)</p> <h1><a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a></h1> <p>PlantUMLについて説明をする前に、<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>についての説明をします。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>とは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Wikipedia">Wikipedia</a>から引用すると、</p> <blockquote><p>統一<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>言語(とういつ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>げんご、<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>、英: Unified Modeling Language)は、主に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%D6%A5%B8%A5%A7%A5%AF%A5%C8%BB%D8%B8%FE">オブジェクト指向</a>分析や設計のための、記法の統一がはかられた(Unified)<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>言語(Modeling Language)である。</p></blockquote> <p>だそうです。</p> <p>簡単に言うと、ソフトウェアに関する設計や処理の流れやデータ構造といったものを図示していくための記法を定めたものです。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>は、データ構造などの構造を表す構造図と動作や変化を表す振る舞い図の大きく二種類に分類されます。</p> <p>構造図としては、クラス図、パッケージ図、オブジェクト図などがあります。 振る舞い図としては、アクティビティ図、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E6%A1%BC%A5%B9%A5%B1%A1%BC%A5%B9">ユースケース</a>図、状態遷移図などがあります。</p> <p>今回は、それぞれの図の具体的な使いみちは他のサイトに譲りますが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>がどんなものか見てもらうためにクラス図についてのみ紹介します。</p> <h2>クラス図</h2> <p>その名の通り、ソフトウェアを構成するクラスがどのような関連を持っているかを図示するものです。 あるクラスが他のクラスとどんな関係にあり、どんな変数を保持しているかが図でわかるようになります。</p> <p>具体的なクラス図は以下です。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180721/20180721225035.png" alt="f:id:siguma_sig:20180721225035p:plain" title="f:id:siguma_sig:20180721225035p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>この図を見ると、Animalが抽象クラスで、Dog、Cat、Humanはそれを実装したクラスとなり、ZooはAnimalのリストを持ってるクラスだということがわかります。</p> <h1>PlantUML</h1> <p><a href="http://plantuml.com/">PlantUML</a>とは、上記で説明した<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>を<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>を記述することですばやく作成できるものです。</p> <p>先程のクラス図は以下のように記述することで生成されます。</p> <pre class="code" data-lang="" data-unlink>@startuml abstract class Animal { - type - color + run } class Dog { + run + eat } class Cat { + run + sleep } class Human { + run + talk } class Zoo { - animalList } Animal &lt;|-- Dog Animal &lt;|-- Cat Animal &lt;|-- Human Zoo *-- Animal @enduml</pre> <p>このように、テキストベースで<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>を記述することができるのがPlantUMLです。(<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>以外にも図示できるらしいです)</p> <p>各図を作図するためにある程度記法を覚える必要がありますが、慣れてしまえばdraw.ioなどで図を書くよりも早く、そしてきれいに図示できると思います。</p> <h1>PlantUML(<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>)の利点</h1> <p>PlantUML(<a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>)を使っていて感じた利点としては以下のようなものがありました。</p> <h2>図を作成することで思考の整理ができる</h2> <p>コードを実際に書く前に設計を図示しておくことで自分の思考が整理され、曖昧だった箇所がはっきりして実装時の手戻りが減ります。</p> <h2>人との考えの共有ができる</h2> <p>言葉で設計について相談するよりも、このようなツールで図示をしてそれをベースに相談したほうが圧倒的に精度が高いと感じました。 言葉の受け取り方は人それぞれな部分もあり、自分が思っていたことと相手の思っていたことが違い手戻りが発生してしまうようなことがあります。 しかし、図示をして相談をすればそういった不明確な要素を排除できます。</p> <h2>実装が楽になる</h2> <p>あらかじめ設計を図示しておくことで、あとはそのとおりに作っていくだけでよくなるので実装時の負担が減ります。 実装中に設計を考えながらやると行き当たりばったりな実装になりがちで、良くないコードになってしまいます。</p> <h2>ドキュメントとして残せる</h2> <p>PlantUMLはテキストベースなのでGit管理もしやすく、ドキュメントとして残す際に便利です。</p> <h1>PlantUMLの欠点</h1> <h2>記法を覚えるのが大変</h2> <p>僕も未だに記法をしっかりと覚えられていないので、書くときに少し時間がかかってしまいます。 頑張って覚えましょう。</p> <h2>どの図を使っていいかわからない</h2> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/UML">UML</a>についてしっかりと理解しないと、クラス図で書くべきなのかオブジェクト図で書くべきなのかがわからなくなってしまったりします。 頑張って理解しましょう。</p> <h1>まとめ</h1> <p>PlantUMLはエンジニアだけが使うツールではないと思います。 例えば、プランナーさんがエンジニアに仕様を伝える際にも、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E6%A1%BC%A5%B9%A5%B1%A1%BC%A5%B9">ユースケース</a>図やシーケンス図が活きてくると思います。</p> <p>是非みんなでPlantUMLを覚えて、適切な議論ができるようにしていきましょう!</p> siguma_sig 【Unity】ゲーム開発におけるStateMachineの有用性 hatenablog://entry/10257846132601727314 2018-07-16T18:56:41+09:00 2018-07-16T18:56:41+09:00 はじめに 最近ゲーム開発での状態管理にStateMachineを用いているのですが、何をしているかがわかりやすくなり変更にも強くて良いと感じているので紹介します。 <h1>はじめに</h1> <p>最近ゲーム開発での状態管理にStateMachineを用いているのですが、何をしているかがわかりやすくなり変更にも強くて良いと感じているので紹介します。</p> <h1>FiniteStateMachine</h1> <p>FiniteStateMachineは日本語でいうと有限<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>です。情報系の大学で学んでいた人なら一度は聞いたことがあるかと思います。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Wikipedia">Wikipedia</a>から説明を引用すると、</p> <blockquote><p>有限<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>または有限状態機械とは、有限個の状態と遷移と動作の組み合わせからなる数学的に抽象化された「ふるまいのモデル」である。</p></blockquote> <p>となります。</p> <p>しっかりとした定義は僕もまだ理解できていませんが、状態と遷移を記述するためのものと理解すれば良いと思います。</p> <p>FiniteStateMachineでは、状態と遷移を以下のような状態遷移図で表します。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180716/20180716175425.png" alt="f:id:siguma_sig:20180716175425p:plain" title="f:id:siguma_sig:20180716175425p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>四角で表されるものが起こりうる状態で、矢印が遷移の条件となります。 図で言えば、状態がIdle、Jump、SecondJumpの3つあり、遷移の条件がFlickUp、Groundとなります。</p> <h1>利点</h1> <h2>バグが出にくくなる</h2> <p>各状態において取りうる遷移のみを受け付けることになるので、意図しない動作が起こりづらくなります。</p> <p>先程の遷移図で具体的に説明します。 図のとおり、初期状態としてIdle状態から始まり、1回目の上フリックによってジャンプをします。 そのとき、地面に着地をする前にもう一度上フリックをすることにより2段ジャンプのStateへと遷移します。</p> <p>通常の実装ではジャンプ中にジャンプを許可するなどとしてしまうと意図せずに3段ジャンプができてしまったりする可能性がありますが、StateMachineを用いた実装ではSecondJump中にFlickUpを受け付けないようになっているので、3段ジャンプができないことが保証されます。 このように、StateMachineにない意図しない動作が起こらなくなるので必然とバグの発生を防ぐことができます。</p> <p>また、StateMachineを用いた実装をするにあたって遷移図を書くことになるのですが、こうすることで仕様の抜けや考慮漏れが発生しづらくなります。</p> <h2>仕様の変更に強い</h2> <p>仕様変更の際には、StateMachineを変更するようにすれば良いので変更箇所がわかりやすくなり、仕様変更に強くなります。</p> <p>こちらも先程の状態遷移図で説明します。 例えば、2段ジャンプの仕様がなくなったとき通常の実装だとコード全体から2段ジャンプに関わる実装を削っていく必要がありますが、StateMachineを用いた実装の場合は2段ジャンプのStateそのものを消し去るだけで完結します。 また、2段ジャンプ中にのみ行えるアクションなどが発生したときも、新たに2段ジャンプから遷移するStateを作って実装をするだけで良く、他の実装に手を加える必要はありません。</p> <h2>実装が理解しやすい</h2> <p>通常の実装では、コード全体を読んで流れを理解する必要がありますが、StateMachineを用いた実装の場合は状態遷移図を理解すれば良いだけです。</p> <h1>欠点</h1> <h2>状態遷移図を用いた実装になれるのが大変</h2> <p>今までのフラグなどを用いて状態を管理していた実装とは異なり、状態遷移図を記述し各状態と遷移を考えながら実装するのは最初はとても大変です。 どんな状態があるかというのを切り出すのもなかなか難しく、各状態からの遷移を漏れなく考えるのも慣れが必要になります。</p> <h1>まとめ</h1> <p>欠点にも書いたとおり、はじめのうちはStateMachineに沿った実装を行うことは今までの考え方とは大きく異なるため大変かもしれません。 しかし、その分かなり大きな利点を得ることができるので、積極的に採用していったほうが良いものだと思います。</p> <p>今回は、具体的な実装方法に関しては触れませんでしたが、今後時間があるときに具体的な実装を交えた記事に関しても書きたいと思います。</p> siguma_sig 【Unity】ToggleボタンのOnOffの状態によって画像を変える hatenablog://entry/17391345971657251095 2018-06-24T15:30:48+09:00 2018-06-24T15:31:03+09:00 はじめに タイトルの通り、ToggleボタンのOnOff状態によって表示するボタンの画像を変える方法です。 ノンコーディングなのでサクッと書きます。 <h1>はじめに</h1> <p>タイトルの通り、ToggleボタンのOnOff状態によって表示するボタンの画像を変える方法です。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CE%A5%F3%A5%B3%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0">ノンコーディング</a>なのでサクッと書きます。</p> <h1>完成形</h1> <p>こんな感じでOnのときは明るいボタン、Offのときは暗いボタンを表示するようにする。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180624/20180624152323.gif" alt="f:id:siguma_sig:20180624152323g:plain" title="f:id:siguma_sig:20180624152323g:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1>やり方</h1> <ol> <li>UI > Toggleからトグルボタン作成</li> <li>子にあるBackgroundにOff時に表示したい素材を適用</li> <li>さらに子にあるCheckmarkにOn時に表示したい素材を適用</li> </ol> <p>これだけです。 Onのときは、Offの画像の上に描画を重ねているだけなのでOnのときとOffのときのボタンのサイズが違うと破綻します。</p> <p>本当に画像を差し替えるだけしたい場合は、コーディングするしかないのかなと思います…(方法あれば教えてください)</p> <h1>まとめ</h1> <p>意外とボタンの画像を変えてるようなものがなかったので、書きました。</p> siguma_sig 【Unity】ディレクトリごとにアセットのインポート設定を変える hatenablog://entry/17391345971657161596 2018-06-24T10:29:44+09:00 2018-06-24T15:31:19+09:00 はじめに アセットをインポートするとき、AssetPostprocessorを用いることでインポート設定を変更したり加工したりできます。 例えば、OnPreprocessTextureだとテクスチャのインポート時にこのメソッドが呼ばれ、処理を行うことができます。 しかし、このままだとインポートした全テクスチャに対して同じ処理が適用されてしまうことになるので、今回はインポートしたディレクトリごとに設定を変える実装をしてみました。 Postprocessor系の勉強で実装したので、実用性があるかはわかりません。 <h1>はじめに</h1> <p>アセットをインポートするとき、<code>AssetPostprocessor</code>を用いることでインポート設定を変更したり加工したりできます。 例えば、<code>OnPreprocessTexture</code>だとテクスチャのインポート時にこのメソッドが呼ばれ、処理を行うことができます。 しかし、このままだとインポートした全テクスチャに対して同じ処理が適用されてしまうことになるので、今回はインポートした<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リごとに設定を変える実装をしてみました。 Postprocessor系の勉強で実装したので、実用性があるかはわかりません。</p> <h1>方針</h1> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リごとに設定を変えるために、<code>ScriptableObject</code>を用います。 ScriptableObjectは、様々なパラメータをアセットデータとして保持しておくことができるものです。</p> <p>これをTextureならテクスチャ用の設定パラメータを含んだもの、Audioならオーディオ用の設定パラメータを含んだものとして作成します。</p> <p>あとは、アセットがインポートされたときにそのアセットの種類に応じた設定用ScriptableObjectがフォルダ内にあるかを確かめて、あればその設定を適用してやるという方針で良さそうです。</p> <h1>実装(コード)</h1> <p>まず、全アセットタイプの共通の処理を行う部分です。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System; <span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> UnityEditor; <span class="synStatement">using</span> System.IO; <span class="synType">public</span> <span class="synType">class</span> CommonProcessor&lt;T&gt; <span class="synStatement">where</span> T : ScriptableObject { <span class="synType">public</span> <span class="synType">void</span> Process(<span class="synType">string</span> assetPath, Action&lt;T&gt; onSetup) { <span class="synType">string</span> dirPath = Path.GetDirectoryName(assetPath); T setting = FindSetting(dirPath); <span class="synStatement">if</span> (setting == <span class="synConstant">null</span>) { Debug.LogWarning($<span class="synConstant">&quot;{typeof(T)} not found at {dirPath}&quot;</span>); <span class="synStatement">return</span>; } onSetup?.Invoke(setting); } <span class="synType">private</span> T FindSetting(<span class="synType">string</span> dirPath) { <span class="synType">string</span>[] assetFilePaths = Directory.GetFiles(dirPath, <span class="synConstant">&quot;*.asset&quot;</span>); <span class="synStatement">foreach</span> (var assetPath <span class="synStatement">in</span> assetFilePaths) { T setting = AssetDatabase.LoadAssetAtPath&lt;T&gt;(assetPath); <span class="synStatement">if</span> (setting != <span class="synConstant">null</span>) { <span class="synStatement">return</span> setting; } } <span class="synStatement">return</span> <span class="synConstant">null</span>; } } </pre> <p>アセットがインポートされたとき、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ内のScriptableObjectを探索して、あったら設定ファイルとしてロードしています。</p> <p>ロードした設定ファイルを使った処理は、コールバックで与えます。余談ですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a> 6.0から使えるNull条件<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B1%E9%BB%BB%BB%D2">演算子</a>がいい感じです。Unityだと、nullの扱いが特殊なのでこういったActionとかにしか使えませんが…</p> <p>次に、例としてTextureに関しての設定を行っている部分です。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> UnityEditor; <span class="synStatement">using</span> Scriptable; <span class="synType">namespace</span> Processor { <span class="synType">public</span> <span class="synType">class</span> TextureProcessor : AssetPostprocessor { <span class="synType">private</span> <span class="synType">void</span> OnPreprocessTexture() { CommonProcessor&lt;TextureSetting&gt; commonProcessor = <span class="synStatement">new</span> CommonProcessor&lt;TextureSetting&gt;(); commonProcessor.Process(assetPath, OnSetup); } <span class="synType">private</span> <span class="synType">void</span> OnSetup(TextureSetting setting) { TextureImporter textureImporter = assetImporter <span class="synStatement">as</span> TextureImporter; textureImporter.textureType = setting.importerType; textureImporter.spriteImportMode = setting.importMode; textureImporter.filterMode = setting.filterMode; textureImporter.wrapMode = setting.wrapMode; textureImporter.spritePixelsPerUnit = setting.pixelPerUnits; } } } </pre> <p>こちらでは、先程作成したCommonProcessorを利用して、処理を行います。 やることは、読み込まれた設定ファイルの値をどう入れるかだけです。</p> <p>最後に、参考までにTextureのパラメータの設定も載せておきます。</p> <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> UnityEngine; <span class="synStatement">using</span> UnityEditor; <span class="synType">namespace</span> Scriptable { <span class="synType">public</span> <span class="synType">class</span> TextureSetting : ScriptableObject { [Header(<span class="synConstant">&quot;テクスチャのインポートタイプ&quot;</span>)] <span class="synType">public</span> TextureImporterType importerType; [Header(<span class="synConstant">&quot;スプライトのインポートモード&quot;</span>)] <span class="synType">public</span> SpriteImportMode importMode; [Header(<span class="synConstant">&quot;テクスチャのピクセル補完方法&quot;</span>)] <span class="synType">public</span> FilterMode filterMode; [Header(<span class="synConstant">&quot;テクスチャの範囲外の設定&quot;</span>)] <span class="synType">public</span> TextureWrapMode wrapMode; [Header(<span class="synConstant">&quot;1mを何ピクセルとするかの設定&quot;</span>)] <span class="synType">public</span> <span class="synType">int</span> pixelPerUnits; } } </pre> <p>テクスチャをあるフォルダにロードしたときに、同じ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ内にこの設定ファイルがあるかを見て処理をします。</p> <h1>まとめ</h1> <p>テクスチャとかは設定が複雑なので実際に使う場合は、パラメータ増えてまくって大変そう… あと、Unity2018からはPresetを使えばよさそう…?</p> siguma_sig 【Unity】ScriptableObjectを作成するエディタ拡張 hatenablog://entry/17391345971655024431 2018-06-17T17:11:55+09:00 2018-06-24T15:17:43+09:00 はじめに 色々なパラメータをアセットデータとして保持することができるScriptableObjectですが、一般的に以下のようなクラスを作成して作ります。 using UnityEngine; using UnityEditor; [CreateAssetMenu(menuName = "ScriptableObject/ExampleAsset")] public class ExampleAsset : ScriptableObject { } このように書くと、以下のようなMenuが追加されExampleAssetの作成が可能となります。 しかし、開発が進むにつれて作成したいScript… <h1>はじめに</h1> <p>色々なパラメータをアセットデータとして保持することができるScriptableObjectですが、一般的に以下のようなクラスを作成して作ります。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> UnityEditor; [CreateAssetMenu(menuName = <span class="synConstant">&quot;ScriptableObject/ExampleAsset&quot;</span>)] <span class="synType">public</span> <span class="synType">class</span> ExampleAsset : ScriptableObject { } </pre> <p>このように書くと、以下のようなMenuが追加されExampleAssetの作成が可能となります。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180617/20180617165745.png" alt="f:id:siguma_sig:20180617165745p:plain" title="f:id:siguma_sig:20180617165745p:plain" class="hatena-fotolife" itemprop="image"></span></p> <p>しかし、開発が進むにつれて作成したいScriptableObjectの数が増えてくるとともに、Menuに追加される項目も増えます。 丁寧に階層分けをするようにパスを記述しても良いですが、めんどくさいので今回はScriptableObjectを渡すと任意の場所にアセットを作成してくれるエディタ拡張を書いたので、コードを載せておきます。</p> <h1>作ったもの</h1> <p>以下のようなウィンドウで、ScriptableObjectを作成します。 TargetScriptにアセットを作りたいScriptableObjectを渡して上げることで簡単にアセットを作成できます。 ScriptableObjectのソースファイル側にはパス等を記述する必要がなくなるので、管理がしやすくなると思います。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20180617/20180617170230.png" alt="f:id:siguma_sig:20180617170230p:plain" title="f:id:siguma_sig:20180617170230p:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1>コード</h1> <p>以下が<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>です。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEditor; <span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.IO; <span class="synType">public</span> <span class="synType">class</span> ScriptableObjectCreator : ScriptableWizard { [SerializeField] <span class="synType">private</span> MonoScript _targetScript; [SerializeField] <span class="synType">private</span> <span class="synType">string</span> _outputObjectName; [SerializeField] <span class="synType">private</span> <span class="synType">string</span> _outputDirectory; <span class="synType">private</span> <span class="synType">static</span> <span class="synType">readonly</span> <span class="synType">string</span> _outputPrefix = <span class="synConstant">&quot;Generated&quot;</span>; <span class="synType">private</span> <span class="synType">static</span> <span class="synType">readonly</span> <span class="synType">string</span> _outputSuffix = <span class="synConstant">&quot;.asset&quot;</span>; <span class="synType">private</span> MonoScript _targetCache = <span class="synConstant">null</span>; [MenuItem(<span class="synConstant">&quot;ScriptableObject/Creator&quot;</span>)] <span class="synType">private</span> <span class="synType">static</span> <span class="synType">void</span> Open() { DisplayWizard&lt;ScriptableObjectCreator&gt;(<span class="synConstant">&quot;Scriptable Object Creator&quot;</span>); } <span class="synType">private</span> <span class="synType">void</span> OnWizardCreate() { SafeCreateDirectory(_outputDirectory); var createdObject = CreateInstance(_targetScript.name); var outputPath = Path.Combine(_outputDirectory, _outputObjectName + _outputSuffix); var uniqueOutputPath = AssetDatabase.GenerateUniqueAssetPath(outputPath); AssetDatabase.CreateAsset(createdObject, uniqueOutputPath); AssetDatabase.Refresh(); } <span class="synType">private</span> <span class="synType">void</span> OnWizardUpdate() { <span class="synStatement">if</span> (_targetScript != <span class="synConstant">null</span> &amp;&amp; _targetCache != _targetScript) { _outputObjectName = _outputPrefix + _targetScript.name; _outputDirectory = Path.GetDirectoryName(AssetDatabase.GetAssetPath(_targetScript)); _targetCache = _targetScript; } isValid = _targetScript != <span class="synConstant">null</span> &amp;&amp; !<span class="synType">string</span>.IsNullOrEmpty(_outputObjectName) &amp;&amp; !<span class="synType">string</span>.IsNullOrEmpty(_outputDirectory); } <span class="synType">private</span> <span class="synType">void</span> SafeCreateDirectory(<span class="synType">string</span> path) { var currentPath = <span class="synConstant">&quot;&quot;</span>; var splitChar = <span class="synStatement">new</span> <span class="synType">char</span>[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; <span class="synStatement">foreach</span> (var dir <span class="synStatement">in</span> path.Split(splitChar)) { var parent = currentPath; currentPath = Path.Combine(currentPath, dir); <span class="synStatement">if</span> (!AssetDatabase.IsValidFolder(currentPath)) { AssetDatabase.CreateFolder(parent, dir); } } } } </pre> <h1>コードの説明</h1> <p>今回は、アセットを作成するので楽をするためにScriptableWizardを使ってみました。<br/> 結果的に、1個作るたびにウィンドウが勝手に閉じて面倒なので、普通にEditorWindowにしとけばよかったと思いました…</p> <p>ポイントは、ScriptableObjectアセットを作成する元の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を受け取るために<em>MonoScript</em>を変数として定義しておくことくらいです。</p> <p>あとは、ファイル名やパスは基本的にはデフォルト値がセットされるようになっているのと、パスに存在しない<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リがあったら作成してから処理をするようにしているだけです。</p> <h1>参考</h1> <p><a href="http://light11.hatenadiary.com/entry/2018/03/22/191516">&#x3010;Unity&#x3011;&#x3010;&#x30A8;&#x30C7;&#x30A3;&#x30BF;&#x62E1;&#x5F35;&#x3011;&#x30B9;&#x30AF;&#x30EA;&#x30D7;&#x30C8;&#x304B;&#x3089;&#x30B9;&#x30AF;&#x30EA;&#x30D7;&#x30C8;&#x30D5;&#x30A1;&#x30A4;&#x30EB;(.cs)&#x3092;&#x751F;&#x6210;&#x3059;&#x308B; - LIGHT11</a></p> siguma_sig ReactNative+TypeScript+Formikでフォーム画面を作る hatenablog://entry/17391345971644063094 2018-05-13T14:00:09+09:00 2018-06-24T15:18:42+09:00 はじめに ReactNative上でログイン機能を実装するために、フォームを実装する必要があったのですが、Formikが想像以上に良かったので書き残します。 説明雑なので、コードを参考にする程度で読んでいただければと思います。 環境は、 ReactNative 0.55.3 Formik 1.0.0-alpha.6 Yup 0.24.1 TypeScript 2.7.2 <h1>はじめに</h1> <p>ReactNative上でログイン機能を実装するために、フォームを実装する必要があったのですが、Formikが想像以上に良かったので書き残します。 説明雑なので、コードを参考にする程度で読んでいただければと思います。</p> <p>環境は、</p> <ul> <li>ReactNative 0.55.3</li> <li>Formik 1.0.0-alpha.6</li> <li>Yup 0.24.1</li> <li>TypeScript 2.7.2</li> </ul> <h1>Formikとは</h1> <p><a href="https://github.com/jaredpalmer/formik">Formik</a>とは、ReactやReactNativeで使用できるフォーム管理のためのライブラリで、以下のような特徴があります</p> <ul> <li>バリデーションが簡単にできる</li> <li>送信ボタンの制御も簡単にできる</li> <li>フォームのStateを簡単に受け取れる</li> <li>State管理のライブラリに依存しない</li> </ul> <h1>使い方</h1> <p>普通にComponentとして書く場合と、HOCとして自分で定義したFormのViewを包んでやる方法があります。<br/> 今回は、HOCを利用する方法で書きました。こちらのほうがViewとフォームの制御に関しての記述が分かれてわかりやすいと思いました。<br/> 実装は、<a href="https://github.com/jaredpalmer/formik#using-formik-with-typescript">Using Formik with TypeScript</a>を参考にしました。<br/> 以下が実装したコード全体です。</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> FormikProps<span class="synStatement">,</span> withFormik <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">&quot;formik&quot;</span> <span class="synStatement">import</span> * <span class="synStatement">as</span> React <span class="synStatement">from</span> <span class="synConstant">&quot;react&quot;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> StyleSheet<span class="synStatement">,</span> View<span class="synStatement">,</span> ViewStyle <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">&quot;react-native&quot;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> <span class="synConstant">Button</span><span class="synStatement">,</span> FormInput<span class="synStatement">,</span> FormLabel<span class="synStatement">,</span> FormValidationMessage<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">&quot;react-native-elements&quot;</span> <span class="synStatement">import</span> Yup <span class="synStatement">from</span> <span class="synConstant">&quot;yup&quot;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> login <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">&quot;../actions/auth&quot;</span> <span class="synStatement">interface</span> FormValues <span class="synIdentifier">{</span> email: <span class="synType">string</span> password: <span class="synType">string</span> url: <span class="synType">string</span> <span class="synIdentifier">}</span> <span class="synStatement">interface</span> OtherProps <span class="synIdentifier">{</span> message: <span class="synType">string</span> <span class="synIdentifier">}</span> <span class="synStatement">const</span> InnerForm <span class="synStatement">=</span> <span class="synStatement">(</span>props: OtherProps &amp; FormikProps<span class="synStatement">&lt;</span>FormValues<span class="synStatement">&gt;)</span> <span class="synStatement">=&gt;</span> <span class="synStatement">(</span> <span class="synStatement">&lt;</span>View<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormLabel<span class="synStatement">&gt;</span>SiteURL<span class="synStatement">&lt;</span>/FormLabel<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormInput onChangeText<span class="synStatement">=</span><span class="synIdentifier">{</span>text <span class="synStatement">=&gt;</span> props.setFieldValue<span class="synStatement">(</span><span class="synConstant">&quot;url&quot;</span><span class="synStatement">,</span> text<span class="synStatement">)</span><span class="synIdentifier">}</span> value<span class="synStatement">=</span><span class="synIdentifier">{</span>props.values.url<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>SwitchFormValidationMessage text<span class="synStatement">=</span><span class="synIdentifier">{</span>props.errors.url<span class="synIdentifier">}</span> submitCount<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitCount<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormLabel<span class="synStatement">&gt;</span>Email<span class="synStatement">&lt;</span>/FormLabel<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormInput onChangeText<span class="synStatement">=</span><span class="synIdentifier">{</span>text <span class="synStatement">=&gt;</span> props.setFieldValue<span class="synStatement">(</span><span class="synConstant">&quot;email&quot;</span><span class="synStatement">,</span> text<span class="synStatement">)</span><span class="synIdentifier">}</span> value<span class="synStatement">=</span><span class="synIdentifier">{</span>props.values.email<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>SwitchFormValidationMessage text<span class="synStatement">=</span><span class="synIdentifier">{</span>props.errors.email<span class="synIdentifier">}</span> submitCount<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitCount<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormLabel<span class="synStatement">&gt;</span>Password<span class="synStatement">&lt;</span>/FormLabel<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormInput onChangeText<span class="synStatement">=</span><span class="synIdentifier">{</span>text <span class="synStatement">=&gt;</span> props.setFieldValue<span class="synStatement">(</span><span class="synConstant">&quot;password&quot;</span><span class="synStatement">,</span> text<span class="synStatement">)</span><span class="synIdentifier">}</span> value<span class="synStatement">=</span><span class="synIdentifier">{</span>props.values.password<span class="synIdentifier">}</span> secureTextEntry<span class="synStatement">=</span><span class="synIdentifier">{</span><span class="synConstant">true</span><span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>SwitchFormValidationMessage text<span class="synStatement">=</span><span class="synIdentifier">{</span>props.errors.password<span class="synIdentifier">}</span> submitCount<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitCount<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span><span class="synConstant">Button</span> onPress<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitForm<span class="synIdentifier">}</span> title<span class="synStatement">=</span><span class="synIdentifier">{</span>props.message<span class="synIdentifier">}</span> buttonStyle<span class="synStatement">=</span><span class="synIdentifier">{</span>styles.loginButton<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>/View<span class="synStatement">&gt;</span> <span class="synStatement">)</span> <span class="synStatement">interface</span> SwitchFormProps <span class="synIdentifier">{</span> text: <span class="synType">any</span> submitCount: <span class="synType">number</span> <span class="synIdentifier">}</span> <span class="synStatement">const</span> SwitchFormValidationMessage <span class="synStatement">=</span> <span class="synStatement">(</span>props: SwitchFormProps<span class="synStatement">)</span> <span class="synStatement">=&gt;</span> <span class="synIdentifier">{</span> <span class="synStatement">if</span> <span class="synStatement">(</span>props.submitCount <span class="synStatement">&gt;</span> <span class="synConstant">0</span><span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">return</span> <span class="synStatement">&lt;</span>FormValidationMessage<span class="synStatement">&gt;</span><span class="synIdentifier">{</span>props.text<span class="synIdentifier">}</span><span class="synStatement">&lt;</span>/FormValidationMessage<span class="synStatement">&gt;</span> <span class="synIdentifier">}</span> <span class="synStatement">return</span> <span class="synStatement">&lt;</span>FormValidationMessage /<span class="synStatement">&gt;</span> <span class="synIdentifier">}</span> <span class="synStatement">interface</span> LoginFromProps <span class="synIdentifier">{</span> message: <span class="synType">string</span> <span class="synIdentifier">}</span> <span class="synStatement">const</span> LoginForm <span class="synStatement">=</span> withFormik<span class="synStatement">&lt;</span>LoginFromProps<span class="synStatement">,</span> FormValues<span class="synStatement">&gt;(</span><span class="synIdentifier">{</span> mapPropsToValues: <span class="synStatement">()</span> <span class="synStatement">=&gt;</span> <span class="synIdentifier">{</span> <span class="synStatement">return</span> <span class="synIdentifier">{</span> email: <span class="synConstant">&quot;&quot;</span><span class="synStatement">,</span> password: <span class="synConstant">&quot;&quot;</span><span class="synStatement">,</span> url: <span class="synConstant">&quot;&quot;</span><span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> validationSchema: Yup.object<span class="synStatement">()</span>.shape<span class="synStatement">(</span><span class="synIdentifier">{</span> email: Yup.<span class="synType">string</span><span class="synStatement">()</span> .email<span class="synStatement">(</span><span class="synConstant">&quot;Please input Email Address&quot;</span><span class="synStatement">)</span> .required<span class="synStatement">(</span><span class="synConstant">&quot;Email Address is required!&quot;</span><span class="synStatement">),</span> password: Yup.<span class="synType">string</span><span class="synStatement">()</span> .max<span class="synStatement">(</span><span class="synConstant">36</span><span class="synStatement">,</span> <span class="synConstant">&quot;Please enter password in 36 characters or less&quot;</span><span class="synStatement">)</span> .required<span class="synStatement">(</span><span class="synConstant">&quot;Password is required!&quot;</span><span class="synStatement">),</span> url: Yup.<span class="synType">string</span><span class="synStatement">()</span> .url<span class="synStatement">(</span><span class="synConstant">&quot;Please input your site url&quot;</span><span class="synStatement">)</span> .required<span class="synStatement">(</span><span class="synConstant">&quot;URL is required!&quot;</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">),</span> handleSubmit: values <span class="synStatement">=&gt;</span> <span class="synIdentifier">{</span> login<span class="synStatement">(</span>values.email<span class="synStatement">,</span> values.password<span class="synStatement">,</span> values.url<span class="synStatement">)</span> .then<span class="synStatement">(</span>token <span class="synStatement">=&gt;</span> console.log<span class="synStatement">(</span>token<span class="synStatement">))</span> .<span class="synSpecial">catch</span><span class="synStatement">(</span>error <span class="synStatement">=&gt;</span> console.log<span class="synStatement">(</span>error<span class="synStatement">))</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)(</span>InnerForm<span class="synStatement">)</span> <span class="synStatement">interface</span> <span class="synConstant">Style</span> <span class="synIdentifier">{</span> loginButton: ViewStyle <span class="synIdentifier">}</span> <span class="synStatement">const</span> styles <span class="synStatement">=</span> StyleSheet.create<span class="synStatement">&lt;</span><span class="synConstant">Style</span><span class="synStatement">&gt;(</span><span class="synIdentifier">{</span> loginButton: <span class="synIdentifier">{</span> backgroundColor: <span class="synConstant">&quot;#0086F6&quot;</span><span class="synStatement">,</span> borderRadius: <span class="synConstant">2</span><span class="synStatement">,</span> marginTop: <span class="synConstant">20</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">export</span> <span class="synStatement">default</span> LoginForm </pre> <h1>コードの説明</h1> <p>まず、withFormikで包むためのフォームのViewを定義した<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>ここでは、Form画面をどのように構成するかを記述します。 今回はReactNativeElementsというUI<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> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">const</span> InnerForm <span class="synStatement">=</span> <span class="synStatement">(</span>props: OtherProps &amp; FormikProps<span class="synStatement">&lt;</span>FormValues<span class="synStatement">&gt;)</span> <span class="synStatement">=&gt;</span> <span class="synStatement">(</span> <span class="synStatement">&lt;</span>View<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormLabel<span class="synStatement">&gt;</span>SiteURL<span class="synStatement">&lt;</span>/FormLabel<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormInput onChangeText<span class="synStatement">=</span><span class="synIdentifier">{</span>text <span class="synStatement">=&gt;</span> props.setFieldValue<span class="synStatement">(</span><span class="synConstant">&quot;url&quot;</span><span class="synStatement">,</span> text<span class="synStatement">)</span><span class="synIdentifier">}</span> value<span class="synStatement">=</span><span class="synIdentifier">{</span>props.values.url<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>SwitchFormValidationMessage text<span class="synStatement">=</span><span class="synIdentifier">{</span>props.errors.url<span class="synIdentifier">}</span> submitCount<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitCount<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormLabel<span class="synStatement">&gt;</span>Email<span class="synStatement">&lt;</span>/FormLabel<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormInput onChangeText<span class="synStatement">=</span><span class="synIdentifier">{</span>text <span class="synStatement">=&gt;</span> props.setFieldValue<span class="synStatement">(</span><span class="synConstant">&quot;email&quot;</span><span class="synStatement">,</span> text<span class="synStatement">)</span><span class="synIdentifier">}</span> value<span class="synStatement">=</span><span class="synIdentifier">{</span>props.values.email<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>SwitchFormValidationMessage text<span class="synStatement">=</span><span class="synIdentifier">{</span>props.errors.email<span class="synIdentifier">}</span> submitCount<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitCount<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormLabel<span class="synStatement">&gt;</span>Password<span class="synStatement">&lt;</span>/FormLabel<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>FormInput onChangeText<span class="synStatement">=</span><span class="synIdentifier">{</span>text <span class="synStatement">=&gt;</span> props.setFieldValue<span class="synStatement">(</span><span class="synConstant">&quot;password&quot;</span><span class="synStatement">,</span> text<span class="synStatement">)</span><span class="synIdentifier">}</span> value<span class="synStatement">=</span><span class="synIdentifier">{</span>props.values.password<span class="synIdentifier">}</span> secureTextEntry<span class="synStatement">=</span><span class="synIdentifier">{</span><span class="synConstant">true</span><span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>SwitchFormValidationMessage text<span class="synStatement">=</span><span class="synIdentifier">{</span>props.errors.password<span class="synIdentifier">}</span> submitCount<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitCount<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span><span class="synConstant">Button</span> onPress<span class="synStatement">=</span><span class="synIdentifier">{</span>props.submitForm<span class="synIdentifier">}</span> title<span class="synStatement">=</span><span class="synIdentifier">{</span>props.message<span class="synIdentifier">}</span> buttonStyle<span class="synStatement">=</span><span class="synIdentifier">{</span>styles.loginButton<span class="synIdentifier">}</span> /<span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>/View<span class="synStatement">&gt;</span> <span class="synStatement">)</span> </pre> <p>また、TypeScriptを使用しているので予めFormValuesというinterfaceを定義して、その中にFormで入力する要素を定義しています。<br/> その他、渡したいデータに関してはOtherPropsに定義します。</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">interface</span> FormValues <span class="synIdentifier">{</span> email: <span class="synType">string</span> password: <span class="synType">string</span> url: <span class="synType">string</span> <span class="synIdentifier">}</span> <span class="synStatement">interface</span> OtherProps <span class="synIdentifier">{</span> message: <span class="synType">string</span> <span class="synIdentifier">}</span> </pre> <p>ここで、一点注意が必要なのが、送信ボタンのonPressに渡しているメソッドについてです。<br/> Formik公式のドキュメントを見ると、props.handleSubmitを渡していますが、<strong>ReactNativeの場合props.handleSubmitではonPressに渡すのに型が合わないので、今回はprops.submitFormを渡します。</strong><br/> こちらに関しては、公式で<a href="https://github.com/jaredpalmer/formik/issues/376">Issue</a>が立てられており、そこの回答を見るとひとまずsubmitFormで対応してくれとのことでした。</p> <p>次に、ロジックの部分に関して説明します。 こちらは、withFormikで自分で定義したForm<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>を包み、そのForm<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> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">const</span> LoginForm <span class="synStatement">=</span> withFormik<span class="synStatement">&lt;</span>LoginFromProps<span class="synStatement">,</span> FormValues<span class="synStatement">&gt;(</span><span class="synIdentifier">{</span> mapPropsToValues: <span class="synStatement">()</span> <span class="synStatement">=&gt;</span> <span class="synIdentifier">{</span> <span class="synStatement">return</span> <span class="synIdentifier">{</span> email: <span class="synConstant">&quot;&quot;</span><span class="synStatement">,</span> password: <span class="synConstant">&quot;&quot;</span><span class="synStatement">,</span> url: <span class="synConstant">&quot;&quot;</span><span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> validationSchema: Yup.object<span class="synStatement">()</span>.shape<span class="synStatement">(</span><span class="synIdentifier">{</span> email: Yup.<span class="synType">string</span><span class="synStatement">()</span> .email<span class="synStatement">(</span><span class="synConstant">&quot;Please input Email Address&quot;</span><span class="synStatement">)</span> .required<span class="synStatement">(</span><span class="synConstant">&quot;Email Address is required!&quot;</span><span class="synStatement">),</span> password: Yup.<span class="synType">string</span><span class="synStatement">()</span> .max<span class="synStatement">(</span><span class="synConstant">36</span><span class="synStatement">,</span> <span class="synConstant">&quot;Please enter password in 36 characters or less&quot;</span><span class="synStatement">)</span> .required<span class="synStatement">(</span><span class="synConstant">&quot;Password is required!&quot;</span><span class="synStatement">),</span> url: Yup.<span class="synType">string</span><span class="synStatement">()</span> .url<span class="synStatement">(</span><span class="synConstant">&quot;Please input your site url&quot;</span><span class="synStatement">)</span> .required<span class="synStatement">(</span><span class="synConstant">&quot;URL is required!&quot;</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">),</span> handleSubmit: values <span class="synStatement">=&gt;</span> <span class="synIdentifier">{</span> login<span class="synStatement">(</span>values.email<span class="synStatement">,</span> values.password<span class="synStatement">,</span> values.url<span class="synStatement">)</span> .then<span class="synStatement">(</span>token <span class="synStatement">=&gt;</span> console.log<span class="synStatement">(</span>token<span class="synStatement">))</span> .<span class="synSpecial">catch</span><span class="synStatement">(</span>error <span class="synStatement">=&gt;</span> console.log<span class="synStatement">(</span>error<span class="synStatement">))</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)(</span>InnerForm<span class="synStatement">)</span> </pre> <p>mapPropsToValuesで、各フォームの値の初期値を設定しています。<br/> validationSchemaでは、Yupというバリデーションライブラリを用いて、バリデーションを行っています。 FormikとYupの相性は非常によく、公式でもYupを使うことが推奨されており、コードを見ていただくとわかるように非常に直感的にバリデーションが書けます。<br/> <strong>validationSchemeでバリデーションを定義しておくことで、バリデーションエラーの場合はsubmitが発火しないようになります。</strong> 最後に、handleSubmitに送信ボタンが押されたときの挙動を書きます。</p> <h1>まとめ</h1> <p>Formikは公式ドキュメントもしっかりしており、便利でしたが意外と記事が少なかったので書いてみました。 TypeScriptでの実装例として参考にしていただければと思います。</p> siguma_sig ReactNativeでTypeScript+Prettier+TSlintな開発環境構築手順 hatenablog://entry/17391345971642397212 2018-05-07T22:32:03+09:00 2018-06-24T15:19:05+09:00 はじめに 定期的にReactNativeでネイティブアプリを作りたくなる時期がくるのですが、そのたびに環境構築に手間取るので、備忘録として残しておきます。 その設定おかしいよとかあれば教えてください。 <h1>はじめに</h1> <p>定期的にReactNativeでネイティブアプリを作りたくなる時期がくるのですが、そのたびに環境構築に手間取るので、備忘録として残しておきます。</p> <p>その設定おかしいよとかあれば教えてください。</p> <h1>導入手順</h1> <p>前提</p> <ol> <li>nodeやwatchmanやyarnは導入済み(ない人は<a class="keyword" href="http://d.hatena.ne.jp/keyword/brew">brew</a>で入れてください)</li> <li>エディタは<a href="https://code.visualstudio.com/">VisualStudioCode</a>(WebStormとかでも動くとは思います)</li> </ol> <h4>ReactNativeの導入</h4> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn global add react-native-cli </pre> <p>上記のコマンドで、ReactNativeプロジェクトを作成するための準備が整います。</p> <h4>プロジェクト作成</h4> <pre class="code lang-sh" data-lang="sh" data-unlink>react-native init testApp </pre> <h4>TypeScript導入</h4> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn add <span class="synSpecial">--dev</span> typescript </pre> <h4>型の導入</h4> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn add <span class="synSpecial">--dev</span> @types/react @types/react-native </pre> <p>型は自分の使いたいライブラリによって随時導入が必要です。基本は、上記のように@types/ライブラリ名でインストールできますが、マイナーなライブラリは型定義がない場合もあります。</p> <h4>tsconfig.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>の編集</h4> <p>TypeScriptの設定ファイルであるtsconfig.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>を編集します。 各オプションは、各々でカスタマイズしてください。 例として、僕のtsconfig.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>を載せます。</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">compilerOptions</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">target</span>&quot;: &quot;<span class="synConstant">es2017</span>&quot;, &quot;<span class="synStatement">module</span>&quot;: &quot;<span class="synConstant">es2015</span>&quot;, &quot;<span class="synStatement">jsx</span>&quot;: &quot;<span class="synConstant">react-native</span>&quot;, &quot;<span class="synStatement">outDir</span>&quot;: &quot;<span class="synConstant">out</span>&quot;, &quot;<span class="synStatement">rootDir</span>&quot;: &quot;<span class="synConstant">src</span>&quot;, &quot;<span class="synStatement">allowJs</span>&quot;: <span class="synConstant">false</span>, &quot;<span class="synStatement">allowSyntheticDefaultImports</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">noImplicitAny</span>&quot;: <span class="synConstant">false</span>, &quot;<span class="synStatement">noImplicitReturns</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">noUnusedParameters</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">noUnusedLocals</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">preserveConstEnums</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">sourceMap</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">lib</span>&quot;: <span class="synSpecial">[</span>&quot;<span class="synConstant">dom</span>&quot;, &quot;<span class="synConstant">es2015</span>&quot;, &quot;<span class="synConstant">es2016</span>&quot;<span class="synSpecial">]</span>, &quot;<span class="synStatement">experimentalDecorators</span>&quot;: <span class="synConstant">true</span> <span class="synSpecial">}</span>, &quot;<span class="synStatement">exclude</span>&quot;: <span class="synSpecial">[</span>&quot;<span class="synConstant">android</span>&quot;, &quot;<span class="synConstant">ios</span>&quot;, &quot;<span class="synConstant">out</span>&quot;, &quot;<span class="synConstant">node_modules</span>&quot;<span class="synSpecial">]</span>, &quot;<span class="synStatement">compileOnSave</span>&quot;: <span class="synConstant">true</span> <span class="synSpecial">}</span> </pre> <h4>Prettierの導入</h4> <p>Prettierはコードフォーマッターで、フロントエンドで使われる言語は全般対応しており、便利なやつです。<br/> 雑にコードを書いても保存時にフォーマットの設定をしておけばフォーマットしてくれるのですごくいいです。 チーム開発でも、フォーマットが揃ってすごくいいと思います。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn add <span class="synSpecial">--dev</span> prettier </pre> <h4>.prettierrcの作成</h4> <p>prettierの設定ファイルです。プロジェクトのルート<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リに置けば、ここで設定したとおりに整形してくれます。 なくても動くと思います。</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">singleQuote</span>&quot;: <span class="synConstant">false</span>, &quot;<span class="synStatement">trailingComma</span>&quot;: &quot;<span class="synConstant">es5</span>&quot;, &quot;<span class="synStatement">semi</span>&quot;: <span class="synConstant">false</span> <span class="synSpecial">}</span> </pre> <h4>TSlintの導入</h4> <p>TSlintは、TypeScript用のlinterです。linterは、コードを監視してだめな書き方してる場合とかに教えてくれるやつです。<br/> なくてもいいですがあると捗ります。特に初心者の場合はこれを入れておくことでだめな書き方に気づけるので絶対いれたほうがいいです。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn add <span class="synSpecial">--dev</span> tslint tslint-react </pre> <h4>TSlintとPrettierを併用するための<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>導入</h4> <p>今回は、PrettierとTSlintが競合した場合に、Prettierを優先するようにします。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn add prettier <span class="synSpecial">--dev</span> tslint-config-prettier tslint-plugin-prettier </pre> <h4>tslint.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>の作成</h4> <p>さっきインストールした<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>たちを有効にするために、tslint.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>を作成して以下のように記述します。 rulesは細かく設定をできるので、興味のある人は調べてみてください。</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">extends</span>&quot;: <span class="synSpecial">[</span>&quot;<span class="synConstant">tslint-react</span>&quot;, &quot;<span class="synConstant">tslint-config-prettier</span>&quot;<span class="synSpecial">]</span>, &quot;<span class="synStatement">rulesDirectory</span>&quot;: <span class="synSpecial">[</span>&quot;<span class="synConstant">tslint-plugin-prettier</span>&quot;<span class="synSpecial">]</span>, &quot;<span class="synStatement">rules</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">prettier</span>&quot;: <span class="synConstant">true</span><span class="synError">,</span> <span class="synError"> }</span> <span class="synSpecial">}</span> </pre> <h4><a class="keyword" href="http://d.hatena.ne.jp/keyword/VSCode">VSCode</a>上の設定</h4> <p>まず、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B3%C8%C4%A5%B5%A1%C7%BD">拡張機能</a>のTSLintとPrettierをインストールしてください。 また、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VSCode">VSCode</a>の設定からsettings.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>を編集します。</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">editor.formatOnType</span>&quot;: <span class="synConstant">true</span>, &quot;<span class="synStatement">editor.formatOnSave</span>&quot;: <span class="synConstant">true</span> <span class="synSpecial">}</span> </pre> <p>これらの設定をすることで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VSCode">VSCode</a>上でコーディングをしながらtslintやprettierが自動で走るようになります。</p> <h4>作成したプロジェクトを編集してみる</h4> <p>src<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リを作成し、App.jsをその下に移動、さらに拡張子を.<a class="keyword" href="http://d.hatena.ne.jp/keyword/tsx">tsx</a>に変更します。 そして、以下のようにファイルを編集しました。<br/> styleとかに型がついて、<a class="keyword" href="http://d.hatena.ne.jp/keyword/flex">flex</a>とかfontSizeの補完が効くようになります。便利。</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> React<span class="synStatement">,</span> <span class="synIdentifier">{</span> Component <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">&quot;react&quot;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Platform<span class="synStatement">,</span> StyleSheet<span class="synStatement">,</span> <span class="synType">Text</span><span class="synStatement">,</span> TextStyle<span class="synStatement">,</span> View<span class="synStatement">,</span> ViewStyle<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">&quot;react-native&quot;</span> <span class="synStatement">const</span> instructions: <span class="synType">string</span> <span class="synStatement">=</span> Platform.select<span class="synStatement">(</span><span class="synIdentifier">{</span> ios: <span class="synConstant">&quot;Press Cmd+R to reload,</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span> + <span class="synConstant">&quot;Cmd+D or shake for dev menu&quot;</span><span class="synStatement">,</span> android: <span class="synConstant">&quot;Double tap R on your keyboard to reload,</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span> + <span class="synConstant">&quot;Shake or press menu button for dev menu&quot;</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">interface</span> Props <span class="synIdentifier">{}</span> <span class="synStatement">export</span> <span class="synStatement">default</span> <span class="synStatement">class</span> App <span class="synStatement">extends</span> Component<span class="synStatement">&lt;</span>Props<span class="synStatement">&gt;</span> <span class="synIdentifier">{</span> render<span class="synStatement">()</span> <span class="synIdentifier">{</span> <span class="synStatement">return</span> <span class="synStatement">(</span> <span class="synStatement">&lt;</span>View style<span class="synStatement">=</span><span class="synIdentifier">{</span>styles.container<span class="synIdentifier">}</span><span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span><span class="synType">Text</span> style<span class="synStatement">=</span><span class="synIdentifier">{</span>styles.welcome<span class="synIdentifier">}</span><span class="synStatement">&gt;</span>Welcome to React Native!<span class="synStatement">&lt;</span>/<span class="synType">Text</span><span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span><span class="synType">Text</span> style<span class="synStatement">=</span><span class="synIdentifier">{</span>styles.instructions<span class="synIdentifier">}</span><span class="synStatement">&gt;</span>To get started<span class="synStatement">,</span> edit App.js<span class="synStatement">&lt;</span>/<span class="synType">Text</span><span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span><span class="synType">Text</span> style<span class="synStatement">=</span><span class="synIdentifier">{</span>styles.instructions<span class="synIdentifier">}</span><span class="synStatement">&gt;</span><span class="synIdentifier">{</span>instructions<span class="synIdentifier">}</span><span class="synStatement">&lt;</span>/<span class="synType">Text</span><span class="synStatement">&gt;</span> <span class="synStatement">&lt;</span>/View<span class="synStatement">&gt;</span> <span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> <span class="synStatement">interface</span> <span class="synConstant">Style</span> <span class="synIdentifier">{</span> container: ViewStyle welcome: TextStyle instructions: TextStyle <span class="synIdentifier">}</span> <span class="synStatement">const</span> styles <span class="synStatement">=</span> StyleSheet.create<span class="synStatement">&lt;</span><span class="synConstant">Style</span><span class="synStatement">&gt;(</span><span class="synIdentifier">{</span> container: <span class="synIdentifier">{</span> flex: <span class="synConstant">1</span><span class="synStatement">,</span> justifyContent: <span class="synConstant">&quot;center&quot;</span><span class="synStatement">,</span> alignItems: <span class="synConstant">&quot;center&quot;</span><span class="synStatement">,</span> backgroundColor: <span class="synConstant">&quot;#F5FCFF&quot;</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> welcome: <span class="synIdentifier">{</span> fontSize: <span class="synConstant">20</span><span class="synStatement">,</span> textAlign: <span class="synConstant">&quot;center&quot;</span><span class="synStatement">,</span> margin: <span class="synConstant">10</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> instructions: <span class="synIdentifier">{</span> textAlign: <span class="synConstant">&quot;center&quot;</span><span class="synStatement">,</span> color: <span class="synConstant">&quot;#333333&quot;</span><span class="synStatement">,</span> marginBottom: <span class="synConstant">5</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> </pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>した結果は、out<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リに出力されるので、index.jsはout<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ以下を見に行くように変更します。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> AppRegistry <span class="synIdentifier">}</span> from <span class="synConstant">&quot;react-native&quot;</span> <span class="synStatement">import</span> App from <span class="synConstant">&quot;./out/App&quot;</span> AppRegistry.registerComponent(<span class="synConstant">&quot;sampleApp&quot;</span>, () =&gt; App) </pre> <h4>TypeScriptファイルを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a></h4> <pre class="code" data-lang="" data-unlink>tsc</pre> <p>基本は上記のコマンドでいいですが、一々<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>するのも面倒なので、package.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>に以下のように定義しておくと便利です。</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">start</span>&quot;: &quot;<span class="synConstant">tsc --watch &amp; node node_modules/react-native/local-cli/cli.js start</span>&quot; <span class="synSpecial">}</span> </pre> <p>こうすると、</p> <pre class="code lang-sh" data-lang="sh" data-unlink>yarn <span class="synStatement">start</span> </pre> <p>上記のコマンドで監視を開始すれば、エディタで保存するたびにTypeScriptから<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>が走ります。</p> <h1>まとめ</h1> <p>ここまで環境を整えれば、あとは非常に快適に開発ができると思います。<br/> ReactNative + TypeScriptの知見が日本語であんまりないように感じるので、もっと増えるとうれしいです。</p> <h1>参考</h1> <ul> <li><a href="https://github.com/Microsoft/TypeScript-React-Native-Starter">Microsoft/TypeScript-React-Native-Starter</a></li> <li><a href="http://neos21.hatenablog.com/entry/2017/10/24/080000">tsconfig.json で指定できる全 compilerOptions をまとめた (TypeScript v2.5 版)</a></li> </ul> siguma_sig ReactNativeでミュージックストリーミングサーバーkoelのネイティブアプリ作ってみた hatenablog://entry/8599973812326956509 2017-12-16T00:06:21+09:00 2018-06-24T15:19:34+09:00 ネタがなかったので、ゲーム関係ないですが、NITMic Advent Calendar 2017 16日目の記事です。 一応うちの部活はゲーム開発を行うだけの部活ではない!(らしい)ので、セーフです。 今までゲームばっかり作ってきていたのですが、最近はあるきっかけから割りとゲーム開発以外もやってみたいなあという思いが一段と強くなり、その流れからReactNativeでネイティブアプリを作ってみました。 今回は、開発環境と、使ったコンポーネントについてだけ書きます。 アプリ開発は初なので温かい目で見て下さい。 完成形はこんな感じです。 個人用なのでUIとか知りません。 iphoneのミュージック… <p>ネタがなかったので、ゲーム関係ないですが、<a href="https://adventar.org/calendars/2586">NITMic Advent Calendar 2017</a> 16日目の記事です。<br/> 一応うちの部活はゲーム開発を行うだけの部活ではない!(らしい)ので、セーフです。</p> <p>今までゲームばっかり作ってきていたのですが、最近はあるきっかけから割りとゲーム開発以外もやってみたいなあという思いが一段と強くなり、その流れからReactNativeでネイティブアプリを作ってみました。</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>についてだけ書きます。</p> <p> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%B3%AB%C8%AF">アプリ開発</a>は初なので温かい目で見て下さい。<br/> 完成形はこんな感じです。 個人用なのでUIとか知りません。<br/> <a class="keyword" href="http://d.hatena.ne.jp/keyword/iphone">iphone</a>のミュージックアプリを参考に作りました。<br/> とりあえず、目標としていたバックグラウンド再生、シャッフル再生、プレイリストごとの再生はできたので満足です!</p> <p> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20171215/20171215213921.gif" alt="f:id:siguma_sig:20171215213921g:plain" title="f:id:siguma_sig:20171215213921g:plain" class="hatena-fotolife" itemprop="image"></span></p> <h1>ミュージックストリーミングサーバー koel</h1> <p>以前、<a href="http://siguma-sig.hatenablog.com/entry/2017/10/14/151302">こちら</a>でも紹介したのですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>な個人用ミュージックストリーミングサーバーを簡単に立てられるのが<a href="https://koel.phanan.net/">koel</a>です。</p> <p>フロントがVue.js、サーバーサイドがLaravelで書かれており、公式サイトにも書いてありますが割りとモダンな感じです。<br/> <a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>なVue.jsのアプリではかなり有名な方らしく、Vue.jsの実装の参考にもされるらしいです。</p> <p>僕は作業中はずっと音楽を流しているのですが、色んな端末に音源を入れるのが面倒なので使っています。<br/> ですが、公式で<a class="keyword" href="http://d.hatena.ne.jp/keyword/iOS">iOS</a>/<a class="keyword" href="http://d.hatena.ne.jp/keyword/Android">Android</a>のネイティブアプリが存在しなかったため、今回作ってみようと思いました。</p> <h1>技術選定</h1> <p>サーバーサイドに関しては、<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>一覧などは存在しないので、頑張ってLaravelの実装を読んでWebアプリで使われている<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を叩くだけです。</p> <p>クライアントサイドに関して、初めにSwiftをこの機会に学んでみようかとも思いましたが、直近でReactを使ってSPAな自分用ツールを作っていたので、ReactNativeで行こうと思いました。<br/> また、今回は自分用なので<a class="keyword" href="http://d.hatena.ne.jp/keyword/iOS">iOS</a>のみで良いのですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%ED%A5%B9%A5%D7%A5%E9%A5%C3%A5%C8%A5%D5%A5%A9%A1%BC%A5%E0">クロスプラットフォーム</a>なところにも惹かれました。</p> <h1>開発環境</h1> <p>初めは、<a href="https://github.com/react-community/create-react-native-app">create-react-native-app</a>を用いて開発をはじめました。<br/> Expoというものを利用していて、<a class="keyword" href="http://d.hatena.ne.jp/keyword/QR%A5%B3%A1%BC%A5%C9">QRコード</a>で実機での動作確認がすぐできて便利でした。<br/> がしかし、こちらはネイティブ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>が基本的には利用できず、ネイティブ<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>はExpo側で用意されたものだけしか使えません。<br/> 音楽再生のネイティブ<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>も提供されていたのですが、こちらはバックグラウンド再生ができなかったので泣く泣く諦めました。</p> <p>結局、react-native-<a class="keyword" href="http://d.hatena.ne.jp/keyword/cli">cli</a>からプロジェクトを作り直して開発を行いました。</p> <p>エディタは、WebStormを使いました。学生のうちに使ってみたかっただけで、基本的には<a class="keyword" href="http://d.hatena.ne.jp/keyword/VSCode">VSCode</a>で良さそうでした。<br/> 今回は、TypeScriptやFlow、ESlintなどは利用せず開発を行いました。理由は導入が大変そうだったからです。<br/> ただ、このアプリの開発が終わったあとにESlintを入れてみたら大変便利で、非常に後悔しているので、ESlintは少なくとも入れたほうが良さそうです。<br/> Reactのお作法の勉強にもなります。</p> <h1>便利だった<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>紹介</h1> <p><a href="https://github.com/react-native-community/react-native-video">react-native-video</a><br/> これがなければ作れなかった。<br/> videoとついてるが、audioのみも再生できる。<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>はいくつかあるが、他はほぼメンテされていなかったりしたのでこれにした。<br/> ボリュームをいじったり、再生時間をいじったりもできる。</p> <p><a href="https://github.com/reactjs/redux">redux</a><br/> 言わずもがな<br/> Fluxの概念を拡張して、stateを管理するための<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a><br/> 僕のような素人でも書き方が大体決まっているので、コードがぐっちゃぐちゃにはなりづらかった。</p> <p><a href="https://github.com/reduxactions/redux-actions">redux-actions</a><br/> reduxのactionCreatorを決まった形式で簡単に作れる。<br/> 実装方法を縛ることで複数人で書くときも書き方が統一されて良さそう。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">export</span> <span class="synStatement">const</span> increment = createAction(COUNTER_INCREMENT); </pre> <p>こんな感じで、incrementのactionが作れる。 引数はpayloadに乗っかる感じ。</p> <p><a href="https://github.com/redux-saga/redux-saga">redux-saga</a><br/> 非同期処理で大変お世話になった。<br/> 初めは、redux-thunkを使っていたけど、<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を叩いたりの非同期処理がactionCreator側に有るのがどうにも気持ち悪かったのを同期に相談したら教えてもらった。<br/> <a href="https://qiita.com/kuy/items/716affc808ebb3e1e8ac">redux-sagaで非同期処理と戦う</a>がとっても参考になる。</p> <p>下のように<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を叩いてPromiseを返すメソッドを用意して、 <script src="https://gist.github.com/89cdb617966533267bf150b784a1530f.js"> </script></p> <p>saga側で呼び出して、返ってきた値を用いてactionをdispatchということができる。 <script src="https://gist.github.com/d821f2aff6f6be7c5b0cf6432d40572e.js"> </script></p> <p>導入の際に覚えることがいくつかあるけど、それに見合ったメリットはあると思った。</p> <p><a href="https://github.com/aksonov/react-native-router-flux">react-native-router-flux</a><br/> Webフロントのreact-routerライクに画面遷移が書ける。<br/> 初めは、react-navigationを使ってみたが、よくわからなかったので、使いやすそうなこちらにした。<br/> ただ、中ではreact-navigationが動いてるらしい。<br/> react-native-navigationというのもあるらしいが、使わなかった。遷移のアニメーションが豊富らしい?</p> <p>他には、lodashとかreact-native-<a class="keyword" href="http://d.hatena.ne.jp/keyword/vector">vector</a>-iconsとかも使った。</p> <h1>まとめ</h1> <p>初コミットから見ると、約2週間くらいで完成しました。<br/> ネイティブ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%B3%AB%C8%AF">アプリ開発</a>の経験もなく、Reactの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%B3%AB%C8%AF">アプリ開発</a>経験もほぼほぼないと言っていいくらいだったけどこんな感じなので、割りと取っ付きやすいのかなあと思いました。<br/> とはいえ、パフォーマンスを気にしたりし始めるとまだまだ学ぶことは多そうです。<br/> アプリをリリースするときは、そういうところもきちんとしないといけないと思うし、UI/UXの勉強も必要だなあと思いました。</p> <p>とはいえ、普段のゲーム開発とは全く違うことをするのも楽しかったです。<br/> こんな感じで就職するまでは、フラフラ色んな技術に浮気しつつ、ゲームも遊びつつ、ゲーム開発の知識も増やしていきたいです。</p> <p>研究頑張ります…。</p> siguma_sig コンピュータ倶楽部NITMicの活動 hatenablog://entry/8599973812323113521 2017-12-03T00:00:04+09:00 2017-12-03T00:00:04+09:00 NITMic Advent Calendar 2017 3日目担当のsigumaです。 今年からAdvent CalendarをやってみようとOBで勝手に盛り上がり、勝手に作りました。 1日目のマホウさんも言っていますが、NITMicのエンジニアだけでなく他の役職であるコンポーザー、デザイナーの方々も参加できるように、QiitaではなくAdventerでやっています。 今日はそんなコンピュータ倶楽部NITMicの活動について書こうと思います。 <p><a href="https://adventar.org/calendars/2586">NITMic Advent Calendar 2017</a> 3日目担当のsigumaです。</p> <p>今年からAdvent CalendarをやってみようとOBで勝手に盛り上がり、勝手に作りました。<br/> 1日目のマホウさんも言っていますが、NITMicのエンジニアだけでなく他の役職であるコンポーザー、デザイナーの方々も参加できるように、QiitaではなくAdventerでやっています。</p> <p>今日はそんな<a href="http://nitmic.club.nitech.ac.jp/">コンピュータ倶楽部NITMic</a>の活動について書こうと思います。</p> <h1>コンピュータ倶楽部NITMicとは</h1> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%BE%B8%C5%B2%B0%B9%A9%B6%C8%C2%E7%B3%D8">名古屋工業大学</a>にある部活で、主にゲーム制作を中心に創作に興味のある学生が集まって、作品の制作に取り組んでいます。</p> <p>コンピュータ倶楽部には、役職という制度があり、入部時に3つの役職に別れます。<br/> 役職は、プログラミングを行うエンジニア、2d,3d問わず絵素材を作るデザイナー、SE,BGMなどの音素材を作るコンポーザーがあります。</p> <p>その中で、ゲームの企画案のある人が企画書を出し、その企画に対して参加したい各役職の人が集まってゲーム制作を行っています。</p> <p>どれくらい歴史があるのかは僕達もよく知りませんが、部室の物置からC43のカタログが発見されたりするので、かなり昔からある部活ではあるようです。</p> <h1>1年の流れ</h1> <p>ここから、NITMicの1年間の活動を軽く紹介します。</p> <h4>4月</h4> <p>主に新入生勧誘を行います。<br/> 部室へ見学に来てくれた人には、昨年制作したゲームをプレイしてもらったりします。</p> <p>最近はエンジニアの希望人数が非常に多いため、入部試験を行ったりもしています。<br/> とはいっても、プログラミング経験者は毎年一人いるかいないかなので、やる気さえあれば大丈夫です。</p> <h4>5月 ~ 8月</h4> <p>各役職で、新入生に今後の活動に必要な知識について講座を行ったりした後に、さっそくゲーム開発に取り掛かります。<br/> この時期は、<a href="http://awards.cesa.or.jp/">日本ゲーム大賞</a>に提出するためのゲームを企画から制作まで行います。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C6%FC%CB%DC%A5%B2%A1%BC%A5%E0%C2%E7%BE%DE">日本ゲーム大賞</a>への応募は一昨年から初めたのですが、未だ誰も受賞には至っていないのでこの先部活から受賞者が出たらとってもうれしいです。</p> <h4>9月</h4> <p>夏休みですが、次のイベントである、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%BE%B8%C5%B2%B0%B9%A9%B6%C8%C2%E7%B3%D8">名古屋工業大学</a>の学祭でゲームセンターブースに展示するための作品の企画を始めます。</p> <h4>10月~11月</h4> <p>部内では一番大きなイベントである(と僕は思っている)、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%BE%B8%C5%B2%B0%B9%A9%B6%C8%C2%E7%B3%D8">名古屋工業大学</a>の学祭に向けて制作物を完成させ、展示を行います。<br/> うちの部活のブースでは例年、制作したゲームを学祭に来てくださったお客さんにプレイしてもらいます。</p> <p>自分の作ったゲームを全く知らない方々に目の前でプレイしてもらえるという機会は本当に貴重だと思います。<br/> また、例年アンケートを取り、人気ゲームがランキング形式で最後に発表されるので部員もバチバチ頑張ります。</p> <p>学祭が終わると世代交代で、部長やその他役職(エンジニア、デザイナー、コンポーザーのリーダーなど)が後輩に託されます。<br/> 色んな思いがこみ上げてきて引退のときには泣きます。</p> <h4>12月 ~ 2月</h4> <p>この時期はイベントも特にないので、各々で制作に対する知識を蓄え、共有し合ったりしています。<br/> 今年も、勉強会が行われるようです。</p> <h4>3月</h4> <p>春休みなので、部内ゲームジャムを行っています。<br/> 2日間で、ゲームを完成させます。<br/> 短い時間でゲームを作る経験の中でも得るものはたくさんあると思うので行っているのだと思います。</p> <h1>その他の活動</h1> <h4><a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%BE%B9%A9%C2%E7">名工大</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%C3%A5%AB%A5%BD%A5%F3">ハッカソン</a></h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/HAL%CC%BE%B8%C5%B2%B0">HAL名古屋</a>、トライデント名古屋と合同で1dayの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%C3%A5%AB%A5%BD%A5%F3">ハッカソン</a>を行っています。<br/> 他の学校の人とチームを組んだりして、技術力の差を思い知り、さらに開発を頑張ろうという気持ちになります。<br/> 学校も学年も混同でチームを作られるので、大変良い交流の機会だと思います。</p> <h4>講座</h4> <p>部員同士で、制作に対する知見の共有を行っています。<br/> エンジニアだと、Gitの講座やUnityの講座などを行っています。</p> <h4>ゲーム</h4> <p>みんなゲームが大好きです。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DC%A1%BC%A5%C9%A5%B2%A1%BC%A5%E0">ボードゲーム</a>も大好きです。<a class="keyword" href="http://d.hatena.ne.jp/keyword/TRPG">TRPG</a>も大好きです。</p> <h1>部内で使われる技術</h1> <h4>エンジニア</h4> <p>新入生は、C,<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>から始めます。<br/> <a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>では、<a href="http://play-siv3d.hateblo.jp/">Siv3D</a>という優れたライブラリがあるので、こちらを利用して開発を行います。<br/> <a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>でゲーム開発の基礎を学んだあとは、DirectX11, cocos2dx, Unity, UnrealEngine等好きな環境で開発を行うようになります。</p> <h4>デザイナー</h4> <p>3d<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E2%A5%C7%A5%EA%A5%F3%A5%B0">モデリング</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Blender">Blender</a>で行っています。<br/> 2dに関しては、記憶に無いです。すみません。</p> <h4>コンポーザー</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Cubase">Cubase</a>とか<a class="keyword" href="http://d.hatena.ne.jp/keyword/FLStudio">FLStudio</a>とかだと思います。<br/> 僕はエンジニアなのでしっかりとはわかりません…すみません…</p> <h1>まとめ</h1> <p>文字ばっかりな上に長くなってしまいましたが、こんな感じです。<br/> 僕は今のところ人生で一番楽しかったのが、この部活動で過ごした3年間なので、是非おすすめしたいです。</p> <p>後輩たちは、このまま良い伝統を続けつつ、さらに良くする何かを企画していってくれればなと思います。</p> siguma_sig OSSなミュージックストリーミングサーバー、koelの導入と躓いたところ hatenablog://entry/8599973812307789880 2017-10-14T15:13:02+09:00 2017-10-14T15:16:39+09:00 最近、VPSをKagoyaからVultrにお引越ししました。 そのついでに、ミュージックストリーミングサーバーも変更しようと思いました。 色々調べた結果koelというOSSのミュージックストリーミングサーバーが非常に魅力的だったので、こちらに乗り換えた話と、その際に躓いた点を書きます。 <p>最近、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>をKagoyaからVultrにお引越ししました。<br/> そのついでに、ミュージックストリーミングサーバーも変更しようと思いました。<br/> 色々調べた結果<a href="https://koel.phanan.net/">koel</a>という<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>のミュージックストリーミングサーバーが非常に魅力的だったので、こちらに乗り換えた話と、その際に躓いた点を書きます。</p> <h2>今までのミュージックストリーミングサーバーとの比較</h2> <p>僕は、今まで<a href="http://beta.madsonic.org/pages/index.jsp">MadSonic</a>を使用していました。<br/> こちらは、以下のようなデメリットがありました。</p> <ul> <li>PCでしか聞けない(<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%DE%A5%DB">スマホ</a>等で聞くには別途月額有償)</li> <li>サーバー側のメモリを喰う</li> <li>UIが使いにくい(あくまで主観です)</li> </ul> <p>特に毎月1ドル払わないとPC(ブラウザ)でしか聞けない点がネックでした。<br/> そこで見つけたのがkoelでした。<br/> koelは以下のような特徴がありました。</p> <ul> <li>アプリ等はないものの、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%DE%A5%DB">スマホ</a>でも問題なく聞ける</li> <li>UIがかなり使いやすい</li> <li>Vue、Laravelを使用して<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>開発されておりモダンな実装</li> <li>新規機能開発が活発に行われている</li> </ul> <p>この他に、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Amazon%20S3">Amazon S3</a>との連携でストレージ容量の少ない<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>でも使用できたり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Youtube">Youtube</a>、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Last.fm">Last.fm</a>との連携も魅力的でした。</p> <h2>導入の注意点</h2> <p>koelの導入に関しては、公式で<a href="https://koel.phanan.net/docs/#/">Document</a>が用意されており、本来はこちらを参考に進めれば概ね問題はありません。<br/> が、… 2017/10月現在、Documentの更新が十分にされていないのか、この通りに進めると途中でエラーを吐いてしまいます。<br/> 僕はこのエラーに3日間くらい悩まされました…(JSわかんない)</p> <p>エラーは<code>php artisan koel:init</code>を実行した際に起きます。 内容は以下の通りです。</p> <pre class="code lang-zsh" data-lang="zsh" data-unlink>$ cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules <span class="synConstant">95</span>% emitting ERROR Failed to compile with <span class="synConstant">49</span> errors15:<span class="synConstant">02</span>:<span class="synConstant">38</span> These dependencies were not found <span class="synStatement">in</span> node_modules: ... Did you forget to run npm install --save <span class="synStatement">for</span> them? Asset Size Chunks Chunk Names img/icon.png <span class="synConstant">10.8</span> kB [emitted] fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713 <span class="synConstant">166</span> kB [emitted] fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9 <span class="synConstant">166</span> kB [emitted] fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e <span class="synConstant">77.2</span> kB [emitted] fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad <span class="synConstant">98</span> kB [emitted] /js/app.c0fba38fc81df5c9aa4d.js <span class="synConstant">619</span> kB <span class="synConstant">0</span> [emitted] [big] /js/app mix-manifest.json <span class="synConstant">53</span> bytes [emitted] img/favicon.ico <span class="synConstant">4.29</span> kB [emitted] img/artists/.gitkeep <span class="synConstant">0</span> bytes [emitted] img/covers/unknown-album.png <span class="synConstant">11.5</span> kB [emitted] img/bars.gif <span class="synConstant">33.2</span> kB [emitted] fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde <span class="synConstant">444</span> kB [emitted] [big] img/tile-wide.png <span class="synConstant">3.25</span> kB [emitted] img/itunes.svg <span class="synConstant">1.54</span> kB [emitted] img/logo.svg <span class="synConstant">5.19</span> kB [emitted] img/logo.png <span class="synConstant">9.28</span> kB [emitted] img/tile.png <span class="synConstant">6.47</span> kB [emitted] fonts/fontawesome-webfont.eot <span class="synConstant">166</span> kB [emitted] fonts/fontawesome-webfont.woff <span class="synConstant">98</span> kB [emitted] fonts/fontawesome-webfont.ttf <span class="synConstant">166</span> kB [emitted] fonts/fontawesome-webfont.woff2 <span class="synConstant">77.2</span> kB [emitted] fonts/fontawesome-webfont.svg <span class="synConstant">444</span> kB [emitted] [big] fonts/FontAwesome.otf <span class="synConstant">135</span> kB [emitted] error Command failed with <span class="synStatement">exit</span> code <span class="synConstant">2</span>. info Visit https://yarnpkg.com/en/docs/cli/run <span class="synStatement">for</span> documentation about this <span class="synSpecial">command</span>. error Command failed with <span class="synStatement">exit</span> code <span class="synConstant">1</span>. info Visit https://yarnpkg.com/en/docs/cli/install <span class="synStatement">for</span> documentation about this <span class="synSpecial">command</span>. </pre> <p>エラーメッセージを信じて、<code>npm install --save</code>を実行してもうまくいきません。<br/> 解決方法を言ってしまうと、yarnのバージョンを下げることで解決します。<br/> 公式Documentに</p> <blockquote><p>NodeJS latest stable with yarn</p></blockquote> <p>と書いてあるのを信じてしまうとこの罠にハマります。<br/> 現在<a href="https://github.com/phanan/koel/issues/656">Issue</a>として上がっているので後々解決はされると思います。</p> <p>肝心のバージョンをいくつにすればよいかですが、上のIssueにもある通り、<br/> <code>npm install yarn@0.25.4 -g</code><br/> でインストールすれば良さそうなので、&lt;=0.25.4であれば良さそうです。<br/> 今の最新バージョンは、1.2.1なので、だいぶ違いますね…<br/> これで解決しない場合は、別件なので、Issueを見て頂くか、公式Documentをまた詳しく読み直してみて下さい…!(.envとかきちんと設定するのを忘れずに!)<br/> Issueでエラーメッセージを検索すると色々解決の糸口が見えてくるというのが今回の経験で得た知見でした。</p> siguma_sig VPSを借りてみてやったこと hatenablog://entry/10328749687258096718 2017-06-02T22:28:14+09:00 2017-06-02T22:28:14+09:00 先月半ば頃にVPSなるものを借りてみた。 僕はサーバーサイドもインフラの知識もほぼ無いのだが、ずっと前から興味はあった。 だけど、去年の同じくらいの時期に何故かポートフォリオサイトを作り、その際にレンタルサーバーを1年契約で借りていたのと、VPSはレンタルサーバーより高いんだぞ〜という情報から月数千円くらいするんだろうなあという勝手な想像で、VPSは借りていなかった。 しかし、先月にレンタルサーバーの契約がそろそろ切れそうだったので、VPSについて調べてみると、思っていたより数倍安いではないか!と気づいた。なんなら、僕が借りていたレンタルサーバーより安いではないか!と。 それで、さらにVPSに… <p>先月半ば頃に<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>なるものを借りてみた。</p> <p>僕はサーバーサイドもインフラの知識もほぼ無いのだが、ずっと前から興味はあった。</p> <p>だけど、去年の同じくらいの時期に何故か<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DD%A1%BC%A5%C8%A5%D5%A5%A9%A5%EA%A5%AA">ポートフォリオ</a>サイトを作り、その際に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%BF%A5%EB%A5%B5%A1%BC%A5%D0%A1%BC">レンタルサーバー</a>を1年契約で借りていたのと、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%BF%A5%EB%A5%B5%A1%BC%A5%D0%A1%BC">レンタルサーバー</a>より高いんだぞ〜という情報から月数千円くらいするんだろうなあという勝手な想像で、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>は借りていなかった。</p> <p>しかし、先月に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%BF%A5%EB%A5%B5%A1%BC%A5%D0%A1%BC">レンタルサーバー</a>の契約がそろそろ切れそうだったので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>について調べてみると、思っていたより数倍安いではないか!と気づいた。なんなら、僕が借りていた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%BF%A5%EB%A5%B5%A1%BC%A5%D0%A1%BC">レンタルサーバー</a>より安いではないか!と。</p> <p>それで、さらに<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>について調べていると、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%BF%A5%EB%A5%B5%A1%BC%A5%D0%A1%BC">レンタルサーバー</a>より色々できて楽しそうだったので、そのまま流れで借りることにした。</p> <p>で、借りてから半月くらいで色々したので、つらつらと書き残そうと思った。</p> <h4>やったこと</h4> <h5> ミュージックストリーミングサーバー</h5> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>借りてみたくなった一番の理由かもしれない。</p> <p><a href="https://play.google.com/music/listen?hl=ja#/sulp">Google Play Music</a>や<a href="https://www.apple.com/jp/apple-music/">Apple Music - Apple(日本)</a>みたいに<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>に音楽ファイルをアップロードして、自分のあらゆる端末からその音楽ファイルを再生できるようになる。</p> <p>そうすれば、端末の容量を圧迫しないし、複数の端末に取り込む必要もなくなる。(通信が必要になるが。)</p> <p>僕は、起きている間は何かしら音楽を聞いているので、これで非常に便利になった。</p> <p>やり方は、</p> <p><a href="http://www.kyoji-kuzunoha.com/2012/04/webdavvps.html">【WebDAV】VPSをミュージックストリーミングサーバーにする【Subsonic】 | くずのは探偵事務所</a></p> <p>ここらへんを見るとなんとなくわかる。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Subsonic">Subsonic</a>はお金がかかるから、SuperSonicが良いと言われてSuperSonicを入れたが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a>を見ると全然更新がされていなかったし、UIも微妙だったので、現在もメンテされている感じのMadSonicに乗り換えた。</p> <p>英語が読みたくないので読んでいないのだけど、多分普通に使う分には無料だと思う(今はプレミアム体験期間みたいで、あと8日で切れるので、それで聞けなくなったら泣ける)</p> <p>悪い点は、メモリを食いまくってるぐらい。</p> <h5>ownCloud</h5> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>の個人用<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>サーバー。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Dropbox">Dropbox</a>とかGoogleDriveとか見たいな感じでファイルを管理できる。</p> <p>保存先が、自分の<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>なので、容量は大きめだし、より安全?なのかな?</p> <p>正直あんまし使ってない。これから使っていきたい。</p> <h5> TwitterBot</h5> <p>せっかく<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>借りたし、なんか作りたいなあと思って作った。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EC%A5%A4%A5%D4%A5%F3%A5%B0">スクレイピング</a>して得た情報を定期的につぶやく、みたいなのを作った。</p> <p>今までほぼゲームしか作ってこなかったから少し大変だったけど、勉強になった。</p> <p>はじめは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>を使って書いたけど、なんか合わなかったので前から気になっていて、<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 class="keyword" href="http://d.hatena.ne.jp/keyword/Golang">Golang</a>を使ってみた。</p> <p>とても書きやすくて、いい感じだったので今後は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Golang">Golang</a>を触っていきたいと思った。</p> <h4>気になること</h4> <h5>セキュリティ</h5> <p>よくわからないが、踏み台にされたりして他のサイトに迷惑が掛かったりするらしいので、調べて色々やってみた。</p> <p>rootログイン制限したり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/SSH">SSH</a>ポート番号を変えたり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/iptables">iptables</a>?でポートを制限したり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/HTTPS">HTTPS</a>化したり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/SSL">SSL</a>テストの評価がAになるようにした。</p> <p>ここらへんを参考にした。</p> <p><a href="https://dogmap.jp/2011/05/12/vps-security/">VPS 借りたら、せめてこれくらいはやっとけというセキュリティ設定 | dogmap.jp</a></p> <p><a href="https://www.slideshare.net/uemera/vps-14728897">VPS借りたけどセキュリティが心配! 初心者が気をつけたいセキュリティの話</a></p> <p>他にやるべきことがあるのかわからないので、是非教えてほしいです。</p> <h5>どこがいいのか</h5> <p>今はカ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B4%A5%E4">ゴヤ</a>というところに借りているが、結局どこで借りるのがよいのだろうか。</p> <p>ミュージックストリーミングサーバーとして使う気まんまんだったので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/SSD">SSD</a>で、容量が他のサイトの同じ価格帯に比べてなぜかめちゃくちゃ多いカ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B4%A5%E4">ゴヤ</a>にしたが、ネットの評価を見ると、ConoHaやさくらが良いみたいだった。</p> <p>たしかにどちらも僕でも名前は知っていたぐらい有名なので、やはりどちらかで借りるべきだったのだろうか。</p> <p>どうして、この二つがおすすめなのか、誰か教えてほしいです。</p> <h5>他にやってみると面白いこと</h5> <p>上のやってみたこと以外で、やってみると便利だよとか楽しいよっていうことがあれば教えてください。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%CB%A1%BC%A5%DD%A5%C3%A5%C8">ハニーポット</a>?とかいうので、自分のサーバーに攻撃してきた人の行動が見れたりするのは面白そうだなあと思っているので、そのうちやってみようと思ってます。</p> <h5> ゲームとの繋がり</h5> <p>例えば、ゲームのランキングとかであれば、HTTP通信が理解できてればできそうだと思うんだけどどうなんだろう?</p> <p>あとは、リアルタイムにキャラクターが動き回るゲームがあるとして、どのくらいの接続には耐えられるのだろう?とか気になることがいっぱい。</p> <p>ここらへんが一番教えて欲しいし、自分でも学習を進めていきたい。</p> <h4>まとめ</h4> <p> ずっと触ってみたかったサーバーサイドに触る機会ができたので、とてもよかった。</p> <p>WebAPIを叩いてみる経験も積めた。</p> <p>今後は簡単なチャットツールくらいから作って、何かゲーム開発に繋げていきたいです。</p> <p>あとは、どっかの開発に携わってみっちり勉強してみたい…</p> siguma_sig 逆求人のすゝめ hatenablog://entry/10328749687251734173 2017-05-24T12:31:01+09:00 2017-05-24T12:31:01+09:00 早いもので、もう19卒のインターン申込みが続々と始まりつつあります。 最近、うちの部活の後輩たちもインターンに向けて動き始めていて、逆求人について聞かれることも多いので、まとめておこうかと思います。 僕が去年参加した逆求人イベントは全てサポーターズさんによるものだったので、ここではサポーターズさんに絞って説明しますが、他のイベントでもほぼ同様な結果が得られるとは思います。 それ書いてもらっちゃまずい!みたいなのあったらサポーターズさん、教えてください。 <p>早いもので、もう19卒の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>申込みが続々と始まりつつあります。</p> <p>最近、うちの部活の後輩たちも<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に向けて動き始めていて、逆求人について聞かれることも多いので、まとめておこうかと思います。</p> <p>僕が去年参加した逆求人イベントは全てサポーターズさんによるものだったので、ここではサポーターズさんに絞って説明しますが、他のイベントでもほぼ同様な結果が得られるとは思います。</p> <p>それ書いてもらっちゃまずい!みたいなのあったらサポーターズさん、教えてください。</p> <h3> 逆求人とは</h3> <p>一般的(?)な就活のスタイルは、自分の興味のある企業に対して学生側から申し込みをして、選考へと進んでいくものだと思います。</p> <p>流れを書くとするならば、</p> <ol> <li>興味のある会社の説明会へ行く</li> <li>選考書類を提出して書類選考</li> <li>通ったら何度か面接</li> <li>内定</li> </ol> <p>逆求人とは、それの反対で企業側が興味のある学生に対して、オファーをします。</p> <p>流れを書くとするならば、</p> <ol> <li>逆求人系のイベントにいく</li> <li>自己アピールをする</li> <li>企業側からオファーをもらう</li> <li>面接 or 内定</li> </ol> <p> 企業側からのオファーにより、書類選考や1次面接をスキップして、いきなり最終面接から始まることもあります。また、噂によると即内定がもらえる可能性もあるとかないとか。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に関しても、書類選考が免除されたり、直接参加へのお誘いが頂けたりします。</p> <p>一般的な就活スタイルが、学生⇒企業の一方通行だとするならば、逆求人は学生⇔企業と双方向な感じです(うまくいえない)。</p> <h3>サポーターズとは</h3> <p>去年僕がとってもお世話になった就活支援をしてくださる企業さんです。</p> <p>就活支援って具体的に何をしてくれるのかというと、<span style="text-decoration: underline;">交通費を負担</span>してくれたりします。</p> <p>これは地方学生にとっては非常に大きく、大変助かりました。</p> <p>あとは、逆求人イベントや<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%C3%A5%AB%A5%BD%A5%F3">ハッカソン</a>、勉強会等を開催してくれます。</p> <p>僕が4回ほど参加した逆求人イベントは全てサポーターズさん主催のものでした。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="サポーターズ | 学生と企業が支援でつながる" src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fsupporterz.jp%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://supporterz.jp/">supporterz.jp</a></cite></p> <p>他にいくつか逆求人イベントを開催してくださる企業を挙げると、</p> <p>ジー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%BF%A5%A4%A5%E9%A5%B9">スタイラス</a>さん</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="株式会社ジースタイラス | 優秀学生の一本釣り採用なら逆求人フェスティバル" src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.gstylus.co.jp%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.gstylus.co.jp/">www.gstylus.co.jp</a></cite>キャリアセレクトさん</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="エンジニア・デザイナー学生のための就活サイト|キャリアセレクト" src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fcareerselect.jp%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://careerselect.jp/">careerselect.jp</a></cite></p> <h3>参加までの流れ</h3> <p>サポーターズさんの逆求人を例に、参加までの流れを書きます。</p> <h5>1. 登録してプロフィールを埋めて待つ</h5> <p>まずは、サポーターズに登録をして、しっかりと全てプロフィールを埋めておきましょう。</p> <p>ここで書いたプロフィールは、逆求人イベントで企業側へ学生側の資料として渡されるものになります。</p> <h5>2. サポーターズからお誘いが来る</h5> <p>プロフィールをしっかり埋めていれば、サポーターズから逆求人イベントのお誘いがきます。</p> <p>キャンセルは非常に迷惑をかけることになるので、しっかりと自分の予定を確認してから、参加申し込みをしましょう。</p> <h5>3. (電話でお話)</h5> <p>初めて逆求人に参加する場合は、サポーターズさんから電話がかかってきて、今までの自分のやってきたことや触ってきた技術について説明をします。</p> <h5>4. 参加</h5> <p>電話の結果、無事参加できることになれば、当日会場に向かいましょう。</p> <p>自己アピールのためのスライド等の資料を作ることになるので、気合を入れて作りましょう。</p> <p>これは非常に重要で、喋るのが下手な人はよりスライドを気合い入れて作っておきましょう。</p> <p>交通費を負担してもらうには、<span style="text-decoration: underline;">領収書を忘れずに!</span></p> <h3> 逆求人に参加してよかったこと</h3> <p>逆求人に参加するかどうか悩んでいる人のために、僕が参加してよかったと思った点を挙げていきます。</p> <h5>・オファーがもらえる</h5> <p>逆求人に行く一番の目的かと思います。</p> <p>オファーをもらえることにより、今まで考えていなかった企業の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>や選考に参加する機会が得られます。</p> <p>僕が就職先に選んだ企業さんは、一番はじめに参加した逆求人でお話して、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>への参加をオファーしてくださった企業さんで、自分の自信の無さから参加することを諦めようと思っていた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>だったので、もしあのとき逆求人に行っていなかったらどうなっていたのだろうとは思います。</p> <h5>・自分の知らなかった企業と出会える</h5> <p>逆求人では、多くの企業さんと1日でお話することができます。</p> <p>その中には、自分が今まで知らなかったような企業さんも含まれたりします。</p> <p>こういったイベントに参加することで、自分の就職活動における<span style="text-decoration: underline;">選択肢の幅が広がります。</span></p> <h5>・自信がつく</h5> <p>こういったイベントに参加することで、企業側からオファーが来ると、自分が今まで作ってきたもの、やってきたことに対して自信が持てます。</p> <h5>・俺より強い奴(学生)に会える</h5> <p>早いうちに逆求人に参加すると顕著かと思いますが、周りの学生のレベルが非常に高いです。(ある言語のコミッターが居たり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/ICPC">ICPC</a>ワールドファイナルチームのメンバーが居たり…)</p> <p>これは人によっては、自信喪失に繋がってしまうのかもしれませんが、僕はモチベーションに繋がりました。</p> <h5>・志望する業界とは違う業界の人(学生)に会える</h5> <p>例えば、僕はゲーム業界を志望していたのですが、こういったイベントに参加することで、IT業界の中の違う業界の方とお話することができました。</p> <p>そういった方々からゲーム業界のイメージをお聞きしたり、その業界のことについてお聞きすることが出来たのは非常に勉強になりました。</p> <p>また、学生に関しても、ゲーム系の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に行くとどうしても周りはゲームを作ってきた学生が多くなりますが、こういった場はゲームに限らずWebエンジニアやインフラエンジニアを志望する学生にも会うことができます。</p> <p> </p> <h4>まとめ</h4> <p>逆求人はいいぞ</p> <p> </p> <p>余談ですが、うちの部活のホームページがリニューアルされてたので是非覗いてみて下さい!</p> <p><a href="http://nitmic.club.nitech.ac.jp/">top-NITMic</a></p> siguma_sig OpenGL(GLSL)のvarying,attribute,in,outについて hatenablog://entry/10328749687239635090 2017-04-25T20:32:50+09:00 2017-05-24T10:41:28+09:00 OpenGLのストレージ修飾子(inとかoutとか)についてです。これもあんまりまとまった記事がなさそうだったので自分用メモ前の記事同様OpenGL学び初めたばかりで、しかもネットの情報のみという浅い知識しか持ち合わせていないので、間違いがあったら教えてもらえるとうれしいです。 <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenGL">OpenGL</a>のストレージ修飾子(inとかoutとか)についてです。<br /><br />これもあんまりまとまった記事がなさそうだったので自分用メモ<br /><br />前の記事同様<a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenGL">OpenGL</a>学び初めたばかりで、しかもネットの情報のみという浅い知識しか持ち合わせていないので、間違いがあったら教えてもらえるとうれしいです。</p> <p><br />OpenGL2.x系では、<span style="text-decoration: underline;">頂点属性の定義はattribute</span>を付けて行っていました。<br /><br />また、<span style="text-decoration: underline;">バーテックスシェーダからフラグメントシェーダに内容を渡したいときはvarying</span>をつけて宣言を行っていました。<br /><br />↓多分こんな感じ(バーテックスシェーダの例)</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #0080ff; font-weight: bold;">attribute </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">position</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">color</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">varying </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">vColor</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #008080; font-weight: bold;">void </span><span style="color: #eeeeee;">main() {<br /></span><span style="color: #eeeeee;"> vColor </span>= <span style="color: #eeeeee;">color</span><span style="color: #f92672;">;<br /></span> <span style="color: #eeeeee;">gl_Position </span>= <span style="color: #eeeeee;">position</span><span style="color: #f92672;">;<br /></span><span style="color: #eeeeee;">}</span></pre> <p>しかし、OpenGL3.0以降では<span style="color: #ff5252;">attribute,varyingは使用されなくなりました。</span></p> <p>attributeはinに置き換え、varyingはinとoutに置き換えされるようになりました。</p> <p>以下に例を示します。</p> <p>OpenGL3.0以降のバーテックスシェーダの例</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #f92672;">#version </span><span style="color: #ae81ff;">150 </span><span style="color: #eeeeee;">core<br /></span><span style="color: #eeeeee;"><br /></span><span style="color: #0080ff; font-weight: bold;">in </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">position</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">in </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">normal</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">in </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">color</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">mat4 </span><span style="color: #eeeeee;">mvpMatrix</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">mat4 </span><span style="color: #eeeeee;">mMatrix</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">out </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">vPosition</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">out </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">vNormal</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">out </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">vColor</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #008080; font-weight: bold;">void </span><span style="color: #eeeeee;">main() {<br /></span><span style="color: #eeeeee;"> vPosition </span>= <span style="color: #eeeeee;">(mMatrix </span>* <span style="color: #008080; font-weight: bold;">vec4</span><span style="color: #eeeeee;">(position, </span><span style="color: #ae81ff;">1.0</span><span style="color: #eeeeee;">))</span><span style="color: #f92672;">.</span><span style="color: #eeeeee;">xyz</span><span style="color: #f92672;">;<br /></span> <span style="color: #eeeeee;">vNormal </span>= <span style="color: #eeeeee;">normal</span><span style="color: #f92672;">;<br /></span> <span style="color: #eeeeee;">vColor </span>= <span style="color: #eeeeee;">color</span><span style="color: #f92672;">;<br /></span> <span style="color: #eeeeee;">gl_Position </span>= <span style="color: #eeeeee;">mvpMatrix </span>* <span style="color: #008080; font-weight: bold;">vec4</span><span style="color: #eeeeee;">(position, </span><span style="color: #ae81ff;">1.0</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span><span style="color: #eeeeee;">}</span></pre> <p>OpenGL3.0以降のフラグメントシェーダの例</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #f92672;">#version </span><span style="color: #ae81ff;">150 </span><span style="color: #eeeeee;">core<br /></span><span style="color: #f92672;">precision </span><span style="color: #0080ff; font-weight: bold;">mediump </span><span style="color: #008080; font-weight: bold;">float</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #0080ff; font-weight: bold;">out </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">fragment</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">mat4 </span><span style="color: #eeeeee;">invMatrix</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">lightPosition</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">ambientColor</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">uniform </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">eyeDirection</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">in </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">vPosition</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">in </span><span style="color: #008080; font-weight: bold;">vec4 </span><span style="color: #eeeeee;">vColor</span><span style="color: #f92672;">;<br /></span><span style="color: #0080ff; font-weight: bold;">in </span><span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">vNormal</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #008080; font-weight: bold;">void </span><span style="color: #eeeeee;">main() {<br /></span> <span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">lightVec </span>= <span style="color: #eeeeee;">lightPosition </span>- <span style="color: #eeeeee;">vPosition</span><span style="color: #f92672;">;<br /></span> <span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">invLight </span>= <span style="color: #eeeeee;">normalize(invMatrix </span>* <span style="color: #008080; font-weight: bold;">vec4</span><span style="color: #eeeeee;">(lightVec, </span><span style="color: #ae81ff;">0.0</span><span style="color: #eeeeee;">))</span><span style="color: #f92672;">.</span><span style="color: #eeeeee;">xyz</span><span style="color: #f92672;">;<br /></span> <span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">invEye </span>= <span style="color: #eeeeee;">normalize(invMatrix </span>* <span style="color: #008080; font-weight: bold;">vec4</span><span style="color: #eeeeee;">(eyeDirection, </span><span style="color: #ae81ff;">0.0</span><span style="color: #eeeeee;">))</span><span style="color: #f92672;">.</span><span style="color: #eeeeee;">xyz</span><span style="color: #f92672;">;<br /></span> <span style="color: #008080; font-weight: bold;">vec3 </span><span style="color: #eeeeee;">halfLE </span>= <span style="color: #eeeeee;">normalize(invLight </span>+ <span style="color: #eeeeee;">invEye)</span><span style="color: #f92672;">;<br /></span> <span style="color: #008080; font-weight: bold;">float </span><span style="color: #eeeeee;">diffuse </span>= <span style="color: #eeeeee;"><a class="keyword" href="http://d.hatena.ne.jp/keyword/clamp">clamp</a>(dot(vNormal, invLight), </span><span style="color: #ae81ff;">0.0</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">1.0</span><span style="color: #eeeeee;">) </span>+ <span style="color: #ae81ff;">0.2</span><span style="color: #f92672;">;<br /></span> <span style="color: #008080; font-weight: bold;">float </span><span style="color: #eeeeee;">specular </span>= <span style="color: #eeeeee;">pow(<a class="keyword" href="http://d.hatena.ne.jp/keyword/clamp">clamp</a>(dot(vNormal, halfLE), </span><span style="color: #ae81ff;">0.0</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">1.0</span><span style="color: #eeeeee;">), </span><span style="color: #ae81ff;">50.0</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span> <span style="color: #eeeeee;">fragment </span>= <span style="color: #eeeeee;">vColor </span>* <span style="color: #008080; font-weight: bold;">vec4</span><span style="color: #eeeeee;">(</span><span style="color: #008080; font-weight: bold;">vec3</span><span style="color: #eeeeee;">(diffuse), </span><span style="color: #ae81ff;">1.0</span><span style="color: #eeeeee;">) </span>+ <span style="color: #008080; font-weight: bold;">vec4</span><span style="color: #eeeeee;">(</span><span style="color: #008080; font-weight: bold;">vec3</span><span style="color: #eeeeee;">(specular), </span><span style="color: #ae81ff;">1.0</span><span style="color: #eeeeee;">) </span>+ <span style="color: #eeeeee;">ambientColor</span><span style="color: #f92672;">;<br /></span><span style="color: #eeeeee;">}</span></pre> <p> </p> <p>上記のように、バーテックスシェーダの<span style="color: #ff5252;">attributeはinに</span>置き換えられました。</p> <p>また、<span style="color: #ff5252;">varying変数は、バーテックスシェーダ側にout,フラグメントシェーダ側にinと修飾子を付けて、変数名を同名に</span>するようになりました。</p> <p> </p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenGL">OpenGL</a>の記事は古いものも多いので、そういった古い記事のシェーダを読むときに知っているとすんなり理解しやすいのかなと思います。</p> <p>OpenGL2.x系とOpenGL3.0以降の違いでもう一つ大事そうなのは、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C6%A5%AF%A5%B9%A5%C1%A5%E3%A5%DE%A5%C3%A5%D4%A5%F3%A5%B0">テクスチャマッピング</a>をする際のビルトイン関数texture2Dが、textureという名前になっているのとかがあります。</p> <p>他にもなんかありそうですが、僕は今のところ困っていないのでわからないですw</p> <p> </p> <p>関係ないですが、色や法線情報のinとoutの前に<a class="keyword" href="http://d.hatena.ne.jp/keyword/flat">flat</a>っていう修飾子をつけると、ラスタライザでの線形補間を行わなくなるみたいで、結構面白いです。</p> <p>↓<a class="keyword" href="http://d.hatena.ne.jp/keyword/flat">flat</a>なし</p> <p><img class="hatena-fotolife" title="f:id:siguma_sig:20170425203010p:plain" src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20170425/20170425203010.png" alt="f:id:siguma_sig:20170425203010p:plain" /></p> <p>↓<a class="keyword" href="http://d.hatena.ne.jp/keyword/flat">flat</a>あり</p> <p><img class="hatena-fotolife" title="f:id:siguma_sig:20170425203037p:plain" src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/siguma_sig/20170425/20170425203037.png" alt="f:id:siguma_sig:20170425203037p:plain" /></p> <p>まだまだわからないことだらけですが、毎日少しずつ理解できているような…気がします。そうだといいな。</p> siguma_sig OpenGLでテクスチャマッピングをする際の注意点 hatenablog://entry/10328749687238972974 2017-04-23T00:45:45+09:00 2017-05-24T10:41:02+09:00 最近、ふと思い立ってOpenGLを使って簡単なゲーム作れるくらいの知識はつけておきたいな〜と考えて勉強しているのですが、いかんせん日本語の情報が少なく、あったとしてもかなり古いものであり、今とはだいぶ違ったりしていて大変です。 そんな感じでさぐりさぐりやっているわけですが、せっかくなので自分用のメモとしてハマったところをメモっておきます。 間違っていたら何かしらの方法で教えてもらえるととっても喜びます…! <p>最近、ふと思い立って<a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenGL">OpenGL</a>を使って簡単なゲーム作れるくらいの知識はつけておきたいな〜と考えて勉強しているのですが、いかんせん日本語の情報が少なく、あったとしてもかなり古いものであり、今とはだいぶ違ったりしていて大変です。</p> <p>そんな感じでさぐりさぐりやっているわけですが、せっかくなので自分用のメモとしてハマったところをメモっておきます。</p> <p> 間違っていたら何かしらの方法で教えてもらえるととっても喜びます…!</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenGL">OpenGL</a>で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C6%A5%AF%A5%B9%A5%C1%A5%E3%A5%DE%A5%C3%A5%D4%A5%F3%A5%B0">テクスチャマッピング</a>をする際には大体以下のような感じになるかと思います。</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #66d9ef;">GLuint </span><span style="color: #eeeeee;"><a class="keyword" href="http://d.hatena.ne.jp/keyword/tex">tex</a></span><span style="color: #f92672;">;<br /></span><span style="color: #808080;">//テクスチャオブジェクトの生成<br /></span><span style="color: #a7ec21;">glGenTextures</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">1</span><span style="color: #eeeeee;">, </span><span style="color: #f92672;">&amp;</span><span style="color: #eeeeee;"><a class="keyword" href="http://d.hatena.ne.jp/keyword/tex">tex</a>)</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #808080;">//テクスチャのバインド<br /></span><span style="color: #a7ec21;">glBindTexture</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE_2D</span><span style="color: #eeeeee;">, <a class="keyword" href="http://d.hatena.ne.jp/keyword/tex">tex</a>)</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #808080;">//画像の読み込み<br /></span><span style="color: #66d9ef;">cv</span><span style="color: #f92672;">::</span><span style="color: #66d9ef;">Mat </span>img <span style="color: #f92672;">= </span><span style="color: #66d9ef;">cv</span><span style="color: #f92672;">::</span><span style="color: #a7ec21;">imread</span><span style="color: #eeeeee;">(</span><span style="color: #e6db74;">"texture0.<a class="keyword" href="http://d.hatena.ne.jp/keyword/png">png</a>"</span><span style="color: #eeeeee;">, </span><span style="color: #66d9ef;">cv</span><span style="color: #f92672;">::</span><span style="color: #9876aa; font-style: italic;">IMREAD_UNCHANGED</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span><span style="color: #808080;">//BGRAからRGBAへ変換<br /></span><span style="color: #66d9ef;">cv</span><span style="color: #f92672;">::</span><span style="color: #a7ec21;">cvtColor</span><span style="color: #eeeeee;">(img, img, </span><span style="color: #66d9ef;">cv</span><span style="color: #f92672;">::</span><span style="color: #9876aa; font-style: italic;">COLOR_BGRA2RGBA</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #808080;">//テクスチャにデータを紐付ける<br /></span><span style="color: #a7ec21;">glTexImage2D</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE_2D</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">0</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_RGBA</span><span style="color: #eeeeee;">, img</span><span style="color: #f92672;">.</span><span style="color: #9373a5;">cols</span><span style="color: #eeeeee;">, img</span><span style="color: #f92672;">.</span><span style="color: #9373a5;">rows</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">0</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_RGBA</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_UNSIGNED_BYTE</span><span style="color: #eeeeee;">,<br /></span><span style="color: #eeeeee;"> img</span><span style="color: #f92672;">.</span><span style="color: #9373a5;">data</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #808080;">//テクスチャユニットを指定<br /></span><span style="color: #ae81ff;">glActiveTexture</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE0</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #808080;">//テクスチャをバインド<br /></span><span style="color: #a7ec21;">glBindTexture</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE_2D</span><span style="color: #eeeeee;">, <a class="keyword" href="http://d.hatena.ne.jp/keyword/tex">tex</a>)</span><span style="color: #f92672;">;<br /></span><span style="color: #f92672;"><br /></span><span style="color: #808080;">//指定したユニット番号をシェーダに送る<br /></span><span style="color: #ae81ff;">glUniform1i</span><span style="color: #eeeeee;">(uniLocation</span><span style="color: #f92672;">[</span><span style="color: #ae81ff;">6</span><span style="color: #f92672;">]</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">0</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;<br /></span></pre> <p> </p> <p>画像の読み込みに関しては、いまいち何を使うのが良いのかわからなかったのでとりあえず<a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenCV">OpenCV</a>のimreadを使用しています。</p> <p>実はここでもハマりました…(<a class="keyword" href="http://d.hatena.ne.jp/keyword/OpenCV">OpenCV</a>がBGRAの順なのを完全に忘れてた…)</p> <p>これで描画できる!やった!!!と思って実行したらなぜか真っ黒に…</p> <p>その後色々調べて</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #ae81ff;">glGenerateMipmap</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE_2D</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;</span></pre> <p>これを、テクスチャにデータを紐付けたあとに呼んでやればうまいこと描画されました。</p> <p>このとき僕は、glTexImage2Dの第二引数でミップマップ使わない設定にしてあるから呼ばなくていいはずなのに…って思ってました。</p> <p>でもこれ、呼ばないとだめなんですね。</p> <p>理由は、テクスチャパラメータのデフォルトの指定が、ミップマップを使うものになっているから。</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #a7ec21;">glTexParameteri</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE_2D</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_TEXTURE_MIN_FILTER</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_LINEAR_MIPMAP_LINEAR</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;</span></pre> <p>どうやら縮小の際の設定のデフォルト値が上記のようになっているらしい。</p> <p>これのせいで、ミップマップを生成しないと、生成されていないミップマップを読み込みに行ってしまい、真っ黒になってしまうみたいです。</p> <p>つまり、ミップマップを生成する必要が無いなら、先にテクスチャパラメータを指定してしまえば、glGenerateMipmapを呼んでやらなくてもうまく表示されるわけです。</p> <pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Ricty Discord'; font-size: 10.5pt;"><span style="color: #a7ec21;">glTexParameteri</span><span style="color: #eeeeee;">(</span><span style="color: #ae81ff;">GL_TEXTURE_2D</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_TEXTURE_MIN_FILTER</span><span style="color: #eeeeee;">, </span><span style="color: #ae81ff;">GL_NEAREST</span><span style="color: #eeeeee;">)</span><span style="color: #f92672;">;</span></pre> <p><em>よって、上のような設定をテクスチャオブジェクトごとにしてやればおっけーです。</em></p> <p>テクスチャパラメータの指定はどうやらテクスチャオブジェクトごとにするようなので、その点だけ注意が必要ですね。</p> <p>多分これで、うまく描画出来ると思います。</p> <p> </p> <p>正直全く自身がないので、本当に間違っていたら教えてもらえるとうれしいです…</p> <p>あと、参考にする方がもしいれば、間違っていても構わないという気持ちでお願いします。</p> siguma_sig 部内Unityハンズオンした話とその資料 hatenablog://entry/10328749687199033356 2016-12-14T22:27:51+09:00 2017-05-24T10:40:43+09:00 12/10と12/14で二回、同じ内容で部内Unityハンズオンしました。 その資料の公開と、感想について書きます。 コンピュータ倶楽部NITMic(トップ | NITMic - 名古屋工業大学 コンピュータ倶楽部)では、定期的に部員同士で主にゲーム制作に関する講座(プログラミング、デザイン、作曲など)や紹介を行っています。 今回は、僕が学祭後で時間が有ったのと、3年生はもう部活動は引退なので後輩に知識を伝えて行きたかったので、企画しました。 ちなみに僕はハンズオンというものに参加したことがないので普通はどんな感じにやるのかわからなかったです… まあ、資料見ながら手を動かしてもらえれば良いかな… <p>12/10と12/14で二回、同じ内容で部内Unityハンズオンしました。</p> <p>その資料の公開と、感想について書きます。</p> <p> </p> <p>コンピュータ倶楽部NITMic<a href="http://nitmic.club.nitech.ac.jp/">(トップ | NITMic - 名古屋工業大学 コンピュータ倶楽部)</a>では、定期的に部員同士で主にゲーム制作に関する講座(プログラミング、デザイン、作曲など)や紹介を行っています。</p> <p>今回は、僕が学祭後で時間が有ったのと、3年生はもう部活動は引退なので後輩に知識を伝えて行きたかったので、企画しました。</p> <p>ちなみに僕はハンズオンというものに参加したことがないので普通はどんな感じにやるのかわからなかったです…</p> <p>まあ、資料見ながら手を動かしてもらえれば良いかなと思って今回はやってみました。</p> <p>具体的な資料は以下です。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Sigsiguma/NITMicUnityHandOn" src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2FSigsiguma%2FNITMicUnityHandOn" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/Sigsiguma/NITMicUnityHandOn">github.com</a></cite></p> <p>対象としては、部内の人で聞きたい人は誰でもということにしましたが、プログラムの知識がある程度ないと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>で一部理解しづらいところがあったかもしれません。</p> <p>内容としては、Unityにおける3Dゲーム開発の基礎が出来るようにということを目標に作成しました。</p> <p>各ウィンドウの説明から始まり、プリミティブの作成、マテリアルの作成、Prefabの作成…と続いていきます。</p> <p>基本的には、目次を見てもられば何があるのかはわかるかと思います。</p> <p> </p> <p>今回この資料を作成するにあたって、まず何で作るか悩みました。</p> <p>そこで我が部の元部長に相談して、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Github">Github</a>ならマークダウンで書けるし<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>のハイライトもできるし、公開しやすいよと教えてもらってこうなりました。</p> <p>実際やってみた感じ、すごく作りやすかったので感謝しています。</p> <p> </p> <p>また、内容として何を含めるかも悩みました。</p> <p>正直どこまで話せば良いのかもわからなかったですが、僕がUnityを触り始めたときに知っていたら良かっただろうなという知識を詰めたつもりです。(ショートカットキーとかも)</p> <p> </p> <p>やってみた感じとしては、まあ大まかには理解してくれたかなといった感じです。</p> <p>ハンズオン自体は6時間程度で終わったのですが、その後も残って自分独自のゲーム性や機能を追加している子もいました。</p> <p>これでUnityに興味を持って、面白いゲームを作ってくれる子がいれば幸いです。</p> <p> </p> <p>資料については、結構作るのに時間が掛かったので、せっかくだし公開しようと言った感じです。</p> <p>正直、間違いや非常にわかりづらい部分(Animatorの章とか…)があると思いますがある程度まとまってはいると思うので、Unityやってみたいけど中々手を出せない方とかは一度目を通してもらえると嬉しかったりです。</p> <p> </p> <p>こんな感じで部内で需要があれば今後も何か資料作ったりして、外部にも公開していきたいです。</p> <p> </p> siguma_sig VisualStudioCode(VSCode)でUnity開発をする際のフォーマッターの設定方法 hatenablog://entry/10328749687196188453 2016-11-27T13:48:15+09:00 2017-05-24T10:40:18+09:00 備忘録として書いておきます。 VSCodeを使ってUnityで開発を行う際に、通常の状態だとスクリプトをフォーマッターがご丁寧に以下のようにしてくれます。 でも、僕としては下のような感じでフォーマットして欲しかったわけです。 ずっとやり方がわからなかったのですが、Facebook上のUnity助け合い所にてほぼ同様の質問があり、そこの回答のおかげでわかったので、メモとして残しておきます。 <p>備忘録として書いておきます。</p> <p>VSCodeを使ってUnityで開発を行う際に、通常の状態だと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>をフォーマッターがご丁寧に以下のようにしてくれます。</p> <p><script src="https://gist.github.com/d0a3d3f66b4514e0cf5e9313af584cc3.js"> </script></p> <p> でも、僕としては下のような感じでフォーマットして欲しかったわけです。</p> <p><script src="https://gist.github.com/6677e410e7568aea307c71391bea6537.js"> </script></p> <p> </p> <p>ずっとやり方がわからなかったのですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Facebook">Facebook</a>上のUnity助け合い所にてほぼ同様の質問があり、そこの回答のおかげでわかったので、メモとして残しておきます。</p> <p>以下のやり方は<a class="keyword" href="http://d.hatena.ne.jp/keyword/MacOS">MacOS</a>における方法です。</p> <p>が、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>の場合も<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ等が変わるだけかと思います。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%BD%A4%E2%A4%BD%A4%E2">そもそも</a>、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>の人は本家VSを使えば良いような気もしますが…</p> <p> </p> <p>まず、この設定を行うには二つの方法があります。</p> <ol> <li>slnファイルが存在するところに、omnisharp.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>ファイルを作ってそこに設定を書く</li> <li>VSCode上の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B3%C8%C4%A5%B5%A1%C7%BD">拡張機能</a>のOmnisharp(<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>)のフォルダ内にあるconfig.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>に設定を書く</li> </ol> <p>僕は、常に先程の例の下のような感じでフォーマットをしてくれればよいと思っているので、今回は2の方法を選択しました。</p> <p>人によっては、会社のプロジェクトの規約や一緒に開発する人に合わせるために、フォーマットのスタイルをUnityプロジェクトによって変えたい場合があるかと思うので、1の方法を選択したほうが良いかもしれません。</p> <p> </p> <p>どちらも記述する内容は同じで、場所が違うだけなのでここからはそれぞれで読み替えて下さい。</p> <p>2の方法を取る場合、/Users/ユーザ名/.vscode/extensions/<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ以下に、ms-vscode.csharpといったようなフォルダがあると思います。</p> <p>その中の、bin/omnisharp/config.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>を編集します。</p> <p>僕は以下のように設定しました。</p> <p><script src="https://gist.github.com/6f1f16addfc87bfe5b291724e67dbb25.js"> </script></p> <p>こうすることで、先程のコードでいう下のようなフォーマットにすることが出来ました。</p> <p>細かい設定等は僕自身もまだ分かっていませんが、スペースを挿入するかどうかなども細かく決めれるらしいので、気になる人は調べてみて下さい。</p> <p> </p> siguma_sig 部活動の引退と学祭で作ったゲーム hatenablog://entry/10328749687195920131 2016-11-25T19:56:15+09:00 2017-05-24T10:39:45+09:00 なんとなく記事が書きたくなったので、書きます。 つい先日行われた名古屋工業大学の工大祭で、僕が所属していたコンピュータ倶楽部NITMicは、毎年恒例のゲームセンターブースを展示しました。 ゲームセンターブースでは、コンピュータクラブの部員が作ったゲームを展示し、実際にお客さんに来ていただいて遊んでもらい、フィードバックを頂く、と言ったことを行っています。 個人的に、うちの部活で最も大事にしている(少なくとも僕は)イベントで、毎年たくさんの面白いゲームが出来ています。 今年は、全部で20を超えるゲームが作られました! その中で僕は、BalloonCoins!という2~4人対戦のゲームを作りました… <p>なんとなく記事が書きたくなったので、書きます。</p> <p>つい先日行われた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CC%BE%B8%C5%B2%B0%B9%A9%B6%C8%C2%E7%B3%D8">名古屋工業大学</a>の工大祭で、僕が所属していたコンピュータ倶楽部NITMicは、毎年恒例のゲームセンターブースを展示しました。</p> <p>ゲームセンターブースでは、コンピュータクラブの部員が作ったゲームを展示し、実際にお客さんに来ていただいて遊んでもらい、フィードバックを頂く、と言ったことを行っています。</p> <p>個人的に、うちの部活で最も大事にしている(少なくとも僕は)イベントで、毎年たくさんの面白いゲームが出来ています。</p> <p>今年は、全部で20を超えるゲームが作られました!</p> <p>その中で僕は、BalloonCoins!という2~4人対戦のゲームを作りました。</p> <p> </p> <p>このゲームは、ごくごく単純で、空中に散らばるコインを自分のキャラクターを操作しながら集めて、一番多く集めた人の勝ち!というルールです。</p> <p>ゲーム的な要素としては、まず、操作が風船の浮上とキャラの移動しかないため操作感が独特であるという点があります。</p> <p>あとは、相手の上からぶつかることで相手の風船を割ると同時にコインを落とさせ、奪うことができるという点があります。</p> <p>僕がこのゲームを思いついたときに一番重視したかったのが二つ目の奪い合いという点でした。</p> <p>結果的に、工大祭のアンケートで、いい感じに友情が破壊できそうなゲームでした。とかコイン無視してお互い倒し合っていた。と言った感想が貰えたので、それなりに達成できたのかなと思います。</p> <p> </p> <p>さて、僕達3年生は今年の学祭でコンピュータ倶楽部は引退となってしまうので、このBalloonCoins!が最後の作品であったわけです。</p> <p>その割に、ゲーム自体ものすごく単純で、作ること自体もそこまで難しそうでなかったため、単純だねとか、3年のくせに作ってるもの簡単そうとか思われたかもしれません。</p> <p>ただ、僕は1,2年次の学祭での経験から出来る限り多くの人に遊んでもらいたかったら、</p> <p>なるべく単純で、かつ、多人数で遊べるようなものが良いことを学んでいたので、今回はこのようなゲームを作りました。</p> <p> </p> <p>学祭では、毎年お客さんからのアンケートを集計して、人気ゲームトップ3が発表されます。</p> <p>結果的に、僕のゲームは今年も3位以内にランクインすることはできませんでした。</p> <p>また、今年の1位はぼうバトという、2人プレイの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B3%CA%C6%AE%A5%B2%A1%BC%A5%E0">格闘ゲーム</a>でした。</p> <p>これはものすごいことで、基本的にうちの部活のアンケートは4人組のお客さんなら4人ひとりひとりに書いてもらうため、例年1~3位は4人プレイ用のゲームが占めていました。</p> <p>また、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B3%CA%C6%AE%A5%B2%A1%BC%A5%E0">格闘ゲーム</a>というプレイする敷居が高めのゲームで1位ということが偉業でした。</p> <p>ただ、部内での<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D0%A5%C3%A5%B0">デバッグ</a>期間でも最も遊ばれていたゲームはぼうバトで、僕もとてもおもしろいと思っていました。</p> <p> </p> <p>なんにしても、また負けてしまった上に、自分が考えていた学祭で勝つためにはこういうゲームでないといけない!という前提が覆されてしまったため、とても悔しかったのと同時に、改めて面白いゲームを作る、お客さんに楽しんでもらえるものを作ることの難しさを感じました。</p> <p>ぼうバトは、製作者も言っていましたが、初心者はガチャプレイでも楽しめて、上級者は極めることができるゲームでした。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>中のゲーム制作でも、上手い人と下手な人の差が出る部分は大事だということは言われて、学んでいたので、それをしっかりと表現してるのがぼうバトなのかな、と思いました。</p> <p> </p> <p>部活動も引退になり、以前にも増してさらに自由な時間が増えたので、今後は後輩に対して技術を教えていったり、自分で自信を持って面白いと言えるゲームを作れるように日々技術力と、ア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%C7%A5%A2">イデア</a>力を磨いていきたいです。</p> <p> </p> <p>久しぶりに文章書いたらめちゃくちゃまとまりなくなってしまった上にものすごく時間がかかってしまった…</p> <p>結局何が言いたかったかというと、<span style="font-size: 150%;">面白いゲーム作るのは難しい!でも楽しい!</span>それだけです。</p> <p> </p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="トップ | NITMic - 名古屋工業大学 コンピュータ倶楽部" src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fnitmic.club.nitech.ac.jp%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://nitmic.club.nitech.ac.jp/">nitmic.club.nitech.ac.jp</a></cite></p> siguma_sig 【2016夏】サマーインターン感想(コロプラ、GREE、サイバーエージェント) hatenablog://entry/10328749687189342851 2016-10-14T11:07:15+09:00 2017-05-24T10:37:49+09:00 ブログを作って、記事を書こうとしたのは良いものの、特にまだ書くようなものもなく… せっかくなので、今年の夏のインターンの感想を書き残しておこうと思います。 どこまで書いていいものなのか分からないので、もしだめな内容があればこそっとメッセとかで教えてください…m(_ _)m <p>ブログを作って、記事を書こうとしたのは良いものの、特にまだ書くようなものもなく…</p> <p>せっかくなので、今年の夏の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>の感想を書き残しておこうと思います。</p> <p>どこまで書いていいものなのか分からないので、もしだめな内容があればこそっとメッセとかで教えてください…m(_ _)m</p> <p> </p> <p> 目次</p> <ul class="table-of-contents"> <li><a href="#株式会社コロプラ"> 株式会社コロプラ</a><ul> <li><a href="#内容">内容</a></li> <li><a href="#感想">感想</a></li> <li><a href="#おまけ">おまけ</a></li> </ul> </li> <li><a href="#グリー株式会社">グリー株式会社</a><ul> <li><a href="#内容-1">内容</a></li> <li><a href="#感想-1">感想</a></li> <li><a href="#おまけ-1">おまけ</a></li> </ul> </li> <li><a href="#株式会社サイバーエージェント株式会社アプリボット">株式会社サイバーエージェント(株式会社アプリボット)</a><ul> <li><a href="#内容-2">内容</a></li> <li><a href="#感想-2">感想</a></li> <li><a href="#おまけ-2">おまけ</a></li> </ul> </li> <li><a href="#まとめ">まとめ</a></li> </ul> <h3 id="株式会社コロプラ"> 株式会社<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%ED%A5%D7%A5%E9">コロプラ</a></h3> <h4 id="内容">内容</h4> <p>5DAYS REAL WORK -GAME- という、ゲームジャム形式の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に参加しました。</p> <p>ほぼ名前の通りで、5日間で、エンジニア3人 + 社員デザイナーさん1人 + 社員エンジニアさん1人で、1本<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AB%A5%B8%A5%E5%A5%A2%A5%EB%A5%B2%A1%BC%A5%E0">カジュアルゲーム</a>を制作するという内容でした。</p> <p>開発環境は、Unity <a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a> + Git にて開発を行いました。</p> <h4 id="感想">感想</h4> <p>個人的に一番悔しかった<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>でした。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>の途中で、悔しくて泣いてしまいそうにもなりました。</p> <p>そう思えるくらい一生懸命になって取り組める内容の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>で、5日間とは思えないくらい充実していて、たくさんの思い出が残りました。</p> <p>また、ゲーム制作に対する姿勢が大きく変わるような<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>だと思います。</p> <p>技術面についても、初対面の人達と、どう制作を進めていくかという練習にもなるし、Unityのさらなる学習にも繋がりました。</p> <p>また、意外とUnity自体をあまり触ったことのない人や、ゲームをあまり作ったことがない人が多くいる印象でした。</p> <p>社員さんがチームに入って制作のお手伝いをしてくれて、現場のデザイナーさんの絵でゲームが作れて、困ったらいつでも現場のエンジニアさんがサポートしてくれるという環境がとても良かったです。</p> <h4 id="おまけ">おまけ</h4> <p>VRゲーム<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%C3%A5%AB%A5%BD%A5%F3">ハッカソン</a>が12/3(土),12/10(土),12/11(日)の3日間かけて行われるそうです!</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="コロプラ | エンジニアインターン「Next Tech Project」" src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fbe-ars.colopl.co.jp%2Finternship%2Fjisedai%2Fengineer%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://be-ars.colopl.co.jp/internship/jisedai/engineer/">be-ars.colopl.co.jp</a></cite></p> <h3 id="グリー株式会社">グリー株式会社</h3> <h4 id="内容-1">内容</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/GREE">GREE</a> Campというゲームジャム形式の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に参加しました。</p> <p>期間は2日間で、エンジニア1人+クリエイター1人+ビジネス/ゲームプランナー1人で、1本<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AB%A5%B8%A5%E5%A5%A2%A5%EB%A5%B2%A1%BC%A5%E0">カジュアルゲーム</a>のモックを制作するという内容でした。</p> <p>開発環境は、エンジニアはUnity <a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a> でした。</p> <h4 id="感想-1">感想</h4> <p>とにかく開発時間が短い!!!!!です。</p> <p>色んなコンテンツを用意してくれているので、実質的な開発時間は8~9時間程度です。</p> <p>普段から1day<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%C3%A5%AB%A5%BD%A5%F3">ハッカソン</a>とかに参加しているので、作業時間の配分などは分かっていたつもりでしたが、完全に失敗してしまいました。</p> <p>メンターさんについては、非常に人数が多かったような印象があります。</p> <p>あと、ひたすら気にかけてくれていて、質問したり、ゲームの内容に関しての相談がとってもしやすかったです。</p> <p>技術面についても、いくつか新しいことにもトライしてみたので、個人開発の2日間とは比べ物にならないくらい成長できたかなと思いました。</p> <p>あとは、社員さんと普段やってるゲームがほぼ同じだったりで、お話がすっごく盛り上がりましたw</p> <h4 id="おまけ-1">おまけ</h4> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/GREE">GREE</a> Campは、8月,9月,10月と開催回数が多く、また冬以降にも開催されるそうなので、是非是非!</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Careers at GREE | インターンシップ" src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fjobs.gree.net%2Fjp%2Fja%2Finternships%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://jobs.gree.net/jp/ja/internships/">jobs.gree.net</a></cite></p> <h3 id="株式会社サイバーエージェント株式会社アプリボット">株式会社<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B5%A5%A4%A5%D0%A1%BC%A5%A8%A1%BC%A5%B8%A5%A7%A5%F3%A5%C8">サイバーエージェント</a>(株式会社アプリボット)</h3> <h4 id="内容-2">内容</h4> <p> WORKという就業型<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に参加しました。</p> <p>期間は1ヶ月で、関われるプロジェクトは60以上!(だったはず)の中から、自分に最も合いそうなプロジェクトを人事さんとの面談や、現場の社員さんとの面談ですり合わせながら決めて、実際にそこで働くというものです。</p> <p>期間についても、7月頭~9月末の中で好きな期間1ヶ月程度で、とても参加しやすいと思います。</p> <p>僕は、子会社の株式会社アプリボットで、新規ゲーム開発プロジェクトに参加しました。</p> <p>開発環境はUnityC# + Git でした。</p> <h4 id="感想-2">感想</h4> <p>ひたすら学べる1ヶ月間でした。</p> <p>技術に関してはもちろん、現場の開発がどのように行われているか、プロジェクトの人数はどれくらいなのか、環境はどんななのか、ということがはっきりとわかりました。</p> <p>もちろん、これは会社やプロジェクトによってまちまちではあると思いますが、一つの指針ができただけでも非常に大きいと思います。</p> <p>技術力に関しても、自分が本当にまだまだであるということが実感でき、どこが足りないのかということもはっきりと感じることができました。</p> <p>あとは、現場の社員さんに自分の書いたコードを見てもらって、アド<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A5%A4%A5%B9">バイス</a>をもらって…というのがとても新鮮で、非常に勉強にもなりました。</p> <p>実際、この<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に行く前と行った後の自分のコードを見比べるとだいぶ変わっている(ような気がする)。</p> <p>他にも、ランチに連れて行ってもらったり、社内勉強会にも行ったり、ゲームを一緒にしたりとすっごく楽しかったです。</p> <h4 id="おまけ-2">おまけ</h4> <p>秋、冬も長期<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>はまだまだやっているようです!</p> <p>また、長期にかかわらず<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B5%A5%A4%A5%D0%A1%BC%A5%A8%A1%BC%A5%B8%A5%A7%A5%F3%A5%C8">サイバーエージェント</a>は非常に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>が多いので、</p> <p>是非どうぞ〜!</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="WORK~Autumn&Winter | 株式会社サイバーエージェント" src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.cyberagent.co.jp%2Frecruit%2Ffresh%2Freal%2Fevent_detail%2Fid%3D12608" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.cyberagent.co.jp/recruit/fresh/real/event_detail/id=12608">www.cyberagent.co.jp</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="REAL | 株式会社サイバーエージェント エンジニア・クリエイター新卒採用サイト" src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.cyberagent.co.jp%2Frecruit%2Ffresh%2Freal%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.cyberagent.co.jp/recruit/fresh/real/">www.cyberagent.co.jp</a></cite></p> <h3 id="まとめ">まとめ</h3> <p>どの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>に参加しても、絶対に損はないと思います!</p> <p>自分と同じ世代でこんな凄い奴がいるのか…とか、もっと技術力があれば…という経験をして、これからのモチベーションにも繋がると思いますし、横の繋がりも広げることができます。</p> <p>まだまだ秋、冬の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%BF%A1%BC%A5%F3">インターン</a>もあると思うので、バシバシ参加していきましょー!</p> <p> </p> <p> </p> <p> </p> <p>(全く関係ないですが、名古屋でもできそうなゲーム開発のアルバイトとか、お仕事あれば紹介してほしいです…)</p> <p> </p> siguma_sig