C++ ときどき ごはん、わりとてぃーぶれいく☆

Wonder Rabbit Projectのなかのひとのブログ。主にC++。

ダーク・エクセル

白い画面は眩しくて疲れます。 Microsoft Excel をダークなテーマで使いたいニーズはそれなりにあると思います。簡単な方法は無いだろうと諦めていたのですが、いつの間にか簡単にダーク・エクセルを実現できるようになっていたようです。

f:id:USAGI-WRP:20170921014103p:plain

この状態にする手順は3段階。

  1. File -> Option -> General -> Personalize your copy of Microsft Office -> Office Theme を Black へ。
  2. Page Layout -> Page Setup -> Background から適当な濃い灰色の1pxの画像ファイルを設定。(適用には若干の負荷がかかる)
  3. すべてセルを選択し、 Home -> Font -> Font Color を Gray に設定。(お好みで White でも何でも)

f:id:USAGI-WRP:20170921014316p:plain

f:id:USAGI-WRP:20170921014858p:plain

f:id:USAGI-WRP:20170921021729p:plain

おまけ: 1px の適当な濃い灰色画像

f:id:USAGI-WRP:20170921015149p:plain:w32

参考

  1. Is there Something like a Dark Background Excel Theme? - Super User

UE4: 4.16 から 4.17 へアップグレードした際に遭遇した諸問題と解決について

4.16で開発していたプロジェクトを4.17へアップグレードした際に遭遇した問題と解決方法を整理します。

問題と解決方法

1. 4.15 向けのプラグインがビルド不能に陥った

レベル内の重力を器用に扱いたい場合に便利なプラグインとして CustomGravityPlugin (参考1)があります↓。更新が4ヶ月以上停止していて、4.15以降ではwarningが生じる状態(但しビルドはできた)でした。4.17ではビルドできなくなりました。

このプラグインを4.17で使う際の問題は、4.15で導入された ReadOnlyTargetRules に対応したコンストラクターを定義した <plugin>.Build.cs が定義されていない事でした。修正せずにビルドを試みると以下のような警告が表示されます。

C:\Users\usagi\tmp\\Plugins\CustomGravityPlugin\Source\CustomGravityPlugin\CustomGravityPlugin.Build.cs: warning: Module constructors should take a ReadOnlyTargetRules argument (rather than a TargetInfo argument) and pass it to the base class c onstructor from 4.15 onwards. Please update the method signature.

また、このプラグインはIWYU(参考2、参考3、参考4)に対応したコードになっていないため、ついでにIWYUにも対応しました。

<plugin>.Build.cs の ctor を ReadOnlyTargetRules をサポートするよう書き換え、IWYUの運用を設定します。

// original
public CustomGravityPlugin(TargetInfo Target)
{
// changed
public CustomGravityPlugin(ReadOnlyTargetRules Target) : base(Target)
{
  // IWYU 対応
  PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
  // memo: CustomGravityPlugin の場合は 4.16 時点でもこれがないとビルドできません。
  // 4.17でも同様なので、他の PublicIncludePaths.AddRange などと同様に ctor 内に書き加えます。
  PublicDependencyModuleNames.AddRange
  ( new string[]
    { "Core", "CoreUObject", "Engine", "InputCore"
    }
  );

その後、プラグインの全ての .h ファイルの冒頭へ #include "CoreMinimal.h" と必要に応じた UE4 のヘッダーファイルの #includeAPI リファレンスなど参考に書き加え、全ての .cpp ファイルの冒頭へ #include "<source>.h" を書き加えます。

2. ヘッドマウントディスプレイ関連のコードがリンクエラーやwarningを出すようになった

4.17からヘッドマウントディスプレイ関連のコードがモジュール化されました(参考5)。これに伴い、ヘッドマウントディスプレイを扱うUE4のプロジェクトを4.16から4.17へアップグレードする際には主に2つの修正作業が必要です。

  1. ヘッドマウントディスプレイ関連の #include "Kismet/<file>.h"#include "<file>.h" へ変更します。(Kismet/が付かなくなります。)
  2. <project>.Build.csPublicDependencyModuleNames.AddRangeHeadMountedDisply を追加しモジュールに必要なライブラリーのリンクを定義します。
// 4.16 以前
#include "Kismet/HeadMountedDisplayFunctionLibrary.h"
// 4.17 以降
#include "HeadMountedDisplayFunctionLibrary.h"
PublicDependencyModuleNames.AddRange
  ( new string[]
    { "Core", "CoreUObject", "Engine", "InputCore"
    // ↓ヘッドマウントディスプレイのモジュール
    , "HeadMountedDisplay"
    }
  );

3. operator[] の呼び出しコードを文法エラー扱いされるようになった

int operator[]( int ) が定義されたクラスの内部でこれを使いたい場合、auto result = operator[]( parameter ); のように記述できるが(参考6)、そのようなコードについて、

error C3861: ‘[]’: identifier not found

のようにエラーを出力されコンパイル不能扱いされてしまいました。

これは正確にはUE4、4.16から4.17へアップグレードとは直接の関係は無く、Windows 環境向けに Microsoft Visual Studio という(残念ながら)昔から間抜けな実装やバグでも有名な開発環境を使用している場合に起こる問題でした。

4.16のプロジェクトを4.17のエンジンでアップグレードして読み込もうとした際に作られるプロジェクトのコピーのプロジェクト設定のPlatform、Windows、Toolchain の設定が default にセットされた状態となり、 4.16 のプロジェクトの際には Visual Studio 2017 を指定していたプロジェクトのソースの一部がアップグレード直後に default ( = Visual Studio 2015 ) でのビルドとなりコンパイル不能となっていました。

少し困った事に、 Unreal Editor の GUI が起動しない事にはどこかにある設定ファイルの該当項目を探し出してテキストエディターで書き換える手間が必要です。あるいは、一時的に operator[]( parameter ) のようなコードを (*this)[ parameter ] のようにバギーな旧式の Visual Studio でも問題の無いコードに書き換えてあげて一端ビルドが通る状態で Unreal Editor を起動させ、プロジェクト設定を GUI から変更する必要があります。

所感

4.16 プロジェクトを 4.17 へアップグレードする手間はゼロではありませんでしたが、それほど大変な事はありませんでした😃

参考

  1. GitHub - mhousse1247/UE4-CustomGravityPlugin: Unreal Engine 4 : Custom Gravity Plugin
  2. IWYUでコーディングしよう - #memo
  3. Game Engine Technology by Unreal
  4. Unreal Engine | IWYU リファレンス ガイド
  5. Game Engine Technology by Unreal
  6. [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

UE4/C++: アクターのメンバー関数内で AActor 派生の任意のユーザー定義型のオブジェクトを列挙する方法

// TActorIterator で AActor 派生の任意のユーザー定義型のオブジェクトを列挙
// AHogePawn は列挙したい AActor 派生のユーザー定義型
// GetWorld() は AActor のメンバー関数
{
  auto counter = 0;
  for ( TActorIterator< AHogePawn > i( GetWorld() ); i; ++i )
    // i は間接参照演算子によりオブジェクトのメンバーへアクセスできるイテレーター
    UE_LOG
    ( LogTemp
    , Log
    , TEXT( "enumerated AHogePawn[%d]: (name)=%s, (has abc tag)=%s" )
    , counter++
    , i->GetName()
    , i->Tags.Contains( "abc" ) ? TEXT( "true" ) : TEXT( "false" )
    );
}

参考2の GetAllActorsOfClass を使う手もあるが単純に列挙したいだけの場合は TActorIterator を使う方がコードがシンプルで楽。

参考

  1. TActorIterator | Unreal Engine API Reference
  2. UGameplayStatics::GetAllActorsOfClass | Unreal Engine API Reference
  3. AActor::GetWorld | Unreal Engine API Reference

UE4/C++: メッシュのマテリアルやテクスチャーをC++コードで制御する方法

要点を整理しようと思ったらソースの例示しかない記事になりました(ノω・)テヘ

// マテリアルをロードしてメッシュへ設定する
// mesh: UPrimitive またはその派生型( UStaticMeshComponent, UProceduralMeshComponent など)のポインターオブジェクト
// element_index: メッシュ内の要素番号。1つしか無ければ 0 のみ。
mesh->SetMaterial
( element_index
, Cast< UMaterial >
  ( StaticLoadObject
    ( UMaterial::StaticClass()
    , nullptr
    , TEXT( "/Game/MyMaterial" ) // Game/MyMaterial は UE4Editor で作り込んでおく
    )
  )
);
// マテリアルをロードしてマテリアルインスタンスを作成した上でメッシュへ設定する
// material_instance は UMaterialInstanceDynamic* 型。クラスメンバー変数として保持して使う事が多いと思う。
auto material_instance =
  mesh->CreateAndSetMaterialInstanceDynamicFromMaterial
  ( element_index
  , Cast< UMaterial >
    ( StaticLoadObject
      ( UMaterial::StaticClass()
      , nullptr
      , TEXT( "/Game/MyMaterial" )
      )
    )
  );

// マテリアルインスタンスへスカラーパラメーターを設定する。与える値の型は float
material_instance ->SetScalarParameterValue( "scalar_parameter_name", 1.23f );
// マテリアルインスタンスへベクターパラメーターを設定する。与える値の型は FLinearColor
material_instance ->SetVectorParameterValue( "vector_parameter_name", { 1.0f, 1.0f, 1.0f, 1.0f } );
// テクスチャーを作成する。
// my_generated_texture は UTexture2D* 型
constexpr int32 width  = 2;
constexpr int32 height = 2;
auto my_generated_texture = UTexture2D::CreateTransient( width, height, PF_R8G8B8A8 );
struct r8g8b8a8 { uint8 r = 255, g = 255, b = 255, a = 255; };
std::array< r8g8b8a8, width * height > my_data{};
auto locked_bulk_data = my_generated_texture->PlatformData->Mips[ 0 ].BulkData.Lock( LOCK_READ_WRITE );
FMemory::Memcpy( locked_bulk_data, my_data.data(), sizeof( r8g8b8a8 ) * my_data.size() );
my_generated_texture->PlatformData->Mips[ 0 ].BulkData.Unlock();
my_generated_texture->UpdateResource();

// マテリアルインスタンスへテクスチャーパラメーターを設定する。与える値の型は UTexture2D*
material->SetTextureParameterValue( "texture_parameter_name", my_generated_texture );
// 作成した my_generated_texture を破棄する(ことにする)。
my_generated_texture->ConditionalBeginDestroy();
my_generated_texture = nullptr;

// 作成した material_instance を破棄する(ことにする)。
material_instance->ConditionalBeginDestroy();
material_instance = nullptr;
// おまけ: アクターのメンバー関数内などで、UE4のCGが破棄する事になっているオブジェクトをに強制的に破棄させる
// GetWorld は AActor のメンバー関数
GetWorld()->ForceGarbageCollection(true);

参考

  1. UMeshComponent::SetMaterial | Unreal Engine API Reference
  2. UPrimitiveComponent::CreateAndSetMaterialInstanceDynamicFromMaterial | Unreal Engine API Reference
  3. UMaterialInstanceDynamic | Unreal Engine API Reference
  4. EPixelFormat | Unreal Engine API Reference
  5. UTexture2D | Unreal Engine API Reference
  6. Unreal Engine | インスタンス化マテリアル
  7. Is it possible to load bitmap or JPG files at runtime and use them on assets as textures? - UE4 AnswerHub
  8. How to set a texture parameter in C++? - UE4 AnswerHub

Boost-1.65 の PolyCollection, Stacktrace, Sync の概要を確認

Boost-1.65 から3つの新しいライブラリーが入るらしいので概要( Introduction )を確認したメモ。

  1. Chapter 28. Boost.PolyCollection - develop
  2. Chapter 38. Boost.Stacktrace 1.0 - develop
  3. http://www.boost.org/doc/libs/develop/libs/sync/index.html (公式のリンクURLなんだけど執筆現在リンク切れ・w・)

Boost.PolyCollection

抽象型を基にした派生クラスのインスタンスとしてのオブジェクト「群」に対して、基礎的なC++の実装では起こり得る次の2点の実行時のパフォーマンス問題:

  1. ヒープアロケーションによってメモリーが不連続化する事によるCPUキャッシュパフォーマンスの低下
  2. ポリモーフィックなオブジェクト「群」に対する仮想関数の操作に対する分岐予測失敗によるパフォーマンス低下

これらの影響を極力避けたい場合に有用なオブジェクト「群」を扱うコレクション型を用意したもの。以下の3種類が少なくとも Boost-1.65 時点から使用可能となる:

  1. boost::base_collection // 古典的な基底型と派生型によるオブジェクト指向プログラミング的なオブジェクト「群」向け
  2. boost::function_collection // std::function による関数のラッピングを用いたオブジェクト「群」向け
  3. boost::any_collection // Boost.TypeEreasure を用いたダッグタイピング的なオブジェクト「群」向け

ゲームとか物理シミュレーションとか書いてると役に立てやすいかもしれないなーと。 UE4 みたいなフレームワークを使っていてもユーザーコードレベルで明らかなボトルネック要因を局所的に Boost.PolyCollection で扱うように細工するとかでも十分に役立てられるのではないかな、と思う。そのうちパフォーマンスの変化を手元で計測してそこそこ効果ありそうなら使ってみようかな。

Boost.Stacktrace

C++の実行時に、関数の呼び出し順の状況や"今"呼ばれている関数は何か、あるいは例外の発生時にどこからその例外が飛ばされてきたのか、そんなような情報を人間にとって読み易く表示する機能を提供するシンプルな、C++03標準以降の規格で動作可能なライブラリー。

そういえば、 Boost.Stacktrace とは直接は関係無いのだけど、さいきん アイエエー 例外? なんで例外? - お前の血は何色だ!! 4 という事で id:rti7743 さんが “この例外を投げたのは誰だー” ってライブラリーを紹介していたなーと思い出すなど。

Boost.Sync

この記事の執筆現在、公式のドキュメントがリンク切れになっている上に、 Boost.Sync のぐぐらびりてぃー的な問題なのか情報がぱっとぐぐった程度ではあまり確かな事がでてきませんでした。

Boost の ML のアーカイブBoost mailing page: [boost] [thread] Dividing into Boost.Thread and Boost.Sync などから Boost-1.65 の Boost.Sync 推量するに、ライブラリーの名前は “同期処理” の意味の “Sync(-ronization)” で、これまで Boost の実装では Boost.SmartPtr, Boost.Log, Boost.Atomic などでそれぞれに実装されていた mutex ( spin_mutex, yield_spin_mutex ) を共通化するために新たに作ったライブラリー、といった具合になっているかもしれない。

私は Boost.Sync 周りの事情を詳しく追いかけていたわけではないし、↑のMLアーカイブもぱっとでてきたので掻い摘んで見ただけだし、実際に Boost-1.65 に取り込まれた(採択された)段階の Boost.Sync の話ではないのでこれについてはほとんど実際どうから疑わしい、くらいの概要確認に留まった。

UE4/C++ Tick が来ない!そんなときのトラブルシュート備忘録

「Tick が来ない!」、たまによくあるのは私だけでしょうか。 UHogeFugaComponentTickComponent が来ない、 AHogeFugaActorTick が来ない、などなど。そんな場合の備忘録です。

疑い(1): bCanEverTickfalse

コンストラクター等で bCanEverTicktrue になっていない場合に Tick が来なくなる。 true にすれば良いし、そもそも通常はデフォルトで true なのでこのミスの可能性はわりと低い。低いけど根本的なところなので一応最初に疑って確認、必要なら修正。

// Actor の場合
PrimaryActorTick.bCanEverTick = true;

// Component の場合
PrimaryComponentTick.bCanEverTick = true;

疑い(2): bStartWithTickEnabledfalse

先の(1)と同様コンストラクター等で true になっていない事があれば開始時に自動的に Tick が発生しないので、とりあえず確認し、必要なら修正。これも通常デフォルトで true なのでコレジャナイ場合がほとんど。

// Actor
PrimaryComponentTick.bStartWithTickEnabled = true;

// Component の場合
PrimaryComponentTick.bStartWithTickEnabled = true;

疑い(3): bAllowTickOnDedicatedServerfalse // サーバーの場合のみ

疑い(1)、(2)と同レベルの段階の問題で、サーバー動作の場合はまた別のフラグが false だと Tick が来なくなる。もしサーバーの Tick が来ない時には一応疑う。

// Actor
PrimaryComponentTick.bAllowTickOnDedicatedServer= true;

// Component の場合
PrimaryComponentTick.bAllowTickOnDedicatedServer= true;

疑い(4): 実行中に SetActorTickEnabled( false ) あるいは SetComponentTickEnabled( false ) している

実行時の状況に応じた負荷制御等でプログラマーが明示的に SetActorTickEnabled( false ) を制御している Actor あるいは SetComponentTickEnabled( false ) を制御している Component の場合、本来意図した必要なタイミングでも false がセットされたままになっていると Tick が来ない。

この機能は Actor ないし Component 自身が Tick 内で false をセットするようなコードがある場合、当然自身の Tick は呼ばれなくなるので true に復帰するためには、どこか外部の何らかのトリガーによって true 引数で呼んであげて、かつ意図せず false が再セットされるような事が無いようにコード実装されていないと Tick が来ない怪が発生してしまう。

疑い(5): BeginPlay() override の実装で Super::BeginPlay() を呼んでいない

よくある Actor や Component の BeginPlayoverride での “うっかり” パターン。

// MyActor.h
protected:
  // よくある BeginPlay の override 宣言。ここまでは特に問題ない・w・
  void BeginPlay() override;
// MyActor.cpp ( Tick  来ないダメ版)
void MyActor::BeginPlay()
{
  // MyActor で必要な BeginPlay タイミングでの初期化とかなんとか実装して・・・
  hoge();
  
  // 実装して・・・
  fuga();
  
  // ”何か忘れていないだろうか・・・”
}

わざとらしくコメントを書いて置いたのでたいていの UE4/C++er は既に気づいたと思う。 Super::BeginPlay() を呼んでいない。

// MyActor.cpp ( Tick 来る良い版)
void MyActor::BeginPlay()
{
  // 実は Actor や Component の BeginPlay の中で Tick 関連のフラグや関数登録が実装されている。
  Super::BeginPlay();
  hoge();
  fuga();
}

↑今日はこのパターンにやられました(ノω・)テヘ

参考

  1. FTickFunction | Unreal Engine API Reference
  2. AActor::Tick | Unreal Engine API Reference
  3. UActorComponent::TickComponent | Unreal Engine API Reference
  4. Tick is never called, even though bCanEverTick is set to true. - UE4 AnswerHub
  5. Tick is never being called even though CanEverTick and StartWithTickEnables is true - UE4 AnswerHub

だそく

  1. Wizardry#4/ウィザードリィ#4 プレイ日記 Metal Page
    • 非常に重要な警告: このリンク先には重大なネタバレが含まれます。

UE4/C++ Interface の作り方

おさらい: ふつうの C++ の “インターフェース"(=抽象型)

C++er 的には virtual なメンバー関数に =0; 定義を入れた Abstruct な class ( or struct ) を作れば広義のプログラミング言語の意味での Interface になるって思うじゃん・w・

// ふつうの C++ 的インターフェース(=抽象型)
struct my_interface
{
  virtual float get_my_value() = 0;
};

// ふつうの C++ 的インターフェースの実装
struct my_implementation
  : public my_interface
{
  float get_my_value() override { }
};

ほんだい: UE4/C++ で Blueprint 対応の「 UE4 の世界のインターフェース」

UE4/C++ で Blueprint 対応の「 UE4 の世界のインターフェース」を定義するのはこれに比べてしまうと少々めんどくさくなる。

// === MyInterface.h ===
// 手間暇(1): UINTERFACE で UInterface を public 継承した U プリフィックスのクラスを定義する
UINTERFACE(BlueprintType)
class MyApp_API UMyInterface: public UInterface
{
  // 要注意: ここでもこちらは U プリフィックス版を使う
  GENERATED_UINTERFACE_BODY()
};

// 手間暇(2): UE4のクラス定義マクロを付けずに先に定義した U プリフィックスのクラスの I プリフィックス版のクラスを定義する
class MyApp_API IMyInterface
{
  // 要注意: ここでもこちらは I プリフィックス版を使う
  GENERATED_IINTERFACE_BODY()
public:
  // 手間暇(3) Blueprint へ関数を公開するめんどくさいマクロをだらだら書く(めんどくさいでござる@w@)
  UFUNCTION( BlueprintNativeEvent, BlueprintCallable, Category = "MyInterface" ) float GetMyValue();
};
// === MyInterface.cpp ===
// 手間暇(4): GENERATED_UINTERFACE_BODY が宣言だけ自動的に生成する U プリフィックス版の方の ctor を定義する
UMyInterface::UMyInterface( const class FObjectInitializer& ObjectInitializer ): Super( ObjectInitializer ) { }

// 手間暇(5): ここでようやく C++ 的な virtual-override における virtual 宣言された基底クラスのメンバー関数を定義
// Note: どこで↓の宣言に相当する virtual retur_type FunctionName_Implementation(); が行われているのかは後述。
float GetMyValue_Implementation() { return 3.14159265358979f; }
// === MyImplementation.h
UCLASS()
class MyImplementation
  : public AActor
  , public IMyInterface
{
  GENERATED_BODY()
protected:
  // 手間暇(6): インターフェースを実装する UE4 オブジェクトクラスでの override 実装の宣言
  float GetMyValue_Implementation() override;
};
// === MyImplementation.cpp
// 手間暇(7): インターフェースを実装する UE4 オブジェクトクラスでの override 実装の定義
float MyImplementation::GetMyValue_Implementation() { return 1.41421356f; }

これで、例えば IMyUnitFunctions など適当なインターフェースで共通の何らかの Getter 群を継承、実装しつつ APawn から派生した AMyPawnACharacter から派生した AMyCharacter について、ブループリントで GetPlayerPawn から Cast To IMyUnitFunctions して IMyUnitFunctions インターフェースを持つオブジェクトを得て、共通の Getter で得た値を表示したり、あるいは何かに使えるようになる。

インターフェースを使わない場合は、 Tag で頑張ったり、 Get All Actors of XXXX から For EachBranch したり、あるいはそこまででなくとも Branch 地獄を書けば同じような事はたぶん表面上は実用可能な程度で実装できなくもないだろうけど、少々準備が面倒でも UE4 のインターフェースとして C++ コードを実装して綺麗に扱った方が良いだろう。

おまけ: xxxx_Implementation の宣言はいつのまにどこでされるのか?

xxxx_Implementation なる override 定義している関数の元の virtual な定義は、 xxxx.generated.h の中で、

#define MyApp_Source_MyApp_MyImplements_h_14_RPC_WRAPPERS \
   virtual float GetMyValue_Implementation(); \
 \
   DECLARE_FUNCTION(execGetMyValue) \
   { \
       P_FINISH; \
       P_NATIVE_BEGIN; \
       *(float*)Z_Param__Result=this->GetMyValue_Implementation(); \
       P_NATIVE_END; \
   }

などのように UE4 のヘッダープリプロセッサーと CPP 黒魔術によって生成されている。興味があればビルドプロセスのヘッダープリプロセッサーによって生成後の xxxx.generated.h を読んでみるとよい。

参考

  1. Interfaces in C++ - Epic Wiki