7

I've been working on converting some blueprint logic over to C++. One of the things I have is a button. The button can be pressed in VR and has a delegate that is called to notify any registered functions that the button press occurred. Here is how the delegate is declared in the AButtonItem.h class.

#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;
};

The delegate's broadcast function is then being called when the button is pressed like so:

ButtonItem_OnPressed.Broadcast();

(This function should defiantly be called because I have a debug statement that prints right before the call. Its also important to note this was all working when it was blueprint logic.)

Here is where I try to register with the delegate and how I declared the function that will be called:

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

}

I put a breakpoint and a print statement in the function OnNextBladeButtonPressed so I should immediately know when it works but its never happening. I also re-created the blueprint itself from scratch but still no luck. Sometimes on compile I get a crash due to the InvocationList being invalid but I haven't found much info on that issue either. Bottom line is, OnNextBladeButtonPressed is not getting called when it should be.

Edit: Here is where I call the broadcast function in my AButtonItem code. It seems to be getting called since i see the UE_LOG output in the console:

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);
            }
        }
    }
}
Katianie
  • 547
  • 1
  • 9
  • 31
  • if you put a breakpoint on the `AddDynamic` line, do you successfully hit that breakpoint? – Ruzihm Jul 01 '19 at 18:30
  • 1
    Yes it looks like I'm hitting it, here is a screen shot: https://i.imgur.com/AVrySOw.png – Katianie Jul 01 '19 at 18:32
  • I am wondering if there is somehow more than one component with a name of `NextBladeButton` and it's finding the wrong one. What happens if you remove the `break;` and put a breakpoint in that `if` block and see how many times it goes into that block? – Ruzihm Jul 01 '19 at 18:42
  • Stepped though with the debugger and it looks like it only happened once, ill get you a screenshot shortly. – Katianie Jul 01 '19 at 19:14
  • 1
    Here is the screenshot: https://i.imgur.com/qCee7ka.png – Katianie Jul 01 '19 at 19:20
  • What's the signature `FButtonItemPressedSignatrue`? – SilvanoCerza Jul 05 '19 at 09:18
  • Also where and how is `ButtonItem_OnPressed` assigned? – SilvanoCerza Jul 05 '19 at 09:22
  • @SilvanoCerza You can see how I declared FButtonItemPressedSignatrue in the ButtonItem.h, AddDelegate is called on ButtonItem_OnPressed in the above BeginPlay function. I added the code to the original question for where broadcast is being called (Tick function in the ButtonItem class) – Katianie Jul 05 '19 at 14:37
  • Is `OnNextBladeButtonPressed` marked `UFUNCTION`? What's its definition? – SilvanoCerza Jul 05 '19 at 15:08
  • I double checked and yes I think it is, here is a screenshot of the declaration: https://i.imgur.com/49FKARG.png and the implementation: https://i.imgur.com/l02szkt.png – Katianie Jul 05 '19 at 15:11
  • What does `ButtonItem_OnPressed.IsBound()` returns right after `AddDynamic()` and right before `Broadcast()`? Also is `AWeaponMaker` a `UObject`? I guess it is but just want to be sure. – SilvanoCerza Jul 06 '19 at 17:04
  • 1
    AWeaponMaker is an AActor not a UObject. In the button class it looks like it is getting unbound (screenshot: https://i.imgur.com/eE1nTJo.png)? Even though AddDynamic is being called on it. I noticed right after AddDynamic that it says bound but the object is stale, here is a screenshot: (https://i.imgur.com/30G2oMs.png). – Katianie Jul 06 '19 at 18:24
  • 1
    I ran it again and it looks like it is bound after AddDynamic, i added a print statement and it is showing up right after AddDynamic. But by the time it gets to the button class its unbound? It also sometimes crashes on the AddDynamic call like this: https://i.imgur.com/lCze5kv.png – Katianie Jul 06 '19 at 18:42
  • @Katianie Who's the owner of `AWeaponMaker` and how and where is instantiated? – SilvanoCerza Jul 08 '19 at 08:18
  • 1
    The blueprint has a bunch of child actors (AButtonItems) and that blueprint's parent class is AWeaponMaker. The blueprint is called WeaponMakerBlueprint The blueprint is placed in the persistent level. – Katianie Jul 09 '19 at 01:55
  • Have you verified that you're calling `Broadcast` on the same instance of `AButtonItem` that has a bound delegate? – SilvanoCerza Jul 15 '19 at 19:53
  • I have to imagine so, I removed all the other buttons except for one and I checked to make sure the name was "NextBladeButton". – Katianie Jul 16 '19 at 00:00
  • Try printing the address of `ButtonItem_OnPressed` before `AddDynamic` and `Broadcast` and verify they're equal. – SilvanoCerza Jul 16 '19 at 09:15
  • @SilvanoCerza Sorry for the delay, I took screenshots of what the addresses are at the time of AddDynamic and Broadcast. Keep in mind that the ButtonItem is a blueprint so in the WeaponMaker, I have a child actor for that button item, here are the screenshots: Button Before Add Dynamic: https://i.imgur.com/TP5eJXH.png ButtonComponent Before Broadcast: https://i.imgur.com/ibvVAZS.png this button item Before Broadcast: https://i.imgur.com/UyzUhPw.png – Katianie Jul 20 '19 at 21:22
  • Lost a total of 150 bounty points on this. – Katianie Aug 11 '19 at 19:24

2 Answers2

4

First of all:

UPROPERTY(EditAnywhere, Category = Callback)
FButtonItemPressedSignatrue ButtonItem_OnPressed;

This should be:

UPROPERTY(BlueprintAssignable, Category = Callback)
FButtonItemPressedSignatrue ButtonItem_OnPressed;

For convenience.

Secondly the tick function may be called before begin play is executed for a number of reasons. Your even't won't be broadcasted if the game hasn't begin play yet. So to avoid just add a check in your tick function.

if(bHasBegunPlay)
{
  // .. your logics ...
}
DDD
  • 2,444
  • 2
  • 11
  • 28
  • I tried this but no luck. I linked some debug images in the above comments to see if the addresses are correct/equal. – Katianie Jul 20 '19 at 21:23
  • It's highly unlikely that you would get such issues with the fixes i mentioned in my answer. Would you be able to update your question with the actual code? – DDD Jul 20 '19 at 23:26
  • No problem, you can now see the update. I set myHasStarted to false in the constructor and then I set it to true in BeginPlay(). – Katianie Jul 21 '19 at 00:15
  • UPROPERTY(EditAnywhere, Category = Callback) FButtonItemPressedSignatrue ButtonItem_OnPressed; }; – DDD Jul 21 '19 at 00:21
  • Did you change this as in my answer? – DDD Jul 21 '19 at 00:21
  • Yea, here's a screenshot: https://i.imgur.com/TDugpnc.png . Also take a look at the comments in the main question, I've been updating them periodically. – Katianie Jul 21 '19 at 00:30
2

Sometimes on compile I get a crash due to the InvocationList being invalid but I haven't found much info on that issue either. Bottom line is, OnNextBladeButtonPressed is not getting called when it should be.

I don't see any issue in the code from the question. At my glance, the issue could be in different location. I would suspect that AWeaponMaker had been deleted at moment of broadcasting.

Andrey Chistyakov
  • 3,830
  • 4
  • 13
  • 1
    Interesting point but I can see the WeaponMaker game object in front of me in the game when I press the button so at least the world game object is not being destroyed, also the button is not being destroyed ether (at least not visually). When I stepped through the debugger I could see "this" and "myNextBladeButton" were created at the point of AddDynamic. Is there a way we can test to see if it is being deleted like you mentioned? – Katianie Jul 05 '19 at 14:43
  • You could add some logging in the destructor to be sure. – SilvanoCerza Jul 06 '19 at 16:36
  • I added these debug statements: https://i.imgur.com/kq4uirX.png in both the tick and the destructor. In the destructor it looks like myNextBladeButton is null at the time it is called, but I'm not deleting it anywhere so why is the destructor getting called after initializing it? – Katianie Jul 06 '19 at 18:26
  • `this == NULL` will always return false since `this` can't be a `nullptr`. – SilvanoCerza Jul 08 '19 at 08:19
  • I would think so too but I saw a weird exception randomly where it was saying this is null. – Katianie Jul 08 '19 at 23:38
  • @SilvanoCerza delete this; It may not be null, but it's doing to be one hell of a ride... or ((MyClass*)nullptr)->SomeMethod(); – One Man Monkey Squad Jul 17 '19 at 15:38