Language:
Page Info
Engine Version:
Share

2. 将变量和函数公开到编辑器

  1. 倒计时定时器当前被硬编码为使用3秒的值。在编辑器中将倒计时设为任意数值十分实用,也很容易实现。在 Visual Studio 中,我们打开Countdown.h可以找到拥有以下代码的行:

    int32 CountdownTime;

    为将此变量公开到 虚幻引擎,我们需要将其设为 UPROPERTY。这是的引擎能在启动游戏或加载保存关卡时保存变量的值。含空括号的UPROPERTY标签将被添加到其所影响变量的正上方:

    UPROPERTY()
    int32 CountdownTime;

    UPROPERTY支持多项参数,其将修改 虚幻引擎 对变量的使用方式。我们需要将变量设为可编辑,因此可以添加 EditAnywhere 参数:

    UPROPERTY(EditAnywhere)
    int32 CountdownTime;

    ExposingVariable.png

    还可在C++中为变量添加一个注释。这个注释将成为 虚幻编辑器 中变量的描述,如下所示:

    // 倒计时运行时长(以秒计)
    UPROPERTY(EditAnywhere)
    int32 CountdownTime;

    CommentingVariable.png

    对UPROPERTY还可进行更多操作,之后可继续研究其它参数,如 BlueprintReadWriteCategory。但目前暂时不需要操作。

    返回 虚幻编辑器 并按下 编译(Compile),变量将出现在之前放置的ACountdown的 细节面板 中。修改此数字并按下 运行(Play) 按钮即可对不同的定时器数值进行测试。

  2. 除修改定时器数值外,还可使非编程开发者能够改变倒计时结束后发生的情况。在 Visual Studio 中打开Countdown.h并找到以下行:

    void CountdownHasFinished();

    将此函数以如下方式设为一个 UFUNCTION,即可将其对 虚幻引擎 公开:

    UFUNCTION()
    void CountdownHasFinished();

    和UPROPERTY宏一样,我们需要提供可使用它进行何种操作的信息,以便为非编程开发者启用更多功能和访问权。可以考虑3个选项:

    1. BlueprintCallable 函数在C++中编写,可从 蓝图图表 中进行调用,但不编辑C++代码便无法对其进行修改或覆盖。以此方式标记的函数通常是已编程、以便非编程人士使用的功能,但不应对其进行修改(或者说完全无需对其进行修改)。任意类型的数学函数便是这个的简单实例。

    2. BlueprintImplementableEvent 函数在C++标头(.h)文件中设置,但函数主体整体在蓝图图表中编写,而非C++。它们被创建出来后,非编程人员即可创建针对特殊情况(其无预期默认操作或标准行为)的自定义响应。举例而言,其为太空船游戏中升级道具与玩家船只接触时发生的事件。

    3. BlueprintNativeEvent 函数则是BlueprintCallable和BlueprintImplementableEvent函数的组合。其默认行为在C++中被编入程序,但在蓝图图表中进行覆盖后即可对其进行补充或替换。如下所示,对其进行编程时,C++代码固定进入一个命名末尾添加有_Implementation的虚拟函数。这是灵活性最强的选项,因此我们将其用于此教程中。

    要使非编程人员能够调用C++函数并使用 蓝图 对其进行覆盖,须对Countdown.h进行以下修改:

    UFUNCTION(BlueprintNativeEvent)
    void CountdownHasFinished();
    virtual void CountdownHasFinished_Implementation();

    然后我们需要在 Countdown.cpp 中将拥有以下代码的行:

    void ACountdown::CountdownHasFinished()

    改为:

    void ACountdown::CountdownHasFinished_Implementation()

现在我们设置好了非编程人员可访问并调整的变量和函数,同时在C++中提供了默认值和功能。为了解非编程人员能够如何进行使用,我们将设置一个ACountdown类的蓝图延展并自行进行修改。

完成的代码

Countdown.h

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Actor.h"
#include "Countdown.generated.h"

UCLASS()
class HOWTO_VTE_API ACountdown : public AActor
{
    GENERATED_BODY()

public: 
    // 设置该Actor属性的默认值
    ACountdown();

protected:
    // 游戏开始时或生成时调用
    virtual void BeginPlay() override;

public:
    // 每帧调用
    virtual void Tick( float DeltaSeconds ) override;

    // 倒计时运行时长(以秒计)
    UPROPERTY(EditAnywhere)
    int32 CountdownTime;

    UTextRenderComponent* CountdownText;

    void UpdateTimerDisplay();

    void AdvanceTimer();

    UFUNCTION(BlueprintNativeEvent)
    void CountdownHasFinished();
    virtual void CountdownHasFinished_Implementation();

    FTimerHandle CountdownTimerHandle;
};

Countdown.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "HowTo_VTE.h"
#include "Countdown.h"

// 设置默认值
ACountdown::ACountdown()
{
    // 设置此Actor每帧调用Tick()。如果不需要,则可将其关闭来改善性能。
    PrimaryActorTick.bCanEverTick = false;

    CountdownText = CreateDefaultSubobject<UTextRenderComponent>(TEXT("CountdownNumber"));
    CountdownText->SetHorizontalAlignment(EHTA_Center);
    CountdownText->SetWorldSize(150.0f);
    RootComponent = CountdownText;

    CountdownTime = 3;
}

// 游戏开始时或生成时调用
void ACountdown::BeginPlay()
{
    Super::BeginPlay();

    UpdateTimerDisplay();
    GetWorldTimerManager().SetTimer(CountdownTimerHandle, this, &ACountdown::AdvanceTimer, 1.0f, true);
}

// 每帧调用
void ACountdown::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

}

void ACountdown::UpdateTimerDisplay()
{
    CountdownText->SetText(FString::FromInt(FMath::Max(CountdownTime, 0)));
}

void ACountdown::AdvanceTimer()
{
    --CountdownTime;
    UpdateTimerDisplay();
    if (CountdownTime < 1)
    {
        // 倒计时结束,停止运行定时器。
        GetWorldTimerManager().ClearTimer(CountdownTimerHandle);
        //在定时器结束时按执行所需的特殊操作。
        CountdownHasFinished();
    }
}

void ACountdown::CountdownHasFinished_Implementation()
{
    // 改为一个特殊的读出
    CountdownText->SetText(TEXT("GO!"));
}