【问题标题】:Unreal Engine 4: C++ Delegate not being called虚幻引擎 4:未调用 C++ 委托
【发布时间】:2019-11-11 06:09:34
【问题描述】:

我一直致力于将一些蓝图逻辑转换为 C++。我有一个按钮。可以在 VR 中按下按钮,并调用一个委托来通知任何已注册的函数按下了按钮。下面是在 AButtonItem.h 类中声明委托的方式。

#pragma once
#include "BaseItem.h"
#include "ButtonItem.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FButtonItemPressedSignatrue);

UCLASS()
class AButtonItem : public ABaseItem
{
    GENERATED_BODY()

protected:
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Touch)
    float myMaxButtonPress;

public:

    UPROPERTY(EditAnywhere, Category = Callback)
    FButtonItemPressedSignatrue ButtonItem_OnPressed;
};

当按下按钮时,代理的广播函数会被调用,如下所示:

ButtonItem_OnPressed.Broadcast();

(应该坚决调用这个函数,因为我有一个在调用之前打印的调试语句。同样重要的是要注意,当它是蓝图逻辑时,这一切都有效。)

这是我尝试向委托注册的地方以及我如何声明将被调用的函数:

WeaponMaker.h:

UFUNCTION()
void OnNextBladeButtonPressed();

WeaponMaker.cpp:

void AWeaponMaker::BeginPlay()
{
    Super::BeginPlay();

    TArray<USceneComponent*> weaponMakerComponents;
    this->GetRootComponent()->GetChildrenComponents(true, weaponMakerComponents);

    for (int componentIndex = 0; componentIndex < weaponMakerComponents.Num(); componentIndex++)
    {
        if (weaponMakerComponents[componentIndex]->GetName().Equals("NextBladeButton") == true)
        {
            myNextBladeButton = (AButtonItem*)weaponMakerComponents[componentIndex];
            break;
        }
    }

    if (myNextBladeButton != NULL)
    {
        myNextBladeButton->ButtonItem_OnPressed.AddDynamic(this, &AWeaponMaker::OnNextBladeButtonPressed);
    }

}

我在函数 OnNextBladeButtonPressed 中放置了一个断点和一个打印语句,所以我应该立即知道它什么时候工作,但它永远不会发生。我还从头开始重新创建了蓝图,但仍然没有运气。有时在编译时由于 InvocationList 无效而导致崩溃,但我也没有找到关于该问题的太多信息。底线是,OnNextBladeButtonPressed 没有被调用。

编辑:这是我在 AButtonItem 代码中调用广播函数的地方。因为我在控制台中看到 UE_LOG 输出,所以它似乎被调用了:

void AButtonItem::Tick(float deltaTime)
{
    FTransform buttonWorldTransform;
    FVector buttonLocalSpacePos;
    FVector ownerLocalSpacePos;
    FVector localDiff;
    float buttonPressAmount;

    if (myHasStarted == true)
    {
        Super::Tick(deltaTime);

        if (myButtonComponent != NULL)
        {
            if (myPrimaryHand != NULL)
            {
                //Get the world space location of the button.
                buttonWorldTransform = myButtonComponent->GetComponentTransform();

                //Convert the location of the button and the location of the hand to local space.
                buttonLocalSpacePos = buttonWorldTransform.InverseTransformPosition(myInitialOverlapPosition);
                ownerLocalSpacePos = buttonWorldTransform.InverseTransformPosition(myPrimaryHand->GetControllerLocation() + (myPrimaryHand->GetControllerRotation().Vector() * myPrimaryHand->GetReachDistance()));

                //Vector distance between button and hand in local space.
                localDiff = ownerLocalSpacePos - buttonLocalSpacePos;

                //Only interested in the z value difference.
                buttonPressAmount = FMath::Clamp(FMath::Abs(localDiff.Z), 0.0f, myMaxButtonPress);
                localDiff.Set(0.0f, 0.0f, buttonPressAmount);

                //Set the new relative position of button based on the hand and the start button position.
                myButtonComponent->SetRelativeLocation(myButtonInitialPosition - localDiff);

                //UE_LOG(LogTemp, Error, TEXT("buttonPressAmount:%f"), buttonPressAmount);
                if (buttonPressAmount >= myMaxButtonPress)
                {
                    if (myHasBeenTouchedOnce == false)
                    {
                        //Fire button pressed delegate
                        if (ButtonItem_OnPressed.IsBound() == true)
                        {
                            ButtonItem_OnPressed.Broadcast();
                            AsyncTask(ENamedThreads::GameThread, [=]()
                            {
                                ButtonItem_OnPressed.Broadcast();
                            });
                        }

                        myHasBeenTouchedOnce = true;
                        myButtonComponent->SetScalarParameterValueOnMaterials("State", 1.0f);
                        Super::VibrateTouchingHands(EVibrationType::VE_TOUCH);
                    }
                }
            }
            else
            {
                //Slowly reset the button position back to the initial position when not being touched.
                FVector newPosition = FMath::VInterpTo(myButtonComponent->GetRelativeTransform().GetLocation(), myButtonInitialPosition, deltaTime, 10.0f);
                myButtonComponent->SetRelativeLocation(newPosition);
            }
        }
    }
}

【问题讨论】:

  • 是的,看起来我在打它,这是一个屏幕截图:i.imgur.com/AVrySOw.png
  • 截图如下:i.imgur.com/qCee7ka.png
  • AWeaponMaker 是 AActor 而不是 UObject。在按钮类中,它看起来好像是未绑定的(屏幕截图:i.imgur.com/eE1nTJo.png)?即使 AddDynamic 正在被调用。我在 AddDynamic 之后注意到它说绑定但对象已过时,这是一个屏幕截图:(i.imgur.com/30G2oMs.png)。
  • 我再次运行它,它看起来像是在 AddDynamic 之后绑定的,我添加了一个打印语句,它在 AddDynamic 之后立即显示。但是当它到达按钮类时,它是未绑定的吗?它有时也会像这样在 AddDynamic 调用中崩溃:i.imgur.com/lCze5kv.png
  • 蓝图有一堆子actor(AButtonItems),蓝图的父类是AWeaponMaker。蓝图称为 WeaponMakerBlueprint 蓝图放置在持久关卡中。

标签: c++ events callback delegates unreal-engine4


【解决方案1】:

有时在编译时由于 InvocationList 无效而导致崩溃,但我也没有找到关于该问题的太多信息。底线是,OnNextBladeButtonPressed 没有被调用。

我在问题的代码中没有看到任何问题。在我看来,问题可能出在不同的位置。我怀疑 AWeaponMaker 在播放时已被删除。

【讨论】:

  • 有趣的一点,但是当我按下按钮时,我可以在游戏中看到我面前的 WeaponMaker 游戏对象,所以至少世界游戏对象没有被破坏,按钮也没有被以太破坏(至少不是视觉上的)。当我单步调试调试器时,我可以看到“this”和“myNextBladeButton”是在 AddDynamic 处创建的。有没有办法可以测试它是否像您提到的那样被删除?
  • 您可以在析构函数中添加一些日志以确保。
  • 我在刻度和析构函数中都添加了这些调试语句:i.imgur.com/kq4uirX.png。在析构函数中,myNextBladeButton 在被调用时看起来为 null,但我没有在任何地方删除它,那么为什么在初始化后调用析构函数?
  • this == NULL 将始终返回 false,因为 this 不能是 nullptr
  • 我也这么认为,但我随机看到一个奇怪的异常,它说这是 null。
【解决方案2】:

首先:

UPROPERTY(EditAnywhere, Category = Callback)
FButtonItemPressedSignatrue ButtonItem_OnPressed;

这应该是:

UPROPERTY(BlueprintAssignable, Category = Callback)
FButtonItemPressedSignatrue ButtonItem_OnPressed;

为了方便。

其次,由于多种原因,可能会在开始播放之前调用 tick 函数。如果游戏尚未开始播放,您的事件将不会被播放。因此,为了避免在您的刻度功能中添加一个检查。

if(bHasBegunPlay)
{
  // .. your logics ...
}

【讨论】:

  • 我试过这个,但没有运气。我在上面的 cmets 中链接了一些调试图像,以查看地址是否正确/相等。
  • 通过我在回答中提到的修复程序,您不太可能遇到此类问题。你能用实际的代码更新你的问题吗?
  • 没问题,你现在可以看到更新了。我在构造函数中将 myHasStarted 设置为 false,然后在 BeginPlay() 中将其设置为 true。
  • UPROPERTY(EditAnywhere, Category = Callback) FButtonItemPressedSignatrue ButtonItem_OnPressed; };
  • 您是否按照我的回答更改了此内容?
猜你喜欢
  • 2018-05-28
  • 2018-12-30
  • 2015-10-12
  • 1970-01-01
  • 2020-07-24
  • 2015-02-04
  • 1970-01-01
  • 2015-08-01
  • 2015-01-13
相关资源
最近更新 更多