Lots of refactoring related to Movement Component State. Still not done yet.

This commit is contained in:
2026-02-13 23:24:18 -05:00
parent 30e53c3054
commit 107cb57b66
17 changed files with 621 additions and 331 deletions

View File

@@ -1,5 +1,6 @@
[/Script/Engine.Engine]
+ActiveClassRedirects=(OldClassName="/Script/Integration.lxLookAtWidget",NewClassName="/Script/Integration.lxLuaWidget")
+ActiveClassRedirects=(OldClassName="/Script/Integration.K2Node_FormatErrorMessage",NewClassName="/Script/Integration.K2Node_FormatLogMessage")
[/Script/Engine.Engine]

Binary file not shown.

View File

@@ -1,12 +1,7 @@
#include "BlueprintErrors.h"
#include "Blueprint/BlueprintExceptionInfo.h"
#include "LuaCall.h"
#include "Internationalization/TextFormatter.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet2/KismetDebugUtilities.h"
#include "Kismet/KismetTextLibrary.h"
#include "AnimQueue.h"
ELogVerbosity::Type UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(ElxLogVerbosity Verbosity) {
switch (Verbosity) {
@@ -20,231 +15,6 @@ ELogVerbosity::Type UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(ElxLogVerbo
}
}
void UlxBlueprintErrorLibrary::FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs)
{
// Generate the formatted string.
//
FText InPatternText(FText::FromString(InPattern));
FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false);
FString MessageString = Message.ToString();
// Get the blueprint name.
//
// Normally, the log function expects you to pass in a filename, and a log
// category name. We use the blueprint name for both.
//
// Using the blueprint name as a log category name is not technically
// correct. However, there is no correct way to create log categories
// from inside of blueprints. Doing it this way at least produces a reasonable
// message inside the log. What doesn't work correctly is the log message
// suppression system. Ie, console commands like 'log <category> verbose'
// don't have any effect here. The design of the log message suppression
// system is such that there just is no reasonable way to hook into it from
// inside of blueprints.
//
FString BlueprintNameString = Context->GetClass()->GetName();
auto BlueprintNameAnsi = StringCast<ANSICHAR>(*BlueprintNameString);
FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName());
// Output to Log
//
ELogVerbosity::Type VerbosityValue = ConvertElxLogVerbosity(Verbosity);
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
{
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
}
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_BoolToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt64(int64 Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataFloat(float Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Float;
Result.ArgumentName = Name;
Result.ArgumentValueFloat = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataDouble(double Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Double;
Result.ArgumentName = Name;
Result.ArgumentValueDouble = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataText(FText Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataString(FString Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_StringToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataName(FName Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataKey(FKey Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(Value.GetFName());
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGender(ETextGender Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Gender;
Result.ArgumentName = Name;
Result.ArgumentValueGender = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataObject(UObject *Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_ObjectToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector(const FVector &Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_VectorToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector2D(const FVector2D &Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_Vector2dToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataRotator(const FRotator &Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_RotatorToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTransform &Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_TransformToText(Value);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(Value->DebugString());
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataAnimationStep(const FlxAnimationStep &Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(UlxAnimationStepLibrary::AnimationStepDebugString(Value));
return Result;
}
FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataBlank(const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText();
return Result;
}
FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject)
{
const UEnum *Enum = Cast<const UEnum>(PinSubCategoryObject);
FFormatArgumentData Result;
if (Enum == nullptr)
{
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
}
else
{
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value));
}
return Result;
}
FlxDebugBlueprintErrorsOutputDevice::FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef)
: Sensitivity(SensitivityRef)
{

View File

@@ -6,7 +6,6 @@
#include "Containers/Array.h"
#include "CoreMinimal.h"
#include "InputCoreTypes.h"
#include "HAL/Platform.h"
#include "Misc/OutputDeviceError.h"
#include "UObject/NameTypes.h"
@@ -16,9 +15,6 @@
#include "BlueprintErrors.generated.h"
class UlxLuaValues;
struct FlxAnimationStep;
/*
* enum class ElxLogVerbosity, below, contains all the same error severity levels
* as ELogVerbosity, but in a form that the blueprint editor can manipulate.
@@ -69,93 +65,11 @@ class UlxBlueprintErrorLibrary : public UBlueprintFunctionLibrary
GENERATED_BODY()
public:
// The Format Error Message blueprint node macroexpands, the following
// function is the core of the expansion. The actual K2Node itself is in
// its own source file.
//
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs);
// A formatting routine for pins that were never connected.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBlank(const FString &Name);
// A specialized formatting routine for pins of enum types.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject);
// Convert an ElxLogVerbosity to an ELogVerbosity::Type
//
static ELogVerbosity::Type ConvertElxLogVerbosity(ElxLogVerbosity Verbosity);
};
/*
* A library that contains functions that convert data into FFormatArgumentData
* structs, so that the data can be passed to FText::Format.
*/
UCLASS(MinimalAPI)
class UlxFormatDataLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBool(bool Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataByte(uint8 Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataInt(int Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataInt64(int64 Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataFloat(float Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataDouble(double Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataText(FText Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataString(FString Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataName(FName Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataKey(FKey Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataGender(ETextGender Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataObject(UObject *Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataVector(const FVector &Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataVector2D(const FVector2D &Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataRotator(const FRotator &Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &Value, const FString &Name);
};
/* Debug Blueprint Errors output device.
*
* When an error message gets written to the log, using "Format Error Message,"

View File

@@ -0,0 +1,205 @@
#include "FormatDataLibrary.h"
#include "LuaCall.h"
#include "AnimQueue.h"
#include "MovementComponentState.h"
#include "Kismet/KismetTextLibrary.h"
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_BoolToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt64(int64 AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataFloat(float AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Float;
Result.ArgumentName = Name;
Result.ArgumentValueFloat = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataDouble(double AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Double;
Result.ArgumentName = Name;
Result.ArgumentValueDouble = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataText(FText AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataString(FString AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_StringToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataName(FName AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataKey(FKey AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(AutoConvertedValue.GetFName());
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGender(ETextGender AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Gender;
Result.ArgumentName = Name;
Result.ArgumentValueGender = AutoConvertedValue;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataObject(UObject *AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_ObjectToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector(const FVector &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_VectorToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector2D(const FVector2D &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_Vector2dToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataRotator(const FRotator &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_RotatorToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTransform &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = UKismetTextLibrary::Conv_TransformToText(AutoConvertedValue);
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(AutoConvertedValue->DebugString());
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(UlxAnimationStepLibrary::AnimationStepDebugString(AutoConvertedValue));
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(UlxMovementComponentStateLibrary::DebugString(AutoConvertedValue));
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBlank(const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText();
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject)
{
const UEnum *Enum = Cast<const UEnum>(PinSubCategoryObject);
FFormatArgumentData Result;
if (Enum == nullptr)
{
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
}
else
{
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value));
}
return Result;
}

View File

@@ -0,0 +1,104 @@
//
// FormatDataLibrary: Functions that convert data into FFormatArgumentData
// structs, so that the data can be passed to FText::Format.
//
#pragma once
#include "CoreMinimal.h"
#include "InputCoreTypes.h"
#include "HAL/Platform.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "FormatDataLibrary.generated.h"
class UlxLuaValues;
struct FlxAnimationStep;
struct FlxMovementComponentState;
/*
* A library that contains functions that convert data into FFormatArgumentData
* structs, so that the data can be passed to FTextFormatter::Format.
*
* The FormatLogMessage K2Node scans this library using reflection,
* looking for functions that have a parameter named "AutoConvertedValue".
* It uses the type of that parameter to determine which function to
* call for a given pin type. Functions without an "AutoConvertedValue"
* parameter are ignored by the reflection scan.
*
*/
UCLASS(MinimalAPI)
class UlxFormatDataLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataByte(uint8 AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataInt(int AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataInt64(int64 AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataFloat(float AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataDouble(double AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataText(FText AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataString(FString AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataName(FName AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataKey(FKey AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataGender(ETextGender AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataObject(UObject *AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataVector(const FVector &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataVector2D(const FVector2D &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataRotator(const FRotator &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name);
// A formatting routine for pins that were never connected.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBlank(const FString &Name);
// A specialized formatting routine for pins of enum types.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject);
};

View File

@@ -1,8 +1,9 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "FormatError.h"
#include "FormatMessage.h"
#include "Internationalization/TextFormatter.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "Containers/EnumAsByte.h"
@@ -301,7 +302,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
//
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && AllowWild)
{
return UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatArgumentDataBlank));
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataBlank));
}
// Try to find a match in the UlxFormatDataLibrary.
@@ -309,7 +310,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
for (auto It = TFieldIterator<UFunction>(UlxFormatDataLibrary::StaticClass()); It; ++It)
{
UFunction* Function = *It;
FProperty* ValueProperty = Function->FindPropertyByName(TEXT("Value"));
FProperty* ValueProperty = Function->FindPropertyByName(TEXT("AutoConvertedValue"));
FEdGraphPinType ValuePinType;
bool Convertible = Schema->ConvertPropertyToPinType(ValueProperty, ValuePinType);
if (!Convertible) continue;
@@ -322,7 +323,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
//
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (nullptr != Cast<const UEnum>(PinType.PinSubCategoryObject)))
{
return UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatArgumentDataEnum));
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataEnum));
}
// A case for subclasses of 'Object' which are not exactly 'Object'
@@ -359,7 +360,7 @@ void UK2Node_FormatMessage::ExpandNode(class FKismetCompilerContext& CompilerCon
UFunction *FormatFunction;
if (IsFormatErrorMessage())
{
FormatFunction = UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatErrorInternal));
FormatFunction = UK2Node_FormatMessage::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UK2Node_FormatMessage, FormatLogMessageInternal));
}
else
{
@@ -399,7 +400,7 @@ void UK2Node_FormatMessage::ExpandNode(class FKismetCompilerContext& CompilerCon
ConvertNode->SetFromFunction(Converter);
ConvertNode->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(ConvertNode, this);
UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("Value"));
UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("AutoConvertedValue"));
UEdGraphPin *NamePin = ConvertNode->FindPinChecked(TEXT("Name"));
UEdGraphPin *SubCategoryObjectPin = ConvertNode->FindPin(TEXT("PinSubCategoryObject"));
@@ -559,7 +560,7 @@ UK2Node_FormatMessage::UK2Node_FormatMessage(const FObjectInitializer& ObjectIni
);
}
UK2Node_FormatErrorMessage::UK2Node_FormatErrorMessage(const FObjectInitializer& ObjectInitializer)
UK2Node_FormatLogMessage::UK2Node_FormatLogMessage(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NodeTooltip = LOCTEXT("NodeTooltip",
@@ -572,4 +573,39 @@ UK2Node_FormatErrorMessage::UK2Node_FormatErrorMessage(const FObjectInitializer&
);
}
void UK2Node_FormatMessage::FormatLogMessageInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs)
{
// Generate the formatted string.
//
FText InPatternText(FText::FromString(InPattern));
FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false);
FString MessageString = Message.ToString();
// Get the blueprint name.
//
// Normally, the log function expects you to pass in a filename, and a log
// category name. We use the blueprint name for both.
//
// Using the blueprint name as a log category name is not technically
// correct. However, there is no correct way to create log categories
// from inside of blueprints. Doing it this way at least produces a reasonable
// message inside the log. What doesn't work correctly is the log message
// suppression system. Ie, console commands like 'log <category> verbose'
// don't have any effect here. The design of the log message suppression
// system is such that there just is no reasonable way to hook into it from
// inside of blueprints.
//
FString BlueprintNameString = Context->GetClass()->GetName();
auto BlueprintNameAnsi = StringCast<ANSICHAR>(*BlueprintNameString);
FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName());
// Output to Log
//
ELogVerbosity::Type VerbosityValue = UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(Verbosity);
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
{
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
}
}
#undef LOCTEXT_NAMESPACE

View File

@@ -3,6 +3,7 @@
#pragma once
#include "BlueprintErrors.h"
#include "FormatDataLibrary.h"
#include "Containers/Array.h"
#include "CoreMinimal.h"
#include "EdGraph/EdGraphNode.h"
@@ -15,7 +16,7 @@
#include "UObject/UObjectGlobals.h"
#include "BlueprintErrors.h"
#include "FormatError.generated.h"
#include "FormatMessage.generated.h"
class FBlueprintActionDatabaseRegistrar;
class FString;
@@ -77,6 +78,12 @@ protected:
/** Our derived class will set this to true, altering the behavior of this K2Node. **/
virtual bool IsFormatErrorMessage() const { return false; }
// When IsFormatErrorMessage is true, the K2Node macroexpands to call this
// function, which formats the message and outputs it to the log.
//
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
static void FormatLogMessageInternal(UObject *Context, ElxLogVerbosity Verbosity, const FString &InPattern, TArray<FFormatArgumentData> InArgs);
protected:
/** When adding arguments to the node, their names are placed here and are generated as pins during construction */
UPROPERTY()
@@ -91,7 +98,7 @@ protected:
// This derives from FormatMessage.
//
UCLASS(MinimalAPI)
class UK2Node_FormatErrorMessage : public UK2Node_FormatMessage
class UK2Node_FormatLogMessage : public UK2Node_FormatMessage
{
GENERATED_UCLASS_BODY()

View File

@@ -0,0 +1,11 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LxCharacterBase.h"
#include "GameFramework/CharacterMovementComponent.h"
void AlxCharacterBase::SetMovementComponentMode(EMovementMode MovementMode)
{
UCharacterMovementComponent *CMC = GetCharacterMovement();
if (CMC) CMC->SetMovementMode(MovementMode);
}

View File

@@ -0,0 +1,29 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MovementComponentState.h"
#include "LxCharacterBase.generated.h"
//////////////////////////////////////////////////////////////
//
// We provide an AlxCharacterBase for characters in Luprex.
//
//////////////////////////////////////////////////////////
UCLASS(BlueprintType)
class INTEGRATION_API AlxCharacterBase : public ACharacter
{
GENERATED_BODY()
public:
AlxCharacterBase() {}
// Set the movement component mode. This is a thin wrapper
// around CharacterMovementComponent::SetMovementMode.
//
UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State")
void SetMovementComponentMode(EMovementMode MovementMode = MOVE_None);
};

View File

@@ -0,0 +1,60 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "MovementComponentState.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Tangible.h"
FlxMovementComponentState::FlxMovementComponentState(UCharacterMovementComponent *CMC)
{
Velocity = CMC->Velocity;
bIsAccelerating = !CMC->GetCurrentAcceleration().IsNearlyZero();
MaxWalkSpeed = CMC->MaxWalkSpeed;
MovementMode = CMC->MovementMode;
bIsFalling = CMC->IsFalling();
bIsCrouching = CMC->IsCrouching();
}
FString UlxMovementComponentStateLibrary::DebugString(const FlxMovementComponentState &State)
{
const UEnum *ModeEnum = StaticEnum<EMovementMode>();
FString ModeName = ModeEnum ? ModeEnum->GetNameStringByValue(State.MovementMode.GetValue()) : FString::FromInt(State.MovementMode.GetValue());
ModeName.RemoveFromStart(TEXT("MOVE_"));
return FString::Printf(TEXT("Vel=(%.1f, %.1f, %.1f) MaxWalk=%.1f Mode=%s Accel=%s Fall=%s Crouch=%s"),
State.Velocity.X, State.Velocity.Y, State.Velocity.Z,
State.MaxWalkSpeed,
*ModeName,
State.bIsAccelerating ? TEXT("true") : TEXT("false"),
State.bIsFalling ? TEXT("true") : TEXT("false"),
State.bIsCrouching ? TEXT("true") : TEXT("false"));
}
bool UlxMovementComponentStateLibrary::GetShouldMove(const FlxMovementComponentState &State)
{
return State.bIsAccelerating && State.Velocity.Size2D() >= 3.0;
}
FlxMovementComponentState UlxMovementComponentStateLibrary::GetMovementComponentState(UAnimInstance *AnimInstance)
{
if (!AnimInstance) return FlxMovementComponentState();
AActor *Actor = AnimInstance->GetOwningActor();
if (!Actor) return FlxMovementComponentState();
UCharacterMovementComponent *CMC = Actor->FindComponentByClass<UCharacterMovementComponent>();
if (CMC && CMC->MovementMode != MOVE_None) return FlxMovementComponentState(CMC);
UlxTangible *Tangible = UlxTangible::GetActorTangibleQuiet(Actor);
if (Tangible) return Tangible->FakeMovementComponentState;
if (CMC) return FlxMovementComponentState(CMC);
return FlxMovementComponentState();
}
FlxMovementComponentState UlxMovementComponentStateLibrary::SetFakeMovementComponentState(AActor *Actor, const FlxMovementComponentState &State)
{
if (!Actor) return State;
UlxTangible *Tangible = UlxTangible::GetActorTangibleOrLog(Actor);
if (Tangible) Tangible->FakeMovementComponentState = State;
return State;
}

View File

@@ -0,0 +1,129 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MovementComponentState.generated.h"
//////////////////////////////////////////////////////////////
//
// Very often, the animation blueprint of a character will
// want to know the state of the character movement
// component, including such things as Velocity,
// Acceleration, IsFalling, etc. However, the movement
// component cannot be accessed directly from the
// animation graph, since it runs on a worker thread.
// By copying the data into this plain struct, the
// animation graph can safely read it.
//
//////////////////////////////////////////////////////////////
USTRUCT(BlueprintType)
struct INTEGRATION_API FlxMovementComponentState
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
FVector Velocity = FVector::ZeroVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
bool bIsAccelerating = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
float MaxWalkSpeed = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
TEnumAsByte<EMovementMode> MovementMode = MOVE_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
bool bIsFalling = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Movement Component State")
bool bIsCrouching = false;
FlxMovementComponentState() = default;
explicit FlxMovementComponentState(class UCharacterMovementComponent *CMC);
};
UCLASS()
class INTEGRATION_API UlxMovementComponentStateLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
///////////////////////////////////////////////////////////
//
// True field getters.
//
///////////////////////////////////////////////////////////
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static FVector GetVelocity(const FlxMovementComponentState &State) { return State.Velocity; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static bool GetIsAccelerating(const FlxMovementComponentState &State) { return State.bIsAccelerating; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static float GetMaxWalkSpeed(const FlxMovementComponentState &State) { return State.MaxWalkSpeed; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static EMovementMode GetMovementMode(const FlxMovementComponentState &State) { return State.MovementMode; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static bool GetIsFalling(const FlxMovementComponentState &State) { return State.bIsFalling; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static bool GetIsCrouching(const FlxMovementComponentState &State) { return State.bIsCrouching; }
///////////////////////////////////////////////////////////
//
// These are not true getters, but actually calculate
// simple values from the existing fields.
//
///////////////////////////////////////////////////////////
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static float GetGroundSpeed(const FlxMovementComponentState &State) { return State.Velocity.Size2D(); }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State", meta = (BlueprintThreadSafe))
static bool GetShouldMove(const FlxMovementComponentState &State);
///////////////////////////////////////////////////////////
//
// Debugging.
//
///////////////////////////////////////////////////////////
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Movement Component State")
static FString DebugString(const FlxMovementComponentState &State);
///////////////////////////////////////////////////////////
//
// Other operations.
//
///////////////////////////////////////////////////////////
// Get a snapshot the Movement Component State.
//
// Normally, this just pulls data directly from the
// character movement component. However, we sometimes
// need to disable the movement component, especially
// during cutscenes. We provide an alternative 'Fake
// Movement Component State' stored on the actor's
// UlxTangible. When the movement component's mode is
// MOVE_None, this function returns the fake movement
// component state instead. The fake movement component
// state can be updated manually in blueprints.
//
UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (DefaultToSelf = "AnimInstance"))
static FlxMovementComponentState GetMovementComponentState(UAnimInstance *AnimInstance);
// Update the fake movement component state stored on
// the actor's tangible. The fake movement component
// state is usually read by the animation blueprint
// when the real movement component is disabled.
//
UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (AutoCreateRefTerm = "State"))
static FlxMovementComponentState SetFakeMovementComponentState(AActor *Actor, const FlxMovementComponentState &State);
};

View File

@@ -234,7 +234,7 @@ class INTEGRATION_API UlxScriptedAnimationLibrary : public UBlueprintFunctionLib
public:
// Get all the major 'World Clocks' in a single struct.
//
UFUNCTION(BlueprintPure, Category = "Utilities|Time", meta=(WorldContext = "WorldContextObject"))
UFUNCTION(BlueprintCallable, Category = "Utilities|Time", meta=(WorldContext = "WorldContextObject"))
static FlxWorldClocks GetAllWorldClocks(const UObject *WorldContextObject);
// Get the data to drive Sequence Evaluators and Multi Blend

View File

@@ -5,6 +5,7 @@
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "AnimQueue.h"
#include "MovementComponentState.h"
#include "ScriptedAnimation.h"
#include "Tangible.generated.h"
@@ -55,10 +56,14 @@ public:
UPROPERTY()
FString ActorBlueprintName;
// This is
// Every tangible can store a set of scripted animations.
UPROPERTY()
UlxScriptedAnimations *ScriptedAnimations = nullptr;
// Every tangible can store a fake movement component state.
UPROPERTY()
FlxMovementComponentState FakeMovementComponentState;
// Animation tracker
FlxAnimTracker AnimTracker;

View File

@@ -239,3 +239,15 @@ FKey UlxUtilityLibrary::GetKeyByNameString(const FString &Name)
FKey Key = FKey(FName(*Name));
return Key.IsValid() ? Key : FKey();
}
FVector UlxUtilityLibrary::GetActorForwardVelocity(const AActor *Actor, double Speed, bool bSnapToXY)
{
if (!Actor) return FVector::ZeroVector;
FVector Forward = Actor->GetActorForwardVector();
if (bSnapToXY)
{
Forward.Z = 0.0;
if (!Forward.Normalize()) return FVector::ZeroVector;
}
return Forward * Speed;
}

View File

@@ -158,4 +158,11 @@ public:
//
UFUNCTION(BlueprintPure, Category = "Input|Key")
static FKey GetKeyByNameString(const FString &Name);
// Get the actor's forward vector multiplied by a speed.
// If SnapToXY is true, the forward vector is projected
// onto the XY plane and renormalized before scaling.
//
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Utility", meta = (DefaultToSelf = "Actor"))
static FVector GetActorForwardVelocity(const AActor *Actor, double Speed = 1.0, bool bSnapToXY = false);
};