Unreal Engine 4で、ランタイム時にテクスチャやマテリアルの色を自由に指定したいときに、UE4 Editorで採用されているColor Pickerを使いたいと思ったことがある方も少なくないのではないでしょうか。
通常は新規Color Pickerウィンドウが生成されると思いますが、今回はUMGとしてこのColor Pickerを画面内に埋め込み、且つ、上記動画サンプルのように、Color Pickerの色を取得するだけではなく、Color Pickerの色を外部から指定することも出来るようにしたいと思います。
◆NativeWidgetHostを使用して、Color PickerをUMGで使えるようにする
Yuki Ogura (unwitherer)さんのブログ記事が完璧ですので、まずはそちらを読んで下さい。私もこの記事で初めてNativeWidgetHostを使えるようになりました。
花繁吹き [UMG]NativeWidgetHostを継承してカラーピッカーを作る
NativeWidgetHostを使って、カラーピッカーを作る方法についてまとめました。 #UE4
花繁吹き: [UMG]NativeWidgetHostを継承してカラーピッカーを作る https://t.co/8FjmBK8jlM— unwitherer (@unwitherer) 2017年7月15日
◆SColorPickerを拡張する
上記Yuki Oguraさんのブログでは、SColorSpectrumを扱っていますが、SColorPickerを使えばUE4ユーザーが普段から見慣れているColor PickerをUMG上で使うことが出来ます。
但し、1つのColor Pickerで複数カ所の色を指定しようとすると(上記映像では、右側の2つの長方形それぞれの色を1つのColor Pickerで指定しています)、ちょっとした工夫が必要になります。
方法は(おそらく)2つあります。
1. SColorPickerクラスのProtectedメンバ関数であるSetNewTargetColorRGB関数をPublicで使えるような、SColorPicker継承クラスを作る
2. 適切なタイミングで新しいColor Pickerを作り、古いColor Pickerを削除する(同じ位置に表示させることで、見た目上単一のColor Pickerを使っているようになる)
結論としては2番目の方が自分のやりたいことが出来ましたが、一応両方の方法を示しておきます。
・SColorPickerを継承したクラス
1 2 3 4 5 6 7 8 9 10 11 12 |
/* Copyright Hirofumi Seo, M.D. */ #pragma once #include "Colors/SColorPicker.h" class SCustomColorPicker : public SColorPicker { public: void SetColorRGB(const FLinearColor new_color) { SetNewTargetColorRGB(new_color, /*bForceUpdate = */true); } }; |
SetNewTargetColorRGB関数がSColorPickerクラスのProtectedメンバ関数として公開されているため、SColorPickerを継承したSCustomColorPickerクラスでPublic関数を作り、この関数からSetNewTargetColorRGBを呼び出してあげます。
HSV用にSetNewTargetColorHSV関数も用意されています。Color Picker系の各種クラスは、場所によってRGBだったりHSVだったりまちまちで、どのような規則で使い分けられているのかイマイチよくわかりませんでした。
・DelegateとTSharedPtr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/* Copyright Hirofumi Seo, M.D. */ #pragma once #include "CoreMinimal.h" #include "Components/NativeWidgetHost.h" #include "ColorPickerWidget.generated.h" class SCustomColorPicker; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FColorPickerColorChangedDelegate, const FLinearColor&, new_color); UCLASS() class BRAINSIMULATOR_API UColorPickerWidget : public UNativeWidgetHost { GENERATED_BODY() public: UColorPickerWidget(const FObjectInitializer& ObjectInitializer); UFUNCTION(BlueprintCallable, Category = "custom_UMG") void SetInitialColor(const FLinearColor initial_color); UPROPERTY(EditAnywhere, BlueprintReadWrite) FLinearColor selected_color_rgb_; UPROPERTY(BlueprintAssignable, Category = "custom_UMG") FColorPickerColorChangedDelegate ColorPickerColorChangedDelegate; private: TSharedPtr<SCustomColorPicker> color_picker_; void GenerateColorPicker(const FLinearColor& initial_color); }; |
Color Pickerで色が変更されたことをBluePrints側で捉えられるように、Delegateを1つ用意します。
また、SCustomClorPickerをメンバ変数として持てるように、上記Yuki Oguraさんのブログで使われているTSharedRefの代わりにTSharedPtrを用います。
実装は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/* Copyright Hirofumi Seo, M.D. */ #include "ColorPickerWidget.h" #include "SCustomColorPicker.h" #include "Kismet/KismetMathLibrary.h" UColorPickerWidget::UColorPickerWidget(const FObjectInitializer& ObjectInitializer) : UNativeWidgetHost(ObjectInitializer), selected_color_rgb_(FLinearColor::White) { GenerateColorPicker(selected_color_rgb_); } void UColorPickerWidget::SetInitialColor(const FLinearColor initial_color) { GenerateColorPicker(initial_color); //color_picker_->SetColorRGB(initial_color); } void UColorPickerWidget::GenerateColorPicker(const FLinearColor& initial_color) { Super::ReleaseSlateResources(/*bReleaseChildren = */true); if (color_picker_.IsValid()) { color_picker_.Reset(); } selected_color_rgb_ = initial_color; SAssignNew(color_picker_, SCustomColorPicker) .TargetColorAttribute(selected_color_rgb_) .OnColorCommitted_Lambda([this](FLinearColor changed_color) { selected_color_rgb_ = changed_color; if (ColorPickerColorChangedDelegate.IsBound()) { ColorPickerColorChangedDelegate.Broadcast(changed_color); } }) .ExpandAdvancedSection(true) .UseAlpha(false) .sRGBOverride(true); SetContent(color_picker_.ToSharedRef()); } |
大切なのは23行目のSAssignNewと、ヘッダで定義したDelegateの呼び出し部分です。
ExpandAdvancedSection, UseAlpha, sRGBOverrideなどは使用用途に合わせて適宜変更して下さい。どれが何をしているかは、SColorPicker.hを見れば何となくわかると思います(私も何となくしかわかっていません)。
12行目と13行目とでコメント行を入れ替えると、Color Picker 1つを使いまわすか、Color Pickerをその都度作り直すかが変わります。
Color Picker 1つを使いまわす場合、Color Pickerの”old”の部分の色が、コンストラクタで設定する最初の1回のみしか指定出来ませんでした。
oldの色はTargetColorAttributeで指定した色が反映され、その後は変更出来ないようです。よって、どうしても変更したい場合には一度Color Pickerを削除して、新たに作る必要がありました。
そのため、GenerateColorPickerで、まず既存のColor Pickerを捨てています。
また、わざわざ selected_color_rgb_ = initial_color としているのは、TargetColorAttributeの引数はローカル変数ではなくグローバル変数である必要があるためです(…理由はよくわかりませんが、そのような仕様のようです)。
Color Pickerで色が変更されたときに発生するイベントであるOnColorCommitted_Lambda内で、Deleagteを走らせるようにしてやればOKです。
◆BluePrints側の実装
一例ですが、以下のようにしてみました。
色の外側をクリックしたときに非選択となるように、ダミーでoutside_buttonというボタンを画面全体に作っています。
これらを実行すると、最初に掲載した映像のようになります。
◆まとめ
NativeWidgetHostについては英語でもほとんど全く情報が無く、Yuki Ogura (unwitherer)さんのブログ記事は超貴重です。
簡単に色々なWidgetをUMGで使えるようになりますので、ぜひ有効活用してみて下さい。
スポイトも使えるので便利です。ただ、sRGB previewのチェックを非表示にしたり、ColorWheelとColorSpectrumとの切り替えボタンを無効にしたりするためには、エンジン改造を行うか、完全自作のColor Pickerを作る必要があるようです。
しかし、これくらいUE4側で公式にUMG側で用意しておいてくれても良いのでは…とも思ったりもします。
packing error: Stack for UAT
I don’t know how to solve this problem… my code is exactly the same as yours.
Do you have github?
Can you share some successful projects?
Thank you for your comment. I recently had the same problem that it can’t be packaged while playable in Play in Editor.
Here is the updated, successful project!
https://github.com/HSeo/ColorPickerTest419
Thank you
Your explanation and example were incredibly helpful.
I struggled for days with this problem. but it really helped me solve my problem.
I sincerely appreciate the effort and knowledge you’ve shared.
Again, thank you for providing such valuable information.
Thank you for the commnet, and I’m sorry the article is written in Japanese.