[visualstudio][cpp][UE5]UE5 Logic Nightで話題になったイベントディスパッチャーとデリゲートに関する調べものvol.03アニメーション終了時にイベント起こす

エンジンソース的には

D:\Program Files\Epic Games\UE_5.3\Engine\Source\Runtime\Engine\Classes\Animation\AnimInstance.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
//中略
#include "Animation/AnimSubsystemInstance.h"
#include "Animation/AnimSync.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "AnimInstance.generated.h"

// Post Compile Validation requires WITH_EDITOR
#define ANIMINST_PostCompileValidation WITH_EDITOR

struct FDisplayDebugManager;
class FDebugDisplayInfo;
class IAnimClassInterface;
class UAnimInstance;
//中略
struct FSmartNameMapping;
struct FAnimNode_LinkedAnimLayer;
struct FNodeDebugData;
enum class ETransitionRequestQueueMode : uint8;
enum class ETransitionRequestOverwriteMode : uint8;
class UAnimMontage;

typedef TArray<FTransform> FTransformArrayA2;

namespace UE::Anim
{
	struct FHeapAttributeContainer;
	using FSlotInertializationRequest = TPair<float, const UBlendProfile*>;
	struct FCurveFilterSettings;
}	// namespace UE::Anim

//中略
//デリゲートのパラメータもらうやつ
DECLARE_DELEGATE_OneParam(FOnMontageStarted, UAnimMontage*)
DECLARE_DELEGATE_TwoParams(FOnMontageEnded, UAnimMontage*, bool /*bInterrupted*/)
DECLARE_DELEGATE_TwoParams(FOnMontageBlendingOutStarted, UAnimMontage*, bool /*bInterrupted*/)
/**
* Delegate for when Montage がスタートした時
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMontageStartedMCDelegate, UAnimMontage*, Montage);

/**
* Delegate for when Montage が再生 completedの時,  中断されたかinterrupted or 終了したかfinished
* このモンタージュの重みは 0.f なので、出力ポーズへの寄与は停止します
*
* プロパティが完了していない場合は bInterrupted = true
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMontageEndedMCDelegate, UAnimMontage*, Montage, bool, bInterrupted);

/**すべてのモンタージュインスタンスが終了したときに委任します。 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAllMontageInstancesEndedMCDelegate);

/**
モンタージュがブレンドアウトを開始したタイミング(中断または終了)のデリゲート
* このモンタージュの DesiredWeight は 0.f になりますが、これは出力ポーズに引き続き影響します
*
* プロパティが終了していない場合は bInterrupted = true
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMontageBlendingOutStartedMCDelegate, UAnimMontage*, Montage, bool, bInterrupted);

/** ネイティブコードがフックして追加の遷移ロジックを提供できるデリゲート */
DECLARE_DELEGATE_RetVal(bool, FCanTakeTransition);

/** ネイティブコードがフックして状態の開始/終了を処理できるデリゲート */
DECLARE_DELEGATE_ThreeParams(FOnGraphStateChanged, const struct FAnimNode_StateMachine& /*Machine*/, int32 /*PrevStateIndex*/, int32 /*NextStateIndex*/);

/** ユーザーがカスタムアニメーションカーブ値を挿入できるようにするデリゲート - 今のところは単一のみです。これを複数のデリゲートにして値を順番に取得する方法はわかりません。 */
DECLARE_DELEGATE_OneParam(FOnAddCustomAnimationCurves, UAnimInstance*)

/** 「PlayMontageNotify」および「PlayMontageNotifyWindow」によって呼び出されるデリゲート **/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FPlayMontageAnimNotifyDelegate, FName, NotifyName, const FBranchingPointNotifyPayload&, BranchingPointPayload);


//中略

/** Helper struct to store a Queued Montage BlendingOut event. */
struct FQueuedMontageBlendingOutEvent
{
	TObjectPtr<class UAnimMontage> Montage;
	bool bInterrupted;
	FOnMontageBlendingOutStarted Delegate;

	FQueuedMontageBlendingOutEvent()
		: bInterrupted(false)
	{}

	FQueuedMontageBlendingOutEvent(class UAnimMontage* InMontage, bool InbInterrupted, FOnMontageBlendingOutStarted InDelegate)
		: Montage(InMontage)
		, bInterrupted(InbInterrupted)
		, Delegate(InDelegate)
	{}
};

/** 以下長すぎるので省略 */

	UPROPERTY(BlueprintAssignable)
	FOnMontageStartedMCDelegate OnMontageStarted;

	/** Called when a montage has ended, whether interrupted or finished*/
	UPROPERTY(BlueprintAssignable)
	FOnMontageEndedMCDelegate OnMontageEnded;

デリゲートするとき統一の呪文のパラメータもらうやつ

DECLARE_DELEGATE_OneParam(FOnMontageStarted, UAnimMontage) DECLARE_DELEGATE_TwoParams(FOnMontageEnded, UAnimMontage, bool /bInterrupted/)
DECLARE_DELEGATE_TwoParams(FOnMontageBlendingOutStarted, UAnimMontage, bool /bInterrupted*/)

Montage が再生 completedの時, 中断されたかinterrupted or 終了したかfinished

  • モンタージュが中断または終了したときに委任する
  • このモンタージュの重みは 0.f なので、出力ポーズへの寄与は停止します
  • プロパティが完了していない場合は bInterrupted = true
    / DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMontageEndedMCDelegate, UAnimMontage, Montage, bool, bInterrupted);

D:\Program Files\Epic Games\UE_5.3\Engine\Source\Runtime\Engine\Private\Animation\AnimInstance.cpp

//SET
void UAnimInstance::Montage_SetEndDelegate(FOnMontageEnded& InOnMontageEnded, UAnimMontage* Montage)
{
	if (Montage)
	{
		FAnimMontageInstance* MontageInstance = GetActiveInstanceForMontage(Montage);
		if (MontageInstance)
		{
			MontageInstance->OnMontageEnded = InOnMontageEnded;
		}
	}
	else
	{
		// If no Montage reference, do it on all active ones.
		for (int32 InstanceIndex = 0; InstanceIndex < MontageInstances.Num(); InstanceIndex++)
		{
			FAnimMontageInstance* MontageInstance = MontageInstances[InstanceIndex];
			if (MontageInstance && MontageInstance->IsActive())
			{
				MontageInstance->OnMontageEnded = InOnMontageEnded;
			}
		}
	}
}


//GET
FOnMontageEnded* UAnimInstance::Montage_GetEndedDelegate(UAnimMontage* Montage)
{
	if (Montage)
	{
		FAnimMontageInstance* MontageInstance = GetActiveInstanceForMontage(Montage);
		if (MontageInstance)
		{
			return &MontageInstance->OnMontageEnded;
		}
	}
	else
	{
		// If no Montage reference, use first active one found.
		for (int32 InstanceIndex = 0; InstanceIndex < MontageInstances.Num(); InstanceIndex++)
		{
			FAnimMontageInstance* MontageInstance = MontageInstances[InstanceIndex];
			if (MontageInstance && MontageInstance->IsActive())
			{
				return &MontageInstance->OnMontageEnded;
			}
		}
	}

	return nullptr;
}

Montage_SetEndDelegate関数によってSETできるデリゲート

void UAnimInstance::Montage_SetEndDelegate(FOnMontageEnded& InOnMontageEnded, UAnimMontage* Montage)

Montage_GetEndedDelegate関数によってGETできるデリゲート

FOnMontageEnded* UAnimInstance::Montage_GetEndedDelegate(UAnimMontage* Montage)

エンジンソース終わり

使い方

AnimInstanceのイベントディスパッチャーで使うと Bind Event To On Montage Endとなる。

動いた

C++サンプル

.h

// ヘッダー内の関数宣言
void OnAnimationEnded(UAnimMontage* Montage, bool bInterrupted);

.cpp

// 実装内 
// デリゲート FOnMontageEnded型のEndDelegate を宣言 
FOnMontageEnded EndDelegate;
// バインド 
EndDelegate.BindUObject(this, &UMyClass::OnAnimationEnded); 
// 設定 
MyAnimInstance->Montage_SetEndDelegate(EndDelegate);

参考URL

AnimInstance->OnMontageEnd に関数をバインドするにはどうすればよい

https://forums.unrealengine.com/t/how-can-i-bind-a-function-to-animinstance-onmontageend/290717/2

[visualstudio][cpp][UE5]UE5 Logic Nightで話題になったイベントディスパッチャーとデリゲートに関する調べものvol.02エンジンソースで見つけたUnrealEngine C++のデリゲート Oculus の例

.h

D:\Program Files\Epic Games\UE_5.3\Engine\Plugins\Online\OnlineSubsystemOculus\Source\Classes\OculusFindSessionsCallbackProxy.h

ヘッダーでデリゲート変数の型指定してる

FOnFindSessionsCompleteDelegate Delegate; これがデリゲート変数の型指定

ヘッダーでデリゲートハンドルの型指定してる。

//登録された OnFindSessionsComplete デリゲートへのハンドル
FDelegateHandle DelegateHandle;

というのは・・・・

D:\Program Files\Epic Games\UE_5.3\Engine\Plugins\Online\OnlineSubsystem\Source\Public\Interfaces\OnlineSessionDelegates.h

でこのように定義されている

OnlineSessionDelegates.hでの FOnFindSessionsCompleteDelegate の定義

  • オンライン セッションの検索が完了したときに呼び出されるデリゲート
  • @param bWasSuccessful 非同期アクションがエラーなしで完了した場合は true、エラーがあった場合は false

    DECLARE_MULTICAST_DELEGATE_OneParam(FOnFindSessionsComplete, bool);
    typedef FOnFindSessionsComplete::FDelegate FOnFindSessionsCompleteDelegate;

.h 全文

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "UObject/Object.h"
#include "Net/OnlineBlueprintCallProxyBase.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "FindSessionsCallbackProxy.h"
#include "OculusFindSessionsCallbackProxy.generated.h"

/**
 * Exposes FindSession of the Platform SDK for blueprint use.
 */
UCLASS(MinimalAPI)
class UOculusFindSessionsCallbackProxy : public UOnlineBlueprintCallProxyBase
{
	GENERATED_UCLASS_BODY()

	// Called when there is a successful query
	UPROPERTY(BlueprintAssignable)
	FBlueprintFindSessionsResultDelegate OnSuccess;

	// Called when there is an unsuccessful query
	UPROPERTY(BlueprintAssignable)
	FBlueprintFindSessionsResultDelegate OnFailure;

	// Searches for matchmaking room sessions with the oculus online subsystem
	UFUNCTION(BlueprintCallable, Category = "Oculus|Session", meta = (BlueprintInternalUseOnly = "true"))
	static UOculusFindSessionsCallbackProxy* FindMatchmakingSessions(int32 MaxResults, FString OculusMatchmakingPool);

	// Searches for moderated room sessions with the oculus online subsystem
	UFUNCTION(BlueprintCallable, Category = "Oculus|Session", meta = (BlueprintInternalUseOnly = "true"))
	static UOculusFindSessionsCallbackProxy* FindModeratedSessions(int32 MaxResults);

	// UOnlineBlueprintCallProxyBase interface
	virtual void Activate() override;
	// End of UOnlineBlueprintCallProxyBase interface

private:
	// Internal callback when the session search completes, calls out to the public success/failure callbacks
	void OnCompleted(bool bSuccess);

private:

	// The delegate executed by the online subsystem
	FOnFindSessionsCompleteDelegate Delegate;//デリゲート宣言

	// Handle to the registered OnFindSessionsComplete delegate
	FDelegateHandle DelegateHandle;//デリゲートハンドル宣言

	// Object to track search results
	TSharedPtr<FOnlineSessionSearch> SearchObject;

	// Maximum number of results to return
	int MaxResults;

	// Optional: if searching within a matchmaking pool
	FString OculusPool;

	bool bSearchModeratedRoomsOnly;
};

CPP

D:\Program Files\Epic Games\UE_5.3\Engine\Plugins\Online\OnlineSubsystemOculus\Source\Private\OculusFindSessionsCallbackProxy.cpp では

デリゲートにOnComplitedをバインドしてる

Super(ObjectInitializer) のあと

Delegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))

デリゲートハンドルにDelegateを渡してる。(コールしてる)

if (OculusSessionInterface.IsValid()) のタイミングで

DelegateHandle = OculusSessionInterface->AddOnFindSessionsCompleteDelegate_Handle(Delegate);

// Copyright Epic Games, Inc. All Rights Reserved.

#include "OculusFindSessionsCallbackProxy.h"
#include "OnlineSubsystemOculusPrivate.h"
#include "Online.h"
#include "OnlineSessionInterfaceOculus.h"
#include "OnlineSubsystemOculusPrivate.h"

UOculusFindSessionsCallbackProxy::UOculusFindSessionsCallbackProxy(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	  , Delegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
	  , MaxResults(0)
	  , bSearchModeratedRoomsOnly(false)
{
//デリゲートにOnComplitedをバインド
}

UOculusFindSessionsCallbackProxy* UOculusFindSessionsCallbackProxy::FindMatchmakingSessions(int32 MaxResults, FString OculusMatchmakingPool)
{
	UOculusFindSessionsCallbackProxy* Proxy = NewObject<UOculusFindSessionsCallbackProxy>();
	Proxy->SetFlags(RF_StrongRefOnFrame);
	Proxy->MaxResults = MaxResults;
	Proxy->OculusPool = MoveTemp(OculusMatchmakingPool);
	Proxy->bSearchModeratedRoomsOnly = false;
	return Proxy;
}

UOculusFindSessionsCallbackProxy* UOculusFindSessionsCallbackProxy::FindModeratedSessions(int32 MaxResults)
{
	UOculusFindSessionsCallbackProxy* Proxy = NewObject<UOculusFindSessionsCallbackProxy>();
	Proxy->SetFlags(RF_StrongRefOnFrame);
	Proxy->MaxResults = MaxResults;
	Proxy->bSearchModeratedRoomsOnly = true;
	return Proxy;
}

void UOculusFindSessionsCallbackProxy::Activate()
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
	auto OculusSessionInterface = Online::GetSessionInterface(OCULUS_SUBSYSTEM);
PRAGMA_ENABLE_DEPRECATION_WARNINGS

	if (OculusSessionInterface.IsValid())
	{
        //デリゲートハンドルにDelegateを渡してる。(コールしてる)
		DelegateHandle = OculusSessionInterface->AddOnFindSessionsCompleteDelegate_Handle(Delegate);

		SearchObject = MakeShareable(new FOnlineSessionSearch);
		SearchObject->MaxSearchResults = MaxResults;
		SearchObject->QuerySettings.Set(SEARCH_OCULUS_MODERATED_ROOMS_ONLY, bSearchModeratedRoomsOnly, EOnlineComparisonOp::Equals);

		if (!OculusPool.IsEmpty())
		{
			SearchObject->QuerySettings.Set(SETTING_OCULUS_POOL, OculusPool, EOnlineComparisonOp::Equals);
		}

		OculusSessionInterface->FindSessions(0, SearchObject.ToSharedRef());
	}
	else
	{
		UE_LOG_ONLINE_SESSION(Error, TEXT("Oculus platform service not available. Skipping FindSessions."));
		TArray<FBlueprintSessionResult> Results;
		OnFailure.Broadcast(Results);
	}
}

void UOculusFindSessionsCallbackProxy::OnCompleted(bool bSuccess)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
	auto OculusSessionInterface = Online::GetSessionInterface(OCULUS_SUBSYSTEM);
PRAGMA_ENABLE_DEPRECATION_WARNINGS

	if (OculusSessionInterface.IsValid())
	{
		OculusSessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(DelegateHandle);
	}

	TArray<FBlueprintSessionResult> Results;

	if (bSuccess && SearchObject.IsValid())
	{
		for (auto& Result : SearchObject->SearchResults)
		{
			FBlueprintSessionResult BPResult;
			BPResult.OnlineResult = Result;
			Results.Add(BPResult);
		}

		OnSuccess.Broadcast(Results);
	}
	else
	{
		OnFailure.Broadcast(Results);
	}
}

ちょっと複雑すぎてちょと何言ってるかわからない感じもする。が

まとめ4つを同じように見つけることができた。

1、デリゲート宣言

FOnFindSessionsCompleteDelegate Delegate;

2、ハンドル初期化

FDelegateHandle DelegateHandle;

3,バインド

Delegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))

4タイミングでコール呼び出し

DelegateHandle = OculusSessionInterface->AddOnFindSessionsCompleteDelegate_Handle(Delegate);

自信がないので間違っていたら連絡してほしいです。

[UE5.3.2][VisualStudio][Cpp]UnrealEngineのCPPプラグインからOpenAI(ChatGPT4o)を呼んで返り値jsonからパースしてPython部分を抜き出して実行!!できた!!!

今回も以下の続き

furcraeaUEOpenAIBPLibrary.h

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "furcraeaUEOpenAIBPLibrary.generated.h"

UCLASS()
class UfurcraeaUEOpenAIBPLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

public:
    UfurcraeaUEOpenAIBPLibrary(const FObjectInitializer& ObjectInitializer);

    UFUNCTION(BlueprintCallable, Category = "furcraeaUEOpenAI")
    static FString furcraeaUEOpenAISampleFunction(FString Param);

private:
    static FString SendChatCompletionRequest(FString UserMessage);
    static void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);

    static void ResponseMessageToPythonCode(FString ResponseMessage);

    static bool RunMyPython(int32& ProcessId,
        FString FullPathOfProgramToRun, TArray<FString> CommandlineArgs, bool Hidden);
};

furcraeaUEOpenAIBPLibrary.cpp

#include "furcraeaUEOpenAIBPLibrary.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Json.h"
#include "JsonUtilities.h"
#include "String.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Engine/Engine.h"
#include "PythonBridge.h"


//IMPLEMENT_GAME_MODULE(FDefaultGameModuleImpl, furcraeaUEOpenAIBPLibrary);

UfurcraeaUEOpenAIBPLibrary::UfurcraeaUEOpenAIBPLibrary(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    UE_LOG(LogTemp, Warning, TEXT("Hello World! UfurcraeaUEOpenAIBPLibrary ObjectInitializer"));
}

FString UfurcraeaUEOpenAIBPLibrary::furcraeaUEOpenAISampleFunction(FString Param)
{
    FString UserMessage = Param;
    FString ResponseMessage = TEXT("");
    UE_LOG(LogTemp, Warning, TEXT("Hello World! furcraeaUEOpenAISampleFunction"));
    
	ResponseMessage= SendChatCompletionRequest(UserMessage);
    return ResponseMessage;
}

FString UfurcraeaUEOpenAIBPLibrary::SendChatCompletionRequest(FString UserMessage)
{
    FString ApiKey = TEXT("sk-から始まるキー");

    if (!FModuleManager::Get().IsModuleLoaded("Http"))
    {
        FModuleManager::Get().LoadModule("Http");
    }

    FHttpModule* Http = &FHttpModule::Get();
    if (!Http)
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to get FHttpModule instance"));
        return FString();
    }

    TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = Http->CreateRequest();
    HttpRequest->SetURL("https://api.openai.com/v1/chat/completions");
    HttpRequest->SetVerb("POST");
    HttpRequest->SetHeader("Content-Type", "application/json");
    HttpRequest->SetHeader("Authorization", "Bearer " + ApiKey);

    FString Content = FString::Printf(TEXT(R"(
    {
        "model": "gpt-4o-mini",
        "messages": [{"role": "user", "content": "%s"}],
        "temperature": 0.7
    }
    )"), *UserMessage);

    HttpRequest->SetContentAsString(Content);

    //HttpRequest->OnProcessRequestComplete().BindUObject(this, &UfurcraeaUEOpenAIBPLibrary::OnResponseReceived);
    HttpRequest->OnProcessRequestComplete().BindLambda([](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
        {
            OnResponseReceived(Request, Response, bWasSuccessful);
        });
    HttpRequest->ProcessRequest();

    return FString();
}

void UfurcraeaUEOpenAIBPLibrary::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
    if (bWasSuccessful && Response.IsValid())
    {
        FString ResponseContent = Response->GetContentAsString();
        TSharedPtr<FJsonObject> JsonObject;
        TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseContent);

        if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
        {
            FString MessageContent = JsonObject->GetArrayField("choices")[0]->AsObject()->GetObjectField("message")->GetStringField("content");
            UE_LOG(LogTemp, Warning, TEXT("OnResponseReceived() Response Message: %s"), *MessageContent);
            ResponseMessageToPythonCode(MessageContent);
        }
        else
        {
            UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON response"));
        }
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Request failed"));
    }
}


static std::string replaceOtherStr(std::string& replacedStr, std::string from, std::string to) {
    const unsigned int pos = replacedStr.find(from);
    const int len = from.length();

    if (pos == std::string::npos || from.empty()) {
        return replacedStr;
    }

    return replacedStr.replace(pos, len, to);
}


void UfurcraeaUEOpenAIBPLibrary::ResponseMessageToPythonCode(FString ResponseMessage)
{
    //ResponseMessage の文字列から「```python」で始まり「```」で終わるまでの文字列を抜き出して
    


        // FString から標準文字列に変換
        FString aFString= ResponseMessage;
        std::string aString = TCHAR_TO_ANSI(*aFString);
        long aStartIndex = aString.find("```python");
        
        long aEndIndex = aString.rfind("```");

		long aLength = aEndIndex - aStartIndex;
        std::string aPythonCode =aString.substr(aStartIndex+9, aLength-9);

        replaceOtherStr(aPythonCode, "?", "//");
		// 標準文字列から FString に変換
		FString PythonCode = FString(aPythonCode.c_str());
        

        UE_LOG(LogTemp, Warning, TEXT("ResponseMessageToPythonCode() StartIndex: %u"), aStartIndex);
        UE_LOG(LogTemp, Warning, TEXT("ResponseMessageToPythonCode() EndIndex: %u"), aEndIndex);

        if (aStartIndex != 0 && aEndIndex != 0)
        {
            


            //FString PythonCode2 = ResponseMessage.Mid(StartIndex + 1, PythonCodeLength);
            UE_LOG(LogTemp, Warning, TEXT("ResponseMessageToPythonCode() 1 PythonCode: %s"), *PythonCode);
            //UE_LOG(LogTemp, Warning, TEXT("ResponseMessageToPythonCode() 2 PythonCode2: %s"), *PythonCode2);
            
             // Define the file path in the Saved directory
            FString FilePath = FPaths::ProjectSavedDir() / TEXT("ExtractedPythonCode.py");

            // Save the Python code to the file
            if (FFileHelper::SaveStringToFile(PythonCode, *FilePath))
            {
                UE_LOG(LogTemp, Warning, TEXT("Python code saved to: %s"), *FilePath);

                // Ensure the file path is correctly formatted for the exec command
                FString RelativeFilePath = FPaths::ConvertRelativePathToFull(FilePath);
                FString Command = FString::Printf(TEXT("py \"%s\""), *RelativeFilePath);
                //FString Command = FString::Printf(TEXT("\"%s\""), *RelativeFilePath);
				//GEngine->Exec(nullptr, *Command);

                UPythonBridge* bridge = UPythonBridge::Get();
                bridge->FunctionImplementedInPython();
                bridge->ExecuteCommand(RelativeFilePath);
            }
            else
            {
                UE_LOG(LogTemp, Error, TEXT("Failed to save Python code to file"));
            }


        }
        else 
        {
            UE_LOG(LogTemp, Warning, TEXT("ResponseMessageToPythonCode() PythonCode: in None"));
        }

    
}


PythonBridge.h (https://forums.unrealengine.com/t/running-a-python-script-with-c/114117/4)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine.h"
#include "UObject/NoExportTypes.h"
#include "PythonBridge.generated.h"

/**
 * 
 */
UCLASS(Blueprintable)
class FURCRAEAUEOPENAI_API UPythonBridge : public UObject
{
	GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = Python)
    static UPythonBridge* Get();

    UFUNCTION(BlueprintImplementableEvent, Category = Python)
    void FunctionImplementedInPython() const;

    UFUNCTION(BlueprintImplementableEvent, Category = Python)
    void ExecuteCommand(const FString& CommandName) const;
};

PythonBridge.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "PythonBridge.h"

UPythonBridge* UPythonBridge::Get()
{
    TArray<UClass*> PythonBridgeClasses;
    GetDerivedClasses(UPythonBridge::StaticClass(), PythonBridgeClasses);
    int32 NumClasses = PythonBridgeClasses.Num();
    if (NumClasses > 0)
    {
        return Cast<UPythonBridge>(PythonBridgeClasses[NumClasses - 1]->GetDefaultObject());
    }
	return nullptr;
}
/*
void UPythonBridge::FunctionImplementedInPython() const
{
    UE_LOG(LogTemp, Warning, TEXT("Hello World! FunctionImplementedInPython"));
}

void UPythonBridge::ExecuteCommand(const FString& CommandName) const
{
    UE_LOG(LogTemp, Warning, TEXT("Hello World! ExecuteCommand"));
}
*/

Project/Content/Python/init_unreal.py

import unreal
@unreal.uclass()
class PythonBridgeImplementation(unreal.PythonBridge):
    @unreal.ufunction(override=True)
    def function_implemented_in_python(self):
        print("function_implemented_in_python")
    @unreal.ufunction(override=True)
    def execute_command(self, command_name):
        print("execute_command  command_name="+command_name)
        exec(open(command_name).read())

で完成!! Cubeがちゃんと並ぶかは五分五分です。

AIのコマンド

https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/?application_version=5.3に乗ってるpythonでpep8に準拠してunrealengine5.3のpythonでレベルにcubeを10個並べて

出力されたpython


import unreal

def spawn_cubes(num_cubes=10, spacing=200.0):
    # //??????????
    actor_spawned = []
    world = unreal.EditorLevelLibrary.get_editor_world()

    # ??????????????????
    cube_mesh = unreal.EditorAssetLibrary.load_asset('/Engine/BasicShapes/Cube')

    for i in range(num_cubes):
        # ??????????????
        spawn_location = unreal.Vector(i * spacing, 0, 0)

        # ?????????
        cube_actor = unreal.EditorLevelLibrary.spawn_actor_from_object(cube_mesh, spawn_location)
        actor_spawned.append(cube_actor)

    return actor_spawned

# ?????10????
spawned_cubes = spawn_cubes()

管理用

https://drive.google.com/file/d/1qjtYztc1AuBDFLBygc1z_0taEzsa0Y4Q/view?usp=sharing

参考URL

https://forums.unrealengine.com/t/running-a-python-script-with-c/114117/4

[VS2022][UE5]Azure Dev OpsでUE5プロジェクトをGitで管理する

ここ

https://historia.co.jp/archives/12245

でVS2022だと画面が違うのでキャプチャを張りなおします。

Azure DevOpsからVSへ接続しながらクローンします。

VS で View > TeamExplorer

コマンドプロンプトは見つからなかったので

Explorer 右クリックして その他のオプションを確認 > Open Git Bash here します。

git lfs install します。

git lfs install

git lfs track “*.uasset” します。

git lfs track "*.uasset"

.gitattributes が生成されます。

VS にもどって Git Changes

Commit And Push します。

UEProjectName/Content/BP_GitLFSTestActor.assetというファイルを作成し、

ファイルに 123456789 //1byte以上ないと git lfs ls-files 反応しない を書き込みますl。

123456789 //1byte以上ないと git lfs ls-files 反応しない

チームエクスプローラーからリモートリポジトリにPushします。

git lfs ls-filesで確認します。

git lfs ls-files

15e2b0d3c3 * ActionCombat/Content/BP_GitLFSTestActor.uasset が出てくればOKです

最後に.gitattributesファイル内を以下にして   Commit And Push します。

#以下のサイトを参考に作成
#https://historia.co.jp/archives/12245/

*.uasset filter=lfs diff=lfs merge=lfs -text
*.umap filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text
*.float filter=lfs diff=lfs merge=lfs -text
*.pcx filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.exr filter=lfs diff=lfs merge=lfs -text
*.dds filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.xlsx filter=lfs diff=lfs merge=lfs -text
*.docx filter=lfs diff=lfs merge=lfs -text
*.pptx filter=lfs diff=lfs merge=lfs -text

VS2022UE5_ProjectBase2.zip

https://drive.google.com/file/d/1wQLoI0ESbsJF_K172fo8lcMZWwSWCxz-/view?usp=sharing

[UE5.3][VisualStudio]C++プラグインを作る方法

ポジTAさんのこのぺーじをvs2022でやる

https://zenn.dev/posita33/books/ue5_starter_cpp_and_bp_001/viewer/chap_01_vs2019_setup

C++Pluginの作り方

C++ Pluginの作成

プロジェクトを作成する際に「C++」でプロジェクトを作成します。

[Edit] > [Plugins]を選択します。

独自のPluginを作成するには左上の「Add」ボタンをクリックします。

プラグインの作成手順は以下の手順になります。

  1. Pluginのテンプレートを選択する
  2. Plugin名を入力する
  3. Plugin情報を入力する(任意)
  4. Create Pluginをクリック

Author制作者名
DescriptionPluginの説明
Author URL制作者のPlugin説明用のURLを書いておくとPluginの制作者名をクリックするとURLに移動する
Is Beta Versionチェックボックスを有効にすると、Plugin名の横にBetaが表示される

Pluginのウィンドウで作成したPlugin名を検索すると一覧に登録されています。

Editボタンや(Plugin名).pluginファイルで設定をより詳細に変更できます。

色々設定して128×128のアイコン設定して。。

ContentsBrowserやVisualStudioに追加されたPluginが表示されます。

以下の感じで吐き出される

furcraeaBluePrintLib.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Modules/ModuleManager.h"

class FfurcraeaBluePrintLibModule : public IModuleInterface
{
public:

	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
};

furcraeaBluePrintLib.cpp

// Copyright Epic Games, Inc. All Rights Reserved.

#include "furcraeaBluePrintLib.h"

#define LOCTEXT_NAMESPACE "FfurcraeaBluePrintLibModule"

void FfurcraeaBluePrintLibModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
	
}

void FfurcraeaBluePrintLibModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.
	
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FfurcraeaBluePrintLibModule, furcraeaBluePrintLib)

furcraeaBluePrintLibBPLibrary.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Kismet/BlueprintFunctionLibrary.h"
#include "furcraeaBluePrintLibBPLibrary.generated.h"

/* 
*	Function library class.
*	Each function in it is expected to be static and represents blueprint node that can be called in any blueprint.
*
*	When declaring function you can define metadata for the node. Key function specifiers will be BlueprintPure and BlueprintCallable.
*	BlueprintPure - means the function does not affect the owning object in any way and thus creates a node without Exec pins.
*	BlueprintCallable - makes a function which can be executed in Blueprints - Thus it has Exec pins.
*	DisplayName - full name of the node, shown when you mouse over the node and in the blueprint drop down menu.
*				Its lets you name the node using characters not allowed in C++ function names.
*	CompactNodeTitle - the word(s) that appear on the node.
*	Keywords -	the list of keywords that helps you to find node when you search for it using Blueprint drop-down menu. 
*				Good example is "Print String" node which you can find also by using keyword "log".
*	Category -	the category your node will be under in the Blueprint drop-down menu.
*
*	For more info on custom blueprint nodes visit documentation:
*	https://wiki.unrealengine.com/Custom_Blueprint_Node_Creation
*/
UCLASS()
class UfurcraeaBluePrintLibBPLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_UCLASS_BODY()

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "Execute Sample function", Keywords = "furcraeaBluePrintLib sample test testing"), Category = "furcraeaBluePrintLibTesting")
	static float furcraeaBluePrintLibSampleFunction(float Param);
};

furcraeaBluePrintLibBPLibrary.cpp

// Copyright Epic Games, Inc. All Rights Reserved.

#include "furcraeaBluePrintLibBPLibrary.h"
#include "furcraeaBluePrintLib.h"

UfurcraeaBluePrintLibBPLibrary::UfurcraeaBluePrintLibBPLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{

}

float UfurcraeaBluePrintLibBPLibrary::furcraeaBluePrintLibSampleFunction(float Param)
{
	return -1;
}

参考URL

全部ポジTAさんですありがとうございます。

https://zenn.dev/posita33/books/ue5_posilab_ue_research_and_development/viewer/category_350_pulugin_start

[UE5][python]selected staticmesh delete collision python

地形アセットを購入したら大量のスタティックメッシュのおかしなコリジョンがついてきた場合にどうぞ!

選択したスタティックメッシュのコリジョンを削除

assets = unreal.EditorUtilityLibrary.get_selected_assets()
AssetDataList = unreal.EditorUtilityLibrary.get_selected_asset_data() 

for a in AssetDataList:
    if a.asset_class_path.asset_name == "StaticMesh":
        mesh = unreal.EditorAssetLibrary.find_asset_data(a.package_name).get_asset()
        unreal.EditorStaticMeshLibrary.remove_collisions(mesh)

[UE5.4.2]UE 5.4.2 – Python を使用して C++ 関数を呼び出す方法

BlancではC++フォルダができなかった。

ThirdPersonでやった。

親クラスに Blueprint Function Libraryを選択

Nameに適当にZFunctionsと入れCreate Class。

VisualStudioへ移動し

ZFunctions.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ZFunctions.generated.h"

/**
 * 
 */
UCLASS()
class UE542PYTHONCALLCPP1_API UZFunctions : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
//ここから追加////////
public:
	UFUNCTION(BlueprintCallable)
	static void CalledFromPython(FString InputString);
//ここまで追加//////
};

ZFunctions.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "ZFunctions.h"
//ここから追加////////
void UZFunctions::CalledFromPython(FString InputString) {
	UE_LOG(LogTemp, Error, TEXT("%s"), *InputString);
}
//ここまで追加//////

SolutionExplorer > your Project Name (right click)>Build

実行する。(Local Windows Debagger)

Outputlogで実行する unrealのクラス一覧

import unreal
for x in sorted(dir(unreal)):
   print(x)

めっちゃ重い

ZFunction が出る

クラスを使ってみる unreal.ZFunctionsのメソッド一覧

import unreal
for x in sorted(dir(unreal.ZFunctions)):
   print(x)

called_from_python が出てくる。

呼んでみる

unreal.ZFunctions.called_from_python('my test string')

作った関数によりエラーのメッセージが返ってきた。

参考URL

[UE4.21]UE 4.21 – Python を使用して C++ 関数を呼び出す方法

コンテンツブラウザを右クリックしてNew C++ Class

親クラスに Blueprint Function Libraryを選択

Nameに適当にZFunctionsと入れCreate Class。

VisualStudioが起動し

ZFunctions.h

public:
UFUNCTION(BlueprintCallable)
static void CalledFromPython(FString InputString);

を追加してこうなる。

//.Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ZFunctions.generated.h"



UCLASS()
class UNREALPYTHONPROJECT_API UZFunctions : public UBlueprintFunctionLibrary
{
GENERATED BODY()

public:
   UFUNCTION(BlueprintCallable)
      static void CalledFromPython(FString InputString);
};

ZFunctions.cpp


//.Fill out your copyright notice in the Description page of Project Settings.

#include "ZFunctions.h"

void UZFunctions::CalledFromPython(FString InputString) {
UE_LOG(LogTemp, Error, TEXT("%s"), *InputString);
}

実行する。(Local Windows Debagger)

Outputlogで実行する

import unreal
for x in sorted(dir(unreal)):
   print(x)

ZFunction が出る

import unreal
for x in sorted(dir(unreal.ZFunctions)):
   print(x)

called_from_python が出てくる。

呼んでみる

unreal.ZFunctions.called_from_python('my test string')

呼ばれて作ったエラーメッセージが出た

参考URL

[UE5][C++]Python からブループリント関数を呼び出すことはできますか?

現時点では、Python はブループリント関数にアクセスできませんが、C++ メソッドにはアクセスできます。したがって
、解決策としては、C++ でブループリント関数を として宣言しBlueprintImplementableEvent、ブループリントでそれをオーバーライドします。

BlueprintImplementableEvent先に進む前に、を C++ から呼び出して、それが機能するかどうかを確認することをお勧めします。
次に、 を呼び出すための 2 番目の C++ 関数を作成しますBlueprintImplementableEvent。この関数は、Python に公開されるものであるため、
として実装する必要があります。BlueprintCallable

最後に、C++ とブループリントがコンパイルされたら、Python から呼び出し元関数を呼び出すだけです。
C++ クラスは、次のように通常の C++ モジュールとして表示されます。unreal.YourClassName.yourCallerFunctionName(yourClassInstance, args)
引数と戻り値は、期待どおりに処理されます。
関数を実行するクラス インスタンスを関数に渡す必要があります。特定の AActor インスタンスを操作するときに非常に便利です。


最後にもう 1 つ注意点があります。関数を、私が行ったようにブループリントイベントではなくブループリント関数として実装する必要がある場合は、関数を定数にするか、戻り値 (使用しない場合でも) を指定する必要があります。そうしないと、ブループリントのオーバーライドにイベントとして表示されます。

私のユースケースからの画面。明らかに、ブループリント関数の実装の詳細は関係ありません。
ブループリント関数

.h

.cpp

.py

参考

https://forums.unrealengine.com/t/is-it-possible-to-call-a-blueprint-function-from-python/447078/3

[UE5]CPPプロジェクトでプロジェクトのバージョンを上げる方法

[UE5] How to upgrade the project version in a CPP project

.uprojectを右クリックして>その他のオプションを確認>Switch Unreal Engine version …

Right-click on .uproject > Check other options > Switch Unreal Engine version…

モジュールのプロジェクト名は変更しないでください。

	"Modules": [
		{
			"Name": "KPUE532CPP",

KPUE54CPP.uprojectの中身も変更されます。

	"EngineAssociation": "5.4",

こんな感じです

変更前

{
	"FileVersion": 3,
	"EngineAssociation": "5.4",
	"Category": "",
	"Description": "",
	"Modules": [
		{
			"Name": "KPUE532CPP",
			"Type": "Runtime",
			"LoadingPhase": "Default"
		}
	],
	"Plugins": [
		{
			"Name": "ModelingToolsEditorMode",
			"Enabled": true,
			"TargetAllowList": [
				"Editor"
			]
		}
	]
}



変更後

{
	"FileVersion": 3,
	"EngineAssociation": "5.4",
	"Category": "",
	"Description": "",
	"Modules": [
		{
			"Name": "KPUE532CPP",
			"Type": "Runtime",
			"LoadingPhase": "Default"
		}
	],
	"Plugins": [
		{
			"Name": "ModelingToolsEditorMode",
			"Enabled": true,
			"TargetAllowList": [
				"Editor"
			]
		}
	]
}

内部的に他のファイルもたくさん変更されるようです。