Language:
Page Info
Engine Version:
Share

3. 用蓝图延展和覆盖C++

教程的该部分说明使用 蓝图 来延展C++类的功能。然而其只用于测试C++代码是否正确编写,而非蓝图教程。如需参考蓝图介绍,建议阅读蓝图快速入门指南

  1. 要在编辑器中修改ACountdown实例(名为Countdown1)的行为,首先需要创建一个它的可编辑蓝图版本。要进行此操作,首先在 世界大纲视图(World Outliner) 中将其选中,然后点击 细节面板 中的 蓝图/添加脚本(Blueprint/Add Script) 按钮。

    AddScript.png

    借此我们可为包含修改后 ACountdown 类的蓝图资源提供路径和命名。

    SelectBlueprintPath.png

    此操作将新建一个代表Countdown1蓝图版本的资源。还将用这个新蓝图的实例替代Countdown1,以便使对蓝图进行的修改影响游戏中的Countdown1。

  2. 虚幻编辑器 将自动把我们带到 内容浏览器(Content Browser) 中的新资源。在资源上 点击右键 并选择“编辑”修改其 蓝图图表(Blueprint Graph)组件(Component) 层级和 默认值(Default Values)

    BlueprintInContentBrowser.png

    EditBlueprint.png

  3. 事件图表(Event Graph) 选项卡中中可找到函数和事件,因此首先对其进行选择。

    SelectEventGraph.png

    然后在 事件图表(Event Graph) 窗口中 右键点击 任意位置,即可将 CountdownHasFinished 函数作为事件节点添加,定义其行为。

    SelectEvent.png

  4. 现在可添加所需的额外功能——点击左键并从新节点右边的白色(执行)引脚连出引线。

    DragExecPin.png

    松开鼠标左键后,引擎将询问需要执行哪个函数或事件。在此教程中,我们将在倒计时结束时生成一个 粒子系统。需要一个 Spawn Emitter At Location 节点,因此在列表中将其选中。其能节约在搜索域中输入不完整短语(如spawn loc)的时间。然后左键点击并拖动黄色的“Location”引脚并将其附加到一个 Get Actor Location 函数。

    GetActorLocation.png

    现在只需要选择希望查看的效果。点击“发射器”模板(Emitter Template)下的“选择资源”,即会出现适当效果资源的列表。P_Explosion便是一个好选择,我们将其选中。

    SelectParticle.png

  5. 点击 蓝图编辑器 左上角的 编译(Emitter Template) 按钮保存修改。

  6. 按下 运行(Play) 后倒计时便会开始,倒计时数字归零后将播放爆炸效果。

    Explosion_Zero.png

    然而,我们编程的结果是让倒计时在最后显示“GO!”,而不是0。此情况已不复存在,因为我们已经用 蓝图 可视脚本完全替代C++功能。在此情况下这并非我们想要的效果,因此需要添加一个对此函数C++版本的调用,方法是右键点击 Countdown Has Finished 节点,从快捷菜单中选择 添加对父函数的调用(Add call to parent function)

    CallToParent_Menu.png

    此操作完成时,一个被标记为 Parent: Countdown Has Finished 的节点将被放置到 事件图表 中。通常会将父节点直接连接到事件节点,我们也如此进行操作。但这并非必需操作,因为父项调用节点与其他节点相同,可在任意处调用,甚至可以多次调用。

    CallToParent_ConnectPins.png

    注意:这将会替代到 Spawn Emitter At Location 的连接,因此需要连接 Parent: Countdown Has Finished 节点的右侧(向外)执行引脚,否则无法运行。

    CallToParent_FixPins.png

    现在运行游戏,倒计时结束后即可看到词语“GO!”(来自C++代码)和爆炸效果(来自蓝图图表)!

    Explosion_Go.png

完成的代码

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!"));
}