【Unreal Engine】構造体(Struct)でデータをまとめる:値型の落とし穴つき

作成: 2025-12-12最終更新: 2026-07-20

変数が増えすぎてグラフが読めない——関連する値はStructにまとめます。Make/Breakの使い方と、配列の中身を書き換えたのに反映されない「値型の罠」を図解し、アイテムデータを設計する実践つき。

Blueprintを作り込んでいくと、変数リストが PlayerNamePlayerHealthPlayerAttackPlayerIcon と増えていきます。関数に渡すたびに 4本のワイヤーを引く ことになり、引数を1つ足したくなったら、呼んでいる箇所を全部直すはめになります。

構造体(Struct) は、この「いつも一緒に扱う値」を 1つの型にまとめる 仕組みです。ワイヤーは1本になり、項目を足しても呼び出し側は無傷です。

ただし構造体には、Blueprintで ほぼ全員が一度は踏む罠 があります。 書き換えたのに反映されない という現象です。この記事では、構造体の作り方と使い方に加えて、この罠が起きる仕組みと直し方を解説します。

バラバラだった4本のワイヤーが、1本にまとまるイメージ

この記事でわかること

  • 構造体は いつも一緒に扱う値 をまとめる型
  • 取り出すのが Break 、組み立てるのが Make
  • 値型 なので、Getで取り出すのは コピー 。ここが最大の罠
  • 配列の中身を書き換えるには Set Array Elem参照で取得
  • 実践:アイテムデータを設計して、書き換えられるようにする

Sponsored

いつ構造体にするか

判断はシンプルです。 いつも一緒に動く値が3つ以上あるかどうか

バラバラの4つの変数と、1つにまとまった構造体を対比した図

アイテムを例に考えてみます。「名前」「攻撃力」「価格」「アイコン」——これらは常にセットで扱われます。名前だけ渡して攻撃力を渡し忘れる、ということは起きません。 1つのモノを表している からです。

こうした値をバラバラの変数にしておくと、次の問題が出てきます。

  • 関数の引数が増える: 4つの値を渡すのに、ワイヤーを4本引く
  • 項目を足すと壊れる: 「レアリティ」を追加した瞬間、呼び出し箇所を全部直すことになる
  • 配列にできない: 「アイテムを10個持つ」を表現するのに、4本の配列を並行管理する羽目になる

構造体にすると、これが 1本のワイヤー、1つの配列 になります。

バラバラの変数構造体
関数に渡すワイヤー4本1本
項目を追加呼び出し側を全部修正構造体を直すだけ
複数持つ配列を4本並行管理配列1本

配列との違い: 配列は 同じ型を並べる もの、構造体は 違う型をまとめる もの。「10個のアイテム」は配列、「1個のアイテムが持つ情報」は構造体です。両方を組み合わせて 「構造体の配列」 にするのが、インベントリの定番の形になります(実際に組む手順は インベントリシステムを作る で扱います)。

作り方とMake/Break

コンテンツブラウザで右クリック → BlueprintsStructure で作れます。名前は S_ を頭に付ける と、変数一覧で見分けやすくなります。

開いたら Add Variable で項目を足していきます。それぞれに型とデフォルト値を設定できます。

そして使うときのノードが2つです。

Makeで値から構造体を組み立て、Breakで構造体から値を取り出す様子の図
ノード向き使う場面
Make値 → 構造体新しく1件作る(アイテムを生成する)
Break構造体 → 値中身を取り出す(名前をUIに表示する)

覚え方は 「Makeで作って、Breakで開ける」 。箱に詰めるのがMake、箱を開けるのがBreakです。

必要なピンだけ出せます。 構造体を繋いだピンを右クリック → Split Struct Pin で、その場に中身を展開できます。また Make/Break ノードの詳細パネルでは、使わないピンを隠せます。10項目ある構造体でも、必要な2つだけ見えていればグラフはすっきりします。

最大の罠:値型のコピー

ここが本題です。 構造体は値型 で、この意味を理解しているかどうかで、詰まる時間が大きく変わります。

配列からGetした構造体はコピーであり、書き換えても元の配列は変わらないことを示す図

たとえば、インベントリ(構造体の配列)の1番目のアイテムの耐久値を減らしたいとします。素直に書くとこうなります。

Get(Inventory, Index 0)        ← アイテムを取り出す
  → Break S_Item                 ← 中身を開ける
  → 耐久値から1を引く
  → Make S_Item                  ← 組み直す

そして、元の配列は1ミリも変わりません。

理由は、Get が返すのが コピーだから です。取り出した時点で別物になっているので、それをいくら書き換えても、配列の中身には影響しません。

直し方は2つあります。

方法1:書き戻す(Set Array Elem)

Get(Inventory, Index 0) → Break → 耐久値を減らす → Make
  → Set Array Elem(Target: Inventory, Index: 0, Item: 作り直した構造体)  ← ★書き戻す

素直で分かりやすい方法です。 取り出す → 変える → 戻す の3手を必ずセットにします。

方法2:参照で取り出す

配列の Get ノードを 右クリック → Get a reference に切り替えると、コピーではなく 元への参照 が返ります。この場合は Set Members in Struct で直接書き換えられます。

なぜこんな仕様なのか。 値型はコピーが渡るぶん 安全 だからです。関数に渡した構造体を関数側が書き換えても、呼び出し元は影響を受けません。この安全性と引き換えに、「書き換えたいときは明示的に書き戻す」必要があります。

症状の見分け方: 「値を変えるノードは通っているのに、次に見ると元に戻っている」——これが出たら、まずコピーを疑ってください。

Sponsored

変更するときの注意

もう1つ、実務で効いてくる注意点があります。 Blueprintの構造体は、あとから項目を変えると危ない

構造体にメンバーを追加したとき、それを使っていたDataTableの値がリセットされる様子の図

構造体にメンバーを追加・削除・並べ替えすると、 それを使っている場所の値がリセットされることがあります 。とくに危険なのが、DataTableに100件のデータを入れた後で構造体を変更するケースです。

対策は現実的なところで2つです。

  • 設計を先に固める: DataTableに大量のデータを入れる前に、構造体の項目を決め切る。あとから足しそうな項目は、最初から入れておくほうが安全です
  • 変更前にバックアップ: DataTableはCSVに書き出せます。構造体をいじる前にエクスポートしておけば、崩れても戻せます

C++で定義するという選択肢もあります。 構造体をC++側で持つと、この問題はかなり軽くなります。

USTRUCT(BlueprintType)
struct FItemData : public FTableRowBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FText DisplayName;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 Attack = 0;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TObjectPtr<UTexture2D> Icon = nullptr;
};

USTRUCT(BlueprintType) を付けるとBlueprintから使えるようになり、FTableRowBase を継承するとDataTableの行として使えます。本格的にデータを持つなら C++側で定義する と覚えておくと、後々効いてきます(→ 最初のC++クラスを作る)。


実践:アイテムデータを設計する

RPGの装備、ローグライトの戦利品、サバイバルゲームのクラフト素材——「アイテムを持つ」仕組みはどのジャンルにも登場します。ここでは 構造体を作り、配列で持ち、中身を書き換える まで通してやります。

再現用の準備: 次を用意します。

種類名前内容
StructureS_Itemアイテム1件の情報
BP_Player の変数InventoryS_Item 型の配列

ステップ1:構造体を作る。 右クリック → BlueprintsStructureS_Item と名付けて開き、次の項目を作ります。

変数名デフォルト値
DisplayNameText(空)
AttackInteger0
DurabilityInteger100

デフォルト値を入れておくと、Make ノードで毎回入力しなくて済みます。 Durability を100にしておく のがポイントです。

ステップ2:配列として持つ。 BP_Player に変数 Inventory を作り、型を S_Item に。そして型の右にあるアイコンから Array(配列) に変更します。

ステップ3:アイテムを追加する。 BeginPlay で2件入れてみます。

Make S_Itemで構造体を組み立て、Add で Inventory 配列に追加するノードグラフ
Event BeginPlay
  → Make S_Item(DisplayName: "鉄の剣", Attack: 10, Durability: 100)
  → Add(Target: Inventory)
  → Make S_Item(DisplayName: "木の盾", Attack: 2, Durability: 50)
  → Add(Target: Inventory)

ステップ4:罠を体験する。 ここで、あえて 間違ったやり方 を試します。

Get(Inventory, Index 0) → Break S_Item
  → Subtract(A: Durability, B: 10)
  → Make S_Item(Durability にその結果、他はBreakの出力をそのまま)
  → Print String(作り直した構造体の Durability)    ← 90 と出る

Print String には 90 が出ます。うまくいったように見えます。

ところが、その直後にもう一度配列から読み直してみてください。

Get(Inventory, Index 0) → Break S_Item
  → Print String(Durability)                       ← 100 のまま!

100のままです。 書き換えたのはコピーで、配列の中身は無傷でした。

ステップ5:正しく書き戻す。 Set Array Elem を追加して、関数の形にまとめます。

GetからBreak、値の変更、Make、そしてSet Array Elemで配列に書き戻すノードグラフ
関数: DamageItem(入力: ItemIndex / Integer, Amount / Integer)
  → Get(Inventory, ItemIndex)
  → Break S_Item
  → Subtract(A: Durability, B: Amount)
  → Make S_Item(Durability にその結果、他はBreakの出力をそのまま)
  → Set Array Elem(Target: Inventory, Index: ItemIndex, Item: 作り直した構造体)

DamageItem(0, 10) を呼んでから配列を読み直すと、 今度は90になっています

Playして確認してください。 Set Array Elem を足す前は100のまま、足した後は90に変わります 。この差が体験できれば成功です。

うまくいかないときの切り分けです。

  • やはり値が戻るSet Array ElemIndex が、読み出したときと違っていないか確認
  • 他の項目が消える → Breakの出力をMakeに繋ぎ忘れています。繋がなかった項目は既定値になります
  • 構造体を変えたら値が消えた → メンバー変更の影響です。入れ直すか、CSVバックアップから戻します

ポイントは2つです。

  • 取り出す・変える・戻す、の3手をセットで覚える: 構造体を配列やMapから取ってきて書き換えるときは、必ず書き戻しが要ります。 「Getしたものはコピー」 と覚えておけば、詰まったときに自力で気づけます
  • 項目はできるだけ先に決める: あとからメンバーを足すと、入れたデータが飛ぶことがあります。「たぶん使う」項目は最初から入れておくほうが、結果的に安全です

アイテムが増えてきたら、Blueprintに直接書くのではなく 表で管理する 段階です。この S_Item はそのまま Data Table の行として使えます。1件ずつを独立したアセットとして持ちたいなら Data Assetでデータ駆動にする という選び方もあります。

Sponsored

おまけ:先に知っておくと良いこと

構造体の中に構造体を入れられます。 S_Item の中に S_Stats(攻撃・防御・素早さ)を持たせる、といった入れ子も可能です。ただし深くしすぎるとBreakが階層的になって読みづらいので、 2階層まで を目安にすると扱いやすくなります。

件数が多くて内容が固定なら、DataTableを先に考える。 「アイテムの一覧」のようなデータは、Blueprintで組み立てるよりDataTableに置くほうが管理しやすくなります。その場合、構造体は 行の形を定義する役割 になります。

Enumと組み合わせると分類ができます。 S_ItemItemType(Enum: 武器 / 防具 / 消耗品)を持たせると、種類ごとの処理が書きやすくなります。ただし「炎属性かつ希少」のように 複数の性質を持たせたい なら、Enumより Gameplay Tags のほうが向いています。

関数の戻り値をまとめるのにも使えます。 出力ピンを複数持つ関数も作れますが、意味的にまとまった値なら構造体1つで返すほうが、呼び出し側がすっきりします。

状態を持たせるなら別の選択肢を。 構造体は値型なので、「常に最新の状態を共有したい」ものには向きません。HPのように複数から参照・更新される状態は、Componentにする ほうが素直です。


まとめ

構造体を使うときに押さえる点は、この4つです。

観点結論
いつ使うかいつも一緒に扱う値が3つ以上
組み立て・分解Make で作り、 Break で開ける
最大の注意値型 。Getはコピーなので、書き戻さないと反映されない
変更の注意メンバーを足すと 既存データが飛ぶことがある

そして実務上の指針が1つあります。 Getしたものはコピー 。これさえ覚えておけば、「変えたはずなのに戻っている」で悩む時間がなくなります。

あなたのBlueprint、いつも4本まとめて引いているワイヤーはありませんか?