UnrealEngine勉強会 ~UEでのイベントスクリプトの導入~

こんにちは。
株式会社アドグローブ ゲーム事業部エンジニアの鈴木です。
今回はアドグローブのゲーム事業部内で定期的に開催しているUnreal Engine勉強会(以下、UE勉強会)で、自分が発表した内容に関してご紹介します。

■はじめに

「UE勉強会」については下記紹介記事をご参照ください。
blog.adglobe.co.jp

今回は、私がUE勉強会で発表した「UE5にLuaMachineを導入」についてご紹介させていただきます。
「LuaMachine」はUnreal Engine上でLuaを実行可能なプラグインとなります。
こちらを用いてイベントスクリプトを実現していきます。

■イベントスクリプトとは

今回の記事では、ADVゲーム(アドベンチャーゲーム)におけるイベントスクリプトについて説明します。
イベントスクリプトは、ゲーム内でキャラクターの立ち絵や会話演出を呼び出すための機能であり、
プレイヤーにストーリーを体験させるうえで重要な役割を果たします。
主要な機能な機能としては以下のものが挙げられます。

  • 演出面
    • フェードイン/フェードアウト
    • BGM/SEの再生
    • 立ち絵表示
    • テキスト表示
  • 操作面
    • スキップ
    • 自動再生

■LuaMachineの紹介

イベントスクリプトの短期間で実現できるプラグインを検討しており、そこで検討したのが「LuaMachine」です。
こちらはテキストベースのスクリプト言語LuaをUnreal Engine上で動作可能なプラグインです。
特徴としては以下のものが挙げられます。
1. Unreal Engineのブループリント/C++でLuaファイルの読み出し再生が可能
2. 直ぐに使用できるようにComponentクラスが提供されている
3. エディタ上でスクリプトを編集できる簡単なエディタも付いてくる
4. パッケージ化の際にはテキスト形式だけではなく、バイトコード出力にも対応

マーケットプレイスで購入せずともGitHubでソースコードも公開されているため、そちらから導入することができます。
ライセンスに関してはMITライセンスで提供されています。

■LuaMachineを使用してみる

プループリントで使用する場合はプラグイン内にサンプルが多く存在する為、 この記事ではC++経由で実行する方法を記載します。

1. LuaStateの派生クラスを生成する

LuaStateはLuaの実行処理を担う仮想マシン(VM)を生成・実行することが出来るクラスです。
こちらをアプリケーション用に拡張する為にLuaStateの派生クラスの作成を行います。

#pragma once

#include "CoreMinimal.h"
#include "LuaState.h"
#include "TestLuaState.generated.h"

UCLASS()
class LUAMACHINETEST_API UTestLuaState : public ULuaState
{
    GENERATED_BODY()
    
};

2. 派生クラスの初期化でLua側で呼び出す関数の登録を行う

LuaStateの派生クラスで、Luaスクリプト側で呼び出したい関数を登録します。
Table.Add関数の引数は、1.Lua側に記述する関数名 2.呼び出すクラスの関数となります。
注意点として、クラスの関数に関してはUFUNCTION()マクロが記述されていないと実行時に呼び出しが失敗してしまいます。

#include "TestLuaState.h"

UTestLuaState::UTestLuaState()
{
    // 関数登録.
    Table.Add("FadeIn", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, FadeIn)));
    Table.Add("FadeOut", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, FadeOut)));
    Table.Add("PlayBGM", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, PlayBGM)));
    Table.Add("StopBGM", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, StopBGM)));
    Table.Add("PlaySE", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, PlaySE)));
    Table.Add("StopSE", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, StopSE)));
    Table.Add("CharacterIn", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, CharacterIn)));
    Table.Add("CharacterOut", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, CharacterOut)));
    Table.Add("Text", FLuaValue::Function(GET_FUNCTION_NAME_CHECKED(UTestLuaState, Text)));
}

3. 呼び出すC++関数の実装

Lua側で呼び出された後のC++側での内部処理を記述します。
Lua側から受け取る引数は全てFLuaValueとして受け取ります。

void UTestLuaState::FadeIn(FLuaValue InFadeType, FLuaValue InFadeSpeed)
{
    // FLuaValue型を適切な変数への変換.
    const EFadeType FadeType = static_cast<EFadeType>(InFadeType.ToInteger());
    const double FadeSpeed = InFadeSpeed.ToFloat();
    UE_LOG(LogTemp, Log, TEXT("FadeIn FadeType:%d FadeSpeed:%f"), static_cast<int>(FadeType), FadeSpeed);
    
    // ここでスクリプトで実行しやすい形に加工する.
    //const FadeManager& FadeManager = GetFadeManager();
    //FadeManager.FadeIn(FadeType, FadeSpeed);
}

4. Luaのアセットファイルを作成する

Editor上でLuaのアセットを作成します。手順としては以下の通りです。
1. コンテンツブラウザで右クリック
2. 「Lua Machine」→「Lua Code」
3. アセットを開いてLuaコードを記載
※テキスト形式のLuaファイルを事前に準備しておいて、それを割り当てることも可能です。

FadeIn_Black = 0
FadeIn_White = 1

Character_A = 1000

function TestScript()
  FadeIn(FadeIn_Black, 1.0)

  FadeOut(FadeIn_White, 2.0)

  PlayBGM(100, 1.0);

  StopBGM(100, 0.8);

  PlaySE(555)

  StopSE(555)

  CharacterIn(Character_A)

  CharacterOut(Character_A)

  Text(Character_A, "Hello LuaMachine")
end

5. C++からの呼び出し

最後にC++側でLuaの実行を行う手順を示します。
1. LuaState派生クラスのブループリントクラスを作成
2. LuaStateのCode Assetにスクリプトのアセットを割り当てる
2. LuaComponentを持ったクラスを生成
3. LuaComponentのメンバ変数LuaStateにLuaStateのブループリントを割り当てる

#pragma once

#include "CoreMinimal.h"
#include "LuaComponent.h"
#include "GameFramework/Actor.h"
#include "TestLuaActor.generated.h"

UCLASS()
class LUAMACHINETEST_API ATestLuaActor : public AActor
{
    GENERATED_BODY()

protected:
    /** コンストラクタ. */
    ATestLuaActor();

    /** 開始時処理. */
    virtual void BeginPlay() override;

public:
    /** Luaコンポーネント. */
    UPROPERTY(EditAnywhere)
    TObjectPtr<ULuaComponent> LuaComponent;
};
#include "TestLuaActor.h"

ATestLuaActor::ATestLuaActor()
{
    LuaComponent = CreateDefaultSubobject<ULuaComponent>(TEXT("LuaComponent"));
}

void ATestLuaActor::BeginPlay()
{
    Super::BeginPlay();

    TArray<FLuaValue> Args;
    LuaComponent->LuaCallFunction("", Args, true);
}

6. 実行結果

おわりに

今回は「LuaMachine」でイベントスクリプトを実行する手法を解説させていただきました。
テキストベースのスクリプトはファイルサイズも小さくバージョン管理も容易なのでおすすめです。
この記事を読んでスクリプト言語の導入に興味を持っていただけたら幸いです!最後までお読みいただきありがとうございました。

参照サイト

https://www.unrealengine.com/marketplace/ja/product/luamachine https://github.com/rdeioris/LuaMachine


アドグローブでは、さまざまなポジションで一緒に働く仲間を募集しています!
詳細については下記からご確認ください。みなさまからのご応募お待ちしております。

採用情報