5.8 KiB
Raw Blame History

ChildCanTick

  • 功能描述: 标记允许其蓝图子类可以接受响应Tick事件
  • 使用位置: UCLASS
  • 引擎模块: Actor
  • 元数据类型: bool
  • 限制类型: Actor或ActorComponent子类
  • 关联项: ChildCannotTick
  • 常用程度: ★★★

要在蓝图中重载Tick事件函数并只会在编译的时候触发判断。

//(BlueprintType = true, ChildCannotTick = , IncludePath = Class/Blueprint/MyActor_ChildTick.h, IsBlueprintBase = true, ModuleRelativePath = Class/Blueprint/MyActor_ChildTick.h)
UCLASS(Blueprintable,meta=(ChildCanTick))
class INSIDER_API AMyActor_ChildCanTick : public AActor
{
	GENERATED_BODY()
public:
	AMyActor_ChildCanTick()
	{
		PrimaryActorTick.bCanEverTick = false;
	}
};

//(BlueprintType = true, ChildCanTick = , IncludePath = Class/Blueprint/MyActor_ChildTick.h, IsBlueprintBase = true, ModuleRelativePath = Class/Blueprint/MyActor_ChildTick.h)
UCLASS(Blueprintable,meta=(ChildCanTick))
class INSIDER_API UMyActorComponent_ChildCanTick : public UActorComponent
{
	GENERATED_BODY()
public:
};

//(BlueprintType = true, ChildCannotTick = , IncludePath = Class/Blueprint/MyActor_ChildTick.h, IsBlueprintBase = true, ModuleRelativePath = Class/Blueprint/MyActor_ChildTick.h)
UCLASS(Blueprintable,meta=(ChildCannotTick))
class INSIDER_API AMyActor_ChildCannotTick : public AActor
{
	GENERATED_BODY()
public:
};

//(BlueprintType = true, ChildCannotTick = , IncludePath = Class/Blueprint/MyActor_ChildTick.h, IsBlueprintBase = true, ModuleRelativePath = Class/Blueprint/MyActor_ChildTick.h)
UCLASS(Blueprintable,meta=(ChildCannotTick))
class INSIDER_API UMyActorComponent_ChildCannotTick : public UActorComponent
{
	GENERATED_BODY()
public:
};

蓝图Actor或ActorComponent里测试

也注意到这个判断跟蓝图中是否开启Tick并没有关系。

Untitled

Untitled

而AMyActor_ChildCanTick类里虽然已经手动关闭了PrimaryActorTick.bCanEverTick但是在子类里依然可以正常的Tick在编译的时候内部可以正常的再重新开启bCanEverTick

Untitled

源码里判断的逻辑:

开启bCanEverTick=true的条件有3一是EngineSettings->bCanBlueprintsTickByDefault二是父类是AActor或UActorComponent本身三是C++基类上有ChildCanTick的标记。

void FKismetCompilerContext::SetCanEverTick() const
{
// RECEIVE TICK
if (!TickFunction->bCanEverTick)
{
	// Make sure that both AActor and UActorComponent have the same name for their tick method
	static FName ReceiveTickName(GET_FUNCTION_NAME_CHECKED(AActor, ReceiveTick));
	static FName ComponentReceiveTickName(GET_FUNCTION_NAME_CHECKED(UActorComponent, ReceiveTick));

	if (const UFunction* ReceiveTickEvent = FKismetCompilerUtilities::FindOverriddenImplementableEvent(ReceiveTickName, NewClass))
	{
		// We have a tick node, but are we allowed to?

		const UEngine* EngineSettings = GetDefault<UEngine>();
		const bool bAllowTickingByDefault = EngineSettings->bCanBlueprintsTickByDefault;

		const UClass* FirstNativeClass = FBlueprintEditorUtils::FindFirstNativeClass(NewClass);
		const bool bHasCanTickMetadata = (FirstNativeClass != nullptr) && FirstNativeClass->HasMetaData(FBlueprintMetadata::MD_ChildCanTick);
		const bool bHasCannotTickMetadata = (FirstNativeClass != nullptr) && FirstNativeClass->HasMetaData(FBlueprintMetadata::MD_ChildCannotTick);
		const bool bHasUniversalParent = (FirstNativeClass != nullptr) && ((AActor::StaticClass() == FirstNativeClass) || (UActorComponent::StaticClass() == FirstNativeClass));

		if (bHasCanTickMetadata && bHasCannotTickMetadata)
		{
			// User error: The C++ class has conflicting metadata
			const FString ConlictingMetadataWarning = FText::Format(
				LOCTEXT("HasBothCanAndCannotMetadataFmt", "Native class %s has both '{0}' and '{1}' metadata specified, they are mutually exclusive and '{1}' will win."),
				FText::FromString(FirstNativeClass->GetPathName()),
				FText::FromName(FBlueprintMetadata::MD_ChildCanTick),
				FText::FromName(FBlueprintMetadata::MD_ChildCannotTick)
			).ToString();
			MessageLog.Warning(*ConlictingMetadataWarning);
		}

		if (bHasCannotTickMetadata)
		{
			// This could only happen if someone adds bad metadata to AActor or UActorComponent directly
			check(!bHasUniversalParent);

			// Parent class has forbidden us to tick
			const FString NativeClassSaidNo = FText::Format(
				LOCTEXT("NativeClassProhibitsTickingFmt", "@@ is not allowed as the C++ parent class {0} has disallowed Blueprint subclasses from ticking.  Please consider using a Timer instead of Tick."),
				FText::FromString(FirstNativeClass->GetPathName())
			).ToString();
			MessageLog.Warning(*NativeClassSaidNo, FindLocalEntryPoint(ReceiveTickEvent));
		}
		else
		{
			if (bAllowTickingByDefault || bHasUniversalParent || bHasCanTickMetadata)
			{
				// We're allowed to tick for one reason or another
				TickFunction->bCanEverTick = true;
			}
			else
			{
				// Nothing allowing us to tick
				const FString ReceiveTickEventWarning = FText::Format(
					LOCTEXT("ReceiveTick_CanNeverTickFmt", "@@ is not allowed for Blueprints based on the C++ parent class {0}, so it will never Tick!"),
					FText::FromString(FirstNativeClass ? *FirstNativeClass->GetPathName() : TEXT("<null>"))
				).ToString();
				MessageLog.Warning(*ReceiveTickEventWarning, FindLocalEntryPoint(ReceiveTickEvent));

				const FString ReceiveTickEventRemedies = FText::Format(
					LOCTEXT("ReceiveTick_CanNeverTickRemediesFmt", "You can solve this in several ways:\n  1) Consider using a Timer instead of Tick.\n  2) Add meta=({0}) to the parent C++ class\n  3) Reparent the Blueprint to AActor or UActorComponent, which can always tick."),
					FText::FromName(FBlueprintMetadata::MD_ChildCanTick)
				).ToString();
				MessageLog.Warning(*ReceiveTickEventRemedies);
			}
		}
	}
}
}