C++ #include <atlbase.h> is not found.

C++ #include is not found」
DirectX12Raytracingのサンプルをビルドすると発生した。。。


■対応フロー
VisualStudioInstaller起動

個別のコンポーネント

「Spectreの軽減策を含む、v141ビルドツール用 C++ ATL (x86およびx64)」
「Spectreの軽減策を含む、最新のv142ビルドツール用 C++ ATL (x86およびx64)」
「Spectreの軽減策を含む、最新のv142ビルドツール用 C++ MFC (x86およびx64)」
「v141ビルドツール用 C++ ATL (x86およびx64)」
「最新のv142ビルドツール用 C++ ATL (x86およびx64)」
をインストール

VisualStudio再起動してリビルド

成功


■参考サイト
https://www.youtube.com/watch?v=Lq3SllRv7a8

DeviceSimulatorについて

blogs.unity3d.com
おぼえがきー

DeviceSimulatorとは

・Editor上で様々なモバイルデバイスの挙動と物理特性をシミュレートするための機能。
・セーフエリアの表示、画面解像度、Orientation、グラフィックスAPI変更など
・現在はPreview

インストール方法

PackageManagerを開き「Show preview packages」にチェックを入れ検索するとヒットします。
1.2.0をインストールします。
f:id:taguchikohei:20191013183333p:plain

使用方法

f:id:taguchikohei:20191014004950p:plain
ここから開きます。

開くとこんな感じ
f:id:taguchikohei:20191013184851p:plain
バイススペック確認、セーフエリア表示、画面回転など制御できます。

現段階で用意されてるデバイスプリセットは以下の11つ
Apple iPad Pro 11
Apple iPad Pro 12.9(2018)
Apple iPhone 7
Apple iPhone 8
Apple iPhone XS Max
Google Pixel 2
Google Pixel 2 XL
Google Pixel 3
Motorola Moto G7 Power
Samsung Galaxy S10+
Samsung Galaxy S10e

com.unity.device-simulator/com.unity.device-simulator フォルダに
バイス定義ファイル(.json)がありますが、今は追加出来ないみたいです。
今後カスタムデバイス定義を追加する方法が提供される予定とか。

シミュレートされたデバイス情報の取得

UnityEngine.ScreenとUnityEngine.SystemInfoから取得可能ですが
定義ファイルJSON内で未定義のパラメータはシミュレートされません。

例えば下記はiPhoneXSMaxの定義ファイルの一部ですが、
deviceModelは定義済なのでSystemInfo.deviceModelにこれが反映されます。
一方、deviceNameは未定義なのでSystemInfo.deviceNameには期待しない値が入ります。(動作環境?)
f:id:taguchikohei:20191013193251p:plain


シミュレータを起動してSystemInfoとScreenの一部パラメータをLogするとこんな感じです。
f:id:taguchikohei:20191013193305p:plain
(deviceNameがPC名になってる...)

また、DeviceとOrientationを変更するとこんな感じです。
f:id:taguchikohei:20191013193320p:plain

Editor再生中にデバイスを変更することも可能なので、
縦横画面切り替わる系のゲーム開発では便利そう。

AdaptivePerformanceについて

blogs.unity3d.com
そういえば触ってなかったということで。

対応端末持っておらず動作確認取れていないので、
こんな感じで使うのかなーという自分へのメッセージ。

AdaptivePerformanceとは

モバイル端末の状態安定のため役立つ仕組みを提供するパッケージ。
現在はSamsung Galaxy S10のみサポート

出来ること

・端末状態変化をイベントとして受信(スロットリング発生,CPUバウンドなど)
・CPU/GPUの性能調整(スロットリング時除く)

例えば、サーマルスロットリング発生早期イベントを受信した際に
ゲーム中の表示物のLODを下げることで未然にそれを防いだり、
軽めの処理が多いシーンではCPU/GPU性能を抑えるなどが可能。

インストール方法

PackageManagerから「AP Sumsung Android」をインストールします。
依存してAdaptivePerformanceもインストールされます。
f:id:taguchikohei:20191014165359p:plain

使用例

端末温度管理

using UnityEngine;
using UnityEngine.AdaptivePerformance;

public class ApThermalStatusTest : MonoBehaviour
{
    // AdaptivePerformanceのインターフェース
    private IAdaptivePerformance m_ap = null;

    void Awake()
    {
        // インスタンス取得。Instanceは再生開始時にPackage側で自動生成される
        m_ap = Holder.Instance;
        if (!m_ap.Active)
            return;

        // 温度に関するイベント
        m_ap.ThermalStatus.ThermalEvent += (metrics) =>
        {
            switch (metrics.WarningLevel)
            {
                // スロットリングの危険はありません
                case WarningLevel.NoWarning:
                    // 通常品質へ
                    break;
                // スロットリングが発生しそうです
                case WarningLevel.ThrottlingImminent:
                   // 品質下げてスロットリング回避する
                    break;
                // スロットリング中です
                case WarningLevel.Throttling:
                   // 品質下げてスロットリング回避する
                    break;
            }
        };
    }

    void Update()
    {
        // ThermalStatusに温度関連情報が詰まってる。ThermalMetricsから取得する
        var tm = m_ap.ThermalStatus.ThermalMetrics;
        
        // TemparatureLevelは正規化された温度で0~1の値をとる.
        // 1は最大温度に達した状態でスロットリングが発生している
        Debug.Log("温度レベル:" + tm.TemperatureLevel);
        
        // TemperatureTrendは正規化された温度の上昇下降傾向で-1~1の値をとる
        // +値は温度上昇、-値は温度下降
        Debug.Log("温度傾向:" + tm.TemperatureTrend);
        
    }
}

端末性能操作

using UnityEngine;
using UnityEngine.AdaptivePerformance;

public class ApDevicePerformanceControlTest : MonoBehaviour
{
    private IAdaptivePerformance m_ap = null;

    void Awake()
    {
        m_ap = Holder.Instance;
        if (!m_ap.Active)
            return;

        var dpc = m_ap.DevicePerformanceControl;
        
        // true:AutoPerformanceLevelControllerに制御を任せる
        // false:自分で制御する
        dpc.AutomaticPerformanceControl = false;
        
        // Cpu性能レベルを最低に設定
        dpc.CpuLevel = 0;
        
        // Gpu性能レベルを最低に設定
        dpc.GpuLevel = 0;
        
        // Cpu性能レベルを最大に設定
        dpc.CpuLevel = dpc.MaxCpuPerformanceLevel;
        
        // Gpu性能レベルを最大に設定
        dpc.GpuLevel = dpc.MaxGpuPerformanceLevel;
        
    }
    
}

端末性能状態の管理

using UnityEngine;
using UnityEngine.AdaptivePerformance;

public class ApPerformanceStatusTest : MonoBehaviour
{
    // AdaptivePerformanceのインターフェース
    private IAdaptivePerformance m_ap = null;

    void Awake()
    {
        // インスタンス取得。Instanceは再生開始時にPackage側で自動生成される
        m_ap = Holder.Instance;
        if (!m_ap.Active)
            return;
        
        // CPU,GPU,フレームレートのバウンド発生でイベント受信
        m_ap.PerformanceStatus.PerformanceBottleneckChangeEvent += (ev) =>
        {
            switch (ev.PerformanceBottleneck)
            {
                // CPUバウンド
                case PerformanceBottleneck.CPU:
                    break;
                // GPUバウンド
                case PerformanceBottleneck.GPU:
                    break;
                // フレームレートバウンド
                case PerformanceBottleneck.TargetFrameRate:
                    break;
                // 不明
                case PerformanceBottleneck.Unknown:
                    break;
            }
        };
        
        // 性能レベル変更イベント
        m_ap.PerformanceStatus.PerformanceLevelChangeEvent += (ev) =>
        {
            Debug.Log("CPUレベル:" + ev.CpuLevel);
            Debug.Log("GPUレベル:" + ev.GpuLevel);
            Debug.Log("CPUレベル差分:" + ev.CpuLevelDelta);
            Debug.Log("GPUレベル差分:" + ev.GpuLevelDelta);
        };
    }

    void Update()
    {
        var metrics = m_ap.PerformanceStatus.PerformanceMetrics;
        Debug.Log("CPUレベル:" + metrics.CurrentCpuLevel);
        Debug.Log("GPUレベル:" + metrics.CurrentGpuLevel);
        Debug.Log("性能ボトルネック:" + metrics.PerformanceBottleneck);

        var frameTiming = m_ap.PerformanceStatus.FrameTiming;
        Debug.Log("現在フレーム時間:" + frameTiming.CurrentFrameTime);
        Debug.Log("平均フレーム時間:" + frameTiming.AverageFrameTime);
        Debug.Log("現在CPUフレーム時間:" + frameTiming.CurrentCpuFrameTime);
        Debug.Log("平均CPUフレーム時間:" + frameTiming.AverageCpuFrameTime);
        Debug.Log("現在GPUフレーム時間:" + frameTiming.CurrentGpuFrameTime);
        Debug.Log("平均GPUフレーム時間:" + frameTiming.AverageGpuFrameTime);
    }
}

所感

いやこれ個人的に非常に便利。GalaxyS10でのみサポートって勿体ない。
Unityも公式ブログで対応デバイス増やすと言ってるし今後に期待・・・!

クラスメンバのメンバのSerializedProperty取得方法

UnityのEditorInspector拡張作業時に詰まったのでメモ。

// 疑似コード
[Serializable]
public class SomethingData : ScriptableObject
{
    [Serializable]
    public class InnerData
    {
    	public int value; // <- これ
    }

    public InnerData inner = new InnerData();
}

上記のようなクラスがあったとして、「value」のSerializedPropertyを取得する方法。

// 疑似コード
[CustomEditor(typeof(SomethingData))]
public class SomethingDataEditor : Editor
{
    public override void OnInspectorGUI()
    {
        /*~いろいろ処理~*/
        SerializedProperty property = serializedObject.FindProperty("inner").FindPropertyRelative("value");
        /*~いろいろ処理~*/     
    }
}

FindProperty()とFindPropertyRelative()を段階的に使用して取得。

同期した位置の線形補間

ネットワークマルチなどで表示物の位置を同期する際、クライアントがサーバーから受信した位置をそのまま使用すると、ラグや低同期レートなどが原因でその表示物の動作がカックカクになるが、これを軽減するために同期フレームとその前のフレームの位置を線形補間しようよってやつ。ベタ中のベタ。予測同期と併せてUnityがまだメジャーになる10数年前に初めて出会い使用しそれっきりだったが、最近ネットワーク同期について調べる機会がありそこで目にしたがやっぱり今でも活用されていて、超簡単、手軽だし書いてみた。回転補間付。

コード

public class TransformInterpolator : MonoBehaviour
{
	private Vector3 m_fromPos;
	private Vector3 m_toPos;
	private Quaternion m_fromRotation;
	private Quaternion m_toRotation;
	private float m_diffTime;
	private float m_lastTime;
	private float m_elapsedTime;

	public void SetTransform(Vector3 newPos,Quaternion newRot)
	{
		m_fromPos = transform.position;
		m_toPos = newPos;
		m_fromRotation = transform.rotation;
		m_toRotation = newRot;
		m_diffTime = Time.time - m_lastTime;
		m_lastTime = Time.time;
		m_elapsedTime = 0.0f;
	}

	void LateUpdate()
	{
		if (m_diffTime <= 0)
			return;

		m_elapsedTime += Time.deltaTime;
		float ratio = m_elapsedTime / m_diffTime;
		if (ratio >= 1.0f)
			ratio = 1.0f;

		transform.position = Vector3.Lerp(m_fromPos, m_toPos, ratio);
		transform.rotation = Quaternion.Lerp(m_fromRotation, m_toRotation, ratio);
	}

}

使用例

※疑似コード

public class SyncObject : MonoBehaviour
{
	private TransformInterpolator m_transformInterpolator;

	void Awake()
	{
		// クライアント側のみに追加
		m_transformInterpolator = gameObject.AddComponent<TransformInterpolator>();
	}

	// サーバーからスナップショットを受信したら呼ばれる
	void OnReceiveSnapshot(Snapshot snapshot)
	{
		m_transformInterpolator.SetTransform(snapshot.newPos,snapshot.newRot);
	}
}


他にも補間手法はありますが、開発中のゲームに組み込んでみると相性が悪くて思ったような挙動にならずそのプロジェクト独自の補間手法(や、サーバー側でのクラ位置管理手段)を模索することになることが多かったが、線形補間はとりあえず実装出来て効果もそこそこ、プロトレベルでは十分なのでコスパよい印象。

Photon BOLT サンプル起動まで覚書

Photon BOLTのサンプル動かすところまでの覚書。
自分が理解出来ればOK状態なので
詳細知りたい人は公式やQiitaで適当な記事を漁ってください。

やりたいこと

サーバーアプリをHeadlessModeで起動し、
そこに複数クライアントを接続し動作。

サーバー、クライアントアプリはWindows

Photon BOLT

www.photonengine.com

詳しい機能は公式参照
doc.photonengine.com

環境

  • Unity : Unity 2019.2.3f1
  • OS : Windows 10 Home

手順

Photonのアカウントを作成

PhotonBOLTをUnityにインポート

Serverアプリ作成

Clientアプリ作成

ServerアプリをHeadlessModeで起動

ClientアプリをServerへ接続して操作

Photonのアカウントを作成

dashboard.photonengine.com
既に持っていたら必要なし。
持ってない場合アカウント登録。
(PhotonBOLTをUnityへインストールした後にAppIDの設定が求められる。)
登録したメールアドレスに届くメールので、パスワードを入力しアカウントを作成。
その後、クーポンIDなどの適用が求めらるが、持っていないのでスキップ。
アプリ作成時「Photonの種別」「アプリケーション名」のみ設定し次へ。

PhotonBOLTをUnityにインポート

UnityのアセットストアからPhotonBOLTをインポート。
検索したら出てくる。
f:id:taguchikohei:20190831151334p:plain

インポートが完了するとこんなダイアログが出てくる。
f:id:taguchikohei:20190831151358p:plain
「Photon Bolt App ID or Email」にAppIDを入力して「Next」

「Samples」の矩形をクリックして、サンプルをインポートし「Next」
f:id:taguchikohei:20190831151407p:plain

Nextし続けるとセットアップ完了ダイアログが出るのでYes。
Unityのコンパイルが走る。

Serverアプリ作成

Unityで新規プロジェクト作成し、BoltHeadlessServerシーンを開く。

HerarchyのHeadlessServerHolderゲームオブジェクトを選択し
inspectorのMapの値をLevel1からTutorial1に変更する。
※デフォルト設定のLevel1がHeadlessModeで再生するとエラーを吐くためTutorial1を代用する。
RoomIDも変更しておくと後でクライアントがルームリスト参照する際にわかりやすい。
空の場合はランダムで作られる。
f:id:taguchikohei:20190831151449p:plain

BuildSettings->Scenes In Buildに以下のシーンを追加
BoltHeadlessServer : 0 (起動シーン)
Tutorial1 : 1

シーン追加後、メニューバーからBolt->Compile Assemblyを実行


Unityを再生。
ここまで上手く進めばこの画面が出来上がる。
WSDAキーで画面上の箱が動く。
f:id:taguchikohei:20190831151508p:plain

何事もなければビルドする。
アプリ名は「PhotonBoltServerTest」

Clientアプリ作成

Unityで新規プロジェクト作成し、Serverと同じようにPhotonBOLTをインポート。
完了後、BuildSettings->Scenes In Buildで以下シーンを追加。
MainMenu : 0 (起動シーン)
Tutorial1 : 1

追加したらビルドする。
アプリ名は「PhotonBoltClientTest」

ここまででServerとClientのアプリが完成。

ServerアプリをHeadlessModeで起動

作成済のServerアプリをHeadlessModeで起動

PhotonBoltServerTest.exe -batchmode -nographics -map Tutorial1

Windowsのプロセス一覧に表示されたら成功。

ClientアプリをServerへ接続して操作

PhotonBoltClientTest.exeを起動し画面に表示されている「Client」をクリックするとルームIDが表示される。
f:id:taguchikohei:20190831151544p:plain
クリックすると

部屋に入れる。
f:id:taguchikohei:20190831151558p:plain

クライアント複数起動&接続で操作可能
f:id:taguchikohei:20190831151249g:plain

裏でサーバープロセスが動いている。
f:id:taguchikohei:20190831151629p:plain