kazuki’s blog

新人エンジニアのブログ

勉強会「LWRPのパス拡張について」を開催しました

こんにちは
かずきです。

お世話になっているサポーターズColabさんで勉強会を開催しました!
今回は前回に引きつづきLWRPのお話です。

・内容はパスの追加について
・カメラスタッキングを利用するには
の2点です!


質疑応答

Q.追加パスで出てくるloadOpのRenderBufferLoadActionのLoadとDon'tCareはどういう意味なのか?

A.SRPでは描画内容をColorとDepthのRenderTextureとしてBufferに担保し描画している。
 Loadは前パスまでの内容を読み込む。
 Don'tCareは前パスまでのパスを気にしない(読み込まなくて良い場合は読み込まない)。

 Qpaqueの場合、前パスの内容を気にしなくて良いのでDon'tCareにすると一部GPUを除きパフォーマンスが向上します。  

新卒で入った会社を1年3ヶ月で退職しました。

こんにちは。

今回は退職者その2Advent Calendarのエントリーです。

昨年4月に新卒に渋谷区のITベンチャーU社のゲーム事業部に入社しましたが、
今年7月に退職しました。

入社前の印象

  • お金はあるから挑戦出来る
  • 基本現場からの意見を尊重するボトムアップ方式でドンドン意見の言える環境
    など良さげなセリフ騙され、世間知らずの私は意気揚々と期待を膨らませていました。

実際の環境

入社してから辞めるまで配属されていたのは、
初内製の新規ゲーム開発プロジェクトでした。

独裁体制の環境


入ってみたら実際の環境は、
現場のトップである事業本部副部長に人事・査定・現場の指揮の裁量&役割があり現場のトップにより環境が全然違う会社でした。
ゲーム事業は現場トップの独裁状態で意見を言うと目を付けられ裁量は剥がれ、タスクは増える環境。

f:id:tc_kazuki:20181127223326p:plain:w300
意見・反論は認めない。 けど責任はお前達が取れと言うスタイルだった。

成長の出来ない環境

・上の役員が見てわかる進捗を最優先
 (最低限のパフォーマンスやセキュリティ<リソースの暗号化など>を捨ててまで最速での実装を強要)

・むちゃくちゃなスケジュールでプロジェクトが動いていた
 (デバッグなど除いて単純に実装のみした場合として半年以上の乖離があったので案の定間に合わず直前で延期)
 (毎日深夜退社&時々休日出勤 + 体調を崩しても出勤しなければならない状況)
 (休みの日も高熱を出して倒れてる事多くプライベートでプログラムを書く時間をあまり取れない状況だった)

・上2つに加え全員Unityでの開発が初のメンバー

業務は物量だけ多い、とても技術的にはレベルの高いとは言えないものな上、
プライベートでは時間が取れない(そもそもプライベートの時間が少ない)環境でした。

人権の無い開発環境

f:id:tc_kazuki:20181127223313p:plain
開発環境がMacbook Pro(2017Early)もしくは、
Windows10(Core i5の3rd or 4th Gen & RAM8G & HDD ・ グラボなし)
の環境でUnityを使用し3Dゲームを開発していたので、
1日最低でも3回くらいはUnityがクラッシュしてた上にSourceTreeすらdiffが多いとクラッシュしていました。

コアタイム8時間の裁量労働制 (良くは無いけど割りとこのタイプの会社はあるので、おまけ)

f:id:tc_kazuki:20181127225724j:plain
労働組合の実態も無く、自分の居た事業部では社員使いホーダイとして使われてました。
採用ページへの記載&事前説明無く、
入社数日前に雇用契約書で初めて知りました。
※来年の法改正に合わせて慌てて役職無しの社員の裁量労働制をやめたっぽい。

転職したキッカケ

・プロトタイプ段階でゲームコアの実装&レビューせずこのまま次に行くのはマズいのでは
 と言う現場の声を無視して無理やり製品版開発進めさせたり(退職時まで1度もレビュー会など無し)、
 制作が進んでる中、検討段階で一切共有せず事後報告で世界観変えるから(スケジュールは変えない)
 と言い出したりと少なくともBtoCのプロダクトを作る環境では無いなと感じていた事

・意見を言う事が気に食わないのを理由に、現場評価及び評価制度を無視してまで、最低評価に捻じ曲げられた事

の2点で転職を決意しました。

現在の環境

現在は虎ノ門スマホゲームを開発・運営をしている会社に転職して4ヶ月ほどになります。
最初は現場の熱量が全然違うのと大小含めレビュー会の頻度の多さ・職種問わずプロダクトに関する意見を求められる事に驚いたりしました。
業務に関してもまったく経験も知識も無いシェーダや描画周りに挑戦させてもらったりしてます(絶賛LWRPに振り回され中)
ヒット作を出し運営している会社だけあって、
人権アリアリの開発環境で前職と比べると圧倒的にレベルの高いエンジニアに囲まれて楽しくやってます!

勉強会「Amplify Shader Editorを使用したLWRP対応について」を開催しました

こんにちは
かずきです!

Amplify Shader Editorを使用した、Lightweight Render Pipeline対応についてお話しする機会がありました!!


資料

www.slideshare.net


シェーダ置換ツール
github.com


LWRP対応Amplify Shaderテンプレート & カスタムノード
github.com

勉強会「AssetBundleちょっと理解した( )」を開催しました!

こんにちは!
かずきです。

サポーターズさんにてAssetBundleをテーマに勉強会を開かせていただきました〜

資料

www.slideshare.net

サンプルプロジェクト

github.com

質疑応答

  • Q . 2018ユーザはAddressable Assetを使用した方が良いとの事でしたが、
    Caching APIの問題は解決されたのか?
  • A . ちゃんと触ってないので確かな事は言えないが解決して無かったように思う。

  • Q . Caching.isReadyの問題はある程度改善されているが、
    キャッシュの個別削除がちゃんと機能していない方が問題に思う。
    サンプルプロジェクトではDownloadHandlerAssetBundleを使っていたがこの問題にはどう対処したか?
  • A . サンプルプロジェクトは今回最小実装を説明する為に作成したものなので対処はしていない。
    実際に商用タイトルなどで使用するものを実装する際はキャッシュ機構を自作した方が良いと思う。

Amplify Shader Editor触ってみた

こんにちは〜 かずきです。

去年のブラックフライデーセールで買ったのに使っていなかった...
今回少しだけ触ってみた(というかサンプル見てみた)ので備忘録的なもの書いてみます!

Amplify Shader Editorとは

Surface ShaderをノードベースのGUIエディタで作成する事の出来るUnity向けアセットです。

サンプルシェーダAnimatedFireを見てみた

f:id:tc_kazuki:20180324221917g:plain
炎が燃えながら動くシェーダです。

f:id:tc_kazuki:20180318010801p:plain シェーダをノードエディタ画面開いたらこのようになっています。

シェーダの各処理を見てみた

UVスクロール

f:id:tc_kazuki:20180318010842p:plain
全体で見た場合、左下の方にある処理です。
UVスクロールとは時間などの変動する値を利用し、テクスチャの描画位置を移動させていくものの事を指します。
ここでは経過時間を20分の1にしたものと、インスペクターから設定したTileSpeedを元にUVスクロールさせています。

f:id:tc_kazuki:20180324223446g:plain
なのでTileSpeedの値を上げると炎の動きが速くなり、 f:id:tc_kazuki:20180324223631g:plain
0にすると炎は止まります。

テクスチャのアルファ値変化

f:id:tc_kazuki:20180324221708j:plain
経過時間を基準値としたSinカーブ(-1 〜 1)の値を加算していったものと、インスペクターから設定したFireIntensityの値を元にアルファ値を変化させています。

f:id:tc_kazuki:20180325000759g:plain FireIntensityの値を上げると炎が強くなり、
f:id:tc_kazuki:20180325000939g:plain 低くすると炎が弱くなります。

テクスチャ合成

f:id:tc_kazuki:20180324235237j:plain
Multiplyノードでテクスチャの合成をしています。
UVスクロール処理を加えたTileableFireテクスチャと処理を加えてないMaskテクスチャを合成したのちに、
アルファ値だけ変化させている白色テクスチャを合成する事で炎の強弱を変化させています。

f:id:tc_kazuki:20180325001528g:plain
なので、FireIntensityを0にすると炎が完全に消え、
f:id:tc_kazuki:20180325001732g:plain
TileableFireのテクスチャ画像を真っ白なものにすると動きがなくなります。

まとめ

ノードベースのGUIエディターでシェーダーが組みやすいとはいえシェーダーの基礎知識が無いと、
作りたいシェーダーを作るのは難しいなと思いました。
とりあえず勉強します!

Behavior Designer触ってみた

こんにちは! かずきです。

今回はBehavior DesignerというゲームAIを作成する為のUnity向けアセットを触ってみたので、 備忘録的なものを書きます。

Behavior Designerとは

Task(行動や条件)をノードとしてツリー構造で管理・実行できて、ツリーをGUIエディターで作ることの出来るアセット。
UE4にデフォでついてるBehavior Treeみたいなもの。

ビジュアル化されているので比較的わかりやすく効率のより思考ルーチンを組む事が出来る(と思う)

Behavior Designerのタスク

エディタ内では
Composite
Decorator
Conditional
Action
の4種類で分けられていますが、大きく分けると
Action Task (行動するタスク)
Conditional Task (条件判断するタスク)
の2種類で構成されています。

- Action Task ・・・ Decorator Action
- Conditional Task ・・・ Composite Conditional
といった感じに分けられます。

Compositeタスク

Conditional Taskの1種で、タスクの実行結果を条件判断値として利用し、子ノードのタスクを実行していきます。

主なCompositeタスク

Sequence

f:id:tc_kazuki:20180304180104p:plain エディタ上で見て左から順に子ノードのタスクを実行します。
Failure(失敗)が子ノードのタスクから返ってきた際には自身もFailureを返し、その後に続く子ノードのタスクは実行されません。

Selector

f:id:tc_kazuki:20180304180048p:plain Sequenceの逆でSuccessが子ノードのタスクから返ってきた際には自身もSuccessを返し、
その後に続く子ノードのタスクは実行されません。

Parallel (並列実行版Sequence)

SequenceとSelectorは子ノードのタスクを左から順番に実行していましたが、
Parallel(並行)は文字通り子ノードのタスクを並列実行します。
実行した子ノードのタスクが1つでもFailureを返したタイミングで他の子ノードのタスクを全て中断して、
自身もFailureを返します。

Parallel Selector (並列実行版Selector)

子ノードのタスクを並列実行します。
実行した子ノードのタスクが1つでもSuccessを返したタイミングで他の子ノードのタスクを全て中断して、
自身もSuccessを返します。

Random Sequence (ランダム実行順版Sequence)

Sequenceでは左から順に子ノードのタスクを実行していましたが、Random Sequenceではランダムな順番で子ノードのタスクが実行されます。
その他の動作はSequenceと同様です。

Random Selector (ランダム実行順版Selector)

Selectorでは左から順に子ノードのタスクを実行していましたが、Random Selectorではランダムな順番で子ノードのタスクが実行されます。
その他の動作はSelectorと同様です。

Priority Selector (優先順位指定版Selector)

Selectorでは左から順に子ノードのタスクを実行していましたが、Priority Selectorでは子ノードのタスクに設定されたPriorityが高い順にタスクを実行します。
その他の動作はSelectorと同様です。

まとめ

今回は軽く触ってみただけなので、軽めの内容になってます。
しっかり触ったのちにもうちょっとちゃんとした内容の記事書きます。。

間違いなどございましたらコメント欄でご指摘していただけると幸いです。

では、また!

SRDebuggerのBugReportをSlackに送信する

こんにちは!
かずきです。
 
今回はUnityのアセットSRDebuggerのBugReport機能を改造して、
Slackに送信してみます!

バグレポートタブを有効化する

まず、BugReportTabControllerを改造します。
適当な名前(今回はMyBugReportTabControllerとします)のC#スクリプト作成し、
StompyRobot/SRDebugger/Scripts/UI/Tabs/BugReportController.csの内容をコピペします。

public bool IsEnabled
{
    get { return Settings.Instance.EnableBugReporter; }
}

ここを以下のように書き換えて保存します。

public bool IsEnabled
{
    get { return true; }
}



編集する為、StompyRobot/SRDebugger/Resources/SRDebugger/UI/Prefabs/Tabs内にあるBugReportTab.prefabをシーン上に配置します。
f:id:tc_kazuki:20180121162138p:plain

 
インスペクターをDebugモードに切り替えて、BugReportTabControllerを先程作ったスクリプトに差し替えます。

 
f:id:tc_kazuki:20180121162159p:plain

  
オブジェクト名・タブ名・キーを変更します。

f:id:tc_kazuki:20180121162232p:plain

StompyRobot/SRDebugger/Resources/SRDebugger/UI/Prefabs/Tabsにプレハブを保存します。(Reaource下のパスが同じなら他のディレクトリでOK)

f:id:tc_kazuki:20180121162245p:plain


f:id:tc_kazuki:20180121162255p:plain
これでバグレポートタブが表示されます。
ですが、このままではレポートの送信が出来ません(;ω;)

バグレポートをSlackに送信する

次はBugReportApiを改造します。
適当な名前(今回はMyBugReportApiとします)のC#スクリプト作成し、
StompyRobot/SRDebugger/Scripts/Internal/BugReportApi.csの内容をコピペします。

作成したスクリプト(MyBugReportApi)を編集していきます。

内容別で送信する為、レポート種別のEnumを作成します。

#region Enums

public enum ReportType : int
{
    ScreenShot = 0,
    SystemInfo,
    Console,
}

#endregion //Enums



次にSlack投稿時に使用する情報を定義します。

       #region Constants

        /// <summary>
        /// Slack API 
        /// </summary>
        private static readonly string SlackApiUrl = "https://slack.com/api";
        private static readonly string UploadEndPoint = "/files.upload";
        /// @note attachment上手行かない。。。
        //private static readonly string PostMessageEndPoint = "/chat.postMessage";
        private static readonly string AccessToken = "YourAPIToken";
        private static readonly string WebHookUrl = "https://hooks.slack.com/services/Txxxxx/Bxxxxxx/xxxxxxxxxxxxxxxxxx";
        private static readonly string Channels = "general";
        private static readonly string UserName = "SRDebuggerBugReport";

        /// <summary>
        /// Attachmentのカラー
        /// </summary>
        private static readonly string SystemInfoColor = "#000000";

        /// <summary>
        /// Log毎にAttachmentを分ける場合のカラー
        /// </summary>
        private static readonly string ErrorLogColor = "#FF0000";
        private static readonly string WarningLogColor = "#FFD700";
        private static readonly string NormalLogColor = "#708090";

        #endregion // Constants



3回に分けて投稿する為、ログIDを生成し保存するための変数を宣言します。

private static uint _logId;



BuildJsonRequestメソッドを削除して、以下の4つのメソッドを作成します。

static WWWForm BuildScrrenShotUploadRequest (BugReport report)
{
    var form = new WWWForm();

    form.AddField("token", AccessToken);
    form.AddField("channels", Channels);
    form.AddField("username", UserName);
    var title = string.Format("ScreenShot ({0})", _logId);
    form.AddField("title", title);
    form.AddBinaryData("file", report.ScreenshotData, "screenShot", "imag/png");
    form.AddField("initial_comment", report.UserDescription);

    return form;
}
static string BuildJsonRequest (int typeValue, BugReport report)
{
    string json = "";
    switch (typeValue)
    {
        case (int)ReportType.ScreenShot:
            var ht = new Hashtable();
            ht.Add("text", report.UserDescription);
            json = Json.Serialize(ht);
            break;
        case (int)ReportType.SystemInfo:
            json = BuildSystemInfoJsonRequest(report);
            break;
        case (int)ReportType.Console:
            json = BuildConsoleLogJsonRequest(report);
            break;
    }
    return json;
}
static string BuildSystemInfoJsonRequest (BugReport report)
{
    var ht = new Hashtable();
    ht.Add("username", UserName);
    var messageTitle = string.Format("SystemInfo ({0})", _logId);
    ht.Add("text", messageTitle);

    List<Hashtable> attachmentList = new List<Hashtable>();
    List<Hashtable> systemInfoList = new List<Hashtable>();
    foreach (var systemInfos in report.SystemInformation)
    {
        var systemInfoHashTable = new Hashtable();
        systemInfoHashTable.Add("color", SystemInfoColor);
        systemInfoHashTable.Add("title", systemInfos.Key);
        var sb = new StringBuilder();
        foreach (var obj in systemInfos.Value)
        {
            sb.AppendLine(obj.ToString());
        }
        systemInfoHashTable.Add("text", sb.ToString());

        attachmentList.Add(systemInfoHashTable);
    }

    ht.Add("attachments", attachmentList);

    var json = Json.Serialize(ht);

    return json;
}
private static string BuildConsoleLogJsonRequest (BugReport report)
{
    var ht = new Hashtable();
    ht.Add("username", UserName);
    var messageTitle = string.Format("Console ({0})", _logId);
    ht.Add("text", messageTitle);

    List<Hashtable> attachmentList = new List<Hashtable>();

    foreach (var logs in CreateConsoleDump())
    {
        StringBuilder sb = new StringBuilder();
        var consoleHashTable = new Hashtable();

        switch (logs[0])
        {
            case "Assert":
            case "Exception":
            case "Error":
                consoleHashTable.Add("color", ErrorLogColor);
                break;
            case "Warning":
                consoleHashTable.Add("color", WarningLogColor);
                break;
            default:
                consoleHashTable.Add("color", NormalLogColor);
                break;
        }

        // LogType + Log
        var title = logs[0] + " : " + logs[1];
        consoleHashTable.Add("title", title);
        // StackTrace
        for (int i = 2; i < logs.Count; i++)
        {
            sb.Append(logs[i]);
        }
        sb.AppendLine();
        consoleHashTable.Add("text", sb.ToString());
        attachmentList.Add(consoleHashTable);
    }

    ht.Add("attachments", attachmentList);

    var json = Json.Serialize(ht);

    return json;
}



Submitメソッドを以下のように編集します。

public IEnumerator Submit ()
{
    //Debug.Log("[BugReportApi] Submit()");

    if (_isBusy)
    {
        throw new InvalidOperationException("BugReportApi is already sending a bug report");
    }

    // Reset state
    _isBusy = true;
    ErrorMessage = "";
    IsComplete = false;
    WasSuccessful = false;
    _www = null;
    _logId = (uint)System.Security.Cryptography.MD5.Create().GetHashCode(); // ハッシュ値ならそうそう被らないだろうという安直な考え

    foreach (var type in Enum.GetValues(typeof(ReportType)))
    {
        try
        {
            if ((int)ReportType.ScreenShot == (int)type && _bugReport.ScreenshotData != null)
            {
                var form = BuildScrrenShotUploadRequest(_bugReport);
                var url = SlackApiUrl + UploadEndPoint;

                _www = new WWW(url, form);
            }
            else
            {
                string json = BuildJsonRequest((int)type, _bugReport);

                var jsonBytes = Encoding.UTF8.GetBytes(json);
                var headers = new Dictionary<string, string>();
                headers["Content-type"] = "application/json";
                headers["Accept"] = "application/json";
                headers["Method"] = "POST";
                headers["data"] = json;

                _www = new WWW(WebHookUrl, jsonBytes, headers);
            }

        }
        catch (Exception e)
        {
            ErrorMessage = e.Message;
        }

        if (_www == null)
        {
            SetCompletionState(false);
            yield break;
        }

        yield return _www;

        if (!string.IsNullOrEmpty(_www.error))
        {
            ErrorMessage = _www.error;
            SetCompletionState(false);

            yield break;
        }

        if (!_www.responseHeaders.ContainsKey("STATUS"))
        {
            ErrorMessage = "Completion State Unknown";
            SetCompletionState(false);
            yield break;
        }

        var status = _www.responseHeaders["STATUS"];

        if (!status.Contains("200"))
        {
            ErrorMessage = SRDebugApiUtil.ParseErrorResponse(_www.text, status);
            SetCompletionState(false);

            yield break;
        }
    }

    SetCompletionState(true);
}



StompyRobot/SRDebugger/Scripts/Services/Implementation内にあるBugReportApiServiceを編集します。

まずはメンバ変数_reportApiの型を作成したスクリプト(MyBugReportApi)の型に変更します。

private BugReportApi _reportApi;
// ↓
private MyBugReportApi _reportApi;



SendBugReportメソッド内でBugReportApiをnewしている場所を作成したスクリプト(MyBugReportApi)に変更します。

public void SendBugReport (BugReport report, BugReportCompleteCallback completeHandler,
    BugReportProgressCallback progressCallback = null)
{

    // 省略


    _reportApi = new BugReportApi(report, Settings.Instance.ApiKey);
    // ↓
    _reportApi = new MyBugReportApi(report, Settings.Instance.ApiKey);
}

これでSlackにバグレポートを送信できます!
f:id:tc_kazuki:20180121172322p:plain
f:id:tc_kazuki:20180121172421p:plain
f:id:tc_kazuki:20180121172426p:plain

追記

bitbucketにソースコードとunitypackage公開しました〜
tc_kazuki / SRDebugger BugReportSend To Slack — Bitbucket

まとめ(というか感想)

  • なるべくSRDebugger側のソースコードは編集しないように作りましたが、
    BugReportApiServiceのみ手を加えることになってしまった。

  • Slack Web API でAttachmentを使おうとしたが上手く表示出来きずWebhookで投稿してます(わかる方教えて下さい!)


    今回はログ種別などの見やすさを重視しましたが
    大きなプロダクトになると、逆に流れて見にくくなってしまうので、
    SystemInfoとConsoleはテキストファイルに1まとめにしてUploadする形にした方が良さそうなので、
    時間がある時作ってみます(後Reflectionも)。