【LeetCode】1. Two Sum (Easy)
【最初に】
LeetCodeのアルゴリズムの最初の問題を解いてみます
https://leetcode.com/problems/two-sum/
【問題】
与えられたint配列に対して足すと指定の数値になる配列内の2つの数値のインデックスを返せ。入力には必ず解が1だけある前提。配列の同じ要素は使わないこと。
Example:
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
【とりあえず】
単純に思いつくのは、とりあえずfor文をぶん回して足していって答えに辿り着いたら終了するパターン。
でもこれはたぶんダメダメなやつ
解答を見ると、上の例だと最後の1個まで解が見つからない場合、ループをn回実行して、各要素ごとにn回ずつ回すので、時間計算量はO(n^2)になるとのこと。
【違うパターン】
ヒントを見るとhashmapを使ったらとあるので、違うパターンを試してみる
最初に数字をkeyに、配列のインデックスをvalueにした連想配列を作る。次にもう一度配列を回して、補数がマップにあるかどうか検索。存在する場合は結果を返して終了する。
速くなった!(メモリは増えたけど)
時間計算量はO(n^2)からO(n)に減少(O(2n)と書かない理由はこちらを参照:https://qiita.com/drken/items/872ebc3a2b5caaa4a0d0)ただし、空間計算量は配列の分だけmapとしてメモリに格納するのでO(1)からO(n)に増える
【さらに違うパターン】
↑だと配列のループを2回回しているけど、解答見るとループを1回で済ませる方法もある
ループを回していって、マップに補数がなければ格納。順にループを回していって過去に追加したマップの値と比較して発見すればインデックスを返して終了する。
【最後に】
気分転換に最初の1個目をやってみましたが面白いですね。アルゴリズムはこれまであまりきちんと勉強して来なかった(正確には大学でやってるはずだけど真面目に授業受けてなかった)ので、もうちょっと続けてみようかと思います。ちなみにC++も大学でやってるはずだけど...(以下同文)C++の作法とかよくわかってないので、その辺りはご容赦を。
【Unity】LWRPプロジェクトの作成とサンプルシーン
Unity2018.1 から LWRP (Lightweight Render Pipeline: 軽量レンダーパイプライン) が導入されました。LWRPはSRP(Scriptable Render Pipeline)の1つで、HDRP (High Definition Render Pipeline: 高画質レンダーパイプライン)に比べ軽量でモバイル向きだそうです。
SRPとはUnity がフレームをどのように描画するかをデベロッパーが C# で制御できるようにするものです。
まずは新規にLWRP用のプロジェクトを作成します。 なお、既存のプロジェクトをLWRPに適用させるのはテラシュールさんの記事がわかりやすかったです。
実行環境
Unity: 2018.3.4f1
LWRP: 4.1.0-preview
LWRPプロジェクトの作成
プロジェクトの新規作成で、Template に Lightweight RP (Preview) が増えているので選択します。ちなみに、LWRPはまだPreview版のようで、2019.1から本格的に使えるようになるようです。
プロジェクトを作成するとこんなサンプルシーンが開きます。
サンプルプロジェクトにはLWRP設定が3種類用意されています。Edit > Project Settings > Graphics の一番上の設定を切り替えれば変更できます。
LWRP-LowQuality | LWRP-MediumQuality | LWRP-HighQuality |
設定の違いは以下のようになっています。
LWRP-LowQuality | LWRP-MediumQuality | LWRP-HighQuality | |
HDR | OFF | ON | ON |
MSAA(Anti Aliasing) | Disabled | 2x | 4x |
Main Light - Shadow Resolution |
512 | 1024 | 1024 |
Additional Lights |
Disabled |
Per Pixel |
Per Pixel |
Additional Lights - Cast Shadows |
- |
OFF |
ON (1024) |
Shadows - Cascades |
No Cascades | Two Cascades | Four Cascades |
Shadows - Soft Shadows |
OFF | ON | ON |
左の絵はHDRがオフなのでスポットライトとブルームが反映されていないのと、真ん中の絵はAdditional LightsのCast Shadowsがオフなので右側にスポットライトの影が出てないのが大きな違いでしょうか。
なお、LWRPと通常のUnityビルドインパイプラインの違いは下記に記載があります。
注意点は、CEDEC2018の発表資料にある通り、
① CameraのCallbackが呼ばれない
Camera.OnRenderImageなどのCamera系のコールバックが呼ばれなくなります。
② Surface Shaderが使えない
vertex shader / fragment shader もしくは、2018.1からの新機能ShaderGraphでのShader記述に置き換える必要があります。
それ以外にも、
③ 現状のLWRPでは複数のカメラに対応していない。Unity2019.1で対応するかもしれないとのこと。
https://forum.unity.com/threads/glitching-with-multiple-cameras-lwrp.592477/
- Remove multi-camera support. LWRP and HDRP will not support multi-camera layered rendering.
つまり、UI用にMainCameraとは別のカメラを追加する、みたいなことは現状ではうまく動作しないと思われます。
④ LWRPは、通常のビルドインのUnityレンダリングやHDRPとは互換性がない。既存のプロジェクトにLWRPを適用するための一括変換コマンドもあるが、すべて正確に変換できるとは限らずアセットによっては手動で修正しなければいけないかもしれません。
Editor > Render Piepeline > Update Project Materials To Lightweight Materials
などがありそうです。そもそもLWRPはまだPreview版で仕様が変更される可能もあります。本格的な使用はUnity2019での正式版まで待った方がいいかもしれません。
【Unity】async/awaitのフレーム消費
C# 6.0から async/await が使えるようになり、コルーチンでは解決できなかった「何もしてないのにフレーム消費されてしまう」問題が解決できそうです。
まずは今までのコルーチン処理です。IEnumeratorを返すメソッドでは下記のように非同期処理を上から順に実行される形で書くことができました。下記の処理では、coroutine1が終わるのを待ってからcoroutine2が実行されます。コールバック処理を書くよりも見通しの良いコードを書くことができました。
yield return coroutine1;
yield return coroutine2;
もちろん、下記のようにMoveNextを使って書くこともできますが、 上記のほうが1行で書けるためスッキリ書けました。
while (coroutine1.MoveNext())
{
yield return null;
}
while (coroutine2.MoveNext())
{
yield return null;
}
ただし、yield return coroutine; の書き方は問題もありました。メソッド内部で仮に何も処理していなくても、必ず1フレーム消費されてしまうことです。
public class AsyncFrameTest1 : MonoBehaviour
{
private int mFrameNum;
private bool mIsPlaying;
private IEnumerator Start()
{
// 念のため1フレーム待って計測
yield return null;
mIsPlaying = true;
mFrameNum = 0;
var previousFrame = mFrameNum;
yield return TestCoroutine1();
Debug.LogFormat("TestCoroutine1: (+{0}フレーム)", mFrameNum - previousFrame);
previousFrame = mFrameNum;
yield return TestCoroutine2();
Debug.LogFormat("TestCoroutine2: (+{0}フレーム)", mFrameNum - previousFrame);
}
public IEnumerator TestCoroutine1()
{
yield break;
}
public IEnumerator TestCoroutine2()
{
yield return null;
}
private void Update()
{
if (mIsPlaying)
{
mFrameNum++;
}
}
}
実行結果はご覧の通りです。TestCoroutine1は内部でyield breakしているだけですが、yield return nullしているTestCoroutine2と同様に処理が終わるまで1フレームかかっています。これが結構厄介で、例えばコルーチンメソッド内でキャッシュがある場合はロードせずにyield breakするような処理を書いても最低1フレームはかかってしまうということになります。
もちろんMoveNextで書いておけば余計なフレームは消費せずに済みます。
previousFrame = mFrameNum;
var testCoroutine1 = TestCoroutine1();
while (testCoroutine1.MoveNext())
{
yield return null;
}
Debug.LogFormat("MoveNext(TestCoroutine1): (+{0}フレーム)", mFrameNum - previousFrame);
var testCoroutine2 = TestCoroutine2();
while (testCoroutine2.MoveNext())
{
yield return null;
}
Debug.LogFormat("MoveNext(TestCoroutine2): (+{0}フレーム)", mFrameNum - previousFrame);
1行でスッキリ書けて、かつ余計なフレームを消費しない書き方があればいいのになあとずっと思っていましたが、async/awaitで実現できるようです。
public class AsyncFrameTest2 : MonoBehaviour
{
private int mFrameNum;
private bool mIsPlaying;
private async void Start()
{
mIsPlaying = true;
mFrameNum = 0;
var previousFrame = mFrameNum;
await TestAsync1();
Debug.LogFormat("TestAsync1: (+{0}フレーム)", mFrameNum - previousFrame);
previousFrame = mFrameNum;
await TestAsync2();
Debug.LogFormat("TestAsync2: (+{0}フレーム)", mFrameNum - previousFrame);
mIsPlaying = false;
}
public async UniTask TestAsync1()
{
// 何もしない
}
public async UniTask TestAsync2()
{
await UniTask.DelayFrame(1); // 1フレーム待つ
}
private void Update()
{
if (mIsPlaying)
{
mFrameNum++;
}
}
}
実行結果です
便利ですね!
【Unity】 async/awaitの実行スレッド
C#6.0から async/await が使えるようになり非同期処理がスッキリ書けそうなので興味あるのですが、マルチスレッドとの関連が不明だったので色々試してみました。
実行環境
Unity2018.3.4f1
Taskのasync/await
まずはシンプルなasync/awaitのサンプル。TestAsyncで3秒待っています。
public class TaskTest1 : MonoBehaviour
{
private async void Start()
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call Start");
await TestAsync();
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Start");
}
public async Task TestAsync()
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call TestAsync");
await Task.Delay(3000);
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End TestAsync");
}
}
結果はこんな感じですべて同じスレッドで実行されます。
つまり、async/awaitってただ書くだけでは非同期処理の待ち合わせはされるが、マルチスレッドにはならないということですかね。
UniTaskのasync/await
UniTask(UniRx.Async)でも同様になります。
public class TaskTest2 : MonoBehaviour
{
private async void Start()
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call Start");
await TestAsync2();
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Start");
}
public async UniTask TestAsync2()
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call TestAsync2");
await Task.Delay(3000);
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End TestAsync2");
}
}
結果、すべて同じスレッドで実行されています。
Taskをマルチスレッドで実行
別のスレッドで実行させたい場合は、Task.Runが使えます(マルチスレッドで実行する方法は他にもあるようですが割愛します)
public class TaskTest3 : MonoBehaviour
{
private async void Start()
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call Start");
await TestAsync();
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Start");
}
public async UniTask TestAsync()
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call TestAsync");
await Task.Run(() =>
{
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Start Run");
Thread.Sleep(3000);
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Run");
});
Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End TestAsync");
}
}
このように書いて実行すると以下のように、Task.Runに渡したActionは別スレッドで実行されます。
ただ、ゲームのUI処理やロジック処理でマルチスレッドを使う機会はほとんどないのでこれ以上は追わないことにします。
UniTaskの作者のこの意見には私も同意です。
UniTaskはどちらかというとJavaScript的(シングルスレッドのための非同期の入れ物)に近いです。Taskは、そうした非同期の入れ物に加えてマルチスレッドのためなどなど、とにかく色々なものが詰まりすぎていて、あまりよろしくはない。非同期とマルチスレッドは違います。明確に分けたほうが良いでしょうし、UnityではC# JobSystemを使ったほうが良いので、カジュアルな用途以外(まぁラクですからね)ではマルチスレッドとしてのTaskの出番は少なくなるでしょう。
【Unity】MagicaVoxel で Export した obj ファイルのマテリアルが変更できなくなった
先日、Unity2017からUnity2018にアップデートしたのですが、MagicaVoxel で エクスポートした obj ファイルのマテリアルを変更するために、objファイルを選択し、Inspector の Materials タブを選択すると Import Materials 以下の項目が何も出なくなりました。FBXファイルを選択しても同様に出てきません。
前までは、ここに Location という項目があり、いつもはこれを Use Embedded Materials にして、defaultMat の項目を変更しているのですが... 何も出てきません...
よく見るとConsoleタブにエラーが大量に吐き出されていました。
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.PropertyHandler.OnGUILayout (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, Boolean includeChildren, UnityEngine.GUILayoutOption options) (at /Users/builduser/buildslave/unity/build/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs:203)
UnityEditor.EditorGUILayout.PropertyField (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, Boolean includeChildren, UnityEngine.GUILayoutOption options) (at /Users/builduser/buildslave/unity/build/Editor/Mono/EditorGUI.cs:8791)
UnityEditor.EditorGUILayout.PropertyField (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, UnityEngine.GUILayoutOption options) (at /Users/builduser/buildslave/unity/build/Editor/Mono/EditorGUI.cs:8780)
UnityEditor.ModelImporterMaterialEditor.DoMaterialsGUI () (at /Users/builduser/buildslave/unity/build/Editor/Mono/ImportSettings/ModelImporterMaterialEditor.cs:364)
UnityEditor.ModelImporterMaterialEditor.OnInspectorGUI () (at /Users/builduser/buildslave/unity/build/Editor/Mono/ImportSettings/ModelImporterMaterialEditor.cs:162)
UnityEditor.AssetImporterTabbedEditor.OnInspectorGUI () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Inspector/TabbedEditor.cs:147)
UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editors, Int32 editorIndex, Boolean rebuildOptimizedGUIBlock, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1367)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
エラー内容を調べてはみたものの、よくわからず...
Unity 2018.2.13f1 だったのですが、Unity のサイトを見ると最新が Unity2018.2.14f1 になっていたので、ダメもとでアップデートしてみたところ、
なおりました・・・
Unity 2018.2.14f1のリリースノートを見ましたが、ズバリコレというのは見つからず。関連してそうなのはこれぐらい・・?
このまま解決できなかったらどうしようかとすごく焦りましたが、とにかく直って良かったです。Unity 2018 はまだ細かい不具合がありそうですね。
【Unity】.vs ファイルを削除する
Unity 2018 にして Visual Studio を使用し始めたところ、ソースコードを修正するたびに以下の差分が出るようになりました。
modified: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/UserPrefs.xml
modified: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/storage.ide
modified: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/storage.ide-shm
modified: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/storage.ide-wal
.gitignore に .vs/ を追加しても消えません。
一度リポジトリに追加してしまうと .gitignore に .vs/ の記述を追加しても、差分を追跡し続けてしまうようです。
記事にある通り、
git rm --cached -r [プロジェクトフォルダ]/.vs/
を実行すると
deleted: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/UserPrefs.xml
deleted: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/db.lock
deleted: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/storage.ide
deleted: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/storage.ide-shm
deleted: [プロジェクトフォルダ]/.vs/rogue2-unity/xs/sqlite3/storage.ide-wal
と削除差分が出てくるのでコミットしたところ、以降は差分が出なくなりました。
【Unity】iPhone に実機転送すると development team がないエラー
久しぶりに iPhone に実機転送しようとしたところ、XCode実行時に以下のエラーが出ました。
Signing for "Unity-iPhone" requires a development team. Select a development team in the project editor. (in target 'Unity-iPhone')
この記事によれば、Xcode8 からXcodeエディタ上でTeamを設定する必要があるそうです。最近、iOSでリリースしてなかったので知りませんでした...
詳しい説明は上の記事のほうが画像付きで詳しいので、簡単に手順だけまとめます
1. General > Signing > Add Account
2. Apple ID と Password を入力して Sign In
3. Team のドロップダウンで自分のアカウントを選択
実行すると、別のエラーが2個発生しました。
Your development team has reached the maximum number of registered iPhone devices.
Provisioning profile "iOS Team Provisioning Profile: (Bundle Id)" doesn't include the currently selected device "(端末名)".
そういえば、最近 iTunes Connect 開いてないなと思って確認してみたら、
デベロッパプログラムのメンバーシップの有効期限切れ
でした...