少しでもアクションRPGっぽいものを作りたいと思いながら、その要素の一つであるレベルアップシステムと経験値バーを実装する方法をまとめました。
※半年以上前に完成していた内容を記事にして投稿しました。
Unreal Engineのアップデートにより、情報が古くなっている可能性があります。
▼環境
Unreal Engine 5.6.1
▼実施期間
2025.10.6~2025.11.04
(動作サンプル)UE5.x _ 経験値テーブルを利用したレベルアップシステムとEXPバーを作ってみた – YouTube
期待する動作と仕様
簡単な全体像

今回手を出そうと思ったのは、RPGのようにレベルの概念のあるゲームではよく見かける経験値バーです。
小さい機能を多く学ぼうと思った結果、「コインを入手する(触る)」仕組みと組み合わせることにしました。
経験値の入手方法はいくつか考えられます。
例えば、敵を倒したり、クエストの報酬だったり、あるいは経験値を入手するアイテムを使用するなど。
ブループリントインターフェイス(Blueprint Interface)機能を使うことで、そのような”きっかけ”を無視して、「経験値をいくつ入手したよ」という通知を送ることができました。
経験値テーブルというワードは、聞いたことがあると思います。
昔は、ゲームの攻略本にその表が載っていたこともありました。
経験値が溜まれば経験値バーに反映して、割合を増加させたり、レベルアップすれば割合をゼロにすることが求められます。
最終的に、作成するファイルは以下の5(6)つになりました。
- 経験値の入手を通知する仕組み(ブループリントインターフェイス)
- 「触れたら消える」立方体オブジェクト(ブループリントクラス)
- 経験値テーブル(データテーブル)
- 経験値テーブルの表頭(構造体)
- プレイヤーのレベルと経験値の所持状況を表示するユーザーインターフェース(ウィジェットブループリント)
- レベルや所持経験値をプレイヤーが保持する(ThirdPersonCharacterのブループリント)
動作チェックも含めて実装するもの

レベルアップシステムと経験値バーを実装するには、それを動作させるトリガーもないと実装を確認できません。
(今回はUnreal Engineのチュートリアルの基本のような、「コインを取得したらコインが消える」処理を取り入れているけど、もっと楽をするなら「特定のキーを押したら経験値を加算する」という処理でいいと思う。)
経験値テーブルを予め用意して、入手した経験値を累積させたり、レベルの値を加算する処理を作ることになりました。
内部の値をプレイヤーが見えるようにするためにウィジェットも必要ですね。
「経験値が溜まったらレベルアップする」という挙動は、言うのは簡単だけれど、考慮しておかなければならないこともありました(Fig1)。
例えば、プレイヤーのレベルが最大の場合、これ以上レベルアップすることはないし、そうであれば経験値を加算する必要はありません。
経験値を入手しないように分岐させることも必要かもしれません。
経験値が溜まってレベルアップしたら、余剰の経験値だって持ち越さないとプレイヤーは納得しないでしょう。
もし某クエストのメタル系のような、リターンの大きい敵を倒して大量の経験値を入手した場合など、一度に複数回レベルアップが発生することだって起こりうることを想像する必要がありました。

アルゴリズム・処理の流れを考える
レベルアップシステムにおいて、考慮しておきたい処理をまとめてみました(Fig2)。
プレイヤーのレベルが上限かどうかを判定して、そうであれば経験値を加算したり、レベルアップ処理そのものをスキップします。
RPGでは敵を何度も倒して経験値を溜めていくので、「経験値の加算」しか処理を行わないことがほとんどかもしれません。
最後のレベルアップを果たしてレベル上限に達した場合、当然ながら複数レベルアップするようなことは起こりません。
入手した経験値によってレベルアップが発生する場合、「持ち越した経験値が次の必要経験値量を上回ったかどうか」も考慮すべき点だと思います。
必要経験値量を下回れば、処理を終了します。

テスト用の学習教材
最初に「オブジェクトに触れて、『入手する』動作」を作りたくて参考にした動画がこれです。
コインのモデルをダウンロードするにはアカウント登録が必要だったので、表情で用意されている立方体モデルで代用しました。
ThirdPersonShootingのテンプレートに含まれる白の立方体に、スターターコンテンツに含まれるマテリアルを割り当てるとコンテナ風になったのでこれで充分かと。
How to Pick Up Coins in Unreal Engine 5 – YouTube
そして、経験値バーの参考になった動画がこちら。
シンプルに、経験値を取得するたびにゲージが増加し、レベルアップするとゲージがリセットされ、プレイヤーのレベルに相当する数字が増加します。
How to Create a Leveling XP System in Unreal Engine 5 – YouTube
経験値テーブルの作成と参照
・DT_ExpTable(データテーブル)
・F_ExpTableHeader(構造体)
経験値テーブルはデータテーブルを用いますが、そのために表頭(ヘッダー)に対応した構造体も作成しました。
当初はレベルと「次のレベルに必要な経験値」と「累計経験値」を用意していましたが、データテーブルが固定で持っている「行の名前」はレベルの数値と同じだと考えることができるので、データの重複をさけるために「レベル」の項目は省略することにしました。
また、経験値テーブルについては先駆者様の知恵を借り、レベルが10までのテーブルを実装しました(Fig3)。
【参考サイト】
ゲーム制作で使える!経験値テーブルの作り方のコツ _ Taiyo Project
構造体の命名規則は「接頭辞がF」。
過去の記事では、他所の解説を真似してSTにしていましたが、AIの見解では「少数派で伝わらない」とのこと。(自分ひとりの開発ならアリか?)

データテーブルの非同期ロードについて
レベルアップ処理の基本は、次の2点だと思います。
- 必要経験値量を上回った場合に、レベルを一つ増加させる
- 必要経験値量を上回った超過分を、「現在の経験値量」として更新する
この処理を行うためには、データテーブルに記述された値を読み込まなければ計算ができません。
そのためにデータテーブルにアクセスしたいのですが、それをずっとメモリに置いておくのはどうなのでしょうか。
「レベルアップのときだけ使いたい」と言いたいところですが、今回は「入手した経験値を加算する際に、必要経験値量を超えたかどうか」のチェックで毎回必要な処理になっています。
非同期ロードと言っても経験値を入手するたびに実行されると考えると頻度は高めで、「だったら経験値テーブルくらいは常に持ってていいんじゃないか」とも思う。
ゲーム開発素人にはわからない。
Unreal Engineを勉強していると、サードパーソンテンプレートに用意されているThirdPersonCharacterのブループリントに変数やノードを追加していくのですが、それが際限なく増えていくのもまた怖かったので、「処理は個別に関数化して管理したい」と思いました。
ブループリントインターフェイスによって取得した経験値を受け取り、それをレベルアップ処理の関数に送りたかったのですが、
「非同期ロードアセットは関数グラフに配置できません」というエラーにより、関数の外に非同期ロードの処理を置かざるを得ませんでした。(Fig4)

処理の流れを追ってみる
では、レベルアップ処理の関数「ProcessExpAndLeveling」の中身を見ていきます。
ThirdPersonCharacterブループリントにレベルアップ処理の関数を作成。
初期化処理の中で「レベルアップの回数」、「レベルアップが可能かどうか」、「経験値テーブルから次レベルへの必要経験値の読み込み」を行っています。(Fig5)
「レベルアップの回数(n)」は、「レベルXからレベルY」への差分を求めるために使います。
「レベルアップが可能かどうか」は、プレイヤーがレベル上限に達しているかどうかを判定します。
※「0」や真偽のノードを簡単に追加するために、参考にしたサイトのマクロを真似しています。
【参考サイト】
20年オヤジのUnreal Engine 4 TIPS – SEGA TECH Blog

現在所持している経験値と、獲得(通知)された経験値を加算して、その値が経験値テーブルから読み込んだ「次レベルへの必要経験値量」を下回れば処理を終了します。(Fig6)
上回ればレベルアップの処理に進み、「レベルアップ可能かどうか」が「偽(真でなくなる)」になれば、ループを抜けてレベルアップの処理を終了。
レベルアップした結果をプリントするノードへ移行します。(Fig7)


レベルアップのループの中では、「プレイヤーのレベル」と「レベルアップした回数」をインクリメントしています。(Fig8)
加算したプレイヤーのレベルが上限に達した場合は、「次レベルへの必要経験値量をゼロ」にして、「レベルアップ不可」にします。

レベル上限に達しない限りは、繰り越した経験値が必要経験値を下回るまでループが続きます。(Fig9)
一つ前で加算したプレイヤーレベルをもとに経験値テーブルから必要経験値の値を読み込んで、繰り越した経験値がまだ必要経験値以上であれば、レベルアップがもう一度行われます。
(ここは関数内ですが、外で非同期ロード時にデータテーブル型として読み込み済みのものを参照しています。)

コンテナに触れて経験値を取得(通知)する
・BI_AddExp(ブループリントインターフェイス)
・BP_ExpObject(ブループリントクラス)
フィールドに配置したコンテナ(アクタ)にプレイヤーが触れると、コンテナは消滅し、経験値を取得します。(Fig10)
取得した経験値はブループリントインターフェイスで通知されます。
これはテストのための簡単なトリガーなので、取得する経験値「15」は決め打ち(ハードコード)です。

非同期ロードの部分で提示したノードを改善しました。
経験値テーブルを参照するよりも前に、「レベル上限に達していれば『EXP FULL!』を表示して以降の処理を行わない」ようにしました。(Fig11)

ウィジェットのバグ修正とレベルアップシステムの完成

・WPB_ExpBar(ウィジェットブループリント)
「処理は間違っていないハズなのに、プログレスバーが動かない」というトラブルにハマって、参考にした動画を見比べたりしていました。
このとき、変換ノードを挟んでも挟まなくても「浮動小数点(倍精度)」と表示されているのに、変換ノードがあると動かない・外すと期待した動作をするのは、正直よくわかっていません。(Fig12)
もしつまづいたら、この辺りも確認した方がいいかも。

完成したシステム
冒頭に添付した動画の通り、完成した状態ではレベル4までは1つのコンテナ(経験値15)につき1回レベルアップが行われます。
そして次のコンテナでレベルが「6」になります。
レベルが4から5に上がった際に「必要経験値は14に更新」され、「持ち越した経験値が14」なので、レベルが6に上がります。
1度の加算処理で2回レベルアップが実現する例です。(Fig13)
プログレスバーも、必要経験値量に対する所持経験値の割合を反映してくれています。

【ブループリントの共有サイト】
4_EXP Bar _ ProcessExpAndLeveling posted by chromitz _ blueprintUE _ PasteBin For Unreal Engine
※ブループリントを共有できるblueprintUEですが、マクロを使っている箇所が意図しない表示になっているので、このページに添付した画像と見比べてほしい。
あとがき
車輪の再発明。
企業が1か月もかけて経験値バーなんて作ってるわけがないんだから、既にあるツールやテンプレートを活用して、時間はもっと他のことに費やしているだろう。
実践して記事にするまで半年も空いてしまった不遇の解説記事。
幸いにも、スライドは完成していたので文章の再構成くらいで済んだ。
もっと自分の力でゲーム開発してみたかったけど、ネット上の知識や解説は体系化されていないし、手探りで調べる時間が多すぎてモチベーションの維持も難しい。
強力なゲームエンジンが公開されていても、学ぶことはとても多く、小さくても一人で完成させられるのか先が見えずにいる。
(UE6の情報は出てきて、ブループリントが非推奨になるとか、Verseという言語が入るとか、そういう話を見かけている。ブループリントと心中する前に、やはりC++(RUST?)は学びたいと思う。)