パルワールドのMOD開発 Hello, World編
パルワールドのMODを作ってみよ~ってことで、
UE4SSを使ったデバッグと簡単なMODを作成と動作確認をやってみる。
UE4SSのセットアップ
UE4SSはUnreal Engine製ゲーム用のスクリプト実行システム。
新たに追加したコードをゲーム内で実行できるようにする機能だけじゃなく、
デバッグのために必要な機能もついてる。
説明にもある通り、基本的にはWindows用。
とりあえず環境構築をしてみる。
最新の安定版のリリースから、xinputバージョンをダウンロードする
https://scrapbox.io/files/65bd7dd5572c6f0024853c88.png
安定版かどうかは、Experimental(実験的)という説明がないかで分かる
ゲームのインストールされているディレクトリに行く
Steamクライアントのライブラリから対象ゲームを右クリック > 管理 > ローカルファイルを閲覧 で開ける
https://scrapbox.io/files/65bd7e626f02a10025b0fd2c.png
開いた Palworld から Palworld > Pal > Binaries > Win64 と進む
https://scrapbox.io/files/65bd7f7100614200264dc882.png
UE4SS_Xinputの中身をWin64にコピーする
https://scrapbox.io/files/65bd7ff0bb617100247e9395.png
パルワールドを起動してConsoleが開かれればセットアップ完了
https://scrapbox.io/files/65bd802dc975920024c76739.png
Win64にコピーした UE4SS-settings.ini を編集し、開発に便利な機能をいくつか有効にしておく
code:UE4SS-settings.ini
EnableHotReloadSystem = 1 ; 0 -> 1
GuiConsoleVisible = 1 ; 0 -> 1
EnableHotReloadSystem ホットリロードを有効にすることで、Ctrl + RでMODの再読み込みができる
GuiConsoleVisible GuiConsoleで各種パラメータをリアルタイムに見ることができる
他にも便利機能に気付いたら後ほどここに追加するかも
Palworld/Pal/Binaries/Win64/Mods ディレクトリを Modsディレクトリと呼ぶことにする
Hello, Worldしてみる
Hello, World用のスクリプトファイルの設置
Mods/MyLuaMod/scripts/ ディレクトリを作成
Mods/MyLuaMod/scripts/main.lua ファイルを作成
main.lua にログを出力するだけの命令を書いておく
code:Mods/MyLuaMod/scripts/main.lua
Mods/mods.txt の末尾に新しいModの行を追加
code:Mods/mods.txt
...
MyLuaMod : 1
ゲームを起動すると追加したログが出力されていることを確認できる
https://scrapbox.io/files/65bdb9960347cf0025a178fb.png
ちなみにファイルパスや名前を間違えていたりすると、 No such file or directory とエラーがでる
https://scrapbox.io/files/65bdb9debb61710024800d4d.png
Hello, Worldって出力したわけじゃないけど、まあGetting Startedらしい内容なのではないかと
プレイヤーの位置を取得する例
キーボードバインドを追加し、Ctrl + F1を押されたらプレイヤーの位置を取得
二度目以降の位置取得では前回の位置との距離を計算して出力する
位置情報の取得は下記の通りに行なわれるとのこと
The player coordinates are retrieved in the following way:
1. Gets the player controller using UE4SS UEHelpers class.
2. Get the Pawn, which represents the actual "physical" entity that the player can control in Unreal Engine.
3. Call the appropriate Unreal Engine method K2_GetActorLocation that returns a Pawn's location (by accessing its parent Actor class).
4. The location is a 3-component vector of Unreal Engine type FVector, having X, Y and Z as its fields.
和訳するよりコード見たほうが分かりやすいと思うのでコードへ
code:Mods/MyLuaMod/scripts/main.lua
local UEHelpers = require("UEHelpers")
local lastLocation = nil
function ReadPlayerLocation()
local FirstPlayerController = UEHelpers:GetPlayerController()
local Pawn = FirstPlayerController.Pawn
local Location = Pawn:K2_GetActorLocation()
print(string.format("MyLuaMod Player location: {x=%.3f, y=%.3f, z=%.3f}\n", Location.X, Location.Y, Location.Z)) if lastLocation then
print(string.format("MyLuaMod Player moved: {delta_x=%.3f, delta_y=%.3f, delta_z=%.3f}\n", Location.X - lastLocation.X,
Location.Y - lastLocation.Y,
Location.Z - lastLocation.Z,)
)
end
lastLocation = Location
end
RegisterKeyBind(Key.F1, {ModifierKey.CONTROL}, function()
ExecuteInGameThread(function() ReadPlayerLocation end)
end)
このまま実行するとエラーになる
まず最初のエラー
[04:13:13] Error: [Lua::execute_file] luaL_loadfile returned LUA_ERRSYNTAX => ...lworld\Pal\Binaries\Win64\Mods\MyLuaMod\scripts\main.lua:19: unexpected symbol near ')'
19行目で突然 ) が登場しておかしいですよーというもの。
下記の string.format 関数の末尾に不要な , があることが原因
code:main.lua
if lastLocation then
print(string.format("MyLuaMod Player moved: {delta_x=%.3f, delta_y=%.3f, delta_z=%.3f}\n", Location.X - lastLocation.X,
Location.Y - lastLocation.Y,
Location.Z - lastLocation.Z,)
)
end
もう一つのエラーが
[04:14:05] Error: [Lua::execute_file] luaL_loadfile returned LUA_ERRSYNTAX => ...lworld\Pal\Binaries\Win64\Mods\MyLuaMod\scripts\main.lua:29: syntax error near 'end'
29行目当たりで突然 end が登場してますよー というもの。
ExecuteInGameThread 内の無名関数内で、ReadPlayerLocation関数を正しく呼び出せていないのが問題
code:main.lua
RegisterKeyBind(Key.F1, {ModifierKey.CONTROL}, function()
ExecuteInGameThread(function() ReadPlayerLocation end)
end)
正解の最終的なコードはこんな感じ
code:Mods/MyLuaMod/scripts/main.lua
local UEHelpers = require("UEHelpers")
local lastLocation = nil
function ReadPlayerLocation()
local FirstPlayerController = UEHelpers:GetPlayerController()
local Pawn = FirstPlayerController.Pawn
local Location = Pawn:K2_GetActorLocation()
print(string.format("MyLuaMod Player location: {x=%.3f, y=%.3f, z=%.3f}\n", Location.X, Location.Y, Location.Z)) if lastLocation then
print(string.format("MyLuaMod Player moved: {delta_x=%.3f, delta_y=%.3f, delta_z=%.3f}\n", Location.X - lastLocation.X,
Location.Y - lastLocation.Y,
Location.Z - lastLocation.Z)
)
end
lastLocation = Location
end
RegisterKeyBind(Key.F1, {ModifierKey.CONTROL}, function()
ExecuteInGameThread(function() ReadPlayerLocation() end)
end)
該当箇所を修正し、パルワールド画面がアクティブになっている状態で、Ctrl + Rでホットリロードすると、
[04:18:18] [Lua] [MyLuaMod] Mod loaded とログが出て、正しく呼び出せていることを確認できる
さっそく適当な位置に移動して、Ctrl + F1を入力すると、Consoleに
[04:21:57] [Lua] [MyLuaMod] Player location: {x=-357444.556, y=268282.568, z=7925.051}
と出て位置情報が取れていることが分かる
そこからゲーム内で少し移動して、再度 Ctrl + F1 と入力
[04:22:06] [Lua] [MyLuaMod] Player location: {x=-359351.996, y=267813.747, z=7994.493}
[04:22:06] [Lua] [MyLuaMod] Player moved: {delta_x=-1907.440, delta_y=-468.821, delta_z=69.442}
移動距離も取れている
もう一回
[04:22:21] [Lua] [MyLuaMod] Player location: {x=-360531.105, y=268632.451, z=8312.958}
[04:22:21] [Lua] [MyLuaMod] Player moved: {delta_x=-1179.109, delta_y=818.705, delta_z=318.465}
ちょっと歩いただけで1000も座標を移動しているのかー
ただし、上記以外にもオブジェクト取れてねーぞ的なエラーがでるので、
パルワールドでは何か工夫か何かがいるのかも?
そのあたりはこれから調べていこう
さいごに、使い終わったテストMODは読み込まれないように無効にしましょう。
code:Mods/mods.txt
MyLuaMod : 0 ; 1 -> 0
更新履歴
2024/02/03 かきおわり
2024/02/03 かきはじめ