UnityのBuildエラー

Tutorial : John Lemon's Haunted Jaunt: 3D Beginnerを終えました。
テキストメインだったのでサクサク行ける。 意図とかはもう少しわかりやすくしてほしいところもあったけど。

最後にBuildしますがうまくいかなかったのでメモ。

1.Build先のフォルダはBuildするプロジェクトの外にしたほうが良いらしい。
2.Unityのバグ。 2019.3.0a1 で報告されており、 今回確認されたのは2019.3.0a2 Personal
こっちはBuildするプラットフォームを一度別のものに切り替えるとうまくいった。
(PC/Mac/Linux Standalone->WebGL->PC/... )

reproducible on Android (every time)とあるけどAndroid用Buildでは常に起きるってことなんだろうか。

Build failed with errors. at UnityEditor.BuildPlayer+Default buildMethods how to resolve it ? - Unity Answers

issuetracker.unity3d.com

ABC-105のC - Base -2 Number

前は解説を見ても解けなくて、久々に解説を見たら解けた。 公式以外の解説が検索でヒットしない?ように見える。

ABC105

提出した解答



そもそも負数の余りって何?という話。
例えばだけど下のコードは何を表示するだろう。 私は-1だろうと思ったがそれは間違いらしい。
実際には2が表示される。まぁ0かどうか判定するなら困らないのかな・・・。

print( -10%3 )#=>?

おまけ

pythonで負数%正の数をすると多分以下のコードと同じようなことなのだろう。

def remain(n, base):
    
    abs_n = abs(n)
    return base - abs_n%base

ans = remain(-10,3)
print(ans)

ちなみに下のコードだと-1が表示される。

print( -10%(-3) )#=>?

C++だと負数であっても正の数と結果が変わらないようだ。

https://wandbox.org/permlink/cTNloRSLa3h8VdXG

#include<iostream>

int main(){

    std::cout <<  -10  %   3  <<"\n";//-1
    std::cout <<  -10  %  -3  <<"\n";//-1    
    
    return 0;    
}

解説を読むと下から決められるらしい。
(問題文から下から決められそう!という感覚なり証明なり出来ないと難しい気がする)

上から決めていくのは難しそうな感覚はある。
(何か小さい数xを-2進数表現するときに、それが 2n - 2m (n != m; n%2==0; m%2==1 )のような表現で表すべき数であるなら何桁になるかすぐにはわからないように思う。 何桁になるかわからないということはそもそも最初、2nのnをどこから始めるのだろう。 適当に大きな数から開始して'0’は除く、とかでも良いのだろうけど。 )


下から決めるのは確かにうまくいくのだけどうまくいかないような気もする。
数nの余りを使ってnを調整?していく(nに足すか引く)ので、その後の処理で余りにはその値は含まれていない。
例えば、n%2の余りを使ってnを調整したら、n%4の値はn%2==0を満たす。
つまり n%pow(2,x) の余りrで調整したら、n%pow(2,x+1)の値はn%pow(2,x)==0を満たし、rを含まない。
これを繰り返すといずれはnは0になる。(そうなんだけどあまりしっくりしない)


調整の方向は、今bitのi桁目を決めているかで決められる。

  • i % 2==0のとき
    • nには正の数として加算されている
    • したがって調整時には減算
  • i % 2 == 1
    • nには負の数として加算されている
    • したがって調整時には加算

Unityのtutorials 2 ( John Lemon's Haunted Jaunt: 3D Beginner)

Tutorial

Player Character: Part 2

1. The Player Character Continued

  • MonoBehavioursの継承はGameObjectにScriptをComponentとして取り付けるのに必要
  • ScriptはAssetの1つとして作られる(Prefabと似てる)
  • GameObjectにComponentとしてScriptを追加することでscriptのinstance化する ( SceneにPrefabを追加するのと同様)
  • (要は作っただけでは意味はなくて、どこかに所属させて初めて有効ですよ、ということ?)

    2. Create your First Script (PlayerMovement)

  • class nameとScript file name は一致させないといけない

  • Create a variable to store a reference to the Animator componentまで1.5時間くらい?英文はやはり時間がかかるなぁ。

Unityのtutorials:( John Lemon's Haunted Jaunt: 3D Beginner)

 ゲーム作るとしたら色々作ってみたいものはあるけど、 3Dだったら隠れながら目的を達成していくような、いわゆるステルスゲームが良いなぁなんて思っていたのでこれはタイミングが良いかもしれない。 ただ現時点では日本語訳がないようだ。

そんなわけでメモ代わりに色々書く。tutorialは10回に分けられている。
数日以内に終われば良い方だろうか。

learn.unity.com



John Lemon's Haunted Jaunt: 3D Beginner

 [雑訳]

 このプロジェクトではステルスゲームの作り方を知るだけではなく、10回に分けたtutorialのそれぞれで背後にある原理を説明する。 やったことがなくても心配はない。このプロジェクトはUnityの出発地点に最適だ。


重要そうなところだけ。 不安なところはGoogle翻訳
雑な訳なのである程度は見逃してほしいが明らかな誤りがあれば教えてほしい。

Tutorial : The Player Character: Part 1

2. Add a Character Model

  • 3DのモデルをUnityで扱うためにGameObjectとして追加します。(これをモデルのinstanceを作成すると言います。)
    Transform componentはScene内で位置と大きさを持つことを意味します。

  • Mesh Renderer キャラクターの可視化に必要。

  • Meshは三角形の集合。
  • Skinned Mesh RendererはMesh Rendererの一種でモデルのボーンの回転や位置でmeshが変形できる。
  • ボーンはモデルのGameObjectの子オブジェクト。(JohnLemonのRootがボーンの集合)



3. What are Prefabs?

  • Prefabは特別なAssetでComponentが付属したsetup済みのGameObjectの集合。
  • 同じもののinstanceを簡単に作れる。
  • 作られたinstanceそれぞれはPrefab Assetに紐づけられるため、Assetに変更があればすべてのSceneのPrefabに変更が加わる。
  • PrefabであることはHierarchy windowで名前とアイコンが青で表示されることからわかる。
  • だが、それに加えて白い紙のようなマークがアイコンにあることがある。
  • これは読み取り専用のPrefabで、modelなどをUnityではそう扱う。



4. Turn the Character into a Prefab

  • ?PrefabをOriginalにするかVariantにするかの違いは? (多分Originalを変更するとVariantも影響を受けるとかなんじゃないかと思受けど要調査。)

  • (変更前にSceneを保存!)

  • Prefabを変更するならPrefabをPrefab modeで開く。(Inspector->Open Prefab のこと)

  • (breadcrumb(パンくず)は通ってきた道筋、履歴くらいの意味?)

  • SceneではGameObjectを編集して、Prefab modeではPrefabを編集する、この切り替えに慣れることがUnityの利用では重要。



5.Animate your character

  • Animator componentは第一propertyにAssetの一種、Animetor Controllerを取る。
  • Animetor ControllerはいつでもAnimator componentが何のAnimationをするか決定する state machine を含んでいる。
  • AnimationはAnimator Controllerに用意されたAnimation clipsに基づく。



6. Create the Animator Controller

  • Animator Controllerのstate machineはAnimator Parametersの現在の値から(現在の状態なりAnimationなりを?)決定する



8. Create Animator Transitions

  • Has Exit Timeがチェック済みだと一定時間経過後に自動で次の状態へ遷移する



10. Assign the Animator Controller to the JohnLemon Prefab

まで終了するまでおよそ3時間ほど。

(Animetor関連の操作はSpaceShooterで少し触っていたので新規の情報がなかったから戸惑うことがなかった。 個人的にはSceneでGameObjectを使ってPrefabを作っていくのとScriptでGameObjectを管理したり操作することに慣れるのが動機の1つなので、今のところ物足りない。)



11.Make your Character React to Physics

  • GameObjectが物理学に則ってふるまうにはRigidBodyとColliderの2つのComponentがいる。
  • AnimatorのApplyRootMotionがチェック済みだとキャラクタが少しだけ落下してから停止する



12.What is Root Motion?

  • Animationで特定のHierarchyのGameObjectすべてが回転、移動する
  • ほとんどの移動と回転は自分の親GameObjectからの相対的なものになる
  • 親を持たないGameObjectはRootと呼ばれ、その移動(おそらく回転も?)は相対的ではない
  • この移動をRoot Motion という
  • 特に今回の場合では構造的にRootにあるものではなくAnimator ComponentをもつものがRoot

  • (確かにGameObject Xの下にAnimator ComponentをもつGameObject Aが複数あったとき(As, それぞれはA0, A1... )、A0の動きはA1とは独立だろうからRoot Motionを共有しないのはその通りだろう。ただし初期位置だとかはXのTransformからの相対値になるのかな?)

  • Apply Root Motion をチェックするとRootの動きのAnimationはフレームごとに適用される
  • したがってAnimatorはIdle状態で移動しない
  • ではなぜ落下が起きたのだろう?
  • これはAnimatorのUpdate Modeによるものだ



13. What is the Update loop?

  • スクリーンに表示する画像を一秒間に何度も変更することで移動を表現する
  • この画像それぞれをFrameと呼ぶ
  • Frameをスクリーンに表示する処理をRenderingと呼ぶ
  • 映画やテレビとは異なり、次に移すべき画像はユーザーの入力によって大きく変わるだろう
  • その素早い対応はUpdate Loopで行う
  • Update Loopによる更新は間隔が一定ではない。
  • Custom ComponentはUpdate methodを持っていて、それが呼ばれて新しい画像がRenderされる
  • それとは別にもう1つ、物理的な操作を行うループがある
  • これはFixedUpdateといい、後進の間隔がばらつかない。
  • したがってAnimatorがUpdateでキャラクタを動かし、同時にRigidBodyもキャラクタを動かしてしまう(?)



14. Fix John Lemon's Movement

  • AnimatorのUpdate ModeをAnimate Physicsに切り替えると落下は起こらなくなる
  • (つまり作る側がそのAnimationは物理的なものなのかどうかを判断してきめるということか?)
  • Use Gravityのチェックを外すと上か下から押されたときに落ちたり浮いたりするのでそれでいいのか考える
  • 代わりにRigidBodyのConstraitsでキャラクタの移動に物理学的な制約を課すことを考えよう



17. Add a Collider to John Lemon

  • Colliderはいくつかの異なるComponentの総称
  • Colliderは物理的な衝突の目的でオブジェクトの形状を定義する
  • (ぶつかったりぶつかられたり)

ここまで#10の続きから1時間くらいなので計4時間か。 1-18までで30分らしいのでなかなか厳しい。

UnityのTutorial(2)

3DのtutorialでAssetStoreからのimport直後にエラーが出る。 chinemachineのdependencyが解決できませんといくつかのエラー。 package managerからchinemachineをremoveする方法もあるらしい?けれど私のケースではそもそもツールバー->windowから選択できなかった。 スタイルシートというものでそのあたりを制御しているらしいのだけど、そのスタイルシートの生成なり対応付けもうまくいってないのだろう。

Unity 2018.3.0f2で確認 Unity 20193.0.a.2にupdate後にtutorial projectを開始するとエラーは出ない。
(開くときにupdateしちゃうよ?だったか確認のダイアログは出る。)

John Lemon’s Haunted Jauntでも The Explore : 3D Game Kitでも同様のエラーが出るのでおそらく同様に解決できるのかな。 とりあえずJohnのほうではエラーは消えるのは確認した。 Latest 2018 Unity and 3d Game Kit error - Unity Answers

Johnは2019に追加されたらしいのでUnity2018ではそもそもDLできないのも仮にできたとしても適切に動作しないのは仕方ないとは思う。 でもThe Explore : 3D Game Kitって前からあったような気がするんだよなぁ。

途中でJohnで色々解決策を試すことに切り替えたため多少マシだったが、3D Game Kitはとても大きいので何度も展開したり削除するのは辛かった。

C++のthread

 非同期処理は前々から手を付けないとなぁと思っていたのでつけ始めた。
多分競プロとかには使えない。
できたコードはこれ
なるほど。容易に自身の足を打ち抜いてしまうなぁ。

 確かに処理時間は体感でも早くなると感じたし、処理内容によっては並列化したコードに書き換えるコストを上回るほど早くなることもあるんだろうと思う。

 非同期処理をやってみるにあたって、知らなければ多いことは多い。
最低限のルールはスレッドセーフという幻想と現実 - yohhoyの日記(別館)がわかりやすいように思う。



 比較的シンプルそうなthreadから触る。
threadごとに並列に処理が進むことが期待されるため高速化できるかもしれない。
かもしれないというのは以下の理由。

なお、複数のスレッドが真に並行実行されるか否かは処理系に依存する。(たとえばDual Core環境で4つのスレッドを作成することはできるが、ハードウェアの制約から同時処理されるのは2スレッド以下に制限される。)thread - cpprefjp C++日本語リファレンス


 極端な例(並列処理ができない環境)ではいくらたくさんのthreadに分割したとしても以下と同様になってしまうのではなかろうか。 (処理を分割したそばから終わるのを待つので分割する意味がない。)

std::vector<std::thread> vt; 
for(auto&e:vt){
  vt.emplace_back([](){/*...*/});
  vt.join();
}



 ちなみに std::thread::hardware_concurrency関数でサポートされるthread並行数を取得することができるかもしれない(0が返ることもあるらしい)。



 いろいろ試しているうちに未定義動作を踏んだ。
ある程度原因を絞り込めたもののtwitterでコメントいただけなかったらかなり厳しかったと思う。
エラーの該当箇所は下。(インデントはリンク先と違う)

try{
    std::vector<std::thread> vt;

    for (ui i{}; i < size; ++i) {
        vt.emplace_back(
            [&]() { v.at(i) = fib(ull{ 35 } +i); }//!!!
        );
    }

    for (auto& e : vt)
        e.join();

}catch (const std::exception & e) {
    std::cerr << e.what() << "\n";
}



 複数のthreadすべてで重い処理をして結果をv[i] (vはvector)に記録している。
これ自体は以下に該当するので、データ競合によるエラーを引き起こさないはず。

(1') 異なる変数に対する同時アクセスは、データ競合とはなりません。
(または、異なるコンテナ要素オブジェクトへのアクセスであれば、同時に変更操作を行ってもデータ競合とはなりません。)
スレッドセーフという幻想と現実 - yohhoyの日記(別館)



  原因はラムダ式がキャプチャする変数iの寿命がforのスコープ外で終わっているので未定義動作。
 確かにそういわれてみると、飛んでくる例外がvectorのstd::out_of_rangeだったり、その時のiの値がsizeだったり、逆算して考えると少し納得する。
 スコープ外でiが評価されたとき、その値はsizeになっているのは直感的だ(何が起きてもおかしくないという前提のわりには、ちょうど++iによってforを抜ける値になっている)。
 そしてvectorのoperator[]とatでアクセスできる範囲は[0,size-1]なのでi(=size)でアクセスしてしまうとout_of_rangeが飛ぶだろう。

C++で関数を合成する

関数 - ウォークスルー Haskell
Haskellには関数を入れ子にしなくてもいい$演算子がある。 そのまま引用する。

関数適用のときに便利な $ 演算子を紹介しましょう. この演算子は関数適用の区切りに用いられ,カッコの多くなった式を読みやすくします. 次の 2 つの式はそれぞれ同じ意味となります. haskell square (double (2 * 4)) -- 値: 256 square $ double $ 2 * 4 -- 値: 256

C++でも似たようなことができるといいのだが、例えば以下のような時にはうまくいかない。 operator+で関数を合成するときに、fとgの型が明らかになっていないといけないからだ(多分)。

auto g(int){...};
auto g(double){...};
auto h = f + g;//+はうまいこと実装したとする
h(5);

一応以下のような形でgがどんな型なのかを明らかにして手動で多重定義を解決するようなことをすれば動くようだが、そんなことはできればしたくない。

auto h = f + static_cast<decltype(g(std::declval<double>()))(*)(double)>(g);

もう一つの解決方法は、関数gが必要になるタイミングをずらせばいい。 遅延評価と呼ばれるものだ。具体的にはラムダλgでgを包んで、fとλgに+を適用する。

[=](auto&&...args){return g(std::forward<decltype(args)>(args) );}

遅延評価 - Wikipedia

wandbox.org

何かできそうで面白い。

エラー文が悲惨になることが想像できるので、可能なら何らかの形で+が受け取るFとGに制限をかけてしまいたいのだけど、どうやって制限されるべきか否かを区別すればいいのかちょっとわからない。 invokable使うのだろうか。難しいな。