littlewing

人間とコンピューターとメディアの接点をデザインするために考えたこと

Unity でビルド時に自動でバージョンに日付を付与する

Unity でビルド時に自動でバージョンに日付を付与するEditor拡張

PlayerSettings.bundleVersion が0.1の時 ビルド時に 0.1.yyyymmddに変更します。

ビルド完了時、もしくはエラー発生時に PlayerSettings.bundleVersion を元に戻します。

Magic Leapの場合は PlayerSettings.Lumin.versionName も合わせて更新している。

#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;

public class BuildVersionWithDate : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
    public static string date = DateTime.Now.ToString("yyyyMMdd");
    private string baseVersion;
    private string baseLuminVersionName;
    private BuildTarget baseTarget;
    /// ビルド前処理
    public void OnPreprocessBuild(BuildReport _report)
    {
        Application.logMessageReceived += OnBuildError;
        baseVersion = PlayerSettings.bundleVersion;
        //バージョンに日付を付与する
        PlayerSettings.bundleVersion = $"{baseVersion}.{date}";
        baseTarget = _report.summary.platform;

        //MagicLeapはLumin.versionNameも更新
        if (baseTarget == BuildTarget.Lumin)
        {
            baseLuminVersionName = PlayerSettings.Lumin.versionName;
            PlayerSettings.Lumin.versionName = PlayerSettings.bundleVersion;
        }
    }

    /// ビルド後処理
    public void OnPostprocessBuild(BuildReport _report)
    {
        Restore();
    }


    private void OnBuildError(string condition, string stackTrace, LogType type)
    {
        if (type == LogType.Error)
        {
            Restore();
        }
    }

    //Rollback
    private void Restore()
    {
        Application.logMessageReceived -= OnBuildError;

        //バージョンを元に戻す
        PlayerSettings.bundleVersion = baseVersion;

        if (baseTarget == BuildTarget.Lumin)
        {
            PlayerSettings.Lumin.versionName = baseLuminVersionName;
        }

        AssetDatabase.SaveAssets();
    }

    // 開発モードか?
    private bool isDevelopment(BuildReport report)
    {
        return (report.summary.options & BuildOptions.Development) != 0;
    }

    ///  実行順
    ///  MagicLeapにおいて、versionNameを動的に設定するために
    ///  MagicLeapManifestBuildProcessorより先に実行したいため-1を設定する
    public int callbackOrder { get { return -1; } }

}

#endif

iOS開発メモ

Provisioning profileの作成

「プロビジョニングプロファイル」とは、「証明書」+「App ID」+「端末のUDID」が一体になったものです。 これを Mac にインストールすれば、自分で作ったアプリを iPhone端末にダウンロードできるようになります。

rightcode.co.jp

秘密鍵のExport

いろいろなアプリの配布方法

qiita.com

UIWidgets メモ/リンク集

UIWidgets

github.com

github.com

Flutterリファレンス

Tipsなど

可変framerateを停止する

  • 本家のReadmeにもあるが、unity 2019.3以降はUIWidgetsがOnDemandRenderAPIを使用して実装されるため、アニメーションが動作していないときは、フレームレートが自動的に落ちるようです。

  • バッテリー消費量を削減できるメリットがある反面、UnityのGameView自体のFPSも落ちてしまうので、3D CGがカクついてしまいます。

  • 以下のコードを書くことで無効化できます。
  • ドキュメントにはVsyncもOffにしろとあったけど、やらなくても、動いたみたい。
public class UIWidgetsExample : UIWidgetsPanel
{
    protected override void OnEnable()
    {
        base.OnEnable();
        //可変framerateを停止させる
        Window.onFrameRateCoolDown = () => { };
        Window.onFrameRateSpeedUp = () => { };
    }
    //....
}

Splitview

Unityで特定の点を中心にScaleを変化させる (ScaleAround)

UnityでGameObjectの原点ではなく、指定した点を中心にScaleを変更する方法。

rotationは原点と回転軸を指定して回転できる RotateAround があるけど、Scaleには無いので作った。

タッチパネルの2本指操作や、両手VRコントローラで拡大させる時に、拡大の中心を動的に変更できて便利。

/// pivot を中心に、target のScaleを変化させる
public void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale)
{
    Vector3 targetPos = target.transform.localPosition;
    Vector3 diff = targetPos - pivot;
    float relativeScale = newScale.x / target.transform.localScale.x;

    Vector3 resultPos = pivot + diff * relativeScale; 
    target.transform.localScale = newScale;
    target.transform.localPosition = resultPos;
}

実際は、Transformの原点で拡大するんだけど、動的に位置を補正している。

Unity Bolt Tips4.State Machine を利用する

Bolt Tips 連続記事です

前回の続き

BoltではState Machineを記述することができます。

State Machine とは?

  • State Machine は 1つ以上の状態(State)と 状態間の遷移(Transition)で構成されます。
  • 状態は、特定のアクションを実行し続けます。
  • 状態は、特定のセンサーからのイベントを検知した際に、別の状態へ遷移します。

参考:状態機械

f:id:pigshape:20200905213256p:plain
StateMachineの例(敵キャラの動作)

BoltにおけるStateMachineの作成

  1. Projectタブで、右クリック > Create > Bolt > State Macro で作成できます。

  2. 初期状態ではStateGraphには何もありません。
    右クリック > Create Flow State で最初のStateを作成します。
    f:id:pigshape:20200905213752p:plain

  3. ダブルクリックすると、Stateの中身を記述できます。

    • On Enter State (Stateが開始されたとき)
    • Update (そのState中に毎フレーム呼ばれる)
    • On Exit State (Stateが終了する時) のUnitを起点として、それぞれの記述ができます。
      f:id:pigshape:20200905214049p:plain
  4. 別のStateはまた、右クリック > 'Create Flow State' で作成できます。 最初のStateをCtrlを押しながら選択し、2つ目のStateへカーソルを動かすと、Transition を作成できます。
    f:id:pigshape:20200905214436g:plain

  5. Transition の矢印をダブルクリックすると Transitionの記述ができます。
    最初は Trigger Transition のUnitがあるだけです。
    f:id:pigshape:20200905214724p:plain

  6. 例えば以下のような記述を行うことでSpaceキーが押されたら遷移するという記述を行うことができます。 f:id:pigshape:20200905215004p:plain

AnyState

  • AnyStateのUnitを利用すると、どのStateであっても、遷移のトリガー条件を満たせば、他の状態を呼び出すことができます。
  • これらを利用して複数のStateを同時に実行することも可能です。
    f:id:pigshape:20200905215443p:plain

呼び出し順番

また、On Enter State の呼び出し順番などは、以下のブログの方が調査結果をまとめているので参考になると思います。

note.com

続き

littlewing.hatenablog.com

Unity Bolt Tips3.Formula Unitを利用する

Bolt Tips 連続記事

前回の続きです

Boltには Formula Unit という、論理式や数式を直接評価できる強力なユニットがあります。

f:id:pigshape:20200905192651p:plain
Formula Unit 利用例

簡単に言うと、Unit内で テキストコードを書くことで、if文や計算式を記述できます。

その使い方を書きます。


Formula Unit のメリット

  • 通常Boltで分岐処理や加減算処理を行うには、Branch Unit (IF文) や Add Unit (足し算) などを利用するのですが、複雑な計算を記述するとノードが複雑なり、読みづらくなってきてしまいます。

  • Formula Unit に直接テキスト形式のコードを記述することで、ある程度複雑な分岐を簡潔に記載することができ、書きやすさ、読みやすさが向上します。

Formula Unit のデメリット

  • パフォーマンスが悪い(遅い)です。
  • オーバーヘッドが発生してしまうため、通常の演算子ユニットを利用するのに比べて Formula Unit のを使用する方が著しく遅くなるとマニュアルに記載されています。
  • そのため、Updateなど毎フレーム呼び出す処理中で利用する事は推奨されていません。

Formula Unitの使い方

  1. Bolt編集画面で、右クリックから Formula Unitを検索、選択します。 f:id:pigshape:20200905193803p:plain

  2. Formula Unitは 数式を入力するテキストフィールドと、入力引数の数を指定するInputsの2つの項目があります。
    f:id:pigshape:20200905193943p:plain

  3. テキストフィールドに条件式などを記述することで、任意の制御を行えます。

    • 入力引数は前から、a,b....z とアルファベット変数に割り当てられるので、式内で利用できます。
    • if 文や3項演算, Vector2/Vector3/Vector4 型なども扱うことができます。
    • abs/sin/cos/floor などの数式も利用できます。
      f:id:pigshape:20200905194546p:plain
  4. 記述内容に意味はあまり無いですがサンプルとして f:id:pigshape:20200905195130p:plain

  5. 式の内容にエラーがある場合は、例外が発生します。
    f:id:pigshape:20200905200413p:plain

EvaluationException: no viable alternative at input '<EOF>' at line 0:-1
Bolt.Dependencies.NCalc.Expression.Evaluate (Bolt.Flow flow) (at <......>:0)

f:id:pigshape:20200905200427p:plain


使える式や制御方法の詳細はマニュアルを確認してください。

docs.unity3d.com

続き

littlewing.hatenablog.com

Unity Bolt Tips2.Boltの独自ノードをC#で作成する

Bolt Tips 連続記事です

前回の続きです BoltのノードはUnityのC#のメソッドが利用できるのですが、自分で関数ノードを作成して再利用する事もできます。

Boltのノード作成の基礎的なメモ

MacroとEmbed

BoltのフローのソースにはMacroかEmbedを選択して利用できます。 f:id:pigshape:20200904150644p:plain

それぞれの違いは以下の通り

項目 Embed Macro
関係性 グラフはFlow Machine Component 自体に埋め込まれます グラフはMacro AssetとしてProjectに保存され、Component から参照されます。
再利用性 グラフを他のFlow Machineから再利用することはできません。
ただし、Prefab間で共有することはできます。
Component自体が削除されると、グラフも一緒に削除されます。
同じMacroを複数のFlowMachine間で共有することができます。
あるGameObjectのFlowMachineを削除した場合でも、Macro AssetはProjectに残ります。
永続性 FlowMachineの設定を、EmbedからMacroに変更するとグラフ自体が削除されます。 MacroからEmbedに切り替えてもグラフ自体はEmbedとして残ります。
Sceneの参照 Prefabとして保存されていない場合は、Embed内のグラフはScene内のGameObjectを参照することができます。 Macroの状態ではどのSceneにも属していないため、シーン内のGameObjectを参照することはできません。
Prefab Editor上でPrefabをInstanciateした場合動作しないことがある。 Editor上でもすべてのPrefab内で利用することができます。

参考: Unity Bolt - it610.com

Macroとして再利用性の高いノードをライブラリとして作成することで、効率的な開発を行うことができます。

Macro利用時のPrefabからのGameObject量産時の変数汚染

ただし、PrefabのGameObjectのFlowMachineとしてMacroを利用した場合 記述内容にも寄るかもしれませんが、 Prefabからシーンに生成されたGameObject間で変数の汚染が発生する事があります。

その場合は、PrefabのMacroをConvertボタンを押してEmbedに変換してやると、解決する場合があります。

詳細調査中なので、間違ってるかもしれません。


SuperUnit

俗にいう関数は、SuperUnitとして定義できます。

  1. 右クリック-> Add NodeでSuperUnitを作成
    f:id:pigshape:20200904152014p:plain

  2. ダブルクリックで編集できます f:id:pigshape:20200904152109p:plain

  3. SuperUnitの入り口と出口はInput/Outputを利用します。 InputノードにControl Inputs(Outputs)キーを入れる必要があります。   f:id:pigshape:20200904152148p:plain

  4. MacroをドラッグアンドドロップすることでSuperUnitとして利用することができる
    f:id:pigshape:20200904152925g:plain

  5. SuperUnitの公式のマニュアルはこちらにあります。


C#で独自Boltノードを作成する

  • C#でコードを書く
    public class HogeTest
    {
        public void TakeDamage(int damage)
        {
            Debug.Log("Damage");
        }
    }
  • SetupWizardでAssemblyを設定しておくと、ノードとして呼び出すことができます。
    f:id:pigshape:20200904153217p:plain

  • 日本語のノードも作成可能です。

public class GraphTest : MonoBehaviour
{
    public int 日本語ノード(int 体力,int 気力)
    {
        return 0;
    }
}

f:id:pigshape:20200904153953p:plainf:id:pigshape:20200904184056p:plain

using Bolt;
using Ludiq;

[UnitTitle("InOutUnit")]
[UnitCategory("Littlewing/Test")]
public class InOutUnit : Unit
{
    [DoNotSerialize]
    public ControlInput input { get; private set; }
    [DoNotSerialize]
    public ControlOutput output { get; private set; }
    [DoNotSerialize]
    public ValueInput valueIn { get; private set; }
    [DoNotSerialize]
    public ValueOutput valueOut { get; private set; }

    protected override void Definition()
    {
        input = ControlInput("in", Enter);
        output = ControlOutput("output");

        valueIn = ValueInput<float>("valueIn");
        valueOut = ValueOutput<float>("valueOut", ReturnFloat);

        Requirement(valueIn, valueOut);
        Succession(input, output);
    }

    public ControlOutput Enter(Flow flow)
    {
        return output;
    }

    public float ReturnFloat(Flow flow)
    {
        return flow.GetValue<float>(valueIn);    
    }
}

f:id:pigshape:20200904153737p:plain


Outputを複数使い分ける 分岐/非同期

ControlOutput を条件に合わせて使い分けたり、Coroutine などを利用して非同期でOutputを行いたい場合は flowのreferenceを取得して、任意のタイミングでRunを実行することで出力タイミングや、出力先を変更することができます。

f:id:pigshape:20200913102438p:plain
InOutUnit2

また、以下の例では

  • [NullMeansSelf] を利用して、TransformにデフォルトでSelfを設定
  • float value にデフォルト値0.5を設定
  • [PortLabelHidden] を利用して、入力Pin inputの名前を隠す 
  • transformvalue は 表示名称に変数名をそのまま利用
  • Succession() を利用して,Relationを明示
  • [TypeIcon] でアイコンの設定

などを行っています。

using Bolt;
using Ludiq;
using UnityEngine;

[UnitOrder(0)] 
[UnitSurtitle("Sur Title")] 
[UnitSubtitle("Sub Title")] 
[UnitTitle("InOutUnit2")]
[UnitShortTitle("Short Title")] 
[TypeIcon(typeof(Transform))]
[UnitCategory("Littlewing/Test")]
public class InOutUnit2 : Unit
{
    [DoNotSerialize]
    [PortLabelHidden]
    public ControlInput input { get; private set; }

    [PortLabel("output1")]
    [DoNotSerialize]
    public ControlOutput output1 { get; private set; }
    [PortLabel("output2")]
    [DoNotSerialize]
    public ControlOutput output2 { get; private set; }

    [DoNotSerialize]
    [NullMeansSelf]
    public ValueInput transform { private set; get; }

    [DoNotSerialize]
    public ValueInput value { private set; get; }

    private Flow flow;
    private GraphReference reference;
    private Transform m_transform;
    private float m_value;
    private ControlOutput out1 => output1;
    private ControlOutput out2 => output2;

    protected override void Definition()
    {
        input = ControlInput(nameof(input), Enter);
        output1 = ControlOutput(nameof(output1));
        output2 = ControlOutput(nameof(output2));

        transform = ValueInput<Transform>(nameof(transform), null);
        value = ValueInput<float>(nameof(value), 0.5f);

        Succession(input, output1);
        Succession(input, output2);
    }

    public ControlOutput Enter(Flow flow)
    {
        this.flow = flow;
        reference = flow.stack.ToReference();
        m_transform = flow.GetValue<Transform>(transform);
        m_value = flow.GetValue<float>(value);
        bool b = true;
        if (b)
            Out1();
        else
            Out2();
        return null;
    }

    private void Out1()
    {
        Flow.New(reference).Run(out1);
        // この呼び方もできる
        //flow.Invoke(out1);
    }
    private void Out2()
    {
        Flow.New(reference).Run(out2);
        // この呼び方もできる
        //flow.Invoke(out2);
    }
}

API Reference

docs.unity3d.com


続き

littlewing.hatenablog.com