20200925のUnityに関する記事は6件です。

Unityでベクターファイル(SVG)を扱う4(画面遷移を作ってみる)

はじめに

前回uGUIのマスクに使用できることがわかったので、今回はベクター画像をマスクとして使用した画面遷移を作ってみます

準備

ヒエラルキーはこんな構造にしておきます
image.png
BGTexture:マスクし、非表示部分となった個所に表示する画像
SVGMask:マスク本体
BeforeScreen:遷移前の画面
AfterScreen:遷移後の画面
Fade:マスク時に同時にかける白フェード

SVG MaskにSVG画像マスクを入れて置き、BeforeScreen画面のアクティブを切って、AfterScreen画面をアクティブにすることで遷移するものとします。
SVG Maskには、SVGImage、Maskコンポーネントを張っておきます
image.png

次に、スクリプト側からアニメーションを設定します。
_maskにSVG MaskのRectTransformを代入しておき、
DoTweenを使用して、少しずつ小さくします
_mask.DOSizeDelta(_loadingMaskSize, 0.8f).SetEase(Ease.OutBack, 0.5f)
ロードが終わったら、少しずつ大きくします
_mask.DOSizeDelta(_nonLoadingSize, 0.8f).SetEase(Ease.InBack, 0.5f)
それに合わせて、フェードやロード中のアニメーションをかけるとこんな感じになります(画像に特に意味はありません)

TogiMemo.gif
これ作るのに休日半日以上つぶしました

SVGImageおよびSVG Mask周りの説明は以上ですが、この遷移について
細かいところ説明すると長くなるので、一応GitHubのリンク張っておきます。
https://github.com/Taka108/unity-svg-test

おわりに

まだ工夫次第で色々できそうですが、まだベータ版なので、触るのはこれくらいにしておきます。早くリリース版が出るといいなぁ。。

参考

校門
https://www.pixiv.net/artworks/12341534

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

Coreで簡易サーバーアプリを作って複数の機器を連携させる

はじめに

設置する機器が複数ある場合など、システムがある程度の規模になった場合、複数のアプリケーションを連携させたくなることがあります。
<例>
・画像をスキャンするアプリケーション
・送信された画像を演出に用いるアプリケーション

同じPCだけでなく、違うPCで連携させたいことがあります。
<例>
・画像撮影用PC
・演出用PC

シンプルなやり方をとりたいので、
.Net Coreでサーバーアプリケーションを作り、通信はhttp(あるいはhttps)を使い、アプリケーション間で連携することにします。

必要なもの

開発環境・実行環境用 共にWindows PCを使用し、
サーバー側PCの実行環境にIIS(Internet Information Services)を使用します。
クライアント機器は、http通信が出来るならばなんでも利用出来そうです。

※多数(具体的には20台より多く)の機器から接続する場合、
サーバーにデスクトップ用のWindowsを使用するとライセンス違反となる場合があるので、その場合はLinux等のOS上に実行環境を構築します。

サーバー側PC

サーバーアプリケーションを配置します。
.Net Coreのランタイム等が必要になります。
https://dotnet.microsoft.com/download/dotnet-core
・開発用に .NET Core SDK
・実行環境用にASP.NET Core Hosting Bundle
をダウンロードし、実行用にIISをインストールします。

クライアント側PC

クライアント側のアプリケーションに、サーバーアプリケーションと通信する機能を実装します。
サーバーアプリを配置しているPCと同じPCでも構いません。

サーバーアプリの作成

Visual Studio上で新しいプロジェクトを作成し、
プロジェクトテンプレートに「ASP .Net Core アプリケーション(C#)」を、
追加のプロジェクトテンプレートに「API」を選択してみます。

以下のクラスを追加します。

SampleItem.cs
    public class SampleItem
    {
        /// <summary>
        /// ID(連番)
        /// </summary>
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public uint ID { get; set; }

        /// <summary>
        /// 名前
        /// </summary>
        [MaxLength(128)]
        public string Name { get; set; }

    (略)
    }


SampleDbContext.cs
public class SampleDbContext: DbContext
{
    public DbSet<SampleItem> Samples { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Filename=ImageDatabase.sqlite");
        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SampleItem>().ToTable("SampleItem", "test");
        base.OnModelCreating(modelBuilder);
    }


また、Startupクラスを以下のように編集します。

Startup.cs
    public class Startup
    {
        /// <summary>
        /// appsettings.jsonなどから読み込んだこのアプリの設定情報
        /// </summary>
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            // appsettings.jsonなどから読み込んだ設定
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<AppSetting>(Configuration);

            // DBコンテキストの作成
            services.AddEntityFrameworkSqlite().AddDbContext<SampleDbContext>();

            // DBのテーブルが無ければ作る。既にある場合はそのまま
            using (var client = new SampleDbContext())
            {
                client.Database.EnsureCreated();
            }
            services.AddControllersWithViews();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            (※省略)
        }
    }


機能の実装

投稿した画像を保存する機能、取得する機能を実装します。

APIController.cs
        [Serializable]
        [DataContract]
        public class ScanImageGetInfo
        {
            [DataMember(Name = "id")]
            public uint ID { get; set; }
            (略)
        }

        [HttpPost]
        public IActionResult PostImage(IFormFile Targetfile, int MachineID, int ImageCodeID)
        {
            if (Targetfile == null)
            {
                return new BadRequestResult(); // HTTPステータスコード 400
            }

            string savePath = SaveFileToWorkFolder(Targetfile, MachineID, ImageCodeID);

            //ファイルをダウンロード
            return new EmptyResult(); // HTTPステータスコード 200
        }

        /// <summary>
        /// 指定したIDの画像データを取得
        /// </summary>
        /// <param name="Id">PopImageで取得したレコードのID</param>
        /// <returns></returns>
        public IActionResult GetImage(int ID)
        {
            using (var context = new SampleDbContext())
            {
                var item = context.ScanImages.FirstOrDefault(x => x.ID == ID);
                if (item == null) return new BadRequestResult();
                filePath = folder + "\\" + item.FilePath;
                fileName = item.Name;
            }

            return File(GetStream(filePath), "image/png", Path.GetFileName(fileName));
        }

        // ファイルをこのPC上に保存する関数
        string SaveFileToWorkFolder(){
                (略)
        }

        // FileStreamを全て読み込み、MemoryStreamとして返す
        private Stream GetStream(String filepath)
        {
            var memory = new MemoryStream();
            using (var stream = new FileStream(filepath, FileMode.Open))
            {
                stream.CopyTo(memory);
            }
            memory.Position = 0;
            return memory;
        }


これで/api/postimageにアクセスするとファイルの保存、/api/getimageにアクセスするとファイルの取得が出来るようになりました。

ファイルの投稿(クライアントスキャナアプリ側)

.Net Frameworkで作成したアプリケーションですが、
以下のように引数を与えてHttpのPostを送信しています。

画像の投稿を行うHttpPostImage()関数
        async public Task<string> HttpPostImage(string imageFilePath, int imageCodeID)
        {
            //File.WriteAllText(System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\log2.txt",
            //    "postimage" + "\n");

            // 送信を行います。
            // POSTする情報
            //WWWForm form = new WWWForm();
            //form.AddField("userId", userId);

            string APIURL = "http://"
                + Properties.Settings.Default.ServerURL
                + ":"
                + Properties.Settings.Default.ServerPort
                + "/API/PostImage";

            using (var stream = File.OpenRead(imageFilePath))
            using (var fileContent = new StreamContent(stream))
            using (var content = new MultipartFormDataContent())
            using (var client = new HttpClient())
            {
                fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data")
                {
                    Name = "Targetfile",
                    FileName = Path.GetFileName(imageFilePath),
                };
                fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");

                content.Add(fileContent);
                content.Add(new StringContent(Properties.Settings.Default.MachineID.ToString()), "MachineID");
                content.Add(new StringContent(imageCodeID.ToString()), "ImageCodeID");

                using (var response = await client.PostAsync(APIURL, content))
                {
                    return await response.Content.ReadAsStringAsync();
                }
            }
        }


ファイルの取得(クライアント演出アプリ側)

Unityのアプリケーションですが、以下のようにしてGetを送信し、テクスチャ画像を取得することが出来ます。

画像の取得を行うコルーチン関数
        IEnumerator AccessCoroutine()
        {
            // Request
            string getImageUri = "http://" + _serverURL + ":" + _serverPort + ServerGetImageAPI;

            // 処理終わるまでループ
            while (isRunning)
            {

                      (略 idに取得したい画像のidを入れる)
                        string queryString = "?id=" + id;
                        // Texture2Dを作成
                        using (UnityWebRequest webGetImageRequest = UnityWebRequestTexture.GetTexture(getImageUri + queryString, true))
                        {
                            yield return webGetImageRequest.SendWebRequest();
                            if (webGetImageRequest.isNetworkError || webGetImageRequest.isHttpError)
                            {
                                Debug.Log(webGetImageRequest.error);
                            }
                            else
                            {
                                // Get downloaded asset bundle
                                Texture2D texture = DownloadHandlerTexture.GetContent(webGetImageRequest);
                                ( テクスチャー画像の処理)
                            }
                        }

                    }
                }
                yield return new WaitForSeconds(1.0f);
            }
        }


これらのプログラムを組み合わせて、PC間で連携したひとつのシステムを作ることが出来ます。

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

Update your C# in Unity ~ 式形式の関数メンバー ~

Unityにおいて古いC#しか使えない時代もありました。しかし、それは過去のことです。本稿執筆時の最新LTSであるUnity 2019.4ではC# 7.3がサポートされています。また、本稿執筆時の最新Beta版であるUnity 2020.2ではC# 8.0がサポート予定です。

長らくUnityで古いC#しか使えなかったことで、「C#にこんな機能あるのか?知らなかった!」となることがある方も多いのではないでしょうか?この「Update your C# in Unity」シリーズでは、「C#の比較的新しい機能をUnityでこんな風に使えるよ!」という紹介を行います。


言語機能名: 式形式の関数メンバー
追加バージョン: C# 6.0で新規追加、C# 7.0で可能なメンバー追加
説明: メソッドやプロパティーなどのメンバーの実装が単一の式の場合、より簡潔にそのメンバーを記述できる機能


C#でコードを書いていると、単一のメソッドを呼び出しているだけのメソッドを書く時があります。例えば、次のようにStartCoroutineを呼び出してCoroutineを返すメソッドです。

using System.Collections;
using UnityEngine;

public class Launcher : MonoBehaviour
{
    public Coroutine Launch()
    {
        return StartCoroutine(LaunchImpl());
    }

    private IEnumerator LaunchImpl()
    {
        // 略
        yield break;
    }
}

このようにメソッドの実装が単一の式で記述されている場合、より簡潔に式形式でメソッドを記述することができます。

using System.Collections;
using UnityEngine;

public class Launcher : MonoBehaviour
{
    // 式形式で記述
    // return や { や }がいらない
    public Coroutine Launch() => StartCoroutine(LaunchImpl());

    private IEnumerator LaunchImpl()
    {
        // 略
        yield break;
    }
}

return{}は処理の本質ではないボイラープレートな記述です。式形式の関数メンバーを活用することで、処理の本質のみを記述した簡潔な記述になりました。

ちなみに次のように、返値がvoidなメソッドも記述できます。

using UnityEditor;

public static class AssetUpdater
{
    [MenuItem("Assets/ForceReserializeAssets")]
    private static void ForceReserializeAssets() => AssetDatabase.ForceReserializeAssets();
}

式形式の関数メンバーの使い所として多いのは、単純な処理で実装されたToStringメソッドとゲッターオンリーのプロパティです。

[Serializable]
public class Circle
{
    [SerializeField]
    private float x;

    [SerializeField]
    private float y;

    [SerializeField]
    private float radius;

    public Circle(float x, float y, float radius)
    {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    // フィールドをそのまま返すのにも使える
    public float X => x;
    public float Y => y;
    public float Radius => radius;

    // ロジックを記述したプロパティにも使える
    public float Area => Mathf.PI * radius * radius; 

    // ToStringの実装にも使える
    public override string ToString() => $"Center ({X},{Y}) Radius:{Radius}";
}

ゲッター・セッター両方あるプロパティーやインデクサーにも使えます。

[Serializable]
public class State
{
    [SerializeField] private int score;

    public State(int score)
    {
        this.score = score;
    }

    public int Score
    {
        get => score;
        set => score = value;
    }
}


public class Dungeon
{
    private int[][] map;

    public int this[int i, int j]
    {
        set => map[i][j] = value;
        get => map[i][j];
    }
}

「別に短くなっても対して嬉しくないんじゃないか?」という疑問を持った方もいるかもしれません。
その疑問への回答は、「短いメンバーがずらっとたくさん並んだ時に、式形式のメンバーで記述すると、非常に短くなって嬉しい」というものです。
例えば、Vector2のいくつかのオペレーターを、従来どおりの書き方で実装すると、おそらくこのようになるでしょう。

    public static Vector2 operator +(Vector2 a, Vector2 b) {
        return new Vector2(a.x + b.x, a.y + b.y);
    }

    public static Vector2 operator -(Vector2 a, Vector2 b) {
        return new Vector2(a.x - b.x, a.y - b.y);
    }

    public static Vector2 operator *(Vector2 a, Vector2 b) {
        return new Vector2(a.x * b.x, a.y * b.y);
    }

    public static Vector2 operator /(Vector2 a, Vector2 b) {
        return new Vector2(a.x / b.x, a.y / b.y);
    }

    public static Vector2 operator -(Vector2 a) {
        return new Vector2(-a.x, -a.y);
    }

    public static Vector2 operator *(Vector2 a, float d) {
        return new Vector2(a.x * d, a.y * d);
    }

    public static Vector2 operator *(float d, Vector2 a) {
        new Vector2(a.x * d, a.y * d);
    }

    public static Vector2 operator /(Vector2 a, float d) {
        return new Vector2(a.x / d, a.y / d);
    }

{}でかなりスペースをとっています。
式形式でメンバーを記述することで嬉しいのは、このようにメンバーがずらっと並んだ時です。本質でない「return{}」で行数やスペースを取らなくなります。

これはこんな感じで短くなります。

    public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.x + b.x, a.y + b.y);

    public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.x - b.x, a.y - b.y);

    public static Vector2 operator *(Vector2 a, Vector2 b) => new Vector2(a.x * b.x, a.y * b.y);

    public static Vector2 operator /(Vector2 a, Vector2 b) => new Vector2(a.x / b.x, a.y / b.y);

    public static Vector2 operator -(Vector2 a) => new Vector2(-a.x, -a.y);

    public static Vector2 operator *(Vector2 a, float d) => new Vector2(a.x * d, a.y * d);

    public static Vector2 operator *(float d, Vector2 a) => new Vector2(a.x * d, a.y * d);

    public static Vector2 operator /(Vector2 a, float d) => new Vector2(a.x / d, a.y / d);

一覧した際に非常に短くなりましたね。

ちなみにコンストラクターでも活用できます。「読み取り専用の自動プロパティ」と「タプルの生成・分解」と組み合わせてこんな感じもかけます。(バリュータプルを使っているけど、内部的にはバリュータプルは生成されません)マイクロソフトの公式ドキュメントではたまにこの書き方を見かけます。

public class Point {
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);
}

// 内部的にはこんな感じ
// バリュータプルを使っているけど、内部的にはバリュータプルは生成されませんhttps://sharplab.io/#v2:EYLgtghgzgLgpgJwDQBMQGoA+ABATARgFgAoE7AZgAI9KAFAewEsA7GSgbxMu+qpbYAaHSgHM4MANyUAvlx4VK/SgE1hYyTLnctvOk1YAKJQA8ki1pQCeASkoBeAHyUDAs8tt3npq9YklZxEA===
//public class Point
//{
//    [CompilerGenerated]
//    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
//    private readonly int <X>k__BackingField;
//
//    [CompilerGenerated]
//    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
//    private readonly int <Y>k__BackingField;
//
//    public int X
//    {
//        [CompilerGenerated]
//        get
//        {
//            return <X>k__BackingField;
//        }
//    }
//
//    public int Y
//    {
//        [CompilerGenerated]
//        get
//        {
//            return <Y>k__BackingField;
//        }
//    }
//
//    public Point(int x, int y)
//    {
//        <X>k__BackingField = x;
//        <Y>k__BackingField = y;
//    }
//}

式形式の関数メンバーを使うと、メソッドやプロパティーなどのメンバーの実装が単一の式の場合、より簡潔にそのメンバーを記述することができます。
「別に短くなっても対して嬉しくないんじゃないか?」という疑問を持った方もいるかもしれません。
しかし「短いメンバーがずらっとたくさん並んだ時に、式形式のメンバーで記述すると、非常に短くなって嬉しい」です。
ぜひ活用してください。

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

UWA GOT Online 機能紹介 /ローカルで任意テスト、オンラインで随時確認

UWA GOT はUWAが提供しているローカル性能診断テストツールで、開発チームが実機でテストを行い、Editorでローカルサーバーを構築し、テストしたデータを確認することができます。現在、UWA GOT既存のローカルテスト機能以外で、オンライン同期分析機能も追加しました。開発チームがローカルのテストデータをUWAウェブサイトにアップロード、オンラインテストレポートを生成します。これによりテスト結果をさらに読みやすく、保存された過去のテスト履歴と比較しやく、またUWAからの性能改善レコマンド機能もあります。
1-1.png

UWA GOT(Online)のダウンロード先

https://jp.uwa4d.com/download にアクセスして「ツールのダウンロード」をクリックしてください。

UWA GOT(Online)機能説明

UWA GOTでテストしたローカルデータをUWAウェブサイトにアップロードし、「プロジェクト」タブをクリックすれば、「Overview」、「Mono」、「Asset」、「Lua」四つの診断モードを確認できます。アップロードされたすべてのテストデータはそれぞれに対応するテストモードに保存されます。
2.jpg
各テストモードで保存されたテストデータの「詳細」をクリックし、左側のメニュー欄で詳細テスト情報を下図のように確認できます。
3.png
4.jpeg
5.png

UWA GOT(Online)レポートの特徴

UWA GOTの「ローカル化」と「素早く」というアドバンスのもとで、オンラインレポート機能はプロジェクトのテストニーズを迅速に対応でき、さらに見やすい、ユーザーフレンドリーの形でプロジェクトメンバーに情報を共有することができます。また、レポートでの各問題に対するレコマンド機能やソリューション提案などによって、開発者がタイムリーにプロジェクトのボトルネックの特定や問題解決することができ、プロジェクトの品質管理、改善に貢献します。

以下は「Overview」モードのレポートを一例として、皆様と一緒にUWA GOT (Online)の機能特徴について見ていきましょう!
6-1.png
概要ページは性能トレンド図とテスト履歴が含まれています。ユーザーは各テストの重要パラメータの性能トレンド、Unityのバージョン、デバイスの型番などの情報を確認したり、ほかのプロジェクトメンバーと共有したり、重要な情報に対して注意書きしたりすることができます。

概要ページ内のテスト履歴の「詳細」ボタンをクリックすれば、さらに詳細な性能情報や最適化レコメンドを確認できます。下図のように、テストしたデバイスの詳細なハードウェア情報や重要パラメータの性能トレンドも確認できます。
7.jpg
8.jpg
上図のように、個別の関数を選択、比較することにより、開発者は正確にCPUコストの真の原因を特定することができます。

同じように, Mono—メモリ分析、Asset診断モードでも同じように重要なパラメータ情報のトレンドや実行時のスクリーンショット、最適化レコメンドなどがあります。
9.jpg
上図では,開発者がメモリの使用状況について明確に把握することができ、迅速にメモリコスト問題の特定や改善することができます。
10.jpeg
上図のように、性能に関する問題点を優先順位ごとにリストアップされます。それぞれに対して最適化のレコメンドがあります。これらの性能改善ソリューションはテストされたデータが見やすく、使いやすく、テスターでも簡単に問題点を早く見つけることができ、開発チームにフィードバックすることができます。また、重要なパラメータに対して、UWAは推薦値をユーザー様に参考用としてご案内しております。

「MAKE IT SIMPLE」という理念に基づいて、UWAはこれから日本のゲーム開発者のため、誰でもよいゲームをよい品質で開発出来ることに貢献いたします。


UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析最適化ソリューション及びコンサルティングサービスを提供している会社でございます。

UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com

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

UnityでのShaderLabメモリの最適化

おそらく、ShaderLabメモリの最適化に困っている開発者は多いでしょう。「価格の高い」メモリスペースに、無視できないボリュームを占めることがよくあります。他のアセットメモリと比べて、「ブラックボックス」に似ているので、最適化するのは難しいです。そのため、いくつかの実験を通じてShaderLabの占有率を分析し、この部分のメモリを最適化する方法を考えました。


一、問題を述べる

1.png
上の図(戦闘シーンに入ったときのメモリスナップショット)から、ShaderLabの占有率が42MBに達していることがわかります。なぜ、ShaderLabの占有率がそれほど高いのですか?

二、問題を分析する

2.png
現在のアイテム(ShaderLab)には詳細なShader占有情報を説明していませんので、他の方法で原因を探さなければなりません。 幸いなことに、メモリスナップショットのAssetsの下のShaderアイテムに詳細な使用情報があります。

そして、StandardのShaderが使用されているのを見ましたが、このプロジェクトにStandardのShaderを使用する場所がありませんので、なぜ存在していますか?

究明のために、一輪の調査を回しました。Standardで使用されている場所のいくつかをクリアし、もう一度テストして、別のメモリスナップショットを作成しました。
3.png
クリアした後、ShaderLabは27.6 MBに低下しました(後で、スタンダードは完全にクリアされ、21 MBに低下しました)。やっぱり、主な原因はStandardにあります。じゃあ、また問題が発生しました。Standardを使用していませんのに、なぜメモリにStandardが存在してありますか?

ここでは二点について話す必要があり、これもこの問題を排除する方法であります。

三、問題を排除する

1.モデルのインポートが導く

モデルをインポートする時、デフォルトで「Import Materials」がチェックされます。モデルがインポートされると、Unityは同じディレクトリに「Materials」ディレクトリを作成し、対応するマテリアルを作成します。このマテリアルはデフォルトでStandardを使用します。

アーティストが製造の過程にPrefabにあるモデルに他のマテリアルを添付しますので、実にはデフォルトのマテリアル(Standard)は使用されません。ただし、モデルをロードすると、デフォルトで作成されたマテリアルが再度ロードされ、シェーダーに解析され、メモリ内でStandardがあるになります。

では、ソリューションも非常に簡単です。「Import Materials」を削除し、使用されていないデフォルトのマテリアルを削除します。
4.png
注:「Import Materials」を削除しないと、他のプロジェクトにインポートしたときにマテリアルがまた自動的に作成されます。

補足:実際のプロジェクトでは、Prefabの変更回数は比較的多く、対応するモデルファイルの変更は比較的少ないため、プロジェクトのモデルと対応するPrefabは別々のAssetBundleにパッケージ化すると、非常に奇妙な状況が発生します。

「Import Materials」をチェックしないモデルファイルは、Prefabをインスタンス化すると、ShaderLabには一つの「Standard」のShaderメモリがありますが、このShaderの参照は一つの「Default-Material」ファイルに指します(しかしこのファイルは存在してありません)。
5.png
ただし、モデルとPrefabが同じAssetBundleにあり、またはResourcesを使用してロードされている場合、「Standard」と「Default-Material」は顕示されません。Unity 5.3.3のバグなのか、Unityの特殊なメカニズムなのかはまだわかりません。

一時的な解決策:モデルをPrefabとは別にパッケージ化する必要がある場合は、「Import Materials」をチェックして、デフォルトで生成された材料を直接使用および変更します。

2.デフォルトモデル(Cube、Sphere)の作成が導く

初期のシーンを構築する時、配置や視覚化しやすいのために、CubeみたいなシステムのデフォルトのMeshがアンカーポイントとして使用され、ゲームを始まる時に禁止させます。これらのCubeは有効になっていないため、パフォーマンスのコストはごくわずかであるため、無視します。

ただし、これはシステムのデフォルトのMeshであるから、作成時に指定されるマテリアルはデフォルトのマテリアル「Default-Material」であり、このマテリアルで使用されるシェーダーはたまたま「Standard」です。だから「Standard」の存在は間違いではありません。
6.png
解決策も非常に簡単です。これらのMeshを削除するか、マテリアルを交換します。 こうすれば、この部分が占める「Standard」は存在しません。

四、まとめ

Standardの変種が多すぎるため、Standardを使用しますと、複数のStandardの変種が同時に存在することがよくあり、大量のメモリを占めます。ShaderLabのメモリが大きすぎと感じっている場合は、上記の原因ですかどうかを調べてみてください。

ShaderLabのメモリ占用が大きすぎとは、完全にStandardの原因ですか?実際にはもっとあります。最適化後の27MB(完全にクリアされた後の21MB)の中に、他の原因があるはずです。しかし、最適化の過程は大きな部分から削除し始まることです。上記のように、少し最適化したら20MB以上を改善できますし、もちろんすぐにやる必要がありますが。サイズが小さいほど最適化効率は低くなるので、現時点では 「Assets」や「Texture2D」などの部分に目差して最適化します。したがって、残ったShaderLabの最適化方法は後であった時に再び補充します。

PS:上記の全ての内容は実機でテストしました。


UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析最適化ソリューション及びコンサルティングサービスを提供している会社でございます。

UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com

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

RTSゲームの経路探索システム

今回の主な話題:RTSゲームの経路探索システム、AssetBundleがパッケージ化された後のmd5は異なる、UGUIの3D HPバー最適化の問題、シーン内の一部のオブジェクトの外観を早く変更する方法。


経路探索システム

Q1: RTSゲームを作っています。UnityのNavMesh経路探索から始めましたが、ブロックとフラッシュの問題が常に発生するため、これはRTSマルチプレーヤー経路探索には適していません。Asset StoreのA *経路探索のコメントに、「衝突に問題があります」というコメントは書いてあります。比較的に通じるソリューションはありませんか?

前のプロジェクトはRTSプロジェクトでした。最初に経路探索の問題を解決する過程で同じような疑問に遭いました。長い時間の変更、調査、調整の後、参照用にいくつかのポイントを共有します。

まずは結論:A Start Pathfinding Project Pro + Unity NavMeshの2層構造を採用しました。

1)主な経路探索スキームは、A Start Pathfinding Project Proプラグインの従来のメッシュ形式を使用し、ディープな変更と拡張しました。主に経路探索過程中の通過性は単位半径を考慮する必要があります。そうすると経路探索の精度を高くなされます。
2)Unity NavMeshシステムを経路探索過程中の衝突システムとして使用します。あなたが言う通りに、この経路探索プラグインの衝突に問題があります。これについては後で説明します。

上記の2つの部分は、「経路探索アルゴリズム、正確な衝突」を構成します。次に、上記のスキームの理由と詳細について説明します。

1.NavMeshの経路探索速度は速いですが、十分に正確ではありません。RTSは従来の正方形のNavGrid経路探索に適しています。既存の経路探索アルゴリズムは、デフォルトで経路探索プロセスにおける経路探索ユニットの半径の影響を考慮することをサポートしていません。弊社のプランナーは「1メートル幅のギャップに2メートル幅のデブが通じる」のようなことに絶対許しませんから、Navmeshを使用して動的にブロッキングを生成すると、経路探索結果はありますが、衝突の問題で単位は大きいギャップに「シェイクする」可能性が高いです。これは自分でしか変更できないため、プラグインを購入して裏から外へ変更し、具体的にアルゴリズムの規則と変更された概略図を変更しました。こちらを参考できます:
http://www.cnblogs.com/yaukey/p/rts_unit_traverse_size_based_path_finding.html。
変更後、毎回経路探索しますとユニットの直径はパラメータの1つとして導入されます。

このプラグインを使用してスレッドを2つくらい開いて経路探索を行うことは私が調整した理想的な結果であります。次のは、マップ内のさまざまなユニットの出産と死亡です。ユニットが静止している場合は、ブロックを生成する必要があります。移動しますとブロックを削除して通過性を保証すべきです。プラグインのDynamicObsculeコンポーネントを変更することにより(元の機能シンプルすぎ)達成します。具体的には、各ユニットに対応するサイズのCapsuleCollider空オブジェクトにこのプラグインを添付して、ユニットが立つ時このオブジェクトをユニットの位置に置いてアクティブさせます。ユニットが移動しますとこの空オブジェクトを隠します。注意すべきのは、このプラグインはマップ通過性のアップデートを導き、このアップデートはマルチスレッドであります。即時性は保証されていないため、同じフレームまたは次のフレームでは、コールバックイベントを使用して応答することを実行しないでください。こうすれば、マップ全体の通過性はユニットによりアップデートします。

2.経路探索の通過性は保証できになり、次は衝突の問題を解決します。このプラグインはrvoアルゴリズムである衝突アルゴリズムが付属していますが、調整実験によると、その衝突は私たちの要求に満たしていません。ユニットの数が多く、混雑している場合、いくつかのユニットは立った後に常にオーバーラップします。最も許容できないのは、2つのユニットが完全にオーバーラップしていることです。私はここで多くの時間を費やしましたが、満足できる結果は修正してでられませんでした。後でrecastnavigationの作者が言ったことを思い出した。UnityのNavMeshはこれに基づいていますが、Detourシステムが大幅に書き換えられました、 Detourに巡航と衝突を含みます。実際の使用時に、NavMeshAgent間の衝突は非常に正確であることがわかりました。考えましたと、巧妙な方法は出てきました。地面にAStarプラグインの経路探索面積と同じサイズのNavMeshを配置します。ブロックはありません(純粋な青として理解できます)。同時に経路探索単位に一つのNavMeshAgentを添付します。しかし、このものは衝突のみを使用し、経路探索を使用しない(ブロックなくて、経路探索は2点直線であり、ほとんどコストがないと見なすことができる)ため、最後に実際の衝突効果は私たちの要求に大体満足できます。

以上は私が言った「2層構造」でした、そして問題を述べます。

1)開発時に使ったUnityバージョンはUnity 5.3.7-5.3.8で、プラグインバージョンはxでした。経路探索プラグインの基層にPhysics.CheckCapsuleコールはあるはずですが、マルチスレッドの多くのコール操作は、基層のPhysX物理エンジンの崩壊、ゲームの突然終了を導きます。必然ではありませんが、回数が増加しますと崩れやすくなります。プラグインの公式BBSにもこのバグに関する議論があります。後でこれを2つのPhysics.CheckSphereコールに置き換えました。2017バージョンはまだテストしていません。
2)1で使用した方法は、UWAパフォーマンスレポートで、Dynamic Colliderは毎回標準を超えました。これは自分で選択する必要がありますが、全体的に私たちのゲームのボトルネックは経路探索に集まることではなく、これらのテストでコストは比較的に理想であります。
3)プラグインのマルチスレッド更新により、たくさんの完了イベントコールバックがあり、基本的にこれを利用してゲームシステムとリンクします。足りない場合は自分で追加します、そうしないと更新が完了しない時にまだ間違い結果は出やすいです。
4)できれば内部のMonoメモリの割り当てを最適化し、ゲームシステムと組み合わせた後に採用の解放と切断することに注意してください。そうしないと、メモリリークが発生しやすくなり、経路探索プラグインが採用した後、多くのオブジェクトが解放できない状況を導きやすいです。
5)経路探索精度と衝突精度がゲーム内の表現、パラメーターを調整するためにより多くの時間を必要とします。たまたまに表現が悪いが、方法が悪いと言う意味ではありません。そして、リアルタイム同期に対する高い要求がある場合、このソリューションをさらに改善する必要があります。


レンダリング

Q2: シーン内の一部のオブジェクトの外観をすばやく変更したいのですが、DrawCallへの影響、材質変更の時間コストなどを考慮すると、最も効率的な方法は何ですか。

材質のみを変更する場合は、Colorの変更など、MaterialPropertyBlockを使用できます。

puts 'The best way to log and share programmers knowledge.'![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/632473fc-6308-8556-be39-f9127d133bcd.png)
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
    mpb.SetColor("_FillColor", Color.red); 
    GetComponent<MeshRenderer>().SetPropertyBlock(mpb);

MaterialPropertyBlockの変更効率は高いため、GPU Instacingと連携してDrawCallをマージすることもできます。 この記事を参照できます:Material Property BlockでMaterial属性操作を差し替え


アセット管理

Q3: Unity 5.6.5でAssetBundleパッケージツールを作成しました。Manifestを付属する最新のPipeline APIを使用しました。たとえば、Perfabをパッケージ化します。このPrefabにはTexture、Shader、スクリプトが含まれます。これで問題が発生します。Androidプラットフォームで、異なるコンピューターができたAssetBundleは異なります。UnityStudioで分析して、Shaderのmd5が異なることが原因です。何かいい方法はありますか?

Unityはいつもこの問題があります。同じコンピューターであっても、Libraryを削除してから再生すると異なります。パッチ適用ツールの比較プロセスを共有しましょう。
最初に、SVNに基づいて2つのバージョンの違いがあるAssetBundlesを見つけ、範囲を絞り込みます。
次に、ABに対応するmanifestファイルを比較して、AssetHashが実際に変更されたかどうかを確認し、変更されていないファイルを除外します。同時にTypeTreeHashもチェックし、変更がある場合は警告を出します。

ところで、このAPIにちょっと文句を言います。
https://docs.unity3d.com/ScriptReference/BuildPipeline.GetHashForAssetBundle.html
このAPIの原理は同じ名前のmanifest内のAssetHash値を直接読み取ることであり、このAPIがHashを計算するためのものであると誤解しやすいです。

MD5の問題は常に存在しており、AssetBundleのHasidで直接判断することをお勧めします。

こちらを参照してください:https://docs.unity3d.com/ScriptReference/BuildAssetBundleOptions.AppendHashToAssetBundleName.html


UI

Q4: UGUI WorldSpace Canvasを使用して、クリープ(クリープは頻繁に移動し、数は数十個くらい)3D UI HPバーを作成しました。HPバーUIは、カメラからの距離に応じて適切なレンダリング順序を自動的に形成します。しかし、このようにすれば、各HPバーにはCanvasが必要であり、一つのCanvasは一つのDrawCallであります。だからすべてのHPバーを同じWorldSpace Canvasに統合し、すべてのHPバーUIに対してDrawCallが1つだけになるようにしますが、これは合理なオクルージョン関係を形成しません。

だから数フレームごとにすべてのHPバーUIとカメラ間の距離を計算して並び、transform.SetSiblingIndexを変更してオクルージョン順序の問題を模擬しますが、これによりCPU計算が増加します。では、数量の少し多い3D UI HPバーの最適化はどうせればいいですか?

まず、UGUIでオクルージョンを変更したい場合、変更できるのはSiblingIndexのみです。並べ替えや変更が必要なHPバーの量が比較的多い場合は、かなりのオーバーヘッドが発生しやすくなります。 ただし、「数十」が大きすぎるかどうかを判断するのは困難です。このアプローチのパフォーマンスをローエンドマシンでテストすることをお勧めします。主な焦点は、距離、並べ替え、変更の総コストを計算することです。数フレームごとに行いますから、結果は1〜3msなら納得できます。


UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析最適化ソリューション及びコンサルティングサービスを提供している会社でございます。

UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com

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