Unreal Engine 的 Actor class 的 OnConstruction() 方法未正确执行
OnConstruction() method of an Unreal Engine's Actor class is not correctly executed
我正在用 C++ 编写 ALadder class,灵感来自 Alan Noon 的教程 https://www.youtube.com/watch?v=axuwbkDWI5U。
目前class很简单:
Ladder.h
#pragma once
#include "GameFramework/Actor.h"
#include "Ladder.generated.h"
class UPaperSprite;
UCLASS()
class ALadder : public AActor
{
GENERATED_BODY()
public:
ALadder();
virtual void OnConstruction(const FTransform& Transform) override;
protected:
UPROPERTY(EditAnywhere, Meta = (MakeEditWidget = true))
FVector TopVector{FVector(0.0f, 0.0f, 100.0f)};
UPROPERTY(EditAnywhere, Meta = (MakeEditWidget = true))
FVector BottomVector{FVector(0.0f, 0.0f, 0.0f)};
UPROPERTY(EditAnywhere)
float RungOffset{ 10.0f };
UPROPERTY(EditAnywhere)
UPaperSprite* RungSprite {};
private:
};
Ladder.cpp
#include "Actors/Ladder.h"
#include "PaperSprite.h"
#include "PaperSpriteComponent.h"
#include "UObject/UObjectIterator.h"
ALadder::ALadder()
{
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootSceneComponent"));
}
void ALadder::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
BottomVector = FVector(0.0f);
TopVector.X = 0.0f;
TopVector.Y = 0.0f;
TArray<UActorComponent*> SpriteComponents = GetComponentsByClass(UPaperSpriteComponent::StaticClass());
// Remove all components
for (auto It = SpriteComponents.CreateIterator(); It; It++)
{
(*It)->DestroyComponent();
}
UPaperSpriteComponent* SpriteComponent = nullptr;
float Diff_Z = (TopVector - BottomVector).Z;
if (Diff_Z < 0.0f)
{
TopVector = BottomVector;
}
int32 RungsNumber = (int)(Diff_Z / RungOffset);
// Add components
for (int32 i = 0; i <= RungsNumber; ++i)
{
SpriteComponent = NewObject<UPaperSpriteComponent>(this);
FAttachmentTransformRules AttachmentRules(EAttachmentRule::KeepRelative, false);
SpriteComponent->AttachToComponent(RootComponent, AttachmentRules);
SpriteComponent->SetRelativeLocation(FVector(0.0f, 0.0f, RungOffset * i));
SpriteComponent->SetSprite(RungSprite);
SpriteComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
当我尝试通过编辑器中的小部件修改 TopVector 时,梯级没有出现,但是当我尝试通过“详细信息”面板修改 TopVector 默认值时,它可以正常工作。
我注意到当我使用 TopVector 小部件时, ALadder::OnConstruction() 被调用了两次;这是两个不同的调用堆栈:
第一个调用堆栈:
> UE4Editor-platformer-6449.dll!ALadder::OnConstruction(const FTransform & Transform) Line 13 C++
UE4Editor-Engine.dll!AActor::ExecuteConstruction(const FTransform & Transform, const FRotationConversionCache * TransformRotationCache, const FComponentInstanceDataCache * InstanceDataCache, bool bIsDefaultTransform) Line 839 C++
UE4Editor-Engine.dll!AActor::RerunConstructionScripts() Line 494 C++
UE4Editor-Engine.dll!AActor::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) Line 125 C++
UE4Editor-UnrealEd.dll!FEdMode::InputDelta(FEditorViewportClient * InViewportClient, FViewport * InViewport, FVector & InDrag, FRotator & InRot, FVector & InScale) Line 378 C++
UE4Editor-PlacementMode.dll!FPlacementMode::InputDelta(FEditorViewportClient * InViewportClient, FViewport * InViewport, FVector & InDrag, FRotator & InRot, FVector & InScale) Line 437 C++
UE4Editor-UnrealEd.dll!FEditorModeTools::InputDelta(FEditorViewportClient * InViewportClient, FViewport * InViewport, FVector & InDrag, FRotator & InRot, FVector & InScale) Line 729 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::InputWidgetDelta(FViewport * InViewport, EAxisList::Type CurrentAxis, FVector & Drag, FRotator & Rot, FVector & Scale) Line 3283 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::InputWidgetDelta(FViewport * InViewport, EAxisList::Type InCurrentAxis, FVector & Drag, FRotator & Rot, FVector & Scale) Line 2383 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::UpdateMouseDelta() Line 2130 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::Tick(float DeltaTime) Line 1180 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::Tick(float DeltaTime) Line 2138 C++
UE4Editor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1800 C++
UE4Editor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 403 C++
UE4Editor.exe!FEngineLoop::Tick() Line 3699 C++
[Inline Frame] UE4Editor.exe!EngineTick() Line 62 C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 174 C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 262 C++
[External Code]
第二个调用堆栈:
> UE4Editor-platformer.dll!ALadder::OnConstruction(const FTransform & Transform) Line 13 C++
UE4Editor-Engine.dll!AActor::ExecuteConstruction(const FTransform & Transform, const FRotationConversionCache * TransformRotationCache, const FComponentInstanceDataCache * InstanceDataCache, bool bIsDefaultTransform) Line 839 C++
UE4Editor-Engine.dll!AActor::RerunConstructionScripts() Line 494 C++
UE4Editor-Engine.dll!AActor::PostEditMove(bool bFinished) Line 147 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::TrackingStopped() Line 2910 C++
UE4Editor-UnrealEd.dll!FMouseDeltaTracker::EndTracking(FEditorViewportClient * InViewportClient) Line 306 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::StopTracking() Line 2655 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::InputKey(FViewport * InViewport, int ControllerId, FKey Key, EInputEvent Event, float __formal, bool __formal) Line 2590 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::InputKey(FViewport * InViewport, int ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool bGamepad) Line 2607 C++
UE4Editor-Engine.dll!FSceneViewport::OnMouseButtonUp(const FGeometry & InGeometry, const FPointerEvent & InMouseEvent) Line 566 C++
UE4Editor-Slate.dll!SViewport::OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MouseEvent) Line 202 C++
[Inline Frame] UE4Editor-Slate.dll!FSlateApplication::RoutePointerUpEvent::__l13::<lambda_8aae5586f97da0914c7f98177fd6d3e0>::operator()(const FArrangedWidget &) Line 5592 C++
UE4Editor-Slate.dll!FEventRouter::Route<FReply,FEventRouter::FBubblePolicy,FPointerEvent,<lambda_8aae5586f97da0914c7f98177fd6d3e0> >(FSlateApplication * ThisApplication, FEventRouter::FBubblePolicy RoutingPolicy, FPointerEvent EventCopy, const FSlateApplication::RoutePointerUpEvent::__l13::<lambda_8aae5586f97da0914c7f98177fd6d3e0> & Lambda) Line 270 C++
UE4Editor-Slate.dll!FSlateApplication::RoutePointerUpEvent(FWidgetPath & WidgetsUnderPointer, FPointerEvent & PointerEvent) Line 5576 C++
UE4Editor-Slate.dll!FSlateApplication::ProcessMouseButtonUpEvent(FPointerEvent & MouseEvent) Line 6087 C++
UE4Editor-Slate.dll!FSlateApplication::OnMouseUp(const EMouseButtons::Type Button, const FVector2D CursorPos) Line 6060 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::ProcessDeferredMessage(const FDeferredWindowsMessage & DeferredMessage) Line 1831 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::DeferMessage(TSharedPtr<FWindowsWindow,0> & NativeWindow, HWND__ * InHWnd, unsigned int InMessage, unsigned __int64 InWParam, __int64 InLParam, int MouseX, int MouseY, unsigned int RawInputFlags) Line 2281 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::ProcessMessage(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 1511 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::AppWndProc(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 766 C++
[External Code]
[Inline Frame] UE4Editor-ApplicationCore.dll!WinPumpMessages() Line 107 C++
UE4Editor-ApplicationCore.dll!FWindowsPlatformApplicationMisc::PumpMessages(bool bFromMainLoop) Line 130 C++
UE4Editor.exe!FEngineLoop::Tick() Line 3615 C++
[Inline Frame] UE4Editor.exe!EngineTick() Line 62 C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 174 C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 262 C++
[External Code]
也许我找到了解决方案,但我并不真正理解它为什么有效。在我创建一个新组件后,我调用了 UActorComponent::RegisterComponent() 方法,它解决了这个问题。有谁知道它为什么有效?奇怪的是,以前它只有在我通过详细信息面板修改 TopVector 时才能正常工作。
OnConstruction() 函数旨在重新初始化,专门用于每次在编辑器中更改 UPROPERTY 时重建的用例。我相信您的代码最终具有与 UE4 CreateDefaultSubobject 和 SetupAttachment 函数类似的功能。 "correct" 方法是在构造函数 ALadder::ALadder() 中生成组件,类似于本教程:https://docs.unrealengine.com/en-us/Programming/Tutorials/Components/1
RegisterComponent 是一种 fix-up 函数,在组件创建过程接近尾声时触发。修改值起作用的原因是因为该操作再次触发了 OnConstruction() 函数。
我正在用 C++ 编写 ALadder class,灵感来自 Alan Noon 的教程 https://www.youtube.com/watch?v=axuwbkDWI5U。 目前class很简单:
Ladder.h
#pragma once
#include "GameFramework/Actor.h"
#include "Ladder.generated.h"
class UPaperSprite;
UCLASS()
class ALadder : public AActor
{
GENERATED_BODY()
public:
ALadder();
virtual void OnConstruction(const FTransform& Transform) override;
protected:
UPROPERTY(EditAnywhere, Meta = (MakeEditWidget = true))
FVector TopVector{FVector(0.0f, 0.0f, 100.0f)};
UPROPERTY(EditAnywhere, Meta = (MakeEditWidget = true))
FVector BottomVector{FVector(0.0f, 0.0f, 0.0f)};
UPROPERTY(EditAnywhere)
float RungOffset{ 10.0f };
UPROPERTY(EditAnywhere)
UPaperSprite* RungSprite {};
private:
};
Ladder.cpp
#include "Actors/Ladder.h"
#include "PaperSprite.h"
#include "PaperSpriteComponent.h"
#include "UObject/UObjectIterator.h"
ALadder::ALadder()
{
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootSceneComponent"));
}
void ALadder::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
BottomVector = FVector(0.0f);
TopVector.X = 0.0f;
TopVector.Y = 0.0f;
TArray<UActorComponent*> SpriteComponents = GetComponentsByClass(UPaperSpriteComponent::StaticClass());
// Remove all components
for (auto It = SpriteComponents.CreateIterator(); It; It++)
{
(*It)->DestroyComponent();
}
UPaperSpriteComponent* SpriteComponent = nullptr;
float Diff_Z = (TopVector - BottomVector).Z;
if (Diff_Z < 0.0f)
{
TopVector = BottomVector;
}
int32 RungsNumber = (int)(Diff_Z / RungOffset);
// Add components
for (int32 i = 0; i <= RungsNumber; ++i)
{
SpriteComponent = NewObject<UPaperSpriteComponent>(this);
FAttachmentTransformRules AttachmentRules(EAttachmentRule::KeepRelative, false);
SpriteComponent->AttachToComponent(RootComponent, AttachmentRules);
SpriteComponent->SetRelativeLocation(FVector(0.0f, 0.0f, RungOffset * i));
SpriteComponent->SetSprite(RungSprite);
SpriteComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
当我尝试通过编辑器中的小部件修改 TopVector 时,梯级没有出现,但是当我尝试通过“详细信息”面板修改 TopVector 默认值时,它可以正常工作。 我注意到当我使用 TopVector 小部件时, ALadder::OnConstruction() 被调用了两次;这是两个不同的调用堆栈:
第一个调用堆栈:
> UE4Editor-platformer-6449.dll!ALadder::OnConstruction(const FTransform & Transform) Line 13 C++
UE4Editor-Engine.dll!AActor::ExecuteConstruction(const FTransform & Transform, const FRotationConversionCache * TransformRotationCache, const FComponentInstanceDataCache * InstanceDataCache, bool bIsDefaultTransform) Line 839 C++
UE4Editor-Engine.dll!AActor::RerunConstructionScripts() Line 494 C++
UE4Editor-Engine.dll!AActor::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) Line 125 C++
UE4Editor-UnrealEd.dll!FEdMode::InputDelta(FEditorViewportClient * InViewportClient, FViewport * InViewport, FVector & InDrag, FRotator & InRot, FVector & InScale) Line 378 C++
UE4Editor-PlacementMode.dll!FPlacementMode::InputDelta(FEditorViewportClient * InViewportClient, FViewport * InViewport, FVector & InDrag, FRotator & InRot, FVector & InScale) Line 437 C++
UE4Editor-UnrealEd.dll!FEditorModeTools::InputDelta(FEditorViewportClient * InViewportClient, FViewport * InViewport, FVector & InDrag, FRotator & InRot, FVector & InScale) Line 729 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::InputWidgetDelta(FViewport * InViewport, EAxisList::Type CurrentAxis, FVector & Drag, FRotator & Rot, FVector & Scale) Line 3283 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::InputWidgetDelta(FViewport * InViewport, EAxisList::Type InCurrentAxis, FVector & Drag, FRotator & Rot, FVector & Scale) Line 2383 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::UpdateMouseDelta() Line 2130 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::Tick(float DeltaTime) Line 1180 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::Tick(float DeltaTime) Line 2138 C++
UE4Editor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1800 C++
UE4Editor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 403 C++
UE4Editor.exe!FEngineLoop::Tick() Line 3699 C++
[Inline Frame] UE4Editor.exe!EngineTick() Line 62 C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 174 C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 262 C++
[External Code]
第二个调用堆栈:
> UE4Editor-platformer.dll!ALadder::OnConstruction(const FTransform & Transform) Line 13 C++
UE4Editor-Engine.dll!AActor::ExecuteConstruction(const FTransform & Transform, const FRotationConversionCache * TransformRotationCache, const FComponentInstanceDataCache * InstanceDataCache, bool bIsDefaultTransform) Line 839 C++
UE4Editor-Engine.dll!AActor::RerunConstructionScripts() Line 494 C++
UE4Editor-Engine.dll!AActor::PostEditMove(bool bFinished) Line 147 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::TrackingStopped() Line 2910 C++
UE4Editor-UnrealEd.dll!FMouseDeltaTracker::EndTracking(FEditorViewportClient * InViewportClient) Line 306 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::StopTracking() Line 2655 C++
UE4Editor-UnrealEd.dll!FEditorViewportClient::InputKey(FViewport * InViewport, int ControllerId, FKey Key, EInputEvent Event, float __formal, bool __formal) Line 2590 C++
UE4Editor-UnrealEd.dll!FLevelEditorViewportClient::InputKey(FViewport * InViewport, int ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool bGamepad) Line 2607 C++
UE4Editor-Engine.dll!FSceneViewport::OnMouseButtonUp(const FGeometry & InGeometry, const FPointerEvent & InMouseEvent) Line 566 C++
UE4Editor-Slate.dll!SViewport::OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MouseEvent) Line 202 C++
[Inline Frame] UE4Editor-Slate.dll!FSlateApplication::RoutePointerUpEvent::__l13::<lambda_8aae5586f97da0914c7f98177fd6d3e0>::operator()(const FArrangedWidget &) Line 5592 C++
UE4Editor-Slate.dll!FEventRouter::Route<FReply,FEventRouter::FBubblePolicy,FPointerEvent,<lambda_8aae5586f97da0914c7f98177fd6d3e0> >(FSlateApplication * ThisApplication, FEventRouter::FBubblePolicy RoutingPolicy, FPointerEvent EventCopy, const FSlateApplication::RoutePointerUpEvent::__l13::<lambda_8aae5586f97da0914c7f98177fd6d3e0> & Lambda) Line 270 C++
UE4Editor-Slate.dll!FSlateApplication::RoutePointerUpEvent(FWidgetPath & WidgetsUnderPointer, FPointerEvent & PointerEvent) Line 5576 C++
UE4Editor-Slate.dll!FSlateApplication::ProcessMouseButtonUpEvent(FPointerEvent & MouseEvent) Line 6087 C++
UE4Editor-Slate.dll!FSlateApplication::OnMouseUp(const EMouseButtons::Type Button, const FVector2D CursorPos) Line 6060 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::ProcessDeferredMessage(const FDeferredWindowsMessage & DeferredMessage) Line 1831 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::DeferMessage(TSharedPtr<FWindowsWindow,0> & NativeWindow, HWND__ * InHWnd, unsigned int InMessage, unsigned __int64 InWParam, __int64 InLParam, int MouseX, int MouseY, unsigned int RawInputFlags) Line 2281 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::ProcessMessage(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 1511 C++
UE4Editor-ApplicationCore.dll!FWindowsApplication::AppWndProc(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 766 C++
[External Code]
[Inline Frame] UE4Editor-ApplicationCore.dll!WinPumpMessages() Line 107 C++
UE4Editor-ApplicationCore.dll!FWindowsPlatformApplicationMisc::PumpMessages(bool bFromMainLoop) Line 130 C++
UE4Editor.exe!FEngineLoop::Tick() Line 3615 C++
[Inline Frame] UE4Editor.exe!EngineTick() Line 62 C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 174 C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 262 C++
[External Code]
也许我找到了解决方案,但我并不真正理解它为什么有效。在我创建一个新组件后,我调用了 UActorComponent::RegisterComponent() 方法,它解决了这个问题。有谁知道它为什么有效?奇怪的是,以前它只有在我通过详细信息面板修改 TopVector 时才能正常工作。
OnConstruction() 函数旨在重新初始化,专门用于每次在编辑器中更改 UPROPERTY 时重建的用例。我相信您的代码最终具有与 UE4 CreateDefaultSubobject 和 SetupAttachment 函数类似的功能。 "correct" 方法是在构造函数 ALadder::ALadder() 中生成组件,类似于本教程:https://docs.unrealengine.com/en-us/Programming/Tutorials/Components/1
RegisterComponent 是一种 fix-up 函数,在组件创建过程接近尾声时触发。修改值起作用的原因是因为该操作再次触发了 OnConstruction() 函数。