5.

UE5 UMGのマウス操作UI|ドラッグ移動とドラッグ&ドロップの実装

編集
この記事の要点
  • UMG ウィジェットをマウスでドラッグ移動させるには OnMouseButtonDown + OnMouseMove + OnMouseButtonUp の組み合わせ
  • 正式なドラッグ&ドロップは DetectDragIfPressed + DragDropOperation
  • 位置の反映は SetRenderTranslation または親 Canvas の Position プロパティ
  • マウス位置取得は GetMousePositionOnViewport または PlayerController の GetMousePosition
  • 入力モードを UI Only または Game and UI にしないとマウスイベントが UMG に届かない

2 種類のマウス操作 UI

Unreal Engine の UMG で「マウスで動かす UI」を作る方法は大きく 2 つあります:

シンプル移動正式ドラッグ&ドロップ
用途ウィジェット自身が動くUI 間でアイテム移動
主な APIOnMouseButtonDown/Move/UpDetectDragIfPressed + DragDropOperation
ペイロードなしOperation オブジェクトで運ぶ
視覚フィードバック自前Drag Visual ウィジェットで自動
難易度やや高

シンプル移動: ウィジェットをドラッグ

Windowsアプリのウィンドウのように、ウィジェット自身をマウスで掴んで動かす実装です。

Blueprint 実装

[User Widget (例: WBP_DraggableWindow)]

変数:
  bIsDragging   (Boolean)
  DragOffset    (Vector2D)

[OnMouseButtonDown] override
  Input: MyGeometry, MouseEvent
  - IsLeftMouseButtonDown? → true
  - bIsDragging = true
  - DragOffset = MouseEvent.GetScreenSpacePosition - MyGeometry.GetAbsolutePosition
  - Return Handled() / Capture Mouse

[OnMouseMove] override
  - if bIsDragging
    - NewPos = MouseEvent.GetScreenSpacePosition - DragOffset
    - SetPositionInViewport(NewPos)  ← Self
  - Return Handled

[OnMouseButtonUp] override
  - bIsDragging = false
  - Return Handled / Release Mouse Capture

C++ 実装

// UDraggableWindow.h
UCLASS()
class MYGAME_API UDraggableWindow : public UUserWidget
{
    GENERATED_BODY()
protected:
    virtual FReply NativeOnMouseButtonDown(
        const FGeometry& InGeometry,
        const FPointerEvent& InMouseEvent) override;

    virtual FReply NativeOnMouseMove(
        const FGeometry& InGeometry,
        const FPointerEvent& InMouseEvent) override;

    virtual FReply NativeOnMouseButtonUp(
        const FGeometry& InGeometry,
        const FPointerEvent& InMouseEvent) override;

    bool bIsDragging = false;
    FVector2D DragOffset;
};

// UDraggableWindow.cpp
FReply UDraggableWindow::NativeOnMouseButtonDown(
    const FGeometry& Geo, const FPointerEvent& Ev)
{
    if (Ev.GetEffectingButton() == EKeys::LeftMouseButton)
    {
        bIsDragging = true;
        DragOffset = Ev.GetScreenSpacePosition() -
                     Geo.GetAbsolutePosition();
        return FReply::Handled().CaptureMouse(TakeWidget());
    }
    return FReply::Unhandled();
}

FReply UDraggableWindow::NativeOnMouseMove(
    const FGeometry& Geo, const FPointerEvent& Ev)
{
    if (bIsDragging)
    {
        FVector2D NewPos = Ev.GetScreenSpacePosition() - DragOffset;
        SetPositionInViewport(NewPos, /*bRemoveDPIScale=*/false);
        return FReply::Handled();
    }
    return FReply::Unhandled();
}

FReply UDraggableWindow::NativeOnMouseButtonUp(
    const FGeometry& Geo, const FPointerEvent& Ev)
{
    bIsDragging = false;
    return FReply::Handled().ReleaseMouseCapture();
}

正式なドラッグ&ドロップ(インベントリ等)

アイテムスロット間でアイコンをドラッグして移動する場合は、Unreal 標準の Drag-and-Drop システムを使います:

手順1: DragDropOperation 派生クラスを作る

[Blueprint Class > DragDropOperation 派生]
名前: BPDDO_Item

変数を追加:
  PayloadItemID (Name)
  PayloadIcon   (Texture2D)
  SourceSlot    (UserWidget Reference)

手順2: ドラッグ元のウィジェットで Detect

[InventorySlot WBP]

[OnMouseButtonDown]
  Input: MouseEvent
  → Detect Drag if Pressed (Pointer Event, Key=Left Mouse Button)
  → Return Handled

[OnDragDetected]
  Input: MouseEvent
  - Create Widget (W_DragVisual)  ← 半透明のアイコン
  - Create DragDropOperation (Class = BPDDO_Item)
    - PayloadItemID = この slot の ItemID
    - PayloadIcon = この slot の Texture
    - SourceSlot = Self
    - Default Drag Visual = W_DragVisual
    - Pivot = Center Center
  - Return Operation

手順3: ドロップ先で受け取る

[InventorySlot WBP / 他の Slot]

[OnDrop]
  Input: Operation
  - Cast to BPDDO_Item
  - if Success:
    - 自スロットに Operation.PayloadItemID を設定
    - SourceSlot を空に
  - Return: true (成功扱い)

[OnDragEnter] / [OnDragLeave] でハイライト切り替え

位置の反映: 3 つの方法

方法使い所
SetPositionInViewport(NewPos)UserWidget 全体を動かす(Add to Viewport で追加した物)
Set Render Translation軽量な見た目だけの移動。レイアウト計算非対象
親 Canvas Panel Slot の PositionCanvas Panel 配下の子の位置移動

マウス位置の取得 API

// PlayerController から
APlayerController* PC = GetWorld()->GetFirstPlayerController();
float MouseX, MouseY;
PC->GetMousePosition(MouseX, MouseY);

// ビューポート座標 (UMG 用、DPI スケール考慮)
FVector2D MousePos;
UWidgetLayoutLibrary::GetMousePositionOnViewport(this, MousePos);

// DPI スケールも考慮
FVector2D ScaledMousePos =
    UWidgetLayoutLibrary::GetMousePositionScaledByDPI(PC);

// Geometry の Local 座標
FVector2D LocalPos = InGeometry.AbsoluteToLocal(
    InMouseEvent.GetScreenSpacePosition());

入力モードの設定(重要)

マウスイベントが UMG に届くには、PlayerController の入力モードが適切でないといけません:

// Pause メニュー等
FInputModeUIOnly UIOnly;
UIOnly.SetWidgetToFocus(MyWidget->TakeWidget());
PlayerController->SetInputMode(UIOnly);
PlayerController->bShowMouseCursor = true;

// HUD でゲーム入力もマウスも両方
FInputModeGameAndUI GameAndUI;
GameAndUI.SetWidgetToFocus(MyWidget->TakeWidget());
GameAndUI.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
GameAndUI.SetHideCursorDuringCapture(false);
PlayerController->SetInputMode(GameAndUI);

// 通常プレイ
FInputModeGameOnly GameOnly;
PlayerController->SetInputMode(GameOnly);
PlayerController->bShowMouseCursor = false;

Z-Order とフォーカス

  • 複数 Widget を重ねた時は AddToViewport(ZOrder) の Z 値で前後関係指定
  • Z が大きいほど手前
  • Pause メニューは Z=1000 など大きめにすると HUD より前に出る
  • マウスホバーが裏のウィジェットに届かないようにVisibility = Visible(または Self Hit Test Invisible でクリックを下に貫通)

Visibility の使い分け

Visibilityマウスイベント
Visible受け取る
Hit Test Invisible受け取らない(自分も子も)
Self Hit Test Invisible自分は受けないが子は受ける
Hidden非表示・受けない・スペース確保
Collapsed非表示・受けない・スペース不要

FAQ

Q: OnMouseButtonDown が呼ばれない
A: ① Visibility = Hit Test Invisible になっていないか、② 入力モードが Game Only になっていないか、③ 親ウィジェットが先にイベントを Handle していないか を確認。

Q: ドラッグ中にマウスがウィジェットから外れると追従が止まる
A: FReply::Handled().CaptureMouse(TakeWidget()) でマウスキャプチャを取得。終了時に ReleaseMouseCapture()

Q: マウス座標がずれる
A: DPI スケールを考慮していない可能性。GetMousePositionScaledByDPI を使うか、SetPositionInViewport の第二引数 bRemoveDPIScale を false に。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. ブループリントでWidgetに引数を追加する方法
  2. Blueprintで特定のクラスのWidgetを閉じる方法
  3. Widgetの前後関係を設定する方法
  4. ウィジェットの画像を選択するとウィジェットを閉じる方法
  5. マウスを動かせるUIを作成する方法
  6. ウィジェットのボタンに文字を書く方法
  7. BPでウィジェットのボタンを選択できなくする方法
  8. ウィジェットの背景ブラーについて
  9. リストビュー (ListView)
  10. EntryWidgetClass
  11. ウィジェットで一部の領域を部品化して再利用する方法

最近更新/作成されたページ