2024年11月1日の 32.00 リリースから、Verse 言語がバージョン 1 に安定化されます。安定性を高め、将来 Verse 言語がアップデートされたときにコードを段階的にアップグレードしやすくするため、プロジェクトのアップグレードをお勧めします。このアップグレードは任意です。プロジェクトの現在のバージョンは、古い Verse バージョンでも常に継続して動作します。とはいえ将来的には、新しいバージョンの Verse をアップロードしたい場合、プロジェクトを新しいバージョンの Verse にアップグレードする必要があるかもしれません。
最初の公開リリース以来、当社では Verse 言語を継続して進化させています。こうした変更は新しい言語バージョンへのアップグレードをしなくてもユーザーに透過的に展開されています。今後もこうした状況が続くと予想しています。言語変更が行われても、以前のバージョンの言語との下位互換性が維持され、UEFN の新しいリリースとともにユーザーに展開されることがほとんどです。
ただし一部では下位互換性がなく、コードのコンパイルにコードの変更が必要となる場合があります。このような下位互換性のない変更は、ユーザーがプロジェクトをアップグレードして新しい言語バージョンをターゲットにした場合にのみトリガーされます。
下位互換性のないコードを保存するとそのたびに警告が表示されるため、コードの動作が言語の新しいバージョンでは非推奨であることがわかります。たとえば、新しい Verse 言語バージョンと互換性のないコーディング手法を引き続き使用すると、コードに関する警告が表示されます。
プロジェクトが V0 で非推奨である旨の警告が表示されずにコンパイルされた場合は、コードの動作を変更することなく V1 にアップグレードできます。UEFN 32.00 で V0 のプロジェクトを開き、エラーや警告が表示されずにコンパイルされる場合は、エディタがアップグレードするかどうかを尋ねます。
何らかの理由でアップグレードまたはダウングレードを後で行いたい場合、[Project Settings] から実行できます。
Click image to enlarge.
または、「Fortnite Projects」フォルダからプロジェクト ファイルを開き、「.uplugin」ファイルの Verse バージョンを変更することでも可能です。
Click image to enlarge.
非推奨への変更はほとんどの場合、将来の言語の改善の可能性を開くものであり、その時点ではユーザーに何のメリットももたらしません。しかし、例外が 2 つあります。ローカル修飾子と構造体の比較です。
V1 の変更点
Set の失敗
set が実行する式は失敗が許可されなくなりました。以前のコードは以下のとおりでした。
F():void=
var X:int = 0
set X = FailableExpression[]
これは許可されており、X は FailableExpression で警告付きとして評価したものにセットできました。V1 ではこれは許可されません。
コードを修正するには、式が失敗しないことを確認する必要があります。次の変更は、式が失敗しないようにする方法の 1 つです。
var X:int = 0
Value:= FailableExpression[] or DefaultValue
set X = Value
マップ リテラル キーの失敗
以前は、次のようにマップ内のリテラル キーを失敗させることができました。
map{ExpressionThatCannotFail=>Value}
その簡単な例としては、0 = 0 が 失敗する map{ (0 = 0) =>0 } があります。これが許可されなくなりました。
ブロックのセミコロン / カンマ / 改行区切り文字の混在
以前は、セミコロン / カンマ / 改行を混在させた部分式の区切りが許可されており、次のようなコードが生成されていました。
A,
B
for (A := 0..2):
# more code here
内部的には、以下に示すようにコードに脱糖されます。
block:
A
B
for (A := 0..2):
# more code here
これは、独自の別個のスコープを持つ暗黙的なブロックが作成されていたため、コード ブロック内の A の両方の定義が互いに競合しないことを意味します。
しかしこれは誤った動作であったので、最新の Verse 言語バージョンでは修正されています。同じコードで各部分式が別々に処理されるようになり、次のような結果になります。
A
B
for (A := 0..2):
# more code here
これは、最初の A と A := 0..2 の 2 番目の定義が互いにシャドウイングを行っており、意味が曖昧になることを意味します。
この問題を解決するには、両方の作成者 (およびこの動作に依存しているすべての人) が、すべての Verse コードでサブ式の区切りにセミコロン、カンマ、改行を混在させて使用するのをやめる必要があります。
Example:
PropPosition := Prop.GetTransform().Translation,
if(Round[PropPosition.Z] = Round[ROOT_POSITION.Z]) { break }
Sleep(0.0)
以下のように変更することを推奨します。
PropPosition := Prop.GetTransform().Translation # note the trailing comma here has been removed
if(Round[PropPosition.Z] = Round[ROOT_POSITION.Z]) { break }
Sleep(0.0)
以前の 28.20 以降では、セパレータの混在が検出されるたびに警告が生成されていました。これは、最新の Verse 言語バージョンでは禁止されています。
一意の指定子の変更
指定子 <unique> を持つクラスには、<allocates> 建築効果が必要になりました。 たとえば、class<unique><computes> は許可されなくなりました。
関数ローカル修飾子
(local:) 修飾子を関数内の識別子に適用して、他の識別子と区別することができます。
For example:
ExternallyDefinedModuleB<public> := module:
ShadowX<public>:int = 10 # `ModuleC` 公開後にのみ追加されます
ModuleC := module:
using{ExternallyDefinedModuleB}
FooNoLocal():float=
ShadowX:float = 0.0
ShadowX
上記のコードは ShadowX が曖昧であるため (ExternallyDefinedModuleB.ShadowX からのものか FooNoLocal 内の ShadowX からのものなのかが曖昧)、シャドウイング エラーが発生します。
これを解決するには、次の例のように (local:) 修飾子を使用して、どの ShadowX が参照されているのかを明確にします。
ExternallyDefinedModuleA<public> := module:
ShadowX<public>:int = 10 # `ModuleB` 公開後にのみ追加されます
ModuleB := module:
using{ExternallyDefinedModuleA}
FooLocal():float=
(local:)ShadowX:float = 0.0 #The `local` qualifier can be used here to disambiguate
(local:)ShadowX
以前は、local という単語がデータ定義識別子として使用されていることが検出されたときに警告を生成していました。しかし、これは現在予約済みキーワードになっています。最新の言語バージョンでは local を使用するとエラーが発生します。そして、local 識別子を通常のデータ定義識別子として使用する場合は、その使用を明示的に修飾する必要があります。
ローカル修飾子の別の例を次に示します。
MyModule := module:
X:int = 1
Foo((local:)X:int):int = (MyModule:)X + (local:)X
この例では、(local:) 修飾子を指定しなかった場合両方の X 識別子が同じスコープ内にあることから、Foo(X:int) の X がすぐ上の X:int = 1 の定義をシャドウイングします。したがって、(local:) 修飾子を使用すると、Foo の引数パラメータ句の X を Foo のスコープ内でのみ特定することで 2 つを区別できるようになります。同じことが Foo のボディの X 識別子にも当てはまります。
構造体の公開フィールド
struct 内のすべてのフィールドを公開にすることが必要になりました。V1 でもデフォルトでこのルールが適用されます (<public> を使用する必要はなくなりました)。
V1 では、2 つの struct を比較する機能も追加されています。構造体のすべてのフィールドが比較可能な場合は、= を使用して構造体の 2 つのインスタンスをフィールドごとに比較できます。次に例を示します。
vector3i := struct{X:int, Y:int, Z:int}
vector3i{X:=0, Y:=0, Z:=0} = vector3i{X:=0, Y:=0, Z:=0} # 成功します
vector3i{X:=0, Y:=0, Z:=0} = vector3i{X:=0, Y:=0, Z:=1} # 2 つのインスタンス間で Z が異なることから失敗します