20200914のUnityに関する記事は7件です。

【Unity】動画で見るLerp

Vector3のLerpを動画にしました。
Lerpをなんとなく使っている人に見ていただけると幸いです。

動画


オブジェクトの色も線形補間(Color.Lerp)によって変化させています。

Lerp(Vector3 a, Vector3 b, float t)

引数

  • Vector3 a :開始点
  • Vector3 b :終了点
  • float t :二点の補間値

二点間を線形補間するメソッド。
$y=f(x)$上の二点$(x_1,y_1),(x_2,y_2)$に対して、

y = y_1 + \frac{y_1-y_2}{x_2-x_1}(x-x_1) 

で近似を行う。

二点の補間値tは以下の式となる。

 t = \frac{x-x_1}{x_2-x_1}

すなわち、$y=f(x)$上の点$(x,y)$が、どのくらい終点に近づいているかを表している。
この辺は以下の記事に大変わかりやすい図が載っています。

[Unity] Vector3.Lerpの使い方

LerpExample

LerpExample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LerpExample : MonoBehaviour
{
    //始点
    public Transform startMarker;
    //終点
    public Transform endMarker;
    //速度
    public float speed = 1.0f;

    //ColorLerpクラスのために非表示public
    //二点の補間値
    [System.NonSerialized]
    public float fractionOfJourney;

    private float startTime;

    private float distance;

    private void Start()
    {
        //動き出した時間
        startTime = Time.time;
        //二点間の距離
        distance = Vector3.Distance(startMarker.position, endMarker.position);
    }

    private void Update()
    {
        //移動距離 = 経過時間 * 速度
        float distCovered = (Time.time - startTime) * speed;

        //移動距離の割合 = 移動距離 / 二点間の距離
        fractionOfJourney = distCovered / distance;

        //線形補間による移動
        transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fractionOfJourney);
    }
}

ColorLerp

ColorLerp.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColorLerp : MonoBehaviour
{
    private LerpExample lerpExample;

    private Material mat;

    private void Start()
    {
        //LerpExampleから補間値を取得
        mat = this.GetComponent<Renderer>().material;
        lerpExample = GetComponent<LerpExample>();
    }

    void Update()
    {
        //青色→赤色へLerpExampleと同じ補間値で推移
        mat.color = Color.Lerp(Color.blue, Color.red, lerpExample.fractionOfJourney);
    }
}

まとめ

  • Lerpは線形補間をするメソッド。
  • 補間を行ってくれるので、これを使用すると動きが滑らかになる。
  • 次回はSlerpの記事を書くつもり
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】テクスチャを画像データに変換して端末に保存、読み込み

はじめに

画像の保存処理で手間取ったのでメモを残します。

今回は下記の処理を行います。
・ImageコンポーネントにアタッチされたSprite(Texture)をPngに変換して保存
・Pngを読み込んでImageコンポーネントにSprite(Texture)としてアタッチ

デモ

実際にAndroid端末にて検証したデモがこちらになります。
Qiita画像保存.gif

書き込んだ画像を消去したのち復元に成功しました。
端末内に画像データは保存されているのでアプリを終了しても復元が可能です。


下記画像はPC内部の保存先ディレクトリを参照した際のスクショです。
ちゃんとPng形式で保存されていました。
PaintQiitaSaveSS.PNG

お絵描きの部分のコード

お絵描きの部分の実装は完全にそのまんま使わせていただいてます。

【参考リンク】:UI.Image にペイントする。線を手書きする。

スマホ対応部分とテクスチャのリセットだけ下記のように追記してます。

Painterクラスに追記
    /// <summary>
    /// テクスチャーをリセット
    /// </summary>
    public void ResetTexture()
    {
        var img = GetComponent<Image>();
        var rt = GetComponent<RectTransform>();
        var width = (int)rt.rect.width;
        var height = (int)rt.rect.height;
        texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
        img.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);

        Color32[] texColors = Enumerable.Repeat<Color32>(bgColor, width * height).ToArray();
        texture.SetPixels32(texColors);
        texture.Apply();
    }

    void Update()
    {
#if UNITY_EDITOR
        if (Input.GetMouseButtonDown(0))
        {
            beforeMousePos = GetPosition();
        }
        else if (Input.GetMouseButton(0))
        {
            Vector3 v = GetPosition();
            LineTo(beforeMousePos, v, lineColor);
            beforeMousePos = v;
            texture.Apply();
        }
#elif UNITY_ANDROID && !UNITY_EDITOR
        if (0 < Input.touchCount)
        {
            Touch touch = Input.GetTouch(0);

            if (touch.phase == TouchPhase.Began)
            {
                beforeMousePos = GetPosition();
            }
            else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
            {
                Vector3 v = GetPosition();
                LineTo(beforeMousePos, v, lineColor);
                beforeMousePos = v;
                texture.Apply();
            }
        }
#endif
    }

画像の保存と読み込み

ボタンの押下処理はUniRxで実装してます。特に意味はないです。

using System.IO;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// テクスチャー ⇔ Png画像 の変換と保存と読み込み
/// </summary>
public class TexturePngConverter : MonoBehaviour
{
    [SerializeField] private Button _saveButton;
    [SerializeField] private Button _loadButton;
    [SerializeField] private Button _resetButton;
    [SerializeField] private Image _paintImage;
    [SerializeField] private Painter _painter;

    private const string IMAGE_SAVE_FOLDER = "Image";

    private void Start()
    {
        //セーブボタン
        _saveButton.OnPointerClickAsObservable().Subscribe(_ => ConvertToPngAndSave(GetSavePath(IMAGE_SAVE_FOLDER))).AddTo(this);
        //ロードボタン
        _loadButton.OnPointerClickAsObservable().Subscribe(_ => ConvertToTextureAndLoad(GetSavePath(IMAGE_SAVE_FOLDER))).AddTo(this);
        //リセットボタン
        _resetButton.OnPointerClickAsObservable().Subscribe(_ => _painter.ResetTexture());
    }

    ///  /// <summary>
    /// 保存先のパス取得
    /// </summary>
    /// <param name="folderName">区切りのフォルダ名</param>
    /// <returns>保存先のパス</returns>
    private string GetSavePath(string folderName)
    {
        string directoryPath = Application.persistentDataPath + "/" + folderName + "/";

        if (!Directory.Exists(directoryPath))
        {
            //まだ存在してなかったら作成
            Directory.CreateDirectory(directoryPath);
            return directoryPath + "paint.png";
        }

        return directoryPath + "paint.png";
    }

    /// <summary>
    /// 画像に変換&保存
    /// </summary>
    private void ConvertToPngAndSave(string path)
    {
        //Pngに変換
        byte[] bytes = _paintImage.sprite.texture.EncodeToPNG();
        //保存
        File.WriteAllBytes(path, bytes);
    }

    /// <summary>
    /// テクスチャに変換&読み込み
    /// </summary>
    private void ConvertToTextureAndLoad(string path)
    {
        //読み込み
        byte[] bytes = File.ReadAllBytes(path);
        //画像をテクスチャに変換
        Texture2D loadTexture = new Texture2D(2, 2); 
        loadTexture.LoadImage(bytes);
        //テクスチャをスプライトに変換
        _paintImage.sprite = Sprite.Create(loadTexture, new Rect(0, 0, loadTexture.width, loadTexture.height), Vector2.zero);
    }
}

Application.persistentDataPath

Application.persistentDataPathを呼ぶと
文字通りアプリ内に存在する永続的なパスへアクセスできます。
ここにフォルダを作って画像を保存しています。

EncodeToPNG

ImageConversionクラスからEncodeToPNGを呼び出すとbyte配列としてpngデータに変換してくれます。

【参考リンク】:ImageConversion.EncodeToPNG

WriteAllBytes,ReadAllBytes

WriteAllBytes,ReadAllBytesそれぞれデータの書き込みと読み込みを担います。
これ書くだけでやってくれるのでありがとうございますって感じです。

おわりに

書き込み、読み込み時のリソースの開放とかがよくわかりませんでした。
書かなくても動いたので"まあいっか"ってなってます。
素直で良い子なので危険なコード書いてたら教えて下さい。

参考リンク

【参考リンク】:EncodeToPNG hangs the script
【参考リンク】:はなちるのマイノート

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity 開発に関する 50 の Tips 〜ベストプラクティス〜(2016 Edition)

20200910152454.png

この記事は「Unity開発に関する50のTips 〜ベストプラクティス〜(翻訳)」を読んでとても参考になったので、その続編になる「50 Tips and Best Practices for Unity (2016 Edition)」の内容を超訳(勉強のため個人的に意訳・誤訳)したものです。

ブログにも掲載していますが、5日作業の結構な労力だったので Qiita にも掲載しておきます。(辺境のブログtwitter をよろしくしてくれると幸いであります?)

変なことを書いていたり、誤訳等あったらごめんなさい。仕事から帰ってきてから趣味でやっていたので、疲れたところなんかは、翻訳ツールが増えてます。まわりくどく感じた文の省略や、省略による文脈の欠落もあるかもしれません。(そのあたりは、2012 年版の翻訳と同じで、諸々あらかじめご了承ください)
コメントに書いているのは、個人的にメモとして加筆しています。
(括弧)の内容は、訳した元々の文章から括弧をされていたり、個人的に加筆したものがあります。



前書き

以下の記事の内容は、Gamasutra のコミュニティメンバーによって書かれたものです。考えや意見は執筆者のものであって、Gamasutra や Unity(公式)のものではありません。

約4年前にオリジナルの「50 Tips for working with Unity」の記事を公開しました。その内容の多くはいまでも関連性がありますが、多くのことが変わっています:

  • Unity がよくなったこと
    たとえば、今では FPS カウンターを信用しています。(Tips 42 のこと)Property Drawer が使えるようになったことで、カスタムエディターを書く必要がなくなりました。Prefab の働きによって、明示的にネストされた Prefab や代替案を用意する必要が減りました。Scriptable object はより便利になりました。
  • Visual Studio との統合が改善された
    デバッグが各段に楽になり、引っ掻き回すようなデバッグが減りました。
  • サードパーティ製のツールやライブラリーが改善された
    Assets Store には、デバッグやロギングを改善するよいものがあります。
  • バージョン管理がよくなった
    Prefab の複製やバックアップコピーを用意する必要がなくなった。(というか、バージョン管理の効果的な使い方がわかってきた)
  • 私がより多くの経験を得た
    過去4年間、私は多くの Unity で仕事をしました。その中には、たくさんのゲームのプロトタイプ(30日で30個のゲーム)を作った経験、Father.IO のような(執筆者が作った)プロダクションゲームの経験、そして、私たちの代表的な Unity Assets である Grids(現在は非推奨になって使えない)が含まれます。

ここでの Tips は、上述のすべてを考慮してオリジナルの Tips を修正したものです。Tips に入るまえに、ここで免責事項を述べておきます。(基本的にオリジナルと同じ)

これらの Tips はすべての Unity プロジェクトに当てはまるわけではないとした上で:

  • Tips は、3~20人ほどの小さなプロジェクトでの私の経験に基づいている。
  • 構造、再利用性、明確さなどには(時間のように)対価がある。チームの規模、プロジェクトの規模・目標によって、その対価を支払うべきかどうかが決まる。たとえば、GameJam(クリエイターが集まって短時間でゲームを作るイベント)ではこれらの多くは使うことはない(リファクタリングの条件を説明しているのだと思います)
  • 多くの Tips は好みの問題になる。(ここで挙げた Tips は衝突するものがあるけど、どの Tips にも優れたメリットがある)
  • Unity のサイトにもいくつかのベストプラクティスが掲載されている。(ただ、多くはパフォーマンスの観点によるもの)



ワークフロー (Workflow)

1. 最初にスケールを決定して、すべて同じスケールになるようにビルドする

そうしないと、あとでアセットを作り直す必要がでるかもしれません。(たとえば、アニメーションのスケールはいつも正しいとは限りません)3Dゲームの場合、通常は1unity unit = 1m がベストです。

ライティングや物理を使用しない2Dゲームでは、通常1 unity unit = 1 pixel がよいでしょう。UI(と2Dゲーム)では、解像度 (HD or 2xHD) を選択して、その解像度でスケールするようにすべてのアセットをデザインします。


2. すべてのシーンを実行できる状態にしておくこと

ゲームを実行するためにシーンを切り替える必要がないようにします。ただし、すべてのシーンで必要とされる永続的なオブジェクトを持っている場合は厄介です。方法をひとつ挙げると、オブジェクト自身を singleton にすることです。singleton については、別の tips で説明します。

旧 tips.10 と同じ内容だと思います。


3. バージョン管理システム(Git など)の効率的な使い方を学んで利用すること

  • アセットをテキストとしてシリアライズする。
    実際は、シーンや Prefab がマージしやすくなるわけではないけど、何が変更されたか確認しやすくなります。
  • シーンと Prefab をまとめる手法を採用する
    一般的に、複数の人が同じシーンや Prefab を作業してはいけません。小さなチームの場合、あらかじめ(作業をはじめる前に)、他に作業していないことを聞いておくだけで十分かもしれません。シーンの所有権を示すための物理的なトークンを用意しておくと便利かもしれません。(机の上にシーンの所有権を示すトークンがあるときだけ、そのシーンの作業することができる)
  • タグをブックマークとして利用する
  • ブランチの手法を決めて採用する
    シーンと Prefab はスムーズにマージできないので、ブランチ化はすこし複雑です。どのようにブランチを使うのか決めるにしても、シーンと Prefab は一緒にするようにします。
  • サブモジュールは注意して利用する
    サブモジュールは、再利用可能なコードを維持するための素晴らしい方法です。しかし、いくつか注意点があります。
    • メタファイルは一般的に複数のプロジェクトで一貫性がありません。一般的には、non-Monobehaviour or non-Scriptable object なコードでは問題ありませんが、MonoBehaviours and Scriptable objects では問題になる恐れがあります。
    • 多くのプロジェクト(サブモジュールを1つ以上含む)で作業をしていると、ときには雪崩を起こすことがあり、すべてのプロジェクトのコード管理を安定させるために、様々のプロジェクトで pull-merge-commit-push をしなければいけないことがあります。(そして、このメンテナンス中に他の誰かが変更を加えてしまうと、さらに雪崩を起こす)この影響を最小化するための方法のひとつは、常にサブモジュール専用のプロジェクトからサブモジュールに変更を加えることです。サブモジュールを使うプロジェクトは、常に pull するだけでよくなります。


4. テストシーンとコードは分離すること

一時的なアセットやスクリプトはリポジトリーにコミットして、完了したらプロジェクトから削除します。


5. ツール(もっぱら Unity 本体)をアップデートするときは、他の人も同時にする

Unity は異なるバージョンでプロジェクトを開いたときの対応がとてもうまくなりましたが、複数の作業者が異なるバージョンで作業するとリンクが失われることがあります。

旧 tips.2 とは少し違う内容です。


6. 綺麗な状態のプロジェクトにサードパーティー製アセットをインポートして、そこで新しいパッケージをエクスポートして利用する

アセットは、プロジェクトに直接インポートすると問題を発生することがあります。

  • 特に、Plugins フォルダーの直下にファイルを追加するアセット、または、Standard Assets を利用しているアセットは、衝突(ファイル名など)する可能性があります。
  • 展開されたファイルは、プロジェクト上に整理されていないかもしれません。これは使用しないと決めて削除した場合に問題(面倒)になります。

安全にインポートをする手順は以下に従ってみてください。

  1. 新しいプロジェクトを作成し、アセットをインポート
  2. サンプルを実行して、動作することを確認
  3. アセットをより適切なフォルダー構造に整理
    (通常だと、アセットに自分の好むフォルダー構造を強制しません。しかし、すべてのファイルがひとつのフォルダーに入っていること、そして、プロジェクトを上書きするような重要な場所にファイルが追加されないことを確認します)
  4. サンプルを実行して、動作することを再確認
  5. 不要なものをすべて削除する(サンプルなど)
  6. アセットがコンパイルされ、Prefab がすべてリンクを持っていることを確認し、なにかあれば、テスト
  7. すべてのアセットを選択し、パッケージをエクスポート
  8. プロジェクトにインポート


7. ビルドプロセスを自動化する

小さなプロジェクトでも有効ですが、とくに有効なのは次のとおり:

  • ゲームの異なるバージョンをたくさんつくる場合
  • 技術力の劣るメンバーがビルドをする場合
  • ビルドをする前に、プロジェクトに微調整を加える場合

詳細は「Unity Builds Scripting: Basic and advanced possibilities」(著者の過去記事)を参照してください。


8. コーディング資料 (your setup) をドキュメント化すること

ほとんどのドキュメントはコードの中にあるべきですが、しかし、特定のものはコードの外でドキュメント化されるべきです。

コーディング資料を得るために、デザイナーにコードを調べさせるのは、時間の無駄です。ドキュメント化されたコーディング資料は、開発効率を向上させます。(ドキュメントが最新の状態を保持できれば)

ドキュメントがフォローするもの:

  • タグの使用
  • レイヤーの用途(collision, culling, raycasting など、どのレイヤーに入れるか)
  • レイヤーの GUI の深さ(なにをどの上に表示していくか)
  • 複雑な Prefab の構造
  • イディオムの設定
  • ビルドの方法

わりと Unity の設定(セットアップ)に関する技術資料を指している気がします。

旧 Tips 49 と同じ内容だと思います。



一般的なコーディングについて (General Coding)

9. すべてのコードを名前空間に入れること

これにより、独自のライブラリーとサードパーティのコートとの衝突を避けることができます。しかし、重要なクラスとの衝突を避ける目的で名前空間を頼らないこと。異なる名前空間を使用する場合であっても、クラスの名前を「Object」、「Action」、「Event」のようにしないでください。

後ろはリーダブルコードのような内容の指摘だと思います。


10. Assert を使うこと

アサーションは、コードの実行結果が変わらないことをテストしたり、ロジックのバグを洗い出したりするのに便利な機能です。

Unity では「Unity.Assertions.Assert」を利用できます。これらはなんらかの条件のテストをして、条件を満たさなかった場合、コンソールにエラーメッセージを表示します。

アサーション(というよりも、テスト)がどのように役に立つのかをよく知らない場合は、「The Benefits of programming with assertions(リンク切れ)」を参照してください。


11. 表示されるテキスト以外の文字列は使用しない

特に、オブジェクトや Prefab の識別子としてテキストをそのまま使わないこと。例外もあります。(Unity では名前でしかアクセスできないものが、まだいくつかあります)そのような場合は、 AnimationNames や AudioModuleNames のようにファイルで文字列を定数として定義してください。これらのクラスが管理しきれなくなった場合は、入れ子になったクラスを使用して、AnimationNames.Player.Run のように定義します。

旧 Tips 34 と同じ内容だと思います。


12. Invoke と SendMessage を使わないこと

MonoBehaviour のこれらのメソッドは、名前をつけて他のメソッドを呼び出します。テキストの名前で呼び出されるメソッドはコードで追跡するのが難しいです。(Usages を見つけることができないし、SendMessage は範囲が広いので、さらに追跡できない)

コルーチンや C# のアクションを使って、独自の Invoke を簡単に用意できます:

public static Coroutine Invoke(this MonoBehaviour monoBehaviour, Action action, float time)
{
   return monoBehaviour.StartCoroutine(InvokeImpl(action, time));
}

private static IEnumerator InvokeImpl(Action action, float time)
{
   yield return new WaitForSeconds(time);

   action();
}

MonoBehavior から、次のように使うことができます:

this.Invoke(ShootEnemy); //ShootEnemy メソッドの引数はなし (void)

独自に MonoBehaviour を継承する基底クラスを実装する場合は、そこに独自の Invoke メソッドを追加することができます。

旧 Tips 21 と同じ内容です。

より安全な SendMessage の代替を実装するのは、難しいです。その代わりとして、通常は GetComponent を使って、対象のコンポーネントを取得し、直接呼び出すようにします。

補足として、Unity の「ExecuteEvent」についての提案が挙がっています。今のところ、よく調べられていませんが調査する価値がありそうです。


13. ゲーム実行中に生成したオブジェクトがヒエラルキーをごちゃごちゃにしないこと

親オブジェクトをシーンオブジェクトに設定することで、ゲームの実行中にオブジェクトを見つけやすくします。

コードからのアクセスをしやすくするために、空の Game Object や、Behaviour を持たない Singleton クラスを使用することもできます。

こうしたオブジェクトのことを DynamicObjects と呼びます。

旧 Tips 28 と同じような内容です。


14. 正しい値として null を使用するときは具体的にして、可能な限りそれを避けること

null は不正なコードを検出するための役にたちます。しかし、もし、null を暗黙的に渡すことを習慣にしてしまうと、不正なコードは楽々実行されるようになり、また、そのバグに気づくのはかなり後になります。

さらに、それぞれのレイヤーが null の値を渡すことで、コードの深い部分でそれがはっきりすることもあります。私は、null を正しい値として使用することを完全に避けるようにしています。

私の好ましいイディオムは null の値のチェックを一切行わず、問題のあるところではコードを失敗させることです。より深いレベルのインターフェースとして機能するメソッドでは、値が null であるかどうかをチェックし、それが失敗する可能性のある他のメソッドに渡す代わりに例外を投げます。

場合によっては、正しい値が null になることがあり、別のやり方で対処する必要があります。このような場合は、コメントを追加して、いつ、なぜ、値が null になっているのかを説明するコメントを追加してください。

よくあるシナリオ(ケース)では、インスペクターが設定した値を使用することです。ユーザーは値を指定することができますが、ユーザーが指定しない場合はデフォルト値が使用されます。

これより好ましい方法は、T の値をラップする Optional<T> クラスを使用することです。(Nullable<T> とすこし似てる)

特別なプロパティのレンダラーを使って、チェックボックスをレンダリングして、チェックが入っている場合にのみ、値を編集するボックスを表示するようにします。(残念ながら、ジェネリッククラスを直接使うことはできないので、T を特定する型にクラスを拡張しないとダメです)

[Serializable]
public class Optional<T>
{
   public bool useCustomValue;
   public T value;
}

コードでは次のように使用することができます:

health = healthMax.useCustomValue ? healthMax.Value : DefaultHealthMax;

補足として、これは多くのコメントで struct を使ったほうがよいという指摘がありました。ただし、これだと、非ジェネリッククラスのベースクラスとしては使えないということなので、実際にインスペクターで使えるフィールドに適します。


15. コルーチンを使うなら効率的な使い方を学ぶ

コルーチンは多くの問題を解決する強力な手段になりえます。しかし、デバッグが難しく、(自分を含めた)誰もが理解できないよくわからないコードを簡単につくることができます。

知っておくべきことは:

  • コルーチンを並列に実行する方法
  • コルーチンを連続して実行する方法
  • 既存のコルーチンから新しいコルーチンを作成する方法
  • CustomYieldInstruction を使ったカスタムコルーチンの作成方法
Enumerator RunInSequence()
{
   yield return StartCoroutine(Coroutine1());
   yield return StartCoroutine(Coroutine2());
}

public void RunInParallel()
{
   StartCoroutine(Coroutine1());
   StartCoroutine(Coroutine1());
}

Coroutine WaitASecond()
{
   return new WaitForSeconds(1);
} 


16. インターフェースを共有するコンポーネントを操作するときは拡張メソッドを利用する

今は、GetComponent はインターフェースも動作するようになったので、この Tips は冗長になっています。

特定のインターフェースを実装したコンポーネントを取得したり、そんなコンポーネントを持つオブジェクトを見つけるときに便利です。

以下の実装では、これの汎用的な実装に typeof を使用しています。ジェネリック版だと、インターフェースは動作しないけど、typeof は動作します。以下のメソッドは、これを通常のメソッドでラップしています。

public static TInterface GetInterfaceComponent<TInterface>(this Component thisComponent)
   where TInterface : class
{
   return thisComponent.GetComponent(typeof(TInterface)) as TInterface;
}


17. 拡張メソッドを使用して構文をより簡便にする

たとえば、つぎのようなもの:

public static class TransformExtensions 
{
   public static void SetX(this Transform transform, float x)
   {
      Vector3 newPosition = 
         new Vector3(x, transform.position.y, transform.position.z);

      transform.position = newPosition;
   }
   ...
}

旧 Tips 24 と同じような内容です。


18. 防御的な GetComponent の代替メソッドを使うこと

RequireComponent を使用してコンポーネントの依存関係を強制しても、他のクラスで GetComponent を呼び出す場合は、常に取得可能であるとは限らないし、望ましいことでもないです。RequireComponent を使用する場合であっても、コンポーネントを取得するコードの中で、コンポーネントが存在することを期待しているので、存在しない場合はエラーであることを示すとよいです。

エラーメッセージを表示するか、見つからなかった場合に役立つ例外をスローする拡張メソッドを用意します。

public static T GetRequiredComponent(this GameObject obj) where T : MonoBehaviour
{
   T component = obj.GetComponent();

   if(component == null)
   {
      Debug.LogError("Expected to find component of type " 
         + typeof(T) + " but found none", obj);
   }

   return component;
}

旧 Tips 25 と同じような内容です。


19. 同じことをするのに異なるイディオムを使うのは避ける

多くの場合、ひとつのものに複数のイディオム(慣用句)があります。そんな場合、プロジェクト全体で使う慣用句をひとつ選びましょう。その理由は:

  • イディオム同時はうまく働かない。ひとつのイディオムを使うと別のイディオムには適していない方向にデザインを強制されます。
  • 全体をとおして同じイディオムを使うことで、チームメンバーは、そこでなにをしているのか理解しやすくなります。構造・コードが理解しやすくなって、ミスをしづらくなります。

同じ意味のイディオムになるケースを挙げると:

  • Coroutine と State Machine
  • Nested Prefab と Lined Prefab と God Prefab
  • データを分離するための手法
  • 2D ゲームのスプライトの使用方法
  • Prefab の構造
  • スポーンするときのやり方
  • オブジェクト見つけるための方法:type, name, tag, layer, reference などいろんなやり方がある
  • オブジェクトをグループ化する方法:type, name, tag, layer, reference などいろんなやり方がある
  • 他のコンポーネントからメソッドを呼び出す方法(Tips 12 みたいなこと)
  • オブジェクトのグループを見つける方法とグループに登録する方法(Tips 13 みたいなこと)
  • 実行順序の制御
  • ゲーム内でのマウスによるオブジェクト・位置・ターゲットの選択
  • シーン変更の間にデータを保持する方法:PlayerPrefab を介して、または、新しいシーンがロードされたときに Destroy されないオブジェクト
  • アニメーションを組み合わせる方法
  • 入力のやり方

これだけ聞くと、半端に UniRx を使うのって微妙な気持ちになる気も。学習不足でコードをあまり追えない状態で利用すると、いくつかの Tips に反してしまうけど、前書きのリファクタリングに関する説明がそれを肯定する関係に思った。


20. ポーズを簡単にするために独自の時間クラスを準備する

ポーズとタイムスケール(遅くしたりする)をするために Time.DeltaTimeTime.TimeSinceLevelLoad をラップしておきます。これは厳格さを必要とするけど、特に異なるタイマーを管理している場合は物事を簡単にします。(インターフェースのアニメーションとゲームのアニメーション速度が別々になるときなど)

補足として、Unity は unscaledTimeunscaledDeltaTime をサポートしており、多くの状況で独自の時間クラスを持つことは冗長になります。Tips 21 のようなケースの場合はまだ便利です。

旧 Tips 20 と同じ内容だと思います。


21. 更新を必要とするカスタムクラスはグローバルな静的時間にアクセスしないこと

更新を必要とするカスタムクラスはグローバルな静的時間にアクセスするべきではないです。

その代わりに、Update メソッドのパラメーターに delta time をとる必要があります。上の Tips 20 で説明したように、ポーズの機能を実装する場合や、カスタムクラスの動作を高速化したり遅くしたりする場合に、このクラスを使用することができる。


22. WWW を使うときは、共通のやり方にする

サーバー通信の多いゲームでは、数十もの WWW のコールがあるものです。

Unity の用意した WWW クラスを使用する場合でも、プラグインを使用する場合でも、その上にひな形 (boiler plate) になるレイヤーを入れることで便利になります。

通常だと、Call メソッド(Get と Post をひとつずつ)、CallImpl コルーチン、MakeHandler を定義します。基本的に、Call メソッドは Make Handler メソッドを使用して、パーサー・成功時と失敗時のハンドラを構築します。また、CallImpl コルーチンを呼び出し、URL を入れて、呼び出し、完了するまで待機してからスーパーハンドラを呼び出します。

大まかにはこんな感じ:

public void Call<T>(string call, Func<string, T> parser, Action<T> onSuccess, Action<string> onFailure)
{
    var handler = MakeHandler(parser, onSuccess, onFailure);
    StartCoroutine(CallImpl(call, handler));
} 

public IEnumerator CallImpl<T>(string call, Action<T> handler)
{
    var www = new WWW(call);
    yield return www;
    handler(www);
}

public Action<WWW> MakeHandler<T>(Func<string, T> parser, Action<T> onSuccess, Action<string> onFailure)
{
   return (WWW www) =>
   {
      if(NoError(www)) 
      {
         var parsedResult = parser(www.text);
         onSuccess(parsedResult);
      }
      else
      {
         onFailure("error text");
      }
   }
}

これには、いくつかのメリットがあります。

  • 定型的なコードを書く必要がなくなります。
  • 特定のこと(読み込み中の UI コンポーネントの表示や、特定の一般的なエラー処理など)をまとめて処理することができます。

23. 文字数の多いテキストは、ファイルにする

インスペクターで編集するフィールドに入れないでください。

Unity エディターを開かなくても、シーンを保存しなくても、簡単に変更できるようにしておきましょう。

旧 Tips 38 と同じ内容だと思います。


24. ローカライズを計画しているなら、すべての文字列をひとつのところに配置します

このやりかたはたくさんあります。ひとつの方法は、各文字列のpublic string のフィールドを持つ Text クラスを定義して、デフォルトを英語に設定しておくことです。他の言語は、これを基底としたサブクラスにして、対応する言語でフィールドを再設定します。

より洗練されたテクニック(テキストの本文が長い or 多くの言語に対応する)は、スプレッドシートを読み込んで、選択した言語に基づいて正しい文字列を選択するロジックを提供することです。

旧 Tips 39 と同じ内容だと思います。


クラスのデザイン (Class Design)

25. インスペクターで操作できるフィールドの実装方法を決めて、標準化する

フィールドは、public にするか private にして「SerializeField」属性を与えるか2つの方法があります。

後者は「より正しい」方法ですが、便利ではありません。(Unity が普及させた方法でないことは確かです)どちらの方法を選ぶにしても、あなたのチームの開発者が public フィールドをどのように扱うのか知っているように、それを標準としてください。

  • インスペクターで操作できるフィールドが public のとき、そのフィールドは「実行時にデザイナーが変更しても安全であり、コードで値を設定しない」ことを意味します。
  • インスペクターで操作できるフィールドが private だけど SerializeFiled 属性を持つとき、public なフィールドは「コード中でこの変数を変更しても安全だ」という意味です。(なので、あまり多くは表示されないはずで、MonoBehaviourScriptable Object には public フィールドは存在しないはずです)


26. コンポーネントはインスペクターで調整するべきではない変数を決して公開しないこと

そうしないと、そのパラメーターがなにをするのか明確でない場合、デザイナーによって調整されてしまいます。レアケースとして、それが避けることができない場合があります。(たとえば、エディタースクリプトが取得する必要がある場合など)その場合は、HideInInspector 属性を使ってインスペクターの中から隠すことができます。

旧 Tips 30 と同じ内容だと思います。


27. インスペクターの独自 (Propery drawers) を利用して、フィールドをより使いやすくすること

インスペクターの独自 (Propery drawers) は、インスペクターのコントロールをカスタマイズするために使用することができます。

これによって、データの性質にあわせたコントロールを作成したり、特定のセーフガードを設置したりできます。(Range のような範囲を設定するなど)Header 属性を利用してフィールドを整理したり、ToolTips 属性を利用して追加のドキュメント(あんちょこ)を提供します。

例に挙がっているとおり、属性でインスペクターに追加するフィールドをわかりやすくすることだと思います。ただ、単純に属性を追加するカスタムエディターと違う点だけフォローする。


28. カスタムエディターよりも独自の (Propery drawers) を優先すること

独自の (Propery drawers) はフィールドタイプごとに実装されているため、実装の手間が大幅に軽減できます。また、再利用性も高いので、ある型のために一度実装すれば、どのクラスでも再適用できます。

カスタムエディターは MonoBehaviour ごとに実装されるため、再利用性が低く、実装の手間がかかります。


29. デフォルトでは MonoBehaviour を seal すること

一般的には、Unity の MonoBehaviour は継承にフレンドリーではありません:

  • Unity が Start() や Update() などのメソッドを呼び出す方法は、サブクラスからこれらのメソッドを扱うのは面倒です。注意しないと、間違ったものが呼ばれたり、ベースになるメソッドの呼び出しを忘れてしまったりします。
  • カスタムエディターを使う場合、通常はエディターの継承構造を複製している必要があります。クラスのひとつを拡張したいなら、独自のエディターを提供するか、提供されているものを利用します。

継承が必要な場合は、避けられる場合は Unity のメソッド(Start や Update)を提供しないようにしてください。もしも、それらを提供するなら仮想化してはいけません。必要であれば、メソッドから呼び出される空の virtual 関数を定義して、子クラスが override して追加の作業をすること。

public class MyBaseClass
{
   public sealed void Update()
   {
      CustomUpdate();
   }

   virtual public void CustomUpdate(){};
}

public class Child : MyBaseClass
{
   override public void CustomUpdate()
   {

   }
}

これによって、誤って override することを防ぎますが、それでも Unity のメッセージにフックされることがある。このパターンがよくと思わない理由のひとつは、物事の順序が問題になることです。上の例では、クラスが自身の更新をした後に、子クラスで更新があるかもしれません。

最後は、整合性がとれないという指摘だと思います。


30. インターフェースをゲームロジックから分離すること

インターフェースのコンポーネントは、通常だと使用されるゲームについて何も知らないはずです。
認識するために必要なデータを与え、イベントを購読して、ユーザーがそれらと相互に作用したときにわかるようにしておきます。

インターフェースのコンポーネントは、ゲームロジックを行うべきではないです。入力をフィルタリングして有効であることを確認することはできるけど、主なルールチェックは別のところですべきです。

多くのパズルゲームでは、コマはインターフェースの延長線上にあって、ルールを含むべきではありません。(例えば、チェスの駒は駒自体が次の手を計算してはいけない)

同様に、入力はその入力に基づいて動作するロジックから切り離されるべきです。入力コントローラーを使用して、手を動かす意図をだけをアクターに通知します。(コントローラーの操作自体ではない)

ユーザーがリストの中から武器を選択する UI コンポーネントを例にします。これらのクラスがゲームについて知っていることは Weapon クラスだけです。(Weapon クラスは、このコンテナに表示するデータ自身です)逆にゲームはコンテナーについて、何も知りません。

public WeaponSelector : MonoBehaviour
{
   public event Action OnWeaponSelect {add; remove; } 

   public void OnInit(List  weapons)
   {
      foreach(var weapon in weapons)
      {

          var button = ... //Instantiates a child button and add it to the hierarchy

          buttonOnInit(weapon, () => OnSelect(weapon)); 
          // child button displays the option, 
          // and sends a click-back to this component
      }
   }
   public void OnSelect(Weapon weapon)
  {
      if(OnWepaonSelect != null) OnWeponSelect(weapon);
   }
}

public class WeaponButton : MonoBehaviour
{
    private Action<> onClick;

    public void OnInit(Weapon weapon, Action onClick)
    {
        ... //set the sprite and text from weapon

        this.onClick = onClick;
    }

    public void OnClick() //Link this method in as the OnClick of the UI Button component
    {
       Assert.IsTrue(onClick != null);  //Should not happen

       onClick();
    }    
}

旧 Tips 31 と同じ内容だと思います。個人的には、GUI クラスをすべてクリアしたとしても、そのゲームはコンパイルできるべきだ、という文言が好きでした。疎結合感が伝わりやすい。


31. コンフィグ・ステート・記録を分離すること

  • コンフィグに関する変数
    これは、インスペクターで微調整される変数で、プロパティを通してオブジェクトを定義します。たとえば、maxHealth のような値です。
  • ステート(状態)に関する変数
    これは、オブジェクトの現在の状態を決定する変数で、ゲームがセーブをサポートしているならセーブする必要がある変数です。たとえば、currentHealth のような値です。
  • 記録 (Bookkeeping) に関数変数
    これは、スピード・利便性・移り変わる状態のために使用する変数です。それらはステートに関する変数から決定することができます。たとえば、previousHealth のような変数です。

これらのタイプの変数を分離することで、何を変更できるのか、何を保存する必要があるのか、何をネットワーク経由で送受信する必要があるのかを容易に知ることができ、(変数の管理に対して)ある程度の強制力を持つことができるようになります。単純な例を示します。

public class Player
{
   [Serializable]
   public class PlayerConfigurationData
   {
      public float maxHealth;
   }

   [Serializable]
   public class PlayerStateData
   {
      public float health;
   }

   public PlayerConfigurationData configuration;
   private PlayerState stateData;

   //book keeping
   private float previousHealth;

   public float Health
   {
      public get { return stateData.health; }
      private set { stateData.health = value; }
   }
}

旧 Tips 32 と同じ内容かもしれません。サンプルを見る限りだと Bookkeeping は Memento に近いようにも思いました。


32. インデックスで関連づけた public 配列の使用を避けること

たとえば、武器の配列、弾丸の配列、パーティクルの配列を定義してはいけません。

public void SelectWeapon(int index)
{ 
   currentWeaponIndex = index;
   Player.SwitchWeapon(weapons[currentWeapon]);
}

public void Shoot()
{
   Fire(bullets[currentWeapon]);
   FireParticles(particles[currentWeapon]);
}

この場合、コード中でやるというより、インスペクターで間違わないように設定するべきです。いっそのこと、3つの変数をカプセル化したクラスを定義して、それを配列にします。

[Serializable]
public class Weapon
{
   public GameObject prefab;
   public ParticleSystem particles;
   public Bullet bullet;
}

コードがすっきりしているように見えるけど、(大切なのは)インスペクターでデータを設定する際にミスをしづらくなります。

旧 Tips 35 と同じ内容だと思います。


33. シーケンス以外のデータ(連続性のないデータ)に配列を使用することを避けるべき

たとえば、プレイヤーは、3つの攻撃タイプを持っているとします。それぞれ、今に装備している武器を使用するけど、異なる弾を生成して、異なる動作をします。

3つの弾丸を配列に入れて、このようなロジックを使いたくなるかもしれませんが、避けるべきです。

public void FireAttack()
{
   /// behaviour
   Fire(bullets[0]);
}

public void IceAttack()
{
   /// behaviour
   Fire(bullets[1]);
}

public void WindAttack()
{
   /// behaviour
   Fire(bullets[2]);
}

列挙型はコードの見栄えをよくしますが、インスペクターではできません。

public void WindAttack()
{
   /// behaviour
   Fire(bullets[WeaponType.Wind]);
}

別々の変数を使ったほうが、どの内容を入れるのかを示すのに役立つように、名前は別の変数を使ったほうがよいでしょう。クラスを使ってすっきりさせましょう。

[Serializable]
public class Bullets
{
   public Bullet fireBullet;
   public Bullet iceBullet;
   public Bullet windBullet;
}

補足すると、これは3つの属性(火・氷・風)以外のデータがないことを前提にしています。(拡張性に留意)

旧 Tips 36 の内容と同じだと思います。


34. インスペクターの中をすっきりさせるために、Serializable なクラスにしてデータをグループ化すること

いくつかのエンティティに、微調節可能な変数がたくさんあるとき、インスペクターで適切な変数を調節するのはひどい悪夢みたいな作業になるかもしれません。これを簡単にするためには、次の手順に従ってみてください:

  • 変数のグループに対して個別のクラスを定義する
  • それらを public なクラスにして、Serializable 属性を付与する
  • プライマリークラス(項1,2のクラスを含むクラス)では、定義されたそれぞれのクラスを public 変数で定義する
  • これらの変数はシリアライズ可能なので Awake() や Start() で初期化しない
  • (コード中の)定義に値を代入することで、これまでと同じようにデフォルト値を指定することができる

これによってインスペクター内で折りたたみができるようになるので、変数のグループを扱いやすくなる。

[Serializable]
public class MovementProperties //Not a MonoBehaviour!
{
   public float movementSpeed;
   public float turnSpeed = 1; //default provided
}

public class HealthProperties //Not a MonoBehaviour!
{
   public float maxHealth;
   public float regenerationRate;
}

public class Player : MonoBehaviour
{
   public MovementProperties movementProeprties;
   public HealthPorperties healthProeprties;
}

旧 Tips 37 と同じ内容だと思います。


35. public フィールドを使用しておらず MonoBehaviour ではないクラスはシリアライズ可能なクラスにすること

インスペクターがデバッグモードになっているときに、インスペクターでクラスのフィールドを確認することができます。これは入れ子になっているクラス (private or public) でも動作します。


36. インスペクターの微調整できる変数は、コード内で変更を加えないようにすること

インスペクターで調整可能な変数は設定変数であるため、実行時定数として扱われ、状態に関する変数として二重に扱うべきではないです。

このルールに従うことで、コンポーネントの状態を初期状態にリセットするメソッドが書きやすくなり、変数はなにをするのか明確になります。

public class Actor : MonoBehaviour
{
   public float initialHealth = 100;

   private float currentHealth;

   public void Start()
   {
      ResetState();
   }   

   private void Respawn()
   {
      ResetState();
   } 

   private void ResetState()
   {
      currentHealth = initialHealth;
   }
}



パターン (Patterns)

パターンとは、頻繁に発生する問題を標準的な方法で解決する方法のことです。

Bob Nystrom氏の著書「Game Programming Patterns」(オンライン上で無料で読める)は、ゲームプログラミングで発生する問題にパータンがどのように適用されるのかを知るために便利な資料です。

Unity もこれらのパターンの多くを使っています。Instantiate() は prototype パターンの一例だし、MonoBehaviour は template パターンに従っているし、UI とアニメーションは observer パターンを使っているし、新しいアニメーションのエンジンは state machine パターンを使用しています。

これら(ここで)の Tips は、特に Unity で使用するパターンを紹介します。


37. 便利に singleton を使うこと

つぎのクラスは、継承しているクラスを自動的に singleton にします。

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
   protected static T instance;

   //Returns the instance of this singleton.
   public static T Instance
   {
      get
      {
         if(instance == null)
         {
            instance = (T) FindObjectOfType(typeof(T));

            if (instance == null)
            {
               Debug.LogError("An instance of " + typeof(T) + 
                  " is needed in the scene, but there is none.");
            }
         }

         return instance;
      }
   }
}

singleton は ParticleManager、AudioManager、GUIManager などのようなマネージャー(クラス)のために役にたちます。

多くのプログラマーは、クラスの名前を XManager のように曖昧なものにすることに対して警告していますが、それは名前のつけ方が悪かったか、関連性のないタスクが多すぎるクラスになっていることを示しています。一般的には、私もこれに同意します。しかし、どんなゲームにもちょっとはマネージャークラスが存在していて、どんなゲームでも同じことをしていて、これらのクラスは実際のところイディオムです。

  • マネージャーではない Prefab のユニークなインスタンスに singleton を利用することは避けてください。(Player など)
    この原則に従わないと、継承が複雑になって、変更が難しくなります。マネージャーで管理したいなら、GameManager のようなもの(適切なマネージャークラス)にこれらの参照を追加してください。
  • クラスの外から頻繁に使用されるように(しやすいように)、static プロパティ、メソッドを定義します。こうすることで、GameMAnager.Instance.Player とする代わりに GameManager.Player と書くことができる。

他の Tips で説明したように、singleton は、オブジェクトのデフォルト生成位置を作成したり、シーンやロードの間を超えて永続しているオブジェクトを持つのにも便利です。

旧 Tips 29 と同じ内容だと思います。


38. state machine(パターン)を使用して、複数の振る舞い (state) をつくり、状態遷移をしてコードを実行すること

簡易な state machine はいくつかの state を持ち、それぞれの state に対して、その state に入る・存在しているときに実行・更新するアクションが決まります。

これによって、コードを綺麗な状態にして、エラーを発生しづらくすることができます。state machine の恩恵を受けることができる兆候は、Update() メソッドのコードに if 文や switch 文があってそれらがなにかを変更する場合や、hasShownGameOverMessage のような変数がある場合です。

public void Update()
{
   if(health <= 0)
   {
      if(!hasShownGameOverMessage) 
      {
         ShowGameOverMessage();
         hasShownGameOverMessage = true; //Respawning resets this to false
      }
   }
   else
   {
      HandleInput();
   }
}

state の数が増えてくると、この問題は非常にやっかいになります。state machine パターンを適用することで、よりすっきりしたコードにすることができます。


39. UnityEvent 型のフィールドを使用して、インスペクターに observer パターンを使えるようにしておくこと

UnityEvent クラスを使用すると、インスペクターで、ボタンのイベントと同じ UI インターフェースを使用して、(最大4つの引数を使える)メソッドをリンクすることができます。この機能は、特に入力を扱う場合に便利です。


40. observer パターンを使って、フィールドの値が変化したタイミングを検出する

変数の値が変化したときだけ、コードを実行したいというケースは、ゲームの中で頻繁に発生します。この問題の一般的な解決策として、値が変わるたびにイベント登録できる汎用クラスを準備します。

ここでは health を例に示します。以下のようになります:

/*ObservedValue*/ health = new ObservedValue(100);
health.OnValueChanged += () => { if(health.Value <= 0) Die(); };

これで、例えばこんな感じで、チェックする場所ごとにやらなくても、どこでもチェックできるように(通知してくれるように)なりました。

if(hit) health.Value -= 10;

health の値が0以下になれば Die() メソッドが呼び出されます。これについて、さらなる議論・実装等については、こちらの「記事」を参照してください。

これも、個人的に気になったので訳をした「記事」をあげました。


41. Prefab に actor パターンを使用する

このパターンは標準的なパターンではありません。基本的な考え方は、Kieran Load の「プレゼンテーション」からのものになります。

actor とは、Prefab のメインコンポーネントであり、通常は Prefab の「アイデンティティ」を決めるコンポーネントであり、上位レベルのコードが最も頻繁に相互作用するものになります。actor は、同じオブジェクト(子オブジェクト上)から他のコンポーネントに「ヘルパー」を使って作用します。

Unity でメニューからボタンオブジェクトを作成すると、Sprite と Button コンポーネントを持つ Game Object が生成されます。(Text コンポーネントを持つ子要素も生成されます)この場合、ボタンは actor コンポーネントです。

同様に、メインカメラも通常は Camera コンポーネントが付属しているだけではなくて、いくつかのコンポーネント(GUI and Flare layer、Audio Listener)が付属しています。Camera も actor です。

actor が正しく動作するためには、他のコンポーネントが必要になる場合があります。actor コンポーネントに以下の属性を与えることで、Prefab をより堅牢で有用なものにすることができます。

  • RequiredComponent を使用して、actor が同じゲームオブジェクト上で必要とするすべてのコンポーネントを指定します。(これによって、actor は常に安全に GetComponent を呼び出すことができ、返された値が null かどうかをチェックしなくてもよくなります)
  • 複数の同じコンポーネントがアタッチされるのを防ぐためには、DisallowMultipleComponent を使用します。これによって、同じコンポーネントが複数存在しないことになるので、actor は常に GetComponent を呼び出すことができます。
  • actor オブジェクトに子要素がある場合は、SelectionBase を使用します。これによって、シーンビューでの選択がやりやすくなります。
[RequiredComponent(typeof(HelperComponent))]
[DisallowMultipleComponent]
[SelectionBase]
public class Actor : MonoBehaviour
{
   ...//
}


42. ランダムなものとパターンのあるデータストリームには generator を使用すること

これは標準的なパターンではないけれど、非常に有用であることがわかりました。

generator は random generator に似ています。これは、Next() メソッドを持つオブジェクトで、特定の方の新しいアイテムを取得するために呼び出すことができます。

generator はそのアイテムを構築する間に操作して、多様なパターン、異なるタイプのアイテムとして生成することができます。

generator は、新しいアイテムを生成するロジックをアイテムが必要な場所と別のところに(generator 自身のロジックとして)配置することができるので、コードをよりすっきりさせることができるので、便利です。

いくつかの例を挙げます:

var generator = Generator
   .RamdomUniformInt(500)
   .Select(x => 2*x); //Generates random even numbers between 0 and 998

var generator = Generator
   .RandomUniformInt(1000)
   .Where(n => n % 2 == 0); //Same as above

var generator = Generator
    .Iterate(0, 0, (m, n) => m + n); //Fibonacci numbers

var generator = Generator
   .RandomUniformInt(2)
   .Select(n => 2*n - 1)
   .Aggregate((m, n) => m + n); //Random walk using steps of 1 or -1 one randomly

var generator = Generator
   .Iterate(0, Generator.RandomUniformInt(4), (m, n) => m + n - 1)
   .Where(n >= 0); //A random sequence that increases on average

私たちは、障害物の生成、背景色の変更、手続き的な音楽、単語ゲームの単語の文字列生成などに generator を使用してきました。また、generator の機能は、一定ではない間隔で繰り返すコルーチンを制御することもうまく機能します。

while (true)
{
   //Do stuff

   yield return new WaitForSeconds(timeIntervalGenerator.Next());
}

generator について詳しく知りたいなら、これちらの「記事」を確認ください。



Prefab と Scriptable object

43. すべてのものに Prefab を使うこと

シーンの中で Prefab(または Prefab の一部)であってはならないゲームオブジェクトはフォルダーだけです。

一度しか使用されないユニークなオブジェクトであっても、Prefab にするべきです。これによって、シーンを変更せずに(オブジェクトの)変更を簡単にすることができます。

旧 Tips 16 と同じ内容だと思います。


44. Prefab を Prefab にリンクし、インスタンスをインスタンスにリンクしないこと

Prefab へのリンクは、Prefab をシーンにドロップしたときに維持されます。インスタンスへのリンクは維持されません。シーンへの設定を減らして、Prefab にリンクさせることで、シーンを変更する必要を減らすことができます。

可能な限り、インスタンス間のリンクは自動的に確立します。もし、インスタンス間にリンクの必要がないなら、プログラム(コーディング)からリンクを確立します。たとえば、Player の Prefab は GameManager が起動したときに、自身を登録したり、インスタンスを見つけて登録すること。

旧 Tips 18 と同じ内容だと思います。


45. なにかスクリプトを追加したいとき、Prefab のルートにメッシュを配置しないこと

メッシュから Prefab を作るときは、最初にメッシュを空の game object の親として、それをルートにします。

スクリプトはメッシュのノードではなく、ルートに配置します。そうすれば、インスペクターで設定を見失うことなく、メッシュを別のメッシュに置換することもやりやすくなります。

旧 Tips 19 と同じ内容だと思います。


46. 共有するコンフィグ(設定)データには、Prefab の代わりに Scriptable object を使用すること

そうするとこうなります:

  • シーンが小さくなる
  • (Prefab のインスタンス上の)ひとつのシーンに対して誤って変更を加えることができない


47. レベルを表すデータには、 ScriptableObject を使用すること

レベルを表すデータには、XML や JSON で保存されることが多いですが、代わりに ScriptableObject を利用すると、いくつかの利点があります:

  • エディターで編集できる。これによって、データの検証が楽になって、技術的な知識のないデザイナーにもフレンドリーになる。さらに、カスタムエディターを使っておくことで、編集がもっと楽になる。
  • データの read/write や解析を気にする必要がなくなる。
  • 分割したり、ネストしたり、結果として得られる Assets の管理が楽になるので、大きなコンフィグからレベルを構成するのではなく、積み木 (building block) からレベルを構成します。


48. ScriptableObject を利用して、インスペクターの中で振る舞いを設定すること

Scriptable Object は通常、データを設定することに使いますが、「メソッド」をデータのようにして設定することもできます。

Enemy という型があって、それぞれの Enemy がいくつも SuperPower というデータを持っているシナリオを検討してみます。

これらの基礎になるクラスを作成して、Enemy クラスの中にそれらのデータをリストに持たせることができます……が、カスタムエディターでなければ、異なる SuperPower(それぞれ独自のプロパティを持つ)リストをインスペクターの中で設定することができません。

しかし、SuperPower をアセット(ScriptableObject として実装すれば)、(設定を)できるようになります。

こんな感じになります:

public class Enemy : MonoBehaviour
{
   public SuperPower superPowers;

   public UseRandomPower()
   {
       superPowers.RandomItem().UsePower(this);
   }
}

public class BasePower : ScriptableObject
{
   virtual void UsePower(Enemy self)
   {
   }
}

[CreateAssetMenu("BlowFire", "Blow Fire")
public class BlowFire : SuperPower
{
   public strength;
   override public void UsePower(Enemy self)
   {
      ///program blowing fire here
   }
}

このパターンに従う際は、いくつか注意するべきことがあります:

  • ScriptableObject を抽象化することはできません。代わりに、具体的な base クラスを使って、抽象化するべきメソッドは NotImplementedException の例外を投げるようにします。また、abstract を定義して、抽象化するべきクラスやメソッドにマークをつけることもできます。
  • ScriptableObject はシリアライズできません。しかし、ジェネリックな base クラスを使用して、ジェネリックを指定したサブクラスのみをすべてシリアライズすることができます。


49. 特殊化した Prefab に ScriptableObject を使用すること

もしも、2つのオブジェクトの構成が一部のプロパティにだけ違いがあるなら、シーンの中に2つのインスタンスを配置して、インスタンスの上でそれらのプロパティを調整するのが普通です。

大抵は異なる2つのプロパティを持つオブジェクト同士を、別々の ScriptableObject として分けたほうがよいです。

(そうすると)、柔軟さを得ます:

  • 特殊化したクラスの継承を使用して、型の異なるオブジェクトに特定のプロパティを与えることができます。
  • シーンの設計はより安全になります。(オブジェクトを目的の型にするために、すべてのプロパティを調整しないし、適切な ScriptableObject を選択するだけになります)
  • コードを通じて実行時にこれらのオブジェクトを操作するのが、もっと楽になります。
  • 2つの型のインスタンスを複数持っているなら、変更を加えたときにそれらのプロパティが常に等価(一貫してるもの)であることがわかります。
  • コンフィグの変数のセットを、混ぜたり・合わせたりできるセットに分けることができます。

設定の簡単な例を紹介します:

[CreateAssetMenu("HealthProperties.asset", "Health Properties")]
public class HealthProperties : ScriptableObject
{
   public float maxHealth;
   public float resotrationRate;
}

public class Actor : MonoBehaviour
{
   public HealthProperties healthProperties;
}

特殊化したものの数が多いときは、特殊化したものを通常のクラスとして定義して、そのリストを ScriptableObject の中で用いて、それを取得できる適切な場所(GameManager クラスなど)にリンクをしておくとよいです。

安全に、速く、便利にするためにはもうすこし貼り付ける(馴染ませる)必要があります。以下に小さな例を示します:

public enum ActorType
{
   Vampire, Wherewolf
}

[Serializable]
public class HealthProperties
{
   public ActorType type;
   public float maxHealth;
   public float resotrationRate;
}

[CreateAssetMenu("ActorSpecialization.asset", "Actor Specialization")]
public class ActorSpecialization : ScriptableObject
{
   public List healthProperties;

   public this[ActorType]
   {
       get { return healthProperties.First(p => p.type == type); } //Unsafe version!
   }
}

public class GameManager : Singleton
{
   public ActorSpecialization actorSpecialization;

   ...
}

public class Actor : MonoBehaviour
{
   public ActorType type;
   public float health;

   //Example usage
   public Regenerate()
   {
      health 
         += GameManager.Instance.actorSpecialization[type].resotrationRate;
   }
}

この内容は最近 Unity Blog に載っていたものと近い気がしました。まとめたのが「これ」。インスペクターでの設定機能を強化することを好む Tips が多い印象です。


50. CreateAssetMenu の属性を利用して、自動的に ScriptableObject の作成をするメニューを追加すること

本文はありません。タイトルだけで全部です。Project ビューを右クリックして ScriptableObject を追加できるようにすることだと思います。



デバッグ (Debugging)

51. Unity のデバッグ機能を有効に使う方法を学ぶこと

  • Debug.Log() ステートメントに context object を追加しておけば、どこから(ログが)生成されたのかを確認することができます。
  • Debug.Break() を使用すればエディターからゲームを一時停止することができます。(たとえば、エラーになる状態が発生したとき、エラーが発生したフレームにおけるコンポーネントのプロパティを調べたりする際に便利です)
  • ビジュアルのよいデバッグには、Debug.DrawRay()Debug.DrawLine() 関数を使用します。(たとえば、Debug.DrawRay() は、レイキャストがヒットしない原因をデバッグする際にとても便利です)
  • ビジュアルのよいデバッグのために、Gizmos を利用します。DrawGizmo の属性を付与すると、MonoBerhaviour の他に Gizmo の機能による(デバッグ画面への)描画を提供することもできます。
  • インスペクターの画面にあるデバッグ機能を使用します(インスペクターを使用すると、Unity 実行時に private なフィールドの値を確認することができます)


52. IDE にあるデバッガー機能を有効に使う方法を学ぶこと

Debugging Unity games in Visual Studio」のサンプルを確認してください。


53. 時間の経過で変わる値のグラフを描画する知覚的なデバッガーを使用すること

これは、物理・アニメーション・その他の動的なプロセス・特に散発的な不具合のデバッグにとても役立ちます。グラフの中に不具合を見て取ることができるし、他の変数がどのように変化しているのか確認することができます。

また、目視によるチェックでは、よく変化する値や、原因がわからないけど値が狂う値など、いくつかのタイプの奇妙な挙動をするバグも明らかになります。

私たちは、Monitor Component を利用していますが、他にもいろいろあります。


54. 改善されたコンソールロギングを使用すること

カテゴリーに応じて出力するログを色分けしておけば、このカテゴリーに応じて出力するログをフィルタリングできるエディター拡張機能を使用します。

私たちは、Editor Console Pro を使用していますが、これも他にもいろいろあります。


55. Unity のテストツールを使用して、特にアルゴリズムや数学を使うコードをテストすること

これの詳細は、「Unity Test Tools(リンク切れ)」のチュートリアルや「Unit testing at the speed of light with Unity Test Tools」を参照してください。


56. Unity のテストツールを使用して、「スクラッチパッド」のテストをすること

Unity のテストツールは、フォーマルなテストに適当なだけではありません。

シーンを実行していなくても、エディターで走らせることができる便利なスクラッチパッドのテストにも利用することができます。

正直なところ、私はこれが何を指摘しているのかよくわかりませんでした。「scratchpad」という名詞が強調されていますが、よくわかりません。unrealengine にも同じ名前のパネルがあるみたいです。意味的にはメモ用紙のつづり紙のような、ちょっとしたものくらいことかとも思いました。


57. スクリーンショットを撮影するためのショートカットを実装すること

多くのバグは目に見えるものなので、画像を撮影できるようにすれば報告しやすくなります。理想的なシステムは、連続したスクリーンショットが上書きされないように、Prefab の中でカウンターを維持しておくことです。

また、誤ってリポジトリーに(画像を)コミットしてしまわないように、画像はプロジェクトフォルダーの外に保存しておくこと。

旧 TIps 43 と同じ内容だと思います。


58. 重要な変数のスナップショットを記録するためのショートカットを実装すること

ゲーム中になにか予期しないことが起こったとき、チェックできる(に使える)情報を簡単に記録することができる。もちろんだけど、どの変数もゲームに依存しています。(なので)あなたは、あなたのゲームで発生するバグに引き回されます。たとえば、プレイヤーと敵の位置、AI のロジックなどが挙げられます。

これは、画像を記録するという意味ではないと思います。


59. テストを簡単にするためのデバッグオプションを実装すること

いくつか例を挙げると:

  • すべてのアイテムをアンロックする
  • 敵の(出現など)を無効化する
  • GUI を無効化する
  • プレイヤーを無敵にする
  • すべてのゲーム動作を無効化する

これらのデバッグオプションを誤ってコミットしないように注意してください。デバッグオプションの変更によって、他チームの開発者を混乱させる恐れがあります。

旧 Tips 45, 46 と同じ内容だと思います。RPG のデバッグルームのような話だと思います。


60. デバッグ用のショートカットキーの定数を定義して、ひとつのところにまとめておくこと

デバッグに使うキーは、ゲーム入力のあまったところのように、普通だと一か所にありません。

ショートカットはキーとキーの衝突を避けるために、中央的な(一元的な)ところに定数を定義します。別の方法としては、デバッグの関数であるかどうかは関係なく、すべてのキーを一か所で処理することになります。(欠点は、このクラスのためにオブジェクトへの余分な参照が必要になるかもしれません)


61. Procredural Mesh を生成するときに、小さな sphere を頂点して描画・スポーンすること

これは triangle や UV をいじってメッシュを表示するまえに、メッシュが正しいサイズであること、正しいと想定したところにあることを確認するための役に立ちます。

これもよくわかりませんでした。なので、変な訳になっている恐れがあります。



パフォーマンス (Performance)

62. パフォーマンス上の理由からの設計・デザインに関する一般的なアドバイスには注意すること

  • このようなアドバイスには、神話であったりテストに裏打ちされていません。
  • 時にはテストに裏打ちされているかもしれないけれど、そのテストには欠陥があると思っておきましょう。
  • 時にはアドバイスは正しいテストに裏打ちされているかもしれないけれど、それは非現実的なものであったり、異なるコンテキストかもしれないです。(たとえば、list より array を使うほうが高速であることを示すのは簡単です。しかし、実際のゲームの中では、ほとんどの場合この違いはごく僅かなことです。同じように、テストをするときターゲットのデバイスと異なるものでやっているなら、その結果はあなたにとって意味のあるものではないかもしれないです)
  • アドバイスは正しい(かった)が、時代遅れかもしれないです。
  • 時にはそのアドバイスが適用することもあります。しかし、トレードオフがあります。完成した低速な(パフォーマンスが十分ではない)ゲームは、完成しなかったパフォーマンスのよいゲームよりもよいものでしょう。また、いきすぎた最適化は、完成を遅らせる可能性が高いトリッキーなコードを含んでいるかもしれません。

パフォーマンスのアドバイスは、以下にアウトラインしたプロセスを使って、実際の問題の発生源をより速く見つけるための手助けになります。


63. 開発の初期段階からターゲットとなるデバイスで定期的にテストすること

デバイスによってパフォーマンスの特徴は大きく異なるので、驚かないように。問題は早くに知れば知るほど、より効率的に問題に対処することができます。


64. プロファイラーを効果的に使用することで、パフォーマンスの問題の原因を調べる方法を学ぶこと

  • もしプロファイリングに初めてさわるなら、「Introduction to the Profiler」を参照してください。
  • 独自にフレームを定義する方法(Profiler.BeginFrameProfiler.EndFrame を使う)を習得して、詳細な分析をする方法を説明します。
  • iOS 用のビルドイン プロファイラーなど、プラットフォーム固有のプロファイリングの使用方法を学びます。
  • ビルトインプレーヤーの「profile to file(リンク切れ)」とプロファイラーの「display the data(リンク切れ)」について学びます。


65. より正確なプロファイリングをするために、必要に応じてカスタムプロファイラを利用すること

時には Unity のプロファイラはなにが起こっているのかをわかりやすく表示することができないことがあります。プロファイルのフレームが不足していたり、詳細プロファイル (Deep Profile) によってゲームの速度が低下してテストが意味をなくすことがあります。

私たちはこのことに対して、独自のプロファイラーを使用しますが、Asset Store で他のプロファイラを見つけることができるはずです。


66. パフォーマンスを強化する影響を測定すること

パフォーマンスを上げるための変更をおこなったときは、本当に改善されたかどうかを確認するために測定します。もしも、測定できなかったり、測定に漏れがあった場合は元に戻します。


67. パフォーマンスのために読みにくいコードを書かないこと

もしそうしたコードを書くときは:

  • プログラムに問題があるなら、プロファイラでソースを特定し、変更のあとに改善点を測定して、その改善のメリットが保守性の損失と比較して十分だと判断した場合
  • 自分で自分がなにをしているのか分かっている場合



命名規則とフォルダーの構造

68. 文書化した命名規則とフォルダー構造に従うこと

一貫性のある命名規則とフォルダー構造は、データを見つけやすく、わかりやすくします。

独自の命名規則とフォルダー構造を作りたいと思っているだろうけど、一例は以下のようになります。


一般的な規則

  1. (命名する)モノが「なに」であるかを呼んでみる。単に鳥であったなら「bird」とするべきです。
  2. 発音できて、覚えることができる名前を選ぶ。もし、あなたがマヤ族のゲームを作るなら、あなたは「Quetzalcoatl(ケツァルコアトル) is Return」みたいな名前はやめよう。(発音できない、覚えれない)
  3. 一貫性があること。(なんらかの)名前を選択したとき、その名前を一貫してつかう。どこかで ButtonHolder としたら、別のところで ButtonContainer とするのはやめましょう。
  4. Pascal ケースの命名規則を適用すること。たとえば、ComplicatedVerySpecificObject のようにします。 (space), _, - は使用しないこと。(例外:最後に書いてある「オブジェクトに様子(アスペクト)の名前を付け加える」を参照しておく)
  5. バージョン番号や進行状況を表すような単語を使わないこと。(WIP, Final とか)
  6. 略語にしないこと。(DVamp@W は、DarkVampire@Walk とするべきです)
  7. デザインのドキュメントにある用語を使用すること。もしも、ドキュメントに死ぬアニメーションを「Die」としているなら、DarkVampire@Death ではなくて DarkVampire@Die とすること。
  8. 名前の語句は(左並びを)ふつうに保つこと。VampireDark ではなく DarkVampire がいい。ButtonPaused ではなく PauseButton がいい。たとえば、すべてのボタンが Button で始まらないほうが、インスペクターで一時停止を見つけやすくなります。逆の並びを好むのは視覚的にわかりやすいからだけど、名前はグループ化するためのものではないです。(フォルダーはそのためのものだけど)名前は、同じ種類のオブジェクトを区別して、それらを確実かつ迅速に見つけることができるようにするためのものです。
  9. 名前の中には、連続性を構成するものがあります。これらの名前には、PathNode0, PathNode1 のように数字を使用すること。必ずこれは1からではなく、0から開始すること。
  10. 連続性のないものには数字を使用しないこと。たとえば、Bird0, Bird1, Bird2 は、 Flamingo, Eagle, Swallow のようにします。
  11. 一時的なオブジェクトの名前の前に二重のアンダースコア __ をつけること。(例:__Player_Backup)


オブジェクトに様子(アスペクト)の名前を付け加える (Naming Different Aspects of the Same Thing)

オブジェクトの主要な「なに」を表す(コアな名前)と様子(アスペクト)を記述もるものの間には、アンダースコア _ を使うこと。たとえば、このような感じです:

  • GUI ボタンの状態:EnterButton_Active, EnterButton_InActive
  • テクスチャー:DarkVampire_Diffuse, DarkVampire_Normalmap
  • スカイボックス: JungleSky_Top, JungleSky_North
  • LOD グループ: DarkVampire_LOD0, DarkVampire_LOD1

この規則はアイテムの種類が異なるものを区別するために使用しないこと。たとえば、Rock_Small と Rock_Large は SmallRock と LargeRock にすべきです。


構造 (Structure)

あなたのシーン・プロジェクトフォルダー・スクリプトフォルダーの構成は、似たような構成にするべきです。ここでは、あなたが(こうした構成を)はじめるためのいくつかの簡単な例を挙げます。


フォルダーの構成

MyGame
   Helper
      Design 
      Scratchpad
   Materials
   Meshes
      Actors
         DarkVampire
         LightVampire
         ...
      Structures
         Buildings
         ...
      Props
         Plants
         ...
      ...
   Resources
      Actors
      Items
      ...
   Prefabs
      Actors
      Items
      ...
   Scenes
      Menus
      Levels
   Scripts
   Tests
   Textures
      UI
      Effects
      ...
   UI 
MyLibray
   ...
Plugins
SomeOtherAsset1
SomeOtherAsset2
...


シーンの構成

Main
Debug
Managers 
Cameras
Lights
UI
   Canvas
      HUD
      PauseMenu
      ...
World
   Ground
   Props
   Structures
   ...
Gameplay
   Actors
   Items
   ...
Dynamic Objects
Scripts Folder Structure
Debug
Gameplay
   Actors
   Items
   ...
Framework
Graphics
UI
...

旧 Tips 50 と同じ内容だと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python + Unity 強化学習環境構築

PythonでML-Agentを動かすための環境構築

使用環境

Python 3.7.6
mlagents 0.17.0
ML-Agents Release3
Unity 2019.2.21f1
Pyenv
Poetry

前提条件

Unity Hubのダウンロード

1.Unity ML-Agentsのインストール

(1)以下のサイトからUnity ML-Agents Release3をインストールする
https://github.com/Unity-Technologies/ml-agents

スクリーンショット 2020-09-14 130014.png
スクリーンショット 2020-09-14 130030.png

(2)コピーしたURLをGit cloneコマンドの後ろに付けて実行 ファイル一式をダウンロードできる
(※git clone する前に作業をするディレクトリを作成!)

terminal
mkdir [任意のディレクトリ名]
cd [ディレクトリ名]
git clone [コピーしたURL]

(3)ml-agentsディレクトリが作成され、中に以下のようなファイルが入っていることを確認!
スクリーンショット 2020-09-14 131106.png

2.Python3.7.6の仮想環境の構築!

(1)pyenvをインストール!

terminal
brew install pyenv

(2)Python3.7.6のインストール

terminal
pyenv install 3.7.6

(3)このディレクトリはPython3.7.6で実行することを設定

terminal
pyenv local 3.7.6

(4)Pythonの管理パッケージPoetryをダウンロード&インストール

terminal
curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python

(5)Poetryの仮想環境準備
設定を確認
スクリーンショット 2020-09-14 134557.png
プロジェクト内に仮想環境が作られるように設定し、再び設定状態を確認

terminal
poetry config virtualenvs.in-project true

(6)Python3.7.6で仮想環境構築
スクリーンショット 2020-09-14 132533.png

(7)mlagent==0.17.0のインストール!

terminal
poetry add mlagents==0.17.0

(7)mlagents-learnコマンドが実行できることを確認
下のコマンド実行後下の画像のようなものが出てくることを確認

terminal
mlagents-learn

スクリーンショット 2020-09-14 132912.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ThreeDPoseTracker Ver.0.3.0アップデート

はじめに

Top030.png

この記事はUSBカメラモーションキャプチャーThreeDPoseTrackerのバージョン0.3.0のアップデートについての記事です。ThreeDPoseTrackerってナニ?という方は先に

「USBカメラモーションキャプチャーThreeDPoseTrackerの説明」
https://qiita.com/yukihiko_a/items/43d09db5628334789fab
「ThreeDPoseTracker Ver.0.2.0アップデート」
https://qiita.com/yukihiko_a/items/e5b07bd045611c73bbbe

をご覧ください。
そのうち記事を一つにまとめます。
この記事ではとりあえず変更点だけ記載しておきます。

v0.3.0について

ダウンロード先

下記URLからダウンロードしてください。
https://digital-standard.com/threedpose/ThreeDPoseTracker_Win_x64_v0_3_0.zip

更新内容

今回の主な更新内容です。今回は前後移動のバグ修正が主たる目的のリリースです。

  • 前バージョンでは、実質的に前後にほとんど移動しないバグがありました。これを修正しました。今回のリリースはこの対応がメインになります。
  • リップシンク、自動瞬き(オートブリンク)の追加
  • 手首の固定機能の追加
  • カメラのコントロール機能の追加・変更
  • 背景に部屋の表示を追加
  • High Quality Modelの精度向上(数字上は上がってる筈ですが、見た目ではわかりません)

設定画面

リップシンク、自動瞬き機能用にLipSyncタブ、背景の部屋の設定用にRoomタブを追加しました。

LipSync

ここではリップシンク、自動瞬き(オートブリンク)の設定を行います。VRMのBlendShapeが無いアバターだと動きません。

LipSync.png

  • Use Lip Sync
    リップシンク機能をオンオフを設定します。

  • マイク選択
    使用できるマイクのリストが表示されます。使用するマイクを選択します。前回使用したマイクを覚えていますが、見つからない場合は始めに見つかったマイクを使用します。

  • Smooth Amount
    動きのスムーズさ、0~100で設定します。

  • Sensitivity
    感度を設定します。0~2の値です。

  • Use Auto Blink
    自動瞬きを設定します。

  • Time Blink
    瞬きしている時間を設定します。

  • Threshold
    次の瞬きを行うかの乱数の閾値。0~1で設定します。小さい程瞬きしやすくなります。1だと瞬きしなくなります。

  • Interval
    次の瞬きまでの時間です。指定した時間を中心にランダムで瞬きします。

Room

背景にglTFデータの部屋「The Charterhouse Great Chamber」の表示の設定をします。
RoomImg.png
このデータは、
The Charterhouse Great Chamber
David Fletcher (@artfletch)さん
のデータを使用させて頂いています。

Room.png

  • Show Room
    背景に部屋を表示するかどうかを設定します。

  • Position、Rotation、Scale
    部屋の表示位置を調整します

  • Receive Shadow
    床に影を表示するかを設定します。

  • Use Ground IK
    床への足付きを設定します。テストコードなので使わないでください。使用するアバターを選択後この機能をオンにすると機能するかも知れません。アバターを切り替えると動作がおかしくなることがあります。テストコードなので、起動時にオンになる事はありません。

  • IK Position weight
    IKのききを設定します。0.2~0.3程度で十分です。

  • Leg Position weight
    基本、0のままでいいです。

  • Height Offset
    足の高さを設定します。靴底の厚みと考えるとわかりやすです。

Predict

Predict.png

  • Lock Hand 手首を固定する機能を追加しました。オンにすると固定されます。常時オンにするよりも、アバターのロードが終わってTポーズになってからオンにした方がよさそうです。

カメラコントロール

メニューの「Close Menu」でメニューを非表示にするとマウスとキーボードでカメラをコントロールすることができます。
非表示にしたメニューは、「ESC」、「スペース」キーで再表示されます。これらの機能は以前から少しはありましたが、今回のタイミングで整理しました。

Key 動作
1 デフォルトのカメラ位置
2 アバター左側からのアップ
3 アバター右側からのアップ
4 正面からのアップ
5 左回りに自動で回転・停止
6 右回りに自動で回転・停止
w 前進
s 後進
a 左に移動
d 右に移動
z 右に回転
c 右に回転
q 下に移動
e 上に移動
p アバター変更
左クリック アバター中心に回転
右クリック カメラ中心に回転
ホイール回転 前後移動
ホイールクリック 平行移動

最後に

今回は臨時のリリースだったのであまり大きな機能追加はありません。そうは言っても、今後もそんなに大きな追加をする予定もないですが。とりあえず今回は一部屋固定でglTFデータを使用しましたが、自分としては読み込み機能が欲しいのと、PC上で別のアプリの画面のキャプチャ機能とくにパワポの画面の取り込み機能を追加したいと思ってます。これは、「2020/08/31 xR Developers Community Conference」の登壇で使用した機能ですが、突貫でこの登壇専用に作った物なのでもっと汎用的に使いたいなと、いう事で次のリリースは製作者の私用の機能になる予定です。ちなみに登壇で使用したglTFデータと今回のお部屋とは違うデータです。
LT20200831.png

以下は前回のコピーです。
ちなみにこのアプリのソースは下記のリポジトリにあります(準備中)。コードのライセンスはGithubに書いてあるライセンスに従ってください。姿勢推定でのお仕事もしているので全て無料とはいかないのです。機能追加して商用で使いたいという方はライセンスを購入いただくかお仕事の依頼を下さい。どうしても無料で機能追加して商用利用したいという方は、このアプリ版は無料で通すつもりなので、マージの依頼なりプルリクなりを頂ければ、そのうちアプリに追加してリリースするかも知れません(保証はしません)。

https://github.com/digital-standard/ThreeDPoseTracker

Licence

このThreeDPoseTrackerの実行ファイルについては営利・非営利問わずご自由にお使いください。この実行ファイルを使用した配信、モーションデータも同様にご自由に利用可能です。
GithubにあるソースコードについてはGithubにあるライセンスに従ってください。

免責事項

本ソフトウエアの使用に起因する、ソフトウエア、ハードウエア上の事故その他の損害、または使用できなかったことから生じる一切の損害に関して、製作者はいかなる責任も負わず、修正する義務を負いません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Animation Riggingを調べてみた

UnityでIKが実装されたっぽい?

UnityでIKがデフォで使えるよ!って聞いたので、いろいろ調べてみた。

参考資料

題名 イメージ
【Unity】Animation Rigging、人型以外のキャラクターにもIKを!
UnityのAnimation Riggingの使い方とサンプルの作成

わかったこと

※Importボタン無いんだが?って人向けのメモ
一番最初にインポートしようとしたときに、
②のimportのボタンないやんけ!!と思ったけど、
よく見たら①らへん所にInstallボタンっぽいのあって
あほくさってなりました()

まだ試用版

「まだ不安定だよ」っていうメッセージが飛んできたんで、
まぁそうなんだろうなって。
でも使えるならOKです。


文章はここで途切れている.....
どうやら力尽きたようだ。




[2020/9/14 01:13]
嘘です。
後でちゃんと調べて書いておきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity2020にアップデートしてWebGLにビルドしたら動かなくなった

Unity2020にアップデートしてWebGLのプロジェクトをビルドしてS3に上げたらロードが最後までいかなくなってしまった。

どうやらBuildファイルがデフォルトで圧縮されるようになってしまっていた模様。

BuilSettings>Player Settings>Player>PublishingSettingsのCompression FormatをDisableにしてビルドしたら圧縮されずにファイルが出力された。

それをS3にガボッと入れたら動いた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む