【書評】エンジニアのためのマネジメントキャリアパス ―テックリードからCTOまでマネジメントスキル向上ガイド
読書感想文です。
雑感
メンターから技術戦略担当まで、マネジメントに関わる仕事のノウハウが管理対象の規模に応じて段階ごとに書かれている。 著者の体験に基づいているため事例がイメージしやすく、翻訳が良いこともあってかなり読みやすかった。
グッときたところ
コミュニケーションについて
- 一度で伝わると思わない。分かってくれると思わない。本当に伝えたいことは粘り強く分かるように何度でも伝える。
- 業務外の内容も含め、お互いにコミュニケーションをするのが当たり前な空気感を日頃から醸成していく。
- メンティー相手の場合など、自分が目上の場合は特に自分からコンタクトを心がける。
- 評価を伝える際にはきちんと長所も時間をとって伝える。
チーム作りについて
- ルール化や仕組み化も手段の一つ、こだわりすぎない。何を解決したいのかを常に意識する。
- コードレビューや技術選定の基準などを明確に言語化することで、チームでのやりとりや将来の引き継ぎもやりやすくなる。
- チームが見積もりや開発をできるような土台は自ら作り上げる。
- 部下に対して定期的継続的にフィードバックを行う。それを実現するために常に部下を観察し長所や短所を理解する。
リーダーの仕事について
EOF2019 に参加してきた
こんにちは、じぬ(@reximology)です。
EOF 2019 というイベントに参加してきました。これはエンジニアリングマネジメントを主題としたカンファレンスです。 eof.connpass.com
ここでは参加したセッションと、特に印象深かった点をまとめます。
はじめに
参加したセッションは次の通りです。(タイムスケジュール)
- Opening Session 私たちの現在地点、向かうべきところ。
- 正しいものを正しくともにつくる
- 筋肉質なエンジニア組織を目指して ~失敗と成功から学ぶエンジニア組織の作り方~
- 社員100人規模のWebサービスにおけるエンジニアリングマネジメント
- エンジニア採用どうしてる? 〜エンジニアのトップが語る、2019年の採用活動の本音〜
- 持続可能なエンジニア組織のデザインに必要な事とは
- 学習する組織の作り方
- DMM改革の1年、その実際と反省
Opening Session 私たちの現在地点、向かうべきところ。
本イベントの概要説明などです。カンファレンスの価値は廊下にある(セッション後に参加者同士でコミュニケーションがとれる場があること)という話をしてました。そこに繋がる流れで隣の人となんかしゃべってくださいはびっくりしましたが、どうにか自己紹介して交流できました。
正しいものを正しくともにつくる
カイゼン・ジャーニーの著者である市谷 聡啓さんのセッション。
www.slideshare.net
https://www.amazon.co.jp/dp/B078HZKLMBwww.amazon.co.jp
要約
- 不確実性の多い序盤は選択肢を広くとり、仮説検証を繰り返してモデル化することで選択肢を狭めていく
- 検証をすべて PO に任せるのは間違い、チーム全体(の各個人)で取り組むべき
- 仮説検証に応じた思考を可視化し、みんなで意見を言い合える環境にする
- 検証に応じて可変性のある、スプリントと機能完成の中間となるマイル(ジャーニー)の提案
グッときたところ
- 不確実性があるのは当然、それへの対処を明確に言語化されているのは非常に参考になる
- スプリントという決まった大きさの物を仮説検証に合わせて柔軟に対応するため、ジャーニーという新しい単位の提案
筋肉質なエンジニア組織を目指して ~失敗と成功から学ぶエンジニア組織の作り方~
要約
- 表題の通り、自らの失敗を交えた取り組みの紹介
- Cheif EM になったが、現場エンジニアのマインドのままだったことが失敗の大きな原因
- 変化を恐れず、これまでの積み上げに頼らず、常に自分をアップデートするという覚悟
- 部長との対話を増やし、事業部の要望に応えつつ組織の成長を担うのが EM の仕事
グッときたところ
- 1on1 にてもうあとがないですよ、と言われてしまうほどに自分の強烈な失敗の話をしてくれたのは衝撃だった
- エンジニアと EM ではマインドから違うという点、それを認識してきちんと自身を対応させたのは見習うべき
- 組織はリーダーの器以上にはならない、だからこそ自分を伸ばす必要がある、という金言
社員100人規模のWebサービスにおけるエンジニアリングマネジメント
要約
- スタートアップ初期、10人〜30人、それ以上、というフェーズごとに CTO として何をしてきたのか
- スピード優先で開発してきたことによる負債や、人数増加によって手が回らなくなる、など正に成長する企業がぶつかる壁の話
- 30人を超えると CEO から権限を委譲させた、スケールのために自分も CTO を交代した
- マネージメントとはいろんな課題を整理してなんとかする、そもそもが曖昧な仕事である
- 他人の給料が上げられるのならその人は配分をもらう資格がある、というのがマネージャーの給料が高いことに対する解釈
グッときたところ
- 情シスの設置や企業の社会的責任など、企業が大きくなるにつれて現れる課題の話は新鮮だった
- マネージャー育成の話は、自分がいま読んでいる本と通じる箇所も多く参考になった
- 技術力が高いとは限らないマネージャーの価値とは何か、が給料面の解釈含めてきちんと言語化されていた
エンジニア採用どうしてる? 〜エンジニアのトップが語る、2019年の採用活動の本音〜
要約
- Classi, Repro という二社での採用に繋がる取り組みの話
- 優れたエンジニアは面白い課題を求める、会社としてそれを提供するためには数年先を見据えた取り組みをきちんと検討しておくことが必要
- 採用に関してはとにかくタッチポイントを増やしている、社内イベントに呼んだり技術相談の場を作ってみたり
- 採用面接やリファラルに向けて社内への取り組みも重要、面接に出られるようにするための研修をする会社もある。Repro では毎週役員レベルの会議に関して議事録公開や質問受付の時間を設けている
- 採用がうまくいかなかった場合、行った会社の人とコンタクトできるなら答え合わせ(なぜ採用できなかったか)をすることもある
グッときたところ
- 自身もリファラルを考えたときにいまいちうまくイメージできなかった経験がある。なのでそれに向けて社内への呼びかけや情報伝達を行う取り組みは良いと感じた。
- カンファレンスのスポンサーになるなど、採用に対して効果はあるが測定できない、そのため予算を確定しにくい、というのはたしかにと思った。
持続可能なエンジニア組織のデザインに必要な事とは
要約
- 創業から5年間の取り組み(エンジニアが0人から40人になるまで)
- 初期は知名度皆無、なのでエージェントを訪問してその場で一緒に書類審査するなど、泥臭く信頼関係を構築していった
- 運営を引き継ぐという事業の特性上、開発者の暗黙知は消滅を避けられない
- どんなに人手不足でも新規入場者をすぐ高負荷なチームには入れることはしないという決断
- ビジネスが安定してくることによるチャレンジの不足、それによるシニア技術者の不満
グッときたところ
- エージェントとの信頼関係、と言う観点
- チャレンジ不足と言う不満は現場からは「でも事業的に仕方が無いですよね」、という形で現れていた
- 実際に不満があるかどうかよりも、事業が成功した結果チャレンジが不要な物であると捉えられていることこそが大きな問題
- 成長した先には光があるべき、シニアが憧れを受ける組織であるべき
- 事業貢献と技術的チャレンジは双方が必須、前者がなければそもそも価値を満たせないが、後者がなければエンジニアの時間を切り売りしているだけ
学習する組織の作り方
要約
- フロント技術だけで見ても学習するべきことはどんどん増えている、時間は無限に必要
- 書籍「学習する組織」から学んだ五つの観点:自己マスタリー、共有ビジョン、システム思考、メンタルモデル、チーム学習
- 毎週作った物を見せ合う場を用意し、アウトプットとそのためのインプットの仕組み化
- 社内 ISUCON やハッカソンによる体感を通した学習
- 自分がエンジニアコミュニティで経験してきたことを社内で再現した
グッときたところ
- 製品版にはセキュリティなど様々な制約が必要
- それを外した状態でどこまで速度が出るのか、どんな品質で動くのか、という体感を通して理想を得る
- 自身がエンジニアコミュニティで得た体験を社内で再現するという観点は新鮮だった
DMM改革の1年、その実際と反省
要約
- エンジニア/デザイナーだけで1000人近い組織をテックカンパニー化することが与えられたミッション
- マネジメント観点以外にも様々な視点(売り上げを元に並べてみたりやサークルやチャット上の交流など横のつながり)で組織構造を眺め、その結果からまんべんなくヒアリングを実施して現状を把握した
- 課題、その課題に対する現場がどんなフィルターを通して解釈しているか、そこから逆算して現場の状況や目的はどうか、を理解していく
- ミッションやビジョンを通して現場における行動までを説明できる一貫したストーリーを整える、それが企業における文化
- 情報公開の場所やタイミングを統一してアクセスを容易にし、自分からの発信とインタビューなど外部を通した発信を重ねることで情報に浸透圧をかけていく
グッときたところ
- 正しく組織をハックするといった内容
- 様々な視点を通した組織の俯瞰、偏りのないヒアリング対象の選定
- トップにあるミッションからそれを具現化していく戦術までの一貫したストーリー
- 自分からの発信をどうやって現場に浸透させるかまでの具体的な見通し
- ヒアリングを通して見えた課題の解消をパッケージ化し、少しずつ現場に浸透させてその成功を認めることで各メンバーに理想的振る舞いのメンタルモデルができていく、それこそが企業の文化
- 何気ない会話の中にミッションが混ざる、例えば slack のスタンプにそれが垣間見える、それこそが文化の浸透
振り返り
イベントに関して
弊社も含めてやはり課題は似たようなものが多いんだなと言う印象です。組織規模の拡大によって無視できなくなる技術負債、エンジニア採用、継続的なチャレンジ課題、文化醸成、などなど。 マネジメントやチーム作りは新卒やよほどの技術スペシャリストでなければ一度はぶつかる課題かと思うので、エンジニアならたいていの人にとって身近な話題だと思います。 それだけに講演を聴くだけでもいろいろと学んだ気になることが多いです。ですがそこでとどめず、自分の振る舞いや自分のチームにおいてどう活かすか、まで踏み込めると正しく血肉にできるのかな、と感じます(自戒)。
その他の講演
聞けなかった講演の資料をまとめようと思ってましたが、こちらでまとめてくださってたので貼っておきます。 medium.com
Unity 内部のコンパイラを追ってみた
こんにちは、じぬ(@reximology)です。
これは Unity ゆるふわサマーアドベントカレンダー 2019 #ゆるふわアドカレ 24 日目の記事です。
この記事では、Unity 用 SDK 開発において遭遇したトラブルと、その解決に至るまでを紹介します。 ※ 本記事は調査手順をなぞりたいと思い時系列で記述していますが、結論だけ知りたい方は解決をご覧ください。
背景
自分は AROW というサービスの開発を行っており、その一部として Unity 用 SDK を提供しています。 SDK は、開発者登録を行うだけでなんと無料で使うことができます(これは宣伝)。
さて、SDK は提供時に DLL 化を行っています。今回紹介するのは、DLL に関連する話です。
開発環境は次のとおりです。(両刀使いのため時々 Windows が出てきますがメインは Mac です)
- macOS HighSierra 10.13.6
- Unity 2017.4.25
- .NET 4.6
DLL の作り方
GUI から作ることもできますが、自動化したかったので CLI で作成することを考えます。
ドキュメント を確認し、mcs
というコマンドを使うことを確認しました。
(当時どのようにインストールしたかは不明ですが、おそらく brew install mono
で環境を構築したかと思います)
実際に DLL を作成するには、以下のようなコマンドを実行しています。(一部省略)
mcs \ -r:$(UnityDirectory)/Contents/Managed/UnityEngine.dll \ -target:library \ -recurse:'Assets/Scripts/*.cs' \ -out:AROW.dll
この場合だと UnityEditor への参照は含まれていないため、Editor 用が必要な場合はもう一つ DLL を作成する必要があります。
問題
エラーとその調査
string str = "abc.def"; string[] ary = str.Split('.'); // ここでエラー!
問題になったのは、string.Split
です。これは引数の文字で文字列を区切り、配列で返すメソッドです。
開発中、つまり Unity で検証している際にエラーはありませんでした。
しかし、上記処理を含むファイルを DLL 内部に含むと、次のエラーが発生するようになりました。
MissingMethodException: Method 'string.Split' not found.
正直、このエラーだけではなにが問題なのか判断できません。 同じ処理を Unity 2018 で実行した場合、もう少し詳細なエラーが確認できます。
MissingMethodException: string[] string.Split(char,System.StringSplitOptions)
コールしたいのは char
を一つ受け取るメソッドですが、エラーが出ているのは別にオーバーロードされたメソッドのようです。
まず、このメソッドについてドキュメントを確認します。
使おうとしているのはこちらです。
params char[]
と宣言されているため、引数が char
一つでも該当します。
逆に、エラーが示すような引数のメソッドは定義されていません。
試しに、リフレクションでメソッドを出力してみます。
// string の関数を出力 var s = ""; foreach (var v in typeof(string).GetMethods(BindingFlags.Public | BindingFlags.Instance)) { s += v.ToString(); s += "\n"; } Console.WriteLine(s);
// Unity での実行結果 ... System.String[] Split(Char[]) System.String[] Split(Char[], Int32) System.String[] Split(Char[], System.StringSplitOptions) System.String[] Split(Char[], Int32, System.StringSplitOptions) System.String[] Split(System.String[], System.StringSplitOptions) System.String[] Split(System.String[], Int32, System.StringSplitOptions) ...
やはり、エラーとなっている char, System.StringSplitOptions
を引数にとる string.Split
は見当たりません。
コンパイラ
ところが改めてドキュメントを見直すと、.NET Core にはこのメソッドが存在しています。
しかも第二引数にデフォルト値が宣言されているため、char
一つを受け取る場合は本来呼び出したかったものと見分けがつかないはずです。
つまり、「自分たちとしては params char[]
のメソッドを呼び出したかったが、コンパイルされる時点では char, System.StringSplitOptions
のメソッドに対して紐付けが行われた。
ただし実行時にはそのメソッドは存在しないためエラーが発生している。」と推測できます。
開発中も DLL 利用時も、実行しているのは Unity であるため共通のはずです。
となると、DLL を作成した際に利用した mcs
に原因があると考えられます。
普段の Unity が利用しているコンパイラはどこにあるのでしょうか。
Unity 内で mcs に関する検索を行うと、次の場所でそれらしきファイルが発見できます。
bin には mcs、lib には mcs.exe が存在します。
// Mac /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/mcs // Windows だと少しパスが異なる /c/Program\ Files/Unity/Hub/Editor/2017.4.25f1/Editor/Data/MonoBleedingEdge/bin/mcs
おそらくこれなんだろうなと思いつつ、Unity から利用されている確証がほしいところです。 そこでこんな実験をします。
Unity が使っているとおぼしきファイルをリネームしました。 後述しますが、ファイル名を変えたのは exe ファイルです。 参照するファイルがなくなっていれば多分エラーになるでしょう。 戻せば回復するでしょう。多分きっと。 ※ 元に戻せるようきちんと確認してから行ってください
ApplicationException: Unable to find csharp compiler in /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/lib/mono/4.5 UnityEditor.Scripting.Compilers.MonoCSharpCompiler.GetCompilerPath (System.Collections.Generic.List`1[T] arguments) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs:88) UnityEditor.Scripting.Compilers.MonoCSharpCompiler.StartCompiler () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs:59) UnityEditor.Scripting.Compilers.ScriptCompilerBase.BeginCompiling () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs:43) UnityEditor.Scripting.ScriptCompilation.CompilationTask.QueuePendingAssemblies () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:191) UnityEditor.Scripting.ScriptCompilation.CompilationTask.Poll () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:137) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.ScriptAssemblySettings scriptAssemblySettings, System.String tempBuildDirectory, UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.Scripting.ScriptCompilation.EditorBuildRules+TargetAssembly[]& notCompiledTargetAssemblies) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:749) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:627) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.TickCompilationPipeline (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:896) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface+<TickCompilationPipeline>c__AnonStorey3.<>m__0 () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:219) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.EmitExceptionAsError[T] (System.Func`1[TResult] func, T returnValue) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:75) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface:TickCompilationPipeline(EditorScriptCompilationOptions, BuildTargetGroup, BuildTarget)
やはりエラーになりました。ということは、Unity が利用しているコンパイラはこの mcs
で間違いないでしょう。
細かい話をすると、バイナリファイル(bin/mcs)をリネームしてもエラーにはなりません。エラーになるのは実行ファイル(lib/.../mcs.exe)を対象にしたときです。
しかし mcs.exe をリネームすると、bin/mcs を直接実行した場合にもエラーが出ます。
$ /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/mcs --version Cannot open assembly '/Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/../lib/mono/4.5/mcs.exe': No such file or directory.
この挙動から、bin/mcs が内部で mcs.exe を利用していると判断できます。
そのため、DLL 化する際にはこの mcs
を使うことで Unity 開発時と同じ環境のコンパイルができるはずです。
通常の mcs
と Unity 内部の mcs
を比較すると、次のような差が確認できます。
$ mcs --version Mono C# compiler version 6.0.0.0 $ /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/mcs --version Mono C# compiler version 5.0.1.0
ドキュメントにあった .NET の差分から推察すると、新しい方の mcs
では .NET Core の実装を参照していると考えられます。
(この点については未検証です)
解決
Mcs := $(UnityDirectory)/Contents/MonoBleedingEdge/bin/mcs make_dll: $(Mcs) \ -r:$(UnityDirectory)/Contents/Managed/UnityEngine.dll \ -target:library \ -recurse:'Assets/Scripts/*.cs' \ -out:AROW.dll
先ほど述べたとおり、Unity 内部の mcs
を利用することで今回の問題は解決できました。
開発環境の精査を怠ったことが原因となったトラブルでしたが、コンパイラについて調査するいい機会となりました。
余談
Unity 2018 の場合
Unity 2018.4 で試したところ、先ほどの mcs.exe をリネームしてもエラーが起きませんでした。 おそらく原因は、こちらにあるように Unity 2018.3 以降の .NET 4.x 系統では Roslyn が使われていることかと思われます。
Roslyn は csc
を使っています。
そのため、次の csc(.exe) をリネームすると同様にエラーが発生します。
// Mac /Applications/Unity/Hub/Editor/2018.4.0f1/Unity.app/Contents/Tools/Roslyn/csc // Windows C:\Program Files\Unity\Hub\Editor\2018.4.2f1\Editor\Data\Tools\Roslyn\csc.exe
Exception: 'C:\Program Files\Unity\Hub\Editor\2018.4.2f1\Editor\Data\Tools\Roslyn\csc.exe' not found. Is your Unity installation corrupted? UnityEditor.Scripting.Compilers.MicrosoftCSharpCompiler.ThrowCompilerNotFoundException (System.String path) (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs:65) UnityEditor.Scripting.Compilers.MicrosoftCSharpCompiler.StartCompilerImpl (System.Collections.Generic.List`1[T] arguments, System.String argsPrefix) (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs:112) UnityEditor.Scripting.Compilers.MicrosoftCSharpCompiler.StartCompiler () (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs:220) UnityEditor.Scripting.Compilers.ScriptCompilerBase.BeginCompiling () (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs:60) UnityEditor.Scripting.ScriptCompilation.CompilationTask.QueuePendingAssemblies () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:228) UnityEditor.Scripting.ScriptCompilation.CompilationTask.Poll () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:135) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScriptAssemblies (UnityEditor.Scripting.ScriptCompilation.ScriptAssembly[] scriptAssemblies, UnityEditor.Scripting.ScriptCompilation.ScriptAssemblySettings scriptAssemblySettings, System.String tempBuildDirectory, UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.Scripting.ScriptCompilation.CompilationTaskOptions compilationTaskOptions, UnityEditor.Scripting.ScriptCompilation.EditorCompilation+CompileScriptAssembliesOptions compileScriptAssembliesOptions) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:1149) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.ScriptAssemblySettings scriptAssemblySettings, System.String tempBuildDirectory, UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.Scripting.ScriptCompilation.EditorBuildRules+TargetAssembly[]& notCompiledTargetAssemblies, System.String[]& notCompiledScripts) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:1047) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:939) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.TickCompilationPipeline (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:1377) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface+<TickCompilationPipeline>c__AnonStorey5.<>m__0 () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:331) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.EmitExceptionAsError[T] (System.Func`1[TResult] func, T returnValue) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:97) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface:TickCompilationPipeline(EditorScriptCompilationOptions, BuildTargetGroup, BuildTarget)
Visual Studio の場合
Visual Studio で DLL を作成した場合は今回の問題は起きませんでした。 実際に実行されているコマンドは次のものです。
/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/msbuild/Current/bin/Roslyn/csc.exe /noconfig /nowarn:1701,1702,2008 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG /errorendlocation /preferreduilang:ja-JP /highentropyva+ /reference:/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/4.7-api/mscorlib.dll /reference:/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/4.7-api/System.Core.dll /reference:/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/4.7-api/System.dll /debug+ /debug:portable /optimize- /out:obj/Debug/Temp.dll /subsystemversion:6.00 /target:library /utf8output MyClass.cs Properties/AssemblyInfo.cs "/var/folders/j2/xntwf0xj7311rg06_2p570d00000gp/T/.NETFramework,Version=v4.7.AssemblyAttributes.cs"
試したところ、mscorlib
を指定することで本現象が解決できるようです。
ただし mcs
で試した場合は System
などで多重定義のエラーが起きるため、noconfig
などのオプションが必要になります。
csc
に乗り換えるリスクや、実行コマンドが複雑になることを避けるため、今回の問題解決としてはこれらの手段は採用しませんでした。
GitHub の Wiki で子ページを一覧化する
※ やりたいことはまだ途中なんですが、キリがよくなったのでいったんまとめています
やりたいこと
GitHub で Wiki を書くと、サイドバーに積み上がっていきます。 これは増えすぎると隠れてしまうので、一覧で表示されればいいなと思います。
やったこと
Wiki はそれ自体もリポジトリを clone することが可能です。 Wiki ページの右下で URL を確認できます。
今回の場合だとこんな感じです。
https://github.com/ryuj/Unity.wiki.git
確認したところ、生成したページが .md ファイルとして存在します。 トップページは Home.md です。 なので、以下のように対応を考えます。
- Home.md 以外の .md ファイルをリンクのフォーマットで出力する
- 出力結果をリストにして Home.md へ上書きする
上記に対応したスクリプトは以下です。 これを実行すれば、Home.md に子ページの一覧を作成できます。 (Ruby は思いつくまま書ける感じが好きですが、Ruby ぽく書けているのかは未知数)
HOME_FILE = "Home.md" def main html = "" Dir.glob("*.md").each do |file| next if file == HOME_FILE base = File.basename(file, ".md") s = "* [#{base}](./#{base})\n" html << s end File.open(HOME_FILE, "w") { |io| io.puts html } end main
できていないこと
現状、スクリプトを手動で実行しなければ一覧化できません。 Wiki はブラウザ等で編集することが多いと思います。 なので、ブラウザ等で編集された際に上記スクリプトを動かして Home.md を更新する、というのがベストです。
git log で確認したところ、ブラウザからの編集もコミットログで残っているため、おそらく普通に push を検知できれば行けそうです。 未確認ですが、このあたりはおそらく webhook で対応できるのでは、と予想してます。(あとでやる、つもり、、、)
「Unity Designer's Cafe #2」に参加しました
本日は、こちらの勉強会に参加しました。
unity-designers-cafe.connpass.com
しばらく勉強会は行ってなかったので、よい刺激になりました。(当日朝まで補欠だったんで不参加のつもりでしたが)
本日の目的
本イベントはデザイナーの方が中心のイベントです。自分はエンジニアですが、以下の目的で参加しました。
- シェーダーなどの見た目系 Tips 聞きたい
- デザイナー観点での困ったことや解決事例を聞きたい
一つ目はまぁ誰しも聞きたい内容だと思うのですが、今回は特に二つ目に興味がありました。自分は以前のチームでもデザイナー向けのツールを作った経験があり、以下の実感がありました。
- エンジニアの力で他の職掌の課題解決ができること
- ただしそのためにはきちんとワークフローを理解するのが重要なこと
- 課題には本人たちも気づいていないようなものも含まれること
そのため、どんな課題があるか、またそのあぶり出しにどんな取り組みが行われているのか、という点に興味がありました。
本日の感想
上記の目的から言うと、@LandyNagata さんの「プログラマに相談したらデザイナーの仕事が楽になった話」が一番参考になりました。(もちろん他の発表も非常に面白かったです)
ここでは、実際にエンジニアが作った事例が紹介されていました。
- ImportSettings の自動設定
- RGBA16bit ディザ加工
- スプライト指定で使用している prefab リストアップ
- オブジェクト透過キャプチャ
- アセットバンドルとマスターが絡むオブジェクトのビューア
特に5番のビューアは、プランナーのデータ設定時やエンジニアのデバッグ時にも役立ちそうだなと感じました。また、ツールで解決すべき単調な作業を「お刺身たんぽぽ」と表していたのも印象的です。(エンジニア観点で言えば、名前をつけるのは課題解決への第一歩だと思います)
懇親会でもそのあたりのお話をさせていただき、各社似たような課題を感じているのは実感できました。改善の取り組みとしては、以下のようなものが挙がっていました。
- 専用チャットなどやり取りしやすい場を用意する
- 席を近くして日常的に相談しやすくする
- 困ったらあの人に聞こう、というような人をたてる
- 振り返り会で積極的に課題を挙げ、みんなで解決を意識する
弊社でも似たような取り組みを行っているため、少なくとも間違ってはいなそうだという実感が得られたことや、自分たちの取り組みから得られた知見もぜひ発表してみたいなと感じられたのは、非常に良い収穫だったと思います。
スマホゲームの UI について思うこと
ここで書いているのは、特にアウトゲーム(遊び以外の部分・画面)の UI について思うことです。
主題
ネイティブアプリに比べると、スマホゲームの UI 周りは、スマホに特化させた操作性をあまり導入していない印象がある。
背景
自分はもともとネイティブアプリのエンジニアをしており、途中からゲームアプリ開発へ切り替わった。その中で、主題のようなことをちょこちょこと感じる機会がある。
例えば Facebook の場合、写真を閉じる UI として上下にスワイプするというのがある。
Facebook は上下にスクロールするのが基本操作であるため、写真を閉じてそのまま次の操作へシームレスにつながるというのが非常に心地よい操作であると思う。
他にもメールアプリでは、左にスワイプすると削除ボタンが表示され、そのままスワイプを振り切ることで削除を確定することができる。これなら一度指を離してから決定ボタンをタップする、といった二度手間がない。
Kyash というアプリでは、送金の際の操作を自分の前方にスワイプすることで確定するため、自分から送り出す、という行為を直感的な操作で実現していると思う。
翻ってゲームの操作を考えると、こういった操作性はあまり見かけないと思う。
キャラクタやアイテムの一覧はスクロール操作、タップや長押しで詳細表示、画像を全画面表示した場合はもう一度タップしたり画面端に表示されたバツボタンで閉じる。
もちろん、他でもよく見る UI で統一することでユーザの学習コストを下げ、安心して操作してもらえるという利点はある。必ずしも凝った操作性が優れているわけではないため、現状に問題があるとも思わない。
仮説
とは言え、ゲームアプリではなぜ凝った UI の導入がないのか、という点が自分の中ではずっと気になっている。そこで、自分なりに考えをまとめてみたい。
① 既存のゲームが強烈な見本として存在しているため
スマートフォンアプリは、これまでにない存在だったと感じる。そもそもスマートフォンというそれまで無かったデバイスと密接に関わる存在であり、マウスやキーボードを通さず直接操作するという新しい体感をもたらした。
そのためスマートフォンアプリの開発では、PC アプリケーションなど既存の知見に習わず、実在の体感に如何に近づけるか、といった志向で UIUX の開発が行われてきた。
一方、スマホゲームはスマートフォンアプリであるとは言え、コンシューマやアーケードのような「既存のゲーム」という楽しさをいかに取り込むか、というところが主眼だったのではないか。既存の操作性は当然ボタン操作などの堅実なものが多く、それに倣ったことが昨今のゲーム UI のベースになったのではないか。
② ゲームというミスが許されない環境下での操作性検討が影響しているため
これは、先日の勉強会でいただいた意見を元にした考えである。
ゲームでは、誤った操作でキャラクタがやられてしまうなど、ユーザにとって当然のようにミスが許されない環境がある。そのため、開発者も絶対に誤解しないような UI、というものを検討すると考えられる。
これは本来インゲーム(遊びの部分)に導入される文脈ではあるが、その考え方を持ってアプリ全体の UI が検討されるため、凝った UI というものがあまり導入されないのではないか。
③ ゲーム開発者にそのあたりに特化したタイプがいないから
これはやや暴論な気がするが、自分が関わってきた中では割と当てはまっている印象はある。ゲームを開発することに強いモチベーションを持つ開発者(エンジニアに限らず)は、「遊び」を作る部分への興味が強い人が多い。そうでなくても、課金導線やユーザのゲームサイクル構築など、もう少し俯瞰的なレイヤーでチカラを発揮するタイプが多い印象がある。
デザイナも、どちらかと言えばキャラクタやアイテムなど、イラスト作成のタイプが多い印象がある。UI 設計、というもの自体に特化した人材がゲーム業界にそこまでモチベーションを持っていない、少なくとも昨今のネイティブアプリやウェブサービス開発に流れている、というのはあるのではないだろうか。
余談
ここまでゲームに凝った UI が一切ないような書き方をしてきたが、そんなことはないとは思っている。
自分が知っている中では、デスティニーチャイルドが当てはまる。
トップ画面に設置された UI は引っ張り出すような操作が可能であり、そのまま次の画面に遷移することが可能である。
他にもドラガリアロストは画面切り替わりが極めてシームレスになっており、操作性は普通でも新しい体感を提供していると感じる。
おわりに
繰り返しになるが、自分は必ずしも凝った UI が良いとは思っていない。ゲームアプリはあくまでインゲームが主役であり、ソシャゲであればそれに加えてユーザが興味を持つのはガチャ画面あたりではないだろうか。
メニュー画面やキャラクタ強化画面などはあくまでサブの位置づけだと思うし、わかりやすさが最優先なのも正しいと思う。それでも、スマートフォンというデバイスに沿った新しい操作性がゲームから生み出されれば、それもまた新しいゲーム性の発明につながるのではないかと思う。
「【TECH×GAME COLLEGE#16】ゲームに学ぶUXデザイン」に参加しました
勉強会に参加してきました。 techxgamecollege.connpass.com
自分が UIUX に興味があることもあり、非常に刺激を受けられてよかったです。
考察
UX が捉える範囲が、サービスを使う前から、サービスを繰り返し使うことによって受ける影響まで含む、というのは考えが及んでいなかった。 他にも、感覚で理解していたもののうまく言語化できていなかった点で気付きが多かった。
- ゲームはサービスとコンテンツが混在していること
- ゲームはあえて苦痛を作る必要性があること
- ゲームならユーザの状況を設計することができること
- 共感を得る、説得力のあるキャラクタの重要性
その他
懇親会の最後で直接質問させていただいたゲームと非ゲームの UI の違いについては、以前から自分が気になっていることでもある。 この点はまた別の機会に考えをまとめてみたいと思う。
聴講中のメモ
UX とは
意:サービスを使って得られる体験
- 使用前、使用中、使用後すべて含まれる
- @使用中
- 知覚と反応のサイクルを繰り返す
- 反応:感情、思考、行動
- @使用後
- 体験全体を内省して記憶に残す(いいね、など
- 誘発された次の行動もこれに含む
- @繰り返し使用後
- 経験、記憶が累積していく
これらすべてが UX に含まれる
- 使用前:価値を期待させる
- 使用中:使いやすさ、心地よさを提供する
- 使用後:次の行動を誘発する(行動のコントロール
- 繰り返し使用後:ブランド価値を高める
ペルソナ・シナリオ・カスタマージャーニーマップ&ユーザ工学
- 要求定義・要件定義のためのもの、ではある
- 本来は使用前から繰り返し使用まですべてを網羅するべきもの
行動経済学/ナッジ
- 行動を誘発する
CX デザイン
- 企業ブランディング
UX デザイナの仕事
- ターゲット決定・インタビュー・観察
- 分析・アンケートで検証
- ペルソナの作成
↑でカスタマージャーニーマップができる
- ペルソナ:個人
- これ:行動
- 状況・行動・心理
- タッチポイント(ユーザとの接点
理想シナリオから要求/要件を定義する
施策・UX 評価
ユーザの協力を得て良否を評価する
- 専門家の判断に安心せず、当事者からの情報を得る
ゲームの特異性
- 楽しさをデザインする
- 苦痛をデザインする
- 状況(ゲーム内ルール)をデザインする
- 状況(キャラクタ)をデザインする
楽しさ
- web にとってはサービスは手段である(楽しいのはコンテンツ
- ゲームはコンテンツと手段が一体化している
楽しさとは厳密には方法論では語れない(語った人はいる
- ロジェ・カイヨワ
- ロビン・ハニッケ
- グリー株式会社
解釈
- ゲームではない娯楽もある
- 使いやすさは楽しさに影響する
- ゲームの雰囲気は楽しさに影響する
事例(ボタン
- web: わかりやすさ優先のシンプル
- game: 雰囲気演出を優先
事例(演出
- twitter: 上部が最新であることを暗に伝える演出
- game: 雰囲気作り(実用的な意味はない演出
ゲーム外でも雰囲気を演出する
- 世界観に沿ったツイートとか PUSH 通知とか
苦痛をデザイン
- web: 使いづらさは排除
- 挑戦する楽しさのために敵キャラや障害物を提供
- 現代:課金させるために苦痛を与える(ゲーム以外でもあり得る)
- カスタマージャーニーを把握して課金を促す
- 課金してでも解決したい気持ちが最大化するタイミング
カスタマージャーニー
- 画面フロー図にプレイヤー心理と施策を追記するとできる
- サブゲーム:時間は有限だからこそそれを消費させる
- ここ自体が楽しいのはインゲームと融合しているからか?
- 他人のコレクションを見せて羨ましがらせるためにソーシャル感がある
ゲーム内ルールをデザインする
- ゲーム内の状況は設計できる
- これは web ではあまりできない
- 手段じゃないからか
- 基本的に状況は行動を縛る
状況の事例
- 弾に当たると死ぬ緊張感(COD
バリアで無敵になれる爽快感(HALO
理由なく嫌われる緊張感(納得できない
- 理由なくモテる爽快感(納得できる
- ゲームのシステムとしてはヒロインの好感度の違いだけ
- 些細なパラメータの違いだけで印象や展開を変える
キャラクタをデザインする
- ユーザの周囲にいる人物も状況の一つ
- ゲームキャラはユーザの周囲にいる人物になりうる
- シンデレラが結婚してハッピーエンドなのは、本人が幸せだと納得したから
- 無理ゲーでもチャレンジするのは、(誰かを助けるというストーリを持った)キャラに共感するから
- 説明文で促すのではなくキャラがユーザに頼む、とかいい例
- キャラのセリフで説明の読み飛ばしを防ぐ
ユーザの共感を得るには?
- 人格があると錯誤させる:aibo とか
- 言動を理解の範囲内に抑える
- ナラティブを共有する
ナラティブ:原意:当事者の語る体験談
- 自分ごととして認識できる押し付け感のないストーリ
- 擬人化も擬人化対象が与えていたナラティブを元ネタにできる手法
Question
ゲームにしかできないことってありそう?
- 無いと思っている。今はできてないだけ。
ナラティブ
- 平たく言えば同人誌が作りやすいかどうか
- キャラの関係性に想像の余地があると作りやすい
懇親会メモ(うろ覚えです
製品開発以外を含めて俯瞰できる UX デザイナーいない?
- そういう人材はいるが、あまりデザイナーと呼ばれてないのでは?
- Apple などは広告含めてユーザと製品の出会いを作るという考え方がある
昔は攻略本などのメディアミックスも盛んだった印象
- 流行ってお互いにやりすぎて廃れた印象もある
ガチャをひくユーザであっても、追加ストーリーいくらで購入、みたいのはヘイト多い印象
- コンシューマゲームではなくソシャゲを中心としているユーザにとっては無料で遊べることが大前提
- これまでと異なる背景をもったユーザが増えてきてるのではないか
最近サブスクリプション増えたね
- 他業種でも増えている
- おそらくビジネスモデルは今でも模索中
そもそもお金周り見れる開発者っているの?
- ゲーム学校の生徒にはあまり見かけない
ゲームより非ゲームのアプリの方が UI が洗練されているのでは?
- その傾向はあると思う
- ゲームは操作ミスによって失うものが大きいから、誤解されうる操作性を使いにくいのではないか