0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UE6時代に向けたVerse入門 ― 現状の整理から基礎・応用・ハマりどころ・他言語との比較ま

0
Posted at

【2026年11月版】UE6時代に向けたVerse入門 ― 現状の整理から基礎・応用・ハマりどころ・他言語との比較まで

Verse自体は既に何年も前から存在していました、自分も多少触れたことがあり、
この度、その時のメモを掘り起こしました、いかんせん内容が古いので、公式のコードをみたりして内容を精査、後はクロードコードにドキュメント化を手伝ってもらい、内容をまとめております。

この記事の前提(必ずお読みください)

  • 本記事は 2026年11月7日時点 の公開情報をもとに書いています。UE6はまだ開発中で、方針は今後も変わる可能性があります(実際、2026年6月から11月の間に大きく変わりました)。
  • Verseのコードは、UEFN(Unreal Editor for Fortnite)で今動いているVerse と、Epic公式の言語仕様書 The Book of Verse に合わせて書いています。
  • Book of Verseは 開発ブランチ先頭の仕様 を書いたものなので、まだリリースされていない機能も入っています。本文では未リリースの機能に 🚧未リリース と付けています。
  • API名(デバイスやScene Graphのコンポーネント)はUEFNのバージョンによって変わります。コピペする前に、お使いのバージョンのAPIリファレンスで確認してください。

目次

  1. UE6をめぐる現状の整理
  2. Verseとはどんな言語か
  3. 基礎文法を詳しく
  4. 応用:実践的なコード例
  5. 現場で詰まりやすいポイント集
  6. 他言語との違い
  7. UE6に向けた学習ロードマップ
  8. まとめ
  9. 参考資料

1. UE6をめぐる現状の整理

1.1 タイムライン

日付 出来事 内容
2023年3月 UEFNとVerseの公開 VerseがUEFNで使えるようになる。ここからFortniteのクリエイティブ島で実際に使われ続けている
2025年 Tim Sweeney氏のポッドキャスト出演 「VerseはUE6の中核になる」「プレビューは2〜3年以内」と発言
2026/05/24 RLCS Paris Major UE6を初めて公式に発表。最初に採用するタイトルは『Rocket League』
2026/06/17 Unreal Fest Chicago「State of Unreal 2026」 UE6のロードマップ発表、UE 5.8リリース、バージョン管理システム「Lore」のオープンソース化
同上 当時の方針 Actor・ブループリントは UE6初期版にも残し、新フレームワークが成熟してから非推奨にする
2026/11/03 Unreal Fest Tokyo 2026 基調講演 方針変更:Actor・ブループリント・C++ Gameplay Frameworkを UE6の早期アクセス時点から削除
2026/11 第2週(予定) UE 5.9 プレビュー AIワークフローの改善、Webをターゲットプラットフォームに追加、レンダリングとアニメーションの更新
2027年末(目標) UE6 早期アクセス Epicが公表している目標時期
早期アクセスの12〜18か月後(目標) UE6 正式リリース だいたい2029年前後

1.2 UE6とは何か ― 「UE5 + UEFN = UE6」

UE6は UE5とUEFNを1つの製品にまとめた次世代エンジン です。Sweeney氏は基調講演で「UE5 plus UEFN equals UE6」と説明しています。

Epicが挙げている主な取り組みは次の3つです。

  1. ゲームプレイのプログラミングモデルをVerseに移す
    Epicの言葉では、Verseは「C++をトランザクション化したもの」です。何千人もの開発者が関わる、ずっと続く大規模なライブ体験を作れるようにすることが目的です。
  2. オープンスタンダードによる相互運用性
    glTFやUSDなど既存の標準で足りるところは、それをエンジンで正式に扱う形式にします。標準がないところは、Epicが仕様を作ってVerse APIとともに公開する方針です。
  3. AI / LLMとの統合
    エンジンの機能をMCP(Model Context Protocol)で外から使えるようにして、ClaudeやGemini、Codexなどのモデルを開発者が自由に組み合わせられる「オープンなMCP基盤」を作るとしています。

これらに加えて、レンダリングの改善、クック時間の短縮、作業の繰り返しを速くすること、モバイル向けの強化も挙げられています。

1.3 Unreal Fest Tokyo 2026 で何が変わったか

ここがいちばん大事な更新点です。

6月(シカゴ)の方針

  • Actorとブループリントは UE6の早期アクセスと初期リリースでもサポートする
  • 新しいフレームワークが十分に成熟してから非推奨にし、移行ツールも用意する

11月3日(東京)の方針

  • 基調講演のスライドでは、Actor / ブループリント / C++ Gameplay Framework の3つすべてに「Remove(削除)」と書かれていた
  • 削除は UE6の早期アクセスの時点から
  • UE6で標準になる構成は Scene Graph + Verse + Verse Gameplay API + MCPとPythonによる制作
  • Sweeney氏はこれを「痛みを伴う変更」と呼び、ブループリントを愛用してきた開発者に謝罪した
  • 理由として挙げられたのは AIによる生産性の向上。Epic社内ではプログラミング、デバッグ、レベルデザインで1.5〜4倍の生産性向上が出ているという。さらに「AIはブループリントよりVerseのコードのほうがずっと正確に読める」とも説明された
  • Epicの公式リリースでは、AIの効率化だけでなく、目指すオープンエコシステムのためでもあり、大規模で安定し、スケールする開発基盤を作るための決断だと説明されている

1.4 残るもの・変わるもの

項目 UE5 UE6(11/3時点の発表)
ゲームプレイ記述 ブループリント / C++ Verse
シーンの基本単位 Actor + ActorComponent Scene Graph(Entity + Component)
C++ Gameplay Framework あり 削除
エンジンの低レイヤー(レンダリング・物理など) C++ C++のまま
制作の自動化 Python / Editor Utility MCP + Python
既存のUE5プロジェクト ― UE5系に残ることもできる。移行にはAIを使った変換ツールが提供される予定(形式は未定)

Fortnite自身も、数千あるブループリントと残っているC++のゲームプレイコードをVerseに移しているところで、Epicは「最初に痛みを受けるのは自分たちだ」と説明しています。その経験とツールを、順次コミュニティに共有する予定です。

1.5 まだ分かっていないこと

本稿の時点で、公式に明らかになっていない点も整理しておきます。

  • プラグイン(C++)からゲームプレイにどこまで関われるか
  • BP / C++ → Verse 変換ツールの形式や精度
  • UE5系を何年サポートするか
  • 各機能の最終的な仕様(Book of Verseにある「未リリース機能」がいつ入るか)

Sweeney氏はX上で「今の最先端のAIモデルは学習時にVerseをあまり見ていないが、1月までに変わる」という趣旨の発言をしています。今はAIが生成したVerseを そのまま信じない ほうが安全です(5章でも触れます)。

1.6 今どう動くべきか

  • UE5で開発中・出荷予定のプロジェクト:そのままUE5系で問題ありません。UE6はまだ早期アクセス前です。
  • これから学ぶ人・数年先のプロジェクトを考えている人:Epic Games Japan代表の河﨑高之氏が勧めているとおり、まずUEFNでVerseとScene Graphを触る のがいちばん確実です。今のところ、Verseを本番環境で動かせる場所はUEFNだけです。

2. Verseとはどんな言語か

2.1 設計の考え方

Verseは、関数型・論理型・命令型の3つの考え方を組み合わせた言語です。Haskellの共同開発者として知られるSimon Peyton Jones氏なども設計に関わっています。

Book of Verseは、3つの基本原則を挙げています。

原則 意味
It's just code 複雑な概念も、言語の基本的な構文の組み合わせで表す
Just one language コンパイル時と実行時で同じ構文を使う
Metaverse first 世界中で共有される巨大なシミュレーション環境を前提に設計する

ここから、Verseの特徴的な仕組みが生まれています。

  • 失敗(Failure):例外もnullも使わずに、「値が出るか、出ないか」で処理を分ける
  • トランザクション:失敗した処理の中で行った変更は、自動で巻き戻る
  • エフェクト指定子:関数が「何をしうるか」(状態を書き換える、待機するなど)を型に書く
  • 構造化された並行処理:race や sync が言語の構文として組み込まれている
  • 後方互換性のチェック:公開済みのAPIを壊す変更は、公開する時点でコンパイラが止める

2.2 開発環境

項目 内容
実行環境 UEFN(無料。Epic Games Launcherから入手)
エディタ VS Code + Verse拡張(UEFNから開くと案内されます)
ファイル .verse
ビルド UEFNのメニュー「Verse → Compile Verse Code」
ログ Print() の出力はUEFNのアウトプットログに出ます
命名規則 型やクラスは snake_case、関数と変数は PascalCase(Epic標準)

2.3 Hello World(クリエイティブデバイス)

UEFNでいちばん基本になる書き方は、creative_device を継承したクラスを作って、レベルに置くことです。

hello_world_device.verse
# --- 使うモジュールを読み込む(C#の using、C++の #include に近い) ---
using { /Fortnite.com/Devices }                    # creative_device など
using { /Verse.org/Simulation }                    # Sleep など
using { /UnrealEngine.com/Temporary/Diagnostics }  # Print など

# クラスの定義: 名前 := class(親クラス):
hello_world_device := class(creative_device):

    # ゲームが始まるとエンジンから呼ばれる入口
    #   <override>  : 親クラスのメソッドを上書きする
    #   <suspends>  : 途中で待機できる(非同期の)関数
    #   :void       : 戻り値なし
    OnBegin<override>()<suspends>:void =
        Print("Hello, world!")
        Print("2 + 2 = {2 + 2}")   # {} の中に式を書いて文字列に埋め込める

ポイント

  • := は「型を推論して定義する」演算子です。ここではクラス型 hello_world_device を定義しています。
  • ブロックは インデント で表します(Pythonと同じ)。{} で囲む書き方もできます。
  • OnBegin には <suspends> が付いているので、中で Sleep() などを使って待つことができます。

3. 基礎文法を詳しく

3.1 式指向とコメント

Verseでは ほぼすべてが式 で、値を返します。if や for も値を返します。関数は 最後に評価した式の値 を戻り値として返します。

# 1行コメント

<# 複数行の
   ブロックコメント #>

# if は式なので、そのまま代入できる
Label := if (Score > 100) then "High" else "Low"

# 関数の戻り値は最後の式
Square(X:int):int = X * X

3.2 定数と変数

# 定数(デフォルトは不変)
MaxHp : int = 100        # 型を書いて定義
Name := "Hero"           # 型を推論(string になる)

# 変数(var を付けると変更できる)
var Hp : int = 100       # var では型と初期値の両方が必要(:= は使えない)
set Hp = 80              # 再代入には必ず set を付ける
set Hp -= 10             # += -= *= も set と一緒に使う
書き方 意味 他言語での近いもの
X : int = 1 不変、型を明示 const int x = 1;
X := 1 不変、型を推論 const x = 1; / val x = 1
var X : int = 1 変更可能 int x = 1; / let mut x = 1;
set X = 2 再代入 x = 2;

Verseではシャドーイング(同じ名前での定義し直し)ができません。 内側のスコープでも、すでにある名前を := や var で定義し直すとコンパイルエラーになります。値を変えたいときは set を使ってください。
めんどくさい:frowning2:

また、var の値を 読む には <reads>、set で 書く には <reads> と <writes> のエフェクトが必要です(3.9節)。状態を書き換えるかどうかが、関数のシグネチャに出てくる仕組みです。

3.3 プリミティブ型

型 内容 注意点
int 整数 リテラルは64bitの範囲に収める必要がある。実行時の扱いはVMによって違う(VerseVMは64bitを超えて大きくなるが、BPVMはオーバーフローするとエラー)。64bitの範囲内で書くのが安全
float 64bit浮動小数点 NaNは1種類で、NaN同士が等しい。負のゼロはない。0 と書くとエラーなので 0.0 と書く
rational 有理数(正確な分数) int / int の結果は rational になる(しかも失敗しうる)
logic true / false ? で真偽を問い合わせる
char / char32 UTF-8の1コード単位 / 1コードポイント 整数との暗黙の変換はない
string []char の別名 .Length は文字数ではなくUTF-8のバイト数
void 値なし
any すべての型の親 できる操作はほとんどない
A : int = 7
B : float = 3.0

# int → float:1.0 を掛ける
AsFloat : float = A * 1.0

# float → int:Floor / Ceil / Round / Int は「失敗しうる」ので [] で呼ぶ
if (Rounded := Round[2.5]):
    Print("{Rounded}")   # 2(偶数への丸めなので 3 ではない)

# int / int は失敗しうる(ゼロ除算)。結果は rational
if (Half := Floor(A / 2)):   # Floor は rational を受け取るときは失敗しない
    Print("{Half}")           # 3

3.4 コンテナ型

# --- 配列 []t ---
Numbers : []int = array{10, 20, 30}
Count := Numbers.Length              # 3

# 配列へのアクセスは「失敗しうる」(範囲外になることがある)
if (First := Numbers[0]):
    Print("先頭: {First}")

# 要素を追加するときは連結して作り直す(配列は値型)
var Items : []string = array{}
set Items = Items + array{"Sword"}

# --- マップ [キー]値 ---
var Scores : [string]int = map{"Alice" => 10, "Bob" => 20}

if (AliceScore := Scores["Alice"]):  # 検索は失敗しうる
    Print("Alice: {AliceScore}")

if (set Scores["Carol"] = 30):       # 追加・更新も失敗しうる式として書く
    Print("Carol を追加")

# --- オプション ?t(null の代わり) ---
var Target : ?string = false         # false が「空」
set Target = option{"Enemy01"}       # 値を入れる

if (T := Target?):                   # ? で取り出す。空なら失敗
    Print("ターゲット: {T}")

# --- タプル ---
Pair := (1, "one")
Num := Pair(0)                       # 取り出しは () で

3.5 演算子

種類 Verse 補足
等しい = == はありません。比較は「失敗しうる式」
等しくない <> != ではありません
大小比較 < <= > >= これも失敗しうる式
論理演算 and or not && || ! ではありません
定義 :=
代入 set X = ...
範囲 0..4 両端を含む(0, 1, 2, 3, 4)

3.6 制御フロー

# --- if(1行の書き方) ---
Grade := if (Score >= 80) then "A" else "B"

# --- if(ブロックの書き方) ---
if (Score >= 80):
    Print("A")
else if (Score >= 50):
    Print("B")
else:
    Print("C")

# --- if(条件が長いときの書き方) ---
if:
    Player := FindPlayer[Name]      # 条件を複数行に分けて書ける
    Player.Score > 100
then:
    Print("High scorer!")
else:
    Print("該当なし")

# --- for(コレクションを回す) ---
for (N : Numbers):
    Print("{N}")

# インデックスと値を一緒に取る
for (Index -> Value : Numbers):
    Print("[{Index}] = {Value}")

# 範囲を回す
for (I := 1..5):
    Print("{I}")

# フィルター付き:失敗した要素は飛ばされる
for (N : Numbers, N > 15):
    Print("{N}")   # 20, 30

# for も式。結果を配列として受け取れる(map や filter の代わりに使える)
Doubled := for (N : Numbers) { N * 2 }   # array{20, 40, 60}

# --- loop(無限ループ。break で抜ける) ---
var Counter : int = 0
loop:
    set Counter += 1
    if (Counter >= 10):
        break

# --- case(パターンマッチ) ---
game_phase := enum{Waiting, Playing, Finished}

DescribePhase(Phase:game_phase):string =
    case (Phase):
        game_phase.Waiting  => "待機中"
        game_phase.Playing  => "プレイ中"
        game_phase.Finished => "終了"

3.7 関数

# 基本形: 名前(引数:型, ...)<エフェクト>:戻り値の型 = 本体
Add(A:int, B:int):int = A + B

# 名前付き引数・省略できる引数(名前の前に ? を付ける)
Log(Message:string, ?Level:int = 1, ?Tag:string = "Game"):void =
    Print("[{Tag}][Lv{Level}] {Message}")

Log("起動")                              # [Game][Lv1] 起動
Log("警告", ?Level := 3, ?Tag := "AI")   # 名前で指定するので順番は自由

# オーバーロード(引数の型が違えば同じ名前を使える)
Describe(X:int):string    = "int: {X}"
Describe(X:float):string  = "float: {X}"

# 拡張メソッド(あとから型にメソッドを足す)
(Value:int).Doubled()<computes>:int = Value * 2
Ten := 5.Doubled()

# ジェネリクス(where 句で型引数を宣言する)
Identity(X:t where t:type):t = X

# 型に制約を付ける(comparable の派生型だけ = で比較できる)
IndicesOf(Items:[]t, Target:t where t:subtype(comparable))<computes>:[]int =
    for (Index -> Item : Items, Item = Target):
        Index

ラムダ式(=> で書く無名関数)は今のVerseにはありません。 関数の中で関数を定義する(ネスト関数)書き方もBook of Verseに載っていますが、これも 🚧未リリース です。コールバックには、クラスのメソッドを渡すのが今の定番です(4.1節)。

3.8 失敗(Failure)― Verseの最重要概念

ここを理解できるかどうかで、Verseを使いこなせるかが決まります。

Verseの「失敗しうる式」は、値を出すか、何も出さないか のどちらかです。エラーでも例外でもnullでもありません。「この条件は成り立たなかった」という結果を、処理の流れとして扱います。

失敗しうる式の例

式 失敗する条件
A = B、A < B 比較が成り立たない
Array[I] 範囲外
Map[K] キーがない
Opt? オプションが空
X / Y(int同士) Yが0
player[Agent](キャスト) 型が違う
<decides> が付いた関数の呼び出し F[] 関数の中で失敗した

失敗コンテキスト

失敗しうる式は、失敗を受け止められる場所(失敗コンテキスト) でしか書けません。

  • if の条件
  • for の条件(ループ対象とフィルター)
  • <decides> が付いた関数の本体
  • or の左側、not の対象、option{} の中 など
# NG:失敗しうる式を、失敗を受け止められない場所に書いている → コンパイルエラー
# First := Numbers[0]

# OK:if の条件の中
if (First := Numbers[0]):
    Print("{First}")

# 条件をカンマでつなぐと「全部成功したときだけ」になる(短絡評価の AND)
if (Player := FindPlayer[Name], Score := GetScore[Player], Score > 100):
    Print("{Name} は {Score} 点")

<decides> 関数と [] での呼び出し

# 失敗しうる関数を作るには <decides> を付ける
# 本体のどの行で失敗しても、関数全体が失敗する
ValidateAge(Age:int)<decides><computes>:int =
    Age >= 0      # 負なら失敗
    Age <= 150    # 大きすぎても失敗
    Age           # 両方通ったら Age を返す

# 呼ぶときは () ではなく [] を使う
#  → 「ここは失敗するかもしれない」とコードを見ただけで分かる
if (Valid := ValidateAge[Input]):
    Print("OK: {Valid}")
else:
    Print("不正な年齢")

or(代わりの値)と not(否定)

# 左から順に試して、最初に成功したものを使う
Weapon := PrimaryWeapon[] or SecondaryWeapon[] or DefaultWeapon

# オプションの既定値
Value := MaybeValue? or 42

# not は「中の式が失敗したら成功」
if (not FindEnemy[]):
    Print("敵はいない")

投機的実行とロールバック(トランザクション)

失敗コンテキストの中で行った変更は「仮の変更」になります。 そのコンテキスト全体が成功したときだけ確定し、失敗すれば 自動で巻き戻ります。

wallet := class:
    var Gold : int = 100

    # <transacts>:状態を読み書きできる  <decides>:失敗しうる
    TryPay(Cost:int)<transacts><decides>:void =
        set Gold = Gold - Cost   # ① 先に引いてしまう(仮の変更)
        Gold >= 0                # ② 足りなければここで失敗する

W := wallet{}
if (W.TryPay[150]):
    Print("支払い成功")
else:
    # ② で失敗したので、① の減算は巻き戻っている
    Print("残高 {W.Gold}")   # 100

C#やC++なら、「チェックしてから変更する」か「変更して、失敗したら元に戻す処理を自分で書く」必要があります。Verseでは 「変更してから検証し、だめなら全部なかったことにする」 という書き方ができます。

3.9 エフェクト指定子

関数が「何をしうるか」を型として宣言します。

指定子 系統 意味
(省略) Heap <transacts> と同じ(読み・書き・生成ができる)
<transacts> Heap 状態の読み書きとオブジェクト生成ができる
<computes> Heap 純粋な計算。変更できる状態には一切触れない
<reads> Heap 状態を読むだけ
<writes> Heap 状態を書き換える
<allocates> Heap 他と区別できる値(<unique> なクラスなど)を生成する
<decides> 成否 失敗しうる
<suspends> 待機 途中で待機・再開できる(何フレームにもまたがりうる)
<converges> 停止性 必ず終わる(今は <native> 関数などに限られる)
<predicts> 予測 クライアント側での予測を許す 🚧未リリース(既定はサーバー側で決める dictates)

ルール

  • 呼び出した関数のエフェクトは、呼び出し元にも伝わります。 呼び出し元は、呼んだ関数が持つエフェクトを宣言する必要があります。
  • <decides> と <suspends> は1つの関数に一緒に付けられません。
  • <suspends> 関数から <decides> 関数を呼ぶときは [] で呼びます。
  • if の条件は失敗を吸収し、spawn は待機を吸収します。
# エフェクトの包含関係:<computes> は <reads> や <transacts> の代わりに使える
PureAdd(X:int)<computes>:int = X + 1

F1 : type{_(:int)<transacts>:int} = PureAdd   # OK(エフェクトを広げるのは可)
# 逆(<transacts> の関数を <computes> の型に入れる)はエラー

エフェクトは「この関数を呼ぶと何が起きうるか」の契約です。 <computes> と書かれていれば、その関数が状態を書き換えないことをコンパイラが保証します。レビューのときに、関数の中身を読まなくても副作用の有無が分かるのは大きな利点です。

3.10 struct / enum / class / interface

# --- struct:値型。代入するとコピーされる ---
grid_pos := struct:
    X : int = 0
    Y : int = 0

P := grid_pos{X := 3, Y := 4}

# --- enum ---
rarity := enum{Common, Rare, Epic}

# --- class:参照型 ---
character := class:
    Name : string                 # 不変、既定値なし → 生成時に必ず指定する
    var Health : int = 100        # 変更可能、既定値あり
    MaxHealth : int = 100

    # メソッド。<transacts> は省略しても同じ
    TakeDamage(Amount:int):void =
        set Health = Max(0, Health - Amount)

# インスタンスの生成(アーキタイプ式)。フィールドは名前で指定し、順番は自由
Hero := character{Name := "Aldric", Health := 80}

# --- 継承と override ---
boss := class(character):
    Phase : int = 1

    TakeDamage<override>(Amount:int):void =
        (super:)TakeDamage(Amount / 2)   # ← 注意:int / int は失敗しうる(5章参照)

# --- interface ---
damageable := interface:
    TakeHit(Amount:int)<transacts>:void
    GetHp()<reads>:int

crate := class(damageable):
    var Hp : int = 30
    TakeHit<override>(Amount:int)<transacts>:void = set Hp -= Amount
    GetHp<override>()<reads>:int = Hp

上の boss.TakeDamage の Amount / 2 は、わざと詰まりポイントを残した例 です。int / int は失敗しうる式で、しかも rational を返すので、このままではコンパイルできません。正しい書き方は5章の「⑥」を見てください。

クラスに付けられる主な指定子

指定子 意味
<abstract> インスタンスを作れない。抽象メソッドを持てる
<final> 継承を禁止する(メソッドに付けるとoverrideを禁止)
<concrete> 全フィールドに既定値が必要。config{} のように空で生成できる
<unique> = でインスタンスが同じかどうかを比べられる
<castable> castable_subtype 制約で使えるようにする
<persistable> セーブデータとして保存できる(4.4節)
<final_super> 親クラスを固定する(Scene Graphのコンポーネントで必須)

初期化処理:フィールドの既定値には関数呼び出しを書けません。生成時に処理をしたいときは block: を使います。

logged_entity := class:
    ID : int
    var Label : string = ""

    block:                      # インスタンスが作られるときに実行される
        set Label = "entity-{ID}"

3.11 可変性と、値・参照の違い

種類 代入したとき 例
struct / 配列 / マップ / タプル コピーされる(値型) var B = A; set B[0] = 9 としても A は変わらない
class 同じものを共有する(参照型) var P2 = P1; set P2.Health = 1 とすると P1 も変わる
# 値型:コピー
var Original : []int = array{1, 2, 3}
var Copy : []int = Original
if (set Copy[0] = 999) {}
# Original[0] は 1 のまま

# 参照型:共有
P1 := character{Name := "A"}
P2 := P1
P2.TakeDamage(30)
# P1.Health も 70 になる

class型の変数に付けた var は 「別のインスタンスを指し直せる」という意味だけ です。フィールドを変更できるかどうかは、クラス定義での var で決まります。

3.12 モジュールとアクセス指定子

using { /Verse.org/Simulation }               # Epicが提供するモジュール
using { /YourName@fortnite.com/MyProject }    # 自分のプロジェクトのモジュール

counter := class:
    Name<public> : string = "counter"         # どこからでも見える
    var Value<private> : int = 0              # このクラスの中からだけ
    Increment<public>():void = set Value += 1
指定子 公開範囲
<public> どこからでも
<protected> そのクラスと派生クラス
<private> そのクラスの中だけ
<internal> 同じモジュールの中(既定)

3.13 並行処理

Verseの非同期処理は、<suspends> 関数の中で使う 構造化された並行処理 が基本です。

構文 動き 終わった後の他の処理
sync: 全部同時に走らせて、全部終わるまで待つ。結果はタプルで返る ―
race: 全部同時に走らせて、最初に終わったものを採用 キャンセルされる
rush: 全部同時に走らせて、最初に終わったものを採用 そのまま走り続ける
branch: バックグラウンドで始めて、すぐ次に進む 囲んでいる関数を抜けるとキャンセル
spawn{} 呼び出し元とは独立したタスクを始める(構造化されていない) 自分で管理する
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

concurrency_demo := class:

    Wait(Seconds:float, Label:string)<suspends>:string =
        Sleep(Seconds)           # 指定した秒数だけ待つ。Sleep(0.0) なら1フレーム待つ
        Label

    Run()<suspends>:void =
        # sync:両方終わるまで待つ → 約2秒後
        Results := sync:
            Wait(1.0, "A")
            Wait(2.0, "B")
        Print("sync: {Results(0)}, {Results(1)}")

        # race:先に終わった方を採用し、遅い方はキャンセル → 約1秒後
        Winner := race:
            Wait(1.0, "Fast")
            Wait(3.0, "Slow")
        Print("race winner: {Winner}")

        # branch:バックグラウンドで実行(Run を抜けるとキャンセルされる)
        branch:
            Sleep(0.5)
            Print("branch done")
        Print("branch の完了を待たずにここに来る")
        Sleep(1.0)   # branch が終わるのを見届けるために少し待つ

イベント

door := class:
    Opened : event() = event(){}    # 引数なしのイベント

    Open():void =
        Opened.Signal()             # 待っている人を1人起こす

    WaitForOpen()<suspends>:void =
        Opened.Await()              # Signal されるまで待つ
        Print("ドアが開いた")

event の Signal は 待っている人がいないと捨てられます(キューに溜まりません)。先に Await している状態を作ってから Signal してください(5章⑩)。


4. 応用:実践的なコード例

4.1 デバイス連携:ボタンを押すとスコアが入る

score_button_device.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

score_button_device := class(creative_device):

    # @editable を付けると、UEFNの詳細パネルからレベル上のデバイスを割り当てられる
    # (UE5の UPROPERTY(EditAnywhere) に近い)
    @editable
    ScoreButton : button_device = button_device{}

    @editable
    ScoreManager : score_manager_device = score_manager_device{}

    var PressCount : int = 0

    OnBegin<override>()<suspends>:void =
        # イベントにハンドラー(このクラスのメソッド)を登録する
        # Subscribe は解除用のハンドル(cancelable)を返す
        ScoreButton.InteractedWithEvent.Subscribe(OnButtonPressed)

    # ハンドラーの引数は「押した人(agent)」
    OnButtonPressed(Agent:agent):void =
        set PressCount += 1
        ScoreManager.Activate(Agent)     # 押した人にスコアを与える
        Print("押された回数: {PressCount}")

解説

  • button_device{} は既定値です。エディタで割り当てるのを忘れると、何もしないデバイスに対して処理が走ります(5章⑳)。
  • Subscribe に渡すのは関数 そのもの(OnButtonPressed)です。OnButtonPressed() と呼び出しの形で書かないように注意してください。
  • OnButtonPressed はエフェクトを省略しているので <transacts> 扱いになり、set を使えます。

4.2 制限時間付きミッション(race を使う)

「ゴールに着く」と「時間切れ」のどちらが先に起きるかを決める処理は、race で素直に書けます。

timed_mission_device.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

timed_mission_device := class(creative_device):

    @editable
    GoalTrigger : trigger_device = trigger_device{}

    @editable
    TimeLimitSeconds : float = 30.0

    OnBegin<override>()<suspends>:void =
        Print("ミッション開始! {TimeLimitSeconds} 秒以内にゴールへ")

        # 先に終わった方の値が Result に入り、もう一方はキャンセルされる
        Result := race:
            block:
                GoalTrigger.TriggeredEvent.Await()   # トリガーが踏まれるまで待つ
                "クリア"
            block:
                CountDown(TimeLimitSeconds)
                "タイムアップ"

        Print("結果: {Result}")

    # 残り時間を5秒ごとに表示するカウントダウン
    CountDown(Total:float)<suspends>:void =
        var Remaining : float = Total
        loop:
            if (Remaining <= 0.0):
                break
            Print("残り {Remaining} 秒")
            Sleep(5.0)
            set Remaining -= 5.0

ポイント

  • ゴールに着いた時点で CountDown は 自動でキャンセル されます。C#でいう CancellationToken を自分で配る必要はありません。
  • キャンセルは 次の待機ポイント(Sleep など)で効きます。loop の中に待機ポイントがないとキャンセルできません(5章⑪)。

4.3 トランザクションを使ったショップ

shop.verse
using { /UnrealEngine.com/Temporary/Diagnostics }

shop_wallet := class:
    var Gold : int = 100
    var Inventory : []string = array{}

    # 購入処理。途中のどこで失敗しても、変更は全部巻き戻る
    TryBuy(ItemName:string, Price:int)<transacts><decides>:void =
        Price > 0                                 # ① 価格がおかしければ失敗
        set Gold = Gold - Price                   # ② 先に引く(仮の変更)
        Gold >= 0                                 # ③ 残高が足りなければ失敗 → ② は巻き戻る
        set Inventory = Inventory + array{ItemName}   # ④ 持ち物に追加

DemoShop():void =
    Wallet := shop_wallet{}

    if (Wallet.TryBuy["剣", 80]):
        Print("剣を購入。残り {Wallet.Gold}G")      # 残り 20G

    if (Wallet.TryBuy["盾", 50]):
        Print("盾を購入")
    else:
        # ③ で失敗したので Gold は 20 のまま、Inventory にも何も入っていない
        Print("ゴールド不足。残り {Wallet.Gold}G")   # 残り 20G

ここがVerseらしいところ

  • 「お金を減らしたのに持ち物に入らなかった」といった 中途半端な状態がそもそも起きません。
  • 複数のオブジェクトにまたがる変更(例えば相手の在庫を減らし、自分の持ち物を増やす)も、1つの失敗コンテキストの中に書けば、まとめて確定するか、まとめて巻き戻ります。

4.4 データの永続化(セーブデータ)

save_manager.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# 保存したいデータの型。class にするなら <final> が必要で、
# 親クラスは持てず、var フィールドも持てない
player_save := class<final><persistable>:
    Version : int = 1          # 将来の形式変更に備えてバージョンを持たせておく
    HighScore : int = 0
    TotalPlays : int = 0

# 永続化する変数は「モジュールのスコープ」に、weak_map(player, t) 型で置く
# プレイヤーが参加すると、保存されていたデータが自動で読み込まれる
var SaveData : weak_map(player, player_save) = map{}

save_manager_device := class(creative_device):

    # agent(プレイヤー以外も含む)から player にキャストして記録する
    RecordResult(Agent:agent, Score:int):void =
        if (Player := player[Agent]):
            # データがなければ既定値から始める
            Old := if (Existing := SaveData[Player]) then Existing else player_save{}

            # var フィールドがないので、新しいインスタンスを作って丸ごと置き換える
            if:
                set SaveData[Player] = player_save:
                    Version := Old.Version
                    HighScore := Max(Old.HighScore, Score)
                    TotalPlays := Old.TotalPlays + 1
            then:
                Print("保存しました(ハイスコア: {Max(Old.HighScore, Score)})")

永続化データの型は、島を公開したあとは自由に変えられません。

  • <persistable> を後から付けたり外したりはできない
  • フィールドの削除はできない(classなら、既定値つきのフィールドの 追加だけ できる)
  • struct は公開後にフィールドを変えられない
  • closed な enum は値を増やせない(増やす予定があるなら open にしておく)
  • 整数は64bitで保存されるので、範囲を超えると実行時エラーになる
  • 参加するプレイヤー全員分のデータが読み込まれるので、1人あたりのデータ量はメモリ使用量に直結する

最初の公開の前に、設計を固めておいてください。

4.5 Scene Graph のコンポーネント(UE6の中心)

UE6でActorの代わりになるのが Scene Graph です。Entity(入れ物)に Component(データと振る舞い)を付けて シーンを作ります。UEFNでは2025年6月(v36.00)からBeta機能として島を公開できるようになっていて、新しいプロジェクトでは最初から有効です。

blink_vfx_component.verse
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# Scene Graph のコンポーネントは component を継承し、<final_super> を付けるのが決まり
blink_vfx_component<public> := class<final_super>(component):

    @editable
    IntervalSeconds : float = 1.0

    # シミュレーションが始まったときに1回呼ばれる(同期処理)
    OnBeginSimulation<override>():void =
        (super:)OnBeginSimulation()       # 親クラスの処理を先に呼ぶ
        Print("blink_vfx_component: 開始")

    # シミュレーション中に走る非同期の入口(<suspends>)
    OnSimulate<override>()<suspends>:void =
        # Entity:このコンポーネントが付いているエンティティ
        # GetComponent[] で、同じエンティティに付いている別のコンポーネントを取り出す(失敗しうる)
        if (Vfx := Entity.GetComponent[particle_system_component]):
            loop:
                Vfx.Disable()
                Sleep(IntervalSeconds)
                Vfx.Enable()
                Sleep(IntervalSeconds)
        else:
            Print("particle_system_component が付いていません")

UE5の考え方との対応

UE5 Scene Graph
AActor entity
UActorComponent component(Verseのクラス)
BeginPlay() OnBeginSimulation()
Tick() OnSimulate() の中の loop と Sleep(0.0)
GetComponentByClass<T>() Entity.GetComponent[T]
子Actorを探す Entity.FindDescendantEntitiesWithComponent(T)
ブループリントクラス Prefab(UEFNでは、作るとVerseのクラスとして登録される)

Scene Graphのコンポーネントの処理は、プレイ中だけでなく エディタ上でも動きます(デバイスはプレイ中だけ)。重い処理や副作用のある処理を書くときは気をつけてください。

4.6 ジェネリクスと拡張メソッドで道具箱を作る

utils.verse
# 値を範囲内に収める拡張メソッド
(Value:int).ClampTo(Lower:int, Upper:int)<computes>:int =
    if (Value < Lower):
        Lower
    else if (Value > Upper):
        Upper
    else:
        Value

# 配列の先頭を取り出す(空なら失敗)
FirstOf(Items:[]t where t:type)<computes><decides>:t = Items[0]

# 条件に合う要素の位置をすべて返す(comparable 制約)
IndicesOf(Items:[]t, Target:t where t:subtype(comparable))<computes>:[]int =
    for (Index -> Item : Items, Item = Target):
        Index

# 使い方
UtilsDemo():void =
    Hp := 150.ClampTo(0, 100)                         # 100
    Names := array{"a", "b", "a"}
    if (Head := FirstOf[Names]):                      # <decides> なので [] で呼ぶ
        Print("先頭: {Head}")
    Positions := IndicesOf(array{1, 2, 1, 3}, 1)      # array{0, 2}

4.7 (先取り)Live Variables 🚧未リリース

Book of Verseには、値の依存関係を宣言しておくと自動で再計算される Live Variables が載っています。UIの表示などで便利になりそうですが、まだリリースされておらず、文法も変わる可能性があります。

# ※ 現時点のコンパイラでは動きません(仕様書に載っている文法の紹介)
var BaseAttack : int = 10
var BuffBonus : int = 0

var live TotalAttack : int = BaseAttack + BuffBonus   # 依存している値が変わると自動で再計算される

set BuffBonus = 5
# TotalAttack は 15 になる

# 条件が成り立つまで待つ await も予定されている
# await{ TotalAttack > 20 }

5. 現場で詰まりやすいポイント集

初めて高校数学や物理学に触れた時のように、もう考えるのをやめようかと何度か思いました:relaxed:

① == がない。= は比較で、しかも失敗しうる

# NG
# if (A == B): ...

# OK
if (A = B):
    Print("同じ")

= は「比較して、等しくなければ失敗する式」です。if の条件など、失敗コンテキストの中でしか使えません。
これが一番慣れなかった:cry:

② 配列やマップへのアクセスがコンパイルエラーになる

# NG:失敗しうる式を、失敗を受け止められない場所に書いている
# Top := Ranking[0]

# OK
if (Top := Ranking[0]):
    Print("1位: {Top}")

set Arr[0] = X や set Map[K] = V も失敗しうるので、if (set ...) {} の形で書きます。

③ () と [] の書き分け

  • <decides> が付いた関数と、キャスト(player[Agent])は []
  • それ以外は ()

エラーメッセージが分かりにくいことがあるので、「失敗しうるかどうか」を最初に確認する癖をつけましょう。

④ <decides> と <suspends> を一緒に付けられない

# NG
# WaitAndCheck()<suspends><decides>:void = ...

# OK:失敗しうる判定と、待つ処理を別の関数に分ける
IsReady()<decides><reads>:void = ReadyFlag?
WaitUntilReady()<suspends>:void =
    loop:
        if (IsReady[]):
            break
        Sleep(0.1)

⑤ float に整数リテラルを書くとエラー

# NG
# var Speed : float = 1

# OK
var Speed : float = 1.0
AsFloat := IntValue * 1.0          # int → float
if (AsInt := Int[FloatValue]):     # float → int(失敗しうる)
    Print("{AsInt}")

⑥ int同士の割り算は失敗しうるうえに、rational が返る

# NG:失敗しうる式であり、しかも int ではなく rational が返る
# Half : int = Total / 2

# OK:失敗コンテキストの中で、Floor(または Ceil)を使って int にする
if (Half := Floor(Total / 2)):
    Print("{Half}")

3.10節の boss の例は、こう直します。

TakeDamage<override>(Amount:int):void =
    if (Reduced := Floor(Amount / 2)):
        (super:)TakeDamage(Reduced)

Floor は負の無限大の方向に丸めるので、Floor((-5) / 2) は -3 になります。0の方向に切り捨てるC++とは違うので注意してください。整数の割り算では set /= も使えません。

⑦ シャドーイングができない

Count := 1
if (Something[]):
    # NG:外側と同じ名前をもう一度定義している
    # Count := 2
    Print("...")

一時的な変数には、別の名前を付けてください。

⑧ string.Length は文字数ではない(日本語の場合は特に注意)

string は []char(UTF-8のコード単位の配列)です。"こんにちは".Length は 5ではなく15 です。文字数制限やUIの文字数カウントを実装するときは、ここでつまずきます。

⑨ Round は偶数への丸め

Round[2.5] = 2、Round[3.5] = 4 です(銀行丸め)。四捨五入のつもりで使うと、ずれます。

⑩ event.Signal() が届かない

event は 待っている人がいないときのSignalを捨てます。開始処理の順番によっては「たまに動かない」というバグになります。

# 対策:先に待つ側を起動してから Signal する
OnBegin<override>()<suspends>:void =
    spawn{ WaitForOpen() }    # 先に Await 状態にしておく
    Sleep(0.0)                # 1フレーム待って、待つ側が確実に待機に入るようにする
    Open()

⑪ 待機ポイントのない loop はキャンセルできない

キャンセルは「次の待機ポイント」で効きます。重い計算を loop で回すと、race で負けても止まりません。Sleep(0.0) を入れて、1フレームごとに処理を返す ようにしてください。

⑫ rush と branch は loop や for の中に直接書けない

関数に切り出して、その関数をループから呼んでください。

⑬ Subscribe の解除を忘れる

Subscribe は cancelable を返します。一時的に購読するだけなら、ハンドルを持っておいて Cancel() で解除しましょう。解除し忘れると、ラウンドをまたいでハンドラーが何度も呼ばれる原因になります。

var MaybeHandle : ?cancelable = false

StartListening():void =
    set MaybeHandle = option{ ScoreButton.InteractedWithEvent.Subscribe(OnButtonPressed) }

StopListening():void =
    if (Handle := MaybeHandle?):
        Handle.Cancel()
        set MaybeHandle = false

⑭ 配列や struct がコピーされていることに気づかない

var Party : []grid_pos = array{grid_pos{}}
# var Temp := Party        # ← NG:var には型が必要(:= は使えない)

var Temp2 : []grid_pos = Party
if (set Temp2[0].X = 5) {}
# Party[0].X は 0 のまま(コピーだから)

逆に class の配列は、要素が共有されます。「なぜか他の場所の値まで変わった」「なぜか変わらない」というときは、値型か参照型かを疑ってください。

⑮ マップを検索してもキーは追加されない/ネストした set が失敗する

var Grid : [string][]int = map{}
# 存在しないキーの中を書き換えようとすると、set 全体が失敗する
if (set Grid["oranges"][0] = 10):
    Print("更新した")
else:
    Print("oranges がない")

⑯ フィールドの既定値で関数を呼べない

spawner := class:
    # NG
    # SpawnTime : float = GetDefaultTime()

    # OK:block で初期化する
    var SpawnTime : float = 0.0
    block:
        set SpawnTime = 3.0

⑰ 公開後に、型や公開APIを変えられない

公開済みのコードは、コンパイラが 後方互換性をチェック します。

  • <public> な定義の削除、名前の変更、種類の変更(class を struct にするなど)はできない
  • class に足せるのは 既定値つきのフィールドだけ。struct はフィールドを増やせない
  • <final> は後から付けられるが、外せない
  • 公開範囲は広げられるが、狭められない(private → public はOK)
  • 継承できるクラスのメソッドは、シグネチャもエフェクトも変えられない

最初から <public> を付けすぎない ことが、将来自分を楽にします。

⑱ Book of Verseに載っている機能が使えない

Book of Verseは 開発ブランチ先頭の仕様 です。ネスト関数、Cancel() の一部、NextTick()、sticky_event、subscribable_event、Live Variables、<predicts>、JSONシリアライズなどは 🚧未リリース です。コンパイラに「知らない」と言われたら、まずここを疑ってください。

⑲ インデントの混在

Verseはインデントでブロックを表します。タブとスペースが混ざるとエラーの原因になります。エディタの設定を統一するか、迷ったら {} で囲む書き方を使ってください。

⑳ @editable の割り当て忘れ

@editable のフィールドは、エディタで割り当てないと 既定値で作られた、何にもつながっていないデバイス のまま動きます。コンパイルエラーにならないので気づきにくいです。

㉑ AIが生成したVerseを信じすぎる

前述のとおり、今のAIモデルはVerseの学習量が少ないです。よく見る間違いは、== を使う、[] が必要なところで () を使う、ラムダを使う、エフェクト指定子が抜けている、存在しないAPIを作り出す といったものです。生成されたコードは、この章のチェックリストで確認してください。
※公式が正です、AIに出力させる場合も、「公式に記載のある事実を元に」等指示の仕方に注意してください


6. 他言語との違い

6.1 比較表

観点 Verse C++(UE) ブループリント C#(Unity) Python Haskell / Prolog
パラダイム 関数型 + 論理型 + 命令型 命令型 + OOP ビジュアル・命令型 OOP + 一部関数型 命令型・動的型付け 純粋関数型 / 論理型
型付け 静的・強い 静的 静的 静的 動的 静的 / 動的
既定の可変性 不変 可変 可変 可変 可変 不変
うまくいかない場合の扱い 失敗(値の有無) 戻り値・例外 分岐ノード 例外・null 例外・None Maybe / バックトラック
null なし(?t) nullptr IsValid null None Maybe
状態の巻き戻し 言語に組み込み 自前で実装 自前で実装 自前で実装 自前で実装 STMなど
副作用の表し方 エフェクト指定子 なし なし なし なし モナド
非同期 <suspends> + 構造化(race/sync) Latent / Task Latentノード async/await・コルーチン asyncio ―
比較演算子 =(失敗しうる) == ==ノード == == == / 単一化
ラムダ なし(今のところ) あり なし あり あり あり
継承 単一継承 + interface 多重継承 単一継承 単一継承 + interface 多重継承 ―
API互換性のチェック 公開時にコンパイラが行う なし なし なし なし なし

6.2 同じ処理を書き比べる

例1:nullかもしれない値をたどって処理する

C#
// null チェックが入れ子になるか、?. でつなぐ
var player = FindPlayer(name);
if (player != null)
{
    var score = player.GetScore();
    if (score.HasValue && score.Value > 100)
    {
        Debug.Log($"{name}: {score.Value}");
    }
}
Verse
# 失敗しうる式をカンマでつなぐだけ。どれかが失敗したら全体が失敗する
if (Player := FindPlayer[Name], Score := Player.GetScore[], Score > 100):
    Print("{Name}: {Score}")

例2:タイムアウト付きで待つ

C#
// CancellationToken と Task.WhenAny を自分で組み合わせる
using var cts = new CancellationTokenSource();
var goal = WaitForGoalAsync(cts.Token);
var timeout = Task.Delay(30_000, cts.Token);
var finished = await Task.WhenAny(goal, timeout);
cts.Cancel(); // 負けた方を止めるのは自分で
Verse
# race が、負けた方のキャンセルまで面倒を見る
Result := race:
    block:
        WaitForGoal()
        "クリア"
    block:
        Sleep(30.0)
        "タイムアップ"

例3:失敗したら元に戻す処理

C++
// 失敗したときの戻し処理を自分で書く必要がある
bool TryTrade(Inventory& A, Inventory& B, Item X, Item Y) {
    if (!A.Remove(X)) return false;
    if (!B.Remove(Y)) { A.Add(X); return false; }   // 戻し処理
    A.Add(Y); B.Add(X);
    return true;
}
Verse
# 失敗コンテキストの中なら、途中で失敗しても全部巻き戻る
TryTrade(A:inventory, B:inventory, X:item, Y:item)<transacts><decides>:void =
    A.Remove[X]
    B.Remove[Y]
    A.Add(Y)
    B.Add(X)

6.3 UE5(C++ / ブループリント)経験者向けの対応表

UE5での概念 Verseでの書き方
UPROPERTY(EditAnywhere) @editable
const 既定(何も付けない)
普通の変数 var + set
nullptr / IsValid() ?t と X?
Cast<T>(Obj) T[Obj](失敗しうる)
TArray<T> / TMap<K,V> []t / [k]v(値型)
BeginPlay OnBegin(デバイス)/ OnBeginSimulation(Scene Graph)
Tick OnSimulate 内の loop + Sleep(0.0)
SetTimer Sleep(秒)
Delegate / イベントディスパッチャー event / listenable の Subscribe と Await
Latentアクション(Delay等) <suspends> 関数
SaveGame weak_map(player, t) と <persistable>
サーバー権限 既定でサーバー側(dictates)。クライアント予測の <predicts> は 🚧未リリース
check() / ensure() <decides> による失敗(ただし失敗は「異常」ではなく処理の流れの一部)

6.4 考え方の切り替え

  1. 「エラーを処理する」ではなく「成功した場合だけ先に進む」:失敗は例外的な状況ではなく、普通の処理の分かれ道です。
  2. 「チェックしてから変える」ではなく「変えてから確かめる」:トランザクションがあるので、楽観的に書けます。
  3. 「スレッドを管理する」ではなく「処理の構造を書く」:race や sync のブロックの形が、そのままタスクの生存期間になります。
  4. 「あとで直せばいい」ではなく「公開したら変えられない」:公開APIとセーブデータの設計は、最初が勝負です。

7. UE6に向けた学習ロードマップ

段階 やること 目標
1 UEFNを入れて、Hello Worldのデバイスを動かす ビルドと実行の流れをつかむ
2 3章の文法を、小さなデバイスで一通り試す 失敗とエフェクト を体で覚える
3 デバイス連携(4.1)、race を使ったミッション(4.2) イベントと並行処理
4 トランザクション(4.3)、永続化(4.4) データの整合性と保存の設計
5 Scene Graph:Entity、Component、Prefab(4.5) UE6でActorの代わりになるものに慣れる
6 既存のUE5プロジェクトのBPやC++を、Verseならどう書くか考えてみる 移行の準備
7 Book of Verseを通読する 言語仕様の全体像をつかむ(未リリース機能も含めて)

8. まとめ

  • UE6はUE5とUEFNを統合したエンジン で、早期アクセスは2027年末が目標です。
  • 2026年11月3日のUnreal Fest Tokyoで、Actor・ブループリント・C++ Gameplay FrameworkをUE6の早期アクセス時点から削除する方針に変わりました。 ゲームプレイはVerseとScene Graphに一本化され、C++はエンジンの低レイヤーに残ります。
  • Verseの核は 失敗・トランザクション・エフェクト・構造化された並行処理 の4つです。ほかの言語の常識(==、例外、null、ラムダ)が通用しないところが多いので、最初は戸惑います。
  • 現場で詰まりやすいのは、失敗コンテキスト、[] と () の書き分け、数値型の変換、string.Length、イベントのSignalが消えること、公開後の互換性 です。
  • 今できるいちばん確実な準備は、UEFNでVerseとScene Graphを実際に触ること です。

9. 参考資料

UE6関連(公式)

UE6関連(報道)

Verse言語

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?